UNPKG

quasar-app-extension-ssg

Version:
286 lines (253 loc) 7.36 kB
const { join, win32 } = require('path'); const { merge } = require('webpack-merge'); const appPaths = require('@quasar/app-webpack/lib/app-paths'); const cssVariables = require('@quasar/app-webpack/lib/helpers/css-variables'); const postcssConfigFilename = appPaths.postcssConfigFilename || appPaths.resolve.app('.postcssrc.js'); const quasarCssPaths = [ join('node_modules', 'quasar', 'dist'), join('node_modules', 'quasar', 'src'), join('node_modules', '@quasar'), ]; const absoluteUrlRE = /^[a-z][a-z0-9+.-]*:/i; const protocolRelativeRE = /^\/\//; const templateUrlRE = /^[{}[\]#*;,'§$%&(=?`´^°<>]/; const rootRelativeUrlRE = /^\//; /** * Inspired by loader-utils > isUrlRequest() * Mimics Webpack v4 & css-loader v3 behavior */ function shouldRequireUrl(url) { return ( // an absolute url and it is not `windows` path like `C:\dir\file`: (absoluteUrlRE.test(url) === true && win32.isAbsolute(url) === false) // a protocol-relative: || protocolRelativeRE.test(url) === true // some kind of url for a template: || templateUrlRE.test(url) === true // not a request if root isn't set and it's a root-relative url || rootRelativeUrlRE.test(url) === true ) === false; } function create( rule, modules, pref, loader, loaderOptions, ) { if (rule.uses.has('null-loader')) { rule.uses.delete('null-loader'); } if (rule.uses.has('mini-css-extract')) { rule.uses.delete('mini-css-extract'); } /** * replace vue-style-loader by @freddy38510/vue-style-loader * which has consistent ssrId hashes between client and server * https://github.com/freddy38510/vue-style-loader/commit/54d6307692790136dfbda64ead5f8ad35d46390f */ if (rule.uses.has('vue-style-loader')) { rule .use('vue-style-loader') .loader(require.resolve('@freddy38510/vue-style-loader')) .tap((options) => merge(options, { ssrId: true, })); } else { rule .use('vue-style-loader') .loader(require.resolve('@freddy38510/vue-style-loader')) .options({ sourceMap: pref.sourceMap, ssrId: true, }) .before('css-loader'); } let numberOfLoaders = loader ? 1 : 0; if (loader === 'sass-loader') { numberOfLoaders = 2; } const cssLoaderOptions = { sourceMap: pref.sourceMap, url: { filter: shouldRequireUrl }, importLoaders: 1 // stylePostLoader injected by vue-loader + 1 // postCSS loader + (pref.minify ? 1 : 0) // postCSS with cssnano + numberOfLoaders, }; if (modules) { Object.assign(cssLoaderOptions, { modules: { localIdentName: '[name]_[local]_[hash:base64:5]', exportOnlyLocals: false, }, }); } if (rule.uses.has('css-loader')) { rule .use('css-loader') .tap(() => cssLoaderOptions); } else { rule.use('css-loader') .loader('css-loader') .options(cssLoaderOptions); } if (pref.minify && !rule.uses.has('cssnano')) { // needs to be applied separately, // otherwise it messes up RTL rule .use('cssnano') .loader('postcss-loader') .options({ sourceMap: pref.sourceMap, postcssOptions: { plugins: [ require('cssnano')({ preset: ['default', { mergeLonghand: false, convertValues: false, cssDeclarationSorter: false, reduceTransforms: false, }], }), ], }, }); } if (!rule.uses.has('postcss-loader')) { // need a fresh copy, otherwise plugins // will keep on adding making N duplicates for each one delete require.cache[postcssConfigFilename]; const postCssConfig = require(postcssConfigFilename); let postCssOpts = { sourceMap: pref.sourceMap, ...postCssConfig }; if (pref.rtl) { const postcssRTL = require('postcss-rtlcss'); const postcssRTLOptions = pref.rtl === true ? {} : pref.rtl; if ( typeof postCssConfig.plugins !== 'function' && (postcssRTLOptions.source === 'ltr' || typeof postcssRTLOptions === 'function') ) { const originalPlugins = postCssOpts.plugins ? [...postCssOpts.plugins] : []; postCssOpts = (ctx) => { const plugins = [...originalPlugins]; const isClientCSS = quasarCssPaths.every((item) => ctx.resourcePath.indexOf(item) === -1); plugins.push(postcssRTL( typeof postcssRTLOptions === 'function' ? postcssRTLOptions(isClientCSS, ctx.resourcePath) : { ...postcssRTLOptions, source: isClientCSS ? 'rtl' : 'ltr', }, )); return { sourceMap: pref.sourceMap, plugins }; }; } else { postCssOpts.plugins.push(postcssRTL(postcssRTLOptions)); } } rule.use('postcss-loader') .loader('postcss-loader') .options({ postcssOptions: postCssOpts }); } if (loader && !rule.uses.has(loader)) { rule .use(loader) .loader(loader) .options({ sourceMap: pref.sourceMap, ...loaderOptions, }); if (loader === 'sass-loader') { if ( loaderOptions && loaderOptions.sassOptions && loaderOptions.sassOptions.indentedSyntax ) { rule .use('quasar-sass-variables-loader') .loader(cssVariables.loaders.sass); } else { rule .use('quasar-scss-variables-loader') .loader(cssVariables.loaders.scss); } } } } function injectRule( chain, pref, lang, test, loader = undefined, loaderOptions = undefined, ) { const baseRule = chain.module.rule(lang).test(test).after('mjs'); if (pref.inlineCssFromSFC) { // rules for Vue SFC <style module> const modulesRule = baseRule.oneOf('modules-query').resourceQuery(/module/); create(modulesRule, true, pref, loader, loaderOptions); // rules for Vue SFC <style> const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/).after('modules-query'); create(vueNormalRule, false, pref, loader, loaderOptions); } if (!pref.extract) { // rules for *.module.* files const modulesExtRule = baseRule.oneOf('modules-ext').test(/\.module\.\w+$/); create(modulesExtRule, true, pref, loader, loaderOptions); const normalRule = baseRule.oneOf('normal'); create(normalRule, false, pref, loader, loaderOptions); } } module.exports = function injectSFCStyleRules(chain, pref) { injectRule( chain, pref, 'css', /\.css$/, ); injectRule( chain, pref, 'stylus', /\.styl(us)?$/, 'stylus-loader', pref.stylusLoaderOptions, ); injectRule( chain, pref, 'scss', /\.scss$/, 'sass-loader', merge( { sassOptions: { outputStyle: /* required for RTL */ 'expanded' } }, pref.scssLoaderOptions, ), ); injectRule( chain, pref, 'sass', /\.sass$/, 'sass-loader', merge( { sassOptions: { indentedSyntax: true, outputStyle: /* required for RTL */ 'expanded', }, }, pref.sassLoaderOptions, ), ); injectRule( chain, pref, 'less', /\.less$/, 'less-loader', pref.lessLoaderOptions, ); };