From a0dee4fd34dd1b9892dac7645a4e57ec134e561b Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Wed, 15 Sep 2021 17:54:44 +0300 Subject: [PATCH] feat: added `[folder]` placeholder --- README.md | 3 +- src/utils.js | 44 +- .../__snapshots__/modules-option.test.js.snap | 456 ++++++++++++++++++ test/fixtures/modules/ComponentName/index.js | 5 + .../modules/ComponentName/index.modules.css | 11 + test/modules-option.test.js | 78 +++ 6 files changed, 584 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/modules/ComponentName/index.js create mode 100644 test/fixtures/modules/ComponentName/index.modules.css diff --git a/README.md b/README.md index 4a106ddf..1c690008 100644 --- a/README.md +++ b/README.md @@ -746,9 +746,10 @@ For more information on options see: Supported template strings: - `[name]` the basename of the resource +- `[folder]` the folder the resource relative to the `compiler.context` option or `modules.localIdentContext` option. - `[path]` the path of the resource relative to the `compiler.context` option or `modules.localIdentContext` option. - `[file]` - filename and path. -- `[ext]` - extension with leading . +- `[ext]` - extension with leading `.`. - `[hash]` - the hash of the string, generated based on `localIdentHashSalt`, `localIdentHashFunction`, `localIdentHashDigest`, `localIdentHashDigestLength`, `localIdentContext`, `resourcePath` and `exportName` - `[:hash::]` - hash with hash settings. - `[local]` - original class. diff --git a/src/utils.js b/src/utils.js index a674bfb3..22a45fcc 100644 --- a/src/utils.js +++ b/src/utils.js @@ -319,32 +319,35 @@ function defaultGetLocalIdent( ) { let relativeMatchResource = ""; + const { context } = options; + const { resourcePath } = loaderContext; + // eslint-disable-next-line no-underscore-dangle if (loaderContext._module && loaderContext._module.matchResource) { relativeMatchResource = `${normalizePath( // eslint-disable-next-line no-underscore-dangle - path.relative(options.context, loaderContext._module.matchResource) + path.relative(context, loaderContext._module.matchResource) )}\x00`; } const relativeResourcePath = normalizePath( - path.relative(options.context, loaderContext.resourcePath) + path.relative(context, resourcePath) ); // eslint-disable-next-line no-param-reassign options.content = `${relativeMatchResource}${relativeResourcePath}\x00${localName}`; let { hashFunction, hashDigest, hashDigestLength } = options; - const mathes = localIdentName.match( + const matches = localIdentName.match( /\[(?:([^:\]]+):)?(?:(hash|contenthash|fullhash))(?::([a-z]+\d*))?(?::(\d+))?\]/i ); - if (mathes) { - const hashName = mathes[2] || hashFunction; + if (matches) { + const hashName = matches[2] || hashFunction; - hashFunction = mathes[1] || hashFunction; - hashDigest = mathes[3] || hashDigest; - hashDigestLength = mathes[4] || hashDigestLength; + hashFunction = matches[1] || hashFunction; + hashDigest = matches[3] || hashDigest; + hashDigestLength = matches[4] || hashDigestLength; // `hash` and `contenthash` are same in `loader-utils` context // let's keep `hash` for backward compatibility @@ -373,11 +376,11 @@ function defaultGetLocalIdent( .replace(/^\d/g, "_"); // TODO need improve on webpack side, we should allow to pass hash/contentHash without chunk property, also `data` for `getPath` should be looks good without chunk property - const ext = path.extname(loaderContext.resourcePath); - const base = path.basename(loaderContext.resourcePath); + const ext = path.extname(resourcePath); + const base = path.basename(resourcePath); const name = base.slice(0, base.length - ext.length); const data = { - filename: path.relative(options.context, loaderContext.resourcePath), + filename: path.relative(context, resourcePath), contentHash: localIdentHash, chunk: { name, @@ -389,8 +392,25 @@ function defaultGetLocalIdent( // eslint-disable-next-line no-underscore-dangle let result = loaderContext._compilation.getPath(localIdentName, data); + if (/\[folder\]/gi.test(result)) { + const dirname = path.dirname(resourcePath); + let directory = normalizePath( + path.relative(context, `${dirname + path.sep}_`) + ); + + directory = directory.substr(0, directory.length - 1); + + let folder = ""; + + if (directory.length > 1) { + folder = path.basename(directory); + } + + result = result.replace(/\[folder\]/gi, () => folder); + } + if (options.regExp) { - const match = loaderContext.resourcePath.match(options.regExp); + const match = resourcePath.match(options.regExp); if (match) { match.forEach((matched, i) => { diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap index bc48d34a..b58457dd 100644 --- a/test/__snapshots__/modules-option.test.js.snap +++ b/test/__snapshots__/modules-option.test.js.snap @@ -5563,6 +5563,462 @@ Array [ exports[`"modules" option should work with 'resolve.extensions': warnings 1`] = `Array []`; +exports[`"modules" option should work with [folder] #2: errors 1`] = `Array []`; + +exports[`"modules" option should work with [folder] #2: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\".test-localIdentName {\\\\n background: red;\\\\n}\\\\n\\\\n._test-localIdentName {\\\\n background: blue;\\\\n}\\\\n\\\\n.className-localIdentName {\\\\n background: red;\\\\n}\\\\n\\\\n#someId-localIdentName {\\\\n background: green;\\\\n}\\\\n\\\\n.className-localIdentName .subClass-localIdentName {\\\\n color: green;\\\\n}\\\\n\\\\n#someId-localIdentName .subClass-localIdentName {\\\\n color: blue;\\\\n}\\\\n\\\\n.-a0-34a___f-localIdentName {\\\\n color: red;\\\\n}\\\\n\\\\n.m_x_\\\\\\\\@-localIdentName {\\\\n margin-left: auto !important;\\\\n margin-right: auto !important;\\\\n}\\\\n\\\\n.B\\\\\\\\&W\\\\\\\\?-localIdentName {\\\\n margin-left: auto !important;\\\\n margin-right: auto !important;\\\\n}\\\\n\\\\n/* matches elements with class=\\\\\\":\`(\\\\\\" */\\\\n.\\\\\\\\3A \\\\\\\\\`\\\\\\\\(-localIdentName {\\\\n color: aqua;\\\\n}\\\\n\\\\n/* matches elements with class=\\\\\\"1a2b3c\\\\\\" */\\\\n.\\\\\\\\31 a2b3c-localIdentName {\\\\n color: aliceblue;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"#fake-id\\\\\\" */\\\\n#\\\\\\\\#fake-id-localIdentName {\\\\n color: antiquewhite;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"-a-b-c-\\\\\\" */\\\\n#-a-b-c--localIdentName {\\\\n color: azure;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"©\\\\\\" */\\\\n#©-localIdentName {\\\\n color: black;\\\\n}\\\\n\\\\n.♥-localIdentName { background: lime; }\\\\n.©-localIdentName { background: lime; }\\\\n.“‘’”-localIdentName { background: lime; }\\\\n.☺☃-localIdentName { background: lime; }\\\\n.⌘⌥-localIdentName { background: lime; }\\\\n.𝄞♪♩♫♬-localIdentName { background: lime; }\\\\n.💩-localIdentName { background: lime; }\\\\n.\\\\\\\\?-localIdentName { background: lime; }\\\\n.\\\\\\\\@-localIdentName { background: lime; }\\\\n.\\\\\\\\.-localIdentName { background: lime; }\\\\n.\\\\\\\\3A \\\\\\\\)-localIdentName { background: lime; }\\\\n.\\\\\\\\3A \\\\\\\\\`\\\\\\\\(-localIdentName { background: lime; }\\\\n.\\\\\\\\31 23-localIdentName { background: lime; }\\\\n.\\\\\\\\31 a2b3c-localIdentName { background: lime; }\\\\n.\\\\\\\\-localIdentName { background: lime; }\\\\n.\\\\\\\\<\\\\\\\\>\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\>\\\\\\\\>\\\\\\\\<\\\\\\\\>-localIdentName { background: lime; }\\\\n.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\[\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\-\\\\\\\\]\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\<\\\\\\\\<\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\.\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\.-localIdentName { background: lime; }\\\\n.\\\\\\\\#-localIdentName { background: lime; }\\\\n.\\\\\\\\#\\\\\\\\#-localIdentName { background: lime; }\\\\n.\\\\\\\\#\\\\\\\\.\\\\\\\\#\\\\\\\\.\\\\\\\\#-localIdentName { background: lime; }\\\\n.\\\\\\\\_-localIdentName { background: lime; }\\\\n.\\\\\\\\{\\\\\\\\}-localIdentName { background: lime; }\\\\n.\\\\\\\\#fake\\\\\\\\-id-localIdentName { background: lime; }\\\\n.foo\\\\\\\\.bar-localIdentName { background: lime; }\\\\n.\\\\\\\\3A hover-localIdentName { background: lime; }\\\\n.\\\\\\\\3A hover\\\\\\\\3A focus\\\\\\\\3A active-localIdentName { background: lime; }\\\\n.\\\\\\\\[attr\\\\\\\\=value\\\\\\\\]-localIdentName { background: lime; }\\\\n.f\\\\\\\\/o\\\\\\\\/o-localIdentName { background: lime; }\\\\n.f\\\\\\\\\\\\\\\\o\\\\\\\\\\\\\\\\o-localIdentName { background: lime; }\\\\n.f\\\\\\\\*o\\\\\\\\*o-localIdentName { background: lime; }\\\\n.f\\\\\\\\!o\\\\\\\\!o-localIdentName { background: lime; }\\\\n.f\\\\\\\\'o\\\\\\\\'o-localIdentName { background: lime; }\\\\n.f\\\\\\\\~o\\\\\\\\~o-localIdentName { background: lime; }\\\\n.f\\\\\\\\+o\\\\\\\\+o-localIdentName { background: lime; }\\\\n\\\\n.foo\\\\\\\\/bar-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\\\\\\\\\bar-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\/bar\\\\\\\\/baz-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\\\\\\\\\bar\\\\\\\\\\\\\\\\baz-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\", \\"\\"]); +// Exports +___CSS_LOADER_EXPORT___.locals = { + \\"123\\": \\"123-localIdentName\\", + \\"test\\": \\"test-localIdentName\\", + \\"_test\\": \\"_test-localIdentName\\", + \\"className\\": \\"className-localIdentName\\", + \\"someId\\": \\"someId-localIdentName\\", + \\"subClass\\": \\"subClass-localIdentName\\", + \\"-a0-34a___f\\": \\"-a0-34a___f-localIdentName\\", + \\"m_x_@\\": \\"m_x_@-localIdentName\\", + \\"B&W?\\": \\"B&W?-localIdentName\\", + \\":\`(\\": \\":\`(-localIdentName\\", + \\"1a2b3c\\": \\"1a2b3c-localIdentName\\", + \\"#fake-id\\": \\"#fake-id-localIdentName\\", + \\"-a-b-c-\\": \\"-a-b-c--localIdentName\\", + \\"©\\": \\"©-localIdentName\\", + \\"♥\\": \\"♥-localIdentName\\", + \\"“‘’”\\": \\"“‘’”-localIdentName\\", + \\"☺☃\\": \\"☺☃-localIdentName\\", + \\"⌘⌥\\": \\"⌘⌥-localIdentName\\", + \\"𝄞♪♩♫♬\\": \\"𝄞♪♩♫♬-localIdentName\\", + \\"💩\\": \\"💩-localIdentName\\", + \\"?\\": \\"?-localIdentName\\", + \\"@\\": \\"@-localIdentName\\", + \\".\\": \\".-localIdentName\\", + \\":)\\": \\":)-localIdentName\\", + \\"

\\": \\"

-localIdentName\\", + \\"<><<<>><>\\": \\"<><<<>><>-localIdentName\\", + \\"++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.\\": \\"++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.-localIdentName\\", + \\"#\\": \\"#-localIdentName\\", + \\"##\\": \\"##-localIdentName\\", + \\"#.#.#\\": \\"#.#.#-localIdentName\\", + \\"_\\": \\"_-localIdentName\\", + \\"{}\\": \\"{}-localIdentName\\", + \\"foo.bar\\": \\"foo.bar-localIdentName\\", + \\":hover\\": \\":hover-localIdentName\\", + \\":hover:focus:active\\": \\":hover:focus:active-localIdentName\\", + \\"[attr=value]\\": \\"[attr=value]-localIdentName\\", + \\"f/o/o\\": \\"f/o/o-localIdentName\\", + \\"f\\\\\\\\o\\\\\\\\o\\": \\"f\\\\\\\\o\\\\\\\\o-localIdentName\\", + \\"f*o*o\\": \\"f*o*o-localIdentName\\", + \\"f!o!o\\": \\"f!o!o-localIdentName\\", + \\"f'o'o\\": \\"f'o'o-localIdentName\\", + \\"f~o~o\\": \\"f~o~o-localIdentName\\", + \\"f+o+o\\": \\"f+o+o-localIdentName\\", + \\"foo/bar\\": \\"foo/bar-localIdentName\\", + \\"foo\\\\\\\\bar\\": \\"foo\\\\\\\\bar-localIdentName\\", + \\"foo/bar/baz\\": \\"foo/bar/baz-localIdentName\\", + \\"foo\\\\\\\\bar\\\\\\\\baz\\": \\"foo\\\\\\\\bar\\\\\\\\baz-localIdentName\\" +}; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with [folder] #2: result 1`] = ` +Array [ + Array [ + "./modules/localIdentName/localIdentName.css", + ".test-localIdentName { + background: red; +} + +._test-localIdentName { + background: blue; +} + +.className-localIdentName { + background: red; +} + +#someId-localIdentName { + background: green; +} + +.className-localIdentName .subClass-localIdentName { + color: green; +} + +#someId-localIdentName .subClass-localIdentName { + color: blue; +} + +.-a0-34a___f-localIdentName { + color: red; +} + +.m_x_\\\\@-localIdentName { + margin-left: auto !important; + margin-right: auto !important; +} + +.B\\\\&W\\\\?-localIdentName { + margin-left: auto !important; + margin-right: auto !important; +} + +/* matches elements with class=\\":\`(\\" */ +.\\\\3A \\\\\`\\\\(-localIdentName { + color: aqua; +} + +/* matches elements with class=\\"1a2b3c\\" */ +.\\\\31 a2b3c-localIdentName { + color: aliceblue; +} + +/* matches the element with id=\\"#fake-id\\" */ +#\\\\#fake-id-localIdentName { + color: antiquewhite; +} + +/* matches the element with id=\\"-a-b-c-\\" */ +#-a-b-c--localIdentName { + color: azure; +} + +/* matches the element with id=\\"©\\" */ +#©-localIdentName { + color: black; +} + +.♥-localIdentName { background: lime; } +.©-localIdentName { background: lime; } +.“‘’”-localIdentName { background: lime; } +.☺☃-localIdentName { background: lime; } +.⌘⌥-localIdentName { background: lime; } +.𝄞♪♩♫♬-localIdentName { background: lime; } +.💩-localIdentName { background: lime; } +.\\\\?-localIdentName { background: lime; } +.\\\\@-localIdentName { background: lime; } +.\\\\.-localIdentName { background: lime; } +.\\\\3A \\\\)-localIdentName { background: lime; } +.\\\\3A \\\\\`\\\\(-localIdentName { background: lime; } +.\\\\31 23-localIdentName { background: lime; } +.\\\\31 a2b3c-localIdentName { background: lime; } +.\\\\-localIdentName { background: lime; } +.\\\\<\\\\>\\\\<\\\\<\\\\<\\\\>\\\\>\\\\<\\\\>-localIdentName { background: lime; } +.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\[\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\>\\\\+\\\\<\\\\<\\\\<\\\\<\\\\-\\\\]\\\\>\\\\+\\\\+\\\\.\\\\>\\\\+\\\\.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\.\\\\+\\\\+\\\\+\\\\.\\\\>\\\\+\\\\+\\\\.\\\\<\\\\<\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\>\\\\.\\\\+\\\\+\\\\+\\\\.\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\.\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\.\\\\>\\\\+\\\\.\\\\>\\\\.-localIdentName { background: lime; } +.\\\\#-localIdentName { background: lime; } +.\\\\#\\\\#-localIdentName { background: lime; } +.\\\\#\\\\.\\\\#\\\\.\\\\#-localIdentName { background: lime; } +.\\\\_-localIdentName { background: lime; } +.\\\\{\\\\}-localIdentName { background: lime; } +.\\\\#fake\\\\-id-localIdentName { background: lime; } +.foo\\\\.bar-localIdentName { background: lime; } +.\\\\3A hover-localIdentName { background: lime; } +.\\\\3A hover\\\\3A focus\\\\3A active-localIdentName { background: lime; } +.\\\\[attr\\\\=value\\\\]-localIdentName { background: lime; } +.f\\\\/o\\\\/o-localIdentName { background: lime; } +.f\\\\\\\\o\\\\\\\\o-localIdentName { background: lime; } +.f\\\\*o\\\\*o-localIdentName { background: lime; } +.f\\\\!o\\\\!o-localIdentName { background: lime; } +.f\\\\'o\\\\'o-localIdentName { background: lime; } +.f\\\\~o\\\\~o-localIdentName { background: lime; } +.f\\\\+o\\\\+o-localIdentName { background: lime; } + +.foo\\\\/bar-localIdentName { + background: hotpink; +} + +.foo\\\\\\\\bar-localIdentName { + background: hotpink; +} + +.foo\\\\/bar\\\\/baz-localIdentName { + background: hotpink; +} + +.foo\\\\\\\\bar\\\\\\\\baz-localIdentName { + background: hotpink; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with [folder] #2: warnings 1`] = `Array []`; + +exports[`"modules" option should work with [folder] #3: errors 1`] = `Array []`; + +exports[`"modules" option should work with [folder] #3: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\".ComponentName-header {\\\\n color: red;\\\\n}\\\\n\\\\n.ComponentName-body {\\\\n color: green;\\\\n}\\\\n\\\\n.ComponentName-footer {\\\\n color: blue; \\\\n}\\\\n\\", \\"\\"]); +// Exports +___CSS_LOADER_EXPORT___.locals = { + \\"header\\": \\"ComponentName-header\\", + \\"body\\": \\"ComponentName-body\\", + \\"footer\\": \\"ComponentName-footer\\" +}; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with [folder] #3: result 1`] = ` +Array [ + Array [ + "./modules/ComponentName/index.modules.css", + ".ComponentName-header { + color: red; +} + +.ComponentName-body { + color: green; +} + +.ComponentName-footer { + color: blue; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with [folder] #3: warnings 1`] = `Array []`; + +exports[`"modules" option should work with [folder] #4: errors 1`] = `Array []`; + +exports[`"modules" option should work with [folder] #4: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\".ComponentName-header {\\\\n color: red;\\\\n}\\\\n\\\\n.ComponentName-body {\\\\n color: green;\\\\n}\\\\n\\\\n.ComponentName-footer {\\\\n color: blue; \\\\n}\\\\n\\", \\"\\"]); +// Exports +___CSS_LOADER_EXPORT___.locals = { + \\"header\\": \\"ComponentName-header\\", + \\"body\\": \\"ComponentName-body\\", + \\"footer\\": \\"ComponentName-footer\\" +}; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with [folder] #4: result 1`] = ` +Array [ + Array [ + "./modules/ComponentName/index.modules.css", + ".ComponentName-header { + color: red; +} + +.ComponentName-body { + color: green; +} + +.ComponentName-footer { + color: blue; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with [folder] #4: warnings 1`] = `Array []`; + +exports[`"modules" option should work with [folder]: errors 1`] = `Array []`; + +exports[`"modules" option should work with [folder]: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\".test-localIdentName-localIdentName {\\\\n background: red;\\\\n}\\\\n\\\\n._test-localIdentName-localIdentName {\\\\n background: blue;\\\\n}\\\\n\\\\n.className-localIdentName-localIdentName {\\\\n background: red;\\\\n}\\\\n\\\\n#someId-localIdentName-localIdentName {\\\\n background: green;\\\\n}\\\\n\\\\n.className-localIdentName-localIdentName .subClass-localIdentName-localIdentName {\\\\n color: green;\\\\n}\\\\n\\\\n#someId-localIdentName-localIdentName .subClass-localIdentName-localIdentName {\\\\n color: blue;\\\\n}\\\\n\\\\n.-a0-34a___f-localIdentName-localIdentName {\\\\n color: red;\\\\n}\\\\n\\\\n.m_x_\\\\\\\\@-localIdentName-localIdentName {\\\\n margin-left: auto !important;\\\\n margin-right: auto !important;\\\\n}\\\\n\\\\n.B\\\\\\\\&W\\\\\\\\?-localIdentName-localIdentName {\\\\n margin-left: auto !important;\\\\n margin-right: auto !important;\\\\n}\\\\n\\\\n/* matches elements with class=\\\\\\":\`(\\\\\\" */\\\\n.\\\\\\\\3A \\\\\\\\\`\\\\\\\\(-localIdentName-localIdentName {\\\\n color: aqua;\\\\n}\\\\n\\\\n/* matches elements with class=\\\\\\"1a2b3c\\\\\\" */\\\\n.\\\\\\\\31 a2b3c-localIdentName-localIdentName {\\\\n color: aliceblue;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"#fake-id\\\\\\" */\\\\n#\\\\\\\\#fake-id-localIdentName-localIdentName {\\\\n color: antiquewhite;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"-a-b-c-\\\\\\" */\\\\n#-a-b-c--localIdentName-localIdentName {\\\\n color: azure;\\\\n}\\\\n\\\\n/* matches the element with id=\\\\\\"©\\\\\\" */\\\\n#©-localIdentName-localIdentName {\\\\n color: black;\\\\n}\\\\n\\\\n.♥-localIdentName-localIdentName { background: lime; }\\\\n.©-localIdentName-localIdentName { background: lime; }\\\\n.“‘’”-localIdentName-localIdentName { background: lime; }\\\\n.☺☃-localIdentName-localIdentName { background: lime; }\\\\n.⌘⌥-localIdentName-localIdentName { background: lime; }\\\\n.𝄞♪♩♫♬-localIdentName-localIdentName { background: lime; }\\\\n.💩-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\?-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\@-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\.-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\3A \\\\\\\\)-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\3A \\\\\\\\\`\\\\\\\\(-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\31 23-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\31 a2b3c-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\<\\\\\\\\>\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\>\\\\\\\\>\\\\\\\\<\\\\\\\\>-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\[\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\>\\\\\\\\+\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\<\\\\\\\\-\\\\\\\\]\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\<\\\\\\\\<\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\.\\\\\\\\+\\\\\\\\+\\\\\\\\+\\\\\\\\.\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\.\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\-\\\\\\\\.\\\\\\\\>\\\\\\\\+\\\\\\\\.\\\\\\\\>\\\\\\\\.-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\#-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\#\\\\\\\\#-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\#\\\\\\\\.\\\\\\\\#\\\\\\\\.\\\\\\\\#-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\_-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\{\\\\\\\\}-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\#fake\\\\\\\\-id-localIdentName-localIdentName { background: lime; }\\\\n.foo\\\\\\\\.bar-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\3A hover-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\3A hover\\\\\\\\3A focus\\\\\\\\3A active-localIdentName-localIdentName { background: lime; }\\\\n.\\\\\\\\[attr\\\\\\\\=value\\\\\\\\]-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\/o\\\\\\\\/o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\\\\\\\\\o\\\\\\\\\\\\\\\\o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\*o\\\\\\\\*o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\!o\\\\\\\\!o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\'o\\\\\\\\'o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\~o\\\\\\\\~o-localIdentName-localIdentName { background: lime; }\\\\n.f\\\\\\\\+o\\\\\\\\+o-localIdentName-localIdentName { background: lime; }\\\\n\\\\n.foo\\\\\\\\/bar-localIdentName-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\\\\\\\\\bar-localIdentName-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\/bar\\\\\\\\/baz-localIdentName-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\\\n.foo\\\\\\\\\\\\\\\\bar\\\\\\\\\\\\\\\\baz-localIdentName-localIdentName {\\\\n background: hotpink;\\\\n}\\\\n\\", \\"\\"]); +// Exports +___CSS_LOADER_EXPORT___.locals = { + \\"123\\": \\"123-localIdentName-localIdentName\\", + \\"test\\": \\"test-localIdentName-localIdentName\\", + \\"_test\\": \\"_test-localIdentName-localIdentName\\", + \\"className\\": \\"className-localIdentName-localIdentName\\", + \\"someId\\": \\"someId-localIdentName-localIdentName\\", + \\"subClass\\": \\"subClass-localIdentName-localIdentName\\", + \\"-a0-34a___f\\": \\"-a0-34a___f-localIdentName-localIdentName\\", + \\"m_x_@\\": \\"m_x_@-localIdentName-localIdentName\\", + \\"B&W?\\": \\"B&W?-localIdentName-localIdentName\\", + \\":\`(\\": \\":\`(-localIdentName-localIdentName\\", + \\"1a2b3c\\": \\"1a2b3c-localIdentName-localIdentName\\", + \\"#fake-id\\": \\"#fake-id-localIdentName-localIdentName\\", + \\"-a-b-c-\\": \\"-a-b-c--localIdentName-localIdentName\\", + \\"©\\": \\"©-localIdentName-localIdentName\\", + \\"♥\\": \\"♥-localIdentName-localIdentName\\", + \\"“‘’”\\": \\"“‘’”-localIdentName-localIdentName\\", + \\"☺☃\\": \\"☺☃-localIdentName-localIdentName\\", + \\"⌘⌥\\": \\"⌘⌥-localIdentName-localIdentName\\", + \\"𝄞♪♩♫♬\\": \\"𝄞♪♩♫♬-localIdentName-localIdentName\\", + \\"💩\\": \\"💩-localIdentName-localIdentName\\", + \\"?\\": \\"?-localIdentName-localIdentName\\", + \\"@\\": \\"@-localIdentName-localIdentName\\", + \\".\\": \\".-localIdentName-localIdentName\\", + \\":)\\": \\":)-localIdentName-localIdentName\\", + \\"

\\": \\"

-localIdentName-localIdentName\\", + \\"<><<<>><>\\": \\"<><<<>><>-localIdentName-localIdentName\\", + \\"++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.\\": \\"++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.-localIdentName-localIdentName\\", + \\"#\\": \\"#-localIdentName-localIdentName\\", + \\"##\\": \\"##-localIdentName-localIdentName\\", + \\"#.#.#\\": \\"#.#.#-localIdentName-localIdentName\\", + \\"_\\": \\"_-localIdentName-localIdentName\\", + \\"{}\\": \\"{}-localIdentName-localIdentName\\", + \\"foo.bar\\": \\"foo.bar-localIdentName-localIdentName\\", + \\":hover\\": \\":hover-localIdentName-localIdentName\\", + \\":hover:focus:active\\": \\":hover:focus:active-localIdentName-localIdentName\\", + \\"[attr=value]\\": \\"[attr=value]-localIdentName-localIdentName\\", + \\"f/o/o\\": \\"f/o/o-localIdentName-localIdentName\\", + \\"f\\\\\\\\o\\\\\\\\o\\": \\"f\\\\\\\\o\\\\\\\\o-localIdentName-localIdentName\\", + \\"f*o*o\\": \\"f*o*o-localIdentName-localIdentName\\", + \\"f!o!o\\": \\"f!o!o-localIdentName-localIdentName\\", + \\"f'o'o\\": \\"f'o'o-localIdentName-localIdentName\\", + \\"f~o~o\\": \\"f~o~o-localIdentName-localIdentName\\", + \\"f+o+o\\": \\"f+o+o-localIdentName-localIdentName\\", + \\"foo/bar\\": \\"foo/bar-localIdentName-localIdentName\\", + \\"foo\\\\\\\\bar\\": \\"foo\\\\\\\\bar-localIdentName-localIdentName\\", + \\"foo/bar/baz\\": \\"foo/bar/baz-localIdentName-localIdentName\\", + \\"foo\\\\\\\\bar\\\\\\\\baz\\": \\"foo\\\\\\\\bar\\\\\\\\baz-localIdentName-localIdentName\\" +}; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with [folder]: result 1`] = ` +Array [ + Array [ + "./modules/localIdentName/localIdentName.css", + ".test-localIdentName-localIdentName { + background: red; +} + +._test-localIdentName-localIdentName { + background: blue; +} + +.className-localIdentName-localIdentName { + background: red; +} + +#someId-localIdentName-localIdentName { + background: green; +} + +.className-localIdentName-localIdentName .subClass-localIdentName-localIdentName { + color: green; +} + +#someId-localIdentName-localIdentName .subClass-localIdentName-localIdentName { + color: blue; +} + +.-a0-34a___f-localIdentName-localIdentName { + color: red; +} + +.m_x_\\\\@-localIdentName-localIdentName { + margin-left: auto !important; + margin-right: auto !important; +} + +.B\\\\&W\\\\?-localIdentName-localIdentName { + margin-left: auto !important; + margin-right: auto !important; +} + +/* matches elements with class=\\":\`(\\" */ +.\\\\3A \\\\\`\\\\(-localIdentName-localIdentName { + color: aqua; +} + +/* matches elements with class=\\"1a2b3c\\" */ +.\\\\31 a2b3c-localIdentName-localIdentName { + color: aliceblue; +} + +/* matches the element with id=\\"#fake-id\\" */ +#\\\\#fake-id-localIdentName-localIdentName { + color: antiquewhite; +} + +/* matches the element with id=\\"-a-b-c-\\" */ +#-a-b-c--localIdentName-localIdentName { + color: azure; +} + +/* matches the element with id=\\"©\\" */ +#©-localIdentName-localIdentName { + color: black; +} + +.♥-localIdentName-localIdentName { background: lime; } +.©-localIdentName-localIdentName { background: lime; } +.“‘’”-localIdentName-localIdentName { background: lime; } +.☺☃-localIdentName-localIdentName { background: lime; } +.⌘⌥-localIdentName-localIdentName { background: lime; } +.𝄞♪♩♫♬-localIdentName-localIdentName { background: lime; } +.💩-localIdentName-localIdentName { background: lime; } +.\\\\?-localIdentName-localIdentName { background: lime; } +.\\\\@-localIdentName-localIdentName { background: lime; } +.\\\\.-localIdentName-localIdentName { background: lime; } +.\\\\3A \\\\)-localIdentName-localIdentName { background: lime; } +.\\\\3A \\\\\`\\\\(-localIdentName-localIdentName { background: lime; } +.\\\\31 23-localIdentName-localIdentName { background: lime; } +.\\\\31 a2b3c-localIdentName-localIdentName { background: lime; } +.\\\\-localIdentName-localIdentName { background: lime; } +.\\\\<\\\\>\\\\<\\\\<\\\\<\\\\>\\\\>\\\\<\\\\>-localIdentName-localIdentName { background: lime; } +.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\[\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\>\\\\+\\\\+\\\\+\\\\>\\\\+\\\\<\\\\<\\\\<\\\\<\\\\-\\\\]\\\\>\\\\+\\\\+\\\\.\\\\>\\\\+\\\\.\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\.\\\\+\\\\+\\\\+\\\\.\\\\>\\\\+\\\\+\\\\.\\\\<\\\\<\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\+\\\\.\\\\>\\\\.\\\\+\\\\+\\\\+\\\\.\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\.\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\-\\\\.\\\\>\\\\+\\\\.\\\\>\\\\.-localIdentName-localIdentName { background: lime; } +.\\\\#-localIdentName-localIdentName { background: lime; } +.\\\\#\\\\#-localIdentName-localIdentName { background: lime; } +.\\\\#\\\\.\\\\#\\\\.\\\\#-localIdentName-localIdentName { background: lime; } +.\\\\_-localIdentName-localIdentName { background: lime; } +.\\\\{\\\\}-localIdentName-localIdentName { background: lime; } +.\\\\#fake\\\\-id-localIdentName-localIdentName { background: lime; } +.foo\\\\.bar-localIdentName-localIdentName { background: lime; } +.\\\\3A hover-localIdentName-localIdentName { background: lime; } +.\\\\3A hover\\\\3A focus\\\\3A active-localIdentName-localIdentName { background: lime; } +.\\\\[attr\\\\=value\\\\]-localIdentName-localIdentName { background: lime; } +.f\\\\/o\\\\/o-localIdentName-localIdentName { background: lime; } +.f\\\\\\\\o\\\\\\\\o-localIdentName-localIdentName { background: lime; } +.f\\\\*o\\\\*o-localIdentName-localIdentName { background: lime; } +.f\\\\!o\\\\!o-localIdentName-localIdentName { background: lime; } +.f\\\\'o\\\\'o-localIdentName-localIdentName { background: lime; } +.f\\\\~o\\\\~o-localIdentName-localIdentName { background: lime; } +.f\\\\+o\\\\+o-localIdentName-localIdentName { background: lime; } + +.foo\\\\/bar-localIdentName-localIdentName { + background: hotpink; +} + +.foo\\\\\\\\bar-localIdentName-localIdentName { + background: hotpink; +} + +.foo\\\\/bar\\\\/baz-localIdentName-localIdentName { + background: hotpink; +} + +.foo\\\\\\\\bar\\\\\\\\baz-localIdentName-localIdentName { + background: hotpink; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with [folder]: warnings 1`] = `Array []`; + exports[`"modules" option should work with \`@\` character in scoped packages: errors 1`] = `Array []`; exports[`"modules" option should work with \`@\` character in scoped packages: module 1`] = ` diff --git a/test/fixtures/modules/ComponentName/index.js b/test/fixtures/modules/ComponentName/index.js new file mode 100644 index 00000000..c2f20db3 --- /dev/null +++ b/test/fixtures/modules/ComponentName/index.js @@ -0,0 +1,5 @@ +import css from './index.modules.css'; + +__export__ = css; + +export default css; diff --git a/test/fixtures/modules/ComponentName/index.modules.css b/test/fixtures/modules/ComponentName/index.modules.css new file mode 100644 index 00000000..ebefbd06 --- /dev/null +++ b/test/fixtures/modules/ComponentName/index.modules.css @@ -0,0 +1,11 @@ +.header { + color: red; +} + +.body { + color: green; +} + +.footer { + color: blue; +} diff --git a/test/modules-option.test.js b/test/modules-option.test.js index d658af16..e585c035 100644 --- a/test/modules-option.test.js +++ b/test/modules-option.test.js @@ -1976,4 +1976,82 @@ describe('"modules" option', () => { expect(getWarnings(stats)).toMatchSnapshot("warnings"); expect(getErrors(stats)).toMatchSnapshot("errors"); }); + + it("should work with [folder]", async () => { + const compiler = getCompiler("./modules/localIdentName/localIdentName.js", { + modules: { localIdentName: "[local]-[folder]-[name]" }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource("./modules/localIdentName/localIdentName.css", stats) + ).toMatchSnapshot("module"); + expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot( + "result" + ); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + + it("should work with [folder] #2", async () => { + const compiler = getCompiler("./modules/localIdentName/localIdentName.js", { + modules: { + localIdentName: "[local]-[folder][name]", + localIdentContext: path.resolve( + __dirname, + "fixtures", + "modules", + "localIdentName" + ), + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource("./modules/localIdentName/localIdentName.css", stats) + ).toMatchSnapshot("module"); + expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot( + "result" + ); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + + it("should work with [folder] #3", async () => { + const compiler = getCompiler("./modules/ComponentName/index.js", { + modules: { + localIdentName: "[folder]-[local]", + localIdentContext: path.resolve(__dirname, "fixtures", "modules"), + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource("./modules/ComponentName/index.modules.css", stats) + ).toMatchSnapshot("module"); + expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot( + "result" + ); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + + it("should work with [folder] #4", async () => { + const compiler = getCompiler("./modules/ComponentName/index.js", { + modules: { + localIdentName: "[FOLDER]-[LOCAL]", + localIdentContext: path.resolve(__dirname, "fixtures", "modules"), + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource("./modules/ComponentName/index.modules.css", stats) + ).toMatchSnapshot("module"); + expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot( + "result" + ); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); });