Created
November 12, 2019 00:30
-
-
Save joepie91/606cd5a48987c484bce027c10f268282 to your computer and use it in GitHub Desktop.
css-loader internals notes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loader utils | |
- parseString: Parse a given string as if it were a JSON-encoded string, mapping single-quote string boundaries to double-quote boundaries or just flat-out making up those boundaries, so that JSON.parse doesn't complain. If cannot be parsed as JSON, just return the string as-is. Seems to be used to decode escape codes in a variety of (non-JSON) strings. | |
- urlToRequest: "Converts some resource URL to a webpack module request." | |
- isUrlRequest: "Before call urlToRequest you need call isUrlRequest to ensure it is requestable url" | |
Docs here: https://www.npmjs.com/package/icss-utils | |
===================== | |
PostCSS | |
Node types: | |
- root: The CSS file itself? | |
- atrule: At-rule, like a media query? | |
- rule: Regular rule, like `#foo .bar { ... }`? | |
- decl: Property declaration, like `color: black` | |
- comment: Comment, like `/* foo bar */` | |
ICSS utils: | |
- replaceValueSymbols: takes a (value, replacementMap), extracts all identifiers (\w + dash sequences), and literal-replaces each one that exists as a key in the `replacementMap` to the corresponding value. | |
- replaceSymbols: takes a (PostCSS AST, replacementMap), and calls replaceValueSymbols for each thing that needs checking for replacements (selectors, at-rule selectors, property values). | |
- extractICSS: takes a PostCSS AST, and return an {icssImports, icssExports} that contains, respectively, all the import and export statements in the AST. | |
Imports are a (sourcePath => (localKey => remoteKey)) mapping. | |
Exports are just an (exportedKey => exportedValue) mapping. The `exportedValue` could (and probably will) just be a class name! | |
The original statements are removed from the AST. | |
======================= | |
PostCSS ecosystem | |
postcss-modules-local-by-default | |
(changes assumed untagged default from global to local) | |
untagged class names -> :local class names | |
:global class names -> untagged class names | |
postcss-modules-scope | |
(assumed global) | |
:local class names -> mangled class names + :export statements for the mapping | |
:global class names -> untagged class names | |
untagged class names -> untagged class names | |
postcss-icss-selectors | |
Depending on mode, either: | |
(assumed local) | |
untagged class names -> mangled class names + :export statements for the mapping? | |
:local class names -> mangled class names + :export statements for the mapping? | |
:global class names -> untagged class names | |
or: | |
(assumed global) | |
:local class names -> mangled class names + :export statements for the mapping? | |
:global class names -> untagged class names | |
untagged class names -> untagged class names | |
postcss-icss-composes | |
composes -> :export statement with concatenated class names | |
postcss-modules-extract-imports | |
external composes -> :import statement + local composes (referencing the temp-generated localKey) | |
postcss-modules-resolve-imports | |
:import statements -> directly inline the imported keys into the CSS wherever the localKey is used, consuming the :import statement | |
postcss-icss-values | |
@value statements -> :export statements for those values + values directly inlined where variable names referenced | |
postcss-modules-values | |
... same? | |
Custom css-loader plugins | |
icss-parser | |
- Extracts (and removes) all :import/:export statements from the CSS | |
- For each :import(url) { remoteKey -> localKey } | |
- index += 1 | |
- Store mapping in importReplacements: remoteKey -> ___CSS_LOADER_IMPORT___${index}___ | |
- Store `icss-import` entry: { url, index, export: localKey } | |
- Get importItemCode for {url, media} | |
- Store `import` entry: { import: importItemCode, item: { url, media } } | |
? If not already queued/processed, queue a bundler import + process of `url` (TODO: Look up in index.js what these `import`-type messages actually do) | |
- Apply all importReplacements to the AST using replaceSymbols | |
- For each :export { exportedKey -> exportedValue } | |
- If any identifier in `exportedValue` exists in `importReplacements`, replace it with the replacement value (this is probably to handle re-exports, because replaceSymbols would presumably ignore :export statements) | |
- Get exportItemCode for `name` | |
- Store `export` entry: { export: exportItemCode, item: { name: exportedKey, value: exportedValue } } | |
import-parser | |
NOTE: This parses @import rules, *not* :import rules! The :import rules are handled by the icss-parser. | |
- Extracts (and removes) all top-level @import statements from the CSS | |
- For each @import(url [mediaQuery]) | |
- Get a list of argument AST nodes using `postcss-value-parser` | |
- Extract the URL at the start, stringifying together a bunch of non-string (unquoted) nodes if necessary -> url | |
- Extract the media specification after that, if one exists (resulting in an empty string if not) -> media | |
- Return {url, media} | |
- Filter down the [{url, media}] list to unique entries only | |
- For each {url, media} | |
- Get importItemCode for {url, media} | |
- Store `import` entry: { import: importItemCode } | |
url-parser | |
TODO | |
Custom css-loader utilities | |
getImportItemCode(item) | |
NOTE: exports.push / exports.i / etc. refer to the "runtime API" in src/runtime/api.js, which gets injected(?) into the output | |
- If the item is a URL (ie. cannot be bundled) | |
- Return `exports.push([ module.id, "@import url($url);", "$media" ]);` | |
- Else | |
- Return `exports.i(require("${toBundlerRequest(url)}"), "$media");` | |
getExportItemCode(item) | |
- Return `module.exports = { ... object of mappings ... }` | |
Runtime API | |
- exports.push(entry): Add single [moduleId, content?, mediaQuery] entry | |
- exports.i(entries, mediaQuery): Batch-add multiple [moduleId, content?, mediaQuery] entries, except for those whose moduleId has already been added, and adding the `mediaQuery` constraint (if any) to each item | |
==================== | |
css-loader plugin pipeline: | |
- postcss-modules-values | |
@value statements -> :export statements for those values + values directly inlined where variable names referenced | |
- postcss-modules-local-by-default | |
(changes assumed untagged default from global to local) | |
untagged class names -> :local class names | |
:global class names -> untagged class names | |
- postcss-modules-extract-imports | |
external composes -> :import statement + local composes (referencing the temp-generated localKey) | |
- postcss-modules-scope | |
:local class names -> mangled class names + :export statements for the mapping | |
:global class names -> untagged class names | |
untagged class names -> untagged class names | |
By this point, CSS contains: | |
1) :import statements from the original input | |
2) :import statements generated from the external composes (aliased to auto-generated untagged class names) | |
3) :export statements from the original input | |
4) :export statements generated from the @value statements | |
5) :export statements generated from the mangled :local/untagged class names | |
6) untagged class names, generated from the :global class names | |
7) mangled class names, generated from the :local/untagged class names | |
8) local composes from the original input | |
8) local composes, referencing auto-generated local aliases, generated from the external composes | |
then, the custom plugin pipeline: | |
- icss-parser | |
:import statements -> `icss-import` messages { url, index, export: localKey } + unique-filtered `import` messages { import, item: { url, media } } | |
:import'ed identifiers in regular CSS -> ___CSS_LOADER_IMPORT___${index}___ placeholders | |
re-:export'ed :import'ed identifiers -> ___CSS_LOADER_IMPORT___${index}___ placeholders | |
:export statements -> `export` messages { export, item: { name, value } } | |
- import-parser | |
- url-parser |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment