Repository: tivac/modular-css
Pull request #667 (2019-09-11 06:53:30)

feat(rollup-rewriter): let loader option be a function
<!--- Provide a general summary of your changes in the Title above -->

## Description
Let the `loader` option be either a string or a function.

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
Allows for picking a file out of the bundle to be used, even if the output name is hashed by rollup.

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
Added tests.

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.



Patch:
diff --git a/package-lock.json b/package-lock.json
index b6097e49..9a7979dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5095,9 +5095,9 @@
       }
     },
     "eslint-plugin-jest": {
-      "version": "22.15.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.15.1.tgz",
-      "integrity": "sha512-CWq/RR/3tLaKFB+FZcCJwU9hH5q/bKeO3rFP8G07+q7hcDCFNqpvdphVbEbGE6o6qo1UbciEev4ejUWv7brUhw==",
+      "version": "22.16.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.16.0.tgz",
+      "integrity": "sha512-eBtSCDhO1k7g3sULX/fuRK+upFQ7s548rrBtxDyM1fSoY7dTWp/wICjrJcDZKVsW7tsFfH22SG+ZaxG5BZodIg==",
       "dev": true,
       "requires": {
         "@typescript-eslint/experimental-utils": "^1.13.0"
diff --git a/packages/rollup-rewriter/README.md b/packages/rollup-rewriter/README.md
index c3148f8a..336a26a1 100644
--- a/packages/rollup-rewriter/README.md
+++ b/packages/rollup-rewriter/README.md
@@ -84,9 +84,9 @@ export default {
 
 ## Options
 
-### `loader` (string)
+### `loader` (string|function)
 
-The `loader` option can be set if you want the plugin to inject a reference to a CSS loader that returns a promise (I like [`lazyload-css`](https://npmjs.com/lazyload-css)). This loader must be available statically, so this option is most useful in `es`/`esm` mode where it can be loaded via `import`.
+The `loader` option can be set if you want the plugin to inject a reference to a CSS loader that returns a promise (I like [`lazyload-css`](https://npmjs.com/lazyload-css)). This loader must be available statically, so this option is most useful in `es`/`esm` mode where it can be loaded via `import`. If given a function instead of a string that function will be called once per chunk being modified, it gets passed a single argument of the form `{ chunks }` where `chunks` is the raw rollup output chunks.
 
 ### `loadfn` (string)
 
diff --git a/packages/rollup-rewriter/formats/amd.js b/packages/rollup-rewriter/formats/amd.js
index 36ffbd8f..61248460 100644
--- a/packages/rollup-rewriter/formats/amd.js
+++ b/packages/rollup-rewriter/formats/amd.js
@@ -17,7 +17,7 @@ exports.regex = (deps) => {
     );
 };
 
-exports.loader = (options, str) => {
+exports.loader = ({ content, str }) => {
     const s = str.toString();
     const i = s.indexOf(search);
 
@@ -28,7 +28,7 @@ exports.loader = (options, str) => {
     // + 1 is for the newline...
     str.appendRight(
         i + search.length + 1,
-        `${options.loader}\n`
+        `${content}\n`
     );
 };
 
diff --git a/packages/rollup-rewriter/formats/es.js b/packages/rollup-rewriter/formats/es.js
index 390edc8a..d2283479 100644
--- a/packages/rollup-rewriter/formats/es.js
+++ b/packages/rollup-rewriter/formats/es.js
@@ -11,7 +11,7 @@ exports.regex = (deps) => {
     );
 };
 
-exports.loader = (options, str) => str.prepend(`${options.loader}\n`);
+exports.loader = ({ content, str }) => str.prepend(`${content}\n`);
 
 exports.load = (options, imports, statement) => `
     Promise.all([
diff --git a/packages/rollup-rewriter/formats/system.js b/packages/rollup-rewriter/formats/system.js
index a3b2e597..544495d9 100644
--- a/packages/rollup-rewriter/formats/system.js
+++ b/packages/rollup-rewriter/formats/system.js
@@ -13,7 +13,7 @@ exports.regex = (deps) => {
     );
 };
 
-exports.loader = (options, str) => {
+exports.loader = ({ content, str }) => {
     const s = str.toString();
     const i = s.indexOf(search);
 
@@ -24,7 +24,7 @@ exports.loader = (options, str) => {
     // + 1 is for the newline...
     str.appendRight(
         i + search.length + 1,
-        `${options.loader}\n`
+        `${content}\n`
     );
 };
 
diff --git a/packages/rollup-rewriter/rewriter.js b/packages/rollup-rewriter/rewriter.js
index 155afe2c..3e0a95c7 100644
--- a/packages/rollup-rewriter/rewriter.js
+++ b/packages/rollup-rewriter/rewriter.js
@@ -75,7 +75,11 @@ module.exports = (opts) => {
                 const str = new MagicString(code);
 
                 if(options.loader) {
-                    loader(options, str);
+                    const content = typeof options.loader === "function" ?
+                        options.loader({ chunks, options }) :
+                        options.loader;
+
+                    loader({ content, str });
                 }
 
                 // Yay stateful regexes


Test patch:
diff --git a/packages/rollup-rewriter/test/__snapshots__/rewriter.test.js.snap b/packages/rollup-rewriter/test/__snapshots__/rewriter.test.js.snap
index 225a2d17..ae6a96e4 100644
--- a/packages/rollup-rewriter/test/__snapshots__/rewriter.test.js.snap
+++ b/packages/rollup-rewriter/test/__snapshots__/rewriter.test.js.snap
@@ -1031,6 +1031,294 @@ System.register([], function (exports) {
 }
 `;
 
+exports[`rollup-rewriter should support loader being a function ("amd") 1`] = `
+Object {
+  "a": "
+define(['require'], function (require) { 'use strict';
+import chunkCountIs8 from \\"./css.js\\";
+
+    var css = {
+        \\"a\\": \\"a\\"
+    };
+
+    function a() {
+        console.log(css);
+
+        new Promise(function (resolve, reject) { Promise.all([
+    lazyload(\\"./assets/c.css\\"),
+    new Promise(function (resolve, reject) { require(['./c'], resolve, reject) })
+])
+.then((results) => resolve(results[results.length - 1]))
+.catch(reject) }).then(console.log);
+    }
+
+    return a;
+
+});
+",
+  "b": "
+define(['require'], function (require) { 'use strict';
+import chunkCountIs8 from \\"./css.js\\";
+
+    var css = {
+        \\"b\\": \\"b\\"
+    };
+
+    function b() {
+        console.log(css);
+
+        new Promise(function (resolve, reject) { Promise.all([
+    lazyload(\\"./assets/d.css\\"),
+    new Promise(function (resolve, reject) { require(['./d'], resolve, reject) })
+])
+.then((results) => resolve(results[results.length - 1]))
+.catch(reject) }).then(console.log);
+    }
+
+    return b;
+
+});
+",
+  "c": "
+define(['exports'], function (exports) { 'use strict';
+
+    var css = {
+        \\"c\\": \\"c\\"
+    };
+
+    console.log(css);
+
+    exports.default = css;
+
+});
+",
+  "d": "
+define(['exports'], function (exports) { 'use strict';
+
+    var css = {
+        \\"d\\": \\"d\\"
+    };
+
+    console.log(css);
+
+    exports.default = css;
+
+});
+",
+}
+`;
+
+exports[`rollup-rewriter should support loader being a function ("es") 1`] = `
+Object {
+  "a": "
+import chunkCountIs8 from \\"./css.js\\";
+var css = {
+    \\"a\\": \\"a\\"
+};
+
+function a() {
+    console.log(css);
+
+    Promise.all([
+    lazyload(\\"./assets/c.css\\"),
+    import('./c.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+}
+
+export default a;
+",
+  "b": "
+import chunkCountIs8 from \\"./css.js\\";
+var css = {
+    \\"b\\": \\"b\\"
+};
+
+function b() {
+    console.log(css);
+
+    Promise.all([
+    lazyload(\\"./assets/d.css\\"),
+    import('./d.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+}
+
+export default b;
+",
+  "c": "
+var css = {
+    \\"c\\": \\"c\\"
+};
+
+console.log(css);
+
+export default css;
+",
+  "d": "
+var css = {
+    \\"d\\": \\"d\\"
+};
+
+console.log(css);
+
+export default css;
+",
+}
+`;
+
+exports[`rollup-rewriter should support loader being a function ("esm") 1`] = `
+Object {
+  "a": "
+import chunkCountIs8 from \\"./css.js\\";
+var css = {
+    \\"a\\": \\"a\\"
+};
+
+function a() {
+    console.log(css);
+
+    Promise.all([
+    lazyload(\\"./assets/c.css\\"),
+    import('./c.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+}
+
+export default a;
+",
+  "b": "
+import chunkCountIs8 from \\"./css.js\\";
+var css = {
+    \\"b\\": \\"b\\"
+};
+
+function b() {
+    console.log(css);
+
+    Promise.all([
+    lazyload(\\"./assets/d.css\\"),
+    import('./d.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+}
+
+export default b;
+",
+  "c": "
+var css = {
+    \\"c\\": \\"c\\"
+};
+
+console.log(css);
+
+export default css;
+",
+  "d": "
+var css = {
+    \\"d\\": \\"d\\"
+};
+
+console.log(css);
+
+export default css;
+",
+}
+`;
+
+exports[`rollup-rewriter should support loader being a function ("system") 1`] = `
+Object {
+  "a": "
+System.register([], function (exports, module) {
+    'use strict';
+import chunkCountIs8 from \\"./css.js\\";
+    return {
+        execute: function () {
+
+            exports('default', a);
+
+            var css = {
+                \\"a\\": \\"a\\"
+            };
+
+            function a() {
+                console.log(css);
+
+                Promise.all([
+    lazyload(\\"./assets/c.css\\"),
+    module.import('./c.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+            }
+
+        }
+    };
+});
+",
+  "b": "
+System.register([], function (exports, module) {
+    'use strict';
+import chunkCountIs8 from \\"./css.js\\";
+    return {
+        execute: function () {
+
+            exports('default', b);
+
+            var css = {
+                \\"b\\": \\"b\\"
+            };
+
+            function b() {
+                console.log(css);
+
+                Promise.all([
+    lazyload(\\"./assets/d.css\\"),
+    module.import('./d.js')
+])
+.then((results) => results[results.length - 1]).then(console.log);
+            }
+
+        }
+    };
+});
+",
+  "c": "
+System.register([], function (exports) {
+    'use strict';
+    return {
+        execute: function () {
+
+            var css = {
+                \\"c\\": \\"c\\"
+            };
+
+            console.log(css);
+            exports('default', css);
+
+        }
+    };
+});
+",
+  "d": "
+System.register([], function (exports) {
+    'use strict';
+    return {
+        execute: function () {
+
+            var css = {
+                \\"d\\": \\"d\\"
+            };
+
+            console.log(css);
+            exports('default', css);
+
+        }
+    };
+});
+",
+}
+`;
+
 exports[`rollup-rewriter shouldn't require a loader ("amd") 1`] = `
 Object {
   "a.js": "
diff --git a/packages/rollup-rewriter/test/rewriter.test.js b/packages/rollup-rewriter/test/rewriter.test.js
index 6274097a..c8e16b7d 100644
--- a/packages/rollup-rewriter/test/rewriter.test.js
+++ b/packages/rollup-rewriter/test/rewriter.test.js
@@ -111,6 +111,35 @@ describe("rollup-rewriter", () => {
 
         expect(result).toMatchRollupSnapshot();
     });
+    
+    it.each(formats)("should support loader being a function (%p)", async (format) => {
+        const bundle = await rollup({
+            input : [
+                require.resolve("./specimens/dynamic-imports/a.js"),
+                require.resolve("./specimens/dynamic-imports/b.js"),
+            ],
+            plugins : [
+                css({
+                    namer,
+                    map,
+                }),
+                rewriter({
+                    loader : ({ chunks }) => `import chunkCountIs${Object.keys(chunks).length} from "./css.js";`,
+                    loadfn : "lazyload",
+                }),
+            ],
+        });
+
+        const result = await bundle.generate({
+            format,
+            sourcemap,
+
+            assetFileNames,
+            chunkFileNames,
+        });
+
+        expect(result).toMatchRollupCodeSnapshot();
+    });
 
     it.each(formats)("should only rewrite when necessary (%p)", async (format) => {
         const bundle = await rollup({