@mpxjs/webpack-plugin
Version:
mpx compile core
276 lines (236 loc) • 6.94 kB
JavaScript
/* eslint-disable operator-linebreak */
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
// base on css-loader@6.7.1
const postcss = require('postcss')
const postcssPkg = require('postcss/package.json')
const { satisfies } = require('semver')
const CssSyntaxError = require('./CssSyntaxError')
const Warning = require('./Warning')
const schema = require('./options.json')
const { icssParser, importParser, urlParser } = require('./plugins')
const {
normalizeOptions,
shouldUseModulesPlugins,
shouldUseImportPlugin,
shouldUseURLPlugin,
shouldUseIcssPlugin,
getPreRequester,
getExportCode,
getFilter,
getImportCode,
getModuleCode,
getModulesPlugins,
normalizeSourceMap,
sort,
combineRequests,
stringifyRequest
} = require('./utils')
const createHelpers = require('../helpers')
const RN_PRESET_OPTIMISATION = {
reduceInitial: false,
normalizeWhitespace: false,
minifyFontValues: false,
convertValues: false
}
module.exports = async function loader (content, map, meta) {
const rawOptions = this.getOptions(schema)
const plugins = []
const callback = this.async()
const mpx = this.getMpx()
const externals = mpx.externals
const root = mpx.projectRoot
const sourceMap = mpx.cssSourceMap || false
const isRN = ['ios', 'android', 'harmony'].includes(mpx.mode)
let options
try {
options = normalizeOptions(Object.assign({}, rawOptions, { sourceMap }), this)
} catch (error) {
callback(error)
return
}
const replacements = []
const exports = []
if (shouldUseModulesPlugins(options)) {
plugins.push(...getModulesPlugins(options, this))
}
const importPluginImports = []
const importPluginApi = []
let isSupportAbsoluteURL = false
// TODO enable by default in the next major release
if (
this._compilation &&
this._compilation.options &&
this._compilation.options.experiments &&
this._compilation.options.experiments.buildHttp
) {
isSupportAbsoluteURL = true
}
const isSupportDataURL =
options.esModule && Boolean('fsStartTime' in this._compiler)
if (shouldUseImportPlugin(options)) {
const { getRequestString } = createHelpers(this)
plugins.push(
importParser({
isSupportAbsoluteURL: false,
isSupportDataURL: false,
externals,
root,
isCSSStyleSheet: options.exportType === 'css-style-sheet',
loaderContext: this,
imports: importPluginImports,
api: importPluginApi,
filter: options.import.filter,
urlHandler: (url) => {
url = combineRequests(getPreRequester(this)(options.importLoaders), url)
return getRequestString('styles', { src: url }, {
isStatic: true,
issuerResource: this.resource,
fromImport: true
})
}
})
)
}
const urlPluginImports = []
if (shouldUseURLPlugin(options)) {
const needToResolveURL = !options.esModule
plugins.push(
urlParser({
isSupportAbsoluteURL,
isSupportDataURL,
externals,
root,
imports: urlPluginImports,
replacements,
context: this.context,
rootContext: this.rootContext,
filter: getFilter(options.url.filter, this.resourcePath),
resolver: needToResolveURL
? this.getResolve({ mainFiles: [], extensions: [] })
: // eslint-disable-next-line no-undefined
undefined,
urlHandler: (url) => stringifyRequest(this, url)
// Support data urls as input in new URL added in webpack@5.38.0
})
)
}
const icssPluginImports = []
const icssPluginApi = []
const needToUseIcssPlugin = shouldUseIcssPlugin(options)
if (needToUseIcssPlugin) {
plugins.push(
icssParser({
loaderContext: this,
imports: icssPluginImports,
api: icssPluginApi,
replacements,
exports,
urlHandler: (url) =>
stringifyRequest(
this,
combineRequests(getPreRequester(this)(options.importLoaders), url)
)
})
)
}
if (this.minimize) {
const cssnano = require('cssnano')
const minimizeOptions = rawOptions.minimize || {}
const presetOptimisation = Object.assign(
{},
isRN ? RN_PRESET_OPTIMISATION : {},
minimizeOptions.optimisation
)
let cssnanoConfig = {
preset: ['cssnano-preset-default', presetOptimisation]
}
if (minimizeOptions.advanced) {
cssnanoConfig = {
preset: ['cssnano-preset-advanced', presetOptimisation]
}
}
plugins.push(cssnano(cssnanoConfig))
}
// Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
if (meta) {
const { ast } = meta
if (
ast &&
ast.type === 'postcss' &&
satisfies(ast.version, `^${postcssPkg.version}`)
) {
// eslint-disable-next-line no-param-reassign
content = ast.root
}
}
const { resourcePath } = this
let result
try {
result = await postcss(plugins).process(content, {
hideNothingWarning: true,
from: resourcePath,
to: resourcePath,
map: options.sourceMap
? {
prev: map ? normalizeSourceMap(map, resourcePath) : null,
inline: false,
annotation: false
}
: false
})
} catch (error) {
if (error.file) {
this.addDependency(error.file)
}
callback(
error.name === 'CssSyntaxError' ? new CssSyntaxError(error) : error
)
return
}
for (const warning of result.warnings()) {
this.emitWarning(new Warning(warning))
}
const imports = []
.concat(icssPluginImports.sort(sort))
.concat(importPluginImports.sort())
.concat(urlPluginImports.sort(sort))
const api = []
.concat(importPluginApi.sort(sort))
.concat(icssPluginApi.sort(sort))
if (options.modules.exportOnlyLocals !== true) {
imports.unshift({
type: 'api_import',
importName: '___CSS_LOADER_API_IMPORT___',
url: stringifyRequest(this, '!!' + require.resolve('./runtime/api'))
})
if (options.sourceMap) {
imports.unshift({
importName: '___CSS_LOADER_API_SOURCEMAP_IMPORT___',
url: stringifyRequest(this, '!!' + require.resolve('./runtime/sourceMaps'))
})
} else {
imports.unshift({
importName: '___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___',
url: stringifyRequest(this, '!!' + require.resolve('./runtime/noSourceMaps'))
})
}
}
const importCode = getImportCode(imports, options)
let moduleCode
try {
moduleCode = getModuleCode(result, api, replacements, options, this)
} catch (error) {
callback(error)
return
}
const exportCode = getExportCode(
exports,
replacements,
needToUseIcssPlugin,
options
)
callback(null, `${importCode}${moduleCode}${exportCode}`)
}