@ -70,12 +70,18 @@ For further usage examples, refer to the usual Browserify documentation. `icssif
Plugin options (all optional):
* __extensions:__ An array of extensions (without the leading dot!) that should be considered "CSS files"; this is useful when you eg. name your files `.postcss` to indicate that you are using non-standard syntax. This list of extensions will *replace* the default list of extensions, so you will need to explicitly specify `"css"` in the list, if you want to keep parsing `.css` files. Defaults to `[ "css" ]`.
* __mode:__ Whether to assume that untagged class names in your CSS (ie. those without a `:local` or `:global` tag) are local or global. Defaults to `"local"`, but you can set this to `"global"` if you want to make the class name mangling *opt-in*. You'll generally want to leave this at the default setting.
* __before:__ PostCSS transforms to run *before* the ICSS transforms, ie. before imports/exports are analyzed. This is usually where you want custom PostCSS plugins to go.
* __after:__ PostCSS transforms to run *after* the ICSS transforms, ie. after mangling the class names, but before bundling it all together into a single file. You'll rarely need to use this.
## Changelog
### v1.2.0 (March 6, 2020)
- __Feature:__ You can now specify custom `extensions` that should be treated as CSS files, besides `".css"`.
@ -17,48 +12,57 @@ function isPostcssPlugin(value) {
// FIXME: When there's >1 icssify instance set up on the Browserify instance (can this happen when a module specifies its own dependency on icssify?), either throw an error (if incompatible?) or merge the two somehow, so that there's only one resulting .css file
functionsetupPipeline(browserify,options){
/* Not shown here: the default 'deps' phase stream will process all the CSS files through our custom transform (defined elsewhere). That transform handles the discovery of CSS dependencies, by pre-processing the CSS files (normalizing 'composes' statements etc. into :import/:export statements), and then extracting all the imports from them, emitting them to Browserify as additional dependencies to fetch. */
letdepsPhase=browserify.pipeline.get("deps");
/* This stream marks CSS files as 'discardable' when they are only required by other CSS files and not by any JS files; this allows for their to be stripped from the final bundle, saving space. */
depsPhase.push(createCssDepsStream());
letsyntaxPhase=browserify.pipeline.get("syntax");
/* These streams sneak the CSS past the JS syntax checker contained in Browserify, by encoding it in a "JS" file that contains a comment (which will always pass validation), and unpacking it into CSS again after the syntax check has occurred. This effectively opts the CSS files out of the syntax check, which only knows about JS. */
syntaxPhase.unshift(createSyntaxHideStream());
syntaxPhase.push(createSyntaxUnhideStream());
letsortPhase=browserify.pipeline.get("sort");
/* This stream re-sorts the dependencies into dependency order using Kahn's algorithm; that is to say, each dependency always appears before the file(s) that depend on it. This is needed for our final CSS processing (when we resolve the exported/imported identifiers between them), to make sure that we've processed all the dependencies of a file before processing the file itself (as otherwise we wouldn't know all the resolve identifiers to use). This is a deterministic algorith, which means it *does not* break the determinism guarantee of Browserify's own hash-sorting mechanism, and shouldn't interfere with anything else. It could be broken by other plugins adding their own sorting mechanism after this one, though; so this plugin should be loaded last. */
sortPhase.push(createKahnSortingStream());
letdedupePhase=browserify.pipeline.get("dedupe");
/* This stream does the actual bundling of CSS files. It does the final processing of all CSS files (ICSS :import/:export resolution, identifier replacing, etc.), then throws away all the files that were marked as 'discardable' earlier, converts the remaining files into JS-formatted class name exports (for use with eg. JSX), and finally inserts the fully-bundled CSS into the loader shim. Ta-da! */
/* Not shown here: the default 'deps' phase stream will process all the CSS files through our custom transform (defined elsewhere). That transform handles the discovery of CSS dependencies, by pre-processing the CSS files (normalizing 'composes' statements etc. into :import/:export statements), and then extracting all the imports from them, emitting them to Browserify as additional dependencies to fetch. */
letdepsPhase=browserify.pipeline.get("deps");
/* This stream marks CSS files as 'discardable' when they are only required by other CSS files and not by any JS files; this allows for their to be stripped from the final bundle, saving space. */
depsPhase.push(createCssDepsStream());
letsyntaxPhase=browserify.pipeline.get("syntax");
/* These streams sneak the CSS past the JS syntax checker contained in Browserify, by encoding it in a "JS" file that contains a comment (which will always pass validation), and unpacking it into CSS again after the syntax check has occurred. This effectively opts the CSS files out of the syntax check, which only knows about JS. */
syntaxPhase.unshift(createSyntaxHideStream());
syntaxPhase.push(createSyntaxUnhideStream());
letsortPhase=browserify.pipeline.get("sort");
/* This stream re-sorts the dependencies into dependency order using Kahn's algorithm; that is to say, each dependency always appears before the file(s) that depend on it. This is needed for our final CSS processing (when we resolve the exported/imported identifiers between them), to make sure that we've processed all the dependencies of a file before processing the file itself (as otherwise we wouldn't know all the resolve identifiers to use). This is a deterministic algorith, which means it *does not* break the determinism guarantee of Browserify's own hash-sorting mechanism, and shouldn't interfere with anything else. It could be broken by other plugins adding their own sorting mechanism after this one, though; so this plugin should be loaded last. */
sortPhase.push(createKahnSortingStream());
letdedupePhase=browserify.pipeline.get("dedupe");
/* This stream does the actual bundling of CSS files. It does the final processing of all CSS files (ICSS :import/:export resolution, identifier replacing, etc.), then throws away all the files that were marked as 'discardable' earlier, converts the remaining files into JS-formatted class name exports (for use with eg. JSX), and finally inserts the fully-bundled CSS into the loader shim. Ta-da! */
// Yes, there is a good reason this is a separate module. In the future we may need to add more heuristics to determine whether a "CSS file" *really* is a CSS file, rather than just going off the filename, and this centralizes the checking logic we'd need to change for that. If this ever occurs, though, keep in mind that this function doesn't always get a *full* file object (sometimes it just gets an object with a filename), so any such heuristics would need to be able to deal with that.
// NOTE: This stream is inserted directly after the 'dedupe' phase, and *not* before the 'pack' phase like in postcssify-icss, because that would break `extract-css` - which inserts itself before the 'label' phase that comes after the 'dedupe' phase, and by which time all the CSS needs to be prepared.
// ... or, if no entry file is specified, go off the current working directory
:process.cwd()
returnstream((item)=>{
// And the same for the loader shim path. All this relative-path stuff is to prevent absolute filesystem URLs from leaking into the output, as those might contain sensitive information.
// ... or, if no entry file is specified, go off the current working directory
:process.cwd()
returnstream((item)=>{
// And the same for the loader shim path. All this relative-path stuff is to prevent absolute filesystem URLs from leaking into the output, as those might contain sensitive information.
// Stockpile this item, to emit at the very end, right before the browser-pack operation (because by that time, we'll have processed and therefore concatenated all the CSS files)
thrownewError("Processed CSS, but global loader was not encountered. This should never happen, please report it as a bug!");
}
});
}elseif(item.file===loaderShimPath){
// Stockpile this item, to emit at the very end, right before the browser-pack operation (because by that time, we'll have processed and therefore concatenated all the CSS files)
// NOTE: We check the `chunk` here, not the `encoding`, because in at least one instance a Buffer was passed in claiming to be a "utf8" string according to the `encoding` argument
// NOTE: We check the `chunk` here, not the `encoding`, because in at least one instance a Buffer was passed in claiming to be a "utf8" string according to the `encoding` argument
// This ensures that whenever *any* CSS file is processed, the global loader file gets included as well (which is the file that eventually inserts the concatenated CSS)
// This ensures that whenever *any* CSS file is processed, the global loader file gets included as well (which is the file that eventually inserts the concatenated CSS)