Skip to content

Commit

Permalink
feat: support ecma modules for the 'parser', 'stringifier' and 'syn…
Browse files Browse the repository at this point in the history
…tax' options (#519)
  • Loading branch information
cap-Bernardito committed Mar 11, 2021
1 parent 55630b8 commit cc69754
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default async function loader(content, sourceMap, meta) {
? options.sourceMap
: this.sourceMap;

const { plugins, processOptions } = getPostcssOptions(
const { plugins, processOptions } = await getPostcssOptions(
this,
loadedConfig,
options.postcssOptions
Expand Down
39 changes: 32 additions & 7 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,35 @@ function pluginFactory() {
};
}

function getPostcssOptions(
async function load(module) {
let exports;

try {
// eslint-disable-next-line import/no-dynamic-require, global-require
exports = require(module);

return exports;
} catch (requireError) {
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" && importESM) {
exports = await importESM(module);

return exports.default;
}

throw requireError;
}
}

async function getPostcssOptions(
loaderContext,
loadedConfig = {},
postcssOptions = {}
Expand Down Expand Up @@ -253,8 +281,7 @@ function getPostcssOptions(

if (typeof processOptions.parser === "string") {
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
processOptions.parser = require(processOptions.parser);
processOptions.parser = await load(processOptions.parser);
} catch (error) {
loaderContext.emitError(
new Error(
Expand All @@ -266,8 +293,7 @@ function getPostcssOptions(

if (typeof processOptions.stringifier === "string") {
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
processOptions.stringifier = require(processOptions.stringifier);
processOptions.stringifier = await load(processOptions.stringifier);
} catch (error) {
loaderContext.emitError(
new Error(
Expand All @@ -279,8 +305,7 @@ function getPostcssOptions(

if (typeof processOptions.syntax === "string") {
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
processOptions.syntax = require(processOptions.syntax);
processOptions.syntax = await load(processOptions.syntax);
} catch (error) {
loaderContext.emitError(
new Error(
Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/esparser/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function stringify() {
return "";
}

function parse() {
return "";
}

export default { stringify, parse };
11 changes: 11 additions & 0 deletions test/fixtures/esparser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "module",
"exports": {
".": {
"require": "./index.js",
"import": "./index.mjs"
}
},
"name": "test",
"version": "1.0.0"
}
46 changes: 46 additions & 0 deletions test/fixtures/esparser/runManual.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import webpack from 'webpack';
import path from "path";

const __dirname = path.resolve();
const rootDir = path.resolve(__dirname, "test/helpers");

const compiler = webpack({
target: 'node',
mode: "development",
devtool: false,
context: path.resolve(rootDir, "../fixtures"),
entry: path.resolve(rootDir, "../fixtures", "./sss/index.js"),
output: {
path: path.resolve(rootDir, "../outputs"),
filename: "[name].bundle.js",
chunkFilename: "[name].chunk.js",
publicPath: "/webpack/public/path/",
},
module: {
rules: [
{
test: /\.(css|sss)$/i,
use: [
'css-loader',
{
loader: path.resolve(rootDir, "../../dist"),
options: {
postcssOptions: {
parser: path.resolve(rootDir, "../fixtures/esparser/index.mjs"),
stringifier: path.resolve(rootDir, "../fixtures/esparser/index.mjs"),
syntax: path.resolve(rootDir, "../fixtures/esparser/index.mjs"),
},
},
},
],
},
],
},
plugins: [],
});

compiler.run((error) => {
if (error) {
throw error;
}
});
48 changes: 48 additions & 0 deletions test/postcssOptins.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,54 @@ describe('"postcssOptions" option', () => {
expect(getErrors(stats)).toMatchSnapshot("errors");
});

// TODO jest have not good support for ES modules for testing it, tested manually
it.skip('should work with the "parser" option with "Object" value with ESM', async () => {
const compiler = getCompiler("./sss/index.js", {
postcssOptions: {
parser: path.resolve(__dirname, "../fixtures/esparser/index.mjs"),
},
});
const stats = await compile(compiler);

const codeFromBundle = getCodeFromBundle("style.sss", stats);

expect(codeFromBundle.css).toMatchSnapshot("css");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

// TODO jest have not good support for ES modules for testing it, tested manually
it.skip('should work with the "stringifier" option with "Object" value with ESM', async () => {
const compiler = getCompiler("./sss/index.js", {
postcssOptions: {
stringifier: path.resolve(__dirname, "../fixtures/esparser/index.mjs"),
},
});
const stats = await compile(compiler);

const codeFromBundle = getCodeFromBundle("style.sss", stats);

expect(codeFromBundle.css).toMatchSnapshot("css");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

// TODO jest have not good support for ES modules for testing it, tested manually
it.skip('should work with the "syntax" option with "Object" value with ESM', async () => {
const compiler = getCompiler("./sss/index.js", {
postcssOptions: {
syntax: path.resolve(__dirname, "../fixtures/esparser/index.mjs"),
},
});
const stats = await compile(compiler);

const codeFromBundle = getCodeFromBundle("style.sss", stats);

expect(codeFromBundle.css).toMatchSnapshot("css");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

it('should work with the "parser" option with "Function" value', async () => {
const compiler = getCompiler("./sss/index.js", {
postcssOptions: {
Expand Down

0 comments on commit cc69754

Please sign in to comment.