UNPKG

@nx/webpack

Version:

The Nx Plugin for Webpack contains executors and generators that support building applications using Webpack.

165 lines (164 loc) • 6.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostcssCliResources = PostcssCliResources; const loader_utils_1 = require("loader-utils"); const path = require("path"); const url = require("node:url"); function wrapUrl(url) { let wrappedUrl; const hasSingleQuotes = url.indexOf("'") >= 0; if (hasSingleQuotes) { wrappedUrl = `"${url}"`; } else { wrappedUrl = `'${url}'`; } return `url(${wrappedUrl})`; } async function resolve(file, base, resolver) { try { return await resolver(`./${file}`, base); } catch { return resolver(file, base); } } module.exports.postcss = true; function PostcssCliResources(options) { const { deployUrl = '', baseHref = '', resourcesOutputPath = '', rebaseRootRelative = false, filename, loader, publicPath = '', } = options; const dedupeSlashes = (url) => url.replace(/\/\/+/g, '/'); const process = async (inputUrl, context, resourceCache) => { // If root-relative, absolute or protocol relative url, leave as is if (/^((?:\w+:)?\/\/|data:|chrome:|#)/.test(inputUrl)) { return inputUrl; } if (!rebaseRootRelative && /^\//.test(inputUrl)) { return inputUrl; } // If starts with a caret, remove and return remainder // this supports bypassing asset processing if (inputUrl.startsWith('^')) { return inputUrl.slice(1); } const cacheKey = path.resolve(context, inputUrl); const cachedUrl = resourceCache.get(cacheKey); if (cachedUrl) { return cachedUrl; } if (inputUrl.startsWith('~')) { inputUrl = inputUrl.slice(1); } if (inputUrl.startsWith('/')) { let outputUrl = ''; if (deployUrl.match(/:\/\//) || deployUrl.startsWith('/')) { // If deployUrl is absolute or root relative, ignore baseHref & use deployUrl as is. outputUrl = `${deployUrl.replace(/\/$/, '')}${inputUrl}`; } else if (baseHref.match(/:\/\//)) { // If baseHref contains a scheme, include it as is. outputUrl = baseHref.replace(/\/$/, '') + dedupeSlashes(`/${deployUrl}/${inputUrl}`); } else { // Join together base-href, deploy-url and the original URL. outputUrl = dedupeSlashes(`/${baseHref}/${deployUrl}/${publicPath}/${inputUrl}`); } resourceCache.set(cacheKey, outputUrl); return outputUrl; } const { pathname, hash, search } = url.parse(inputUrl.replace(/\\/g, '/')); const resolver = (file, base) => new Promise((resolve, reject) => { loader.resolve(base, decodeURI(file), (err, result) => { if (err) { reject(err); return; } resolve(result); }); }); const result = await resolve(pathname, context, resolver); return new Promise((resolve, reject) => { loader.fs.readFile(result, (err, content) => { if (err) { reject(err); return; } let outputPath = (0, loader_utils_1.interpolateName)({ resourcePath: result }, filename, { content }); if (resourcesOutputPath) { outputPath = path.posix.join(resourcesOutputPath, outputPath); } loader.addDependency(result); loader.emitFile(outputPath, content, undefined); let outputUrl = outputPath.replace(/\\/g, '/'); if (hash || search) { outputUrl = url.format({ pathname: outputUrl, hash, search }); } const loaderOptions = loader.loaders[loader.loaderIndex].options; if (deployUrl && loaderOptions.ident !== 'extracted') { outputUrl = url.resolve(deployUrl, outputUrl); } resourceCache.set(cacheKey, outputUrl); resolve(outputUrl); }); }); }; return { postcssPlugin: 'postcss-cli-resources', Once(root) { const urlDeclarations = []; /** * TODO: Explore if this can be rewritten using the new `Declaration()` * listener added in postcss v8 */ root.walkDecls((decl) => { if (decl.value && decl.value.includes('url')) { urlDeclarations.push(decl); } }); if (urlDeclarations.length === 0) { return; } const resourceCache = new Map(); return Promise.all(urlDeclarations.map(async (decl) => { const value = decl.value; const urlRegex = /url(?:\(\s*(['"]?))(.*?)(?:\1\s*\))/g; const segments = []; let match; let lastIndex = 0; let modified = false; // We want to load it relative to the file that imports const inputFile = decl.source && decl.source.input.file; const context = (inputFile && path.dirname(inputFile)) || loader.context; while ((match = urlRegex.exec(value))) { const originalUrl = match[2]; let processedUrl; try { processedUrl = await process(originalUrl, context, resourceCache); } catch (err) { loader.emitError(decl.error(err.message, { word: originalUrl })); continue; } if (lastIndex < match.index) { segments.push(value.slice(lastIndex, match.index)); } if (!processedUrl || originalUrl === processedUrl) { segments.push(match[0]); } else { segments.push(wrapUrl(processedUrl)); modified = true; } lastIndex = match.index + match[0].length; } if (lastIndex < value.length) { segments.push(value.slice(lastIndex)); } if (modified) { decl.value = segments.join(''); } })); }, }; }