reboost
Version:
A super fast dev server for rapid web development
141 lines (140 loc) • 6.53 kB
JavaScript
;
// Heavily inspired by (kinda copy of) these
// - https://github.com/webpack-contrib/css-loader/blob/master/src/plugins/postcss-import-parser.js
// - https://github.com/webpack-contrib/css-loader/blob/master/src/plugins/postcss-url-parser.js
Object.defineProperty(exports, "__esModule", { value: true });
exports.urlParser = exports.hasURLsIn = exports.importParser = exports.hasImportsIn = void 0;
const tslib_1 = require("tslib");
const postcss_value_parser_1 = (0, tslib_1.__importDefault)(require("postcss-value-parser"));
const shouldHandleURL = (url, testerFn) => {
if (/^[a-z][a-z0-9+.-]*:/i.test(url))
return false;
return testerFn(url);
};
const importRE = /@import/i;
const hasImportsIn = (css) => importRE.test(css);
exports.hasImportsIn = hasImportsIn;
const importParser = (imports, testerFn) => ({
postcssPlugin: 'import-parser',
prepare: (result) => ({
AtRule: {
import(node) {
if (node.parent.type !== 'root')
return;
if (node.nodes) {
result.warn('Import statement should not contain child nodes. Please fix your import statement (check for missing semicolons)', { node, word: '@import' });
return;
}
const [firstNode, ...mediaNodes] = (0, postcss_value_parser_1.default)(node.params).nodes;
if ((!firstNode && !mediaNodes.length) || !['string', 'function'].includes(firstNode.type)) {
result.warn('Unable to resolve URL of the import statement', { node });
return;
}
let importPath;
if (firstNode.type === 'string') {
importPath = firstNode.value;
}
else {
if (firstNode.value.toLowerCase() !== 'url') {
result.warn('Unable to resolve URL of the import statement', { node });
return;
}
const { nodes } = firstNode;
const isString = nodes.length !== 0 && nodes[0].type === 'string';
importPath = isString ? nodes[0].value : postcss_value_parser_1.default.stringify(nodes);
}
if (!importPath.trim().length) {
result.warn('The import statement imports nothing', { node });
return;
}
if (shouldHandleURL(importPath, testerFn)) {
imports.push({
url: importPath,
media: postcss_value_parser_1.default.stringify(mediaNodes).trim().toLowerCase()
});
node.remove();
}
}
}
})
});
exports.importParser = importParser;
const urlRE = /url/i;
const imageSetRE = /(-webkit-)?image-set/i;
const hasURLOrImageSet = /(url|(-webkit-)?image-set)\(/i;
const getFirstNodeOf = ({ nodes }) => nodes && nodes[0];
const getURL = ({ nodes }) => {
const isString = nodes.length !== 0 && nodes[0].type === 'string';
const url = isString ? nodes[0].value : postcss_value_parser_1.default.stringify(nodes);
return url;
};
const isBlankURL = (url, result, node) => {
if (url.trim())
return false;
result.warn('The url is blank', { node });
return true;
};
const hasURLsIn = (css) => hasURLOrImageSet.test(css);
exports.hasURLsIn = hasURLsIn;
const urlParser = (urls, testerFn) => ({
postcssPlugin: 'url-parser',
prepare: (result) => {
const replacements = [];
let idx = 0;
const makeReplacement = (decl, parsedValue, node, quoted) => {
const replacement = `__URL_REPLACEMENT_${idx++}__`;
node.type = 'word';
node.value = quoted ? `"${replacement}"` : replacement;
replacements.push([decl, parsedValue.toString()]);
return replacement;
};
return {
Declaration(decl) {
if (!hasURLOrImageSet.test(decl.value))
return;
const parsedValue = (0, postcss_value_parser_1.default)(decl.value);
parsedValue.walk((node) => {
if (node.type !== 'function')
return;
if (urlRE.test(node.value)) {
const url = getURL(node);
if (!isBlankURL(url, result, decl) && shouldHandleURL(url, testerFn)) {
urls.push({
url,
replacement: makeReplacement(decl, parsedValue, getFirstNodeOf(node), false)
});
}
return false;
}
else if (imageSetRE.test(node.value)) {
node.nodes.forEach((nestedNode) => {
if (nestedNode.type === 'function' && urlRE.test(nestedNode.value)) {
const url = getURL(nestedNode);
if (!isBlankURL(url, result, decl) && shouldHandleURL(url, testerFn)) {
urls.push({
url,
replacement: makeReplacement(decl, parsedValue, getFirstNodeOf(nestedNode), false)
});
}
}
else if (nestedNode.type === 'string') {
const url = nestedNode.value;
if (!isBlankURL(url, result, decl) && shouldHandleURL(url, testerFn)) {
urls.push({
url,
replacement: makeReplacement(decl, parsedValue, nestedNode, true)
});
}
}
});
return false;
}
});
},
OnceExit() {
replacements.forEach(([decl, replacement]) => decl.value = replacement);
}
};
}
});
exports.urlParser = urlParser;