diff --git a/.eslintignore b/.eslintignore index 78d25a616..e38c4aeb4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,9 +1,9 @@ coverage/ -parsers/ -node_modules/ -specimens/ -results/ -gen/ dangerfile.js -gen/ dist/ +gen/ +node_modules/ +parsers/ +results/ +specimens/ +specimens/output/ diff --git a/docs/svelte.md b/docs/svelte.md index fe08acf9e..6a78aa8bd 100644 --- a/docs/svelte.md +++ b/docs/svelte.md @@ -14,7 +14,7 @@ All options are passed to the underlying `Processor` instance, see [Options](api const filename = "./Component.html"; const { processor, preprocess } = require("modular-css-svelte")({ - css : "./dist/bundle.css" + // Processor options }); const processed = await svelte.preprocess( @@ -27,24 +27,30 @@ const result = processor.output(); fs.writeFileSync("./dist/bundle.css", result.css); ``` -### `rollup-plugin-svelte` +### `modular-css-rollup` #### API ```js const rollup = require("rollup").rollup; -const { preprocess, plugin } = require("modular-css-svelte/rollup")({ - css : "./dist/bundle.css" +const { preprocess, processor } = require("modular-css-svelte")({ + // Processor options }); const bundle = await rollup({ input : "./Component.html", + plugins : [ require("rollup-plugin-svelte")({ - preprocess + preprocess, + }), + + require("modular-css-rollup)({ + processor, + + common : "common.css", }), - plugin ] }); @@ -58,21 +64,28 @@ bundle.write({ #### `rollup.config.js` ```js -const { preprocess, plugin } = require("modular-css-svelte/rollup")({ - css : "./dist/bundle.css" +const { preprocess, processor } = require("modular-css-svelte")({ + // Processor options }); module.exports = { input : "./Component.html", + output : { format : "es", file : "./dist/bundle.js" }, + plugins : [ require("rollup-plugin-svelte")({ - preprocess + preprocess, + }), + + require("modular-css-rollup)({ + processor, + + common : "common.css", }), - plugin ] }; ``` diff --git a/package.json b/package.json index 7ddd0d547..5809b5093 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,8 @@ "jest": { "coveragePathIgnorePatterns": [ "node_modules", - "parsers" + "parsers", + "test-utils" ], "watchPathIgnorePatterns": [ "test/output", diff --git a/packages/rollup/README.md b/packages/rollup/README.md index 80cd16c47..e6c000725 100644 --- a/packages/rollup/README.md +++ b/packages/rollup/README.md @@ -44,28 +44,28 @@ export default { ## Options -### `include`/`exclude` +### `common` -A minimatch pattern, or an array of minimatch patterns, relative to `process.cwd()`. `include` defaults to `**/*.css`. +File name to use in case there are any CSS dependencies that appear in multiple bundles. -### `css` +### `include`/`exclude` -Boolean to determine if CSS files should be output at the end of compilation. Defaults to `true`. +A minimatch pattern, or an array of minimatch patterns, relative to `process.cwd()`. `include` defaults to `**/*.css`. ### `json` Boolean to determine if JSON files should be output at the end of compilation. Defaults to `false`. -### `namedExports` - -By default this plugin will create both a default export and named `export`s for each class in a CSS file. You can disable this by setting `namedExports` to `false`. - ### `map` Boolean to determine if inline source maps should be included. Defaults to `true`. To force the creation of external source maps set the value to `{ inline : false }`. +### `namedExports` + +By default this plugin will create both a default export and named `export`s for each class in a CSS file. You can disable this by setting `namedExports` to `false`. + ### Shared Options All other options are passed to the underlying `Processor` instance, see [Options](https://github.com/tivac/modular-css/blob/master/docs/api.md#options). diff --git a/packages/rollup/package-lock.json b/packages/rollup/package-lock.json index a9372639b..f868f2d88 100644 --- a/packages/rollup/package-lock.json +++ b/packages/rollup/package-lock.json @@ -581,6 +581,11 @@ "estree-walker": "^0.5.2", "micromatch": "^2.3.11" } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" } } } diff --git a/packages/rollup/package.json b/packages/rollup/package.json index da2671046..18eaef4d5 100644 --- a/packages/rollup/package.json +++ b/packages/rollup/package.json @@ -21,6 +21,7 @@ "esutils": "^2.0.2", "mkdirp": "^0.5.1", "modular-css-core": "file:../core", - "rollup-pluginutils": "^2.0.1" + "rollup-pluginutils": "^2.0.1", + "slash": "^2.0.0" } } diff --git a/packages/rollup/rollup.js b/packages/rollup/rollup.js index a0f2b5f30..0b70bc3c4 100644 --- a/packages/rollup/rollup.js +++ b/packages/rollup/rollup.js @@ -4,6 +4,7 @@ const path = require("path"); const keyword = require("esutils").keyword; const utils = require("rollup-pluginutils"); +const slash = require("slash"); const Processor = require("modular-css-core"); const output = require("modular-css-core/lib/output.js"); @@ -14,8 +15,14 @@ const map = { mappings : "", }; +function extensionless(file) { + return path.join(path.dirname(file), path.basename(file, path.extname(file))); +} + module.exports = function(opts) { const options = Object.assign(Object.create(null), { + common : false, + json : false, map : true, @@ -26,17 +33,13 @@ module.exports = function(opts) { const filter = utils.createFilter(options.include, options.exclude); + const processor = options.processor || new Processor(options); + let runs = 0; - let processor = new Processor(options); - let source; return { name : "modular-css", - options({ input }) { - source = input; - }, - transform : function(code, id) { let removed = []; @@ -69,7 +72,7 @@ module.exports = function(opts) { // Add dependencies out = out.concat( processor.dependencies(id).map((file) => - `import "${file.replace(/\\/g, "/")}";` + `import "${slash(file)}";` ) ); @@ -96,32 +99,52 @@ module.exports = function(opts) { }; }); }, - + buildEnd() { runs++; }, generateBundle : async function(outputOptions, bundle) { - await Promise.all( - Object.keys(bundle).map(async (entry) => { - const base = path.basename(entry, path.extname(entry)); - const files = Object.keys(bundle[entry].modules).filter(filter); + const bundles = []; + const common = processor.dependencies(); - // No point continuing if nothing to output! - if(!files.length) { - return; - } + Object.keys(bundle).forEach((entry) => { + const files = Object.keys(bundle[entry].modules).filter(filter); + + if(!files.length) { + return; + } + + // remove the files being exported from the common bundle + files.forEach((file) => + common.splice(common.indexOf(file), 1) + ); + + bundles.push({ + entry, + files, + base : extensionless(entry), + }); + }); + // Common chunk only emitted if configured & if necessary + if(options.common && common.length) { + bundles.push({ + entry : options.common, + base : extensionless(options.common), + files : common + }); + } + + await Promise.all( + bundles.map(async ({ base, files }) => { // TODO: docs say that empty string arg to .emitAsset() shouldn't be required // https://github.com/rollup/rollup/wiki/Plugins#plugin-context const css = this.emitAsset(`${base}.css`, ""); const result = await processor.output({ - from : source, - to : css, - - // Only export for files within this bundle - files : Object.keys(bundle[entry].modules).filter(filter) + to : css, + files }); this.setAssetSource(css, result.css); diff --git a/packages/rollup/test/__snapshots__/rollup.test.js.snap b/packages/rollup/test/__snapshots__/rollup.test.js.snap index 6f59b7100..17caffae5 100644 --- a/packages/rollup/test/__snapshots__/rollup.test.js.snap +++ b/packages/rollup/test/__snapshots__/rollup.test.js.snap @@ -1,5 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`/rollup.js code splitting should support dynamic imports 1`] = ` +"/* packages/rollup/test/specimens/dynamic-imports/a.css */ +.a { + + color: aqua; +} +" +`; + +exports[`/rollup.js code splitting should support dynamic imports 2`] = ` +"/* packages/rollup/test/specimens/dynamic-imports/b.css */ +.b { + color: blue; +} +" +`; + +exports[`/rollup.js code splitting should support dynamic imports 3`] = ` +"/* packages/rollup/test/specimens/dynamic-imports/c.css */ +.c { + + color: cyan; +} +" +`; + +exports[`/rollup.js code splitting should support dynamic imports 4`] = ` +"/* packages/rollup/test/specimens/dynamic-imports/d.css */ +.d { + color: darkred; +} +" +`; + exports[`/rollup.js code splitting should support manual chunks 1`] = ` "/* packages/rollup/test/specimens/manual-chunks/a.css */ .a { @@ -19,8 +53,12 @@ exports[`/rollup.js code splitting should support manual chunks 2`] = ` `; exports[`/rollup.js code splitting should support manual chunks 3`] = ` -"/* packages/rollup/test/specimens/manual-chunks/c.css */ -.c { color: green; } +"/* packages/rollup/test/specimens/manual-chunks/d.css */ +/* packages/rollup/test/specimens/manual-chunks/c.css */ +.c { + color: green; + background: darkred; +} " `; @@ -33,13 +71,29 @@ exports[`/rollup.js code splitting should support splitting up CSS files 1`] = ` `; exports[`/rollup.js code splitting should support splitting up CSS files 2`] = ` -"/* packages/rollup/test/specimens/named.css */ -.a { +"/* packages/rollup/test/specimens/dependencies.css */ +.wooga { + + background: blue; +} +" +`; + +exports[`/rollup.js should accept an existing processor instance 1`] = ` +"/* packages/rollup/test/specimens/simple.css */ +.fooga { color: red; } " `; +exports[`/rollup.js should accept an existing processor instance 2`] = ` +"/* packages/rollup/test/specimens/fake.css */ +.fake { + color: yellow; +}" +`; + exports[`/rollup.js should allow disabling of named exports 1`] = ` "var css = { \\"str\\": \\"\\\\\\"string\\\\\\"\\", @@ -57,6 +111,15 @@ console.log(fooga); " `; +exports[`/rollup.js should correctly pass to/from params for relative paths 1`] = ` +"/* packages/rollup/test/specimens/relative-paths.css */ +.wooga { + color: red; + background: url(\\"packages/rollup/test/specimens/folder/to.png\\"); +} +" +`; + exports[`/rollup.js should generate CSS 1`] = ` "/* packages/rollup/test/specimens/simple.css */ .fooga { diff --git a/packages/rollup/test/rollup.test.js b/packages/rollup/test/rollup.test.js index 33b6c616e..f88d7d7ed 100644 --- a/packages/rollup/test/rollup.test.js +++ b/packages/rollup/test/rollup.test.js @@ -7,9 +7,12 @@ const rollup = require("rollup").rollup; const dedent = require("dedent"); const shell = require("shelljs"); -const read = require("test-utils/read.js")(__dirname); -const exists = require("test-utils/exists.js")(__dirname); -const namer = require("test-utils/namer.js"); +const read = require("test-utils/read.js")(__dirname); +const exists = require("test-utils/exists.js")(__dirname); +const namer = require("test-utils/namer.js"); +const watching = require("test-utils/rollup-watching.js"); + +const Processor = require("modular-css-core"); const plugin = require("../rollup.js"); @@ -17,63 +20,52 @@ function error(root) { throw root.error("boom"); } -function watching(cb) { - var count = 0; - - return (details) => { - if(details.code === "ERROR" || details.code === "FATAL") { - throw details.error; - } - - if(details.code !== "END") { - return; - } - - count++; - - cb(count, details); - }; -} - error.postcssPlugin = "error-plugin"; +const output = "./packages/rollup/test/output"; const assetFileNames = "assets/[name][extname]"; const format = "es"; +const map = false; +const sourcemap = false; describe("/rollup.js", () => { /* eslint max-statements: "off" */ - afterEach(() => shell.rm("-rf", "./packages/rollup/test/output/*")); + afterEach(() => shell.rm("-rf", `${output}/*`)); it("should be a function", () => expect(typeof plugin).toBe("function") ); - it("should generate exports", () => - rollup({ + it("should generate exports", async () => { + const bundle = await rollup({ input : require.resolve("./specimens/simple.js"), plugins : [ plugin({ namer }) ] - }) - .then((bundle) => bundle.generate({ format })) - .then((result) => expect(result.code).toMatchSnapshot()) - ); + }); + + const result = await bundle.generate({ format }); + + expect(result.code).toMatchSnapshot(); + }); - it("should be able to tree-shake results", () => - rollup({ + it("should be able to tree-shake results", async () => { + const bundle = await rollup({ input : require.resolve("./specimens/tree-shaking.js"), plugins : [ plugin({ namer }) ] - }) - .then((bundle) => bundle.generate({ format })) - .then((result) => expect(result.code).toMatchSnapshot()) - ); + }); + + const result = await bundle.generate({ format }); + + expect(result.code).toMatchSnapshot(); + }); it("should generate CSS", async () => { const bundle = await rollup({ @@ -81,7 +73,7 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - map : false + map, }) ] }); @@ -89,11 +81,31 @@ describe("/rollup.js", () => { await bundle.write({ format, assetFileNames, - file : "./packages/rollup/test/output/simple.js", + file : `${output}/simple.js`, }); expect(read("assets/simple.css")).toMatchSnapshot(); }); + + it("should correctly pass to/from params for relative paths", async () => { + const bundle = await rollup({ + input : require.resolve("./specimens/relative-paths.js"), + plugins : [ + plugin({ + namer, + map, + }) + ] + }); + + await bundle.write({ + format, + assetFileNames, + file : `${output}/relative-paths.js`, + }); + + expect(read("assets/relative-paths.css")).toMatchSnapshot(); + }); it("should avoid generating empty CSS", async () => { const bundle = await rollup({ @@ -108,14 +120,14 @@ describe("/rollup.js", () => { await bundle.write({ format, assetFileNames, - file : "./packages/rollup/test/output/no-css.js", + file : `${output}/no-css.js`, }); expect(exists("assets/no-css.css")).toBe(false); }); - it("should generate JSON", () => - rollup({ + it("should generate JSON", async () => { + const bundle = await rollup({ input : require.resolve("./specimens/simple.js"), plugins : [ plugin({ @@ -123,16 +135,16 @@ describe("/rollup.js", () => { json : true }) ] - }) - .then((bundle) => bundle.write({ + }); + + await bundle.write({ format, assetFileNames, - file : "./packages/rollup/test/output/simple.js", - })) - .then(() => - expect(read("assets/simple.json")).toMatchSnapshot() - ) - ); + file : `${output}/simple.js`, + }); + + expect(read("assets/simple.json")).toMatchSnapshot(); + }); it("should provide named exports", async () => { const bundle = await rollup({ @@ -165,7 +177,7 @@ describe("/rollup.js", () => { await bundle.write({ format, assetFileNames, - file : "./packages/rollup/test/output/simple.js", + file : `${output}/simple.js`, }); // Have to parse it into JSON so the propertyMatcher can exclude the file property @@ -240,7 +252,7 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - map : false, + map, }) ] }); @@ -248,34 +260,71 @@ describe("/rollup.js", () => { const source = await bundle.generate({ format, assetFileNames, - sourcemap : false + sourcemap, }); expect(source.map).toBe(null); await bundle.write({ - format, assetFileNames, + format, + sourcemap, - file : "./packages/rollup/test/output/no-maps.js", - sourcemap : false, + file : `${output}/no-maps.js`, }); expect(read("assets/no-maps.css")).toMatchSnapshot(); }); - it("should respect the CSS dependency tree", () => - rollup({ + it("should respect the CSS dependency tree", async () => { + const bundle = await rollup({ input : require.resolve("./specimens/dependencies.js"), plugins : [ plugin({ namer }) ] - }) - .then((bundle) => bundle.generate({ format })) - .then((result) => expect(result.code).toMatchSnapshot()) - ); + }); + + const result = await bundle.generate({ format }); + + expect(result.code).toMatchSnapshot(); + }); + + it("should accept an existing processor instance", async () => { + const processor = new Processor({ + namer, + map, + }); + + await processor.string("./packages/rollup/test/specimens/fake.css", dedent(` + .fake { + color: yellow; + } + `)); + + const bundle = await rollup({ + input : require.resolve("./specimens/simple.js"), + plugins : [ + plugin({ + processor, + + common : "common.css" + }) + ] + }); + + await bundle.write({ + format, + sourcemap, + assetFileNames, + + file : `${output}/existing-processor.js` + }); + + expect(read("assets/existing-processor.css")).toMatchSnapshot(); + expect(read("assets/common.css")).toMatchSnapshot(); + }); describe("errors", () => { function checkError(err) { @@ -288,7 +337,7 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - css : "./packages/rollup/test/output/errors.css", + css : `${output}/errors.css`, before : [ error ] }) ] @@ -302,7 +351,7 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - css : "./packages/rollup/test/output/errors.css", + css : `${output}/errors.css`, after : [ error ] }) ] @@ -317,28 +366,28 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - css : "./packages/rollup/test/output/errors.css", + css : `${output}/errors.css`, done : [ error ] }) ] }) .then((bundle) => bundle.write({ format, - file : "./packages/rollup/test/output/done-error.js" + file : `${output}/done-error.js` })) ); }); describe("watch", () => { - var watch = require("rollup").watch, - watcher; + const watch = require("rollup").watch; + let watcher; afterEach(() => watcher.close()); it("should generate correct builds in watch mode when files change", (done) => { // Create v1 of the file fs.writeFileSync( - "./packages/rollup/test/output/watched.css", + `${output}/watched.css`, ".one { color: red; }" ); @@ -346,20 +395,20 @@ describe("/rollup.js", () => { watcher = watch({ input : require.resolve("./specimens/watch.js"), output : { - file : "./packages/rollup/test/output/watch-output.js", + file : `${output}/watch-output.js`, format, assetFileNames, }, plugins : [ plugin({ - map : false + map, }) ] }); // Create v2 of the file after a bit setTimeout(() => fs.writeFileSync( - "./packages/rollup/test/output/watched.css", + `${output}/watched.css`, ".two { color: blue; }" ), 200); @@ -379,13 +428,13 @@ describe("/rollup.js", () => { it("should correctly update files within the dependency graph in watch mode when files change", (done) => { // Create v1 of the files - fs.writeFileSync("./packages/rollup/test/output/one.css", dedent(` + fs.writeFileSync(`${output}/one.css`, dedent(` .one { color: red; } `)); - fs.writeFileSync("./packages/rollup/test/output/two.css", dedent(` + fs.writeFileSync(`${output}/two.css`, dedent(` .two { composes: one from "./one.css"; @@ -393,7 +442,7 @@ describe("/rollup.js", () => { } `)); - fs.writeFileSync("./packages/rollup/test/output/watch.js", dedent(` + fs.writeFileSync(`${output}/watch.js`, dedent(` import css from "./two.css"; console.log(css); `)); @@ -402,19 +451,19 @@ describe("/rollup.js", () => { watcher = watch({ input : require.resolve("./output/watch.js"), output : { - file : "./packages/rollup/test/output/watch-output.js", + file : `${output}/watch-output.js`, format, assetFileNames, }, plugins : [ plugin({ - map : false + map, }) ] }); // Create v2 of the file after a bit - setTimeout(() => fs.writeFileSync("./packages/rollup/test/output/one.css", dedent(` + setTimeout(() => fs.writeFileSync(`${output}/one.css`, dedent(` .one { color: green; } @@ -437,7 +486,7 @@ describe("/rollup.js", () => { it("should correctly add new css files in watch mode when files change", (done) => { // Create v1 of the files fs.writeFileSync( - "./packages/rollup/test/output/one.css", + `${output}/one.css`, dedent(` .one { color: red; @@ -446,7 +495,7 @@ describe("/rollup.js", () => { ); fs.writeFileSync( - "./packages/rollup/test/output/watch.js", + `${output}/watch.js`, dedent(` console.log("hello"); `) @@ -456,20 +505,20 @@ describe("/rollup.js", () => { watcher = watch({ input : require.resolve("./output/watch.js"), output : { - file : "./packages/rollup/test/output/watch-output.js", + file : `${output}/watch-output.js`, format, assetFileNames, }, plugins : [ plugin({ - map : false + map, }) ] }); // Create v2 of the file after a bit setTimeout(() => fs.writeFileSync( - "./packages/rollup/test/output/watch.js", + `${output}/watch.js`, dedent(` import css from "./one.css"; @@ -493,37 +542,44 @@ describe("/rollup.js", () => { }); describe("code splitting", () => { + const experimentalCodeSplitting = true; + const chunkFileNames = "[name].js"; + it("should support splitting up CSS files", async () => { const bundle = await rollup({ - experimentalCodeSplitting : true, + experimentalCodeSplitting, input : [ require.resolve("./specimens/simple.js"), - require.resolve("./specimens/named.js"), + require.resolve("./specimens/dependencies.js"), ], plugins : [ plugin({ namer, - map : false + map, + + common : "common.css" }) ] }); await bundle.write({ format, + assetFileNames, + chunkFileNames, - dir : "./packages/rollup/test/output/" + dir : `${output}/` }); - expect(read("assets/simple.css")).toMatchSnapshot(); - expect(read("assets/named.css")).toMatchSnapshot(); + expect(read("assets/chunk.css")).toMatchSnapshot(); + expect(read("assets/dependencies.css")).toMatchSnapshot(); }); it("should support manual chunks", async () => { const bundle = await rollup({ - experimentalCodeSplitting : true, + experimentalCodeSplitting, input : [ require.resolve("./specimens/manual-chunks/a.js"), @@ -539,23 +595,59 @@ describe("/rollup.js", () => { plugins : [ plugin({ namer, - map : false + map, }) ] }); await bundle.write({ format, - assetFileNames, - chunkFileNames : "[name].js", + assetFileNames, + chunkFileNames, - dir : "./packages/rollup/test/output/" + dir : `${output}/` }); expect(read("assets/a.css")).toMatchSnapshot(); expect(read("assets/b.css")).toMatchSnapshot(); expect(read("assets/shared.css")).toMatchSnapshot(); }); + + it("should support dynamic imports", async () => { + const bundle = await rollup({ + experimentalCodeSplitting, + + // treeshake : false, + + input : [ + require.resolve("./specimens/dynamic-imports/a.js"), + require.resolve("./specimens/dynamic-imports/b.js"), + ], + + plugins : [ + plugin({ + namer, + map, + + common : "common.css" + }) + ] + }); + + await bundle.write({ + format, + + assetFileNames, + chunkFileNames, + + dir : `${output}/` + }); + + expect(read("assets/a.css")).toMatchSnapshot(); + expect(read("assets/b.css")).toMatchSnapshot(); + expect(read("assets/c.css")).toMatchSnapshot(); + expect(read("assets/common.css")).toMatchSnapshot(); + }); }); }); diff --git a/packages/rollup/test/specimens/dynamic-imports/a.css b/packages/rollup/test/specimens/dynamic-imports/a.css new file mode 100644 index 000000000..3fb12cfc3 --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/a.css @@ -0,0 +1,5 @@ +.a { + composes: d from "./d.css"; + + color: aqua; +} diff --git a/packages/rollup/test/specimens/dynamic-imports/a.js b/packages/rollup/test/specimens/dynamic-imports/a.js new file mode 100644 index 000000000..a6d22eff2 --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/a.js @@ -0,0 +1,3 @@ +import css from "./a.css"; + +console.log(css); diff --git a/packages/rollup/test/specimens/dynamic-imports/b.css b/packages/rollup/test/specimens/dynamic-imports/b.css new file mode 100644 index 000000000..908ccb0c9 --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/b.css @@ -0,0 +1,3 @@ +.b { + color: blue; +} diff --git a/packages/rollup/test/specimens/dynamic-imports/b.js b/packages/rollup/test/specimens/dynamic-imports/b.js new file mode 100644 index 000000000..d22896709 --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/b.js @@ -0,0 +1,7 @@ +import css from "./b.css"; + +export default function() { + console.log(css); + + import("./c.js").then(console.log); +} diff --git a/packages/rollup/test/specimens/dynamic-imports/c.css b/packages/rollup/test/specimens/dynamic-imports/c.css new file mode 100644 index 000000000..009607941 --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/c.css @@ -0,0 +1,5 @@ +.c { + composes: d from "./d.css"; + + color: cyan; +} diff --git a/packages/rollup/test/specimens/dynamic-imports/c.js b/packages/rollup/test/specimens/dynamic-imports/c.js new file mode 100644 index 000000000..13c3c44db --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/c.js @@ -0,0 +1,5 @@ +import css from "./c.css"; + +console.log(css); + +export default css; diff --git a/packages/rollup/test/specimens/dynamic-imports/d.css b/packages/rollup/test/specimens/dynamic-imports/d.css new file mode 100644 index 000000000..bbb030a2e --- /dev/null +++ b/packages/rollup/test/specimens/dynamic-imports/d.css @@ -0,0 +1,3 @@ +.d { + color: darkred; +} diff --git a/packages/rollup/test/specimens/manual-chunks/c.css b/packages/rollup/test/specimens/manual-chunks/c.css index f038abbf5..bae2acd2e 100644 --- a/packages/rollup/test/specimens/manual-chunks/c.css +++ b/packages/rollup/test/specimens/manual-chunks/c.css @@ -1 +1,6 @@ -.c { color: green; } +@value d from "./d.css"; + +.c { + color: green; + background: d; +} diff --git a/packages/rollup/test/specimens/manual-chunks/d.css b/packages/rollup/test/specimens/manual-chunks/d.css new file mode 100644 index 000000000..0b4eb55ef --- /dev/null +++ b/packages/rollup/test/specimens/manual-chunks/d.css @@ -0,0 +1 @@ +@value d: darkred; diff --git a/packages/rollup/test/specimens/relative-paths.css b/packages/rollup/test/specimens/relative-paths.css new file mode 100644 index 000000000..d85862bfc --- /dev/null +++ b/packages/rollup/test/specimens/relative-paths.css @@ -0,0 +1,4 @@ +.wooga { + color: red; + background: url("./folder/to.png"); +} diff --git a/packages/rollup/test/specimens/relative-paths.js b/packages/rollup/test/specimens/relative-paths.js new file mode 100644 index 000000000..c58c7657d --- /dev/null +++ b/packages/rollup/test/specimens/relative-paths.js @@ -0,0 +1,3 @@ +import css from "./relative-paths.css"; + +console.log(css); diff --git a/packages/svelte/README.md b/packages/svelte/README.md index b2dd9b644..93c667273 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -37,7 +37,7 @@ into what is effectively this while allowing you to use all of the usual `modular-css` goodies. -Alternatively you can use `` tags to reference CSS external to the component. +Alternatively you can use `` tags to reference CSS external to the component. ## Install @@ -51,7 +51,7 @@ Alternatively you can use `` tags to reference CSS external to the compone const filename = "./Component.html"; const { processor, preprocess } = require("modular-css-svelte")({ - css : "./dist/bundle.css" + // Processor options }); const processed = await svelte.preprocess( @@ -64,24 +64,30 @@ const result = processor.output(); fs.writeFileSync("./dist/bundle.css", result.css); ``` -### `rollup-plugin-svelte` +### `modular-css-rollup` #### API ```js const rollup = require("rollup").rollup; -const { preprocess, plugin } = require("modular-css-svelte/rollup")({ - css : "./dist/bundle.css" +const { preprocess, processor } = require("modular-css-svelte")({ + // Processor options }); const bundle = await rollup({ input : "./Component.html", + plugins : [ require("rollup-plugin-svelte")({ - preprocess + preprocess, + }), + + require("modular-css-rollup)({ + processor, + + common : "common.css", }), - plugin ] }); @@ -95,21 +101,28 @@ bundle.write({ #### `rollup.config.js` ```js -const { preprocess, plugin } = require("modular-css-svelte/rollup")({ - css : "./dist/bundle.css" +const { preprocess, processor } = require("modular-css-svelte")({ + // Processor options }); module.exports = { input : "./Component.html", + output : { format : "es", file : "./dist/bundle.js" }, + plugins : [ require("rollup-plugin-svelte")({ - preprocess + preprocess, + }), + + require("modular-css-rollup)({ + processor, + + common : "common.css", }), - plugin ] }; ``` diff --git a/packages/svelte/package-lock.json b/packages/svelte/package-lock.json index 787154c78..dd68cc2ac 100644 --- a/packages/svelte/package-lock.json +++ b/packages/svelte/package-lock.json @@ -4,6 +4,215 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" + }, + "estree-walker": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", + "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==" + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "^2.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "^1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", @@ -286,10 +495,102 @@ } } }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "randomatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "rollup-pluginutils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.0.tgz", + "integrity": "sha512-xB6hsRsjdJdIYWEyYUJy/3ki5g69wrf0luHPGNK3ZSocV6HLNfio59l3dZ3TL4xUwEKgROhFi9jOCt6c5gfUWw==", + "requires": { + "estree-walker": "^0.5.2", + "micromatch": "^2.3.11" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" } } } diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 293ec5d72..7e52bf5ec 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -21,8 +21,11 @@ "postcss" ], "dependencies": { + "dedent": "^0.7.0", "mkdirp": "^0.5.1", "modular-css-core": "file:../core", - "resolve-from": "^4.0.0" + "resolve-from": "^4.0.0", + "rollup-pluginutils": "^2.3.0", + "slash": "^2.0.0" } } diff --git a/packages/svelte/rollup.js b/packages/svelte/rollup.js deleted file mode 100644 index df6af6e8c..000000000 --- a/packages/svelte/rollup.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; - -const fs = require("fs"); -const path = require("path"); - -const mkdirp = require("mkdirp"); - -const Processor = require("modular-css-core"); -const methods = require("./methods.js"); - -module.exports = function(args) { - const processor = new Processor(args); - let source; - - return { - preprocess : { - markup : methods.markup(processor), - style : methods.style - }, - - plugin : { - name : "modular-css-rollup-svelte", - - options : ({ input }) => { - source = input; - }, - - ongenerate : (bundle, result) => { - result.css = processor.output({ - from : source, - to : args.css - }); - }, - - onwrite : (bundle, result) => - result.css.then((data) => { - if(args.css) { - mkdirp.sync(path.dirname(args.css)); - - fs.writeFileSync( - args.css, - data.css - ); - } - }) - } - }; -}; diff --git a/packages/svelte/src/markup.js b/packages/svelte/src/markup.js new file mode 100644 index 000000000..7c5626b27 --- /dev/null +++ b/packages/svelte/src/markup.js @@ -0,0 +1,103 @@ +"use strict"; + +const path = require("path"); + +const resolve = require("resolve-from"); +const dedent = require("dedent"); +const slash = require("slash"); + +const styleRegex = / + " `; -exports[`/svelte.js should generate exports 2`] = ` +exports[`/svelte.js should extract CSS from a tag (existing script) 2`] = ` "/* packages/svelte/test/specimens/simple.css */ .fooga { color: red; @@ -22,45 +30,108 @@ exports[`/svelte.js should generate exports 2`] = ` background: blue; } -/* packages/svelte/test/specimens/svelte.html */ +/* packages/svelte/test/specimens/external.css */ .flex { - display: flex; - } + display: flex; +} .text { - color: #000; - } + color: #000; +} .active { - color: #F00; - } + color: #F00; +} " `; -exports[`/svelte.js should ignore files without " + background: blue; +} +/* packages/svelte/test/specimens/external.css */ +.flex { + display: flex; +} +.text { + color: #000; +} +.active { + color: #F00; +} +" `; -exports[`/svelte.js should support external css via 1`] = ` -"
Text
+Text
Text
+Text
+Text
+Text
+Text
+Text
Text
+Text