Skip to content

Commit

Permalink
Add support for globs in package.json#source (#9590)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Mar 20, 2024
1 parent 90ca165 commit 9f08a86
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 48 deletions.
130 changes: 82 additions & 48 deletions packages/core/core/src/requests/EntryRequest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow strict-local

import type {Async, FilePath, PackageJSON} from '@parcel/types';
import type {Async, FilePath, PackageJSON, Glob} from '@parcel/types';
import type {StaticRunOpts} from '../RequestTracker';
import type {Entry, InternalFile, ParcelOptions} from '../types';
import type {FileSystem} from '@parcel/fs';
Expand Down Expand Up @@ -41,6 +41,7 @@ export type EntryRequest = {|
export type EntryResult = {|
entries: Array<Entry>,
files: Array<InternalFile>,
globs: Array<Glob>,
|};

const type = 'entry_request';
Expand Down Expand Up @@ -68,8 +69,10 @@ async function run({input, api, options}): Promise<EntryResult> {

// If the entry specifier is a glob, add a glob node so
// we invalidate when a new file matches.
if (isGlob(filePath)) {
api.invalidateOnFileCreate({glob: input});
for (let glob of result.globs) {
api.invalidateOnFileCreate({
glob: toProjectPath(options.projectRoot, glob),
});
}

// Invalidate whenever an entry is deleted.
Expand Down Expand Up @@ -177,8 +180,9 @@ export class EntryResolver {
(p, res) => ({
entries: p.entries.concat(res.entries),
files: p.files.concat(res.files),
globs: p.globs.concat(res.globs),
}),
{entries: [], files: []},
{entries: [], files: [], globs: [entry]},
);
}

Expand All @@ -193,6 +197,7 @@ export class EntryResolver {
filePath: toProjectPath(this.options.projectRoot, filePath),
},
];
let globs = [];

let targetsWithSources = 0;
if (pkg.targets) {
Expand All @@ -204,36 +209,48 @@ export class EntryResolver {
? target.source
: [target.source];
let i = 0;
for (let relativeSource of targetSources) {
let source = path.join(entry, relativeSource);
for (let source of targetSources) {
let sources;
if (isGlob(source)) {
globs.push(source);
sources = await glob(source, this.options.inputFS, {
onlyFiles: true,
cwd: entry,
});
} else {
sources = [source];
}
let keyPath = `/targets/${targetName}/source${
Array.isArray(target.source) ? `/${i}` : ''
}`;
await assertFile(
this.options.inputFS,
entry,
relativeSource,
filePath,
keyPath,
this.options,
);
for (let relativeSource of sources) {
let source = path.join(entry, relativeSource);
await assertFile(
this.options.inputFS,
entry,
relativeSource,
filePath,
keyPath,
this.options,
);

entries.push({
filePath: toProjectPath(this.options.projectRoot, source),
packagePath: toProjectPath(this.options.projectRoot, entry),
target: targetName,
loc: {
filePath: toProjectPath(
this.options.projectRoot,
pkg.filePath,
),
...getJSONSourceLocation(
pkg.map.pointers[keyPath],
'value',
),
},
});
i++;
entries.push({
filePath: toProjectPath(this.options.projectRoot, source),
packagePath: toProjectPath(this.options.projectRoot, entry),
target: targetName,
loc: {
filePath: toProjectPath(
this.options.projectRoot,
pkg.filePath,
),
...getJSONSourceLocation(
pkg.map.pointers[keyPath],
'value',
),
},
});
i++;
}
}
}
}
Expand All @@ -251,25 +268,40 @@ export class EntryResolver {
: [pkg.source];
let i = 0;
for (let pkgSource of pkgSources) {
let source = path.join(path.dirname(filePath), pkgSource);
let sources;
if (isGlob(pkgSource)) {
globs.push(pkgSource);
sources = await glob(pkgSource, this.options.inputFS, {
onlyFiles: true,
cwd: path.dirname(filePath),
});
} else {
sources = [pkgSource];
}
let keyPath = `/source${Array.isArray(pkg.source) ? `/${i}` : ''}`;
await assertFile(
this.options.inputFS,
entry,
pkgSource,
filePath,
keyPath,
this.options,
);
entries.push({
filePath: toProjectPath(this.options.projectRoot, source),
packagePath: toProjectPath(this.options.projectRoot, entry),
loc: {
filePath: toProjectPath(this.options.projectRoot, pkg.filePath),
...getJSONSourceLocation(pkg.map.pointers[keyPath], 'value'),
},
});
i++;
for (let relativeSource of sources) {
let source = path.join(path.dirname(filePath), relativeSource);
await assertFile(
this.options.inputFS,
entry,
relativeSource,
filePath,
keyPath,
this.options,
);
entries.push({
filePath: toProjectPath(this.options.projectRoot, source),
packagePath: toProjectPath(this.options.projectRoot, entry),
loc: {
filePath: toProjectPath(
this.options.projectRoot,
pkg.filePath,
),
...getJSONSourceLocation(pkg.map.pointers[keyPath], 'value'),
},
});
i++;
}
}
}

Expand All @@ -278,6 +310,7 @@ export class EntryResolver {
return {
entries,
files,
globs,
};
}
}
Expand All @@ -304,6 +337,7 @@ export class EntryResolver {
},
],
files: [],
globs: [],
};
}

Expand Down
154 changes: 154 additions & 0 deletions packages/core/integration-tests/test/monorepos.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
run,
overlayFS,
getNextBuild,
runBundle,
} from '@parcel/test-utils';

const distDir = path.join(__dirname, '/integration/monorepo/dist/default');
Expand Down Expand Up @@ -963,4 +964,157 @@ describe('monorepos', function () {
});
});
});

it('should support globs in the source field', async function () {
const dir = path.join(__dirname, 'source-glob');
overlayFS.mkdirp(dir);

await fsFixture(overlayFS, dir)`
yarn.lock:
package.json:
{
"source": "foo/*.js",
"targets": {
"default": {
"outputFormat": "esmodule",
"isLibrary": true
}
}
}
foo/a.js:
export default 'a';
foo/b.js:
export default 'b';
`;

let b = await bundle(dir, {
inputFS: overlayFS,
mode: 'production',
});

assertBundles(b, [
{
assets: ['a.js'],
},
{
assets: ['b.js'],
},
]);

for (let bundle of b.getBundles()) {
let res = await runBundle(b, bundle);
assert.equal(res.default, bundle.name[0]);
}
});

it('should support globs in target-specific source field', async function () {
const dir = path.join(__dirname, 'source-target-glob');
overlayFS.mkdirp(dir);

await fsFixture(overlayFS, dir)`
yarn.lock:
package.json:
{
"targets": {
"foo-esm": {
"source": "foo/*.js",
"outputFormat": "esmodule",
"isLibrary": true
},
"foo-cjs": {
"source": "foo/*.js",
"outputFormat": "commonjs",
"isLibrary": true
}
}
}
foo/a.js:
export default 'a';
foo/b.js:
export default 'b';
`;

let b = await bundle(dir, {
inputFS: overlayFS,
mode: 'production',
});

assertBundles(b, [
{
assets: ['a.js'],
},
{
assets: ['b.js'],
},
{
assets: ['a.js'],
},
{
assets: ['b.js'],
},
]);

for (let bundle of b.getBundles()) {
let res = await runBundle(b, bundle);
assert.equal(res.default, bundle.name[0]);
}
});

it('should watch globs in source field', async function () {
const dir = path.join(__dirname, 'input', 'source-glob-watch');
await overlayFS.mkdirp(dir);
await inputFS.mkdirp(dir);

await fsFixture(overlayFS, dir)`
yarn.lock:
package.json:
{
"source": "foo/*.js",
"targets": {
"default": {
"outputFormat": "esmodule",
"isLibrary": true
}
}
}
foo/a.js:
export default 'a';
`;

let b = await bundler(dir, {
inputFS: overlayFS,
});

subscription = await b.watch();
let evt = await getNextBuild(b);

assertBundles(evt.bundleGraph, [
{
assets: ['a.js'],
},
]);

await fsFixture(overlayFS, dir)`
foo/b.js:
export default 'b';
`;

evt = await getNextBuild(b);
assertBundles(evt.bundleGraph, [
{
assets: ['a.js'],
},
{
assets: ['b.js'],
},
]);
});
});

0 comments on commit 9f08a86

Please sign in to comment.