Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse import assertions #12278

Merged
merged 8 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ declare module "browserslist" {
export = browserslist;
}

// TODO remove that when @types/estree is updated
interface ImportAttributeNode {
type: "ImportAttribute";
key: import("estree").Identifier | import("estree").Literal;
value: import("estree").Literal;
}

type TODO = any;

type RecursiveArrayOrRecord<T> =
Expand Down
6 changes: 6 additions & 0 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,12 @@ export interface ModuleOptions {
* A rule description with conditions and effects for modules.
*/
export interface RuleSetRule {
/**
* Match on import assertions of the dependency.
*/
assert?: {
[k: string]: RuleSetConditionOrConditions;
};
/**
* Match the child compiler name.
*/
Expand Down
8 changes: 6 additions & 2 deletions lib/NormalModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,13 @@ class NormalModule extends Module {
*/
identifier() {
if (this.layer === null) {
return this.request;
if (this.type === "javascript/auto") {
return this.request;
} else {
return `${this.type}|${this.request}`;
}
} else {
return `${this.request}|${this.layer}`;
return `${this.type}|${this.request}|${this.layer}`;
}
}

Expand Down
10 changes: 8 additions & 2 deletions lib/NormalModuleFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ModuleGraph = require("./ModuleGraph");
const NormalModule = require("./NormalModule");
const BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
const DescriptionDataMatcherRulePlugin = require("./rules/DescriptionDataMatcherRulePlugin");
const ObjectMatcherRulePlugin = require("./rules/ObjectMatcherRulePlugin");
const RuleSetCompiler = require("./rules/RuleSetCompiler");
const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
const LazySet = require("./util/LazySet");
Expand All @@ -44,6 +44,7 @@ const { parseResource } = require("./util/identifier");
* @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
* @property {string} context
* @property {string} request
* @property {Record<string, any> | undefined} assertions
* @property {ModuleDependency[]} dependencies
* @property {Object} createData
* @property {LazySet<string>} fileDependencies
Expand Down Expand Up @@ -182,7 +183,8 @@ const ruleSetCompiler = new RuleSetCompiler([
new BasicMatcherRulePlugin("issuer"),
new BasicMatcherRulePlugin("compiler"),
new BasicMatcherRulePlugin("issuerLayer"),
new DescriptionDataMatcherRulePlugin(),
new ObjectMatcherRulePlugin("assert", "assertions"),
new ObjectMatcherRulePlugin("descriptionData"),
new BasicEffectRulePlugin("type"),
new BasicEffectRulePlugin("sideEffects"),
new BasicEffectRulePlugin("parser"),
Expand Down Expand Up @@ -339,6 +341,7 @@ class NormalModuleFactory extends ModuleFactory {
context,
dependencies,
request,
assertions,
resolveOptions,
fileDependencies,
missingDependencies,
Expand Down Expand Up @@ -447,6 +450,7 @@ class NormalModuleFactory extends ModuleFactory {
resourceQuery: resourceDataForRules.query,
resourceFragment: resourceDataForRules.fragment,
scheme,
assertions,
mimetype: matchResourceData ? "" : resourceData.data.mimetype || "",
dependency: dependencyType,
descriptionData: matchResourceData
Expand Down Expand Up @@ -694,6 +698,7 @@ class NormalModuleFactory extends ModuleFactory {
const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
const dependency = dependencies[0];
const request = dependency.request;
const assertions = dependency.assertions;
const contextInfo = data.contextInfo;
const fileDependencies = new LazySet();
const missingDependencies = new LazySet();
Expand All @@ -704,6 +709,7 @@ class NormalModuleFactory extends ModuleFactory {
resolveOptions,
context,
request,
assertions,
dependencies,
fileDependencies,
missingDependencies,
Expand Down
30 changes: 18 additions & 12 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,18 +482,6 @@ const applyModuleDefaults = (
or: ["text/javascript", "application/javascript"]
},
...esm
},
{
dependency: "url",
oneOf: [
{
scheme: /^data$/,
type: "asset/inline"
},
{
type: "asset/resource"
}
]
}
];
if (asyncWebAssembly) {
Expand Down Expand Up @@ -541,6 +529,24 @@ const applyModuleDefaults = (
...wasm
});
}
rules.push(
{
dependency: "url",
oneOf: [
{
scheme: /^data$/,
type: "asset/inline"
},
{
type: "asset/resource"
}
]
},
{
assert: { type: "json" },
type: "json"
}
);
return rules;
});
};
Expand Down
9 changes: 6 additions & 3 deletions lib/dependencies/HarmonyExportDependencyParserPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency")
const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
const {
harmonySpecifierTag
harmonySpecifierTag,
getAssertions
} = require("./HarmonyImportDependencyParserPlugin");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");

Expand Down Expand Up @@ -48,7 +49,8 @@ module.exports = class HarmonyExportDependencyParserPlugin {
parser.state.module.addPresentationalDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(
source,
parser.state.lastHarmonyImportOrder
parser.state.lastHarmonyImportOrder,
getAssertions(statement)
);
sideEffectDep.loc = Object.create(statement.loc);
sideEffectDep.loc.index = -1;
Expand Down Expand Up @@ -127,7 +129,8 @@ module.exports = class HarmonyExportDependencyParserPlugin {
harmonyNamedExports,
null,
this.strictExportPresence,
null
null,
settings.assertions
);
} else {
dep = new HarmonyExportSpecifierDependency(id, name);
Expand Down
6 changes: 4 additions & 2 deletions lib/dependencies/HarmonyExportImportedSpecifierDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
* @param {ReadonlyArray<HarmonyExportImportedSpecifierDependency> | Iterable<HarmonyExportImportedSpecifierDependency>} otherStarExports other star exports in the module before this import
* @param {boolean} strictExportPresence when true, missing exports in the imported module lead to errors instead of warnings
* @param {HarmonyStarExportsList} allStarExports all star exports in the module
* @param {Record<string, any>=} assertions import assertions
*/
constructor(
request,
Expand All @@ -168,9 +169,10 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
activeExports,
otherStarExports,
strictExportPresence,
allStarExports
allStarExports,
assertions
) {
super(request, sourceOrder);
super(request, sourceOrder, assertions);

this.ids = ids;
this.name = name;
Expand Down
6 changes: 5 additions & 1 deletion lib/dependencies/HarmonyImportDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ class HarmonyImportDependency extends ModuleDependency {
*
* @param {string} request request string
* @param {number} sourceOrder source order
* @param {Record<string, any>=} assertions import assertions
*/
constructor(request, sourceOrder) {
constructor(request, sourceOrder, assertions) {
super(request);
this.sourceOrder = sourceOrder;
this.assertions = assertions;
}

get category() {
Expand Down Expand Up @@ -201,12 +203,14 @@ class HarmonyImportDependency extends ModuleDependency {
serialize(context) {
const { write } = context;
write(this.sourceOrder);
write(this.assertions);
super.serialize(context);
}

deserialize(context) {
const { read } = context;
this.sourceOrder = read();
this.assertions = read();
super.deserialize(context);
}
}
Expand Down
45 changes: 40 additions & 5 deletions lib/dependencies/HarmonyImportDependencyParserPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const HarmonyExports = require("./HarmonyExports");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");

/** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclaration */
/** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclaration */
/** @typedef {import("estree").Identifier} Identifier */
/** @typedef {import("estree").ImportDeclaration} ImportDeclaration */
/** @typedef {import("estree").ImportExpression} ImportExpression */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../optimize/InnerGraph").InnerGraph} InnerGraph */
/** @typedef {import("../optimize/InnerGraph").TopLevelSymbol} TopLevelSymbol */
Expand All @@ -29,8 +33,32 @@ const harmonySpecifierTag = Symbol("harmony import");
* @property {number} sourceOrder
* @property {string} name
* @property {boolean} await
* @property {Record<string, any> | undefined} assertions
*/

/**
* @param {ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression} node node with assertions
* @returns {Record<string, any> | undefined} assertions
*/
function getAssertions(node) {
// TODO remove cast when @types/estree has been updated to import assertions
const assertions = /** @type {{ assertions?: ImportAttributeNode[] }} */ (
node
).assertions;
if (assertions === undefined) {
return undefined;
}
const result = {};
for (const assertion of assertions) {
const key =
assertion.key.type === "Identifier"
? assertion.key.name
: assertion.key.value;
result[key] = assertion.value.value;
}
return result;
}

module.exports = class HarmonyImportDependencyParserPlugin {
constructor(options) {
this.strictExportPresence = options.strictExportPresence;
Expand Down Expand Up @@ -65,9 +93,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
clearDep.loc = statement.loc;
parser.state.module.addPresentationalDependency(clearDep);
parser.unsetAsiPosition(statement.range[1]);
const assertions = getAssertions(statement);
const sideEffectDep = new HarmonyImportSideEffectDependency(
source,
parser.state.lastHarmonyImportOrder
parser.state.lastHarmonyImportOrder,
assertions
);
sideEffectDep.loc = statement.loc;
parser.state.module.addDependency(sideEffectDep);
Expand All @@ -82,7 +112,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
name,
source,
ids,
sourceOrder: parser.state.lastHarmonyImportOrder
sourceOrder: parser.state.lastHarmonyImportOrder,
assertions: getAssertions(statement)
});
return true;
}
Expand All @@ -97,7 +128,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
settings.ids,
settings.name,
expr.range,
this.strictExportPresence
this.strictExportPresence,
settings.assertions
);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = true;
Expand All @@ -118,7 +150,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
ids,
settings.name,
expr.range,
this.strictExportPresence
this.strictExportPresence,
settings.assertions
);
dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
dep.loc = expr.loc;
Expand All @@ -138,7 +171,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
ids,
settings.name,
callee.range,
this.strictExportPresence
this.strictExportPresence,
settings.assertions
);
dep.directImport = members.length === 0;
dep.call = true;
Expand Down Expand Up @@ -206,3 +240,4 @@ module.exports = class HarmonyImportDependencyParserPlugin {
};

module.exports.harmonySpecifierTag = harmonySpecifierTag;
module.exports.getAssertions = getAssertions;
4 changes: 2 additions & 2 deletions lib/dependencies/HarmonyImportSideEffectDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */

class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
constructor(request, sourceOrder) {
super(request, sourceOrder);
constructor(request, sourceOrder, assertions) {
super(request, sourceOrder, assertions);
}

get type() {
Expand Down
12 changes: 10 additions & 2 deletions lib/dependencies/HarmonyImportSpecifierDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
const idsSymbol = Symbol("HarmonyImportSpecifierDependency.ids");

class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
constructor(request, sourceOrder, ids, name, range, strictExportPresence) {
super(request, sourceOrder);
constructor(
request,
sourceOrder,
ids,
name,
range,
strictExportPresence,
assertions
) {
super(request, sourceOrder, assertions);
this.ids = ids;
this.name = name;
this.range = range;
Expand Down
9 changes: 8 additions & 1 deletion lib/dependencies/ModuleDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ class ModuleDependency extends Dependency {
this.request = request;
this.userRequest = request;
this.range = undefined;
// assertions must be serialized by subclasses that use it
/** @type {Record<string, any> | undefined} */
this.assertions = undefined;
}

/**
* @returns {string | null} an identifier to merge equal requests
*/
getResourceIdentifier() {
return `module${this.request}`;
let str = `module${this.request}`;
if (this.assertions !== undefined) {
str += JSON.stringify(this.assertions);
}
return str;
}

/**
Expand Down