css-loader 6.11.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +305 -43
- package/dist/index.js +18 -2
- package/dist/options.json +10 -1
- package/dist/utils.js +27 -58
- package/package.json +25 -26
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Then add the plugin to your `webpack` config. For example:
|
|
|
47
47
|
**file.js**
|
|
48
48
|
|
|
49
49
|
```js
|
|
50
|
-
import css from "file.css";
|
|
50
|
+
import * as css from "file.css";
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
**webpack.config.js**
|
|
@@ -192,7 +192,7 @@ type importFn =
|
|
|
192
192
|
media: string,
|
|
193
193
|
resourcePath: string,
|
|
194
194
|
supports?: string,
|
|
195
|
-
layer?: string
|
|
195
|
+
layer?: string,
|
|
196
196
|
) => boolean;
|
|
197
197
|
};
|
|
198
198
|
```
|
|
@@ -315,18 +315,29 @@ type modules =
|
|
|
315
315
|
getLocalIdent: (
|
|
316
316
|
context: LoaderContext,
|
|
317
317
|
localIdentName: string,
|
|
318
|
-
localName: string
|
|
318
|
+
localName: string,
|
|
319
319
|
) => string;
|
|
320
320
|
namedExport: boolean;
|
|
321
321
|
exportGlobals: boolean;
|
|
322
322
|
exportLocalsConvention:
|
|
323
|
-
| "
|
|
324
|
-
| "
|
|
325
|
-
| "
|
|
323
|
+
| "as-is"
|
|
324
|
+
| "camel-case"
|
|
325
|
+
| "camel-case-only"
|
|
326
326
|
| "dashes"
|
|
327
|
-
| "
|
|
327
|
+
| "dashes-only"
|
|
328
328
|
| ((name: string) => string);
|
|
329
329
|
exportOnlyLocals: boolean;
|
|
330
|
+
getJSON: ({
|
|
331
|
+
resourcePath,
|
|
332
|
+
imports,
|
|
333
|
+
exports,
|
|
334
|
+
replacements,
|
|
335
|
+
}: {
|
|
336
|
+
resourcePath: string;
|
|
337
|
+
imports: object[];
|
|
338
|
+
exports: object[];
|
|
339
|
+
replacements: object[];
|
|
340
|
+
}) => Promise<void> | void;
|
|
330
341
|
};
|
|
331
342
|
```
|
|
332
343
|
|
|
@@ -489,7 +500,10 @@ To import from multiple modules use multiple `composes:` rules.
|
|
|
489
500
|
|
|
490
501
|
```css
|
|
491
502
|
:local(.className) {
|
|
492
|
-
composes:
|
|
503
|
+
composes:
|
|
504
|
+
edit highlight from "./edit.css",
|
|
505
|
+
button from "module/button.css",
|
|
506
|
+
classFromThisModule;
|
|
493
507
|
background: red;
|
|
494
508
|
}
|
|
495
509
|
```
|
|
@@ -599,8 +613,9 @@ module.exports = {
|
|
|
599
613
|
localIdentContext: path.resolve(__dirname, "src"),
|
|
600
614
|
localIdentHashSalt: "my-custom-hash",
|
|
601
615
|
namedExport: true,
|
|
602
|
-
exportLocalsConvention: "
|
|
616
|
+
exportLocalsConvention: "as-is",
|
|
603
617
|
exportOnlyLocals: false,
|
|
618
|
+
getJSON: ({ resourcePath, imports, exports, replacements }) => {},
|
|
604
619
|
},
|
|
605
620
|
},
|
|
606
621
|
},
|
|
@@ -620,7 +635,7 @@ type auto =
|
|
|
620
635
|
| ((
|
|
621
636
|
resourcePath: string,
|
|
622
637
|
resourceQuery: string,
|
|
623
|
-
resourceFragment: string
|
|
638
|
+
resourceFragment: string,
|
|
624
639
|
) => boolean);
|
|
625
640
|
```
|
|
626
641
|
|
|
@@ -726,7 +741,7 @@ type mode =
|
|
|
726
741
|
| ((
|
|
727
742
|
resourcePath: string,
|
|
728
743
|
resourceQuery: string,
|
|
729
|
-
resourceFragment: string
|
|
744
|
+
resourceFragment: string,
|
|
730
745
|
) => "local" | "global" | "pure" | "icss");
|
|
731
746
|
```
|
|
732
747
|
|
|
@@ -1094,7 +1109,7 @@ Type:
|
|
|
1094
1109
|
type getLocalIdent = (
|
|
1095
1110
|
context: LoaderContext,
|
|
1096
1111
|
localIdentName: string,
|
|
1097
|
-
localName: string
|
|
1112
|
+
localName: string,
|
|
1098
1113
|
) => string;
|
|
1099
1114
|
```
|
|
1100
1115
|
|
|
@@ -1135,21 +1150,13 @@ Type:
|
|
|
1135
1150
|
type namedExport = boolean;
|
|
1136
1151
|
```
|
|
1137
1152
|
|
|
1138
|
-
Default: `false
|
|
1153
|
+
Default: Depends on the value of the `esModule` option. If the value of the `esModule` options is `true`, this value will also be `true`, otherwise it will be `false`.
|
|
1139
1154
|
|
|
1140
1155
|
Enables/disables ES modules named export for locals.
|
|
1141
1156
|
|
|
1142
1157
|
> **Warning**
|
|
1143
1158
|
>
|
|
1144
|
-
>
|
|
1145
|
-
> `camelCaseOnly` value by default. You can set this back to any other valid option but selectors
|
|
1146
|
-
> which are not valid JavaScript identifiers may run into problems which do not implement the entire
|
|
1147
|
-
> modules specification.
|
|
1148
|
-
|
|
1149
|
-
> **Warning**
|
|
1150
|
-
>
|
|
1151
|
-
> It is not allowed to use JavaScript reserved words in css class names unless
|
|
1152
|
-
> `exportLocalsConvention` is `"asIs"`.
|
|
1159
|
+
> It is not allowed to use the `default` reserved word in css classes.
|
|
1153
1160
|
|
|
1154
1161
|
**styles.css**
|
|
1155
1162
|
|
|
@@ -1167,9 +1174,11 @@ Enables/disables ES modules named export for locals.
|
|
|
1167
1174
|
```js
|
|
1168
1175
|
import * as styles from "./styles.css";
|
|
1169
1176
|
|
|
1170
|
-
|
|
1171
|
-
// or if using `exportLocalsConvention: "asIs"`:
|
|
1177
|
+
// If using `exportLocalsConvention: "as-is"` (default value):
|
|
1172
1178
|
console.log(styles["foo-baz"], styles.bar);
|
|
1179
|
+
|
|
1180
|
+
// If using `exportLocalsConvention: "camel-case-only"`:
|
|
1181
|
+
console.log(styles.fooBaz, styles.bar);
|
|
1173
1182
|
```
|
|
1174
1183
|
|
|
1175
1184
|
You can enable a ES module named export using:
|
|
@@ -1236,29 +1245,35 @@ Type:
|
|
|
1236
1245
|
|
|
1237
1246
|
```ts
|
|
1238
1247
|
type exportLocalsConvention =
|
|
1239
|
-
| "
|
|
1240
|
-
| "
|
|
1241
|
-
| "
|
|
1248
|
+
| "as-is"
|
|
1249
|
+
| "camel-case"
|
|
1250
|
+
| "camel-case-only"
|
|
1242
1251
|
| "dashes"
|
|
1243
|
-
| "
|
|
1252
|
+
| "dashes-only"
|
|
1244
1253
|
| ((name: string) => string);
|
|
1245
1254
|
```
|
|
1246
1255
|
|
|
1247
|
-
Default:
|
|
1256
|
+
Default: Depends on the value of the `modules.namedExport` option, if `true` - `as-is`, otherwise `camel-case-only`.
|
|
1257
|
+
|
|
1258
|
+
> **Warning**
|
|
1259
|
+
>
|
|
1260
|
+
> Names of locals are converted to camelcase when the named export is `false`, i.e. the `exportLocalsConvention` option has
|
|
1261
|
+
> `camelCaseOnly` value by default. You can set this back to any other valid option but selectors
|
|
1262
|
+
> which are not valid JavaScript identifiers may run into problems which do not implement the entire modules specification.
|
|
1248
1263
|
|
|
1249
1264
|
Style of exported class names.
|
|
1250
1265
|
|
|
1251
1266
|
###### `string`
|
|
1252
1267
|
|
|
1253
|
-
By default, the exported JSON keys mirror the class names (i.e `
|
|
1268
|
+
By default, the exported JSON keys mirror the class names (i.e `as-is` value).
|
|
1254
1269
|
|
|
1255
|
-
|
|
|
1256
|
-
|
|
|
1257
|
-
|
|
|
1258
|
-
| **`'
|
|
1259
|
-
| **`'
|
|
1260
|
-
|
|
|
1261
|
-
|
|
|
1270
|
+
| Name | Type | Description |
|
|
1271
|
+
| :---------------------: | :------: | :----------------------------------------------------------------------------------------------- |
|
|
1272
|
+
| **`'as-is'`** | `string` | Class names will be exported as is. |
|
|
1273
|
+
| **`'camel-case'`** | `string` | Class names will be camelized, the original class name will not to be removed from the locals |
|
|
1274
|
+
| **`'camel-case-only'`** | `string` | Class names will be camelized, the original class name will be removed from the locals |
|
|
1275
|
+
| **`'dashes'`** | `string` | Only dashes in class names will be camelized |
|
|
1276
|
+
| **`'dashes-only'`** | `string` | Dashes in class names will be camelized, the original class name will be removed from the locals |
|
|
1262
1277
|
|
|
1263
1278
|
**file.css**
|
|
1264
1279
|
|
|
@@ -1284,7 +1299,7 @@ module.exports = {
|
|
|
1284
1299
|
loader: "css-loader",
|
|
1285
1300
|
options: {
|
|
1286
1301
|
modules: {
|
|
1287
|
-
exportLocalsConvention: "
|
|
1302
|
+
exportLocalsConvention: "camel-case-only",
|
|
1288
1303
|
},
|
|
1289
1304
|
},
|
|
1290
1305
|
},
|
|
@@ -1333,7 +1348,7 @@ module.exports = {
|
|
|
1333
1348
|
name.replace(/-/g, "_"),
|
|
1334
1349
|
// dashesCamelCase
|
|
1335
1350
|
name.replace(/-+(\w)/g, (match, firstLetter) =>
|
|
1336
|
-
firstLetter.toUpperCase()
|
|
1351
|
+
firstLetter.toUpperCase(),
|
|
1337
1352
|
),
|
|
1338
1353
|
];
|
|
1339
1354
|
},
|
|
@@ -1381,6 +1396,252 @@ module.exports = {
|
|
|
1381
1396
|
};
|
|
1382
1397
|
```
|
|
1383
1398
|
|
|
1399
|
+
##### `getJSON`
|
|
1400
|
+
|
|
1401
|
+
Type:
|
|
1402
|
+
|
|
1403
|
+
```ts
|
|
1404
|
+
type getJSON = ({
|
|
1405
|
+
resourcePath,
|
|
1406
|
+
imports,
|
|
1407
|
+
exports,
|
|
1408
|
+
replacements,
|
|
1409
|
+
}: {
|
|
1410
|
+
resourcePath: string;
|
|
1411
|
+
imports: object[];
|
|
1412
|
+
exports: object[];
|
|
1413
|
+
replacements: object[];
|
|
1414
|
+
}) => Promise<void> | void;
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
Default: `undefined`
|
|
1418
|
+
|
|
1419
|
+
Enables a callback to output the CSS modules mapping JSON. The callback is invoked with an object containing the following:
|
|
1420
|
+
|
|
1421
|
+
- `resourcePath`: the absolute path of the original resource, e.g., `/foo/bar/baz.module.css`
|
|
1422
|
+
|
|
1423
|
+
- `imports`: an array of import objects with data about import types and file paths, e.g.,
|
|
1424
|
+
|
|
1425
|
+
```json
|
|
1426
|
+
[
|
|
1427
|
+
{
|
|
1428
|
+
"type": "icss_import",
|
|
1429
|
+
"importName": "___CSS_LOADER_ICSS_IMPORT_0___",
|
|
1430
|
+
"url": "\"-!../../../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!../../../../../node_modules/postcss-loader/dist/cjs.js!../../../../../node_modules/sass-loader/dist/cjs.js!../../../../baz.module.css\"",
|
|
1431
|
+
"icss": true,
|
|
1432
|
+
"index": 0
|
|
1433
|
+
}
|
|
1434
|
+
]
|
|
1435
|
+
```
|
|
1436
|
+
|
|
1437
|
+
(Note that this will include all imports, not just those relevant to CSS modules.)
|
|
1438
|
+
|
|
1439
|
+
- `exports`: an array of export objects with exported names and values, e.g.,
|
|
1440
|
+
|
|
1441
|
+
```json
|
|
1442
|
+
[
|
|
1443
|
+
{
|
|
1444
|
+
"name": "main",
|
|
1445
|
+
"value": "D2Oy"
|
|
1446
|
+
}
|
|
1447
|
+
]
|
|
1448
|
+
```
|
|
1449
|
+
|
|
1450
|
+
- `replacements`: an array of import replacement objects used for linking `imports` and `exports`, e.g.,
|
|
1451
|
+
|
|
1452
|
+
```json
|
|
1453
|
+
{
|
|
1454
|
+
"replacementName": "___CSS_LOADER_ICSS_IMPORT_0_REPLACEMENT_0___",
|
|
1455
|
+
"importName": "___CSS_LOADER_ICSS_IMPORT_0___",
|
|
1456
|
+
"localName": "main"
|
|
1457
|
+
}
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
Using `getJSON`, it's possible to output a files with all CSS module mappings.
|
|
1461
|
+
In the following example, we use `getJSON` to cache canonical mappings and
|
|
1462
|
+
add stand-ins for any composed values (through `composes`), and we use a custom plugin
|
|
1463
|
+
to consolidate the values and output them to a file:
|
|
1464
|
+
|
|
1465
|
+
**webpack.config.js**
|
|
1466
|
+
|
|
1467
|
+
```js
|
|
1468
|
+
const path = require("path");
|
|
1469
|
+
const fs = require("fs");
|
|
1470
|
+
|
|
1471
|
+
const CSS_LOADER_REPLACEMENT_REGEX =
|
|
1472
|
+
/(___CSS_LOADER_ICSS_IMPORT_\d+_REPLACEMENT_\d+___)/g;
|
|
1473
|
+
const REPLACEMENT_REGEX = /___REPLACEMENT\[(.*?)]\[(.*?)]___/g;
|
|
1474
|
+
const IDENTIFIER_REGEX = /\[(.*?)]\[(.*?)]/;
|
|
1475
|
+
const replacementsMap = {};
|
|
1476
|
+
const canonicalValuesMap = {};
|
|
1477
|
+
const allExportsJson = {};
|
|
1478
|
+
|
|
1479
|
+
function generateIdentifier(resourcePath, localName) {
|
|
1480
|
+
return `[${resourcePath}][${localName}]`;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function addReplacements(resourcePath, imports, exportsJson, replacements) {
|
|
1484
|
+
const importReplacementsMap = {};
|
|
1485
|
+
|
|
1486
|
+
// create a dict to quickly identify imports and get their absolute stand-in strings in the currently loaded file
|
|
1487
|
+
// e.g., { '___CSS_LOADER_ICSS_IMPORT_0_REPLACEMENT_0___': '___REPLACEMENT[/foo/bar/baz.css][main]___' }
|
|
1488
|
+
importReplacementsMap[resourcePath] = replacements.reduce(
|
|
1489
|
+
(acc, { replacementName, importName, localName }) => {
|
|
1490
|
+
const replacementImportUrl = imports.find(
|
|
1491
|
+
(importData) => importData.importName === importName,
|
|
1492
|
+
).url;
|
|
1493
|
+
const relativePathRe = /.*!(.*)"/;
|
|
1494
|
+
const [, relativePath] = replacementImportUrl.match(relativePathRe);
|
|
1495
|
+
const importPath = path.resolve(path.dirname(resourcePath), relativePath);
|
|
1496
|
+
const identifier = generateIdentifier(importPath, localName);
|
|
1497
|
+
return { ...acc, [replacementName]: `___REPLACEMENT${identifier}___` };
|
|
1498
|
+
},
|
|
1499
|
+
{},
|
|
1500
|
+
);
|
|
1501
|
+
|
|
1502
|
+
// iterate through the raw exports and add stand-in variables
|
|
1503
|
+
// ('___REPLACEMENT[<absolute_path>][<class_name>]___')
|
|
1504
|
+
// to be replaced in the plugin below
|
|
1505
|
+
for (const [localName, classNames] of Object.entries(exportsJson)) {
|
|
1506
|
+
const identifier = generateIdentifier(resourcePath, localName);
|
|
1507
|
+
|
|
1508
|
+
if (CSS_LOADER_REPLACEMENT_REGEX.test(classNames)) {
|
|
1509
|
+
// if there are any replacements needed in the concatenated class names,
|
|
1510
|
+
// add them all to the replacements map to be replaced altogether later
|
|
1511
|
+
replacementsMap[identifier] = classNames.replaceAll(
|
|
1512
|
+
CSS_LOADER_REPLACEMENT_REGEX,
|
|
1513
|
+
(_, replacementName) =>
|
|
1514
|
+
importReplacementsMap[resourcePath][replacementName],
|
|
1515
|
+
);
|
|
1516
|
+
} else {
|
|
1517
|
+
// otherwise, no class names need replacements so we can add them to
|
|
1518
|
+
// canonical values map and all exports JSON verbatim
|
|
1519
|
+
canonicalValuesMap[identifier] = classNames;
|
|
1520
|
+
|
|
1521
|
+
allExportsJson[resourcePath] = allExportsJson[resourcePath] || {};
|
|
1522
|
+
allExportsJson[resourcePath][localName] = classNames;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
function replaceReplacements(classNames) {
|
|
1528
|
+
return classNames.replaceAll(
|
|
1529
|
+
REPLACEMENT_REGEX,
|
|
1530
|
+
(_, resourcePath, localName) => {
|
|
1531
|
+
const identifier = generateIdentifier(resourcePath, localName);
|
|
1532
|
+
|
|
1533
|
+
if (identifier in canonicalValuesMap) {
|
|
1534
|
+
return canonicalValuesMap[identifier];
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
// Recurse through other stand-in that may be imports
|
|
1538
|
+
const canonicalValue = replaceReplacements(replacementsMap[identifier]);
|
|
1539
|
+
|
|
1540
|
+
canonicalValuesMap[identifier] = canonicalValue;
|
|
1541
|
+
|
|
1542
|
+
return canonicalValue;
|
|
1543
|
+
},
|
|
1544
|
+
);
|
|
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
|
+
}
|
|
1564
|
+
|
|
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
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
module.exports = {
|
|
1611
|
+
module: {
|
|
1612
|
+
rules: [
|
|
1613
|
+
{
|
|
1614
|
+
test: /\.css$/i,
|
|
1615
|
+
loader: "css-loader",
|
|
1616
|
+
options: { modules: { getJSON } },
|
|
1617
|
+
},
|
|
1618
|
+
],
|
|
1619
|
+
},
|
|
1620
|
+
plugins: [
|
|
1621
|
+
new CssModulesJsonPlugin({
|
|
1622
|
+
filepath: path.resolve(__dirname, "./output.css.json"),
|
|
1623
|
+
}),
|
|
1624
|
+
],
|
|
1625
|
+
};
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1628
|
+
In the above, all import aliases are replaced with `___REPLACEMENT[<resourcePath>][<localName>]___` in `getJSON`, and they're resolved in the custom plugin. All CSS mappings are contained in `allExportsJson`:
|
|
1629
|
+
|
|
1630
|
+
```json
|
|
1631
|
+
{
|
|
1632
|
+
"foo/bar/baz.module.css": {
|
|
1633
|
+
"main": "D2Oy",
|
|
1634
|
+
"header": "thNN"
|
|
1635
|
+
},
|
|
1636
|
+
"foot/bear/bath.module.css": {
|
|
1637
|
+
"logo": "sqiR",
|
|
1638
|
+
"info": "XMyI"
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
```
|
|
1642
|
+
|
|
1643
|
+
This is saved to a local file named `output.css.json`.
|
|
1644
|
+
|
|
1384
1645
|
### `importLoaders`
|
|
1385
1646
|
|
|
1386
1647
|
Type:
|
|
@@ -1770,7 +2031,8 @@ With the help of the `/* webpackIgnore: true */`comment, it is possible to disab
|
|
|
1770
2031
|
.class {
|
|
1771
2032
|
/* Disabled url handling for the second url in the 'background' declaration */
|
|
1772
2033
|
color: red;
|
|
1773
|
-
background:
|
|
2034
|
+
background:
|
|
2035
|
+
url("./url/img.png"),
|
|
1774
2036
|
/* webpackIgnore: true */ url("./url/img.png");
|
|
1775
2037
|
}
|
|
1776
2038
|
|
|
@@ -1904,7 +2166,7 @@ module.exports = {
|
|
|
1904
2166
|
alias: {
|
|
1905
2167
|
"/assets/unresolved/img.png": path.resolve(
|
|
1906
2168
|
__dirname,
|
|
1907
|
-
"assets/real-path-to-img/img.png"
|
|
2169
|
+
"assets/real-path-to-img/img.png",
|
|
1908
2170
|
),
|
|
1909
2171
|
},
|
|
1910
2172
|
},
|
|
@@ -2029,8 +2291,8 @@ File treated as `CSS Module`.
|
|
|
2029
2291
|
Using both `CSS Module` functionality as well as SCSS variables directly in JavaScript.
|
|
2030
2292
|
|
|
2031
2293
|
```jsx
|
|
2032
|
-
import svars from "variables.scss";
|
|
2033
|
-
import styles from "Component.module.scss";
|
|
2294
|
+
import * as svars from "variables.scss";
|
|
2295
|
+
import * as styles from "Component.module.scss";
|
|
2034
2296
|
|
|
2035
2297
|
// Render DOM with CSS modules class name
|
|
2036
2298
|
// <div className={styles.componentClass}>
|
package/dist/index.js
CHANGED
|
@@ -45,9 +45,9 @@ async function loader(content, map, meta) {
|
|
|
45
45
|
if (this._compilation && this._compilation.options && this._compilation.options.experiments && this._compilation.options.experiments.buildHttp) {
|
|
46
46
|
isSupportAbsoluteURL = true;
|
|
47
47
|
}
|
|
48
|
-
const isSupportDataURL = options.esModule && Boolean("fsStartTime" in this._compiler);
|
|
49
48
|
if ((0, _utils.shouldUseImportPlugin)(options)) {
|
|
50
49
|
plugins.push((0, _plugins.importParser)({
|
|
50
|
+
// TODO need to fix on webpack side, webpack tries to resolve `./runtime/api.js paths like `http://site.com/runtime/api.js`, maybe we should try first request like absolute, the second like a relative to context
|
|
51
51
|
isSupportAbsoluteURL: false,
|
|
52
52
|
isSupportDataURL: false,
|
|
53
53
|
isCSSStyleSheet: options.exportType === "css-style-sheet",
|
|
@@ -63,7 +63,7 @@ async function loader(content, map, meta) {
|
|
|
63
63
|
const needToResolveURL = !options.esModule;
|
|
64
64
|
plugins.push((0, _plugins.urlParser)({
|
|
65
65
|
isSupportAbsoluteURL,
|
|
66
|
-
isSupportDataURL,
|
|
66
|
+
isSupportDataURL: options.esModule,
|
|
67
67
|
imports: urlPluginImports,
|
|
68
68
|
replacements,
|
|
69
69
|
context: this.context,
|
|
@@ -171,5 +171,21 @@ async function loader(content, map, meta) {
|
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
173
173
|
const exportCode = (0, _utils.getExportCode)(exports, replacements, needToUseIcssPlugin, options, isTemplateLiteralSupported);
|
|
174
|
+
const {
|
|
175
|
+
getJSON
|
|
176
|
+
} = options.modules;
|
|
177
|
+
if (typeof getJSON === "function") {
|
|
178
|
+
try {
|
|
179
|
+
await getJSON({
|
|
180
|
+
resourcePath,
|
|
181
|
+
imports,
|
|
182
|
+
exports,
|
|
183
|
+
replacements
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
callback(error);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
174
190
|
callback(null, `${importCode}${moduleCode}${exportCode}`);
|
|
175
191
|
}
|
package/dist/options.json
CHANGED
|
@@ -154,10 +154,14 @@
|
|
|
154
154
|
{
|
|
155
155
|
"enum": [
|
|
156
156
|
"asIs",
|
|
157
|
+
"as-is",
|
|
157
158
|
"camelCase",
|
|
159
|
+
"camel-case",
|
|
158
160
|
"camelCaseOnly",
|
|
161
|
+
"camel-case-only",
|
|
159
162
|
"dashes",
|
|
160
|
-
"dashesOnly"
|
|
163
|
+
"dashesOnly",
|
|
164
|
+
"dashes-only"
|
|
161
165
|
]
|
|
162
166
|
},
|
|
163
167
|
{
|
|
@@ -169,6 +173,11 @@
|
|
|
169
173
|
"description": "Export only locals.",
|
|
170
174
|
"link": "https://github.com/webpack-contrib/css-loader#exportonlylocals",
|
|
171
175
|
"type": "boolean"
|
|
176
|
+
},
|
|
177
|
+
"getJSON": {
|
|
178
|
+
"description": "Allows outputting of CSS modules mapping through a callback.",
|
|
179
|
+
"link": "https://github.com/webpack-contrib/css-loader#getJSON",
|
|
180
|
+
"instanceof": "Function"
|
|
172
181
|
}
|
|
173
182
|
}
|
|
174
183
|
}
|
package/dist/utils.js
CHANGED
|
@@ -42,43 +42,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
const WEBPACK_IGNORE_COMMENT_REGEXP = exports.WEBPACK_IGNORE_COMMENT_REGEXP = /webpackIgnore:(\s+)?(true|false)/;
|
|
45
|
-
const matchRelativePath = /^\.\.?[/\\]/;
|
|
46
|
-
function isAbsolutePath(str) {
|
|
47
|
-
return _path.default.posix.isAbsolute(str) || _path.default.win32.isAbsolute(str);
|
|
48
|
-
}
|
|
49
|
-
function isRelativePath(str) {
|
|
50
|
-
return matchRelativePath.test(str);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// TODO simplify for the next major release
|
|
54
45
|
function stringifyRequest(loaderContext, request) {
|
|
55
|
-
|
|
56
|
-
return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
|
|
57
|
-
}
|
|
58
|
-
const splitted = request.split("!");
|
|
59
|
-
const {
|
|
60
|
-
context
|
|
61
|
-
} = loaderContext;
|
|
62
|
-
return JSON.stringify(splitted.map(part => {
|
|
63
|
-
// First, separate singlePath from query, because the query might contain paths again
|
|
64
|
-
const splittedPart = part.match(/^(.*?)(\?.*)/);
|
|
65
|
-
const query = splittedPart ? splittedPart[2] : "";
|
|
66
|
-
let singlePath = splittedPart ? splittedPart[1] : part;
|
|
67
|
-
if (isAbsolutePath(singlePath) && context) {
|
|
68
|
-
singlePath = _path.default.relative(context, singlePath);
|
|
69
|
-
if (isAbsolutePath(singlePath)) {
|
|
70
|
-
// If singlePath still matches an absolute path, singlePath was on a different drive than context.
|
|
71
|
-
// In this case, we leave the path platform-specific without replacing any separators.
|
|
72
|
-
// @see https://github.com/webpack/loader-utils/pull/14
|
|
73
|
-
return singlePath + query;
|
|
74
|
-
}
|
|
75
|
-
if (isRelativePath(singlePath) === false) {
|
|
76
|
-
// Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules).
|
|
77
|
-
singlePath = `./${singlePath}`;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return singlePath.replace(/\\/g, "/") + query;
|
|
81
|
-
}).join("!"));
|
|
46
|
+
return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
|
|
82
47
|
}
|
|
83
48
|
|
|
84
49
|
// We can't use path.win32.isAbsolute because it also matches paths starting with a forward slash
|
|
@@ -272,7 +237,7 @@ const filenameReservedRegex = /[<>:"/\\|?*]/g;
|
|
|
272
237
|
// eslint-disable-next-line no-control-regex
|
|
273
238
|
const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g;
|
|
274
239
|
function escapeLocalIdent(localident) {
|
|
275
|
-
// TODO simplify
|
|
240
|
+
// TODO simplify?
|
|
276
241
|
return escape(localident
|
|
277
242
|
// For `[hash]` placeholder
|
|
278
243
|
.replace(/^((-?[0-9])|--)/, "_$1").replace(filenameReservedRegex, "-").replace(reControlChars, "-").replace(/\./g, "-"));
|
|
@@ -317,10 +282,8 @@ function defaultGetLocalIdent(loaderContext, localIdentName, localName, options)
|
|
|
317
282
|
}
|
|
318
283
|
let localIdentHash = "";
|
|
319
284
|
for (let tier = 0; localIdentHash.length < hashDigestLength; tier++) {
|
|
320
|
-
// TODO remove this in the next major release
|
|
321
|
-
const hash = loaderContext.utils && typeof loaderContext.utils.createHash === "function" ? loaderContext.utils.createHash(hashFunction) :
|
|
322
285
|
// eslint-disable-next-line no-underscore-dangle
|
|
323
|
-
loaderContext._compiler.webpack.util.createHash(hashFunction);
|
|
286
|
+
const hash = loaderContext._compiler.webpack.util.createHash(hashFunction);
|
|
324
287
|
if (hashSalt) {
|
|
325
288
|
hash.update(hashSalt);
|
|
326
289
|
}
|
|
@@ -440,7 +403,7 @@ function getValidLocalName(localName, exportLocalsConvention) {
|
|
|
440
403
|
}
|
|
441
404
|
const IS_MODULES = /\.module(s)?\.\w+$/i;
|
|
442
405
|
const IS_ICSS = /\.icss\.\w+$/i;
|
|
443
|
-
function getModulesOptions(rawOptions, exportType, loaderContext) {
|
|
406
|
+
function getModulesOptions(rawOptions, esModule, exportType, loaderContext) {
|
|
444
407
|
if (typeof rawOptions.modules === "boolean" && rawOptions.modules === false) {
|
|
445
408
|
return false;
|
|
446
409
|
}
|
|
@@ -470,6 +433,8 @@ function getModulesOptions(rawOptions, exportType, loaderContext) {
|
|
|
470
433
|
outputOptions
|
|
471
434
|
} = loaderContext._compilation;
|
|
472
435
|
const needNamedExport = exportType === "css-style-sheet" || exportType === "string";
|
|
436
|
+
const namedExport = typeof rawModulesOptions.namedExport !== "undefined" ? rawModulesOptions.namedExport : needNamedExport || esModule;
|
|
437
|
+
const exportLocalsConvention = typeof rawModulesOptions.exportLocalsConvention !== "undefined" ? rawModulesOptions.exportLocalsConvention : namedExport ? "as-is" : "camel-case-only";
|
|
473
438
|
const modulesOptions = {
|
|
474
439
|
auto,
|
|
475
440
|
mode: "local",
|
|
@@ -484,21 +449,25 @@ function getModulesOptions(rawOptions, exportType, loaderContext) {
|
|
|
484
449
|
localIdentRegExp: undefined,
|
|
485
450
|
// eslint-disable-next-line no-undefined
|
|
486
451
|
getLocalIdent: undefined,
|
|
487
|
-
|
|
488
|
-
exportLocalsConvention: (rawModulesOptions.namedExport === true || needNamedExport) && typeof rawModulesOptions.exportLocalsConvention === "undefined" ? "camelCaseOnly" : "asIs",
|
|
452
|
+
// TODO improve me and enable by default
|
|
489
453
|
exportOnlyLocals: false,
|
|
490
454
|
...rawModulesOptions,
|
|
491
|
-
|
|
455
|
+
exportLocalsConvention,
|
|
456
|
+
namedExport
|
|
492
457
|
};
|
|
493
|
-
let exportLocalsConventionType;
|
|
494
458
|
if (typeof modulesOptions.exportLocalsConvention === "string") {
|
|
495
|
-
|
|
459
|
+
// eslint-disable-next-line no-shadow
|
|
460
|
+
const {
|
|
461
|
+
exportLocalsConvention
|
|
462
|
+
} = modulesOptions;
|
|
496
463
|
modulesOptions.exportLocalsConvention = name => {
|
|
497
|
-
switch (
|
|
464
|
+
switch (exportLocalsConvention) {
|
|
465
|
+
case "camel-case":
|
|
498
466
|
case "camelCase":
|
|
499
467
|
{
|
|
500
468
|
return [name, camelCase(name)];
|
|
501
469
|
}
|
|
470
|
+
case "camel-case-only":
|
|
502
471
|
case "camelCaseOnly":
|
|
503
472
|
{
|
|
504
473
|
return camelCase(name);
|
|
@@ -507,10 +476,12 @@ function getModulesOptions(rawOptions, exportType, loaderContext) {
|
|
|
507
476
|
{
|
|
508
477
|
return [name, dashesCamelCase(name)];
|
|
509
478
|
}
|
|
479
|
+
case "dashes-only":
|
|
510
480
|
case "dashesOnly":
|
|
511
481
|
{
|
|
512
482
|
return dashesCamelCase(name);
|
|
513
483
|
}
|
|
484
|
+
case "as-is":
|
|
514
485
|
case "asIs":
|
|
515
486
|
default:
|
|
516
487
|
return name;
|
|
@@ -548,33 +519,29 @@ function getModulesOptions(rawOptions, exportType, loaderContext) {
|
|
|
548
519
|
modulesOptions.mode = modulesOptions.mode(loaderContext.resourcePath, loaderContext.resourceQuery, loaderContext.resourceFragment);
|
|
549
520
|
}
|
|
550
521
|
if (needNamedExport) {
|
|
551
|
-
if (
|
|
522
|
+
if (esModule === false) {
|
|
552
523
|
throw new Error("The 'exportType' option with the 'css-style-sheet' or 'string' value requires the 'esModule' option to be enabled");
|
|
553
524
|
}
|
|
554
525
|
if (modulesOptions.namedExport === false) {
|
|
555
526
|
throw new Error("The 'exportType' option with the 'css-style-sheet' or 'string' value requires the 'modules.namedExport' option to be enabled");
|
|
556
527
|
}
|
|
557
528
|
}
|
|
558
|
-
if (modulesOptions.namedExport === true) {
|
|
559
|
-
|
|
560
|
-
throw new Error("The 'modules.namedExport' option requires the 'esModule' option to be enabled");
|
|
561
|
-
}
|
|
562
|
-
if (typeof exportLocalsConventionType === "string" && exportLocalsConventionType !== "asIs" && exportLocalsConventionType !== "camelCaseOnly" && exportLocalsConventionType !== "dashesOnly") {
|
|
563
|
-
throw new Error('The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly" or "dashesOnly"');
|
|
564
|
-
}
|
|
529
|
+
if (modulesOptions.namedExport === true && esModule === false) {
|
|
530
|
+
throw new Error("The 'modules.namedExport' option requires the 'esModule' option to be enabled");
|
|
565
531
|
}
|
|
566
532
|
return modulesOptions;
|
|
567
533
|
}
|
|
568
534
|
function normalizeOptions(rawOptions, loaderContext) {
|
|
569
535
|
const exportType = typeof rawOptions.exportType === "undefined" ? "array" : rawOptions.exportType;
|
|
570
|
-
const
|
|
536
|
+
const esModule = typeof rawOptions.esModule === "undefined" ? true : rawOptions.esModule;
|
|
537
|
+
const modulesOptions = getModulesOptions(rawOptions, esModule, exportType, loaderContext);
|
|
571
538
|
return {
|
|
572
539
|
url: typeof rawOptions.url === "undefined" ? true : rawOptions.url,
|
|
573
540
|
import: typeof rawOptions.import === "undefined" ? true : rawOptions.import,
|
|
574
541
|
modules: modulesOptions,
|
|
575
542
|
sourceMap: typeof rawOptions.sourceMap === "boolean" ? rawOptions.sourceMap : loaderContext.sourceMap,
|
|
576
543
|
importLoaders: typeof rawOptions.importLoaders === "string" ? parseInt(rawOptions.importLoaders, 10) : rawOptions.importLoaders,
|
|
577
|
-
esModule
|
|
544
|
+
esModule,
|
|
578
545
|
exportType
|
|
579
546
|
};
|
|
580
547
|
}
|
|
@@ -869,6 +836,8 @@ function convertToTemplateLiteral(str) {
|
|
|
869
836
|
function dashesCamelCase(str) {
|
|
870
837
|
return str.replace(/-+(\w)/g, (match, firstLetter) => firstLetter.toUpperCase());
|
|
871
838
|
}
|
|
839
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
|
|
840
|
+
const keywords = new Set(["abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with"]);
|
|
872
841
|
function getExportCode(exports, replacements, icssPluginUsed, options, isTemplateLiteralSupported) {
|
|
873
842
|
let code = "// Exports\n";
|
|
874
843
|
if (icssPluginUsed) {
|
|
@@ -879,7 +848,7 @@ function getExportCode(exports, replacements, icssPluginUsed, options, isTemplat
|
|
|
879
848
|
for (const name of normalizedNames) {
|
|
880
849
|
const serializedValue = isTemplateLiteralSupported ? convertToTemplateLiteral(value) : JSON.stringify(value);
|
|
881
850
|
if (options.modules.namedExport) {
|
|
882
|
-
if (
|
|
851
|
+
if (!validIdentifier.test(name) || keywords.has(name)) {
|
|
883
852
|
identifierId += 1;
|
|
884
853
|
const id = `_${identifierId.toString(16)}`;
|
|
885
854
|
localsCode += `var ${id} = ${serializedValue};\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-loader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "css loader module for webpack",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "webpack-contrib/css-loader",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"main": "dist/cjs.js",
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">= 12.
|
|
16
|
+
"node": ">= 18.12.0"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "npm run build -- -w",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"postbuild": "npm run validate:runtime",
|
|
25
25
|
"commitlint": "commitlint --from=master",
|
|
26
26
|
"security": "npm audit --production",
|
|
27
|
-
"lint:prettier": "prettier --list-different .",
|
|
27
|
+
"lint:prettier": "prettier --cache --list-different .",
|
|
28
28
|
"lint:js": "eslint --cache .",
|
|
29
|
-
"lint:spelling": "cspell \"**/*.*\"",
|
|
29
|
+
"lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"",
|
|
30
30
|
"lint": "npm-run-all -l -p \"lint:**\"",
|
|
31
31
|
"fix:js": "npm run lint:js -- --fix",
|
|
32
32
|
"fix:prettier": "npm run lint:prettier -- --write",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
|
|
37
37
|
"pretest": "npm run lint",
|
|
38
38
|
"test": "npm run test:coverage",
|
|
39
|
-
"prepare": "husky
|
|
39
|
+
"prepare": "husky && npm run build",
|
|
40
40
|
"release": "standard-version"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@rspack/core": "0.x || 1.x",
|
|
47
|
-
"webpack": "^5.
|
|
47
|
+
"webpack": "^5.27.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"@rspack/core": {
|
|
@@ -68,38 +68,37 @@
|
|
|
68
68
|
"@babel/cli": "^7.23.4",
|
|
69
69
|
"@babel/core": "^7.23.7",
|
|
70
70
|
"@babel/preset-env": "^7.23.7",
|
|
71
|
-
"@commitlint/cli": "^
|
|
72
|
-
"@commitlint/config-conventional": "^
|
|
71
|
+
"@commitlint/cli": "^19.2.1",
|
|
72
|
+
"@commitlint/config-conventional": "^19.1.0",
|
|
73
73
|
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
|
74
|
-
"babel-jest": "^
|
|
74
|
+
"babel-jest": "^29.7.0",
|
|
75
75
|
"cross-env": "^7.0.3",
|
|
76
|
-
"cspell": "^6.
|
|
77
|
-
"del": "^
|
|
78
|
-
"del-cli": "^4.0.1",
|
|
76
|
+
"cspell": "^8.6.1",
|
|
77
|
+
"del-cli": "^5.1.0",
|
|
79
78
|
"es-check": "^7.1.0",
|
|
80
79
|
"eslint": "^8.54.0",
|
|
81
|
-
"eslint-config-prettier": "^
|
|
80
|
+
"eslint-config-prettier": "^9.1.0",
|
|
82
81
|
"eslint-plugin-import": "^2.29.0",
|
|
83
82
|
"file-loader": "^6.2.0",
|
|
84
|
-
"husky": "^
|
|
85
|
-
"jest": "^
|
|
86
|
-
"jest-environment-jsdom": "^
|
|
83
|
+
"husky": "^9.0.11",
|
|
84
|
+
"jest": "^29.7.0",
|
|
85
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
87
86
|
"less": "^4.2.0",
|
|
88
|
-
"less-loader": "^
|
|
89
|
-
"lint-staged": "^
|
|
90
|
-
"memfs": "^
|
|
87
|
+
"less-loader": "^12.2.0",
|
|
88
|
+
"lint-staged": "^15.2.2",
|
|
89
|
+
"memfs": "^4.8.1",
|
|
91
90
|
"mini-css-extract-plugin": "^2.7.5",
|
|
92
91
|
"npm-run-all": "^4.1.5",
|
|
93
|
-
"postcss-loader": "^
|
|
94
|
-
"postcss-preset-env": "^
|
|
95
|
-
"prettier": "^2.
|
|
92
|
+
"postcss-loader": "^8.1.1",
|
|
93
|
+
"postcss-preset-env": "^9.5.4",
|
|
94
|
+
"prettier": "^3.2.5",
|
|
96
95
|
"sass": "^1.69.7",
|
|
97
|
-
"sass-loader": "^
|
|
96
|
+
"sass-loader": "^14.1.1",
|
|
98
97
|
"standard-version": "^9.5.0",
|
|
99
98
|
"strip-ansi": "^6.0.0",
|
|
100
|
-
"style-loader": "^3.3.
|
|
101
|
-
"stylus": "^0.
|
|
102
|
-
"stylus-loader": "^
|
|
99
|
+
"style-loader": "^3.3.4",
|
|
100
|
+
"stylus": "^0.63.0",
|
|
101
|
+
"stylus-loader": "^8.1.0",
|
|
103
102
|
"url-loader": "^4.1.1",
|
|
104
103
|
"webpack": "^5.89.0"
|
|
105
104
|
},
|