Skip to content

Commit a0dee4f

Browse files
feat: added [folder] placeholder
1 parent aea023d commit a0dee4f

File tree

6 files changed

+584
-13
lines changed

6 files changed

+584
-13
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,10 @@ For more information on options see:
746746
Supported template strings:
747747

748748
- `[name]` the basename of the resource
749+
- `[folder]` the folder the resource relative to the `compiler.context` option or `modules.localIdentContext` option.
749750
- `[path]` the path of the resource relative to the `compiler.context` option or `modules.localIdentContext` option.
750751
- `[file]` - filename and path.
751-
- `[ext]` - extension with leading .
752+
- `[ext]` - extension with leading `.`.
752753
- `[hash]` - the hash of the string, generated based on `localIdentHashSalt`, `localIdentHashFunction`, `localIdentHashDigest`, `localIdentHashDigestLength`, `localIdentContext`, `resourcePath` and `exportName`
753754
- `[<hashFunction>:hash:<hashDigest>:<hashDigestLength>]` - hash with hash settings.
754755
- `[local]` - original class.

src/utils.js

+32-12
Original file line numberDiff line numberDiff line change
@@ -319,32 +319,35 @@ function defaultGetLocalIdent(
319319
) {
320320
let relativeMatchResource = "";
321321

322+
const { context } = options;
323+
const { resourcePath } = loaderContext;
324+
322325
// eslint-disable-next-line no-underscore-dangle
323326
if (loaderContext._module && loaderContext._module.matchResource) {
324327
relativeMatchResource = `${normalizePath(
325328
// eslint-disable-next-line no-underscore-dangle
326-
path.relative(options.context, loaderContext._module.matchResource)
329+
path.relative(context, loaderContext._module.matchResource)
327330
)}\x00`;
328331
}
329332

330333
const relativeResourcePath = normalizePath(
331-
path.relative(options.context, loaderContext.resourcePath)
334+
path.relative(context, resourcePath)
332335
);
333336

334337
// eslint-disable-next-line no-param-reassign
335338
options.content = `${relativeMatchResource}${relativeResourcePath}\x00${localName}`;
336339

337340
let { hashFunction, hashDigest, hashDigestLength } = options;
338-
const mathes = localIdentName.match(
341+
const matches = localIdentName.match(
339342
/\[(?:([^:\]]+):)?(?:(hash|contenthash|fullhash))(?::([a-z]+\d*))?(?::(\d+))?\]/i
340343
);
341344

342-
if (mathes) {
343-
const hashName = mathes[2] || hashFunction;
345+
if (matches) {
346+
const hashName = matches[2] || hashFunction;
344347

345-
hashFunction = mathes[1] || hashFunction;
346-
hashDigest = mathes[3] || hashDigest;
347-
hashDigestLength = mathes[4] || hashDigestLength;
348+
hashFunction = matches[1] || hashFunction;
349+
hashDigest = matches[3] || hashDigest;
350+
hashDigestLength = matches[4] || hashDigestLength;
348351

349352
// `hash` and `contenthash` are same in `loader-utils` context
350353
// let's keep `hash` for backward compatibility
@@ -373,11 +376,11 @@ function defaultGetLocalIdent(
373376
.replace(/^\d/g, "_");
374377

375378
// 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
376-
const ext = path.extname(loaderContext.resourcePath);
377-
const base = path.basename(loaderContext.resourcePath);
379+
const ext = path.extname(resourcePath);
380+
const base = path.basename(resourcePath);
378381
const name = base.slice(0, base.length - ext.length);
379382
const data = {
380-
filename: path.relative(options.context, loaderContext.resourcePath),
383+
filename: path.relative(context, resourcePath),
381384
contentHash: localIdentHash,
382385
chunk: {
383386
name,
@@ -389,8 +392,25 @@ function defaultGetLocalIdent(
389392
// eslint-disable-next-line no-underscore-dangle
390393
let result = loaderContext._compilation.getPath(localIdentName, data);
391394

395+
if (/\[folder\]/gi.test(result)) {
396+
const dirname = path.dirname(resourcePath);
397+
let directory = normalizePath(
398+
path.relative(context, `${dirname + path.sep}_`)
399+
);
400+
401+
directory = directory.substr(0, directory.length - 1);
402+
403+
let folder = "";
404+
405+
if (directory.length > 1) {
406+
folder = path.basename(directory);
407+
}
408+
409+
result = result.replace(/\[folder\]/gi, () => folder);
410+
}
411+
392412
if (options.regExp) {
393-
const match = loaderContext.resourcePath.match(options.regExp);
413+
const match = resourcePath.match(options.regExp);
394414

395415
if (match) {
396416
match.forEach((matched, i) => {

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

+456
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import css from './index.modules.css';
2+
3+
__export__ = css;
4+
5+
export default css;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.header {
2+
color: red;
3+
}
4+
5+
.body {
6+
color: green;
7+
}
8+
9+
.footer {
10+
color: blue;
11+
}

test/modules-option.test.js

+78
Original file line numberDiff line numberDiff line change
@@ -1976,4 +1976,82 @@ describe('"modules" option', () => {
19761976
expect(getWarnings(stats)).toMatchSnapshot("warnings");
19771977
expect(getErrors(stats)).toMatchSnapshot("errors");
19781978
});
1979+
1980+
it("should work with [folder]", async () => {
1981+
const compiler = getCompiler("./modules/localIdentName/localIdentName.js", {
1982+
modules: { localIdentName: "[local]-[folder]-[name]" },
1983+
});
1984+
const stats = await compile(compiler);
1985+
1986+
expect(
1987+
getModuleSource("./modules/localIdentName/localIdentName.css", stats)
1988+
).toMatchSnapshot("module");
1989+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
1990+
"result"
1991+
);
1992+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
1993+
expect(getErrors(stats)).toMatchSnapshot("errors");
1994+
});
1995+
1996+
it("should work with [folder] #2", async () => {
1997+
const compiler = getCompiler("./modules/localIdentName/localIdentName.js", {
1998+
modules: {
1999+
localIdentName: "[local]-[folder][name]",
2000+
localIdentContext: path.resolve(
2001+
__dirname,
2002+
"fixtures",
2003+
"modules",
2004+
"localIdentName"
2005+
),
2006+
},
2007+
});
2008+
const stats = await compile(compiler);
2009+
2010+
expect(
2011+
getModuleSource("./modules/localIdentName/localIdentName.css", stats)
2012+
).toMatchSnapshot("module");
2013+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
2014+
"result"
2015+
);
2016+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
2017+
expect(getErrors(stats)).toMatchSnapshot("errors");
2018+
});
2019+
2020+
it("should work with [folder] #3", async () => {
2021+
const compiler = getCompiler("./modules/ComponentName/index.js", {
2022+
modules: {
2023+
localIdentName: "[folder]-[local]",
2024+
localIdentContext: path.resolve(__dirname, "fixtures", "modules"),
2025+
},
2026+
});
2027+
const stats = await compile(compiler);
2028+
2029+
expect(
2030+
getModuleSource("./modules/ComponentName/index.modules.css", stats)
2031+
).toMatchSnapshot("module");
2032+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
2033+
"result"
2034+
);
2035+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
2036+
expect(getErrors(stats)).toMatchSnapshot("errors");
2037+
});
2038+
2039+
it("should work with [folder] #4", async () => {
2040+
const compiler = getCompiler("./modules/ComponentName/index.js", {
2041+
modules: {
2042+
localIdentName: "[FOLDER]-[LOCAL]",
2043+
localIdentContext: path.resolve(__dirname, "fixtures", "modules"),
2044+
},
2045+
});
2046+
const stats = await compile(compiler);
2047+
2048+
expect(
2049+
getModuleSource("./modules/ComponentName/index.modules.css", stats)
2050+
).toMatchSnapshot("module");
2051+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
2052+
"result"
2053+
);
2054+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
2055+
expect(getErrors(stats)).toMatchSnapshot("errors");
2056+
});
19792057
});

0 commit comments

Comments
 (0)