Skip to content

Commit 15f793d

Browse files
docs: update logic (#1587)
1 parent 9c165a4 commit 15f793d

File tree

5 files changed

+396
-130
lines changed

5 files changed

+396
-130
lines changed

README.md

Lines changed: 84 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ type modules =
337337
imports: object[];
338338
exports: object[];
339339
replacements: object[];
340-
}) => any;
340+
}) => Promise<void> | void;
341341
};
342342
```
343343

@@ -1411,7 +1411,7 @@ type getJSON = ({
14111411
imports: object[];
14121412
exports: object[];
14131413
replacements: object[];
1414-
}) => any;
1414+
}) => Promise<void> | void;
14151415
```
14161416

14171417
Default: `undefined`
@@ -1457,81 +1457,21 @@ Enables a callback to output the CSS modules mapping JSON. The callback is invok
14571457
}
14581458
```
14591459

1460-
**webpack.config.js**
1461-
1462-
```js
1463-
// supports a synchronous callback
1464-
module.exports = {
1465-
module: {
1466-
rules: [
1467-
{
1468-
test: /\.css$/i,
1469-
loader: "css-loader",
1470-
options: {
1471-
modules: {
1472-
getJSON: ({ resourcePath, exports }) => {
1473-
// synchronously write a .json mapping file in the same directory as the resource
1474-
const exportsJson = exports.reduce(
1475-
(acc, { name, value }) => ({ ...acc, [name]: value }),
1476-
{},
1477-
);
1478-
1479-
const outputPath = path.resolve(
1480-
path.dirname(resourcePath),
1481-
`${path.basename(resourcePath)}.json`,
1482-
);
1483-
1484-
const fs = require("fs");
1485-
fs.writeFileSync(outputPath, JSON.stringify(json));
1486-
},
1487-
},
1488-
},
1489-
},
1490-
],
1491-
},
1492-
};
1493-
1494-
// supports an asynchronous callback
1495-
module.exports = {
1496-
module: {
1497-
rules: [
1498-
{
1499-
test: /\.css$/i,
1500-
loader: "css-loader",
1501-
options: {
1502-
modules: {
1503-
getJSON: async ({ resourcePath, exports }) => {
1504-
const exportsJson = exports.reduce(
1505-
(acc, { name, value }) => ({ ...acc, [name]: value }),
1506-
{},
1507-
);
1508-
1509-
const outputPath = path.resolve(
1510-
path.dirname(resourcePath),
1511-
`${path.basename(resourcePath)}.json`,
1512-
);
1513-
1514-
const fsp = require("fs/promises");
1515-
await fsp.writeFile(outputPath, JSON.stringify(json));
1516-
},
1517-
},
1518-
},
1519-
},
1520-
],
1521-
},
1522-
};
1523-
```
1524-
15251460
Using `getJSON`, it's possible to output a files with all CSS module mappings.
15261461
In the following example, we use `getJSON` to cache canonical mappings and
15271462
add stand-ins for any composed values (through `composes`), and we use a custom plugin
15281463
to consolidate the values and output them to a file:
15291464

1465+
**webpack.config.js**
1466+
15301467
```js
1468+
const path = require("path");
1469+
const fs = require("fs");
1470+
15311471
const CSS_LOADER_REPLACEMENT_REGEX =
15321472
/(___CSS_LOADER_ICSS_IMPORT_\d+_REPLACEMENT_\d+___)/g;
1533-
const REPLACEMENT_REGEX = /___REPLACEMENT\[(.*?)\]\[(.*?)\]___/g;
1534-
const IDENTIFIER_REGEX = /\[(.*?)\]\[(.*?)\]/;
1473+
const REPLACEMENT_REGEX = /___REPLACEMENT\[(.*?)]\[(.*?)]___/g;
1474+
const IDENTIFIER_REGEX = /\[(.*?)]\[(.*?)]/;
15351475
const replacementsMap = {};
15361476
const canonicalValuesMap = {};
15371477
const allExportsJson = {};
@@ -1570,9 +1510,8 @@ function addReplacements(resourcePath, imports, exportsJson, replacements) {
15701510
// add them all to the replacements map to be replaced altogether later
15711511
replacementsMap[identifier] = classNames.replaceAll(
15721512
CSS_LOADER_REPLACEMENT_REGEX,
1573-
(_, replacementName) => {
1574-
return importReplacementsMap[resourcePath][replacementName];
1575-
},
1513+
(_, replacementName) =>
1514+
importReplacementsMap[resourcePath][replacementName],
15761515
);
15771516
} else {
15781517
// otherwise, no class names need replacements so we can add them to
@@ -1586,22 +1525,86 @@ function addReplacements(resourcePath, imports, exportsJson, replacements) {
15861525
}
15871526

15881527
function replaceReplacements(classNames) {
1589-
const adjustedClassNames = classNames.replaceAll(
1528+
return classNames.replaceAll(
15901529
REPLACEMENT_REGEX,
15911530
(_, resourcePath, localName) => {
15921531
const identifier = generateIdentifier(resourcePath, localName);
1532+
15931533
if (identifier in canonicalValuesMap) {
15941534
return canonicalValuesMap[identifier];
15951535
}
15961536

1597-
// recurse through other stand-in that may be imports
1537+
// Recurse through other stand-in that may be imports
15981538
const canonicalValue = replaceReplacements(replacementsMap[identifier]);
1539+
15991540
canonicalValuesMap[identifier] = canonicalValue;
1541+
16001542
return canonicalValue;
16011543
},
16021544
);
1545+
}
1546+
1547+
function getJSON({ resourcePath, imports, exports, replacements }) {
1548+
const exportsJson = exports.reduce((acc, { name, value }) => {
1549+
return { ...acc, [name]: value };
1550+
}, {});
1551+
1552+
if (replacements.length > 0) {
1553+
// replacements present --> add stand-in values for absolute paths and local names,
1554+
// which will be resolved to their canonical values in the plugin below
1555+
addReplacements(resourcePath, imports, exportsJson, replacements);
1556+
} else {
1557+
// no replacements present --> add to canonicalValuesMap verbatim
1558+
// since all values here are canonical/don't need resolution
1559+
for (const [key, value] of Object.entries(exportsJson)) {
1560+
const id = `[${resourcePath}][${key}]`;
1561+
1562+
canonicalValuesMap[id] = value;
1563+
}
16031564

1604-
return adjustedClassNames;
1565+
allExportsJson[resourcePath] = exportsJson;
1566+
}
1567+
}
1568+
1569+
class CssModulesJsonPlugin {
1570+
constructor(options) {
1571+
this.options = options;
1572+
}
1573+
1574+
// eslint-disable-next-line class-methods-use-this
1575+
apply(compiler) {
1576+
compiler.hooks.emit.tap("CssModulesJsonPlugin", () => {
1577+
for (const [identifier, classNames] of Object.entries(replacementsMap)) {
1578+
const adjustedClassNames = replaceReplacements(classNames);
1579+
1580+
replacementsMap[identifier] = adjustedClassNames;
1581+
1582+
const [, resourcePath, localName] = identifier.match(IDENTIFIER_REGEX);
1583+
1584+
allExportsJson[resourcePath] = allExportsJson[resourcePath] || {};
1585+
allExportsJson[resourcePath][localName] = adjustedClassNames;
1586+
}
1587+
1588+
fs.writeFileSync(
1589+
this.options.filepath,
1590+
JSON.stringify(
1591+
// Make path to be relative to `context` (your project root)
1592+
Object.fromEntries(
1593+
Object.entries(allExportsJson).map((key) => {
1594+
key[0] = path
1595+
.relative(compiler.context, key[0])
1596+
.replace(/\\/g, "/");
1597+
1598+
return key;
1599+
}),
1600+
),
1601+
null,
1602+
2,
1603+
),
1604+
"utf8",
1605+
);
1606+
});
1607+
}
16051608
}
16061609

16071610
module.exports = {
@@ -1610,63 +1613,14 @@ module.exports = {
16101613
{
16111614
test: /\.css$/i,
16121615
loader: "css-loader",
1613-
options: {
1614-
modules: {
1615-
getJSON: ({ resourcePath, imports, exports, replacements }) => {
1616-
const exportsJson = exports.reduce(
1617-
(acc, { name, value }) => ({ ...acc, [name]: value }),
1618-
{},
1619-
);
1620-
1621-
if (replacements.length > 0) {
1622-
// replacements present --> add stand-in values for absolute paths and local names,
1623-
// which will be resolved to their canonical values in the plugin below
1624-
addReplacements(
1625-
resourcePath,
1626-
imports,
1627-
exportsJson,
1628-
replacements,
1629-
);
1630-
} else {
1631-
// no replacements present --> add to canonicalValuesMap verbatim
1632-
// since all values here are canonical/don't need resolution
1633-
for (const [key, value] of Object.entries(exportsJson)) {
1634-
const id = `[${resourcePath}][${key}]`;
1635-
1636-
canonicalValuesMap[id] = value;
1637-
}
1638-
1639-
allExportsJson[resourcePath] = exportsJson;
1640-
}
1641-
},
1642-
},
1643-
},
1616+
options: { modules: { getJSON } },
16441617
},
16451618
],
16461619
},
16471620
plugins: [
1648-
{
1649-
apply(compiler) {
1650-
compiler.hooks.done.tap("CssModulesJsonPlugin", () => {
1651-
for (const [identifier, classNames] of Object.entries(
1652-
replacementsMap,
1653-
)) {
1654-
const adjustedClassNames = replaceReplacements(classNames);
1655-
replacementsMap[identifier] = adjustedClassNames;
1656-
const [, resourcePath, localName] =
1657-
identifier.match(IDENTIFIER_REGEX);
1658-
allExportsJson[resourcePath] = allExportsJson[resourcePath] || {};
1659-
allExportsJson[resourcePath][localName] = adjustedClassNames;
1660-
}
1661-
1662-
fs.writeFileSync(
1663-
"./output.css.json",
1664-
JSON.stringify(allExportsJson, null, 2),
1665-
"utf8",
1666-
);
1667-
});
1668-
},
1669-
},
1621+
new CssModulesJsonPlugin({
1622+
filepath: path.resolve(__dirname, "./output.css.json"),
1623+
}),
16701624
],
16711625
};
16721626
```
@@ -1675,11 +1629,11 @@ In the above, all import aliases are replaced with `___REPLACEMENT[<resourcePath
16751629

16761630
```json
16771631
{
1678-
"/foo/bar/baz.module.css": {
1632+
"foo/bar/baz.module.css": {
16791633
"main": "D2Oy",
16801634
"header": "thNN"
16811635
},
1682-
"/foot/bear/bath.module.css": {
1636+
"foot/bear/bath.module.css": {
16831637
"logo": "sqiR",
16841638
"info": "XMyI"
16851639
}

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ export default async function loader(content, map, meta) {
274274
);
275275

276276
const { getJSON } = options.modules;
277+
277278
if (typeof getJSON === "function") {
278279
try {
279280
await getJSON({ resourcePath, imports, exports, replacements });

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy