From 64cce93fb45a6cad82877ff2ca1c0b9b9bdda170 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 29 Oct 2020 12:48:06 +0700 Subject: [PATCH 1/2] Support for Less and Stylus --- README.md | 168 +++++++++++-- index.js | 666 ++++++++++++++++++++++++++++++++------------------- package.json | 2 +- 3 files changed, 562 insertions(+), 274 deletions(-) diff --git a/README.md b/README.md index 3e553cb..2e97cc0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ npm npm -Add support for css module laravel mix. +Add support for css module laravel mix. **CSS, SCSS, LESS & STYLUS** ## Installation @@ -12,12 +12,6 @@ Add support for css module laravel mix. npm i laravel-mix-vue-css-modules ``` -## Update v1 to v2 - -``` -npm install laravel-mix-vue-css-modules@latest -``` - ## Usage First, VueCssModules must be enabled. Your `webpack.mix.js` could look like this: @@ -46,7 +40,7 @@ You can then use it in your templates with a dynamic class binding: ```vue ``` @@ -60,24 +54,58 @@ If you only want to use CSS Modules in some of your Vue components, you can set mix.vueCssModules({ oneOf: true }); ``` -#### Pre-Processors +#### Custom Injectname + +```js + + + +``` -CSS Modules can be used along with other pre-processors. default pre-processor is enable. to disable it set `preProcessor` to `false` +## Pre-Processors + +By default all pre-processors are disabled. + +#### For Scss ```js -mix.vueCssModules({ preProcessor: false }); +mix.vueCssModules({ preProcessor: { scss: true } }); +``` + +#### For Less + +`npm i less less-loader --save-dev` + +then set `less` to `true` + +```js +mix.vueCssModules({ preProcessor: { less: true } }); +``` + +#### For Stylus + +`npm i stylus stylus-loader --save-dev` + +then set `stylus` to `true` + +```js +mix.vueCssModules({ preProcessor: { stylus: true } }); ``` #### Custom localIdentName Default: -- `'[path][name]__[local]` for development -- `'[hash:base64]'` for production +- `[path][name]__[local]` for development +- `[hash:base64]` for production ```js mix.vueCssModules({ - cssLoaderOptions: { localIdentName: "[path][name]__[local]" }, + cssLoaderOptions: { localIdentName: "[path][name]__[local]" } }); ``` @@ -103,6 +131,12 @@ Default: `1` mix.vueCssModules({ cssLoaderOptions: { importLoaders: 2 } }); ``` +#### Exclude + +```js +mix.vueCssModules({ exclude: [path.resolve(__dirname, "node-modules")] }); +``` + #### Exclude css you may want some of your css exluded from generated class by css module. @@ -111,12 +145,104 @@ you may want some of your css exluded from generated class by css module. const getLocalIdent = require("css-loader/lib/getLocalIdent"); mix.vueCssModules({ - cssLoaderOptions: { - getLocalIdent: (context, localIdentName, localName, options) => { - return context.resourcePath.includes("x.scss") - ? localName - : getLocalIdent(context, localIdentName, localName, options); - }, - }, + cssLoaderOptions: { + getLocalIdent: (context, localIdentName, localName, options) => { + return context.resourcePath.includes("x.scss") + ? localName + : getLocalIdent(context, localIdentName, localName, options); + } + } }); ``` + +## Example + +```vue + + + + + + + + + + + + + + + + + + + +``` diff --git a/index.js b/index.js index 0ef818f..629e907 100644 --- a/index.js +++ b/index.js @@ -1,266 +1,428 @@ const mix = require("laravel-mix"); class VueCssModules { - /** - * Register the component. - * - * @param {Object} options - * @param {Boolean} [options.oneOf] - * @param {Boolean} [options.preProcessor] - * @param {String} [options.localIdentNameType] - * @param {Object} [options.cssLoaderOptions] - */ - register(options = {}) { - const config = { - modules: true, // {Boolean\|String\|Object} - sourceMap: false, // {Boolean} - importLoaders: 1, // {Number} // webpackDefault: 0 // laravel-mix default: 1 - esModule: true, // {Boolean}, - localIdentName: - options.cssLoaderOptions && options.cssLoaderOptions.localIdentName - ? options.cssLoaderOptions.localIdentName - : this.defaultLocalIdentName(options.localIdentNameType), // {String} - }; - - const cssLoaderOptions = { - ...config, - ...options.cssLoaderOptions, - }; - - delete options.cssLoaderOptions; - - this.options = Object.assign( - { - oneOf: false, - preProcessor: true, - exclude: [], - cssLoaderOptions: cssLoaderOptions, - }, - options - ); - } - - /** - * Override the generated webpack configuration. - * - * @param {Object} config - */ - webpackConfig(config) { - // for css-loader - const cssLoaders = config.module.rules.find( - (rule) => rule.test.toString() === "/\\.css$/" - ); - - if (this.options.oneOf) this.handleOneOfCss(cssLoaders); - else this.handleCss(cssLoaders); - - // only if pre-processor activated || default is active - if (this.options.preProcessor) { - // for sass-loader - const sassLoaders = config.module.rules.find( - (rule) => rule.test.toString() === "/\\.scss$/" - ); - - if (this.options.oneOf) this.handleOneOfPreProcessor(sassLoaders); - else this.handlePreProcessor(sassLoaders); + /** + * Register the component. + * + * @param {Object} options + * @param {Boolean} [options.oneOf] + * @param {Boolean} [options.preProcessor] + * @param {String} [options.localIdentNameType] + * @param {Object} [options.cssLoaderOptions] + */ + register(options = {}) { + const config = { + modules: true, + sourceMap: false, + importLoaders: 1, // laravel-mix default: 1 + esModule: true, + localIdentName: + options.cssLoaderOptions && + options.cssLoaderOptions.localIdentName + ? options.cssLoaderOptions.localIdentName + : this.defaultLocalIdentName(options.localIdentNameType) + }; + + const cssLoaderOptions = { + ...config, + ...options.cssLoaderOptions + }; + + const { cssLoaderOptions: deleted, ...newOptions } = options; + + this.options = Object.assign( + { + oneOf: false, + preProcessor: { + scss: false, + less: false, + stylus: false + }, + exclude: [], + cssLoaderOptions: cssLoaderOptions + }, + newOptions + ); } - } - - /** - * handle normal css-module - * - * @param {*} cssLoaders - * @returns - * @memberof VueCssModule - * - */ - handleCss(cssLoaders) { - this.handleExclude(cssLoaders); - - cssLoaders.loaders.forEach((cssLoader) => { - if (cssLoader.loader === "css-loader") { - Object.assign(cssLoader, { - options: this.options.cssLoaderOptions, + + /** + * Override the generated webpack configuration. + * + * @param {Object} config + */ + webpackConfig(config) { + const cssLoaders = config.module.rules.find( + rule => rule.test.toString() === "/\\.css$/" + ); + + if (this.options.oneOf) this.handleOneOfCss(cssLoaders); + else this.handleCss(cssLoaders); + + if (this.options.preProcessor.scss) { + const scssLoaders = config.module.rules.find( + rule => rule.test.toString() === "/\\.scss$/" + ); + + this.options.oneOf + ? this.handleOneOfScss(scssLoaders) + : this.handleScss(scssLoaders); + } + + if (this.options.preProcessor.less) { + const lessLoaders = config.module.rules.find( + rule => rule.test.toString() === "/\\.less$/" + ); + + this.options.oneOf + ? this.handleOneOfLess(lessLoaders) + : this.handleLess(lessLoaders); + } + + if (this.options.preProcessor.stylus) { + const stylusLoaders = config.module.rules.find( + rule => rule.test.toString() === "/\\.styl(us)?$/" + ); + + this.options.oneOf + ? this.handleOneOfStylus(stylusLoaders) + : this.handleStylus(stylusLoaders); + } + } + + /** + * handle normal css-module + * + * @param {*} cssLoaders + * @returns + * @memberof VueCssModule + * + */ + handleCss(cssLoaders) { + this.handleExclude(cssLoaders); + + cssLoaders.loaders.forEach(cssLoader => { + if (cssLoader.loader === "css-loader") { + Object.assign(cssLoader, { + options: this.options.cssLoaderOptions + }); + } }); - } - }); - - return cssLoaders; - } - - /** - * handle oneOf css-module - * - * @param {*} cssLoaders - * @returns - * @memberof VueCssModule - */ - handleOneOfCss(cssLoaders) { - this.handleExclude(cssLoaders); - - // keep default config for postcss-loader - const postCssLoader = cssLoaders.loaders.find( - (cssLoader) => cssLoader.loader === "postcss-loader" - ); - - // reset loaders change with use - delete cssLoaders.loaders; - - cssLoaders.oneOf = [ - { - resourceQuery: /module/, - use: [ - "style-loader", - { - loader: "css-loader", - options: this.options.cssLoaderOptions, - }, - ], - }, - { - use: ["style-loader", "css-loader", postCssLoader], - }, - ]; - - return cssLoaders; - } - - /** - * handle normal css-module for pre-processcor - * - * @param {*} sassLoaders - * @returns - * @memberof VueCssModule - */ - handlePreProcessor(sassLoaders) { - this.handleExclude(sassLoaders); - - const [postCssLoader, sassLoader] = this.getDefaultPreProcessorConfig( - sassLoaders - ); - - // re-create config & add custom css-loader for .scss - sassLoaders.loaders = [ - "style-loader", - { - loader: "css-loader", - options: this.options.cssLoaderOptions, - }, - postCssLoader, - sassLoader, - ]; - - return sassLoaders; - } - - /** - * handle oneOf css-module for pre-processcor - * - * @param {*} sassLoaders - * @returns - * @memberof VueCssModule - */ - handleOneOfPreProcessor(sassLoaders) { - this.handleExclude(sassLoaders); - - const [postCssLoader, sassLoader] = this.getDefaultPreProcessorConfig( - sassLoaders - ); - - delete sassLoaders.loaders; - - sassLoaders.oneOf = [ - { - resourceQuery: /module/, - use: [ - "style-loader", - { - loader: "css-loader", - options: this.options.cssLoaderOptions, - }, - postCssLoader, - sassLoader, - ], - }, - { - use: ["style-loader", "css-loader", postCssLoader, sassLoader], - }, - ]; - - return sassLoaders; - } - - /** - * get default config from laravel-mix - * - * @param {*} sassLoaders - * @returns - * @memberof VueCssModule - */ - getDefaultPreProcessorConfig(sassLoaders) { - // keep default config for postcss-loader - const postCssLoader = sassLoaders.loaders.find( - (sassLoader) => sassLoader.loader === "postcss-loader" - ); - - // keep default config for sass-loader - const sassLoader = sassLoaders.loaders.find( - (sassLoader) => sassLoader.loader === "sass-loader" - ); - - return [postCssLoader, sassLoader]; - } - - /** - * handle exclude - * - * @param {*} loaders - * @returns - * @memberof VueCssModule - */ - handleExclude(loaders) { - if (this.options.exclude.length > 0) { - if (loaders.exclude === undefined) { - loaders.exclude = this.options.exclude; - } else { - this.options.exclude.forEach((e) => loaders.exclude.push(e)); - } + + return cssLoaders; + } + + /** + * handle oneOf css-module + * + * @param {*} cssLoaders + * @returns + * @memberof VueCssModule + */ + handleOneOfCss(cssLoaders) { + this.handleExclude(cssLoaders); + + const postCssLoader = cssLoaders.loaders.find( + cssLoader => cssLoader.loader === "postcss-loader" + ); + + delete cssLoaders.loaders; + + cssLoaders.oneOf = [ + { + resourceQuery: /module/, + use: [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + } + ] + }, + { + use: ["style-loader", "css-loader", postCssLoader] + } + ]; + + return cssLoaders; + } + + /** + * handle normal css-module for pre-processcor + * + * @param {*} scssLoaders + * @returns + * @memberof VueCssModule + */ + handleScss(scssLoaders) { + this.handleExclude(scssLoaders); + + const [postCssLoader, sassLoader] = this.getDefaultScssConfig( + scssLoaders + ); + + scssLoaders.loaders = [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + sassLoader + ]; + + return scssLoaders; + } + + /** + * handle oneOf css-module for pre-processcor + * + * @param {*} sassLoaders + * @returns + * @memberof VueCssModule + */ + handleOneOfScss(scssLoaders) { + this.handleExclude(scssLoaders); + + const [postCssLoader, sassLoader] = this.getDefaultScssConfig( + scssLoaders + ); + + delete scssLoaders.loaders; + + scssLoaders.oneOf = [ + { + resourceQuery: /module/, + use: [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + sassLoader + ] + }, + { + use: ["style-loader", "css-loader", postCssLoader, sassLoader] + } + ]; + + return scssLoaders; + } + + /** + * handle normal css-module for pre-processcor + * + * @param {*} lessLoaders + * @returns + * @memberof VueCssModule + */ + handleLess(lessLoaders) { + this.handleExclude(lessLoaders); + + const postCssLoader = this.getDefaultLessAndStylusConfig(lessLoaders); + + lessLoaders.loaders = [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + "less-loader" + ]; + + return lessLoaders; } - return loaders; - } - - /** - * get default type for localIdentName - * - * @returns - * @memberof VueCssModule - */ - defaultLocalIdentName(type) { - if (type === "react") { - return this.reactLocalIdentName(); + /** + * + * @param {*} lessLoaders + * @returns + * @memberof VueCssModules + */ + handleOneOfLess(lessLoaders) { + this.handleExclude(lessLoaders); + + const postCssLoader = this.getDefaultLessAndStylusConfig(lessLoaders); + + delete lessLoaders.loaders; + + lessLoaders.oneOf = [ + // oneOf + { + resourceQuery: /module/, + use: [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + "less-loader" + ] + }, + // normal + { + use: [ + "style-loader", + "css-loader", + postCssLoader, + "less-loader" + ] + } + ]; + + return lessLoaders; + } + /** + * + * @param {*} stylusLoaders + * @returns + * @memberof VueCssModules + */ + handleStylus(stylusLoaders) { + this.handleExclude(stylusLoaders); + + const postCssLoader = this.getDefaultLessAndStylusConfig(stylusLoaders); + + stylusLoaders.loaders = [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + "stylus-loader" + ]; + + return stylusLoaders; + } + + /** + * + * @param {*} stylusLoaders + * @returns + * @memberof VueCssModules + */ + handleOneOfStylus(stylusLoaders) { + this.handleExclude(stylusLoaders); + + const postCssLoader = this.getDefaultLessAndStylusConfig(stylusLoaders); + + delete stylusLoaders.loaders; + + stylusLoaders.oneOf = [ + // oneOf + { + resourceQuery: /module/, + use: [ + "style-loader", + { + loader: "css-loader", + options: this.options.cssLoaderOptions + }, + postCssLoader, + "stylus-loader" + ] + }, + // normal + { + use: [ + "style-loader", + "css-loader", + postCssLoader, + "stylus-loader" + ] + } + ]; + + return stylusLoaders; + } + + /** + * get default config from laravel-mix + * + * @param {*} scssLoaders + * @returns + * @memberof VueCssModule + */ + getDefaultScssConfig(scssLoaders) { + const postCssLoader = scssLoaders.loaders.find( + postCssLoader => postCssLoader.loader === "postcss-loader" + ); + + const sassLoader = scssLoaders.loaders.find( + scssLoader => scssLoader.loader === "sass-loader" + ); + + return [postCssLoader, sassLoader]; + } + + /** + * + * @param {*} loaders + * @returns + * @memberof VueCssModules + */ + getDefaultLessAndStylusConfig(loaders) { + const postCssLoader = loaders.loaders.find( + postCssLoader => postCssLoader.loader === "postcss-loader" + ); + + return postCssLoader; + } + + /** + * handle exclude + * + * @param {*} loaders + * @returns + * @memberof VueCssModule + */ + handleExclude(loaders) { + if (this.options.exclude.length > 0) { + if (loaders.exclude === undefined) { + loaders.exclude = this.options.exclude; + } else { + this.options.exclude.forEach(e => loaders.exclude.push(e)); + } + } + + return loaders; } - if (type === "discord") { - return this.discordLocalIdentName(); + /** + * get default type for localIdentName + * + * @returns + * @memberof VueCssModule + */ + defaultLocalIdentName(type) { + if (type === "react") { + return this.reactLocalIdentName(); + } + + if (type === "discord") { + return this.discordLocalIdentName(); + } + + return Mix.inProduction() ? "[hash:base64]" : "[path][name]__[local]"; } - return Mix.inProduction() ? "[hash:base64]" : "[path][name]__[local]"; - } - - /** - * Example localIdentName like react - */ - reactLocalIdentName() { - return "[name]___[local]___[hash:base64:5]"; - } - - /** - * Example localIdentName like discord - */ - discordLocalIdentName() { - return "[local]-[hash:base64:5]"; - } + /** + * Example localIdentName like react + */ + reactLocalIdentName() { + return "[name]___[local]___[hash:base64:5]"; + } + + /** + * Example localIdentName like discord + */ + discordLocalIdentName() { + return "[local]-[hash:base64:5]"; + } } mix.extend("vueCssModules", new VueCssModules()); diff --git a/package.json b/package.json index 5d843b2..eb4ac15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laravel-mix-vue-css-modules", - "version": "2.1.3", + "version": "3.0.0", "description": "A Laravel Mix extension for css modules support.", "main": "index.js", "scripts": { From 6c22a69ecfeabbd2e06c2ae39896436f0fc44b59 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 29 Oct 2020 13:02:21 +0700 Subject: [PATCH 2/2] Update Readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 2e97cc0..d47ced4 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,20 @@ Add support for css module laravel mix. **CSS, SCSS, LESS & STYLUS** npm i laravel-mix-vue-css-modules ``` +## v2 to v3 + +Before + +```js +mix.vueCssModules({ preProcessor: false }); +``` + +After + +```js +mix.vueCssModules({ preProcessor: { scss: true, less: true, stylus: true } }); +``` + ## Usage First, VueCssModules must be enabled. Your `webpack.mix.js` could look like this: