add support for `@keyframes`, `animation`, CSS Variables
This commit is contained in:
parent
b5b92dc20e
commit
54fff4dd0a
|
@ -183,7 +183,7 @@ module.exports = __webpack_require__.p + "89a353e9c515885abd8e.png";
|
|||
/******/
|
||||
/******/ var uniqueName = "app";
|
||||
/******/ var loadCssChunkData = (chunkId, link) => {
|
||||
/******/ var data, token = "", token2, exports = {}, exportsWithId = [], i = 0, cc = 1;
|
||||
/******/ var data, token = "", token2, exports = {}, exportsWithId = [], exportsWithDashes = [], i = 0, cc = 1;
|
||||
/******/ try { if(!link) link = loadStylesheet(chunkId); data = link.sheet.cssRules; data = data[data.length - 1].style; } catch(e) { data = getComputedStyle(document.head); }
|
||||
/******/ data = data.getPropertyValue("--webpack-" + uniqueName + "-" + chunkId);
|
||||
/******/ if(!data) return;
|
||||
|
@ -191,8 +191,8 @@ module.exports = __webpack_require__.p + "89a353e9c515885abd8e.png";
|
|||
/******/ cc = data.charCodeAt(i);
|
||||
/******/ if(cc == 40) { token2 = token; token = ""; }
|
||||
/******/ else if(cc == 41) { exports[token2.replace(/^_/, "")] = token.replace(/^_/, ""); token = ""; }
|
||||
/******/ else if(cc == 47) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); token = ""; }
|
||||
/******/ else if(!cc || cc == 44) { token = token.replace(/^_/, ""); exportsWithId.forEach((x) => (exports[x] = uniqueName + "-" + token + "-" + exports[x])); __webpack_require__.r(exports); __webpack_require__.m[token] = ((exports, module) => {
|
||||
/******/ else if(cc == 47 || cc == 37) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); if(cc == 37) exportsWithDashes.push(token); token = ""; }
|
||||
/******/ else if(!cc || cc == 44) { token = token.replace(/^_/, ""); exportsWithId.forEach((x) => (exports[x] = uniqueName + "-" + token + "-" + exports[x])); exportsWithDashes.forEach((x) => (exports[x] = "--" + exports[x])); __webpack_require__.r(exports); __webpack_require__.m[token] = ((exports, module) => {
|
||||
/******/ module.exports = exports;
|
||||
/******/ }).bind(null, exports); token = ""; exports = {}; exportsWithId.length = 0; }
|
||||
/******/ else if(cc == 92) { token += data[++i] }
|
||||
|
@ -427,12 +427,48 @@ body {
|
|||
background: red;
|
||||
}
|
||||
|
||||
:root {
|
||||
--app-6-large: 72px;
|
||||
}
|
||||
|
||||
.app-6-main {
|
||||
font-size: large;
|
||||
font-size: var(--app-6-large);
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
head{--webpack-app-0:_4,_2,_1,_5,main/_6;}
|
||||
head{--webpack-app-0:_4,_2,_1,_5,large%main/_6;}
|
||||
```
|
||||
|
||||
## production
|
||||
|
||||
```javascript
|
||||
@import url("https://fonts.googleapis.com/css?family=Open+Sans");
|
||||
.img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: url(89a353e9c515885abd8e.png);
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
background: green;
|
||||
font-family: "Open Sans";
|
||||
}
|
||||
|
||||
body {
|
||||
background: red;
|
||||
}
|
||||
|
||||
:root {
|
||||
--app-491-b: 72px;
|
||||
}
|
||||
|
||||
.app-491-D {
|
||||
font-size: var(--app-491-b);
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
head{--webpack-app-179:_548,_431,_258,_268,b%D/_491;}
|
||||
```
|
||||
|
||||
# dist/1.output.css
|
||||
|
@ -450,16 +486,16 @@ head{--webpack-app-1:_7;}
|
|||
## Unoptimized
|
||||
|
||||
```
|
||||
assets by chunk 16.6 KiB (name: main)
|
||||
asset output.js 16.2 KiB [emitted] (name: main)
|
||||
asset 0.output.css 333 bytes [emitted] (name: main)
|
||||
assets by chunk 16.8 KiB (name: main)
|
||||
asset output.js 16.4 KiB [emitted] (name: main)
|
||||
asset 0.output.css 385 bytes [emitted] (name: main)
|
||||
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: images/file.png] (auxiliary name: main)
|
||||
asset 1.output.css 49 bytes [emitted]
|
||||
Entrypoint main 16.6 KiB (14.6 KiB) = output.js 16.2 KiB 0.output.css 333 bytes 1 auxiliary asset
|
||||
chunk (runtime: main) output.js, 0.output.css (main) 218 bytes (javascript) 301 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 9.8 KiB (runtime) [entry] [rendered]
|
||||
Entrypoint main 16.8 KiB (14.6 KiB) = output.js 16.4 KiB 0.output.css 385 bytes 1 auxiliary asset
|
||||
chunk (runtime: main) output.js, 0.output.css (main) 218 bytes (javascript) 335 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 9.94 KiB (runtime) [entry] [rendered]
|
||||
> ./example.js main
|
||||
runtime modules 9.8 KiB 9 modules
|
||||
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 301 bytes (css) 42 bytes (css-import) [dependent] 6 modules
|
||||
runtime modules 9.94 KiB 9 modules
|
||||
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 335 bytes (css) 42 bytes (css-import) [dependent] 6 modules
|
||||
./example.js 176 bytes [built] [code generated]
|
||||
[no exports]
|
||||
[used exports unknown]
|
||||
|
@ -476,21 +512,21 @@ webpack 5.64.4 compiled successfully
|
|||
## Production mode
|
||||
|
||||
```
|
||||
assets by chunk 4.1 KiB (name: main)
|
||||
asset output.js 3.77 KiB [emitted] [minimized] (name: main)
|
||||
asset 179.output.css 341 bytes [emitted] (name: main)
|
||||
assets by chunk 4.2 KiB (name: main)
|
||||
asset output.js 3.82 KiB [emitted] [minimized] (name: main)
|
||||
asset 179.output.css 385 bytes [emitted] (name: main)
|
||||
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: images/file.png] (auxiliary name: main)
|
||||
asset 159.output.css 53 bytes [emitted]
|
||||
Entrypoint main 4.1 KiB (14.6 KiB) = output.js 3.77 KiB 179.output.css 341 bytes 1 auxiliary asset
|
||||
Entrypoint main 4.2 KiB (14.6 KiB) = output.js 3.82 KiB 179.output.css 385 bytes 1 auxiliary asset
|
||||
chunk (runtime: main) 159.output.css 23 bytes
|
||||
> ./lazy-style.css ./example.js 4:0-26
|
||||
./lazy-style.css 23 bytes [built] [code generated]
|
||||
[no exports]
|
||||
import() ./lazy-style.css ./example.js 4:0-26
|
||||
chunk (runtime: main) output.js, 179.output.css (main) 218 bytes (javascript) 301 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 9.8 KiB (runtime) [entry] [rendered]
|
||||
chunk (runtime: main) output.js, 179.output.css (main) 218 bytes (javascript) 335 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 9.95 KiB (runtime) [entry] [rendered]
|
||||
> ./example.js main
|
||||
runtime modules 9.8 KiB 9 modules
|
||||
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 301 bytes (css) 42 bytes (css-import) [dependent] 6 modules
|
||||
runtime modules 9.95 KiB 9 modules
|
||||
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 335 bytes (css) 42 bytes (css-import) [dependent] 6 modules
|
||||
./example.js 176 bytes [built] [code generated]
|
||||
[no exports]
|
||||
[no exports used]
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
:root {
|
||||
--large: 72px;
|
||||
}
|
||||
|
||||
.main {
|
||||
font-size: large;
|
||||
font-size: var(--large);
|
||||
color: darkblue;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ _{{dist/output.js}}_
|
|||
_{{dist/0.output.css}}_
|
||||
```
|
||||
|
||||
## production
|
||||
|
||||
```javascript
|
||||
_{{production:dist/179.output.css}}_
|
||||
```
|
||||
|
||||
# dist/1.output.css
|
||||
|
||||
```javascript
|
||||
|
|
|
@ -139,7 +139,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
)};`
|
||||
: "// data-webpack is not used as build has no uniqueName",
|
||||
`var loadCssChunkData = ${runtimeTemplate.basicFunction("chunkId, link", [
|
||||
'var data, token = "", token2, exports = {}, exportsWithId = [], i = 0, cc = 1;',
|
||||
'var data, token = "", token2, exports = {}, exportsWithId = [], exportsWithDashes = [], i = 0, cc = 1;',
|
||||
"try { if(!link) link = loadStylesheet(chunkId); data = link.sheet.cssRules; data = data[data.length - 1].style; } catch(e) { data = getComputedStyle(document.head); }",
|
||||
`data = data.getPropertyValue(${
|
||||
uniqueName
|
||||
|
@ -159,9 +159,11 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
`else if(cc == ${cc(
|
||||
")"
|
||||
)}) { exports[token2.replace(/^_/, "")] = token.replace(/^_/, ""); token = ""; }`,
|
||||
`else if(cc == ${cc(
|
||||
"/"
|
||||
)}) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); token = ""; }`,
|
||||
`else if(cc == ${cc("/")} || cc == ${cc(
|
||||
"%"
|
||||
)}) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); if(cc == ${cc(
|
||||
"%"
|
||||
)}) exportsWithDashes.push(token); token = ""; }`,
|
||||
`else if(!cc || cc == ${cc(
|
||||
","
|
||||
)}) { token = token.replace(/^_/, ""); exportsWithId.forEach(${runtimeTemplate.expressionFunction(
|
||||
|
@ -179,6 +181,9 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
})
|
||||
}`,
|
||||
"x"
|
||||
)}); exportsWithDashes.forEach(${runtimeTemplate.expressionFunction(
|
||||
`exports[x] = "--" + exports[x]`,
|
||||
"x"
|
||||
)}); ${RuntimeGlobals.makeNamespaceObject}(exports); ${
|
||||
RuntimeGlobals.moduleFactories
|
||||
}[token] = (${runtimeTemplate.basicFunction(
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
const { ConcatSource } = require("webpack-sources");
|
||||
const HotUpdateChunk = require("../HotUpdateChunk");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const SelfModuleFactory = require("../SelfModuleFactory");
|
||||
const CssExportDependency = require("../dependencies/CssExportDependency");
|
||||
const CssImportDependency = require("../dependencies/CssImportDependency");
|
||||
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
|
||||
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
|
||||
const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
||||
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
||||
const {
|
||||
|
@ -62,7 +64,7 @@ const escapeCss = (str, omitOptionalUnderscore) => {
|
|||
/[^a-zA-Z0-9_\u0081-\uffff-]/g,
|
||||
s => `\\${s}`
|
||||
);
|
||||
return !omitOptionalUnderscore && /^[0-9_-]/.test(escaped)
|
||||
return !omitOptionalUnderscore && /^(?!--)[0-9_-]/.test(escaped)
|
||||
? `_${escaped}`
|
||||
: escaped;
|
||||
};
|
||||
|
@ -79,6 +81,7 @@ class CssModulesPlugin {
|
|||
compiler.hooks.compilation.tap(
|
||||
plugin,
|
||||
(compilation, { normalModuleFactory }) => {
|
||||
const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
|
||||
compilation.dependencyFactories.set(
|
||||
CssUrlDependency,
|
||||
normalModuleFactory
|
||||
|
@ -91,6 +94,14 @@ class CssModulesPlugin {
|
|||
CssLocalIdentifierDependency,
|
||||
new CssLocalIdentifierDependency.Template()
|
||||
);
|
||||
compilation.dependencyFactories.set(
|
||||
CssSelfLocalIdentifierDependency,
|
||||
selfFactory
|
||||
);
|
||||
compilation.dependencyTemplates.set(
|
||||
CssSelfLocalIdentifierDependency,
|
||||
new CssSelfLocalIdentifierDependency.Template()
|
||||
);
|
||||
compilation.dependencyTemplates.set(
|
||||
CssExportDependency,
|
||||
new CssExportDependency.Template()
|
||||
|
@ -262,11 +273,16 @@ class CssModulesPlugin {
|
|||
metaData.push(
|
||||
`${
|
||||
exports
|
||||
? Array.from(exports, ([n, v]) =>
|
||||
v === `${uniqueName ? uniqueName + "-" : ""}${moduleId}-${n}`
|
||||
? Array.from(exports, ([n, v]) => {
|
||||
const shortcutValue = `${
|
||||
uniqueName ? uniqueName + "-" : ""
|
||||
}${moduleId}-${n}`;
|
||||
return v === shortcutValue
|
||||
? `${escapeCss(n)}/`
|
||||
: `${escapeCss(n)}(${escapeCss(v)})`
|
||||
).join("")
|
||||
: v === "--" + shortcutValue
|
||||
? `${escapeCss(n)}%`
|
||||
: `${escapeCss(n)}(${escapeCss(v)})`;
|
||||
}).join("")
|
||||
: ""
|
||||
}${escapeCss(moduleId)}`
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ const ConstDependency = require("../dependencies/ConstDependency");
|
|||
const CssExportDependency = require("../dependencies/CssExportDependency");
|
||||
const CssImportDependency = require("../dependencies/CssImportDependency");
|
||||
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
|
||||
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
|
||||
const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
||||
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
||||
const walkCssTokens = require("./walkCssTokens");
|
||||
|
@ -70,18 +71,21 @@ class LocConverter {
|
|||
|
||||
const CSS_MODE_TOP_LEVEL = 0;
|
||||
const CSS_MODE_IN_RULE = 1;
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_URL = 2;
|
||||
const CSS_MODE_IN_LOCAL_RULE = 2;
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_URL = 3;
|
||||
// TODO implement layer and supports for @import
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS = 3;
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_MEDIA = 4;
|
||||
const CSS_MODE_AT_OTHER = 5;
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS = 4;
|
||||
const CSS_MODE_AT_IMPORT_EXPECT_MEDIA = 5;
|
||||
const CSS_MODE_AT_OTHER = 6;
|
||||
|
||||
const explainMode = mode => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL:
|
||||
return "parsing top level css";
|
||||
case CSS_MODE_IN_RULE:
|
||||
return "parsing css rule content";
|
||||
return "parsing css rule content (global)";
|
||||
case CSS_MODE_IN_LOCAL_RULE:
|
||||
return "parsing css rule content (local)";
|
||||
case CSS_MODE_AT_IMPORT_EXPECT_URL:
|
||||
return "parsing @import (expecting url)";
|
||||
case CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS:
|
||||
|
@ -124,12 +128,19 @@ class CssParser extends Parser {
|
|||
|
||||
const module = state.module;
|
||||
|
||||
const declaredCssVariables = new Set();
|
||||
|
||||
const locConverter = new LocConverter(source);
|
||||
let mode = CSS_MODE_TOP_LEVEL;
|
||||
let modePos = 0;
|
||||
let modeNestingLevel = 0;
|
||||
let modeData = undefined;
|
||||
let singleClassSelector = undefined;
|
||||
let lastIdentifier = undefined;
|
||||
const modeStack = [];
|
||||
const isTopLevelLocal = () =>
|
||||
modeData === "local" ||
|
||||
(this.defaultMode === "local" && modeData === undefined);
|
||||
const eatWhiteLine = (input, pos) => {
|
||||
for (;;) {
|
||||
const cc = input.charCodeAt(pos);
|
||||
|
@ -240,7 +251,57 @@ class CssParser extends Parser {
|
|||
pos = eatWhiteLine(input, pos);
|
||||
return pos;
|
||||
};
|
||||
const eatPropertyName = eatUntil(":{};");
|
||||
const processLocalDeclaration = (input, pos) => {
|
||||
modeData = undefined;
|
||||
const start = pos;
|
||||
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
|
||||
const propertyNameStart = pos;
|
||||
const [propertyNameEnd, propertyName] = eatText(
|
||||
input,
|
||||
pos,
|
||||
eatPropertyName
|
||||
);
|
||||
if (input.charCodeAt(propertyNameEnd) !== CC_COLON) return start;
|
||||
pos = propertyNameEnd + 1;
|
||||
if (propertyName.startsWith("--")) {
|
||||
// CSS Variable
|
||||
const { line: sl, column: sc } = locConverter.get(propertyNameStart);
|
||||
const { line: el, column: ec } = locConverter.get(propertyNameEnd);
|
||||
const name = propertyName.slice(2);
|
||||
const dep = new CssLocalIdentifierDependency(
|
||||
name,
|
||||
[propertyNameStart, propertyNameEnd],
|
||||
"--"
|
||||
);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
declaredCssVariables.add(name);
|
||||
} else if (
|
||||
propertyName === "animation-name" ||
|
||||
propertyName === "animation"
|
||||
) {
|
||||
modeData = "animation";
|
||||
lastIdentifier = undefined;
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
const processDeclarationValueDone = (input, pos) => {
|
||||
if (modeData === "animation" && lastIdentifier) {
|
||||
const { line: sl, column: sc } = locConverter.get(lastIdentifier[0]);
|
||||
const { line: el, column: ec } = locConverter.get(lastIdentifier[1]);
|
||||
const name = input.slice(lastIdentifier[0], lastIdentifier[1]);
|
||||
const dep = new CssSelfLocalIdentifierDependency(name, lastIdentifier);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
}
|
||||
};
|
||||
const eatKeyframes = eatUntil("{};/");
|
||||
const eatNameInVar = eatUntil(",)};/");
|
||||
walkCssTokens(source, {
|
||||
isSelector: () => {
|
||||
return mode !== CSS_MODE_IN_RULE && mode !== CSS_MODE_IN_LOCAL_RULE;
|
||||
},
|
||||
url: (input, start, end, contentStart, contentEnd) => {
|
||||
const value = cssUnescape(input.slice(contentStart, contentEnd));
|
||||
switch (mode) {
|
||||
|
@ -298,6 +359,27 @@ class CssParser extends Parser {
|
|||
supports: undefined
|
||||
};
|
||||
}
|
||||
if (name === "@keyframes") {
|
||||
let pos = end;
|
||||
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
|
||||
if (pos === input.length) return pos;
|
||||
const [newPos, name] = eatText(input, pos, eatKeyframes);
|
||||
const { line: sl, column: sc } = locConverter.get(pos);
|
||||
const { line: el, column: ec } = locConverter.get(newPos);
|
||||
const dep = new CssLocalIdentifierDependency(name, [pos, newPos]);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
pos = newPos;
|
||||
if (pos === input.length) return pos;
|
||||
if (input.charCodeAt(pos) !== CC_LEFT_CURLY) {
|
||||
throw new Error(
|
||||
`Unexpected ${input[pos]} at ${pos} during parsing of @keyframes (expected '{')`
|
||||
);
|
||||
}
|
||||
mode = CSS_MODE_IN_LOCAL_RULE;
|
||||
modeNestingLevel = 1;
|
||||
return pos + 1;
|
||||
}
|
||||
return end;
|
||||
},
|
||||
semicolon: (input, start, end) => {
|
||||
|
@ -320,18 +402,31 @@ class CssParser extends Parser {
|
|||
module.addDependency(dep);
|
||||
break;
|
||||
}
|
||||
case CSS_MODE_IN_LOCAL_RULE: {
|
||||
processDeclarationValueDone(input, start);
|
||||
return processLocalDeclaration(input, end);
|
||||
}
|
||||
case CSS_MODE_IN_RULE: {
|
||||
return end;
|
||||
}
|
||||
}
|
||||
mode = CSS_MODE_TOP_LEVEL;
|
||||
modeData = undefined;
|
||||
singleClassSelector = undefined;
|
||||
return end;
|
||||
},
|
||||
leftCurlyBracket: (input, start, end) => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL:
|
||||
mode = CSS_MODE_IN_RULE;
|
||||
mode = isTopLevelLocal()
|
||||
? CSS_MODE_IN_LOCAL_RULE
|
||||
: CSS_MODE_IN_RULE;
|
||||
modeNestingLevel = 1;
|
||||
if (mode === CSS_MODE_IN_LOCAL_RULE)
|
||||
return processLocalDeclaration(input, end);
|
||||
break;
|
||||
case CSS_MODE_IN_RULE:
|
||||
case CSS_MODE_IN_LOCAL_RULE:
|
||||
modeNestingLevel++;
|
||||
break;
|
||||
}
|
||||
|
@ -339,9 +434,44 @@ class CssParser extends Parser {
|
|||
},
|
||||
rightCurlyBracket: (input, start, end) => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_IN_LOCAL_RULE:
|
||||
processDeclarationValueDone(input, start);
|
||||
/* falls through */
|
||||
case CSS_MODE_IN_RULE:
|
||||
if (--modeNestingLevel === 0) {
|
||||
mode = CSS_MODE_TOP_LEVEL;
|
||||
modeData = undefined;
|
||||
singleClassSelector = undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return end;
|
||||
},
|
||||
id: (input, start, end) => {
|
||||
singleClassSelector = false;
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL:
|
||||
if (isTopLevelLocal()) {
|
||||
const name = input.slice(start + 1, end);
|
||||
const dep = new CssLocalIdentifierDependency(name, [
|
||||
start + 1,
|
||||
end
|
||||
]);
|
||||
const { line: sl, column: sc } = locConverter.get(start);
|
||||
const { line: el, column: ec } = locConverter.get(end);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return end;
|
||||
},
|
||||
identifier: (input, start, end) => {
|
||||
singleClassSelector = false;
|
||||
switch (mode) {
|
||||
case CSS_MODE_IN_LOCAL_RULE:
|
||||
if (modeData === "animation") {
|
||||
lastIdentifier = [start, end];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -350,18 +480,19 @@ class CssParser extends Parser {
|
|||
class: (input, start, end) => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL: {
|
||||
if (
|
||||
modeData === "local" ||
|
||||
(this.defaultMode === "local" && modeData === undefined)
|
||||
) {
|
||||
const dep = new CssLocalIdentifierDependency(
|
||||
input.slice(start + 1, end),
|
||||
[start + 1, end]
|
||||
);
|
||||
if (isTopLevelLocal()) {
|
||||
const name = input.slice(start + 1, end);
|
||||
const dep = new CssLocalIdentifierDependency(name, [
|
||||
start + 1,
|
||||
end
|
||||
]);
|
||||
const { line: sl, column: sc } = locConverter.get(start);
|
||||
const { line: el, column: ec } = locConverter.get(end);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
if (singleClassSelector === undefined) singleClassSelector = name;
|
||||
} else {
|
||||
singleClassSelector = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -392,6 +523,7 @@ class CssParser extends Parser {
|
|||
return end;
|
||||
},
|
||||
pseudoClass: (input, start, end) => {
|
||||
singleClassSelector = false;
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL: {
|
||||
const name = input.slice(start, end);
|
||||
|
@ -436,12 +568,41 @@ class CssParser extends Parser {
|
|||
}
|
||||
return end;
|
||||
},
|
||||
function: (input, start, end) => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_IN_LOCAL_RULE: {
|
||||
const name = input.slice(start, end - 1);
|
||||
if (name === "var") {
|
||||
let pos = walkCssTokens.eatWhitespaceAndComments(input, end);
|
||||
if (pos === input.length) return pos;
|
||||
const [newPos, name] = eatText(input, pos, eatNameInVar);
|
||||
if (!name.startsWith("--")) return end;
|
||||
const { line: sl, column: sc } = locConverter.get(pos);
|
||||
const { line: el, column: ec } = locConverter.get(newPos);
|
||||
const dep = new CssSelfLocalIdentifierDependency(
|
||||
name.slice(2),
|
||||
[pos, newPos],
|
||||
"--",
|
||||
declaredCssVariables
|
||||
);
|
||||
dep.setLoc(sl, sc, el, ec);
|
||||
module.addDependency(dep);
|
||||
return newPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
},
|
||||
comma: (input, start, end) => {
|
||||
switch (mode) {
|
||||
case CSS_MODE_TOP_LEVEL:
|
||||
modeData = undefined;
|
||||
modeStack.length = 0;
|
||||
break;
|
||||
case CSS_MODE_IN_LOCAL_RULE:
|
||||
processDeclarationValueDone(input, start);
|
||||
break;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
/**
|
||||
* @typedef {Object} CssTokenCallbacks
|
||||
* @property {function(string, number): boolean} isSelector
|
||||
* @property {function(string, number, number, number, number): number=} url
|
||||
* @property {function(string, number, number): number=} string
|
||||
* @property {function(string, number, number): number=} leftParenthesis
|
||||
* @property {function(string, number, number): number=} rightParenthesis
|
||||
* @property {function(string, number, number): number=} pseudoFunction
|
||||
* @property {function(string, number, number): number=} function
|
||||
* @property {function(string, number, number): number=} pseudoClass
|
||||
* @property {function(string, number, number): number=} atKeyword
|
||||
* @property {function(string, number, number): number=} class
|
||||
|
@ -190,7 +192,7 @@ const consumeNumberSign = (input, pos, callbacks) => {
|
|||
const start = pos;
|
||||
pos++;
|
||||
if (pos === input.length) return pos;
|
||||
if (_startsIdentifier(input, pos)) {
|
||||
if (callbacks.isSelector(input, pos) && _startsIdentifier(input, pos)) {
|
||||
pos = _consumeIdentifier(input, pos);
|
||||
if (callbacks.id !== undefined) {
|
||||
return callbacks.id(input, start, pos);
|
||||
|
@ -244,7 +246,8 @@ const consumeDot = (input, pos, callbacks) => {
|
|||
if (pos === input.length) return pos;
|
||||
const cc = input.charCodeAt(pos);
|
||||
if (_isDigit(cc)) return consumeNumericToken(input, pos - 2, callbacks);
|
||||
if (!_startsIdentifier(input, pos)) return pos;
|
||||
if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))
|
||||
return pos;
|
||||
pos = _consumeIdentifier(input, pos);
|
||||
if (callbacks.class !== undefined) return callbacks.class(input, start, pos);
|
||||
return pos;
|
||||
|
@ -264,10 +267,19 @@ const consumeNumericToken = (input, pos, callbacks) => {
|
|||
const consumeOtherIdentifier = (input, pos, callbacks) => {
|
||||
const start = pos;
|
||||
pos = _consumeIdentifier(input, pos);
|
||||
// we could check for CC_LEFT_PARENTHESIS here,
|
||||
// but we don't need that info
|
||||
if (callbacks.identifier !== undefined) {
|
||||
return callbacks.identifier(input, start, pos);
|
||||
if (
|
||||
pos !== input.length &&
|
||||
!callbacks.isSelector(input, pos) &&
|
||||
input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
|
||||
) {
|
||||
pos++;
|
||||
if (callbacks.function !== undefined) {
|
||||
return callbacks.function(input, start, pos);
|
||||
}
|
||||
} else {
|
||||
if (callbacks.identifier !== undefined) {
|
||||
return callbacks.identifier(input, start, pos);
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
@ -349,7 +361,8 @@ const consumePotentialUrl = (input, pos, callbacks) => {
|
|||
const consumePotentialPseudo = (input, pos, callbacks) => {
|
||||
const start = pos;
|
||||
pos++;
|
||||
if (!_startsIdentifier(input, pos)) return pos;
|
||||
if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))
|
||||
return pos;
|
||||
pos = _consumeIdentifier(input, pos);
|
||||
let cc = input.charCodeAt(pos);
|
||||
if (cc === CC_LEFT_PARENTHESIS) {
|
||||
|
|
|
@ -18,11 +18,13 @@ class CssLocalIdentifierDependency extends NullDependency {
|
|||
/**
|
||||
* @param {string} name name
|
||||
* @param {[number, number]} range range
|
||||
* @param {string=} prefix prefix
|
||||
*/
|
||||
constructor(name, range) {
|
||||
constructor(name, range, prefix = "") {
|
||||
super();
|
||||
this.name = name;
|
||||
this.range = range;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
get type() {
|
||||
|
@ -51,6 +53,7 @@ class CssLocalIdentifierDependency extends NullDependency {
|
|||
const { write } = context;
|
||||
write(this.name);
|
||||
write(this.range);
|
||||
write(this.prefix);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
|
@ -58,17 +61,20 @@ class CssLocalIdentifierDependency extends NullDependency {
|
|||
const { read } = context;
|
||||
this.name = read();
|
||||
this.range = read();
|
||||
this.prefix = read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
const escapeCssIdentifier = str => {
|
||||
const escapeCssIdentifier = (str, omitUnderscore) => {
|
||||
const escaped = `${str}`.replace(
|
||||
// cspell:word uffff
|
||||
/[^a-zA-Z0-9_\u0081-\uffff-]/g,
|
||||
s => `\\${s}`
|
||||
);
|
||||
return /^[0-9-]/.test(escaped) ? `_${escaped}` : escaped;
|
||||
return !omitUnderscore && /^(?!--)[0-9-]/.test(escaped)
|
||||
? `_${escaped}`
|
||||
: escaped;
|
||||
};
|
||||
|
||||
CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTemplate extends (
|
||||
|
@ -91,16 +97,15 @@ CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTempla
|
|||
.getUsedName(dep.name, runtime);
|
||||
const moduleId = chunkGraph.getModuleId(module);
|
||||
const identifier =
|
||||
dep.prefix +
|
||||
(runtimeTemplate.outputOptions.uniqueName
|
||||
? runtimeTemplate.outputOptions.uniqueName + "-"
|
||||
: "") +
|
||||
moduleId +
|
||||
"-" +
|
||||
(used || "");
|
||||
(used ? moduleId + "-" + used : "-");
|
||||
source.replace(
|
||||
dep.range[0],
|
||||
dep.range[1] - 1,
|
||||
escapeCssIdentifier(identifier)
|
||||
escapeCssIdentifier(identifier, dep.prefix)
|
||||
);
|
||||
if (used) cssExports.set(used, identifier);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Ivan Kopeykin @vankop
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Dependency = require("../Dependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const CssLocalIdentifierDependency = require("./CssLocalIdentifierDependency");
|
||||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
||||
|
||||
class CssSelfLocalIdentifierDependency extends CssLocalIdentifierDependency {
|
||||
/**
|
||||
* @param {string} name name
|
||||
* @param {[number, number]} range range
|
||||
* @param {string=} prefix prefix
|
||||
* @param {Set<string>=} declaredSet set of declared names (will only be active when in declared set)
|
||||
*/
|
||||
constructor(name, range, prefix = "", declaredSet = undefined) {
|
||||
super(name, range, prefix);
|
||||
this.declaredSet = declaredSet;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "css self local identifier";
|
||||
}
|
||||
|
||||
get category() {
|
||||
return "self";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | null} an identifier to merge equal requests
|
||||
*/
|
||||
getResourceIdentifier() {
|
||||
return `self`;
|
||||
}
|
||||
/**
|
||||
* Returns the exported names
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {ExportsSpec | undefined} export names
|
||||
*/
|
||||
getExports(moduleGraph) {
|
||||
if (this.declaredSet && !this.declaredSet.has(this.name)) return;
|
||||
return super.getExports(moduleGraph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @param {RuntimeSpec} runtime the runtime for which the module is analysed
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph, runtime) {
|
||||
if (this.declaredSet && !this.declaredSet.has(this.name))
|
||||
return Dependency.NO_EXPORTS_REFERENCED;
|
||||
return [[this.name]];
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
const { write } = context;
|
||||
write(this.declaredSet);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
const { read } = context;
|
||||
this.declaredSet = read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
CssSelfLocalIdentifierDependency.Template = class CssSelfLocalIdentifierDependencyTemplate extends (
|
||||
CssLocalIdentifierDependency.Template
|
||||
) {
|
||||
/**
|
||||
* @param {Dependency} dependency the dependency for which the template should be applied
|
||||
* @param {ReplaceSource} source the current replace source which can be modified
|
||||
* @param {DependencyTemplateContext} templateContext the context object
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(dependency, source, templateContext) {
|
||||
const dep = /** @type {CssSelfLocalIdentifierDependency} */ (dependency);
|
||||
if (dep.declaredSet && !dep.declaredSet.has(dep.name)) return;
|
||||
super.apply(dependency, source, templateContext);
|
||||
}
|
||||
};
|
||||
|
||||
makeSerializable(
|
||||
CssSelfLocalIdentifierDependency,
|
||||
"webpack/lib/dependencies/CssSelfLocalIdentifierDependency"
|
||||
);
|
||||
|
||||
module.exports = CssSelfLocalIdentifierDependency;
|
|
@ -71,6 +71,8 @@ module.exports = {
|
|||
require("../dependencies/CssImportDependency"),
|
||||
"dependencies/CssLocalIdentifierDependency": () =>
|
||||
require("../dependencies/CssLocalIdentifierDependency"),
|
||||
"dependencies/CssSelfLocalIdentifierDependency": () =>
|
||||
require("../dependencies/CssSelfLocalIdentifierDependency"),
|
||||
"dependencies/CssExportDependency": () =>
|
||||
require("../dependencies/CssExportDependency"),
|
||||
"dependencies/CssUrlDependency": () =>
|
||||
|
|
|
@ -17,7 +17,13 @@ it("should allow to create css modules", done => {
|
|||
: "./style.module.css-local5 ./style.module.css-local6",
|
||||
nested: prod
|
||||
? "my-app-491-RX undefined my-app-491-X2"
|
||||
: "./style.module.css-nested1 undefined ./style.module.css-nested3"
|
||||
: "./style.module.css-nested1 undefined ./style.module.css-nested3",
|
||||
ident: prod ? "my-app-491-yR" : "./style.module.css-ident",
|
||||
keyframes: prod ? "my-app-491-y3" : "./style.module.css-localkeyframes",
|
||||
animation: prod ? "my-app-491-oQ" : "./style.module.css-animation",
|
||||
vars: prod
|
||||
? "--my-app-491-y4 my-app-491-gR undefined my-app-491-xk"
|
||||
: "--./style.module.css-local-color ./style.module.css-vars undefined ./style.module.css-globalVars"
|
||||
});
|
||||
} catch (e) {
|
||||
return done(e);
|
||||
|
|
|
@ -20,24 +20,47 @@
|
|||
color: pink;
|
||||
}
|
||||
|
||||
/* @keyframes localkeyframes {
|
||||
#ident {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
@keyframes localkeyframes {
|
||||
0% {
|
||||
left: var(--pos1x);
|
||||
top: var(--pos1y);
|
||||
color: var(--theme-color1);
|
||||
}
|
||||
100% {
|
||||
left: var(--pos2x);
|
||||
top: var(--pos2y);
|
||||
color: var(--theme-color2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes localkeyframes2 {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
100% {
|
||||
left: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.animation {
|
||||
animation-name: localkeyframes;
|
||||
animation: 3s ease-in 1s 2 reverse both paused localkeyframes, localkeyframes;
|
||||
} */
|
||||
animation: 3s ease-in 1s 2 reverse both paused localkeyframes, localkeyframes2;
|
||||
--pos1x: 0px;
|
||||
--pos1y: 0px;
|
||||
--pos2x: 10px;
|
||||
--pos2y: 20px;
|
||||
}
|
||||
|
||||
/* .composed {
|
||||
composes: local1;
|
||||
composes: local2;
|
||||
} */
|
||||
|
||||
/* .vars {
|
||||
.vars {
|
||||
color: var(--local-color);
|
||||
--local-color: red;
|
||||
}
|
||||
|
@ -45,4 +68,4 @@
|
|||
.globalVars :global {
|
||||
color: var(--global-color);
|
||||
--global-color: red;
|
||||
} */
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import * as style from "./style.module.css";
|
||||
import { local1, local2, local3, local4 } from "./style.module.css";
|
||||
import { local1, local2, local3, local4, ident } from "./style.module.css";
|
||||
|
||||
export default {
|
||||
global: style.global,
|
||||
class: style.class,
|
||||
local: `${local1} ${local2} ${local3} ${local4}`,
|
||||
local2: `${style.local5} ${style.local6}`,
|
||||
nested: `${style.nested1} ${style.nested2} ${style.nested3}`
|
||||
nested: `${style.nested1} ${style.nested2} ${style.nested3}`,
|
||||
ident,
|
||||
keyframes: style.localkeyframes,
|
||||
animation: style.animation,
|
||||
vars: `${style["local-color"]} ${style.vars} ${style["global-color"]} ${style.globalVars}`
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
module.exports = [
|
||||
[/export 'global' \(imported as 'style'\) was not found/],
|
||||
[/export 'nested2' \(imported as 'style'\) was not found/],
|
||||
[/export 'global-color' \(imported as 'style'\) was not found/],
|
||||
[/export 'global' \(imported as 'style'\) was not found/],
|
||||
[/export 'nested2' \(imported as 'style'\) was not found/]
|
||||
[/export 'nested2' \(imported as 'style'\) was not found/],
|
||||
[/export 'global-color' \(imported as 'style'\) was not found/]
|
||||
];
|
||||
|
|
|
@ -174,6 +174,9 @@ class FakeSheet {
|
|||
);
|
||||
});
|
||||
walkCssTokens(css, {
|
||||
isSelector() {
|
||||
return selector === undefined;
|
||||
},
|
||||
leftCurlyBracket(source, start, end) {
|
||||
if (selector === undefined) {
|
||||
selector = source.slice(last, start).trim();
|
||||
|
|
|
@ -5,6 +5,7 @@ describe("walkCssTokens", () => {
|
|||
it(`should ${name}`, () => {
|
||||
const results = [];
|
||||
walkCssTokens(content, {
|
||||
isSelector: () => true,
|
||||
url: (input, s, e, cs, ce) => {
|
||||
results.push(["url", input.slice(s, e), input.slice(cs, ce)]);
|
||||
return e;
|
||||
|
|
Loading…
Reference in New Issue