Skip to content

Commit

Permalink
only apply user plugins to loader (#111)
Browse files Browse the repository at this point in the history
* only apply user plugins to loader

The input `css` has already been processed by any plugins before `postcss-module`, and the output will be processed by any plugins after. We shoud not run all the user plugins again.

However, for anything that is loaded in by the loader (such as from `composes`), we should run the plugins that were listed before `postcss-modules`, so that the css that is brought in can catch up.

Fixes  #70

* update tests

* also test plugin isn't called multiple times

* include filename in comment

* add start and end markers

* use findIndex

* separate tests for only calling plugins once

* fix missing check
  • Loading branch information
tjenkinson committed Aug 20, 2020
1 parent 2472d47 commit d1c997f
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 6 deletions.
16 changes: 10 additions & 6 deletions src/index.js
Expand Up @@ -61,8 +61,8 @@ function getDefaultPluginsList(opts, inputFile) {
});
}

function isResultPlugin(plugin) {
return plugin.postcssPlugin !== PLUGIN_NAME;
function isOurPlugin(plugin) {
return plugin.postcssPlugin === PLUGIN_NAME;
}

function dashesCamelCase(string) {
Expand All @@ -76,13 +76,17 @@ module.exports = postcss.plugin(PLUGIN_NAME, (opts = {}) => {

return async (css, result) => {
const inputFile = css.source.input.file;
const resultPlugins = result.processor.plugins.filter(isResultPlugin);
const pluginList = getDefaultPluginsList(opts, inputFile);
const plugins = [...pluginList, ...resultPlugins];
const loader = getLoader(opts, plugins);
const resultPluginIndex = result.processor.plugins.findIndex((plugin) => isOurPlugin(plugin));
if (resultPluginIndex === -1) {
throw new Error('Plugin missing from options.');
}
const earlierPlugins = result.processor.plugins.slice(0, resultPluginIndex);
const loaderPlugins = [...pluginList, ...earlierPlugins];
const loader = getLoader(opts, loaderPlugins);
const parser = new Parser(loader.fetch.bind(loader));

await postcss([...plugins, parser.plugin]).process(css, {
await postcss([...pluginList, parser.plugin]).process(css, {
from: inputFile,
});

Expand Down
153 changes: 153 additions & 0 deletions test/__snapshots__/test.js.snap
Expand Up @@ -100,6 +100,159 @@ Object {
}
`;

exports[`only calls plugins once when it allows to make CSS global: plugins once - allows to make CSS global - CSS 1`] = `
"/* validator-2-start (global.css) */
/* validator-1-start (global.css) */
.page {
padding: 20px;
}
._global_title {
color: green;
}
/* validator-1-end (global.css) */
/* validator-2-end (global.css) */
"
`;

exports[`only calls plugins once when it composes rules: plugins once - composes rules - CSS 1`] = `
"/* validator-2-start (composes.css) *//* validator-1-start (composes.a.css) */._composes_a_another-mixin {
display: -webkit-flex;
display: -moz-box;
display: flex;
height: 100px;
width: 200px;
}._composes_a_hello {
foo: bar;
}/* validator-1-end (composes.a.css) *//* validator-1-start (composes.mixins.css) */._composes_mixins_title {
color: black;
font-size: 40px;
}._composes_mixins_title:hover {
color: red;
}._composes_mixins_figure {
text-align: center
}._composes_mixins_title:focus, ._composes_mixins_figure:focus {
outline: none;
border: 1px solid red;
}/* validator-1-end (composes.mixins.css) */
/* validator-1-start (composes.css) */
.page {
padding: 20px;
}
._composes_title {
color: green;
}
._composes_article {
font-size: 16px;
}
._composes_figure {
display: -webkit-flex;
display: -moz-box;
display: flex;
}
/* validator-1-end (composes.css) */
/* validator-2-end (composes.css) */
"
`;

exports[`only calls plugins once when it generates scoped name with interpolated string: plugins once - generates scoped name with interpolated string - CSS 1`] = `
"/* validator-2-start (interpolated.css) */
/* validator-1-start (interpolated.css) */
.interpolated__title___2P3iB {
color: green;
}
.interpolated__article___Skl0x {
color: black;
}
/* validator-1-end (interpolated.css) */
/* validator-2-end (interpolated.css) */
"
`;

exports[`only calls plugins once when it preserves comments: plugins once - preserves comments - CSS 1`] = `
"/* validator-2-start (comments.css) */
/* validator-1-start (comments.css) */
/**
* This is a doc comment...
*/
p {
/* ...and a line comment */
padding: 0;
}
/* validator-1-end (comments.css) */
/* validator-2-end (comments.css) */
"
`;

exports[`only calls plugins once when it processes classes: plugins once - processes classes - CSS 1`] = `
"/* validator-2-start (classes.css) */
/* validator-1-start (classes.css) */
.page {
padding: 20px;
}
._classes_title {
color: green;
}
._classes_article {
color: black;
}
/* validator-1-end (classes.css) */
/* validator-2-end (classes.css) */
"
`;

exports[`only calls plugins once when it processes values: plugins once - processes values - CSS 1`] = `
"/* validator-2-start (values.css) *//* validator-1-start (values.colors.css) *//* validator-1-end (values.colors.css) */
/* validator-1-start (values.css) */
._values_title {
color: green;
}
._values_article {
color: blue;
}
/* validator-1-end (values.css) */
/* validator-2-end (values.css) */
"
`;

exports[`only calls plugins once when it saves origin plugins: plugins once - saves origin plugins - CSS 1`] = `
"/* validator-2-start (plugins.css) *//* validator-1-start (plugins.css) */
._plugins_title {
display: -webkit-flex;
display: -moz-box;
display: flex;
color: green;
}/* validator-1-end (plugins.css) *//* validator-2-end (plugins.css) */
"
`;

exports[`preserves comments: preserves comments - CSS 1`] = `
"/**
* This is a doc comment...
Expand Down
43 changes: 43 additions & 0 deletions test/test.js
Expand Up @@ -55,6 +55,49 @@ Object.keys(cases).forEach((name) => {
expect(result.css).toMatchSnapshot(`${description} - CSS`);
expect(resultJson).toMatchSnapshot(`${description} - JSON`);
});

it(`only calls plugins once when it ${description}`, async () => {
const sourceFile = path.join(fixturesPath, "in", `${name}.css`);
const source = fs.readFileSync(sourceFile).toString();

const rootsSeenBeforePlugin = new Set();
const rootsSeenAfterPlugin = new Set();

const plugins = [
autoprefixer,
postcss.plugin(
'validator-1',
() => (root) => {
if (rootsSeenBeforePlugin.has(root)) {
throw new Error('Plugin before ours was called multiple times.')
}
rootsSeenBeforePlugin.add(root);
root.prepend(`/* validator-1-start (${path.basename(root.source.input.file)}) */`);
root.append(`/* validator-1-end (${path.basename(root.source.input.file)}) */`);
}
),
plugin({
scopeBehaviour,
generateScopedName: scopedNameGenerator,
getJSON: () => {},
}),
postcss.plugin(
'validator-2',
() => (root) => {
if (rootsSeenAfterPlugin.has(root)) {
throw new Error('Plugin after ours was called multiple times.')
}
rootsSeenAfterPlugin.add(root);
root.prepend(`/* validator-2-start (${path.basename(root.source.input.file)}) */`);
root.append(`/* validator-2-end (${path.basename(root.source.input.file)}) */`);
}
),
];

const result = await postcss(plugins).process(source, { from: sourceFile });

expect(result.css).toMatchSnapshot(`plugins once - ${description} - CSS`);
});
});

it("saves JSON next to CSS by default", async () => {
Expand Down

0 comments on commit d1c997f

Please sign in to comment.