"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.hoistAtRules = exports.minifyCSS = exports.importCssRE = exports.cssDataUriRE = exports.cssUrlRE = exports.formatPostcssSourceMap = exports.cssPostPlugin = exports.cssPlugin = exports.isDirectCSSRequest = exports.isCSSRequest = exports.commonjsProxyRE = exports.cssLangRE = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const fast_glob_1 = __importDefault(require("fast-glob")); const picocolors_1 = __importDefault(require("picocolors")); const postcss_load_config_1 = __importDefault(require("postcss-load-config")); const pluginutils_1 = require("@rollup/pluginutils"); const utils_1 = require("../utils"); const asset_1 = require("./asset"); const magic_string_1 = __importDefault(require("magic-string")); const esbuild_1 = require("esbuild"); const preprocess_1 = require("../../../../preprocess"); const constants_1 = require("../../../../constants"); const cleanString_1 = require("../cleanString"); const shared_1 = require("@vue/shared"); const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)`; exports.cssLangRE = new RegExp(cssLangs); const cssModuleRE = new RegExp(`\\.module${cssLangs}`); const directRequestRE = /(\?|&)direct\b/; exports.commonjsProxyRE = /\?commonjs-proxy/; const varRE = /^var\(/i; const isCSSRequest = (request) => exports.cssLangRE.test(request) && !directRequestRE.test(request); exports.isCSSRequest = isCSSRequest; const isDirectCSSRequest = (request) => exports.cssLangRE.test(request) && directRequestRE.test(request); exports.isDirectCSSRequest = isDirectCSSRequest; const cssModulesCache = new WeakMap(); const postcssConfigCache = new WeakMap(); /** * Plugin applied before user plugins */ function cssPlugin(config) { let server; let moduleCache; const resolveUrl = config.createResolver({ preferRelative: true, tryIndex: false, extensions: [], }); const atImportResolvers = createCSSResolvers(config); return { name: 'vite:css', configureServer(_server) { server = _server; }, buildStart() { // Ensure a new cache for every build (i.e. rebuilding in watch mode) moduleCache = new Map(); cssModulesCache.set(config, moduleCache); }, async transform(raw, id) { if (!exports.cssLangRE.test(id) || exports.commonjsProxyRE.test(id)) { return; } const urlReplacer = async (url, importer) => { if (url.startsWith('/') && !url.startsWith('//')) { // /static/logo.png => @/static/logo.png url = '@' + url; } const resolved = await resolveUrl(url, importer); if (resolved) { return (0, asset_1.fileToUrl)(resolved, config, this, true); } return url; }; const { code: css, modules, deps, } = await compileCSS(id, raw, config, urlReplacer, atImportResolvers, server); if (modules) { moduleCache.set(id, modules); } // track deps for build watch mode if (config.command === 'build' && config.build.watch && deps) { for (const file of deps) { this.addWatchFile(file); } } return { code: css, // TODO CSS source map map: { mappings: '' }, }; }, }; } exports.cssPlugin = cssPlugin; function findCssModuleIds(moduleId, cssModuleIds, seen) { if (!cssModuleIds) { cssModuleIds = new Set(); } if (!seen) { seen = new Set(); } if (seen.has(moduleId)) { return cssModuleIds; } seen.add(moduleId); const moduleInfo = this.getModuleInfo(moduleId); if (moduleInfo) { moduleInfo.importedIds.forEach((id) => { if (id.includes(constants_1.PAGES_JSON_JS)) { // 查询main.js时,需要忽略pages.json.js,否则会把所有页面样式加进来 return; } if (exports.cssLangRE.test(id) && !exports.commonjsProxyRE.test(id)) { cssModuleIds.add(id); } else { findCssModuleIds.call(this, id, cssModuleIds, seen); } }); } return cssModuleIds; } /** * Plugin applied after user plugins */ function cssPostPlugin(config, { chunkCssFilename, chunkCssCode, }) { // styles initialization in buildStart causes a styling loss in watch const styles = new Map(); let cssChunks; return { name: 'vite:css-post', buildStart() { cssChunks = new Map(); }, async transform(css, id) { if (!exports.cssLangRE.test(id) || exports.commonjsProxyRE.test(id)) { return; } const modules = cssModulesCache.get(config).get(id); const modulesCode = modules && (0, pluginutils_1.dataToEsm)(modules, { namedExports: true, preferConst: true }); // build CSS handling ---------------------------------------------------- styles.set(id, css); return { code: modulesCode || '', map: { mappings: '' }, // avoid the css module from being tree-shaken so that we can retrieve // it in renderChunk() moduleSideEffects: 'no-treeshake', }; }, async generateBundle() { const moduleIds = Array.from(this.getModuleIds()); moduleIds.forEach((id) => { const filename = chunkCssFilename(id); if (filename) { cssChunks.set(filename, findCssModuleIds.call(this, id)); } }); if (!cssChunks.size) { return; } // resolve asset URL placeholders to their built file URLs and perform // minification if necessary const processChunkCSS = async (css, { filename, inlined, minify, }) => { // replace asset url references with resolved url. css = css.replace(asset_1.assetUrlRE, (_, fileHash, postfix = '') => { return (0, utils_1.normalizePath)(path_1.default.relative(path_1.default.dirname(filename), (0, asset_1.getAssetFilename)(fileHash, config) + postfix)); }); // only external @imports and @charset should exist at this point // hoist them to the top of the CSS chunk per spec (#1845 and #6333) if (css.includes('@import') || css.includes('@charset')) { css = await hoistAtRules(css); } if (minify && config.build.minify) { css = await minifyCSS(css, config); } // 压缩后再处理,小程序平台会补充 @import nvue 代码,esbuild 的压缩会把 `@import "./nvue.css";` 的空格移除,变成 `@import"./nvue.css";` 在支付宝小程序中不支持 return chunkCssCode(filename, css); }; const genCssCode = (fileName) => { return [...cssChunks.get(fileName)] .map((id) => styles.get(id) || '') .join('\n'); }; for (const filename of cssChunks.keys()) { const cssCode = genCssCode(filename); let source = await processChunkCSS(cssCode, { filename: filename, inlined: false, minify: true, }); this.emitFile({ fileName: filename, type: 'asset', source, }); } }, }; } exports.cssPostPlugin = cssPostPlugin; function createCSSResolvers(config) { let cssResolve; let sassResolve; let lessResolve; return { get css() { return (cssResolve || (cssResolve = config.createResolver({ extensions: ['.css'], mainFields: ['style'], tryIndex: false, preferRelative: true, }))); }, get sass() { return (sassResolve || (sassResolve = config.createResolver({ extensions: ['.scss', '.sass', '.css'], mainFields: ['sass', 'style'], tryIndex: true, tryPrefix: '_', preferRelative: true, }))); }, get less() { return (lessResolve || (lessResolve = config.createResolver({ extensions: ['.less', '.css'], mainFields: ['less', 'style'], tryIndex: false, preferRelative: true, }))); }, }; } function getCssResolversKeys(resolvers) { return Object.keys(resolvers); } async function compileCSS(id, code, config, urlReplacer, atImportResolvers, server) { var _a; const { modules: modulesOptions, preprocessorOptions, devSourcemap, } = config.css || {}; const isModule = modulesOptions !== false && cssModuleRE.test(id); // although at serve time it can work without processing, we do need to // crawl them in order to register watch dependencies. const needInlineImport = code.includes('@import'); const hasUrl = exports.cssUrlRE.test(code) || cssImageSetRE.test(code); const postcssConfig = await resolvePostcssConfig(config); const lang = (_a = id.match(exports.cssLangRE)) === null || _a === void 0 ? void 0 : _a[1]; // 1. plain css that needs no processing if (lang === 'css' && !postcssConfig && !isModule && !needInlineImport && !hasUrl) { return { code, map: null }; } let preprocessorMap; let modules; const deps = new Set(); // 2. pre-processors: sass etc. if (isPreProcessor(lang)) { const preProcessor = preProcessors[lang]; let opts = (preprocessorOptions && preprocessorOptions[lang]) || {}; // support @import from node dependencies by default switch (lang) { case "scss" /* PreprocessLang.scss */: case "sass" /* PreprocessLang.sass */: opts = { includePaths: ['node_modules'], alias: config.resolve.alias, ...opts, }; break; case "less" /* PreprocessLang.less */: case "styl" /* PreprocessLang.styl */: case "stylus" /* PreprocessLang.stylus */: opts = { paths: ['node_modules'], alias: config.resolve.alias, ...opts, }; } // important: set this for relative import resolving opts.filename = (0, utils_1.cleanUrl)(id); opts.enableSourcemap = devSourcemap !== null && devSourcemap !== void 0 ? devSourcemap : false; const preprocessResult = await preProcessor(code, config.root, opts, atImportResolvers, !!config.nvue); if (preprocessResult.errors.length) { throw preprocessResult.errors[0]; } // TODO 升级 // if (preprocessResult.error) { // throw preprocessResult.error // } code = preprocessResult.code; preprocessorMap = combineSourcemapsIfExists(opts.filename, preprocessResult.map, preprocessResult.additionalMap); if (preprocessResult.deps) { preprocessResult.deps.forEach((dep) => { // sometimes sass registers the file itself as a dep if ((0, utils_1.normalizePath)(dep) !== (0, utils_1.normalizePath)(opts.filename)) { deps.add(dep); } }); } } // 3. postcss const postcssOptions = (postcssConfig && postcssConfig.options) || {}; const postcssPlugins = postcssConfig && postcssConfig.plugins ? postcssConfig.plugins.slice() : []; if (needInlineImport) { postcssPlugins.unshift((await Promise.resolve().then(() => __importStar(require('postcss-import')))).default({ async resolve(id, basedir) { // const publicFile = checkPublicFile(id, config) // if (publicFile) { // return publicFile // } const resolved = await atImportResolvers.css(id, path_1.default.join(basedir, '*')); if (resolved) { return path_1.default.resolve(resolved); } return id; }, })); } postcssPlugins.push(UrlRewritePostcssPlugin({ replacer: urlReplacer, })); if (isModule) { postcssPlugins.unshift((await Promise.resolve().then(() => __importStar(require('postcss-modules')))).default({ ...modulesOptions, getJSON(cssFileName, _modules, outputFileName) { modules = _modules; if (modulesOptions && (0, shared_1.isFunction)(modulesOptions.getJSON)) { modulesOptions.getJSON(cssFileName, _modules, outputFileName); } }, async resolve(id) { for (const key of getCssResolversKeys(atImportResolvers)) { const resolved = await atImportResolvers[key](id); if (resolved) { return path_1.default.resolve(resolved); } } return id; }, })); } if (!postcssPlugins.length) { return { code, map: preprocessorMap, }; } let postcssResult; try { // postcss is an unbundled dep and should be lazy imported postcssResult = await (await Promise.resolve().then(() => __importStar(require('postcss')))) .default(postcssPlugins) .process(code, { ...postcssOptions, to: id, from: id, ...(devSourcemap ? { map: { inline: false, annotation: false, // postcss may return virtual files // we cannot obtain content of them, so this needs to be enabled sourcesContent: true, // when "prev: preprocessorMap", the result map may include duplicate filename in `postcssResult.map.sources` // prev: preprocessorMap, }, } : {}), }); // record CSS dependencies from @imports for (const message of postcssResult.messages) { if (message.type === 'dependency') { deps.add((0, utils_1.normalizePath)(message.file)); } else if (message.type === 'dir-dependency') { // https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#3-dependencies const { dir, glob: globPattern = '**' } = message; const pattern = (0, utils_1.normalizePath)(path_1.default.resolve(path_1.default.dirname(id), dir)) + `/` + globPattern; const files = fast_glob_1.default.sync(pattern, { ignore: ['**/node_modules/**'], }); for (let i = 0; i < files.length; i++) { deps.add(files[i]); } if (server) { // register glob importers so we can trigger updates on file add/remove if (!(id in server._globImporters)) { ; server._globImporters[id] = { module: server.moduleGraph.getModuleById(id), importGlobs: [], }; } ; server._globImporters[id].importGlobs.push({ base: config.root, pattern, }); } } else if (message.type === 'warning') { let msg = `[vite:css] ${message.text}`; if (message.line && message.column) { msg += `\n${(0, utils_1.generateCodeFrame)(code, { line: message.line, column: message.column, })}`; } config.logger.warn(picocolors_1.default.yellow(msg)); } } } catch (e) { e.message = `[postcss] ${e.message}`; e.code = code; e.loc = { column: e.column, line: e.line, }; throw e; } if (!devSourcemap) { return { ast: postcssResult, code: postcssResult.css, map: { mappings: '' }, modules, deps, }; } const rawPostcssMap = postcssResult.map.toJSON(); const postcssMap = formatPostcssSourceMap( // version property of rawPostcssMap is declared as string // but actually it is a number rawPostcssMap, (0, utils_1.cleanUrl)(id)); return { ast: postcssResult, code: postcssResult.css, map: combineSourcemapsIfExists((0, utils_1.cleanUrl)(id), postcssMap, preprocessorMap), modules, deps, }; } function formatPostcssSourceMap(rawMap, file) { const inputFileDir = path_1.default.dirname(file); const sources = rawMap.sources.map((source) => { const cleanSource = (0, utils_1.cleanUrl)(decodeURIComponent(source)); // postcss returns virtual files if (/^<.+>$/.test(cleanSource)) { return `\0${cleanSource}`; } return (0, utils_1.normalizePath)(path_1.default.resolve(inputFileDir, cleanSource)); }); return { file, mappings: rawMap.mappings, names: rawMap.names, sources, sourcesContent: rawMap.sourcesContent, version: rawMap.version, }; } exports.formatPostcssSourceMap = formatPostcssSourceMap; function combineSourcemapsIfExists(filename, map1, map2) { return map1 && map2 ? (0, utils_1.combineSourcemaps)(filename, [ // type of version property of ExistingRawSourceMap is number // but it is always 3 map1, map2, ]) : map1; } async function resolvePostcssConfig(config) { var _a; let result = postcssConfigCache.get(config); if (result !== undefined) { return result; } // inline postcss config via vite config const inlineOptions = (_a = config.css) === null || _a === void 0 ? void 0 : _a.postcss; if ((0, utils_1.isObject)(inlineOptions)) { const options = { ...inlineOptions }; delete options.plugins; result = { options, plugins: inlineOptions.plugins || [], }; } else { const searchPath = (0, shared_1.isString)(inlineOptions) ? inlineOptions : config.root; try { // @ts-ignore result = await (0, postcss_load_config_1.default)({}, searchPath); } catch (e) { if (!/No PostCSS Config found/.test(e.message)) { if (e instanceof Error) { const { name, message, stack } = e; e.name = 'Failed to load PostCSS config'; e.message = `Failed to load PostCSS config (searchPath: ${searchPath}): [${name}] ${message}\n${stack}`; e.stack = ''; // add stack to message to retain stack throw e; } else { throw new Error(`Failed to load PostCSS config: ${e}`); } } result = null; } } postcssConfigCache.set(config, result); return result; } // https://drafts.csswg.org/css-syntax-3/#identifier-code-point exports.cssUrlRE = /(?<=^|[^\w\-\u0080-\uffff])url\(\s*('[^']+'|"[^"]+"|[^'")]+)\s*\)/; exports.cssDataUriRE = /(?<=^|[^\w\-\u0080-\uffff])data-uri\(\s*('[^']+'|"[^"]+"|[^'")]+)\s*\)/; exports.importCssRE = /@import ('[^']+\.css'|"[^"]+\.css"|[^'")]+\.css)/; const cssImageSetRE = /(?<=image-set\()((?:[\w\-]+\([^\)]*\)|[^)])*)(?=\))/; const UrlRewritePostcssPlugin = (opts) => { if (!opts) { throw new Error('base or replace is required'); } return { postcssPlugin: 'vite-url-rewrite', Once(root) { const promises = []; root.walkDecls((declaration) => { const isCssUrl = exports.cssUrlRE.test(declaration.value); const isCssImageSet = cssImageSetRE.test(declaration.value); if (isCssUrl || isCssImageSet) { const replacerForDeclaration = (rawUrl) => { var _a; const importer = (_a = declaration.source) === null || _a === void 0 ? void 0 : _a.input.file; return opts.replacer(rawUrl, importer); }; const rewriterToUse = isCssImageSet ? rewriteCssImageSet : rewriteCssUrls; promises.push(rewriterToUse(declaration.value, replacerForDeclaration).then((url) => { declaration.value = url; })); } }); if (promises.length) { return Promise.all(promises); } }, }; }; UrlRewritePostcssPlugin.postcss = true; function rewriteCssUrls(css, replacer) { return (0, utils_1.asyncReplace)(css, exports.cssUrlRE, async (match) => { const [matched, rawUrl] = match; return await doUrlReplace(rawUrl, matched, replacer); }); } function rewriteCssDataUris(css, replacer) { return (0, utils_1.asyncReplace)(css, exports.cssDataUriRE, async (match) => { const [matched, rawUrl] = match; return await doUrlReplace(rawUrl, matched, replacer, 'data-uri'); }); } function rewriteImportCss(css, replacer) { return (0, utils_1.asyncReplace)(css, exports.importCssRE, async (match) => { const [matched, rawUrl] = match; return await doImportCSSReplace(rawUrl, matched, replacer); }); } // TODO: image and cross-fade could contain a "url" that needs to be processed // https://drafts.csswg.org/css-images-4/#image-notation // https://drafts.csswg.org/css-images-4/#cross-fade-function const cssNotProcessedRE = /(gradient|element|cross-fade|image)\(/; async function rewriteCssImageSet(css, replacer) { return await (0, utils_1.asyncReplace)(css, cssImageSetRE, async (match) => { const [, rawUrl] = match; const url = await (0, utils_1.processSrcSet)(rawUrl, async ({ url }) => { // the url maybe url(...) if (exports.cssUrlRE.test(url)) { return await rewriteCssUrls(url, replacer); } if (!cssNotProcessedRE.test(url)) { return await doUrlReplace(url, url, replacer); } return url; }); return url; }); } async function doUrlReplace(rawUrl, matched, replacer, funcName = 'url') { let wrap = ''; const first = rawUrl[0]; if (first === `"` || first === `'`) { wrap = first; rawUrl = rawUrl.slice(1, -1); } if ((0, utils_1.isExternalUrl)(rawUrl) || (0, utils_1.isDataUrl)(rawUrl) || rawUrl.startsWith('#') || varRE.test(rawUrl)) { return matched; } const newUrl = await replacer(rawUrl); if (wrap === '' && newUrl !== encodeURI(newUrl)) { // The new url might need wrapping even if the original did not have it, e.g. if a space was added during replacement wrap = "'"; } return `${funcName}(${wrap}${newUrl}${wrap})`; } async function doImportCSSReplace(rawUrl, matched, replacer) { let wrap = ''; const first = rawUrl[0]; if (first === `"` || first === `'`) { wrap = first; rawUrl = rawUrl.slice(1, -1); } if ((0, utils_1.isExternalUrl)(rawUrl) || (0, utils_1.isDataUrl)(rawUrl) || rawUrl.startsWith('#')) { return matched; } return `@import ${wrap}${await replacer(rawUrl)}${wrap}`; } async function minifyCSS(css, config) { try { const { code, warnings } = await (0, esbuild_1.transform)(css, { loader: 'css', minify: true, target: config.build.cssTarget || undefined, }); if (warnings.length) { const msgs = await (0, esbuild_1.formatMessages)(warnings, { kind: 'warning' }); config.logger.warn(picocolors_1.default.yellow(`warnings when minifying css:\n${msgs.join('\n')}`)); } return code; } catch (e) { if (e.errors) { const msgs = await (0, esbuild_1.formatMessages)(e.errors, { kind: 'error' }); e.frame = '\n' + msgs.join('\n'); e.loc = e.errors[0].location; } throw e; } } exports.minifyCSS = minifyCSS; async function hoistAtRules(css) { const s = new magic_string_1.default(css); const cleanCss = (0, cleanString_1.emptyCssComments)(css); let match; // #1845 // CSS @import can only appear at top of the file. We need to hoist all @import // to top when multiple files are concatenated. // match until semicolon that's not in quotes const atImportRE = /@import\s*(?:url\([^\)]*\)|"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'|[^;]*).*?;/gm; while ((match = atImportRE.exec(cleanCss))) { s.remove(match.index, match.index + match[0].length); // Use `appendLeft` instead of `prepend` to preserve original @import order s.appendLeft(0, match[0]); } // #6333 // CSS @charset must be the top-first in the file, hoist the first to top const atCharsetRE = /@charset\s*(?:"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'|[^;]*).*?;/gm; let foundCharset = false; while ((match = atCharsetRE.exec(cleanCss))) { s.remove(match.index, match.index + match[0].length); if (!foundCharset) { s.prepend(match[0]); foundCharset = true; } } return s.toString(); } exports.hoistAtRules = hoistAtRules; const loadedPreprocessors = {}; function loadPreprocessor(lang, root) { var _a, _b; if (lang in loadedPreprocessors) { return loadedPreprocessors[lang]; } try { // Search for the preprocessor in the root directory first, and fall back // to the default require paths. const fallbackPaths = ((_b = (_a = require.resolve).paths) === null || _b === void 0 ? void 0 : _b.call(_a, lang)) || []; const resolved = require.resolve(lang, { paths: [root, ...fallbackPaths] }); return (loadedPreprocessors[lang] = require(resolved)); } catch (e) { throw new Error(`Preprocessor dependency "${lang}" not found. Did you install it?`); } } // .scss/.sass processor const scss = async (source, root, options, resolvers, isNVue) => { const render = loadPreprocessor("sass" /* PreprocessLang.sass */, root).render; const internalImporter = (url, importer, done) => { resolvers.sass(url, importer).then((resolved) => { if (resolved) { rebaseUrls(resolved, options.filename, options.alias, isNVue) .then((data) => done === null || done === void 0 ? void 0 : done(data)) .catch((data) => done === null || done === void 0 ? void 0 : done(data)); } else { done === null || done === void 0 ? void 0 : done(null); } }); }; const importer = [internalImporter]; if (options.importer) { (0, shared_1.isArray)(options.importer) ? importer.push(...options.importer) : importer.push(options.importer); } const { content: data, map: additionalMap } = await getSource(source, options.filename, options.additionalData, options.enableSourcemap); const finalOptions = { ...options, data, file: options.filename, outFile: options.filename, importer, ...(options.enableSourcemap ? { sourceMap: true, omitSourceMapUrl: true, sourceMapRoot: path_1.default.dirname(options.filename), } : {}), }; try { const result = await new Promise((resolve, reject) => { render(finalOptions, (err, res) => { if (err) { reject(err); } else { resolve(res); } }); }); const deps = result.stats.includedFiles; const map = result.map ? JSON.parse(result.map.toString()) : undefined; return { code: result.css.toString(), map, additionalMap, errors: [], deps, }; } catch (e) { // normalize SASS error e.id = e.file; e.frame = e.formatted; return { code: '', errors: [e], deps: [] }; } }; const sass = (source, root, options, aliasResolver, isNVue) => scss(source, root, { ...options, indentedSyntax: true, }, aliasResolver, isNVue); function preprocessCss(content, isNVue = false) { if (content.includes('#endif')) { return isNVue ? (0, preprocess_1.preNVueCss)(content) : (0, preprocess_1.preCss)(content); } return content; } /** * relative url() inside \@imported sass and less files must be rebased to use * root file as base. */ async function rebaseUrls(file, rootFile, alias, isNVue = false) { file = path_1.default.resolve(file); // ensure os-specific flashes // fixed by xxxxxx 条件编译 let contents = preprocessCss(fs_1.default.readFileSync(file, 'utf-8'), isNVue); // in the same dir, no need to rebase const fileDir = path_1.default.dirname(file); const rootDir = path_1.default.dirname(rootFile); if (fileDir === rootDir) { return { file, contents }; } // no url() const hasUrls = exports.cssUrlRE.test(contents); // data-uri() calls const hasDataUris = exports.cssDataUriRE.test(contents); // no @import xxx.css const hasImportCss = exports.importCssRE.test(contents); if (!hasUrls && !hasDataUris && !hasImportCss) { return { file, contents }; } let rebased; const rebaseFn = (url) => { if (url.startsWith('/')) return url; // match alias, no need to rewrite for (const { find } of alias) { const matches = (0, shared_1.isString)(find) ? url.startsWith(find) : find.test(url); if (matches) { return url; } } const absolute = path_1.default.resolve(fileDir, url); const relative = path_1.default.relative(rootDir, absolute); return (0, utils_1.normalizePath)(relative); }; // fix css imports in less such as `@import "foo.css"` if (hasImportCss) { contents = await rewriteImportCss(contents, rebaseFn); } if (hasUrls) { contents = await rewriteCssUrls(rebased || contents, rebaseFn); } if (hasDataUris) { contents = await rewriteCssDataUris(rebased || contents, rebaseFn); } return { file, contents, }; } // .less const less = async (source, root, options, resolvers, isNVue) => { const nodeLess = loadPreprocessor("less" /* PreprocessLang.less */, root); const viteResolverPlugin = createViteLessPlugin(nodeLess, options.filename, options.alias, resolvers, isNVue); const { content, map: additionalMap } = await getSource(source, options.filename, options.additionalData, options.enableSourcemap); let result; try { result = await nodeLess.render(content, { ...options, plugins: [viteResolverPlugin, ...(options.plugins || [])], ...(options.enableSourcemap ? { sourceMap: { outputSourceFiles: true, sourceMapFileInline: false, }, } : {}), }); } catch (e) { const error = e; // normalize error info const normalizedError = new Error(error.message || error.type); normalizedError.loc = { file: error.filename || options.filename, line: error.line, column: error.column, }; return { code: '', errors: [normalizedError], deps: [] }; } const map = result.map && JSON.parse(result.map); if (map) { delete map.sourcesContent; } return { code: result.css.toString(), map, additionalMap, deps: result.imports, errors: [], }; }; /** * Less manager, lazy initialized */ let ViteLessManager; function createViteLessPlugin(less, rootFile, alias, resolvers, isNVue) { if (!ViteLessManager) { ViteLessManager = class ViteManager extends less.FileManager { constructor(rootFile, resolvers, alias) { super(); this.rootFile = rootFile; this.resolvers = resolvers; this.alias = alias; } supports() { return true; } supportsSync() { return false; } async loadFile(filename, dir, opts, env) { const resolved = await this.resolvers.less(filename, path_1.default.join(dir, '*')); if (resolved) { const result = await rebaseUrls(resolved, this.rootFile, this.alias, isNVue); let contents; if (result && 'contents' in result) { contents = result.contents; } else { contents = fs_1.default.readFileSync(resolved, 'utf-8'); } return { filename: path_1.default.resolve(resolved), contents, }; } else { return super.loadFile(filename, dir, opts, env); } } }; } return { install(_, pluginManager) { pluginManager.addFileManager(new ViteLessManager(rootFile, resolvers, alias)); }, minVersion: [3, 0, 0], }; } // .styl const styl = async (source, root, options) => { var _a; const nodeStylus = loadPreprocessor("stylus" /* PreprocessLang.stylus */, root); // Get source with preprocessor options.additionalData. Make sure a new line separator // is added to avoid any render error, as added stylus content may not have semi-colon separators const { content, map: additionalMap } = await getSource(source, options.filename, options.additionalData, options.enableSourcemap, '\n'); // Get preprocessor options.imports dependencies as stylus // does not return them with its builtin `.deps()` method const importsDeps = ((_a = options.imports) !== null && _a !== void 0 ? _a : []).map((dep) => path_1.default.resolve(dep)); try { const ref = nodeStylus(content, options); if (options.enableSourcemap) { ref.set('sourcemap', { comment: false, inline: false, basePath: root, }); } const result = ref.render(); // Concat imports deps with computed deps const deps = [...ref.deps(), ...importsDeps]; // @ts-expect-error sourcemap exists const map = ref.sourcemap; return { code: result, map: formatStylusSourceMap(map, root), additionalMap, errors: [], deps, }; } catch (e) { return { code: '', errors: [e], deps: [] }; } }; function formatStylusSourceMap(mapBefore, root) { if (!mapBefore) return undefined; const map = { ...mapBefore }; const resolveFromRoot = (p) => (0, utils_1.normalizePath)(path_1.default.resolve(root, p)); if (map.file) { map.file = resolveFromRoot(map.file); } map.sources = map.sources.map(resolveFromRoot); return map; } async function getSource(source, filename, additionalData, enableSourcemap, sep = '') { if (!additionalData) return { content: source }; if ((0, shared_1.isFunction)(additionalData)) { const newContent = await additionalData(source, filename); if ((0, shared_1.isString)(newContent)) { return { content: newContent }; } return newContent; } if (!enableSourcemap) { return { content: additionalData + sep + source }; } const ms = new magic_string_1.default(source); ms.appendLeft(0, sep); ms.appendLeft(0, additionalData); const map = ms.generateMap({ hires: true }); map.file = filename; map.sources = [filename]; return { content: ms.toString(), map, }; } const preProcessors = Object.freeze({ ["less" /* PreprocessLang.less */]: less, ["sass" /* PreprocessLang.sass */]: sass, ["scss" /* PreprocessLang.scss */]: scss, ["styl" /* PreprocessLang.styl */]: styl, ["stylus" /* PreprocessLang.stylus */]: styl, }); function isPreProcessor(lang) { return lang && lang in preProcessors; }