diff --git a/lib/utils.js b/lib/utils.js index 6c7574c8..f3b32cd6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -96,7 +96,8 @@ function getLocalIdent(loaderContext, localIdentName, localName, options) { } const request = path.relative(options.context, loaderContext.resourcePath); // eslint-disable-next-line no-param-reassign - options.content = `${options.hashPrefix + request}+${localName}`; + options.content = `${options.hashPrefix + + request.replace(/\\/g, '/')}+${localName}`; // eslint-disable-next-line no-param-reassign localIdentName = localIdentName.replace(/\[local\]/gi, localName); const hash = loaderUtils.interpolateName( diff --git a/test/__snapshots__/localIdentName-option.test.js.snap b/test/__snapshots__/localIdentName-option.test.js.snap new file mode 100644 index 00000000..e57cae37 --- /dev/null +++ b/test/__snapshots__/localIdentName-option.test.js.snap @@ -0,0 +1,365 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`localIdentName option basic: errors 1`] = `Array []`; + +exports[`localIdentName option basic: locals 1`] = ` +Object { + "-a0-34a___f": "_2nJ5XVdGAwkzTI324zw6Pg", + "_test": "_23te_4QsMe8I6hzxjH0MUx", + "className": "_1E8Hx67EIx_tltp58vyNJ0", + "someId": "_3w7JetWWlr8Zm4sxZ-ANzU", + "subClass": "_3lo0JvMLLK_gjw1mbsomb3", + "test": "NW9YAb4ijHq-3lCxF5vFu", +} +`; + +exports[`localIdentName option basic: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + ".NW9YAb4ijHq-3lCxF5vFu { + background: red; +} + +._23te_4QsMe8I6hzxjH0MUx { + background: blue; +} + +._1E8Hx67EIx_tltp58vyNJ0 { + background: red; +} + +#_3w7JetWWlr8Zm4sxZ-ANzU { + background: green; +} + +._1E8Hx67EIx_tltp58vyNJ0 ._3lo0JvMLLK_gjw1mbsomb3 { + color: green; +} + +#_3w7JetWWlr8Zm4sxZ-ANzU ._3lo0JvMLLK_gjw1mbsomb3 { + color: blue; +} + +._2nJ5XVdGAwkzTI324zw6Pg { + color: red; +} +", + "", + ], +] +`; + +exports[`localIdentName option basic: warnings 1`] = `Array []`; + +exports[`localIdentName option should have hash: errors 1`] = `Array []`; + +exports[`localIdentName option should have hash: locals 1`] = ` +Object { + "-a0-34a___f": "localIdentName---a0-34a___f--3RHUZ", + "_test": "localIdentName--_test--3Q--B", + "className": "localIdentName--className--3wBIH", + "someId": "localIdentName--someId--mxosG", + "subClass": "localIdentName--subClass--3jIM-", + "test": "localIdentName--test--1Os7J", +} +`; + +exports[`localIdentName option should have hash: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + ".localIdentName--test--1Os7J { + background: red; +} + +.localIdentName--_test--3Q--B { + background: blue; +} + +.localIdentName--className--3wBIH { + background: red; +} + +#localIdentName--someId--mxosG { + background: green; +} + +.localIdentName--className--3wBIH .localIdentName--subClass--3jIM- { + color: green; +} + +#localIdentName--someId--mxosG .localIdentName--subClass--3jIM- { + color: blue; +} + +.localIdentName---a0-34a___f--3RHUZ { + color: red; +} +", + "", + ], +] +`; + +exports[`localIdentName option should have hash: warnings 1`] = `Array []`; + +exports[`localIdentName option should have path naming with context: errors 1`] = `Array []`; + +exports[`localIdentName option should have path naming with context: locals 1`] = ` +Object { + "-a0-34a___f": "fixtures-modules--localIdentName---a0-34a___f", + "_test": "fixtures-modules--localIdentName--_test", + "className": "fixtures-modules--localIdentName--className", + "someId": "fixtures-modules--localIdentName--someId", + "subClass": "fixtures-modules--localIdentName--subClass", + "test": "fixtures-modules--localIdentName--test", +} +`; + +exports[`localIdentName option should have path naming with context: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + ".fixtures-modules--localIdentName--test { + background: red; +} + +.fixtures-modules--localIdentName--_test { + background: blue; +} + +.fixtures-modules--localIdentName--className { + background: red; +} + +#fixtures-modules--localIdentName--someId { + background: green; +} + +.fixtures-modules--localIdentName--className .fixtures-modules--localIdentName--subClass { + color: green; +} + +#fixtures-modules--localIdentName--someId .fixtures-modules--localIdentName--subClass { + color: blue; +} + +.fixtures-modules--localIdentName---a0-34a___f { + color: red; +} +", + "", + ], +] +`; + +exports[`localIdentName option should have path naming with context: warnings 1`] = `Array []`; + +exports[`localIdentName option should prefixes leading hyphen + digit with underscore: errors 1`] = `Array []`; + +exports[`localIdentName option should prefixes leading hyphen + digit with underscore: locals 1`] = ` +Object { + "-a0-34a___f": "_-1-a0-34a___f", + "_test": "_-1_test", + "className": "_-1className", + "someId": "_-1someId", + "subClass": "_-1subClass", + "test": "_-1test", +} +`; + +exports[`localIdentName option should prefixes leading hyphen + digit with underscore: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + "._-1test { + background: red; +} + +._-1_test { + background: blue; +} + +._-1className { + background: red; +} + +#_-1someId { + background: green; +} + +._-1className ._-1subClass { + color: green; +} + +#_-1someId ._-1subClass { + color: blue; +} + +._-1-a0-34a___f { + color: red; +} +", + "", + ], +] +`; + +exports[`localIdentName option should prefixes leading hyphen + digit with underscore: warnings 1`] = `Array []`; + +exports[`localIdentName option should prefixes two leading hyphens with underscore: errors 1`] = `Array []`; + +exports[`localIdentName option should prefixes two leading hyphens with underscore: locals 1`] = ` +Object { + "-a0-34a___f": "_---a0-34a___f", + "_test": "_--_test", + "className": "_--className", + "someId": "_--someId", + "subClass": "_--subClass", + "test": "_--test", +} +`; + +exports[`localIdentName option should prefixes two leading hyphens with underscore: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + "._--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; +} +", + "", + ], +] +`; + +exports[`localIdentName option should prefixes two leading hyphens with underscore: warnings 1`] = `Array []`; + +exports[`localIdentName option should saves underscore prefix in exported class names: errors 1`] = `Array []`; + +exports[`localIdentName option should saves underscore prefix in exported class names: locals 1`] = ` +Object { + "-a0-34a___f": "-a0-34a___f", + "_test": "_test", + "className": "className", + "someId": "someId", + "subClass": "subClass", + "test": "test", +} +`; + +exports[`localIdentName option should saves underscore prefix in exported class names: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + ".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; +} +", + "", + ], +] +`; + +exports[`localIdentName option should saves underscore prefix in exported class names: warnings 1`] = `Array []`; + +exports[`localIdentName option should use hash prefix: errors 1`] = `Array []`; + +exports[`localIdentName option should use hash prefix: locals 1`] = ` +Object { + "-a0-34a___f": "-a0-34a___f--e99d667fe0ceff9363b011302ac3f508", + "_test": "_test--d745495d407559ef605c9072243801fd", + "className": "className--eab624d1bc6b9c6b6a4278d1030dd690", + "someId": "someId--a0ce220cc9bbb1ee0e85cc0d1f0c6aa9", + "subClass": "subClass--2c82998be8a2b2e94ad7be56c9e685cd", + "test": "test--307c32aa793aaec9aecded85a9fdd448", +} +`; + +exports[`localIdentName option should use hash prefix: module (evaluated) 1`] = ` +Array [ + Array [ + 1, + ".test--307c32aa793aaec9aecded85a9fdd448 { + background: red; +} + +._test--d745495d407559ef605c9072243801fd { + background: blue; +} + +.className--eab624d1bc6b9c6b6a4278d1030dd690 { + background: red; +} + +#someId--a0ce220cc9bbb1ee0e85cc0d1f0c6aa9 { + background: green; +} + +.className--eab624d1bc6b9c6b6a4278d1030dd690 .subClass--2c82998be8a2b2e94ad7be56c9e685cd { + color: green; +} + +#someId--a0ce220cc9bbb1ee0e85cc0d1f0c6aa9 .subClass--2c82998be8a2b2e94ad7be56c9e685cd { + color: blue; +} + +.-a0-34a___f--e99d667fe0ceff9363b011302ac3f508 { + color: red; +} +", + "", + ], +] +`; + +exports[`localIdentName option should use hash prefix: warnings 1`] = `Array []`; diff --git a/test/fixtures/modules/localIdentName.css b/test/fixtures/modules/localIdentName.css new file mode 100644 index 00000000..e670d069 --- /dev/null +++ b/test/fixtures/modules/localIdentName.css @@ -0,0 +1,27 @@ +:local(.test) { + background: red; +} + +:local(._test) { + background: blue; +} + +:local(.className) { + background: red; +} + +:local(#someId) { + background: green; +} + +:local(.className .subClass) { + color: green; +} + +:local(#someId .subClass) { + color: blue; +} + +:local(.-a0-34a___f) { + color: red; +} diff --git a/test/localIdentName-option.test.js b/test/localIdentName-option.test.js new file mode 100644 index 00000000..c830ce60 --- /dev/null +++ b/test/localIdentName-option.test.js @@ -0,0 +1,120 @@ +const path = require('path'); + +const { webpack, evaluated } = require('./helpers'); + +describe('localIdentName option', () => { + it('basic', async () => { + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should have hash', async () => { + const config = { + loader: { + options: { + localIdentName: '[name]--[local]--[hash:base64:5]', + context: path.resolve(__dirname), + }, + }, + }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should have path naming with context', async () => { + const config = { + loader: { + options: { + localIdentName: '[path]-[name]--[local]', + context: path.resolve(__dirname), + }, + }, + }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should use hash prefix', async () => { + const config = { + loader: { + options: { localIdentName: '[local]--[hash]', hashPrefix: 'x' }, + }, + }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should prefixes leading hyphen + digit with underscore', async () => { + const config = { loader: { options: { localIdentName: '-1[local]' } } }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should prefixes two leading hyphens with underscore', async () => { + const config = { loader: { options: { localIdentName: '--[local]' } } }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); + + it('should saves underscore prefix in exported class names', async () => { + const config = { loader: { options: { localIdentName: '[local]' } } }; + const testId = './modules/localIdentName.css'; + const stats = await webpack(testId, config); + const { modules } = stats.toJson(); + const module = modules.find((m) => m.id === testId); + const evaluatedModule = evaluated(module.source, modules); + + expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); + expect(evaluatedModule.locals).toMatchSnapshot('locals'); + expect(stats.compilation.warnings).toMatchSnapshot('warnings'); + expect(stats.compilation.errors).toMatchSnapshot('errors'); + }); +}); diff --git a/test/modules-option.test.js b/test/modules-option.test.js index 4a47f93b..6c39e679 100644 --- a/test/modules-option.test.js +++ b/test/modules-option.test.js @@ -21,8 +21,6 @@ describe('modules option', () => { const module = modules.find((m) => m.id === testId); const evaluatedModule = evaluated(module.source, modules); - // console.log(module) - expect(evaluatedModule).toMatchSnapshot('module (evaluated)'); expect(evaluatedModule.locals).toMatchSnapshot('locals'); expect(stats.compilation.warnings).toMatchSnapshot('warnings');