Skip to content

Commit

Permalink
feat: support ECMA modules for the executableFile option (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Mar 1, 2021
1 parent b46090f commit 1e6675f
Show file tree
Hide file tree
Showing 13 changed files with 10,013 additions and 10,101 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Thumbs.db
*.sublime-project
*.sublime-workspace

/jest*
/dist
/local
/reports
Expand Down
2 changes: 1 addition & 1 deletion .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
_
_
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
transformIgnorePatterns: [
"/node_modules/",
"\\.pnp\\.[^\\/]+$",
"/test/fixtures/",
],
};
19,881 changes: 9,813 additions & 10,068 deletions package-lock.json

Large diffs are not rendered by default.

27 changes: 12 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@
"lint:prettier": "prettier --list-different .",
"lint:js": "eslint --cache .",
"lint": "npm-run-all -l -p \"lint:**\"",
"test:only": "cross-env NODE_ENV=test jest",
"test:only": "cross-env NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest",
"test:watch": "npm run test:only -- --watch",
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
"pretest": "npm run lint",
"test": "npm run test:coverage",
"prepare": "npm run build",
"release": "standard-version",
"defaults": "webpack-defaults",
"postinstall": "husky install",
"prepublishOnly": "pinst --disable",
"postpublish": "pinst --enable"
Expand All @@ -44,31 +43,29 @@
"webpack": "^5.0.0"
},
"devDependencies": {
"@babel/cli": "^7.12.16",
"@babel/core": "^7.12.16",
"@babel/preset-env": "^7.12.16",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@webpack-contrib/defaults": "^6.3.0",
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.8",
"@babel/preset-env": "^7.13.8",
"@commitlint/cli": "^12.0.1",
"@commitlint/config-conventional": "^12.0.1",
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
"babel-jest": "^26.6.3",
"cross-env": "^7.0.3",
"del": "^6.0.0",
"del-cli": "^3.0.1",
"eslint": "^7.20.0",
"eslint-config-prettier": "^7.2.0",
"eslint": "^7.21.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"figlet": "^1.5.0",
"husky": "^5.0.9",
"husky": "^5.1.2",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"memfs": "^3.2.0",
"modernizr": "^3.11.4",
"npm-run-all": "^4.1.5",
"pinst": "^2.1.4",
"pinst": "^2.1.6",
"prettier": "^2.2.1",
"standard-version": "^9.1.0",
"webpack": "^5.22.0"
"standard-version": "^9.1.1",
"webpack": "^5.24.2"
},
"keywords": [
"webpack",
Expand Down
48 changes: 36 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import Module from "module";
import { pathToFileURL } from "url";

import schema from "./options.json";

const parentModule = module;

function exec(code, loaderContext) {
const { resource, context } = loaderContext;

const module = new Module(resource, parentModule);
function execute(code, loaderContext) {
const module = new Module(loaderContext.resource, parentModule);

// eslint-disable-next-line no-underscore-dangle
module.paths = Module._nodeModulePaths(context);
module.filename = resource;
module.paths = Module._nodeModulePaths(loaderContext.context);
module.filename = loaderContext.resource;

// eslint-disable-next-line no-underscore-dangle
module._compile(code, resource);
module._compile(code, loaderContext.resource);

return module.exports;
}
Expand Down Expand Up @@ -67,7 +66,7 @@ function processResult(loaderContext, result) {
);
}

export default function loader(content) {
export default async function loader(content) {
const options = this.getOptions(schema);
const { executableFile } = options;
const callback = this.async();
Expand All @@ -78,15 +77,40 @@ export default function loader(content) {
try {
// eslint-disable-next-line global-require,import/no-dynamic-require
exports = require(executableFile);
} catch (error) {
callback(new Error(`Unable to require "${executableFile}": ${error}`));
return;
} catch (requireError) {
try {
let importESM;

try {
// eslint-disable-next-line no-new-func
importESM = new Function("id", "return import(id);");
} catch (e) {
importESM = null;
}

if (
requireError.code === "ERR_REQUIRE_ESM" &&
pathToFileURL &&
importESM
) {
const urlForConfig = pathToFileURL(executableFile);

exports = await importESM(urlForConfig);
} else {
throw requireError;
}
} catch (error) {
callback(new Error(`Unable to require "${executableFile}": ${error}`));

return;
}
}
} else {
try {
exports = exec(content, this);
exports = execute(content, this);
} catch (error) {
callback(new Error(`Unable to execute "${this.resource}": ${error}`));

return;
}
}
Expand Down
6 changes: 3 additions & 3 deletions test/__snapshots__/executableFile.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Error: Unable to require \\"/test/fixtures/error-require.js\\": Error: This is a

exports[`executableFile option should emit error: warnings 1`] = `Array []`;

exports[`executableFile option should work: errors 1`] = `Array []`;
exports[`executableFile option should work with commonjs format: errors 1`] = `Array []`;

exports[`executableFile option should work: result 1`] = `
exports[`executableFile option should work with commonjs format: result 1`] = `
"{
\\"content\\": \\"module.exports = 315360000000\\",
\\"map\\": null,
Expand All @@ -25,4 +25,4 @@ exports[`executableFile option should work: result 1`] = `
}"
`;

exports[`executableFile option should work: warnings 1`] = `Array []`;
exports[`executableFile option should work with commonjs format: warnings 1`] = `Array []`;
42 changes: 42 additions & 0 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,27 @@ exports[`loader should work the same if a promise is returned: result 1`] = `

exports[`loader should work the same if a promise is returned: warnings 1`] = `Array []`;

exports[`loader should work with ES modules code: errors 1`] = `Array []`;

exports[`loader should work with ES modules code: result 1`] = `
"{
\\"content\\": \\"module.exports = \\\\\\"hello world\\\\\\";\\",
\\"map\\": {
\\"isASourceMap\\": true
},
\\"meta\\": {
\\"isAnAst\\": true
},
\\"dependencies\\": [
\\"test/fixtures/code-commonjs.js\\"
],
\\"contextDependencies\\": [],
\\"buildDependencies\\": []
}"
`;

exports[`loader should work with ES modules code: warnings 1`] = `Array []`;

exports[`loader should work with async function: errors 1`] = `Array []`;

exports[`loader should work with async function: result 1`] = `
Expand All @@ -408,3 +429,24 @@ exports[`loader should work with async function: result 1`] = `
`;

exports[`loader should work with async function: warnings 1`] = `Array []`;

exports[`loader should work with commonjs code: errors 1`] = `Array []`;

exports[`loader should work with commonjs code: result 1`] = `
"{
\\"content\\": \\"export default \\\\\\"hello world\\\\\\";\\",
\\"map\\": {
\\"isASourceMap\\": true
},
\\"meta\\": {
\\"isAnAst\\": true
},
\\"dependencies\\": [
\\"test/fixtures/code-es.js\\"
],
\\"contextDependencies\\": [],
\\"buildDependencies\\": []
}"
`;

exports[`loader should work with commonjs code: warnings 1`] = `Array []`;
47 changes: 46 additions & 1 deletion test/executableFile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from "path";
import { getCompiler, compile, readAsset, normalizeErrors } from "./helpers";

describe("executableFile option", () => {
it("should work", async () => {
it("should work with commonjs format", async () => {
const compiler = getCompiler(
"executableFileEntry.js",
{},
Expand Down Expand Up @@ -47,6 +47,51 @@ describe("executableFile option", () => {
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors");
});

// TODO jest have not good support for ES modules for testing it, tested manually
it.skip("should work with ES modules format", async () => {
const compiler = getCompiler(
"executableFileEntry.js",
{},
{
module: {
rules: [
{
test: /\.(json)$/i,
rules: [
{
loader: require.resolve("./helpers/helperLoader.js"),
},
{
loader: require.resolve("../src"),
options: {
executableFile: path.resolve(
__dirname,
"fixtures",
"executableFileES.mjs"
),
},
},
],
},
{
test: /\.json$/i,
type: "asset/resource",
},
],
},
}
);
const stats = await compile(compiler);

expect(readAsset("val-loader.js", compiler, stats)).toMatchSnapshot(
"result"
);
expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot(
"warnings"
);
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors");
});

it("should emit error", async () => {
const compiler = getCompiler(
"executableFileEntry.js",
Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/code-commonjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function codeES() {
return {
code: 'module.exports = "hello world";',
sourceMap: { isASourceMap: true },
ast: { isAnAst: true },
};
}

module.exports = codeES;
9 changes: 9 additions & 0 deletions test/fixtures/code-es.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function codeES() {
return {
code: 'export default "hello world";',
sourceMap: { isASourceMap: true },
ast: { isAnAst: true },
};
}

module.exports = codeES;
9 changes: 9 additions & 0 deletions test/fixtures/executableFileES.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function yearsInMs(options, loaderContext, content) {
const {years} = JSON.parse(content);
const value = years * 365 * 24 * 60 * 60 * 1000;

return {
cacheable: true,
code: "export default " + value,
};
};
26 changes: 26 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,30 @@ describe("loader", () => {
);
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors");
});

it("should work with commonjs code", async () => {
const compiler = getCompiler("code-es.js");
const stats = await compile(compiler);

expect(readAsset("val-loader.js", compiler, stats)).toMatchSnapshot(
"result"
);
expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot(
"warnings"
);
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors");
});

it("should work with ES modules code", async () => {
const compiler = getCompiler("code-commonjs.js");
const stats = await compile(compiler);

expect(readAsset("val-loader.js", compiler, stats)).toMatchSnapshot(
"result"
);
expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot(
"warnings"
);
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors");
});
});

0 comments on commit 1e6675f

Please sign in to comment.