Skip to content

Commit 1d5761c

Browse files
refactor: removed interpolateName in favor webpack template strings
1 parent 3882705 commit 1d5761c

File tree

7 files changed

+1559
-1238
lines changed

7 files changed

+1559
-1238
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -758,11 +758,28 @@ Type: `String`
758758
Default: `'[hash:base64]'`
759759

760760
Allows to configure the generated local ident name.
761-
See [loader-utils's documentation](https://github.com/webpack/loader-utils#interpolatename) for more information on options.
761+
762+
For more information on options see:
763+
764+
- [webpack template strings](https://webpack.js.org/configuration/output/#template-strings),
765+
- [output.hashdigest](https://webpack.js.org/configuration/output/#outputhashdigest),
766+
- [output.hashDigestLength](https://webpack.js.org/configuration/output/#outputhashdigestlength),
767+
- [output.hashFunction](https://webpack.js.org/configuration/output/#outputhashfunction),
768+
- [output.hashSalt](https://webpack.js.org/configuration/output/#outputhashsalt).
769+
770+
Supported template strings:
771+
772+
- [name] the basename of the resource
773+
- [path] the path of the resource relative to the `compiler.context` option or `modules.localIdentContext` option.
774+
- [file] - filename and path.
775+
- [ext] - extension with leading .
776+
- [hash] - the hash of the string, generated based on `hashPrefix`, `localIdentContext`, `resourcePath` and `exportName`
777+
- [<hashFunction>:hash:<hashDigest>:<hashDigestLength>] - hash with hash settings.
778+
- [local] - original class.
762779

763780
Recommendations:
764781

765-
- use `'[path][name]__[local]'` for development
782+
- use `'[file]__[local]'` for development
766783
- use `'[hash:base64]'` for production
767784

768785
The `[local]` placeholder contains original class.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
},
4444
"dependencies": {
4545
"icss-utils": "^5.1.0",
46-
"loader-utils": "^2.0.0",
4746
"postcss": "^8.2.15",
4847
"postcss-modules-extract-imports": "^3.0.0",
4948
"postcss-modules-local-by-default": "^4.0.0",

src/utils.js

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import { fileURLToPath } from "url";
66
import path from "path";
77

8-
import { interpolateName } from "loader-utils";
98
import modulesValues from "postcss-modules-values";
109
import localByDefault from "postcss-modules-local-by-default";
1110
import extractImports from "postcss-modules-extract-imports";
@@ -25,9 +24,7 @@ function isRelativePath(str) {
2524

2625
function stringifyRequest(loaderContext, request) {
2726
const splitted = request.split("!");
28-
const context =
29-
loaderContext.context ||
30-
(loaderContext.options && loaderContext.options.context);
27+
const { context } = loaderContext;
3128

3229
return JSON.stringify(
3330
splitted
@@ -63,25 +60,14 @@ function stringifyRequest(loaderContext, request) {
6360
const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i;
6461

6562
function urlToRequest(url, root) {
66-
// Do not rewrite an empty url
67-
if (url === "") {
68-
return "";
69-
}
70-
7163
const moduleRequestRegex = /^[^?]*~/;
7264
let request;
7365

7466
if (matchNativeWin32Path.test(url)) {
7567
// absolute windows path, keep it
7668
request = url;
7769
} else if (typeof root !== "undefined" && /^\//.test(url)) {
78-
// if root is set and the url is root-relative
79-
// special case: `~` roots convert to module request
80-
if (moduleRequestRegex.test(root)) {
81-
request = root.replace(/([^~/])$/, "$1/") + url.slice(1);
82-
} else {
83-
request = root + url;
84-
}
70+
request = root + url;
8571
} else if (/^\.\.?\//.test(url)) {
8672
// A relative url stays
8773
request = url;
@@ -325,6 +311,30 @@ function escapeLocalIdent(localident) {
325311
);
326312
}
327313

314+
function resolveFolderTemplate(loaderContext, localIdentName, options) {
315+
const { context } = options;
316+
let { resourcePath } = loaderContext;
317+
const parsed = path.parse(loaderContext.resourcePath);
318+
319+
if (parsed.dir) {
320+
resourcePath = parsed.dir + path.sep;
321+
}
322+
323+
let directory = path
324+
.relative(context, `${resourcePath}_`)
325+
.replace(/\\/g, "/")
326+
.replace(/\.\.(\/)?/g, "_$1");
327+
directory = directory.substr(0, directory.length - 1);
328+
329+
if (directory.length > 1) {
330+
const folder = path.basename(directory);
331+
332+
return localIdentName.replace(/\[folder\]/gi, () => folder);
333+
}
334+
335+
return localIdentName;
336+
}
337+
328338
function defaultGetLocalIdent(
329339
loaderContext,
330340
localIdentName,
@@ -348,7 +358,77 @@ function defaultGetLocalIdent(
348358
// eslint-disable-next-line no-param-reassign
349359
options.content = `${options.hashPrefix}${relativeMatchResource}${relativeResourcePath}\x00${localName}`;
350360

351-
return interpolateName(loaderContext, localIdentName, options);
361+
// eslint-disable-next-line no-underscore-dangle
362+
const { outputOptions } = loaderContext._compilation;
363+
const { hashSalt } = outputOptions;
364+
let { hashFunction, hashDigest, hashDigestLength } = outputOptions;
365+
366+
const mathes = localIdentName.match(
367+
/\[(?:([^:\]]+):)?(?:(hash|contenthash|fullhash))(?::([a-z]+\d*))?(?::(\d+))?\]/i
368+
);
369+
370+
if (mathes) {
371+
const hashName = mathes[2] || hashFunction;
372+
373+
hashFunction = mathes[1] || hashFunction;
374+
hashDigest = mathes[3] || hashDigest;
375+
hashDigestLength = mathes[4] || hashDigestLength;
376+
377+
// `hash` and `contenthash` are same in `loader-utils` context
378+
// let's keep `hash` for backward compatibility
379+
380+
// eslint-disable-next-line no-param-reassign
381+
localIdentName = localIdentName.replace(
382+
/\[(?:([^:\]]+):)?(?:hash|contenthash|fullhash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi,
383+
() => (hashName === "fullhash" ? "[fullhash]" : "[contenthash]")
384+
);
385+
}
386+
387+
// eslint-disable-next-line no-underscore-dangle
388+
const hash = loaderContext._compiler.webpack.util.createHash(hashFunction);
389+
// eslint-disable-next-line no-underscore-dangle
390+
391+
if (hashSalt) {
392+
hash.update(hashSalt);
393+
}
394+
395+
hash.update(options.content);
396+
397+
const localIdentHash = hash
398+
.digest(hashDigest)
399+
.slice(0, hashDigestLength)
400+
.replace(/[/+]/g, "_")
401+
.replace(/^\d/g, "_");
402+
403+
const ext = path.extname(loaderContext.resourcePath);
404+
const base = path.basename(loaderContext.resourcePath);
405+
const name = base.slice(0, base.length - ext.length);
406+
407+
const data = {
408+
filename: path.relative(options.context, loaderContext.resourcePath),
409+
contentHash: localIdentHash,
410+
chunk: {
411+
name,
412+
hash: localIdentHash,
413+
contentHash: localIdentHash,
414+
},
415+
};
416+
417+
// eslint-disable-next-line no-underscore-dangle
418+
let interpolatedFilename = loaderContext._compilation.getPath(
419+
localIdentName,
420+
data
421+
);
422+
423+
if (localIdentName.includes("[folder]")) {
424+
interpolatedFilename = resolveFolderTemplate(
425+
loaderContext,
426+
interpolatedFilename,
427+
options
428+
);
429+
}
430+
431+
return interpolatedFilename;
352432
}
353433

354434
const NATIVE_WIN32_PATH = /^[A-Z]:[/\\]|^\\\\/i;
@@ -526,6 +606,12 @@ function getModulesOptions(rawOptions, loaderContext) {
526606
);
527607
}
528608

609+
if (modulesOptions.localIdentName.includes("[folder]")) {
610+
loaderContext.emitWarning(
611+
"[folder] is deprecated and will be removed in next major release. See documentation for available options (https://github.com/webpack-contrib/css-loader#localidentname)"
612+
);
613+
}
614+
529615
return modulesOptions;
530616
}
531617

test/__snapshots__/esModule-option.test.js.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1
151151
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
152152
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
153153
// Module
154-
___CSS_LOADER_EXPORT___.push([module.id, \\"@charset \\\\\\"UTF-8\\\\\\";\\\\n\\\\n/* Comment */\\\\n\\\\n.oFwPvuANP2XsfGir7HPVz {\\\\n color: red;\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
154+
___CSS_LOADER_EXPORT___.push([module.id, \\"@charset \\\\\\"UTF-8\\\\\\";\\\\n\\\\n/* Comment */\\\\n\\\\n.Yz6vxyapD7cLc0x63wym {\\\\n color: red;\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
155155
// Exports
156156
___CSS_LOADER_EXPORT___.locals = {
157-
\\"class\\": \\"oFwPvuANP2XsfGir7HPVz\\"
157+
\\"class\\": \\"Yz6vxyapD7cLc0x63wym\\"
158158
};
159159
export default ___CSS_LOADER_EXPORT___;
160160
"
@@ -164,7 +164,7 @@ exports[`"esModule" option should work with a value equal to "true" and the "mod
164164
Array [
165165
Array [
166166
"../../src/index.js??ruleSet[1].rules[0].use[0]!./es-module/imported.css",
167-
"._2sn2s-Iv44Mnv3FmSmFVuI {
167+
".rJenlm2lw3fBBPGxPidc {
168168
color: red;
169169
}
170170
",
@@ -176,7 +176,7 @@ Array [
176176
177177
/* Comment */
178178
179-
.oFwPvuANP2XsfGir7HPVz {
179+
.Yz6vxyapD7cLc0x63wym {
180180
color: red;
181181
background: url(replaced_file_protocol_/webpack/public/path/img.png);
182182
}
@@ -200,10 +200,10 @@ var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1
200200
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
201201
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
202202
// Module
203-
___CSS_LOADER_EXPORT___.push([module.id, \\"@charset \\\\\\"UTF-8\\\\\\";\\\\n\\\\n/* Comment */\\\\n\\\\n.oFwPvuANP2XsfGir7HPVz {\\\\n color: red;\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
203+
___CSS_LOADER_EXPORT___.push([module.id, \\"@charset \\\\\\"UTF-8\\\\\\";\\\\n\\\\n/* Comment */\\\\n\\\\n.Yz6vxyapD7cLc0x63wym {\\\\n color: red;\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
204204
// Exports
205205
___CSS_LOADER_EXPORT___.locals = {
206-
\\"class\\": \\"oFwPvuANP2XsfGir7HPVz\\"
206+
\\"class\\": \\"Yz6vxyapD7cLc0x63wym\\"
207207
};
208208
export default ___CSS_LOADER_EXPORT___;
209209
"
@@ -213,7 +213,7 @@ exports[`"esModule" option should work with a value equal to "true" and the "mod
213213
Array [
214214
Array [
215215
"../../src/index.js??ruleSet[1].rules[0].use[0]!./es-module/imported.css",
216-
"._2sn2s-Iv44Mnv3FmSmFVuI {
216+
".rJenlm2lw3fBBPGxPidc {
217217
color: red;
218218
}
219219
",
@@ -225,7 +225,7 @@ Array [
225225
226226
/* Comment */
227227
228-
.oFwPvuANP2XsfGir7HPVz {
228+
.Yz6vxyapD7cLc0x63wym {
229229
color: red;
230230
background: url(replaced_file_protocol_/webpack/public/path/img.png);
231231
}

test/__snapshots__/loader.test.js.snap

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -660,66 +660,66 @@ exports[`loader should work with inline module syntax: result 1`] = `
660660
Array [
661661
Array [
662662
"other.modules.css!=!../../src/index.js??ruleSet[1].rules[0].rules[0]!../../node_modules/sass-loader/dist/cjs.js!./index-loader-syntax.modules.css",
663-
"._14oM7qkZ6dgC62uK5h4NI0 {
663+
".ABsTUeF6gmFCjdFT2gFj {
664664
color: red;
665665
}
666666
667-
._2NqyYysncZeLetATpl0xfW {
667+
._hMCVXaTnfM6PXNxIi9q {
668668
color: white;
669669
}",
670670
"",
671671
],
672672
Array [
673673
"button.modules.css!=!./index-loader-syntax-sass.css",
674-
"._8aapa4kQQdSsOoRODcrwe {
674+
".Drgxp1xjMmc3NE0RSqYo {
675675
width: 5px;
676676
}",
677677
"",
678678
],
679679
Array [
680680
"other.modules.scss!=!../../src/index.js??ruleSet[1].rules[0].rules[0]!../../node_modules/sass-loader/dist/cjs.js!./index-loader-syntax-sass.modules.css",
681-
"._2yVYVQfmDwKLZn9Qj6k_Q5 > ._3rHlnRnPEotX4nTkd82-aE {
681+
".Bf1TxkQnV__64Gk9dM7n > .qOILSEPdF7F3GDpf9VWt {
682682
color: red;
683683
}",
684684
"",
685685
],
686686
Array [
687687
"other.modules.css!=!../../src/index.js??ruleSet[1].rules[0].rules[0]!../../node_modules/sass-loader/dist/cjs.js!./my-inline-loader/index.js!./index-loader-syntax.modules.css",
688-
"._14oM7qkZ6dgC62uK5h4NI0 {
688+
".ABsTUeF6gmFCjdFT2gFj {
689689
color: red;
690690
}
691691
692-
._2NqyYysncZeLetATpl0xfW {
692+
._hMCVXaTnfM6PXNxIi9q {
693693
color: white;
694694
}
695695
696-
._1-Aa1c8UQML46IWY0FjGXT {
696+
._65OKcDrrEG8MI0jTKCQ {
697697
from: custom;
698698
}",
699699
"",
700700
],
701701
Array [
702702
"other.modules.css!=!../../src/index.js??ruleSet[1].rules[0].rules[0]!../../node_modules/sass-loader/dist/cjs.js!./my-inline-loader/index.js!./index-loader-syntax.modules.css",
703-
"._14oM7qkZ6dgC62uK5h4NI0 {
703+
".ABsTUeF6gmFCjdFT2gFj {
704704
color: red;
705705
}
706706
707-
._2NqyYysncZeLetATpl0xfW {
707+
._hMCVXaTnfM6PXNxIi9q {
708708
color: white;
709709
}
710710
711-
._1-Aa1c8UQML46IWY0FjGXT {
711+
._65OKcDrrEG8MI0jTKCQ {
712712
from: custom;
713713
}",
714714
"",
715715
],
716716
Array [
717717
"other.modules.scss!=!../../src/index.js??ruleSet[1].rules[0].rules[0]!../../node_modules/sass-loader/dist/cjs.js!./my-inline-loader/index.js!./index-loader-syntax-sass.modules.css",
718-
"._2yVYVQfmDwKLZn9Qj6k_Q5 > ._3rHlnRnPEotX4nTkd82-aE {
718+
".Bf1TxkQnV__64Gk9dM7n > .qOILSEPdF7F3GDpf9VWt {
719719
color: red;
720720
}
721721
722-
._2kEhhupr-6tgHnCC_d4yO8 {
722+
.iCwSzW_a1wp1hr9lXhSh {
723723
from: custom;
724724
}",
725725
"",
@@ -733,21 +733,21 @@ Array [
733733
],
734734
Array [
735735
"button.modules.css!=!./index-loader-syntax-sass.css",
736-
"._8aapa4kQQdSsOoRODcrwe {
736+
".Drgxp1xjMmc3NE0RSqYo {
737737
width: 5px;
738738
}",
739739
"",
740740
],
741741
Array [
742742
"button.module.scss!=!./base64-loader/index.js?LmZvbyB7IGNvbG9yOiByZWQ7IH0=!./simple.js?foo=bar",
743-
"._2hfVrRfux-FOrqimHr-fzt {
743+
"._fj422pJ2ianfug99ZY_ {
744744
color: red;
745745
}",
746746
"",
747747
],
748748
Array [
749749
"other.module.scss!=!./base64-loader/index.js?LmZvbyB7IGNvbG9yOiByZWQ7IH0=!./simple.js?foo=baz",
750-
"._2jOyhMQwdpQ85V5dDLcfoG {
750+
".KvYw79kUeYHQWQPScCTK {
751751
color: red;
752752
}",
753753
"",
@@ -812,7 +812,7 @@ exports[`loader should work with the "modules.auto" option and the "import.loade
812812
color: #333;
813813
}
814814
/* CSS modules */
815-
._1IwxFskZk1cbjNkH0QoBiv {
815+
.n1RiNLBS8bQwAdVTXBqC {
816816
overflow-x: hidden;
817817
overflow-y: auto;
818818
overflow: hidden auto;
@@ -829,7 +829,7 @@ exports[`loader should work with the "modules.auto" option and the "import.loade
829829
overflow-y: auto;
830830
overflow: hidden auto;
831831
}
832-
._2on1B_I_5ZLmD6I3yY37jX {
832+
._3QMvDiwxmm9ffGzfxlc {
833833
color: #333;
834834
overflow-x: hidden;
835835
overflow-y: auto;

0 commit comments

Comments
 (0)