Skip to content

Commit

Permalink
feat: allow multiple instances of the plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Anidetrix committed Apr 6, 2020
1 parent 39bc4aa commit aa0d4d1
Show file tree
Hide file tree
Showing 17 changed files with 159 additions and 31 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
"prefer-const": "error",
"prefer-template": "error",
"unicorn/prevent-abbreviations": "off",
yoda: ["error", "never", { exceptRange: true }],
},
settings: { "import/resolver": { node: { extensions: [".js", ".ts"] } } },
};
81 changes: 79 additions & 2 deletions __tests__/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,7 @@ var injector_14187a73 = (css, options = {}) => {
const valid = \\"style_valid\\";
const _new = \\"style_new\\";
var css_2f84417a = \\".style_valid {\\\\n color: red;\\\\n}\\\\n\\\\n.style_new {\\\\n color: blue;\\\\n}\\\\n\\";
const stylesheet = \\".style_valid {\\\\n color: red;\\\\n}\\\\n\\\\n.style_new {\\\\n color: blue;\\\\n}\\\\n\\";
const stylesheet = css_2f84417a;
var style = {\\"valid\\":\\"style_valid\\",\\"new\\":\\"style_new\\",\\"_new\\":\\"style_new\\"};
injector_14187a73(css_2f84417a);
Expand Down Expand Up @@ -1370,7 +1370,7 @@ var injector_14187a73 = (css, options = {}) => {
const validhacked = \\"style_valid\\";
const newhacked = \\"style_new\\";
var css_2f84417a = \\".style_valid {\\\\n color: red;\\\\n}\\\\n\\\\n.style_new {\\\\n color: blue;\\\\n}\\\\n\\";
const stylesheet = \\".style_valid {\\\\n color: red;\\\\n}\\\\n\\\\n.style_new {\\\\n color: blue;\\\\n}\\\\n\\";
const stylesheet = css_2f84417a;
var style = {\\"valid\\":\\"style_valid\\",\\"new\\":\\"style_new\\",\\"validhacked\\":\\"style_valid\\",\\"newhacked\\":\\"style_new\\"};
injector_14187a73(css_2f84417a);
Expand All @@ -1386,6 +1386,83 @@ console.log(style$1);
"
`;

exports[`multiple-instances: js 1`] = `
"/* eslint-env browser */
/** @type {HTMLElement[]} */
const containers = [];
/** @type {{prepend:HTMLStyleElement,append:HTMLStyleElement}[]} */
const styleTags = [];
/**
* @param {string|undefined} css
* @param {object} [options={}]
* @param {boolean} [options.prepend]
* @param {boolean} [options.singleTag]
* @param {HTMLElement} [options.container]
* @returns {void}
*/
var injector_14187a73 = (css, options = {}) => {
if (!css || typeof document === \\"undefined\\") return;
const singleTag = typeof options.singleTag !== \\"undefined\\" ? options.singleTag : false;
const container = typeof options.container !== \\"undefined\\" ? options.container : document.head;
const position = options.prepend === true ? \\"prepend\\" : \\"append\\";
const createStyleTag = () => {
const styleTag = document.createElement(\\"style\\");
styleTag.type = \\"text/css\\";
if (position === \\"prepend\\" && container.firstChild) {
container.insertBefore(styleTag, container.firstChild);
} else {
container.append(styleTag);
}
return styleTag;
};
/** @type {HTMLStyleElement} */
let styleTag;
if (singleTag) {
let id = containers.indexOf(container);
if (id === -1) {
id = containers.push(container) - 1;
styleTags[id] = {};
}
if (styleTags[id] && styleTags[id][position]) {
styleTag = styleTags[id][position];
} else {
styleTag = styleTags[id][position] = createStyleTag();
}
} else {
styleTag = createStyleTag();
}
// strip potential UTF-8 BOM if css was read from a file
if (css.charCodeAt(0) === 0xfeff) css = css.slice(1);
if (styleTag.styleSheet) {
styleTag.styleSheet.cssText += css;
} else {
styleTag.textContent += css;
}
};
var css_2f84417a = \\".style {\\\\n color: red;\\\\n}\\\\n\\";
injector_14187a73(css_2f84417a);
var css_2f84417a$1 = \\".less {\\\\n color: magenta;\\\\n}\\\\n\\";
injector_14187a73(css_2f84417a$1);
var css_2f84417a$2 = \\".mod_modular {\\\\n color: royalblue;\\\\n}\\\\n\\";
var mod = {\\"modular\\":\\"mod_modular\\",\\"modularalt\\":\\"mod_modular\\"};
injector_14187a73(css_2f84417a$2);
console.log(mod);
"
`;

exports[`on-extract-fn 1`] = `
"'use strict';
Expand Down
5 changes: 5 additions & 0 deletions __tests__/fixtures/multiple-instances/bar.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@color: magenta;

.less {
color: @color;
}
3 changes: 3 additions & 0 deletions __tests__/fixtures/multiple-instances/foo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.style {
color: red;
}
4 changes: 4 additions & 0 deletions __tests__/fixtures/multiple-instances/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "./foo.css";
import "./bar.less";
import mod from "./mod.mcss";
console.log(mod);
3 changes: 3 additions & 0 deletions __tests__/fixtures/multiple-instances/mod.mcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.modular {
color: royalblue;
}
4 changes: 2 additions & 2 deletions __tests__/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from "fs-extra";
import { rollup } from "rollup";
import commonjs from "@rollup/plugin-commonjs";

import postcss from "../../src";
import styles from "../../src";
import { Options } from "../../src/types";

export interface WriteData {
Expand Down Expand Up @@ -34,7 +34,7 @@ export async function write(data: WriteData): Promise<TestData> {

const bundle = await rollup({
input: fixture(data.input),
plugins: [commonjs(), postcss(data.options)],
plugins: [commonjs(), styles(data.options)],
});

await bundle.write({
Expand Down
40 changes: 31 additions & 9 deletions __tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import path from "path";
import fs from "fs-extra";
import { rollup } from "rollup";

import postcss from "../src";
import styles from "../src";

import { fixture, validateMany, write } from "./helpers";
import { humanlizePath } from "../src/utils/path-utils";

beforeAll(() => fs.remove(fixture("dist")), 30000);
beforeAll(() => fs.remove(fixture("dist")));

validateMany("basic", [
{
Expand Down Expand Up @@ -113,9 +114,7 @@ validateMany("modules", [
input: "named-exports/index.js",
options: {
modules: true,
namedExports: (name): string => {
return `${name}hacked`;
},
namedExports: (name): string => `${name}hacked`,
},
},
{
Expand Down Expand Up @@ -273,17 +272,17 @@ test("on-extract-fn", async () => {
expect(await res.js()).toMatchSnapshot();
expect(await res.isCss()).toBeFalsy();
expect(await res.isMap()).toBeFalsy();
}, 30000);
});

test("augment-chunk-hash", async () => {
const outDir = fixture("dist", "augment-chunk-hash");
const cssFiles = ["simple/foo.css", "simple/foo.css", "simple/bar.css"];

const outputFiles: string[] = [];
for (const file of cssFiles) {
for await (const file of cssFiles) {
const bundle = await rollup({
input: fixture(file),
plugins: [postcss({ extract: true, sourceMap: "inline" })],
plugins: [styles({ extract: true, sourceMap: "inline" })],
});
const entryFileName = file.split(".")[0];
const { output } = await bundle.write({
Expand Down Expand Up @@ -311,4 +310,27 @@ test("augment-chunk-hash", async () => {
// Verify that foo and bar does not hash to the same
expect(barHash).not.toEqual(fooOneHash);
expect(barHash).not.toEqual(fooTwoHash);
}, 30000);
});

test("multiple-instances", async () => {
const bundle = await rollup({
input: fixture("multiple-instances/index.js"),
plugins: [
styles({ extensions: [".css"], use: [] }),
styles({ extensions: [], use: ["less"] }),
styles({
extensions: [".mcss"],
use: [],
modules: true,
namedExports: name => `${name}alt`,
}),
],
});

const outDir = fixture("dist", "multiple-instances");
const { output } = await bundle.write({ dir: outDir });
const outfile = path.join(outDir, output[0].fileName);

await expect(fs.pathExists(outfile)).resolves.toBeTruthy();
await expect(fs.readFile(outfile, "utf8")).resolves.toMatchSnapshot("js");
});
3 changes: 3 additions & 0 deletions __tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// eslint-disable-next-line node/no-extraneous-import
import "expect-puppeteer";
jest.setTimeout(30000);
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ module.exports = {
transform: { "^.+\\.jsx?$": "babel-jest", "^.+\\.tsx?$": "ts-jest" },
globals: { "ts-jest": { packageJson: "package.json" } },
testMatch: ["<rootDir>/__tests__/**/*.(spec|test).[jt]s?(x)"],
setupFilesAfterEnv: ["expect-puppeteer"],
setupFilesAfterEnv: ["<rootDir>/__tests__/setup.ts"],
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"postcss-custom-properties": "^9.1.1",
"postcss-import": "^12.0.1",
"postcss-url": "^8.0.0",
"prettier": "^2.0.3",
"prettier": "^2.0.4",
"puppeteer": "^2.1.1",
"rollup": "^2.3.3",
"rollup-plugin-auto-external": "^2.0.0",
Expand Down
1 change: 1 addition & 0 deletions src/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
"prefer-const": "error",
"prefer-template": "error",
"unicorn/prevent-abbreviations": "off",
yoda: ["error", "never", { exceptRange: true }],
},
settings: { "import/resolver": { node: { extensions: [".js", ".ts"] } } },
};
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,20 @@ export default (options: Options = {}): Plugin => {
const extracted = new Map<string, NonNullable<Payload["extracted"]>>();

const plugin: Plugin = {
name: "postcss",
name: "styles",

async transform(code, id) {
if (!filter(id) || !loaders.isSupported(id)) return null;

// Check if file was already processed into JS
// by other instance(s) of this or other plugin(s)
try {
this.parse(code, {});
return null; // Was already processed, skipping
} catch {
// Was not already processed, continuing
}

if (typeof options.onImport === "function") options.onImport(code, id);

const loaderContext: LoaderContext = {
Expand Down
9 changes: 5 additions & 4 deletions src/loaders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const workQueue = new PQueue({ concurrency: threadPoolSize - 1 });
export default class Loaders {
use: [string, ObjectWithUnknownProps][] = [];
loaders: Loader[] = [];
test: (filepath: string) => boolean;

constructor(options: LoadersOptions) {
this.use = options.use.map(rule => {
Expand All @@ -36,7 +37,7 @@ export default class Loaders {
throw new TypeError("The rule in `use` option must be string or array!");
});

postcssLoader.test = (filepath): boolean =>
this.test = (filepath: string): boolean =>
options.extensions.some(ext => filepath.toLowerCase().endsWith(ext));

this.listLoader(postcssLoader);
Expand All @@ -52,8 +53,8 @@ export default class Loaders {
}

listLoader<T extends ObjectWithUnknownProps>(loader: Loader<T>): void {
const existing = this.getLoader(loader.name);
if (existing) this.unlistLoader(loader.name);
if (!this.use.some(rule => rule[0] === loader.name)) return;
if (this.getLoader(loader.name)) this.unlistLoader(loader.name);
this.loaders.push(loader as Loader);
}

Expand All @@ -62,7 +63,7 @@ export default class Loaders {
}

isSupported(filepath: string): boolean {
return this.loaders.some(loader => matchFile(filepath, loader.test));
return this.test(filepath) || this.loaders.some(loader => matchFile(filepath, loader.test));
}

process(payload: Payload, context: LoaderContext): Promise<Payload> {
Expand Down
8 changes: 2 additions & 6 deletions src/loaders/postcss/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,12 @@ function isModuleFile(file: string): boolean {
const loader: Loader<PostCSSLoaderOptions> = {
name: "postcss",
alwaysProcess: true,
// `test` option is dynamically set in `Loaders` class
async process({ code, map, extracted }) {
const { options } = this;

const config = await loadConfig(this.id, options.config);

const plugins = [
...((options.postcss && options.postcss.plugins) || []),
...(config.plugins || []),
];
const plugins = [...(options.postcss.plugins || []), ...(config.plugins || [])];

const autoModules = options.autoModules && isModuleFile(this.id);
const supportModules = Boolean(options.modules || autoModules);
Expand Down Expand Up @@ -174,7 +170,7 @@ const loader: Loader<PostCSSLoaderOptions> = {
const defaultExport = supportModules ? JSON.stringify(modulesExports[this.id]) : cssVarName;
output += `\n${[
`var ${cssVarName} = ${JSON.stringify(res.css)};`,
`export const stylesheet = ${JSON.stringify(res.css)};`,
`export const stylesheet = ${cssVarName};`,
`export default ${defaultExport};`,
].join("\n")}`;
}
Expand Down
5 changes: 4 additions & 1 deletion src/utils/options-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export function ensurePCSSPlugins(plugins: Options["plugins"]): postcss.Transfor
if (!Array.isArray(plugins)) return plugins;
return plugins.filter(bool).map(p => {
if (!Array.isArray(p)) return ensurePCSSOption(p);
return ensurePCSSOption<postcss.PluginInitializer<unknown>>(p[0])(p[1]);

const [plug, opts] = p;
if (!opts) return ensurePCSSOption<postcss.Transformer>(plug);
return ensurePCSSOption<postcss.PluginInitializer<unknown>>(plug)(opts);
});
}
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6917,10 +6917,10 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=

prettier@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.3.tgz#9a06f0e94a51420e78b6925568b5bec72afe41ea"
integrity sha512-5qpBDBHO9fpE0zruKiTZm8Gxmz7kknO+WlQR/ivV+RMwgDw/WjOgmxLDn66MPrxq/WZPx/EgEZzh87xJO5E6Fw==
prettier@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.4.tgz#2d1bae173e355996ee355ec9830a7a1ee05457ef"
integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==

pretty-format@^25.2.1, pretty-format@^25.2.6:
version "25.2.6"
Expand Down

0 comments on commit aa0d4d1

Please sign in to comment.