From e0b1a0f07a8f51de3e57759a7202dec0f7dd31b4 Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Mon, 16 Dec 2024 14:28:57 +0800 Subject: [PATCH] fix: escape css (#8698) --- .../src/parser_and_generator/mod.rs | 37 +- crates/rspack_plugin_css/src/utils.rs | 13 +- .../tests/__snapshots__/Config.test.js.snap | 66 +-- .../configCases/css/escape-ident/index.js | 9 +- .../css/escape-ident/index.module.css | 6 +- .../css/escape-ident/rspack.config.js | 10 +- .../css/escape-ident/test.config.js | 5 + .../index.js | 9 +- .../rspack.config.js | 36 +- .../style.module.css | 8 + .../ConfigTestCases.basictest.js.snap | 404 ++++++++++++++++++ .../configCases/css/escape-unescape/index.js | 15 + .../css/escape-unescape/style.modules.css | 134 ++++++ .../css/escape-unescape/test.config.js | 11 + .../css/escape-unescape/webpack.config.js | 17 + 15 files changed, 723 insertions(+), 57 deletions(-) create mode 100644 packages/rspack-test-tools/tests/configCases/css/escape-ident/test.config.js create mode 100644 tests/webpack-test/configCases/css/escape-unescape/index.js create mode 100644 tests/webpack-test/configCases/css/escape-unescape/style.modules.css create mode 100644 tests/webpack-test/configCases/css/escape-unescape/test.config.js create mode 100644 tests/webpack-test/configCases/css/escape-unescape/webpack.config.js diff --git a/crates/rspack_plugin_css/src/parser_and_generator/mod.rs b/crates/rspack_plugin_css/src/parser_and_generator/mod.rs index 653aaeff9be..cdca4895fcb 100644 --- a/crates/rspack_plugin_css/src/parser_and_generator/mod.rs +++ b/crates/rspack_plugin_css/src/parser_and_generator/mod.rs @@ -215,6 +215,8 @@ impl ParserAndGenerator for CssParserAndGenerator { css_module_lexer::Dependency::LocalClass { name, range, .. } | css_module_lexer::Dependency::LocalId { name, range, .. } => { let (_prefix, name) = name.split_at(1); // split '#' or '.' + let name = unescape(name); + let local_ident = LocalIdentOptions::new( resource_data, self @@ -223,13 +225,13 @@ impl ParserAndGenerator for CssParserAndGenerator { .expect("should have local_ident_name for module_type css/auto or css/module"), compiler_options, ) - .get_local_ident(name); + .get_local_ident(&name); let convention = self .convention .as_ref() .expect("should have local_ident_name for module_type css/auto or css/module"); let exports = self.exports.get_or_insert_default(); - let convention_names = export_locals_convention(name, convention); + let convention_names = export_locals_convention(&name, convention); for name in convention_names.iter() { update_css_exports( exports, @@ -249,6 +251,7 @@ impl ParserAndGenerator for CssParserAndGenerator { ))); } css_module_lexer::Dependency::LocalKeyframes { name, range, .. } => { + let name = unescape(name); let local_ident = LocalIdentOptions::new( resource_data, self @@ -257,13 +260,13 @@ impl ParserAndGenerator for CssParserAndGenerator { .expect("should have local_ident_name for module_type css/auto or css/module"), compiler_options, ) - .get_local_ident(name); + .get_local_ident(&name); let exports = self.exports.get_or_insert_default(); let convention = self .convention .as_ref() .expect("should have local_ident_name for module_type css/auto or css/module"); - let convention_names = export_locals_convention(name, convention); + let convention_names = export_locals_convention(&name, convention); for name in convention_names.iter() { update_css_exports( exports, @@ -285,6 +288,7 @@ impl ParserAndGenerator for CssParserAndGenerator { ))); } css_module_lexer::Dependency::LocalKeyframesDecl { name, range, .. } => { + let name = unescape(name); let local_ident = LocalIdentOptions::new( resource_data, self @@ -293,13 +297,13 @@ impl ParserAndGenerator for CssParserAndGenerator { .expect("should have local_ident_name for module_type css/auto or css/module"), compiler_options, ) - .get_local_ident(name); + .get_local_ident(&name); let exports = self.exports.get_or_insert_default(); let convention = self .convention .as_ref() .expect("should have local_ident_name for module_type css/auto or css/module"); - let convention_names = export_locals_convention(name, convention); + let convention_names = export_locals_convention(&name, convention); for name in convention_names.iter() { update_css_exports( exports, @@ -324,6 +328,15 @@ impl ParserAndGenerator for CssParserAndGenerator { from, range, } => { + let local_classes = local_classes + .into_iter() + .map(|s| unescape(s).to_string()) + .collect::>(); + let names = names + .into_iter() + .map(|s| unescape(s).to_string()) + .collect::>(); + let mut dep_id = None; if let Some(from) = from && from != "global" @@ -331,31 +344,31 @@ impl ParserAndGenerator for CssParserAndGenerator { let from = from.trim_matches(|c| c == '\'' || c == '"'); let dep = CssComposeDependency::new( from.to_string(), - names.iter().map(|s| (*s).into()).collect(), + names.iter().map(|s| s.to_owned().into()).collect(), DependencyRange::new(range.start, range.end), ); dep_id = Some(*dep.id()); dependencies.push(Box::new(dep)); } else if from.is_none() { dependencies.push(Box::new(CssSelfReferenceLocalIdentDependency::new( - names.iter().map(|s| (*s).into()).collect(), + names.iter().map(|s| s.to_string()).collect(), vec![], ))); } let exports = self.exports.get_or_insert_default(); for name in names { - for &local_class in local_classes.iter() { - if let Some(existing) = exports.get(name) + for local_class in local_classes.iter() { + if let Some(existing) = exports.get(name.as_str()) && from.is_none() { let existing = existing.clone(); exports - .get_mut(local_class) + .get_mut(local_class.as_str()) .expect("composes local class must already added to exports") .extend(existing); } else { exports - .get_mut(local_class) + .get_mut(local_class.as_str()) .expect("composes local class must already added to exports") .insert(CssExport { ident: name.to_string(), diff --git a/crates/rspack_plugin_css/src/utils.rs b/crates/rspack_plugin_css/src/utils.rs index ce55b8a521c..3410b804bfc 100644 --- a/crates/rspack_plugin_css/src/utils.rs +++ b/crates/rspack_plugin_css/src/utils.rs @@ -192,7 +192,7 @@ pub fn stringified_exports<'a>( let content = elements .iter() .map(|CssExport { ident, from, id: _ }| match from { - None => json_stringify(&unescape(ident)), + None => json_stringify(&ident), Some(from_name) => { let from = module .get_dependencies() @@ -233,7 +233,7 @@ pub fn stringified_exports<'a>( writeln!( stringified_exports, " {}: {},", - json_stringify(&unescape(key)), + json_stringify(&key), content ) .map_err(|e| error!(e.to_string()))?; @@ -266,7 +266,7 @@ pub fn css_modules_exports_to_concatenate_module_string<'a>( let content = elements .iter() .map(|CssExport { ident, from, id: _ }| match from { - None => json_stringify(&unescape(ident)), + None => json_stringify(&ident), Some(from_name) => { let from = module .get_dependencies() @@ -297,7 +297,7 @@ pub fn css_modules_exports_to_concatenate_module_string<'a>( format!( "{}({from})[{}]", RuntimeGlobals::REQUIRE, - json_stringify(&unescape(ident)) + json_stringify(&ident) ) } }) @@ -306,7 +306,7 @@ pub fn css_modules_exports_to_concatenate_module_string<'a>( let mut identifier = to_identifier(key); let mut i = 0; while used_identifiers.contains(&identifier) { - identifier = Cow::Owned(format!("{key}{}", itoa!(i))); + identifier = Cow::Owned(format!("{identifier}{}", itoa!(i))); i += 1; } // TODO: conditional support `const or var` after we finished runtimeTemplate utils @@ -314,7 +314,7 @@ pub fn css_modules_exports_to_concatenate_module_string<'a>( "var {identifier} = {content};\n" ))); used_identifiers.insert(identifier.clone()); - scope.register_export(unescape(key).as_ref().into(), identifier.into_owned()); + scope.register_export(key.into(), identifier.into_owned()); } Ok(()) } @@ -330,6 +330,7 @@ static UNESCAPE: LazyLock = static DATA: LazyLock = LazyLock::new(|| Regex::new(r"^(?i)data:").expect("Invalid RegExp")); +// `\/foo` in css should be treated as `foo` in js pub fn unescape(s: &str) -> Cow { UNESCAPE.replace_all(s.as_ref(), |caps: &Captures| { caps diff --git a/packages/rspack-test-tools/tests/__snapshots__/Config.test.js.snap b/packages/rspack-test-tools/tests/__snapshots__/Config.test.js.snap index 5b8eeb86b5f..551ba2eaa1a 100644 --- a/packages/rspack-test-tools/tests/__snapshots__/Config.test.js.snap +++ b/packages/rspack-test-tools/tests/__snapshots__/Config.test.js.snap @@ -54,46 +54,46 @@ Object { exports[`config config/builtins/css-modules-local-ident-name-hash exported tests css modules localIdentName with hash 1`] = ` Object { - #: c36b985d54c6917, - ##: ab513cc8abd7e7d, - #.#.#: b6aa9e623eb8be, - #fake-id: d65fd648c910d0f, - ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.: fe85c4cd33dc7b53, + #: ab5b3430521fed0b, + ##: cb547534b9bb8e, + #.#.#: a4c570203a26b7a, + #fake-id: f7b6eb7b0e0c18, + ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.: c40d0c7abfea21c2, -a-b-c-: b1d2002fed1364, -a0-34a___f: bd6992764ef, - .: f8dc72200543c02, - 123: fc54982b169de010, - 1a2b3c: e0d288534c575c7c, - :): ce45aa5fbb24, - :\`(: c3a47328b233, - :hover: c9cb40d597d6145, - :hover:focus:active: adb078f8a010, - <><<<>><>: e57bed6057bf, -

: d4d795aa031981a3, - ?: ca312bbe575926f7, - @: e73735f9a6fa1e2a, - B&W?: c28b11d8276955d9, - [attr=value]: aefb3cf784f22f09, - _: b892aeed406cef6, + .: aba9a87983bb4, + 123: fc0eed74768cf0, + 1a2b3c: cd4a1680594, + :): f173228435cfb0b1, + :\`(: c028ead51dd2190, + :hover: bcf55b240a0c8c3, + :hover:focus:active: a9b6b582b414966b, + <><<<>><>: c75f56ad7787952, +

: aeefcae4950d33f5, + ?: e40713fc7c65c3f, + @: cd391b9e31254d, + B&W?: daaf197c50aa167, + [attr=value]: fa72982cc3b312, + _: bd050c4fe, _test: f95d7802389a, className: b88ec9c3ddaa088, - f!o!o: bc20be6f8aa6ba2b, - f'o'o: a1ad39e5bcb2a, - f*o*o: ee490440b4d27dd, - f+o+o: b2f2e8203ab92e0, - f/o/o: e8e6467eba546855, - f\\o\\o: ac4b388a402f1b, - foo.bar: bf09604a510669, - foo/bar: f06b4ed00041408a, - foo/bar/baz: b62901907350b529, - foo\\bar: cf1721b3e29, - foo\\bar\\baz: db46f3e1d8ecc82b, - f~o~o: e22953bae58b7, - m_x_@: eede355cc6b0366, + f!o!o: ae3ae8c7841a7dc, + f'o'o: df77f78fe66aae2, + f*o*o: a7fec00e3ce42886, + f+o+o: eb6cf0061b2d78e, + f/o/o: d0e703c722b26d0c, + f\\o\\o: a496c6306a14d13d, + foo.bar: cd2e4302e8b6edf3, + foo/bar: cd90db0f2125295, + foo/bar/baz: f253f688730bc76e, + foo\\bar: eb2a9acb231, + foo\\bar\\baz: b41adbd8ba363134, + f~o~o: b5f0b0aac1e4e, + m_x_@: deacd91fcb5633e3, someId: f39bfe4a4606a57c, subClass: dbb85e97d7af8a70, test: db0a8ac9537cb87c, - {}: c849d7d50310c200, + {}: b674276dd02fd15, ©: fc4038317, “‘’”: b1f8cf023766, ⌘⌥: cad802, diff --git a/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.js b/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.js index 4e304f0b706..9d2083e7a33 100644 --- a/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.js +++ b/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.js @@ -1,10 +1,17 @@ import * as styles from "./index.module.css"; it("should generate correct exports", () => { + const fs = __non_webpack_require__('fs') + const path = __non_webpack_require__('path') expect(styles).toEqual( nsObj({ a: '"aaa" 123', - b: "multiple lines bbb" + b: "multiple lines bbb", + 'a/b': 'a/b-./' }) ); + + const css = fs.readFileSync(path.resolve(__dirname, './bundle0.css')).toString() + const escape = css.replaceAll('\\', '') + expect(escape).toContain(styles['a/b']) }); diff --git a/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.module.css b/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.module.css index 45b39c05e78..c151a106ad2 100644 --- a/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.module.css +++ b/packages/rspack-test-tools/tests/configCases/css/escape-ident/index.module.css @@ -5,4 +5,8 @@ comment2 comment3 */ bbb/* comment4 */ -} \ No newline at end of file +} + +.a\/b { + color: red +}; \ No newline at end of file diff --git a/packages/rspack-test-tools/tests/configCases/css/escape-ident/rspack.config.js b/packages/rspack-test-tools/tests/configCases/css/escape-ident/rspack.config.js index 437774d2216..190c1c403f2 100644 --- a/packages/rspack-test-tools/tests/configCases/css/escape-ident/rspack.config.js +++ b/packages/rspack-test-tools/tests/configCases/css/escape-ident/rspack.config.js @@ -2,5 +2,13 @@ module.exports = { experiments: { css: true - } + }, + module: { + generator: { + 'css/auto': { + exportsOnly: false, + localIdentName: '[local]-[path]' + } + } + }, }; diff --git a/packages/rspack-test-tools/tests/configCases/css/escape-ident/test.config.js b/packages/rspack-test-tools/tests/configCases/css/escape-ident/test.config.js new file mode 100644 index 00000000000..644d363b45f --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/css/escape-ident/test.config.js @@ -0,0 +1,5 @@ +module.exports = { + findBundle() { + return ['bundle0.js'] + } +} diff --git a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/index.js b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/index.js index 3ecfe06d7e7..caea9c85ccb 100644 --- a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/index.js +++ b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/index.js @@ -4,7 +4,12 @@ it("should remove unused local idents", async () => { const fs = __non_webpack_require__("fs"); const path = __non_webpack_require__("path"); expect(styles.a).toBe("./style.module-a"); + expect(styles['local/used']).toBe("./style.module-local/used"); - const css = await fs.promises.readFile(path.resolve(__dirname, "./bundle0.css"), "utf-8"); - expect(css).not.toContain(".module-b") + if (!EXPORTS_ONLY) { + const css = await fs.promises.readFile(path.resolve(__dirname, "./bundle0.css"), "utf-8"); + expect(css).not.toContain(".module-b") + expect(css).toContain("local\\/used") + expect(css).not.toContain("local\\/unused") + } }) diff --git a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/rspack.config.js b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/rspack.config.js index 7759a72c804..e4720a9d612 100644 --- a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/rspack.config.js +++ b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/rspack.config.js @@ -1,7 +1,7 @@ const rspack = require("@rspack/core"); /** @type {import("@rspack/core").Configuration} */ -module.exports = { +const common = { target: 'web', node: { __dirname: false, @@ -25,3 +25,37 @@ module.exports = { css: true } }; + +module.exports = [ + { + ...common, + plugins: [ + new rspack.DefinePlugin({ + EXPORTS_ONLY: false + }) + ] + }, + { + ...common, + plugins: [ + new rspack.DefinePlugin({ + EXPORTS_ONLY: true + }) + ], + module: { + generator: { + "css/auto": { + localIdentName: "[path][name]-[local]", + exportsOnly: true, + } + } + }, + optimization: { + minimize: true, + concatenateModules: true, + minimizer: [ + new rspack.LightningCssMinimizerRspackPlugin(), + ] + }, + } +] diff --git a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/style.module.css b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/style.module.css index 06d3aae76f6..7e355c9a326 100644 --- a/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/style.module.css +++ b/packages/rspack-test-tools/tests/configCases/css/minify-lightning-css-remove-unused-local-idents/style.module.css @@ -5,3 +5,11 @@ .b { color: blue; } + +.local\/used { + color: red; +} + +.local\/unused { + color: blue +} diff --git a/tests/webpack-test/__snapshots__/ConfigTestCases.basictest.js.snap b/tests/webpack-test/__snapshots__/ConfigTestCases.basictest.js.snap index 97ff7de83ad..86e0966a082 100644 --- a/tests/webpack-test/__snapshots__/ConfigTestCases.basictest.js.snap +++ b/tests/webpack-test/__snapshots__/ConfigTestCases.basictest.js.snap @@ -1,5 +1,409 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`ConfigTestCases css escape-unescape exported tests should work with URLs in CSS: classes 1`] = ` +Object { + "#": "-_style_modules_css-#", + "##": "-_style_modules_css-##", + "#.#.#": "-_style_modules_css-#.#.#", + "#fake-id": "-_style_modules_css-#fake-id", + "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.": "-_style_modules_css-++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", + "-a-b-c-": "-_style_modules_css--a-b-c-", + "-a0-34a___f": "-_style_modules_css--a0-34a___f", + ".": "-_style_modules_css-.", + "123": "-_style_modules_css-123", + "1a2b3c": "-_style_modules_css-1a2b3c", + ":)": "-_style_modules_css-:)", + ":\`(": "-_style_modules_css-:\`(", + ":hover": "-_style_modules_css-:hover", + ":hover:focus:active": "-_style_modules_css-:hover:focus:active", + "<><<<>><>": "-_style_modules_css-<><<<>><>", + "

": "-_style_modules_css-

", + "?": "-_style_modules_css-?", + "@": "-_style_modules_css-@", + "B&W?": "-_style_modules_css-B&W?", + "[attr=value]": "-_style_modules_css-[attr=value]", + "_": "-_style_modules_css-_", + "_test": "-_style_modules_css-_test", + "class": "-_style_modules_css-class", + "className": "-_style_modules_css-className", + "f!o!o": "-_style_modules_css-f!o!o", + "f'o'o": "-_style_modules_css-f'o'o", + "f*o*o": "-_style_modules_css-f*o*o", + "f+o+o": "-_style_modules_css-f+o+o", + "f/o/o": "-_style_modules_css-f/o/o", + "f@oo": "-_style_modules_css-f@oo", + "f\\\\o\\\\o": "-_style_modules_css-f\\\\o\\\\o", + "foo.bar": "-_style_modules_css-foo.bar", + "foo/bar": "-_style_modules_css-foo/bar", + "foo/bar/baz": "-_style_modules_css-foo/bar/baz", + "foo\\\\bar": "-_style_modules_css-foo\\\\bar", + "foo\\\\bar\\\\baz": "-_style_modules_css-foo\\\\bar\\\\baz", + "f~o~o": "-_style_modules_css-f~o~o", + "m_x_@": "-_style_modules_css-m_x_@", + "someId": "-_style_modules_css-someId", + "subClass": "-_style_modules_css-subClass", + "test": "-_style_modules_css-test", + "{}": "-_style_modules_css-{}", + "©": "-_style_modules_css-©", + "“‘’”": "-_style_modules_css-“‘’”", + "⌘⌥": "-_style_modules_css-⌘⌥", + "☺☃": "-_style_modules_css-☺☃", + "♥": "-_style_modules_css-♥", + "𝄞♪♩♫♬": "-_style_modules_css-𝄞♪♩♫♬", + "💩": "-_style_modules_css-💩", + "😍": "-_style_modules_css-😍", +} +`; + +exports[`ConfigTestCases css escape-unescape exported tests should work with URLs in CSS: classes 2`] = ` +Object { + "#": "-_style_modules_css-#", + "##": "-_style_modules_css-##", + "#.#.#": "-_style_modules_css-#.#.#", + "#fake-id": "-_style_modules_css-#fake-id", + "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.": "-_style_modules_css-++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", + "-a-b-c-": "-_style_modules_css--a-b-c-", + "-a0-34a___f": "-_style_modules_css--a0-34a___f", + ".": "-_style_modules_css-.", + "123": "-_style_modules_css-123", + "1a2b3c": "-_style_modules_css-1a2b3c", + ":)": "-_style_modules_css-:)", + ":\`(": "-_style_modules_css-:\`(", + ":hover": "-_style_modules_css-:hover", + ":hover:focus:active": "-_style_modules_css-:hover:focus:active", + "<><<<>><>": "-_style_modules_css-<><<<>><>", + "

": "-_style_modules_css-

", + "?": "-_style_modules_css-?", + "@": "-_style_modules_css-@", + "B&W?": "-_style_modules_css-B&W?", + "[attr=value]": "-_style_modules_css-[attr=value]", + "_": "-_style_modules_css-_", + "_test": "-_style_modules_css-_test", + "class": "-_style_modules_css-class", + "className": "-_style_modules_css-className", + "f!o!o": "-_style_modules_css-f!o!o", + "f'o'o": "-_style_modules_css-f'o'o", + "f*o*o": "-_style_modules_css-f*o*o", + "f+o+o": "-_style_modules_css-f+o+o", + "f/o/o": "-_style_modules_css-f/o/o", + "f@oo": "-_style_modules_css-f@oo", + "f\\\\o\\\\o": "-_style_modules_css-f\\\\o\\\\o", + "foo.bar": "-_style_modules_css-foo.bar", + "foo/bar": "-_style_modules_css-foo/bar", + "foo/bar/baz": "-_style_modules_css-foo/bar/baz", + "foo\\\\bar": "-_style_modules_css-foo\\\\bar", + "foo\\\\bar\\\\baz": "-_style_modules_css-foo\\\\bar\\\\baz", + "f~o~o": "-_style_modules_css-f~o~o", + "m_x_@": "-_style_modules_css-m_x_@", + "someId": "-_style_modules_css-someId", + "subClass": "-_style_modules_css-subClass", + "test": "-_style_modules_css-test", + "{}": "-_style_modules_css-{}", + "©": "-_style_modules_css-©", + "“‘’”": "-_style_modules_css-“‘’”", + "⌘⌥": "-_style_modules_css-⌘⌥", + "☺☃": "-_style_modules_css-☺☃", + "♥": "-_style_modules_css-♥", + "𝄞♪♩♫♬": "-_style_modules_css-𝄞♪♩♫♬", + "💩": "-_style_modules_css-💩", + "😍": "-_style_modules_css-😍", +} +`; + +exports[`ConfigTestCases css escape-unescape exported tests should work with URLs in CSS: css 1`] = ` +Array [ + "/* #region \\"./style.modules.css\\" */ +/* +- type: css/auto +*/ +._-_style_modules_css-class { + color: red; +} + +._-_style_modules_css-class { + background: blue; +} + +._-_style_modules_css-test { + background: red; +} + +._-_style_modules_css-_test { + background: blue; +} + +._-_style_modules_css-className { + background: red; +} + +#_-_style_modules_css-someId { + background: green; +} + +._-_style_modules_css-className ._-_style_modules_css-subClass { + color: green; +} + +#_-_style_modules_css-someId ._-_style_modules_css-subClass { + color: blue; +} + +._-_style_modules_css--a0-34a___f { + color: red; +} + +._-_style_modules_css-m_x_\\\\@ { + margin-left: auto !important; + margin-right: auto !important; +} + +._-_style_modules_css-B\\\\&W\\\\? { + margin-left: auto !important; + margin-right: auto !important; +} + +/* matches elements with class=\\":\`(\\" */ +._-_style_modules_css-\\\\:\\\\\`\\\\( { + color: aqua; +} + +/* matches elements with class=\\"1a2b3c\\" */ +._-_style_modules_css-1a2b3c { + color: aliceblue; +} + +/* matches the element with id=\\"#fake-id\\" */ +#_-_style_modules_css-\\\\#fake-id { + color: antiquewhite; +} + +/* matches the element with id=\\"-a-b-c-\\" */ +#_-_style_modules_css--a-b-c- { + color: azure; +} + +/* matches the element with id=\\"©\\" */ +#_-_style_modules_css-© { + color: black; +} + +._-_style_modules_css-♥ { background: lime; } +._-_style_modules_css-© { background: lime; } +._-_style_modules_css-\\\\😍 { background: lime; } +._-_style_modules_css-“‘’” { background: lime; } +._-_style_modules_css-☺☃ { background: lime; } +._-_style_modules_css-⌘⌥ { background: lime; } +._-_style_modules_css-\\\\𝄞♪♩♫♬ { background: lime; } +._-_style_modules_css-\\\\💩 { background: lime; } +._-_style_modules_css-\\\\? { background: lime; } +._-_style_modules_css-\\\\@ { background: lime; } +._-_style_modules_css-\\\\. { background: lime; } +._-_style_modules_css-\\\\:\\\\) { background: lime; } +._-_style_modules_css-\\\\:\\\\\`\\\\( { background: lime; } +._-_style_modules_css-123 { background: lime; } +._-_style_modules_css-1a2b3c { background: lime; } +._-_style_modules_css-\\\\ { background: lime; } +._-_style_modules_css-\\\\<\\\\>\\\\<\\\\<\\\\<\\\\>\\\\>\\\\<\\\\> { background: lime; } +._-_style_modules_css-\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\[\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\>\\\\+\\\\<\\\\<\\\\<\\\\<-\\\\]\\\\>\\\\+\\\\+\\\\.\\\\>\\\\+\\\\.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\.\\\\+\\\\+\\\\+\\\\.\\\\>\\\\+\\\\+\\\\.\\\\<\\\\<\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\>\\\\.\\\\+\\\\+\\\\+\\\\.------\\\\.--------\\\\.\\\\>\\\\+\\\\.\\\\>\\\\. { background: lime; } +._-_style_modules_css-\\\\# { background: lime; } +._-_style_modules_css-\\\\#\\\\# { background: lime; } +._-_style_modules_css-\\\\#\\\\.\\\\#\\\\.\\\\# { background: lime; } +._-_style_modules_css-_ { background: lime; } +._-_style_modules_css-\\\\{\\\\} { background: lime; } +._-_style_modules_css-\\\\#fake-id { background: lime; } +._-_style_modules_css-foo\\\\.bar { background: lime; } +._-_style_modules_css-\\\\:hover { background: lime; } +._-_style_modules_css-\\\\:hover\\\\:focus\\\\:active { background: lime; } +._-_style_modules_css-\\\\[attr\\\\=value\\\\] { background: lime; } +._-_style_modules_css-f\\\\/o\\\\/o { background: lime; } +._-_style_modules_css-f\\\\\\\\o\\\\\\\\o { background: lime; } +._-_style_modules_css-f\\\\*o\\\\*o { background: lime; } +._-_style_modules_css-f\\\\!o\\\\!o { background: lime; } +._-_style_modules_css-f\\\\'o\\\\'o { background: lime; } +._-_style_modules_css-f\\\\~o\\\\~o { background: lime; } +._-_style_modules_css-f\\\\+o\\\\+o { background: lime; } + +._-_style_modules_css-foo\\\\/bar { + background: hotpink; +} + +._-_style_modules_css-foo\\\\\\\\bar { + background: hotpink; +} + +._-_style_modules_css-foo\\\\/bar\\\\/baz { + background: hotpink; +} + +._-_style_modules_css-foo\\\\\\\\bar\\\\\\\\baz { + background: hotpink; +} + +:root { + --main-bg-color: red; + --main-bg-color-\\\\@2: blue; +} + +details { + background-color: var(--main-bg-color); + background-color: var(--main-bg-color-\\\\@2); +} + +@keyframes _-_style_modules_css-f\\\\@oo { from { color: red; } to { color: blue; } } + +/* #endregion \\"./style.modules.css\\" */ + +", +] +`; + +exports[`ConfigTestCases css escape-unescape exported tests should work with URLs in CSS: css 2`] = ` +Array [ + "/* #region \\"./style.modules.css\\" */ +/* +- type: css/auto +*/ +._-_style_modules_css-class { + color: red; +} + +._-_style_modules_css-class { + background: blue; +} + +._-_style_modules_css-test { + background: red; +} + +._-_style_modules_css-_test { + background: blue; +} + +._-_style_modules_css-className { + background: red; +} + +#_-_style_modules_css-someId { + background: green; +} + +._-_style_modules_css-className ._-_style_modules_css-subClass { + color: green; +} + +#_-_style_modules_css-someId ._-_style_modules_css-subClass { + color: blue; +} + +._-_style_modules_css--a0-34a___f { + color: red; +} + +._-_style_modules_css-m_x_\\\\@ { + margin-left: auto !important; + margin-right: auto !important; +} + +._-_style_modules_css-B\\\\&W\\\\? { + margin-left: auto !important; + margin-right: auto !important; +} + +/* matches elements with class=\\":\`(\\" */ +._-_style_modules_css-\\\\:\\\\\`\\\\( { + color: aqua; +} + +/* matches elements with class=\\"1a2b3c\\" */ +._-_style_modules_css-1a2b3c { + color: aliceblue; +} + +/* matches the element with id=\\"#fake-id\\" */ +#_-_style_modules_css-\\\\#fake-id { + color: antiquewhite; +} + +/* matches the element with id=\\"-a-b-c-\\" */ +#_-_style_modules_css--a-b-c- { + color: azure; +} + +/* matches the element with id=\\"©\\" */ +#_-_style_modules_css-© { + color: black; +} + +._-_style_modules_css-♥ { background: lime; } +._-_style_modules_css-© { background: lime; } +._-_style_modules_css-\\\\😍 { background: lime; } +._-_style_modules_css-“‘’” { background: lime; } +._-_style_modules_css-☺☃ { background: lime; } +._-_style_modules_css-⌘⌥ { background: lime; } +._-_style_modules_css-\\\\𝄞♪♩♫♬ { background: lime; } +._-_style_modules_css-\\\\💩 { background: lime; } +._-_style_modules_css-\\\\? { background: lime; } +._-_style_modules_css-\\\\@ { background: lime; } +._-_style_modules_css-\\\\. { background: lime; } +._-_style_modules_css-\\\\:\\\\) { background: lime; } +._-_style_modules_css-\\\\:\\\\\`\\\\( { background: lime; } +._-_style_modules_css-123 { background: lime; } +._-_style_modules_css-1a2b3c { background: lime; } +._-_style_modules_css-\\\\ { background: lime; } +._-_style_modules_css-\\\\<\\\\>\\\\<\\\\<\\\\<\\\\>\\\\>\\\\<\\\\> { background: lime; } +._-_style_modules_css-\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\[\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\>\\\\+\\\\<\\\\<\\\\<\\\\<-\\\\]\\\\>\\\\+\\\\+\\\\.\\\\>\\\\+\\\\.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\.\\\\+\\\\+\\\\+\\\\.\\\\>\\\\+\\\\+\\\\.\\\\<\\\\<\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\>\\\\.\\\\+\\\\+\\\\+\\\\.------\\\\.--------\\\\.\\\\>\\\\+\\\\.\\\\>\\\\. { background: lime; } +._-_style_modules_css-\\\\# { background: lime; } +._-_style_modules_css-\\\\#\\\\# { background: lime; } +._-_style_modules_css-\\\\#\\\\.\\\\#\\\\.\\\\# { background: lime; } +._-_style_modules_css-_ { background: lime; } +._-_style_modules_css-\\\\{\\\\} { background: lime; } +._-_style_modules_css-\\\\#fake-id { background: lime; } +._-_style_modules_css-foo\\\\.bar { background: lime; } +._-_style_modules_css-\\\\:hover { background: lime; } +._-_style_modules_css-\\\\:hover\\\\:focus\\\\:active { background: lime; } +._-_style_modules_css-\\\\[attr\\\\=value\\\\] { background: lime; } +._-_style_modules_css-f\\\\/o\\\\/o { background: lime; } +._-_style_modules_css-f\\\\\\\\o\\\\\\\\o { background: lime; } +._-_style_modules_css-f\\\\*o\\\\*o { background: lime; } +._-_style_modules_css-f\\\\!o\\\\!o { background: lime; } +._-_style_modules_css-f\\\\'o\\\\'o { background: lime; } +._-_style_modules_css-f\\\\~o\\\\~o { background: lime; } +._-_style_modules_css-f\\\\+o\\\\+o { background: lime; } + +._-_style_modules_css-foo\\\\/bar { + background: hotpink; +} + +._-_style_modules_css-foo\\\\\\\\bar { + background: hotpink; +} + +._-_style_modules_css-foo\\\\/bar\\\\/baz { + background: hotpink; +} + +._-_style_modules_css-foo\\\\\\\\bar\\\\\\\\baz { + background: hotpink; +} + +:root { + --main-bg-color: red; + --main-bg-color-\\\\@2: blue; +} + +details { + background-color: var(--main-bg-color); + background-color: var(--main-bg-color-\\\\@2); +} + +@keyframes _-_style_modules_css-f\\\\@oo { from { color: red; } to { color: blue; } } + +/* #endregion \\"./style.modules.css\\" */ + +", +] +`; + exports[`ConfigTestCases css large exported tests should allow to create css modules: dev 1`] = ` Object { "placeholder": "my-app-_tailwind_module_css-placeholder-gray-700", diff --git a/tests/webpack-test/configCases/css/escape-unescape/index.js b/tests/webpack-test/configCases/css/escape-unescape/index.js new file mode 100644 index 00000000000..e415950fa1a --- /dev/null +++ b/tests/webpack-test/configCases/css/escape-unescape/index.js @@ -0,0 +1,15 @@ +import * as styles from "./style.modules.css"; + +it(`should work with URLs in CSS`, done => { + const links = document.getElementsByTagName("link"); + const css = []; + + // Skip first because import it by default + for (const link of links.slice(1)) { + css.push(link.sheet.css); + } + + expect(css).toMatchSnapshot('css'); + expect(styles).toMatchSnapshot('classes'); + done(); +}); diff --git a/tests/webpack-test/configCases/css/escape-unescape/style.modules.css b/tests/webpack-test/configCases/css/escape-unescape/style.modules.css new file mode 100644 index 00000000000..1417ffcb0eb --- /dev/null +++ b/tests/webpack-test/configCases/css/escape-unescape/style.modules.css @@ -0,0 +1,134 @@ +.class { + color: red; +} + +.cla\ss { + background: blue; +} + +.test { + background: red; +} + +._test { + background: blue; +} + +.className { + background: red; +} + +#someId { + background: green; +} + +.className .subClass { + color: green; +} + +#someId .subClass { + color: blue; +} + +.-a0-34a___f { + color: red; +} + +.m_x_\@ { + margin-left: auto !important; + margin-right: auto !important; +} + +.B\&W\? { + margin-left: auto !important; + margin-right: auto !important; +} + +/* matches elements with class=":`(" */ +.\3A \`\( { + color: aqua; +} + +/* matches elements with class="1a2b3c" */ +.\31 a2b3c { + color: aliceblue; +} + +/* matches the element with id="#fake-id" */ +#\#fake-id { + color: antiquewhite; +} + +/* matches the element with id="-a-b-c-" */ +#-a-b-c- { + color: azure; +} + +/* matches the element with id="©" */ +#© { + color: black; +} + +.♥ { background: lime; } +.© { background: lime; } +.😍 { background: lime; } +.“‘’” { background: lime; } +.☺☃ { background: lime; } +.⌘⌥ { background: lime; } +.𝄞♪♩♫♬ { background: lime; } +.💩 { background: lime; } +.\? { background: lime; } +.\@ { background: lime; } +.\. { background: lime; } +.\3A \) { background: lime; } +.\3A \`\( { background: lime; } +.\31 23 { background: lime; } +.\31 a2b3c { background: lime; } +.\ { background: lime; } +.\<\>\<\<\<\>\>\<\> { background: lime; } +.\+\+\+\+\+\+\+\+\+\+\[\>\+\+\+\+\+\+\+\>\+\+\+\+\+\+\+\+\+\+\>\+\+\+\>\+\<\<\<\<\-\]\>\+\+\.\>\+\.\+\+\+\+\+\+\+\.\.\+\+\+\.\>\+\+\.\<\<\+\+\+\+\+\+\+\+\+\+\+\+\+\+\+\.\>\.\+\+\+\.\-\-\-\-\-\-\.\-\-\-\-\-\-\-\-\.\>\+\.\>\. { background: lime; } +.\# { background: lime; } +.\#\# { background: lime; } +.\#\.\#\.\# { background: lime; } +.\_ { background: lime; } +.\{\} { background: lime; } +.\#fake\-id { background: lime; } +.foo\.bar { background: lime; } +.\3A hover { background: lime; } +.\3A hover\3A focus\3A active { background: lime; } +.\[attr\=value\] { background: lime; } +.f\/o\/o { background: lime; } +.f\\o\\o { background: lime; } +.f\*o\*o { background: lime; } +.f\!o\!o { background: lime; } +.f\'o\'o { background: lime; } +.f\~o\~o { background: lime; } +.f\+o\+o { background: lime; } + +.foo\/bar { + background: hotpink; +} + +.foo\\bar { + background: hotpink; +} + +.foo\/bar\/baz { + background: hotpink; +} + +.foo\\bar\\baz { + background: hotpink; +} + +:root { + --main-bg-color: red; + --main-bg-color-\@2: blue; +} + +details { + background-color: var(--main-bg-color); + background-color: var(--main-bg-color-\@2); +} + +@keyframes f\@oo { from { color: red; } to { color: blue; } } diff --git a/tests/webpack-test/configCases/css/escape-unescape/test.config.js b/tests/webpack-test/configCases/css/escape-unescape/test.config.js new file mode 100644 index 00000000000..0623a0e3b3c --- /dev/null +++ b/tests/webpack-test/configCases/css/escape-unescape/test.config.js @@ -0,0 +1,11 @@ +module.exports = { + findBundle: function (i, options) { + return ["bundle0.js"]; + }, + moduleScope(scope) { + const link = scope.window.document.createElement("link"); + link.rel = "stylesheet"; + link.href = "bundle0.css"; + scope.window.document.head.appendChild(link); + } +}; diff --git a/tests/webpack-test/configCases/css/escape-unescape/webpack.config.js b/tests/webpack-test/configCases/css/escape-unescape/webpack.config.js new file mode 100644 index 00000000000..fb903c5cfa6 --- /dev/null +++ b/tests/webpack-test/configCases/css/escape-unescape/webpack.config.js @@ -0,0 +1,17 @@ +/** @type {import("../../../../").Configuration[]} */ +module.exports = [ + { + target: "web", + mode: "development", + experiments: { + css: true + } + }, + { + target: "web", + mode: "production", + experiments: { + css: true + } + } +];