vite-plugin-vue-hoist-ce-styles
Version:
A vite/vue plugin to circumvent a bug on Vue3
148 lines (146 loc) • 6.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.hoistCeStyles = void 0;
const directRe = /\&direct/;
const styleRe = /(\.css|\&type\=style)/;
const defaultIndexRe = /index.*.js/;
const PLACEHOLDER_BEGIN = 'ANCHOR:BEGIN';
const PLACEHOLDER_END = 'ANCHOR:END';
const placeHolderRe = /\"ANCHOR\:BEGIN(.*?)ANCHOR\:END\"/g;
const assetUrlRE = /__VITE_ASSET__([a-z\d]{8})__(?:\$_(.*?)__)?/g;
const assetRetrievalRE = /const __([a-z\d]{8})__ \= (.*?);/g;
const virtualId = 'virtual:hoist-ce-styles/css-helper';
var ExportDeclaration;
(function (ExportDeclaration) {
ExportDeclaration["DEFAULT"] = "ExportDefaultDeclaration";
})(ExportDeclaration || (ExportDeclaration = {}));
function invalidateStyles(server, id) {
const { moduleGraph, ws } = server;
const module = moduleGraph.getModuleById(id);
if (module) {
moduleGraph.invalidateModule(module);
if (ws) {
ws.send({
type: 'full-reload',
path: '*',
});
}
}
}
function toStyleCode(styleCache) {
return styleCache.map((c) => c.code).join('');
}
function toRefCode(referenceMap) {
return [...referenceMap].reduce((current, [hash, linkedRef]) => `${current}\nconst __${hash}__ = ${linkedRef}`, '');
}
function hoistCeStyles({ hostComponent, indexRe = defaultIndexRe }) {
const styleCache = [];
const hostComponentRe = new RegExp(hostComponent);
let refMap = new Map();
let server;
let config;
return {
name: 'vite-plugin-vue-hoist-ce-styles',
enforce: 'post',
configureServer(_server) {
server = _server;
},
configResolved(resolvedConfig) {
config = resolvedConfig;
},
resolveId(id) {
if (id === virtualId) {
return virtualId;
}
},
load(id) {
if (id === virtualId) {
if (config.command === 'serve') {
return `export const styles = ${JSON.stringify(toStyleCode(styleCache))}`;
}
}
},
async transform(code, id) {
if (directRe.test(id))
return null;
if (!styleRe.test(id))
return null;
const ast = this.parse(code);
// default export of SFC in customElement mode will always be in the form of
// "export default " ... styles " <-- this is what we care about.
// we overwrite the styles with the export of our virtual module
if (ast.body[0]?.type === ExportDeclaration.DEFAULT) {
const styleCode = ast.body[0].declaration.value;
if (styleCode) {
const cachePos = styleCache.findIndex((c) => c.origin === id);
const cacheObj = { origin: id, code: styleCode };
if (cachePos >= 0) {
styleCache[cachePos] = cacheObj;
}
else {
styleCache.push(cacheObj);
}
let code;
if (config.command === 'serve') {
invalidateStyles(server, virtualId);
if (hostComponentRe.test(id)) {
code = `import { styles } from "${virtualId}"\nexport default styles`;
}
else {
code = `export default ''`;
}
}
else {
// if asset urls are used in css vite replaces them with __VITE_ASSET__hash__
// we need to hold a reference to these assets and leave them in the code so
// they are parsed by the assetPlugin of vite during renderChunk
// we retrieve these references in the generateBundle hook and replace
// our unparsed assets
const localAssetRefs = new Map();
let assetRefs = styleCode.matchAll(assetUrlRE);
if (assetRefs != null) {
for (const [asset, hash] of assetRefs) {
localAssetRefs.set(hash, asset);
}
}
refMap = new Map([...refMap, ...localAssetRefs]);
code = `const styles = "${PLACEHOLDER_BEGIN}${id}${PLACEHOLDER_END}";\nexport default styles;${toRefCode(localAssetRefs)};`;
}
return {
code,
moduleSideEffects: 'no-treeshake',
map: null,
};
}
}
},
generateBundle(_, bundle) {
// for the bundle step we replace the styles of hostComponent with all found styles if it's the entrypoint
// else we remove the styles for performance
for (const [b, c] of Object.entries(bundle)) {
const jsOutFile = indexRe.test(b);
if (jsOutFile) {
let styleCode = JSON.stringify(toStyleCode(styleCache));
let chunk = c;
// re-add asset urls and clean up afterwards
const assets = chunk.code.matchAll(assetRetrievalRE);
for (const [_, hash, url] of assets) {
styleCode = styleCode.replace(refMap.get(hash), url);
}
chunk.code = chunk.code.replace(assetRetrievalRE, '');
const componentStyles = chunk.code.matchAll(placeHolderRe);
for (const [anchor, id] of componentStyles) {
if (hostComponentRe.test(id)) {
chunk.code = chunk.code.replace(anchor, styleCode);
}
else {
chunk.code = chunk.code.replace(anchor, "''");
}
bundle[b] = chunk;
}
}
}
},
};
}
exports.hoistCeStyles = hoistCeStyles;