styles/ indes.css blocks/ apps-banner.css smart-list.css ...
/* main.css */ @import './test.css'; html, body { margin: 0; padding: 0; width: 100%; height: 100%; } body { /* background-color: #a1616e; */ background-color: red; }
/* main.css */ // imports exports.i(require("-!../node_modules/css-loader/index.js!./test.css"), ""); // module exports.push([module.id, "html, body {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100%;\n}\n\nbody {\n /* background-color: #a1616e; */\n background-color: red;\n}\n", ""]); // exports
/* webpack.config.js */ module.exports = { /* … */ module: { rules: [ /* … */ { test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: true, } }, ], }, ], }, };
project/ components/ CoolComponent/ index.js index.css
/* components/CoolComponent/index.css */ .contentWrapper { padding: 8px 16px; background-color: rgba(45, 45, 45, .3); } .title { font-size: 14px; font-weight: bold; } .text { font-size: 12px; }
/* components/CoolComponent/index.js */ import React from 'react'; import styles from './index.css'; export default ({ text }) => ( <div className={styles.contentWrapper}> <div className={styles.title}> Weird title </div> <div className={styles.text}> {text} </div> </div> );
/* AsyncCoolComponent.js */ import Loadable from 'react-loadable'; import Loading from 'path/to/Loading'; export default Loadable({ loader: () => import(/* webpackChunkName: 'CoolComponent' */'path/to/CoolComponent'), loading: Loading, });
/* webpack.config.js */ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const isDev = process.env.NODE_ENV === 'development'; module.exports = { /* ... */ module: { rules: [ { /* ... */ test: /\.css$/, use: [ (isDev ? 'style-loader' : MiniCssExtractPlugin.loader), { loader: 'css-loader', options: { modules: true, }, }, ], }, ], }, plugins: [ /* ... */ ...(isDev ? [] : [ new MiniCssExtractPlugin({ filename: '[name].[contenthash].css', chunkFilename: '[name].[contenthash].css', }), ]), ], };
// GET /main.aff4f72df3711744eabe.css GET /main.43ed5fc03ceb844eab53.js // CoolComponent , JS CSS GET /CoolComponent.3eaa4773dca4fffe0956.css GET /CoolComponent.2462bbdbafd820781fae.js
<div class="rs2inRqijrGnbl0txTQ8v"> <div class="_2AU-QBWt5K2v7J1vRT0hgn"> Weird title </div> <div class="_1DaTAH8Hgn0BQ4H13yRwQ0"> Lorem ipsum dolor sit amet consectetur. </div> </div>
/* webpack.config.js */ const getScopedName = require('path/to/getScopedName'); const isDev = process.env.NODE_ENV === 'development'; /* ... */ module.exports = { /* ... */ module: { rules: [ /* ... */ { test: /\.css$/, use: [ (isDev ? 'style-loader' : MiniCssExtractPlugin.loader), { loader: 'css-loader', options: { modules: true, ...(isDev ? { localIdentName: '[path]_[name]_[local]', } : { getLocalIdent: (context, localIdentName, localName) => ( getScopedName(localName, context.resourcePath) ), }), }, }, ], }, ], }, };
/* getScopedName.js */ /* , CSS */ // const incstr = require('incstr'); const createUniqueIdGenerator = () => { const uniqIds = {}; const generateNextId = incstr.idGenerator({ // d , ad, // Adblock alphabet: 'abcefghijklmnopqrstuvwxyzABCEFGHJKLMNOPQRSTUVWXYZ', }); // return (name) => { if (!uniqIds[name]) { uniqIds[name] = generateNextId(); } return uniqIds[name]; }; }; const localNameIdGenerator = createUniqueIdGenerator(); const componentNameIdGenerator = createUniqueIdGenerator(); module.exports = (localName, resourcePath) => { // , index.css const componentName = resourcePath .split('/') .slice(-2, -1)[0]; const localId = localNameIdGenerator(localName); const componentId = componentNameIdGenerator(componentName); return `${componentId}_${localId}`; };
<div class="src-components-ErrorNotification-_index_content-wrapper"> <div class="src-components-ErrorNotification-_index_title"> Weird title </div> <div class="src-components-ErrorNotification-_index_text"> Lorem ipsum dolor sit amet consectetur. </div> </div>
<div class="e_f"> <div class="e_g"> Weird title </div> <div class="e_h"> Lorem ipsum dolor sit amet consectetur. </div> </div>
/* */ app.bf70bcf8d769b1a17df1.js app.db3d0bd894d38d036117.css /* */ app.1f296b75295ada5a7223.js app.eb2519491a5121158bd2.css
/* getScopedName.js */ const incstr = require('incstr'); // const { getGeneratorData, saveGeneratorData, } = require('./generatorHelpers'); const createUniqueIdGenerator = (generatorIdentifier) => { // const uniqIds = getGeneratorData(generatorIdentifier); const generateNextId = incstr.idGenerator({ alphabet: 'abcefghijklmnopqrstuvwxyzABCEFGHJKLMNOPQRSTUVWXYZ', }); return (name) => { if (!uniqIds[name]) { uniqIds[name] = generateNextId(); // , // // ( debounce ) saveGeneratorData(generatorIdentifier, uniqIds); } return uniqIds[name]; }; }; // , // const localNameIdGenerator = createUniqueIdGenerator('localName'); const componentNameIdGenerator = createUniqueIdGenerator('componentName'); module.exports = (localName, resourcePath) => { const componentName = resourcePath .split('/') .slice(-2, -1)[0]; const localId = localNameIdGenerator(localName); const componentId = componentNameIdGenerator(componentName); return `${componentId}_${localId}`; };
const fs = require('fs'); const path = require('path'); const getGeneratorDataPath = generatorIdentifier => ( path.resolve(__dirname, `meta/${generatorIdentifier}.json`) ); const getGeneratorData = (generatorIdentifier) => { const path = getGeneratorDataPath(generatorIdentifier); if (fs.existsSync(path)) { return require(path); } return {}; }; const saveGeneratorData = (generatorIdentifier, uniqIds) => { const path = getGeneratorDataPath(generatorIdentifier); const data = JSON.stringify(uniqIds, null, 2); fs.writeFileSync(path, data, 'utf-8'); }; module.exports = { getGeneratorData, saveGeneratorData, };
/* .babelrc.js */ // , const getScopedName = require('path/to/getScopedName'); const isDev = process.env.NODE_ENV === 'development'; module.exports = { /* ... */ plugins: [ /* ... */ ['react-css-modules', { generateScopedName: isDev ? '[path]_[name]_[local]' : getScopedName, }], ], };
/* CoolComponent/index.js */ import React from 'react'; import './index.css'; export default ({ text }) => ( <div styleName="content-wrapper"> <div styleName="title"> Weird title </div> <div styleName="text"> {text} </div> </div> );
/* main.24436cbf94546057cae3.js */ /* … */ function(e, t, n) { e.exports = { "content-wrapper": "e_f", title: "e_g", text: "e_h" } } /* … */
/* webpack.config.js */ const path = require('path'); const resolve = relativePath => path.resolve(__dirname, relativePath); const isDev = process.env.NODE_ENV === 'development'; module.exports = { /* ... */ module: { rules: [ /* ... */ { test: /\.css$/, use: [ ...(isDev ? ['style-loader'] : [ resolve('path/to/webpack-loaders/nullLoader'), MiniCssExtractPlugin.loader, ]), { loader: 'css-loader', /* ... */ }, ], }, ], }, };
/* nullLoader.js */ // , module.exports = () => '// empty';
/* main.35f6b05f0496bff2048a.js */ /* … */ function(e, t, n) {} /* … */
/* null-loader */ export default function() { return '// empty (null-loader)'; } export function pitch() { return '// empty (null-loader)'; }
/* .babelrc.js */ module.exports = { plugins: [ 'transform-vue-jsx', ['react-css-modules', { // attributeNames: { styleName: 'class', }, }], ], };
/* */ import './index.css'; const TextComponent = { render(h) { return( <div styleName="text"> Lorem ipsum dolor. </div> ); }, mounted() { console.log('I\'m mounted!'); }, }; export default TextComponent;
/* */ .component1__title { color: red; } /* */ .component2__title { color: green; } .component2__title_red { color: red; }
.component2__title{color:green} .component2__title_red, .component1__title{color:red}
.component1__title{color:red} .component2__title{color:green} .component2__title_red{color:red}
<div class="component1__title component2__title">Some weird title</div>
/* webpack.config.js */ const cssoLoader = require('path/to/cssoLoader'); /* ... */ module.exports = { /* ... */ plugins: [ /* ... */ new cssoLoader(), ], };
/* cssoLoader.js */ const csso = require('csso'); const RawSource = require('webpack-sources/lib/RawSource'); const getScopes = require('./helpers/getScopes'); const isCssFilename = filename => /\.css$/.test(filename); module.exports = class cssoPlugin { apply(compiler) { compiler.hooks.compilation.tap('csso-plugin', (compilation) => { compilation.hooks.optimizeChunkAssets.tapAsync('csso-plugin', (chunks, callback) => { chunks.forEach((chunk) => { // CSS chunk.files.forEach((filename) => { if (!isCssFilename(filename)) { return; } const asset = compilation.assets[filename]; const source = asset.source(); // ast CSS const ast = csso.syntax.parse(source); // const scopes = getScopes(ast); // ast const { ast: compressedAst } = csso.compress(ast, { usage: { scopes, }, }); const minifiedCss = csso.syntax.generate(compressedAst); compilation.assets[filename] = new RawSource(minifiedCss); }); }); callback(); }); }); } } /* sourceMap, , https://github.com/zoobestik/csso-webpack-plugin" */
/* getScopes.js */ /* , , */ const csso = require('csso'); const getComponentId = (className) => { const tokens = className.split('_'); // , // [componentId]_[classNameId], // if (tokens.length !== 2) { return 'default'; } return tokens[0]; }; module.exports = (ast) => { const scopes = {}; // csso.syntax.walk(ast, (node) => { if (node.type !== 'ClassSelector') { return; } const componentId = getComponentId(node.name); if (!scopes[componentId]) { scopes[componentId] = []; } if (!scopes[componentId].includes(node.name)) { scopes[componentId].push(node.name); } }); return Object.values(scopes); };
Source: https://habr.com/ru/post/428800/
All Articles