Skip to content

Commit

Permalink
Add glob resolver plugin (#5933)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Jun 7, 2021
1 parent f2100c1 commit f582ffd
Show file tree
Hide file tree
Showing 29 changed files with 453 additions and 66 deletions.
1 change: 1 addition & 0 deletions packages/core/core/src/AssetGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ export default class AssetGraph extends ContentGraph<AssetGraphNode> {
depNode.complete = true;
depNodesWithAssets.push([depNode, nodeFromAsset(dependentAsset)]);
}
depNode.value.sourceAssetType = assetNode.value.type;
depNodeIds.push(this.addNode(depNode));
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/core/src/public/Dependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ export default class Dependency implements IDependency {
return this.#dep.sourcePath;
}

get sourceAssetType(): ?string {
return this.#dep.sourceAssetType;
}

get resolveFrom(): ?string {
return this.#dep.resolveFrom ?? this.#dep.sourcePath;
}
Expand Down
24 changes: 14 additions & 10 deletions packages/core/core/src/requests/PathRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import type {StaticRunOpts} from '../RequestTracker';
import type {AssetGroup, Dependency, ParcelOptions} from '../types';
import type {ConfigAndCachePath} from './ParcelConfigRequest';

import ThrowableDiagnostic, {
errorToDiagnostic,
escapeMarkdown,
md,
} from '@parcel/diagnostic';
import ThrowableDiagnostic, {errorToDiagnostic, md} from '@parcel/diagnostic';
import {PluginLogger} from '@parcel/logger';
import {relativePath} from '@parcel/utils';
import nullthrows from 'nullthrows';
Expand Down Expand Up @@ -207,6 +203,7 @@ export class ResolverRunner {
try {
let result = await resolver.plugin.resolve({
filePath,
pipeline,
dependency: dep,
options: this.pluginOptions,
logger: new PluginLogger({origin: resolver.name}),
Expand All @@ -220,6 +217,10 @@ export class ResolverRunner {
};
}

if (result.isAsync != null) {
dependency.isAsync = result.isAsync;
}

if (result.isExcluded) {
return null;
}
Expand All @@ -233,7 +234,10 @@ export class ResolverRunner {
sideEffects: result.sideEffects,
code: result.code,
env: dependency.env,
pipeline: pipeline ?? dependency.pipeline,
pipeline:
result.pipeline === undefined
? pipeline ?? dependency.pipeline
: result.pipeline,
isURL: dependency.isURL,
},
invalidateOnFileCreate: result.invalidateOnFileCreate,
Expand Down Expand Up @@ -275,15 +279,15 @@ export class ResolverRunner {
let resolveFrom = dependency.resolveFrom ?? dependency.sourcePath;
let dir =
resolveFrom != null
? escapeMarkdown(relativePath(this.options.projectRoot, resolveFrom))
? relativePath(this.options.projectRoot, resolveFrom)
: '';

let specifier = escapeMarkdown(dependency.moduleSpecifier || '');

// $FlowFixMe because of the err.code assignment
let err = await this.getThrowableDiagnostic(
dependency,
md`Failed to resolve '${specifier}' ${dir ? `from '${dir}'` : ''}`,
md`Failed to resolve '${dependency.moduleSpecifier}' ${
dir ? `from '${dir}'` : ''
}`,
);

// Merge diagnostics
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export type Dependency = {|
target: ?Target,
sourceAssetId: ?string,
sourcePath: ?string,
sourceAssetType?: ?string,
resolveFrom: ?string,
symbols: ?Map<
Symbol,
Expand Down
200 changes: 161 additions & 39 deletions packages/core/integration-tests/test/glob.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
// @flow
import assert from 'assert';
import path from 'path';
import {bundle, run, assertBundleTree, outputFS} from '@parcel/test-utils';
import {
bundle,
run,
assertBundles,
outputFS,
inputFS,
} from '@parcel/test-utils';
import nullthrows from 'nullthrows';

describe.skip('glob', function() {
describe('glob', function() {
it('should require a glob of files', async function() {
let b = await bundle(path.join(__dirname, '/integration/glob/index.js'));

await assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', '*.js', 'a.js', 'b.js'],
childBundles: [
{
type: 'map',
},
],
});
await assertBundles(b, [
{
name: 'index.js',
assets: ['index.js', '*.js', 'a.js', 'b.js'],
},
]);

let output = await run(b);
assert.equal(typeof output, 'function');
Expand All @@ -26,15 +31,12 @@ describe.skip('glob', function() {
path.join(__dirname, '/integration/glob-deep/index.js'),
);

await assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', '*.js', 'a.js', 'b.js', 'c.js', 'z.js'],
childBundles: [
{
type: 'map',
},
],
});
await assertBundles(b, [
{
name: 'index.js',
assets: ['index.js', '*.js', 'a.js', 'b.js', 'c.js', 'z.js'],
},
]);

let output = await run(b);
assert.equal(typeof output, 'function');
Expand All @@ -46,35 +48,155 @@ describe.skip('glob', function() {
path.join(__dirname, '/integration/glob-css/index.js'),
);

await assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', 'index.css', '*.css', 'other.css', 'local.css'],
childBundles: [
{
name: 'index.css',
assets: ['index.css', 'other.css', 'local.css'],
childBundles: [
{
type: 'map',
},
],
},
{
type: 'map',
},
],
});
await assertBundles(b, [
{
name: 'index.js',
assets: ['index.js'],
},
{
name: 'index.css',
assets: ['*.css', 'index.css', 'other.css', 'local.css'],
},
]);

let output = await run(b);
assert.equal(typeof output, 'function');
assert.equal(output(), 2);

let css = await outputFS.readFile(
path.join(__dirname, '/dist/index.css'),
nullthrows(b.getBundles().find(b => b.type === 'css')).filePath,
'utf8',
);
assert(css.includes('.local'));
assert(css.includes('.other'));
assert(css.includes('.index'));
});

it('should require a glob using a pipeline', async function() {
let b = await bundle(
path.join(__dirname, '/integration/glob-pipeline/index.js'),
);

await assertBundles(b, [
{
name: 'index.js',
assets: ['index.js', '*.js', 'bundle-url.js'],
},
{
type: 'txt',
assets: ['a.txt'],
},
{
type: 'txt',
assets: ['b.txt'],
},
]);

let output = await run(b);
assert.deepEqual(output, {
a: `http://localhost/${path.basename(
nullthrows(b.getBundles().find(b => b.name.startsWith('a'))).filePath,
)}`,
b: `http://localhost/${path.basename(
nullthrows(b.getBundles().find(b => b.name.startsWith('b'))).filePath,
)}`,
});
});

it('should import a glob with dynamic import', async function() {
let b = await bundle(
path.join(__dirname, '/integration/glob-async/index.js'),
);

await assertBundles(b, [
{
name: 'index.js',
assets: [
'index.js',
'*.js',
'bundle-url.js',
'cacheLoader.js',
'js-loader.js',
],
},
{
type: 'js',
assets: ['a.js'],
},
{
type: 'js',
assets: ['b.js'],
},
]);

let output = await run(b);
assert.equal(await output(), 3);
});

it('should error when an unsupported asset type imports a glob', async function() {
let filePath = path.join(__dirname, '/integration/glob-error/index.html');
// $FlowFixMe
await assert.rejects(() => bundle(filePath), {
name: 'BuildError',
diagnostics: [
{
message: "Failed to resolve 'foo/\\*.js' from './index.html'",
origin: '@parcel/core',
},
{
message: 'Glob imports are not supported in html files.',
origin: '@parcel/resolver-glob',
codeFrame: undefined,
},
],
});
});

it('should error when a URL dependency imports a glob', async function() {
let filePath = path.join(__dirname, '/integration/glob-error/index.css');
// $FlowFixMe
await assert.rejects(() => bundle(filePath), {
name: 'BuildError',
diagnostics: [
{
message: "Failed to resolve 'images/\\*.jpg' from './index.css'",
origin: '@parcel/core',
filePath,
codeFrame: {
code: await inputFS.readFile(filePath, 'utf8'),
codeHighlights: [
{
start: {
column: 7,
line: 2,
},
end: {
column: 20,
line: 2,
},
},
],
},
},
{
message: 'Glob imports are not supported in URL dependencies.',
origin: '@parcel/resolver-glob',
codeFrame: {
codeHighlights: [
{
start: {
column: 7,
line: 2,
},
end: {
column: 20,
line: 2,
},
},
],
},
},
],
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var vars = import('./dir/*.js');

module.exports = async function () {
return await vars.a() + await vars.b();
};
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.index {
background: url(images/*.jpg);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script src="foo/*.js"></script>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var vars = require('url:./dir/*.txt');
module.exports = vars;
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
Empty file.
Loading

0 comments on commit f582ffd

Please sign in to comment.