diff --git a/src/index.js b/src/index.js index 4f4b8f5..197449b 100644 --- a/src/index.js +++ b/src/index.js @@ -43,7 +43,15 @@ export default async function stylusLoader(source) { : `${options.additionalData}\n${data}`; } - const stylusOptions = getStylusOptions(this, options); + let stylusOptions; + + try { + stylusOptions = getStylusOptions(this, options); + } catch (err) { + callback(err); + return; + } + const styl = implementation(data, stylusOptions); // include regular CSS on @import @@ -79,34 +87,6 @@ export default async function stylusLoader(source) { ); } - if ( - typeof stylusOptions.use !== "undefined" && - stylusOptions.use.length > 0 - ) { - let { length } = stylusOptions.use; - - // eslint-disable-next-line no-plusplus - while (length--) { - let [item] = stylusOptions.use.splice(length, 1); - if (typeof item === "string") { - try { - const resolved = require.resolve(item); - - // eslint-disable-next-line import/no-dynamic-require, global-require - item = require(resolved)(stylusOptions); - } catch (error) { - callback( - `Failed to load "${item}" Stylus plugin. Are you sure it's installed?\n${error}`, - ); - - return; - } - } - - styl.use(item); - } - } - if (typeof stylusOptions.import !== "undefined") { for (const imported of stylusOptions.import) { styl.import(imported); diff --git a/src/utils.js b/src/utils.js index 510c733..6a09c23 100644 --- a/src/utils.js +++ b/src/utils.js @@ -36,6 +36,29 @@ function getStylusOptions(loaderContext, loaderOptions) { _imports: [], }; + if (typeof stylusOptions.use !== "undefined") { + stylusOptions.use = ( + Array.isArray(stylusOptions.use) ? stylusOptions.use : [stylusOptions.use] + ).map((item) => { + if (typeof item === "string") { + try { + const resolved = require.resolve(item); + + loaderContext.addBuildDependency(resolved); + + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(resolved)(stylusOptions); + } catch (error) { + throw new Error( + `Failed to load "${item}" Stylus plugin. Are you sure it's installed?\n${error}`, + ); + } + } + + return item; + }); + } + // https://github.com/stylus/stylus/issues/2119 stylusOptions.resolveURL = typeof stylusOptions.resolveURL === "boolean" && !stylusOptions.resolveURL diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 897db54..1e74071 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -708,7 +708,7 @@ exports[`loader should emit error when unresolved import: warnings 1`] = `[]`; exports[`loader should emit warning when use unresolved plugin: errors 1`] = ` [ "ModuleBuildError: Module build failed (from \`replaced original path\`): -NonErrorEmittedError: (Emitted value instead of an instance of Error) Failed to load "unresolved" Stylus plugin. Are you sure it's installed?", +Error: Failed to load "unresolved" Stylus plugin. Are you sure it's installed?", ] `; @@ -927,6 +927,17 @@ exports[`loader should work "prefix" option: errors 1`] = `[]`; exports[`loader should work "prefix" option: warnings 1`] = `[]`; +exports[`loader should work "use" option #1: css 1`] = ` +"body { + width: 100%; +} +" +`; + +exports[`loader should work "use" option #1: errors 1`] = `[]`; + +exports[`loader should work "use" option #1: warnings 1`] = `[]`; + exports[`loader should work "use" option as Array: css 1`] = ` "body { font: 12px Helvetica, Arial, sans-serif; @@ -943,6 +954,22 @@ exports[`loader should work "use" option as Array: errors 1`] = `[]`; exports[`loader should work "use" option as Array: warnings 1`] = `[]`; +exports[`loader should work "use" option as string: css 1`] = ` +"body { + font: 12px Helvetica, Arial, sans-serif; +} +a.button { + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +" +`; + +exports[`loader should work "use" option as string: errors 1`] = `[]`; + +exports[`loader should work "use" option as string: warnings 1`] = `[]`; + exports[`loader should work "use" option: css 1`] = ` "body { width: 100%; diff --git a/test/loader.test.js b/test/loader.test.js index 4f9805b..bfd9833 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -570,6 +570,55 @@ describe("loader", () => { expect(getErrors(stats)).toMatchSnapshot("errors"); }); + it('should work "use" option #1', async () => { + function plugin() { + return (style) => { + style.define("add", (a, b) => a.operate("+", b)); + }; + } + + const testId = "./webpack.config-plugin.styl"; + const compiler = getCompiler(testId, { + stylusOptions: { + use: plugin(), + }, + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + const codeFromStylus = await getCodeFromStylus(testId, { + stylusOptions: { + use: plugin(), + }, + }); + + expect(codeFromBundle.css).toBe(codeFromStylus.css); + expect(codeFromBundle.css).toMatchSnapshot("css"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + + it('should work "use" option as string', async () => { + const testId = "./basic.styl"; + const compiler = getCompiler(testId, { + stylusOptions: { + use: "nib", + }, + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + const codeFromStylus = await getCodeFromStylus(testId, { + stylusOptions: { + // eslint-disable-next-line global-require + use: require("nib")(), + }, + }); + + expect(codeFromBundle.css).toBe(codeFromStylus.css); + expect(codeFromBundle.css).toMatchSnapshot("css"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + it('should work "use" option as Array', async () => { const testId = "./basic.styl"; const compiler = getCompiler(testId, {