Skip to content

Commit

Permalink
parse import assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
xtuc authored and sokra committed Jul 16, 2021
1 parent ee6451c commit b166f46
Show file tree
Hide file tree
Showing 17 changed files with 107 additions and 9 deletions.
13 changes: 13 additions & 0 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,10 @@ export interface ModuleOptions {
* A rule description with conditions and effects for modules.
*/
export interface RuleSetRule {
/**
* Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.
*/
assert?: ImportAssertions;
/**
* Match the child compiler name.
*/
Expand Down Expand Up @@ -1427,6 +1431,15 @@ export interface RuleSetRule {
*/
use?: RuleSetUse;
}
/**
* Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.
*/
export interface ImportAssertions {
/**
* The module type.
*/
type?: "json";
}
/**
* Logic operators used in a condition matcher.
*/
Expand Down
12 changes: 12 additions & 0 deletions lib/NormalModuleFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const { parseResource } = require("./util/identifier");
* @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
* @property {string} context
* @property {string} request
* @property {Map} assertions
* @property {ModuleDependency[]} dependencies
* @property {Object} createData
* @property {LazySet<string>} fileDependencies
Expand Down Expand Up @@ -189,6 +190,7 @@ const ruleSetCompiler = new RuleSetCompiler([
new BasicEffectRulePlugin("resolve"),
new BasicEffectRulePlugin("generator"),
new BasicEffectRulePlugin("layer"),
new BasicEffectRulePlugin("assert"),
new UseEffectRulePlugin()
]);

Expand Down Expand Up @@ -339,6 +341,7 @@ class NormalModuleFactory extends ModuleFactory {
context,
dependencies,
request,
assertions = new Map(),
resolveOptions,
fileDependencies,
missingDependencies,
Expand Down Expand Up @@ -514,6 +517,13 @@ class NormalModuleFactory extends ModuleFactory {
} else {
type = "javascript/auto";
}
if (assertions.has("type") && type !== assertions.get("type")) {
throw new Error(
`type mismatch; requested type ${assertions.get(
"type"
)} but got ${type}`
);
}
}
const resolveOptions = settings.resolve;
const layer = settings.layer;
Expand Down Expand Up @@ -694,6 +704,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 +715,7 @@ class NormalModuleFactory extends ModuleFactory {
resolveOptions,
context,
request,
assertions,
dependencies,
fileDependencies,
missingDependencies,
Expand Down
6 changes: 4 additions & 2 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,11 +449,13 @@ const applyModuleDefaults = (
},
{
test: /\.json$/i,
type: "json"
type: "json",
assert: { type: "json" }
},
{
mimetype: "application/json",
type: "json"
type: "json",
assert: { type: "json" }
},
{
test: /\.mjs$/i,
Expand Down
3 changes: 2 additions & 1 deletion lib/dependencies/HarmonyImportDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ class HarmonyImportDependency extends ModuleDependency {
*
* @param {string} request request string
* @param {number} sourceOrder source order
* @param {Map} assertions import assertions
*/
constructor(request, sourceOrder) {
constructor(request, sourceOrder, assertions = new Map()) {
super(request);
this.sourceOrder = sourceOrder;
}
Expand Down
14 changes: 13 additions & 1 deletion lib/dependencies/HarmonyImportDependencyParserPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ const harmonySpecifierTag = Symbol("harmony import");
* @property {boolean} await
*/

function getAssertionMap(node) {
if (node.assertions === undefined) {
return new Map();
}
return node.assertions.reduce((map, assert) => {
map.set(assert.key.name, assert.value.value);
return map;
}, new Map());
}

module.exports = class HarmonyImportDependencyParserPlugin {
constructor(options) {
this.strictExportPresence = options.strictExportPresence;
Expand Down Expand Up @@ -65,9 +75,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
clearDep.loc = statement.loc;
parser.state.module.addPresentationalDependency(clearDep);
parser.unsetAsiPosition(statement.range[1]);
const asserts = getAssertionMap(statement);
const sideEffectDep = new HarmonyImportSideEffectDependency(
source,
parser.state.lastHarmonyImportOrder
parser.state.lastHarmonyImportOrder,
asserts
);
sideEffectDep.loc = statement.loc;
parser.state.module.addDependency(sideEffectDep);
Expand Down
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
1 change: 1 addition & 0 deletions lib/dependencies/ModuleDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ModuleDependency extends Dependency {
this.request = request;
this.userRequest = request;
this.range = undefined;
this.assertions = new Map();
}

/**
Expand Down
3 changes: 2 additions & 1 deletion lib/javascript/JavascriptParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"use strict";

const { Parser: AcornParser } = require("acorn");
const { importAssertions } = require("acorn-import-assertions");
const { SyncBailHook, HookMap } = require("tapable");
const vm = require("vm");
const Parser = require("../Parser");
Expand Down Expand Up @@ -61,7 +62,7 @@ const ALLOWED_MEMBER_TYPES_ALL = 0b11;

// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API

const parser = AcornParser;
const parser = AcornParser.extend(importAssertions);

class VariableInfo {
/**
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
"acorn": "^8.4.1",
"acorn-import-assertions": "^1.4.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.8.0",
Expand Down
2 changes: 1 addition & 1 deletion schemas/WebpackOptions.check.js

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions schemas/WebpackOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,18 @@
"description": "Wrap javascript code into IIFE's to avoid leaking into global scope.",
"type": "boolean"
},
"ImportAssertions": {
"description": "Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"description": "The module type.",
"type": "string",
"enum": ["json"]
}
}
},
"ImportFunctionName": {
"description": "The name of the native import() function (can be exchanged for a polyfill).",
"type": "string"
Expand Down Expand Up @@ -3585,6 +3597,15 @@
"type": "object",
"additionalProperties": false,
"properties": {
"assert": {
"description": "Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.",
"type": "object",
"oneOf": [
{
"$ref": "#/definitions/ImportAssertions"
}
]
},
"compiler": {
"description": "Match the child compiler name.",
"oneOf": [
Expand Down
6 changes: 6 additions & 0 deletions test/Defaults.unittest.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,16 @@ describe("Defaults", () => {
Object {
"test": /\\\\\\.json\\$/i,
"type": "json",
"assert": Object {
"type": "json",
},
},
Object {
"mimetype": "application/json",
"type": "json",
"assert": Object {
"type": "json",
},
},
Object {
"resolve": Object {
Expand Down
2 changes: 1 addition & 1 deletion test/Validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe("Validation", () => {
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.module.rules[0].oneOf[0] has an unknown property 'passer'. These properties are valid:
object { compiler?, dependency?, descriptionData?, enforce?, exclude?, generator?, include?, issuer?, issuerLayer?, layer?, loader?, mimetype?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceFragment?, resourceQuery?, rules?, scheme?, sideEffects?, test?, type?, use? }
object { assert?, compiler?, dependency?, descriptionData?, enforce?, exclude?, generator?, include?, issuer?, issuerLayer?, layer?, loader?, mimetype?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceFragment?, resourceQuery?, rules?, scheme?, sideEffects?, test?, type?, use? }
-> A rule description with conditions and effects for modules."
`)
);
Expand Down
1 change: 1 addition & 0 deletions test/cases/json/data/poison.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
throw new Error("imported")
5 changes: 5 additions & 0 deletions test/cases/json/import-assertions-type-json/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import c from "../data/c.json" assert { type: "json" };

it("should be possible to import json data with import assertion", function() {
expect(c[2]).toBe(3);
});
17 changes: 17 additions & 0 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4361,6 +4361,16 @@ type IgnorePluginOptions =
*/
checkResource?: (resource: string, context: string) => boolean;
};

/**
* Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.
*/
declare interface ImportAssertions {
/**
* The module type.
*/
type?: "json";
}
declare interface ImportModuleOptions {
/**
* the target layer
Expand Down Expand Up @@ -6372,6 +6382,7 @@ declare class ModuleDependency extends Dependency {
request: string;
userRequest: string;
range: any;
assertions: Map<any, any>;
static Template: typeof DependencyTemplate;
static NO_EXPORTS_REFERENCED: string[][];
static EXPORTS_OBJECT_REFERENCED: string[][];
Expand Down Expand Up @@ -8928,6 +8939,7 @@ declare interface ResolveData {
resolveOptions?: ResolveOptionsWebpackOptions;
context: string;
request: string;
assertions: Map<any, any>;
dependencies: ModuleDependency[];
createData: Object;
fileDependencies: LazySet<string>;
Expand Down Expand Up @@ -9339,6 +9351,11 @@ declare interface RuleSetLogicalConditionsAbsolute {
* A rule description with conditions and effects for modules.
*/
declare interface RuleSetRule {
/**
* Specify assertions on the imported module. If one of the assertion can't be met, the module will fail to import.
*/
assert?: ImportAssertions;

/**
* Match the child compiler name.
*/
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,11 @@ acorn-globals@^6.0.0:
acorn "^7.1.1"
acorn-walk "^7.1.1"

acorn-import-assertions@^1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.4.6.tgz#4c319605512950af5da82f4893ab8a087299fde2"
integrity sha512-wMx12i/IaRy3IyA4XzOhwgGzRRLa2Vhj30Pk99pqG2becPTV3ip/3ANM+4zrRilBs2JUTp3opRoIkxJ4f3II4g==

acorn-jsx@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
Expand Down

0 comments on commit b166f46

Please sign in to comment.