@quasar/app-webpack
Version:
Quasar Framework App CLI with Webpack
212 lines (185 loc) • 6.27 kB
JavaScript
const ExtractLoader = require('mini-css-extract-plugin').loader
const { merge } = require('webpack-merge')
const path = require('node:path')
const { pathToFileURL } = require('node:url')
const quasarCssPaths = [
path.join('node_modules', 'quasar', 'dist'),
path.join('node_modules', 'quasar', 'src'),
path.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 && path.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
}
async function createRule ({ rule, isModules, pref, loader, loaderOptions }) {
if (pref.isServerBuild === true) {
rule.use('null-loader')
.loader('null-loader')
return
}
if (pref.extract) {
rule.use('mini-css-extract')
.loader(ExtractLoader)
.options({ publicPath: '../' })
}
else {
rule.use('vue-style-loader')
.loader('vue-style-loader')
.options({
sourceMap: pref.sourceMap
})
}
const cssLoaderOptions = {
sourceMap: pref.sourceMap,
url: { filter: shouldRequireUrl },
importLoaders:
1 // stylePostLoader injected by vue-loader
+ 1 // postCSS loader
+ (!pref.extract && pref.minify ? 1 : 0) // postCSS with cssnano
+ (loader ? (loader === 'sass-loader' ? 2 : 1) : 0)
}
if (isModules) {
Object.assign(cssLoaderOptions, {
modules: {
localIdentName: '[name]_[local]_[hash:base64:5]'
}
})
}
rule.use('css-loader')
.loader('css-loader')
.options(cssLoaderOptions)
if (!pref.extract && pref.minify) {
// 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
} ]
})
]
}
})
}
// need a fresh copy, otherwise plugins
// will keep on adding making N duplicates for each one
const { default: postCssConfig } = await import(
pathToFileURL(pref.appPaths.postcssConfigFilename)
+ '?t='
+ Date.now()
)
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.use(loader)
.loader(loader)
.options({
sourceMap: pref.sourceMap,
...loaderOptions
})
if (loader === 'sass-loader') {
if (loaderOptions?.sassOptions?.syntax === 'indented') {
rule.use('quasar-sass-variables-loader')
.loader(pref.cssVariables.loader)
.options(pref.cssVariables.sass)
}
else {
rule.use('quasar-scss-variables-loader')
.loader(pref.cssVariables.loader)
.options(pref.cssVariables.scss)
}
}
}
}
function injectRule (chain, pref, lang, test, loader, loaderOptions) {
const baseRule = chain.module.rule(lang).test(test)
// rules for <style lang="module">
const modulesRule = baseRule.oneOf('modules-query').resourceQuery(/module/)
// rules for *.module.* files
const modulesExtRule = baseRule.oneOf('modules-ext').test(/\.module\.\w+$/)
// rules for normal styles
const normalRule = baseRule.oneOf('normal')
return Promise.all([
createRule({ rule: modulesRule, isModules: true, pref, loader, loaderOptions }),
createRule({ rule: modulesExtRule, isModules: true, pref, loader, loaderOptions }),
createRule({ rule: normalRule, isModules: false, pref, loader, loaderOptions })
])
}
module.exports.injectStyleRules = async function injectStyleRules (chain, pref) {
await injectRule(chain, pref, 'css', /\.css$/)
await injectRule(chain, pref, 'stylus', /\.styl(us)?$/, 'stylus-loader', pref.stylusLoaderOptions)
await injectRule(chain, pref, 'scss', /\.scss$/, 'sass-loader', merge(
{
sassOptions: {
style: /* required for RTL */ 'expanded',
silenceDeprecations: [ 'import', 'global-builtin' ]
},
api: 'modern-compiler'
},
pref.scssLoaderOptions
))
await injectRule(chain, pref, 'sass', /\.sass$/, 'sass-loader', merge(
{
sassOptions: {
style: /* required for RTL */ 'expanded',
syntax: 'indented',
silenceDeprecations: [ 'import', 'global-builtin' ]
},
api: 'modern-compiler'
},
pref.sassLoaderOptions
))
await injectRule(chain, pref, 'less', /\.less$/, 'less-loader', pref.lessLoaderOptions)
}