From 9af20eb54f1046538a2d371ec41e0f82347dda78 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 10 Dec 2024 18:58:15 +0100 Subject: [PATCH 1/6] feat: implement CSS modules processor support --- package-lock.json | 145 +++++++++++++++++++++++++ package.json | 1 + src/types.ts | 5 + src/utils/processRenderResponse.ts | 95 +++++++++++----- test/fixtures/basic/index.js | 8 +- test/fixtures/css-modules/index.js | 3 + test/fixtures/css-modules/style.scss | 5 + test/fixtures/dependencies/expected.js | 3 +- test/index.test.ts | 44 +++++++- test/snapshots/test/index.test.ts.md | 112 +++++++++---------- test/snapshots/test/index.test.ts.snap | Bin 1057 -> 1056 bytes 11 files changed, 329 insertions(+), 92 deletions(-) create mode 100644 test/fixtures/css-modules/index.js create mode 100644 test/fixtures/css-modules/style.scss diff --git a/package-lock.json b/package-lock.json index a8639fb..69ab46f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "icss-utils": "^5.1.0", "nyc": "^17.0.0", "postcss": "^8.4.16", + "postcss-modules": "^6.0.1", "prettier": "^3.3.3", "rollup": "^1 || ^2 || ^3", "sinon": "^18.0.0", @@ -2416,6 +2417,19 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2999,6 +3013,16 @@ "node": ">=8" } }, + "node_modules/generic-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz", + "integrity": "sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^3.2.0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3786,6 +3810,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4797,6 +4831,110 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-modules": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-6.0.1.tgz", + "integrity": "sha512-zyo2sAkVvuZFFy0gc2+4O+xar5dYlaVy/ebO24KT0ftk/iJevSNyPyQellsBLlnccwh7f6V6Y4GvuKRYToNgpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "generic-names": "^4.0.0", + "icss-utils": "^5.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.3" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz", + "integrity": "sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prettier": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", @@ -5304,6 +5442,13 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", diff --git a/package.json b/package.json index bc9fed8..039515a 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "icss-utils": "^5.1.0", "nyc": "^17.0.0", "postcss": "^8.4.16", + "postcss-modules": "^6.0.1", "prettier": "^3.3.3", "rollup": "^1 || ^2 || ^3", "sinon": "^18.0.0", diff --git a/src/types.ts b/src/types.ts index 0f82de9..2acba2e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,9 +17,14 @@ export type RollupPluginSassProcessorFnOutput = | string | { css: string; + + /** If provided, the default export of the CSS file will be the map returned here */ + cssModules?: Record, + // User processor might add additional exports [key: string]: unknown; }; + export type RollupPluginSassProcessorFn = (styles: string, id: string) => Promise | T; diff --git a/src/utils/processRenderResponse.ts b/src/utils/processRenderResponse.ts index 72187a3..519aab3 100644 --- a/src/utils/processRenderResponse.ts +++ b/src/utils/processRenderResponse.ts @@ -1,10 +1,15 @@ -import { isFunction, isObject, isString } from './helpers'; +import path from 'path'; + +import { makeLegalIdentifier } from '@rollup/pluginutils'; + import { RollupPluginSassOptions, RollupPluginSassState, RollupPluginSassProcessorFnOutput, } from '../types'; +import { isFunction, isObject, isString } from './helpers'; + export const INSERT_STYLE_ID = '___$insertStyle'; export const processRenderResponse = ( @@ -12,7 +17,7 @@ export const processRenderResponse = ( RollupPluginSassOptions, 'insert' | 'processor' | 'output' >, - file: string, + fileId: string, state: RollupPluginSassState, inCss: string, ) => { @@ -23,40 +28,58 @@ export const processRenderResponse = ( return ( Promise.resolve() .then(() => - !isFunction(processor) ? inCss + '' : processor(inCss, file), + !isFunction(processor) ? inCss + '' : processor(inCss, fileId), ) // Gather output requirements - .then((result: Partial) => { - if (!isObject(result)) { - return [result, '']; - } - if (!isString(result.css)) { - throw new Error( - 'You need to return the styles using the `css` property. ' + - 'See https://github.com/differui/rollup-plugin-sass#processor', + .then( + ( + result: Partial, + ): [string, string, Record?] => { + if (!isObject(result)) { + return [result, '']; + } + + if (!isString(result.css)) { + /** @todo consider using rollup utils to throw this error */ + throw new Error( + 'You need to return the styles using the `css` property. ' + + 'See https://github.com/elycruz/rollup-plugin-sass#processor', + ); + } + + if (result.cssModules && !isObject(result.cssModules)) { + /** @todo consider using rollup utils to throw this error */ + throw new Error( + 'You need to provide a js object as `cssModules` property. ' + + 'See https://github.com/elycruz/rollup-plugin-sass#processor', + ); + } + + const { css: outCss, cssModules } = result; + + // Remove "reserved" keys to avoid to add them inside additional exports + delete result.css; + delete result.cssModules; + + const namedExports = Object.keys(result).reduce( + (agg, name) => + agg + `export const ${name} = ${JSON.stringify(result[name])};\n`, + '', ); - } - const outCss = result.css; - delete result.css; - const restExports = Object.keys(result).reduce( - (agg, name) => - agg + `export const ${name} = ${JSON.stringify(result[name])};\n`, - '', - ); - return [outCss, restExports]; - }) + return [outCss, namedExports, cssModules] as const; + }, + ) // Compose output - .then(([resolvedCss, restExports]) => { + .then(([resolvedCss, namedExports, cssModules]) => { const { styleMaps } = state; // Update bundle tracking entry with resolved content - styleMaps[file].content = resolvedCss; - - const out = JSON.stringify(resolvedCss); + styleMaps[fileId].content = resolvedCss; let defaultExport = `""`; - let imports = ''; + let cssCode = JSON.stringify(resolvedCss); + let imports: string[] = []; if (rollupOptions.insert) { /** @@ -65,13 +88,25 @@ export const processRenderResponse = ( * e.g., the path will completely replaced, and re-generated (as a relative path) * by rollup. */ - imports = `import ${INSERT_STYLE_ID} from '${INSERT_STYLE_ID}';\n`; - defaultExport = `${INSERT_STYLE_ID}(${out});`; + imports.push(`import ${INSERT_STYLE_ID} from '${INSERT_STYLE_ID}';`); + cssCode = `${INSERT_STYLE_ID}(${cssCode});`; + defaultExport = cssCode; } else if (!rollupOptions.output) { - defaultExport = out; + defaultExport = cssCode; } - return `${imports}export default ${defaultExport};\n${restExports}`; + const variableName = makeLegalIdentifier( + path.basename(fileId, path.extname(fileId)), + ); + + const codeOutput = [ + ...imports, + `var ${variableName} = ${defaultExport};`, + `export default ${cssModules ? JSON.stringify(cssModules) : variableName};`, + namedExports, + ].join('\n'); + + return codeOutput; }) ); // @note do not `catch` here - let error propagate to rollup level }; diff --git a/test/fixtures/basic/index.js b/test/fixtures/basic/index.js index 23508bf..7007c82 100644 --- a/test/fixtures/basic/index.js +++ b/test/fixtures/basic/index.js @@ -1,5 +1,5 @@ -import atualA from '../../assets/actual_a.scss'; -import atualB from '../../assets/actual_b.scss'; -import atualC from '../../assets/actual_c.sass'; +import actualA from '../../assets/actual_a.scss'; +import actualB from '../../assets/actual_b.scss'; +import actualC from '../../assets/actual_c.sass'; -export default atualA + atualB + atualC; +export default actualA + actualB + actualC; diff --git a/test/fixtures/css-modules/index.js b/test/fixtures/css-modules/index.js new file mode 100644 index 0000000..76dbe2d --- /dev/null +++ b/test/fixtures/css-modules/index.js @@ -0,0 +1,3 @@ +import style from './style.scss'; + +export default style; diff --git a/test/fixtures/css-modules/style.scss b/test/fixtures/css-modules/style.scss new file mode 100644 index 0000000..7d701c7 --- /dev/null +++ b/test/fixtures/css-modules/style.scss @@ -0,0 +1,5 @@ +$color_red: red; + +.something { + color: $color_red; +} diff --git a/test/fixtures/dependencies/expected.js b/test/fixtures/dependencies/expected.js index 63f40f8..ce51f6d 100644 --- a/test/fixtures/dependencies/expected.js +++ b/test/fixtures/dependencies/expected.js @@ -1 +1,2 @@ -export default "body{color:blue}body{color:#fff}body{color:red}"; +var style1 = "body{color:blue}body{color:#fff}body{color:red}"; +export default style1; diff --git a/test/index.test.ts b/test/index.test.ts index 44a88ac..146bcaa 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -13,6 +13,7 @@ import type { } from 'rollup'; import * as sassRuntime from 'sass'; import postcss from 'postcss'; +import postcssModules from 'postcss-modules'; import { extractICSS } from 'icss-utils'; import sass from '../src/index'; @@ -694,7 +695,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( processor: () => ({}), }); const message = - 'You need to return the styles using the `css` property. See https://github.com/differui/rollup-plugin-sass#processor'; + 'You need to return the styles using the `css` property. See https://github.com/elycruz/rollup-plugin-sass#processor'; await t.throwsAsync( () => @@ -711,6 +712,47 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } + +// { +// const title = +// 'should produces CSS modules if `cssModules` is returned from processor'; + +// const macro = test.macro<[RollupPluginSassOptions]>({ +// async exec(t, pluginOptions) { +// const outputBundle = await rollup({ +// input: 'test/fixtures/css-modules/index.js', +// plugins: [ +// sass({ +// ...pluginOptions, +// processor: async (styles, id) => { +// let cssModules = {}; +// const postcssProcessResult = await postcss([ +// postcssModules({ +// getJSON: (_, json) => { +// if (json) cssModules = json; +// }, +// }), +// ]).process(styles, { +// from: id, +// }); + +// return { css: postcssProcessResult.css, cssModules }; +// }, +// }), +// ], +// }); + +// const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); +// const result = getFirstChunkCode(output); + +// t.snapshot(result); +// }, +// title: createApiOptionTestCaseTitle, +// }); + +// test.only(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); +// test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); +// } // #endregion // #region node resolution diff --git a/test/snapshots/test/index.test.ts.md b/test/snapshots/test/index.test.ts.md index a0eb87c..f294f4f 100644 --- a/test/snapshots/test/index.test.ts.md +++ b/test/snapshots/test/index.test.ts.md @@ -8,13 +8,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body {\\n color: red;\\n}";␊ + `var actual_a = "body {\\n color: red;\\n}";␊ ␊ - var atualB = "body {\\n color: green;\\n}";␊ + var actual_b = "body {\\n color: green;\\n}";␊ ␊ - var atualC = "body {\\n color: blue;\\n}";␊ + var actual_c = "body {\\n color: blue;\\n}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` @@ -23,13 +23,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body {\\n color: red;\\n}";␊ + `var actual_a = "body {\\n color: red;\\n}";␊ ␊ - var atualB = "body {\\n color: green;\\n}";␊ + var actual_b = "body {\\n color: green;\\n}";␊ ␊ - var atualC = "body {\\n color: blue;\\n}";␊ + var actual_c = "body {\\n color: blue;\\n}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` @@ -38,13 +38,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var atualC = "body{color:blue}";␊ + var actual_c = "body{color:blue}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` @@ -53,13 +53,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var atualC = "body{color:blue}";␊ + var actual_c = "body{color:blue}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` @@ -68,18 +68,18 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var actualD = "body{color:red;background-color:green}";␊ + `var actual_d = "body{color:red;background-color:green}";␊ ␊ - export { actualD as default };␊ + export { actual_d as default };␊ ` ## should compress the dest CSS using 'api' = 'modern' > Snapshot 1 - `var actualD = "body{color:red;background-color:green}";␊ + `var actual_d = "body{color:red;background-color:green}";␊ ␊ - export { actualD as default };␊ + export { actual_d as default };␊ ` ## should custom importer works using 'api' = 'legacy' (implicit) @@ -104,18 +104,18 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var actualE = "body{color:red}";␊ + `var actual_e = "body{color:red}";␊ ␊ - export { actualE as default };␊ + export { actual_e as default };␊ ` ## should support options.data using 'api' = 'modern' > Snapshot 1 - `var actualE = "body{color:red}";␊ + `var actual_e = "body{color:red}";␊ ␊ - export { actualE as default };␊ + export { actual_e as default };␊ ` ## should insert CSS into head tag using 'api' = 'legacy' (implicit) @@ -142,11 +142,11 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "}der:roloc{ydob";␊ + `var actual_a = "}der:roloc{ydob";␊ ␊ - var atualB = "}neerg:roloc{ydob";␊ + var actual_b = "}neerg:roloc{ydob";␊ ␊ - var index = atualA + atualB;␊ + var index = actual_a + actual_b;␊ ␊ export { index as default };␊ ` @@ -155,11 +155,11 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "}der:roloc{ydob";␊ + `var actual_a = "}der:roloc{ydob";␊ ␊ - var atualB = "}neerg:roloc{ydob";␊ + var actual_b = "}neerg:roloc{ydob";␊ ␊ - var index = atualA + atualB;␊ + var index = actual_a + actual_b;␊ ␊ export { index as default };␊ ` @@ -168,13 +168,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ const foo = "foo";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ const bar = "bar";␊ ␊ - var index = atualA + atualB + foo + bar;␊ + var index = actual_a + actual_b + foo + bar;␊ ␊ export { index as default };␊ ` @@ -183,13 +183,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ const foo = "foo";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ const bar = "bar";␊ ␊ - var index = atualA + atualB + foo + bar;␊ + var index = actual_a + actual_b + foo + bar;␊ ␊ export { index as default };␊ ` @@ -198,11 +198,11 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var index = atualA + atualB;␊ + var index = actual_a + actual_b;␊ ␊ export { index as default };␊ ` @@ -211,11 +211,11 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var index = atualA + atualB;␊ + var index = actual_a + actual_b;␊ ␊ export { index as default };␊ ` @@ -224,13 +224,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var actualA2 = "body{color:red;background:blue}";␊ + `var actual_a_WithIcssExports = "body{color:red;background:blue}";␊ const color = "red";␊ const color2 = "blue";␊ ␊ - var actualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var withIcssExports = actualA2 + actualB;␊ + var withIcssExports = actual_a_WithIcssExports + actual_b;␊ ␊ export { color, color2, withIcssExports as default };␊ ` @@ -239,13 +239,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var actualA2 = "body{color:red;background:blue}";␊ + `var actual_a_WithIcssExports = "body{color:red;background:blue}";␊ const color = "red";␊ const color2 = "blue";␊ ␊ - var actualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var withIcssExports = actualA2 + actualB;␊ + var withIcssExports = actual_a_WithIcssExports + actual_b;␊ ␊ export { color, color2, withIcssExports as default };␊ ` @@ -322,13 +322,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var atualC = "body{color:blue}";␊ + var actual_c = "body{color:blue}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` @@ -337,13 +337,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 - `var atualA = "body{color:red}";␊ + `var actual_a = "body{color:red}";␊ ␊ - var atualB = "body{color:green}";␊ + var actual_b = "body{color:green}";␊ ␊ - var atualC = "body{color:blue}";␊ + var actual_c = "body{color:blue}";␊ ␊ - var index = atualA + atualB + atualC;␊ + var index = actual_a + actual_b + actual_c;␊ ␊ export { index as default };␊ ` diff --git a/test/snapshots/test/index.test.ts.snap b/test/snapshots/test/index.test.ts.snap index f3c7102d3fc856862c3e73cb1b47f12c79f9963b..18706b385fdd5ece54e8001aac1f2476e9872972 100644 GIT binary patch literal 1056 zcmV+*1mF8XRzVZONj3milDnVehCyk&2Br`5u7%wd{7!6=J* z)@jY5eOUt<3nY+r;0*MABhc!8Y^lv9$GfX>3`y!Cv{@kA(1o_bA>)%a+>m~tB$w`H z5#gUXgilKmPLmidib`^+NzAMgSZWlZIwMYZ8gWXht$oqq?4)qG@dJxQ;6?}n$-!=~ z*Q9nvi+I+*Ux-Q+>2y5C4~Eu+T5LYgBa@#!ULL`Fbt0(D>PwfMfe*Bw4kX1*EftDnH?S~J>gnB_r?;y-rR`bp<*nu`MfE{VRE2C_a6Y>!pTHW9 zM8<+e7g`-v9yuGaY0#O!<<{F@3+wGnpxw4c89to|y87mH>#EQ+m&NoyDyB~=Zl-Lg ao%HeYpM&@70lMIS8T<=Zo?zbp8~^~FE(fvz literal 1057 zcmV++1m62WRzV!yk(X00000000B+n9YtGMG(gw5JK{e8{$Hdixc((DTuFiykcWkC_n^~g|vr= zxZPd0J3G@o>gw6pvP=Y=xpU1PxpCyig*V_4cmrMl^?YR~Gqd(goGjYKr?Fk#^{-!5 zb@%j>@yOHsn0|Td6s@!#&OV{34m|($olE%&+hTI-Eh{%oBv1I&iZJAfWsA89d%#eD_9wWrM< z7!=+6YI(ieZfBNvN-ZY_Q59_arM4q4#IvSS37kPM6|$B3Ngn#$b{l6=?2E*fQ5Na4 z+fG4!RRa_YB#^OSH4J?%(5PW#sr1!k_xXD4LXv6%9TvzA^q}M6gz%MY`>OZTg0&$6cw}*gNRcFq{PJG**Kfqjo8Fh)wzs7_7fmn`+-3suu}wqWT8J8 zG^vsCStQqvqG-62^jQ%}cf|Pd#OP3ooo6v*>eJEX`rogkzcP<6S#$$ zDwU~Oxz1ZPbXEdrPJV+ws%`m&qFir8m3i zu@QQ+oml4s23#wP%7Ub*ou!8&-r>(olz4hC=joj)Pf2GMe2v!gm7w~tCaOX%FFBun z!Y44ABat>>(1u32$|GwWn+9F^b1sC(`K@&+(CiuQX}*{XJt}UkOND4AB_tAY{9hU-N6+kY1vYjbAPm1T+Lwg4<2KnJZhTQ_e%^l0p1HRf0L@m43yb=3 zy{H$QXfIhKw?T5E!^q1XESB;2yMFq8x!iA39slGy>hIRm!_vX6a}V;_yH(sRk%=2% zP~?A%r}09}F!R-%-M)D@iPAvo4;LbTxKh5m@zJ3TLJJSs z2_tS~V4*>pemcE?IFr-*n|zA=IE}*~xxcv|Bw3IO4hQ#ArS6U` zk$94mIPHXl(W1m{C+XbhQXD6A7qhO9W?L<`|HU9DHmR#`H(OVQrun~^u23<3T5&sM bLS?0oSMz@UgL+Ud;l=#F(i36(H5>o{FgFv< From d737c5d691c65140d8cf1170ca2f5e7e1256cebd Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 10 Dec 2024 19:22:52 +0100 Subject: [PATCH 2/6] test: add unit tests for css modules --- src/insertStyle.ts | 4 +- src/types.ts | 2 +- test/index.test.ts | 178 ++++++++++++++++++------- test/snapshots/test/index.test.ts.md | 18 +++ test/snapshots/test/index.test.ts.snap | Bin 1056 -> 1133 bytes 5 files changed, 152 insertions(+), 50 deletions(-) diff --git a/src/insertStyle.ts b/src/insertStyle.ts index 1814aa0..d8edf9d 100644 --- a/src/insertStyle.ts +++ b/src/insertStyle.ts @@ -10,9 +10,7 @@ export default function insertStyle( css: string | undefined, ): string | undefined { - if (!css || typeof window === 'undefined') { - return; - } + if (!css || typeof window === 'undefined') return; const style = document.createElement('style'); style.setAttribute('type', 'text/css'); diff --git a/src/types.ts b/src/types.ts index 2acba2e..cbc3f49 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,7 +19,7 @@ export type RollupPluginSassProcessorFnOutput = css: string; /** If provided, the default export of the CSS file will be the map returned here */ - cssModules?: Record, + cssModules?: Record; // User processor might add additional exports [key: string]: unknown; diff --git a/test/index.test.ts b/test/index.test.ts index 146bcaa..b7afc8e 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -20,6 +20,7 @@ import sass from '../src/index'; import type { RollupPluginSassOutputFn, RollupPluginSassOptions, + RollupPluginSassProcessorFn, } from '../src/types'; type ApiValue = Extract; @@ -260,7 +261,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( }, }); } -// #endregion +// #endregion basic tests // #region insert option { @@ -397,7 +398,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } -// #endregion +// #endregion insert option // #region output option { @@ -425,7 +426,6 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( * Right now we can't use snapshot testing on this tests because sometimes rollup mess with the order of imports. * Detailed information can be found here: https://github.com/elycruz/rollup-plugin-sass/pull/143#issuecomment-2227274405 */ - { const title = 'should support output as function'; @@ -713,47 +713,133 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } -// { -// const title = -// 'should produces CSS modules if `cssModules` is returned from processor'; - -// const macro = test.macro<[RollupPluginSassOptions]>({ -// async exec(t, pluginOptions) { -// const outputBundle = await rollup({ -// input: 'test/fixtures/css-modules/index.js', -// plugins: [ -// sass({ -// ...pluginOptions, -// processor: async (styles, id) => { -// let cssModules = {}; -// const postcssProcessResult = await postcss([ -// postcssModules({ -// getJSON: (_, json) => { -// if (json) cssModules = json; -// }, -// }), -// ]).process(styles, { -// from: id, -// }); - -// return { css: postcssProcessResult.css, cssModules }; -// }, -// }), -// ], -// }); - -// const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); -// const result = getFirstChunkCode(output); - -// t.snapshot(result); -// }, -// title: createApiOptionTestCaseTitle, -// }); - -// test.only(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); -// test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); -// } -// #endregion +{ + const postcssModulesProcessor: RollupPluginSassProcessorFn = async ( + styles, + id, + ) => { + let cssModules = {}; + const postcssProcessResult = await postcss([ + postcssModules({ + getJSON: (_, json) => { + if (json) cssModules = json; + }, + }), + ]).process(styles, { + from: id, + }); + + return { css: postcssProcessResult.css, cssModules }; + }; + + { + const title = + 'should produces CSS modules if `cssModules` is returned from processor'; + + const macro = test.macro<[RollupPluginSassOptions]>({ + async exec(t, pluginOptions) { + const outputBundle = await rollup({ + input: 'test/fixtures/css-modules/index.js', + plugins: [ + sass({ + ...pluginOptions, + processor: postcssModulesProcessor, + }), + ], + }); + + const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); + const result = getFirstChunkCode(output); + + t.snapshot(result); + }, + title: createApiOptionTestCaseTitle, + }); + + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); + } + + // { + // const title = + // 'should produces CSS modules alongside `insertStyle` if `cssModules` is returned from processor'; + + // const macro = test.macro<[RollupPluginSassOptions]>({ + // async exec(t, pluginOptions) { + // const outputBundle = await rollup({ + // input: 'test/fixtures/css-modules/index.js', + // plugins: [ + // sass({ + // ...pluginOptions, + // insert: true, + // processor: postcssModulesProcessor, + // }), + // ], + // }); + + // const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); + + // t.is( + // output.length, + // 1, + // 'has 1 chunk (we are bundling all in one single file)', + // ); + + // const [{ moduleIds, modules }] = output; + + // t.is( + // moduleIds.filter((it) => it.endsWith('insertStyle')).length, + // 1, + // 'include insertStyle one time', + // ); + + // const actualAModuleID = moduleIds.find((it) => + // it.endsWith('actual_a.scss'), + // ) as string; + // const actualAModule = modules[actualAModuleID]; + // t.truthy(actualAModule); + // t.snapshot( + // actualAModule.code, + // 'actual_a content is compiled with insertStyle', + // ); + // }, + // title: createApiOptionTestCaseTitle, + // }); + + // test.only(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); + // test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); + // } + + { + const title = 'should throw error when CSS modules is not an object'; + + const macro = test.macro<[RollupPluginSassOptions]>({ + async exec(t, pluginOptions) { + const message = + 'You need to provide a js object as `cssModules` property. See https://github.com/elycruz/rollup-plugin-sass#processor'; + + await t.throwsAsync( + rollup({ + input: 'test/fixtures/css-modules/index.js', + plugins: [ + sass({ + ...pluginOptions, + // @ts-expect-error testing error + processor: () => ({ css: 'body {}', cssModules: 'asd' }), + }), + ], + }), + { message }, + ); + }, + title: createApiOptionTestCaseTitle, + }); + + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); + } +} +// #endregion processor option // #region node resolution { @@ -833,7 +919,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } -// #endregion +// #endregion node resolution { const title = 'should support options.runtime'; @@ -960,7 +1046,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } -// #endregion +// #endregion sourcemap { const title = 'module stylesheets graph should be added to watch list'; diff --git a/test/snapshots/test/index.test.ts.md b/test/snapshots/test/index.test.ts.md index f294f4f..b943d4b 100644 --- a/test/snapshots/test/index.test.ts.md +++ b/test/snapshots/test/index.test.ts.md @@ -250,6 +250,24 @@ Generated by [AVA](https://avajs.dev). export { color, color2, withIcssExports as default };␊ ` +## should produces CSS modules if `cssModules` is returned from processor using 'api' = 'legacy' (implicit) + +> Snapshot 1 + + `var style = {"something":"_something_ngy8h_1"};␊ + ␊ + export { style as default };␊ + ` + +## should produces CSS modules if `cssModules` is returned from processor using 'api' = 'modern' + +> Snapshot 1 + + `var style = {"something":"_something_ngy8h_1"};␊ + ␊ + export { style as default };␊ + ` + ## should resolve ~ as node_modules using 'api' = 'legacy' (implicit) > Snapshot 1 diff --git a/test/snapshots/test/index.test.ts.snap b/test/snapshots/test/index.test.ts.snap index 18706b385fdd5ece54e8001aac1f2476e9872972..2fc71e8c93e8e9d04d8983f19b071ef50482786b 100644 GIT binary patch literal 1133 zcmV-z1d{tfRzVD>m0wa8k}>{IR~NIv%qK0;H61~Dcg%Wo z56JjNS09TA00000000B+nayq^MG(gm5Fq)+0f_?=g)iP^lOPrWi@lD3C_)Pafh@#= z0(QH*?CH%+_o%BUv1J(q9C!o7b@LENJOC0`-hf9z^WlkSXU4H-;{=7| z{_^8N-_`txetYE1CBl!42nNzcf)i;S5IA6gYeD-~ zY9&cApB!!J$Hzi2%O3vo?8XZl;os(S8!tZd_r@_Zz_<;V+hx#(dS8n%Ob!(Q*RD2A zFeqAwYFcmAYIC;zLffH1R0Y<&(7NviIBzVKz!9|1MV^SO-BMS=j4z zs??4%k7x6nxu`^*POH!O(a`8XiR~wOr1I0(iz9ezl?Y0+dg-!L@BvxvCB=ybPP93y z)U=;uB4~#ZkR7{NQj@99*ytR#%ZMyxyAEaTL>UEsWF4;tv=ch4Lhp-=#=W`xpDQ!9 zD^_}U1*N43*C+3T+3ctYP24k5Sq%;{0hSF{f%(&Etadx2_SJakPMFPGIjOf>=(0Y# zqwRD;<62o%79>SYEnO7xZeVVp#M2uYPp_4EO4>8$tG}GD1l2n$qRM6Sn)BI9`1nS1 zB+>>9+Q6u=^2nNyO@+?-OHo$#%T zD2QTAr3G>A*gSkq(o!tA9ctgNhWva~o$&e_@{0*(~z zQ9Uh!@Pb1QpvQ?0;>TXNrHgZi0tQBVb2+Ur-yUXmnxD-d@+NiC9$H7LVZB-JrVm{; z9N%-gZ`P;Fp8CFBPkg1b=%obpVG@)<((Wdk(7eJ68XJz<%gGsID(QScD~rX zk2@K$NnL$;v2~Senww(!9~IO4CD(~2P*!?)^K;MJs{y*^zw`VHcC5$%v>pHe0_-z5 literal 1056 zcmV+*1mF8XRzVZONj3milDnVehCyk&2Br`5u7%wd{7!6=J* z)@jY5eOUt<3nY+r;0*MABhc!8Y^lv9$GfX>3`y!Cv{@kA(1o_bA>)%a+>m~tB$w`H z5#gUXgilKmPLmidib`^+NzAMgSZWlZIwMYZ8gWXht$oqq?4)qG@dJxQ;6?}n$-!=~ z*Q9nvi+I+*Ux-Q+>2y5C4~Eu+T5LYgBa@#!ULL`Fbt0(D>PwfMfe*Bw4kX1*EftDnH?S~J>gnB_r?;y-rR`bp<*nu`MfE{VRE2C_a6Y>!pTHW9 zM8<+e7g`-v9yuGaY0#O!<<{F@3+wGnpxw4c89to|y87mH>#EQ+m&NoyDyB~=Zl-Lg ao%HeYpM&@70lMIS8T<=Zo?zbp8~^~FE(fvz From 8e9dad0b35afa9b395763ad4d468402f750c3539 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 10 Dec 2024 20:32:23 +0100 Subject: [PATCH 3/6] =?UTF-8?q?test:=20use=20snapshots=20in=20=E2=80=9Cmod?= =?UTF-8?q?ule=20stylesheets=20graph=E2=80=9D=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fixtures/dependencies/expected.js | 2 -- test/index.test.ts | 5 +---- test/snapshots/test/index.test.ts.md | 16 ++++++++++++++++ test/snapshots/test/index.test.ts.snap | Bin 1133 -> 1215 bytes 4 files changed, 17 insertions(+), 6 deletions(-) delete mode 100644 test/fixtures/dependencies/expected.js diff --git a/test/fixtures/dependencies/expected.js b/test/fixtures/dependencies/expected.js deleted file mode 100644 index ce51f6d..0000000 --- a/test/fixtures/dependencies/expected.js +++ /dev/null @@ -1,2 +0,0 @@ -var style1 = "body{color:blue}body{color:#fff}body{color:red}"; -export default style1; diff --git a/test/index.test.ts b/test/index.test.ts index b7afc8e..e349590 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1122,10 +1122,7 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( // Test final content output // ---- - const expectedFinalContent = await fs.readFile( - 'test/fixtures/dependencies/expected.js', - ); - t.is(targetModule.code.trim(), expectedFinalContent.toString().trim()); + t.snapshot(targetModule.code, 'Final content output'); }, title: createApiOptionTestCaseTitle, }); diff --git a/test/snapshots/test/index.test.ts.md b/test/snapshots/test/index.test.ts.md index b943d4b..3275a28 100644 --- a/test/snapshots/test/index.test.ts.md +++ b/test/snapshots/test/index.test.ts.md @@ -365,3 +365,19 @@ Generated by [AVA](https://avajs.dev). ␊ export { index as default };␊ ` + +## module stylesheets graph should be added to watch list using 'api' = 'legacy' (implicit) + +> Final content output + + `var style1 = "body{color:blue}body{color:#fff}body{color:red}";␊ + export default style1;␊ + ` + +## module stylesheets graph should be added to watch list using 'api' = 'modern' + +> Final content output + + `var style1 = "body{color:blue}body{color:#fff}body{color:red}";␊ + export default style1;␊ + ` diff --git a/test/snapshots/test/index.test.ts.snap b/test/snapshots/test/index.test.ts.snap index 2fc71e8c93e8e9d04d8983f19b071ef50482786b..fcc9e6a87a300dd9be8ccf0e47576efa33819fdf 100644 GIT binary patch literal 1215 zcmV;w1VH;iRzVUC*hx^5{?p%21TWEsY$F^rC_O1B-NaBy4OgjB-`3oU7U?n9IpMqAQ4y>K_FS! z+}UZ8JMOeRi{DML5=C`74&(a+qeCTD9@is-pLH*1@J<~Dl~KKL*qQJlS?#CEi3Sd} z*>6;|pXV5?M-0dgJ*>#d3}<3=hPJbStkiZM$ktP26b7+%vKr88M@49onvu$CaFGeHY*0tcUyc*BD><~UCqwT<(Y#TBdb5Kb zbI?0l$tE@*?gr^y;oDJLNu?qKAWjN zfzcd^v;l)QG%CtGvL@1|L1q4)TW^0WthZx>0Lhmc1eMxvPsh^GBJf@8&*`}XEF>RlZ5{>7QOQ!!* zJ^CX~w3be%%OE+?R_tXBM&qP?-=DO{au&T1QXi+J43hQ^5Ppp8lor^XFl!JFFNgex)6U$594@LXTpyz4RvUP(85D@z=#_eSZENYUym<=XL`7MmAlCICp-*N^qOmd zmx!sor6@F*9XIhvk_D+?YiE0cG`gluAs*x)y6uoKUTnB+r}OUTHO-FgB&Bj0&;lQc|{k6c7cFfc4| zA(hKPU_zjXKD8b)%U$rIM68oeX+2@Co*rZcbi0i0RQt_-zdtUjn$nwaN`{)0kNrd{ d=4Hi`)Jt~8J|E7-40&3J{tpX`HldLq005Q-My&t< literal 1133 zcmV-z1d{tfRzVD>m0wa8k}>{IR~NIv%qK0;H61~Dcg%Wo z56JjNS09TA00000000B+nayq^MG(gm5Fq)+0f_?=g)iP^lOPrWi@lD3C_)Pafh@#= z0(QH*?CH%+_o%BUv1J(q9C!o7b@LENJOC0`-hf9z^WlkSXU4H-;{=7| z{_^8N-_`txetYE1CBl!42nNzcf)i;S5IA6gYeD-~ zY9&cApB!!J$Hzi2%O3vo?8XZl;os(S8!tZd_r@_Zz_<;V+hx#(dS8n%Ob!(Q*RD2A zFeqAwYFcmAYIC;zLffH1R0Y<&(7NviIBzVKz!9|1MV^SO-BMS=j4z zs??4%k7x6nxu`^*POH!O(a`8XiR~wOr1I0(iz9ezl?Y0+dg-!L@BvxvCB=ybPP93y z)U=;uB4~#ZkR7{NQj@99*ytR#%ZMyxyAEaTL>UEsWF4;tv=ch4Lhp-=#=W`xpDQ!9 zD^_}U1*N43*C+3T+3ctYP24k5Sq%;{0hSF{f%(&Etadx2_SJakPMFPGIjOf>=(0Y# zqwRD;<62o%79>SYEnO7xZeVVp#M2uYPp_4EO4>8$tG}GD1l2n$qRM6Sn)BI9`1nS1 zB+>>9+Q6u=^2nNyO@+?-OHo$#%T zD2QTAr3G>A*gSkq(o!tA9ctgNhWva~o$&e_@{0*(~z zQ9Uh!@Pb1QpvQ?0;>TXNrHgZi0tQBVb2+Ur-yUXmnxD-d@+NiC9$H7LVZB-JrVm{; z9N%-gZ`P;Fp8CFBPkg1b=%obpVG@)<((Wdk(7eJ68XJz<%gGsID(QScD~rX zk2@K$NnL$;v2~Senww(!9~IO4CD(~2P*!?)^K;MJs{y*^zw`VHcC5$%v>pHe0_-z5 From 2b4f583145acdf8be3837d049e968eb03b022f25 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 10 Dec 2024 20:51:02 +0100 Subject: [PATCH 4/6] refactor(processRenderResponse): refine namedExports code generation --- src/utils/processRenderResponse.ts | 30 ++++++++++--------------- test/snapshots/test/index.test.ts.md | 6 ++--- test/snapshots/test/index.test.ts.snap | Bin 1215 -> 1215 bytes 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/utils/processRenderResponse.ts b/src/utils/processRenderResponse.ts index 519aab3..0dc938b 100644 --- a/src/utils/processRenderResponse.ts +++ b/src/utils/processRenderResponse.ts @@ -34,9 +34,9 @@ export const processRenderResponse = ( .then( ( result: Partial, - ): [string, string, Record?] => { + ): [string, Record, Record?] => { if (!isObject(result)) { - return [result, '']; + return [result, {}]; } if (!isString(result.css)) { @@ -55,18 +55,8 @@ export const processRenderResponse = ( ); } - const { css: outCss, cssModules } = result; - - // Remove "reserved" keys to avoid to add them inside additional exports - delete result.css; - delete result.cssModules; - - const namedExports = Object.keys(result).reduce( - (agg, name) => - agg + `export const ${name} = ${JSON.stringify(result[name])};\n`, - '', - ); - return [outCss, namedExports, cssModules] as const; + const { css, cssModules, ...namedExports } = result; + return [css, namedExports, cssModules]; }, ) @@ -99,14 +89,18 @@ export const processRenderResponse = ( path.basename(fileId, path.extname(fileId)), ); - const codeOutput = [ + const codeOutput: string[] = [ ...imports, + `var ${variableName} = ${defaultExport};`, `export default ${cssModules ? JSON.stringify(cssModules) : variableName};`, - namedExports, - ].join('\n'); - return codeOutput; + ...Object.entries(namedExports).map( + ([n, v]) => `export const ${n} = ${JSON.stringify(v)};`, + ), + ]; + + return codeOutput.join('\n'); }) ); // @note do not `catch` here - let error propagate to rollup level }; diff --git a/test/snapshots/test/index.test.ts.md b/test/snapshots/test/index.test.ts.md index 3275a28..6ced3c9 100644 --- a/test/snapshots/test/index.test.ts.md +++ b/test/snapshots/test/index.test.ts.md @@ -371,13 +371,11 @@ Generated by [AVA](https://avajs.dev). > Final content output `var style1 = "body{color:blue}body{color:#fff}body{color:red}";␊ - export default style1;␊ - ` + export default style1;` ## module stylesheets graph should be added to watch list using 'api' = 'modern' > Final content output `var style1 = "body{color:blue}body{color:#fff}body{color:red}";␊ - export default style1;␊ - ` + export default style1;` diff --git a/test/snapshots/test/index.test.ts.snap b/test/snapshots/test/index.test.ts.snap index fcc9e6a87a300dd9be8ccf0e47576efa33819fdf..5d936cf6abf8a5b7ec43166d098506bee255f4ed 100644 GIT binary patch delta 989 zcmV<310wvt3BL(4K~_N^Q*L2!b7*gLAa*kf0|4`A3O60;NEZ7VCH*9%c>^8=`DdK* z+D^vaNwUgjPVRAa*Y|x@)qj52cRbDa>5o^A(Ms##(R<|T&=Vm2KpP8htP&>zrbP6} zh@dY$Bsi4T1%Z7QdKPqGrB;#@3&_=$etswfvuyXjXBS>rh<|>I&n>+8%)bi<%mCvy zWL}rSIi1t|x(O4jXj0LNq=WBsR^GdC^~m67-OCxg zRfj=kR4*KMCVWU%`)P8bfkSQf8x`%RIR@(y1F}O8D>ZU5!Sq*44a@a(@FLM|-r{aGm%*?JF>75!#D@VRT&Bu|xN_R;KC%`TG_u10q4$N+ zz9hVt)XzrmJf@Au*{08-n6~GmMB};SlIj0ckN$`gt)5K$XoJwg19rfO z8yQ$=5T;*`FM(%zxOHUi9L=!42eZ2nM^L9Ofo|5l8yGa@*$ww}tB^VeMxRA=_ATS|NM4ws@ndL5cQ6kn! zr?j3#uAaWm3g~tj+o|^J{eFL3R5hhH;gk%OUR2tTq+(uHJ4v}@N9_3!P%b9O6TKs6VA_snvsWwo@-uf| zIYHy&NzcXKZvT-oEPv1j6qspu*Nl5(cUxWUB%{%QfCF!UxNaT-i3dR9${X+~$UozZ z*LE`YW|CDlb8?TXyT0$Ms{Zq%zT;`WPk+32j8<9?k3Jw*hn@iG2ijP8Yn3<=FeRc# zMg)E7A;F=vE(q+i(6gWeE47lOSU|3}^z$Pjm}R^FJ-_hMLVxsId|~0`=l)$dUqG0(jJU5*5l67M6@qzh{gg5WGq+>Jzonnsux=-eXZlCv*{R;R0C+U zK(=8W+8z!VAAh#thV%nZa%pcCCHy-l;h&`vjuMUrMWu47Nvv6=V5w0g)tq#?*GQ)% z+uB!MoQ+f*uKmCu5m*;NAX(Vl*=dqH?zB9M-%YU+MRht3>0Lhmc1 zeMxvPsh^GBJf@8&*`}XEF>RlZ5{>7QOQ!!*J^CX~w3be%%OE+?R_tXBM&qP?-=DO{ z(C9y*R zeWU%Um^PSi_i{VUFQ*TAqZVl&tt-`_wbtrpjjkFD@4DUFt?{g5K$XoJwgLw3N3 z8yQ$=5T;*`FM(%zxO Date: Tue, 10 Dec 2024 21:00:37 +0100 Subject: [PATCH 5/6] test: add unit tests for css modules and `insert` --- src/utils/processRenderResponse.ts | 2 +- test/index.test.ts | 98 ++++++++++++------------- test/snapshots/test/index.test.ts.md | 14 ++++ test/snapshots/test/index.test.ts.snap | Bin 1215 -> 1267 bytes 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/utils/processRenderResponse.ts b/src/utils/processRenderResponse.ts index 0dc938b..4287201 100644 --- a/src/utils/processRenderResponse.ts +++ b/src/utils/processRenderResponse.ts @@ -79,7 +79,7 @@ export const processRenderResponse = ( * by rollup. */ imports.push(`import ${INSERT_STYLE_ID} from '${INSERT_STYLE_ID}';`); - cssCode = `${INSERT_STYLE_ID}(${cssCode});`; + cssCode = `${INSERT_STYLE_ID}(${cssCode})`; defaultExport = cssCode; } else if (!rollupOptions.output) { defaultExport = cssCode; diff --git a/test/index.test.ts b/test/index.test.ts index e349590..6e2d485 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -760,55 +760,55 @@ const createApiOptionTestCaseTitle: TitleFn<[RollupPluginSassOptions]> = ( test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); } - // { - // const title = - // 'should produces CSS modules alongside `insertStyle` if `cssModules` is returned from processor'; - - // const macro = test.macro<[RollupPluginSassOptions]>({ - // async exec(t, pluginOptions) { - // const outputBundle = await rollup({ - // input: 'test/fixtures/css-modules/index.js', - // plugins: [ - // sass({ - // ...pluginOptions, - // insert: true, - // processor: postcssModulesProcessor, - // }), - // ], - // }); - - // const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); - - // t.is( - // output.length, - // 1, - // 'has 1 chunk (we are bundling all in one single file)', - // ); - - // const [{ moduleIds, modules }] = output; - - // t.is( - // moduleIds.filter((it) => it.endsWith('insertStyle')).length, - // 1, - // 'include insertStyle one time', - // ); - - // const actualAModuleID = moduleIds.find((it) => - // it.endsWith('actual_a.scss'), - // ) as string; - // const actualAModule = modules[actualAModuleID]; - // t.truthy(actualAModule); - // t.snapshot( - // actualAModule.code, - // 'actual_a content is compiled with insertStyle', - // ); - // }, - // title: createApiOptionTestCaseTitle, - // }); - - // test.only(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); - // test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); - // } + { + const title = + 'should produces CSS modules alongside `insertStyle` if `cssModules` is returned from processor'; + + const macro = test.macro<[RollupPluginSassOptions]>({ + async exec(t, pluginOptions) { + const outputBundle = await rollup({ + input: 'test/fixtures/css-modules/index.js', + plugins: [ + sass({ + ...pluginOptions, + insert: true, + processor: postcssModulesProcessor, + }), + ], + }); + + const { output } = await outputBundle.generate(TEST_GENERATE_OPTIONS); + + t.is( + output.length, + 1, + 'has 1 chunk (we are bundling all in one single file)', + ); + + const [{ moduleIds, modules }] = output; + + t.is( + moduleIds.filter((it) => it.endsWith('insertStyle')).length, + 1, + 'include insertStyle one time', + ); + + const styleModuleID = moduleIds.find((it) => + it.endsWith('style.scss'), + ) as string; + const styleModule = modules[styleModuleID]; + t.truthy(styleModule); + t.snapshot( + styleModule.code, + 'style content is compiled with insertStyle', + ); + }, + title: createApiOptionTestCaseTitle, + }); + + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_LEGACY); + test(title, macro, TEST_PLUGIN_OPTIONS_DEFAULT_MODERN); + } { const title = 'should throw error when CSS modules is not an object'; diff --git a/test/snapshots/test/index.test.ts.md b/test/snapshots/test/index.test.ts.md index 6ced3c9..4e834c4 100644 --- a/test/snapshots/test/index.test.ts.md +++ b/test/snapshots/test/index.test.ts.md @@ -268,6 +268,20 @@ Generated by [AVA](https://avajs.dev). export { style as default };␊ ` +## should produces CSS modules alongside `insertStyle` if `cssModules` is returned from processor using 'api' = 'legacy' (implicit) + +> style content is compiled with insertStyle + + `insertStyle("._something_ngy8h_1{color:red}");␊ + var style = {"something":"_something_ngy8h_1"};` + +## should produces CSS modules alongside `insertStyle` if `cssModules` is returned from processor using 'api' = 'modern' + +> style content is compiled with insertStyle + + `insertStyle("._something_ngy8h_1{color:red}");␊ + var style = {"something":"_something_ngy8h_1"};` + ## should resolve ~ as node_modules using 'api' = 'legacy' (implicit) > Snapshot 1 diff --git a/test/snapshots/test/index.test.ts.snap b/test/snapshots/test/index.test.ts.snap index 5d936cf6abf8a5b7ec43166d098506bee255f4ed..45f30ac1022ffe062da461e369e015ad3df18f1f 100644 GIT binary patch literal 1267 zcmV6wbZ^qrDuAU^8WquF50phy*5J)@#5?9`UM?v%Bi6=ATcxK~d$;Ky-UETG4UsZKi z_dM)5p62`XJ3K-wt%pbNkgEevfE@VRSh%r9oCuf_;a5fkJ?SCAp|mat?6JVJpnWT~ zlBAeVuD0~kLm`-Dd;dPW^ukj3S$=Nm#b^FmI$#DEw*m7y3^t(YXfcG*eFea^r_DMT z6z%(J+-x@*Q?^c_ZQmfO0&8Aq?RWuB8%rhd2sV!4+&uP{e&y{(1CJsdM#%_9DeG~& zF%j*P3ZgMz0vQWdLwBGB8r6*~mA=yP!})X!NUA=xm@iwf0WA;vj1OCIO%8lda%r#U zCHyNR;U9$(j$)1mMX7R$Nu*h+V2M#E)q-@oTT7=n+gg`hoXtcWt_QwBBCsxkK(es4 zyIUuB+-Z51znNks^6Io5#`pV12TH7-)FXwT4lic#MimC7Q9XCqsqg_=JxG!h4IFB- zU#n<8&M?>r8IT=%Sdx<|&e-S_ZRY`5s_i0>Z6wGj@FVMZH=wo9VIB28&tTk|ivOuF zQ@diMw<{nmm2h$J-kYqBiqOP0BbC+QA`@U)zlxYYKZ?<=X3)M8PrVyP^I8t-^)`CU zLGNfaUC_8z7L^4_QBg||McfTcO_X?gJ>%)sGEYf)=6pHx`AVdEtD;o7XkK!Cwi11O zqd5|30|sqiRG4{WO{h(c%KR;}-~O80Z^r`7y3wBIqoL4F(SAEth^Aj)G-EHFcLTjs znRIE9KjBPgg;zTdklQ-p{X0YY&pFyDt`%A6X&wIE(|XKml6$=Oh=3zS zJ?cP0ZRlm93?4%DJ z)gRt*JGYwSc~5=cE{0#}Dta!Yew;;$d0O>J3WV-d$K732Fq310m*M?g=Gd^7=bAmQ zG%CuJoH=g9L#iA&g7Z}_pCfrSU`fDt#+w@@QYzZ9qFVtFPh`Z9Ns?@xFbr0{R9 z1zsSg*0!R+9M=sVNwOdnZ13(&kcRIA5{L&Gh;Az&jJg_bTggWMScv0*u3_3FM6;a^ zcI%_v4A>;EzBt>w$`#F3GCf7f^lr($wF#7!1H5{#_+~YMo{|@edvO}Z$ww}tCFmRG zyO7A`ATS|NgzxkZndL5cVItP(QbneozDx_~RuS8&=hu3@-Z-ynLT|z;o+@d#><3aY dFRh)pT++Lov!PttUS2fM{{S~s(;E09005N8WikK& literal 1215 zcmV;w1VH;iRzV_P00000000B+S ze}33^Jk9s%k5`V-O6%d#d*tfS6CnLS8w+o&5+?$tMD)mrpf5cnIF!}}fqfQw7Ia{x zR+1D8$kmp9ekcU9Z1=xs7hYJ1ev8j7y!gz&3kS>q<2Gbom%%!;94&@0+EV~rd)lml zLDAV$<5p*BY0B0qwH+8lRbtIctsO7KX=AAb9>MwvoLeW}vPa%oTEe4Phf&&tQO0`Q zS(=FUSq;%xAc2eptD)y>fkyRWOQo-M{CGAULy~F$Z5GHjtV7$w0pr6q+>n0YNiOZp zqJ)3vB>b~f!coG}pr}+XHHkH=6f8B0q&g>^?lsaW$+q@Y7iS|ChigADNCehJ5J(m_ zcXpcOjyo;S;&)T5L{XiN!}$Kd=unB3NA<|yXWh#gyj6!mWmGR5b|!pCR{LpkqJcwg z_8S%Lr#S}e5d*SA4=Zvq!Sq*44a@a(@FLM|-r{aGm z%*?JF>75!#D<~UCPVK;(Y#TBdb5Kb zbI?0l$tE@*?gr^y;D=FLNu?qKAWjN zfzcd^v;l)QG%CtGvL@1|L1q4)TW^0WthZx+2|;9wql=?GYhI ziub6#7GZS3A^XtdMB7QT7j5a1*r9;F(f(9S8_c(Rxt-?c(}%oKi?ol{m1@vhYjv|m zR}F@D-R|wyc-B+j_vgc}au&T1QXi(I43hQ^5Ppp8lor^XFl!JFFNgex)6U$594@LXTpyz4RvUP(82?Dz=#_eSZENYUym<=XL`7MnY+mMCp-*N^qOmd zmx!sor6@F*9XIhvk_D+?YiE0cG`gluAs*x)y6uoKUTnB+r}OV)Ax;9iirL&9k9IcL z%};i6VAH(%YQA|@D4OeJdWw?i{fg^E6Dlixy#Cztc0GZflJ7jbNg5`}M=qiz7#J3~ zkjmvCFd Date: Tue, 10 Dec 2024 21:25:10 +0100 Subject: [PATCH 6/6] docs: add processor `cssModules` output examples --- README.md | 65 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2bbe85e..8128300 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Add `allowSyntheticDefaultImports`, or `esModuleInterop` (enables `allowSyntheti } ``` -Reference: (https://www.typescriptlang.org/tsconfig#esModuleInterop) +[`esModuleInterop` reference](https://www.typescriptlang.org/tsconfig#esModuleInterop) Write rollup.config.ts: @@ -113,7 +113,7 @@ sass({ }); ``` -The `processor` also support object result. Reverse `css` filLed for stylesheet, the rest of the properties can be customized. +The `processor` also support object result. Reverse `css` filled for stylesheet, the rest of the properties can be customized. ```js sass({ @@ -133,29 +133,60 @@ Otherwise, you could do: import style, { foo, bar } from 'stylesheet'; ``` +#### Create CSS modules using processor `cssModules` output + +When returning a `cssModules` property inside a processor's output, +the plugin will change the module's default export to the value +of `cssModules` instead of the compiled CSS code. + +The following example uses [`postcss-modules`](https://www.npmjs.com/package/postcss-modules) to create css modules: + +```js +import postcss from 'postcss'; +import postcssModules from 'postcss-modules'; + +sass({ + async processor(css, id) { + let cssModules = {}; + const postcssProcessResult = await postcss([ + postcssModules({ + getJSON: (_, json) => { + if (json) cssModules = json; + }, + }), + ]).process(css, { from: id }); + + return { css: postcssProcessResult.css, cssModules }; + }, +}); +``` + +Which allows you to write something like: + +```js +import style from 'stylesheet'; + +style['some-classes']; +``` + #### Exporting sass variable to \*.js -Example showing how to use [icss-utils](https://www.npmjs.com/package/icss-utils) to extract resulting sass vars -to your \*.js bundle: +Example showing how to use [`icss-utils`](https://www.npmjs.com/package/icss-utils) to extract resulting sass vars to your \*.js bundle: ```js const config = { input: 'test/fixtures/processor-promise/with-icss-exports.js', plugins: [ sass({ - processor: (css) => - new Promise((resolve, reject) => { - const pcssRootNodeRslt = postcss.parse(css), - extractedIcss = extractICSS(pcssRootNodeRslt, true), - cleanedCss = pcssRootNodeRslt.toString(), - out = Object.assign({}, extractedIcss.icssExports, { - css: cleanedCss, - }); - // console.table(extractedIcss); - // console.log(out); - resolve(out); - }), - options: sassOptions, + processor: (css) => { + const pcssRootNodeRslt = postcss.parse(css); + const extractedIcss = extractICSS(pcssRootNodeRslt, true); + const cleanedCss = pcssRootNodeRslt.toString(); + const out = { css: cleanedCss, ...extractedIcss.icssExports }; + // console.table(extractedIcss); + // console.log(out); + return out; + }, }), ], };