Skip to content

Commit

Permalink
feat: added [folder] placeholder
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Sep 15, 2021
1 parent aea023d commit a0dee4f
Show file tree
Hide file tree
Showing 6 changed files with 584 additions and 13 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
- `[<hashFunction>:hash:<hashDigest>:<hashDigestLength>]` - hash with hash settings.
- `[local]` - original class.
Expand Down
44 changes: 32 additions & 12 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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) => {
Expand Down
456 changes: 456 additions & 0 deletions test/__snapshots__/modules-option.test.js.snap

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions test/fixtures/modules/ComponentName/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import css from './index.modules.css';

__export__ = css;

export default css;
11 changes: 11 additions & 0 deletions test/fixtures/modules/ComponentName/index.modules.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.header {
color: red;
}

.body {
color: green;
}

.footer {
color: blue;
}
78 changes: 78 additions & 0 deletions test/modules-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});

0 comments on commit a0dee4f

Please sign in to comment.