rollup-plugin-vue
Version:
> Roll Vue 3 SFCs with Rollup.
179 lines (178 loc) • 7.88 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformSFCEntry = void 0;
const hash_sum_1 = __importDefault(require("hash-sum"));
const path_1 = __importDefault(require("path"));
const querystring_1 = __importDefault(require("querystring"));
const compiler_sfc_1 = require("@vue/compiler-sfc");
const descriptorCache_1 = require("./utils/descriptorCache");
const error_1 = require("./utils/error");
const script_1 = require("./script");
function transformSFCEntry(code, filename, options, sourceRoot, isProduction, isServer, filterCustomBlock, pluginContext) {
const { descriptor, errors } = compiler_sfc_1.parse(code, {
sourceMap: true,
filename,
sourceRoot,
});
descriptorCache_1.setDescriptor(filename, descriptor);
if (errors.length) {
errors.forEach((error) => pluginContext.error(error_1.createRollupError(filename, error)));
return null;
}
const shortFilePath = path_1.default
.relative(sourceRoot, filename)
.replace(/^(\.\.[\/\\])+/, '')
.replace(/\\/g, '/');
const scopeId = hash_sum_1.default(isProduction ? shortFilePath + '\n' + code : shortFilePath);
// feature information
const hasScoped = descriptor.styles.some((s) => s.scoped);
const isTemplateInlined = descriptor.scriptSetup && !(descriptor.template && descriptor.template.src);
const hasTemplateImport = descriptor.template && !isTemplateInlined;
const templateImport = hasTemplateImport
? genTemplateCode(descriptor, scopeId, isServer)
: '';
const renderReplace = hasTemplateImport
? isServer
? `script.ssrRender = ssrRender`
: `script.render = render`
: '';
const scriptImport = genScriptCode(descriptor, scopeId, isProduction, isServer, options, pluginContext);
const stylesCode = genStyleCode(descriptor, scopeId, options.preprocessStyles);
const customBlocksCode = getCustomBlock(descriptor, filterCustomBlock);
const output = [
scriptImport,
templateImport,
stylesCode,
customBlocksCode,
renderReplace,
];
if (hasScoped) {
output.push(`script.__scopeId = ${JSON.stringify(`data-v-${scopeId}`)}`);
}
if (!isProduction) {
output.push(`script.__file = ${JSON.stringify(shortFilePath)}`);
}
else if (options.exposeFilename) {
output.push(`script.__file = ${JSON.stringify(path_1.default.basename(shortFilePath))}`);
}
output.push('export default script');
return {
code: output.join('\n'),
map: {
mappings: '',
},
};
}
exports.transformSFCEntry = transformSFCEntry;
function genTemplateCode(descriptor, id, isServer) {
const renderFnName = isServer ? 'ssrRender' : 'render';
let templateImport = `const ${renderFnName} = () => {}`;
let templateRequest;
if (descriptor.template) {
const src = descriptor.template.src || descriptor.filename;
const idQuery = `&id=${id}`;
const srcQuery = descriptor.template.src ? `&src` : ``;
const attrsQuery = attrsToQuery(descriptor.template.attrs, 'js', true);
const query = `?vue&type=template${idQuery}${srcQuery}${attrsQuery}`;
templateRequest = JSON.stringify(src + query);
templateImport = `import { ${renderFnName} } from ${templateRequest}`;
}
return templateImport;
}
function genScriptCode(descriptor, scopeId, isProd, isServer, options, pluginContext) {
let scriptImport = `const script = {}`;
const script = script_1.resolveScript(descriptor, scopeId, isProd, isServer, options, pluginContext);
if (script) {
const src = script.src || descriptor.filename;
const attrsQuery = attrsToQuery(script.attrs, 'js');
const srcQuery = script.src ? `&src` : ``;
const query = `?vue&type=script${srcQuery}${attrsQuery}`;
const scriptRequest = JSON.stringify(src + query);
scriptImport =
`import script from ${scriptRequest}\n` + `export * from ${scriptRequest}`; // support named exports
}
return scriptImport;
}
function genStyleCode(descriptor, scopeId, preprocessStyles) {
let stylesCode = ``;
let hasCSSModules = false;
if (descriptor.styles.length) {
descriptor.styles.forEach((style, i) => {
const src = style.src || descriptor.filename;
// do not include module in default query, since we use it to indicate
// that the module needs to export the modules json
const attrsQuery = attrsToQuery(style.attrs, 'css', preprocessStyles);
const attrsQueryWithoutModule = attrsQuery.replace(/&module(=true|=[^&]+)?/, '');
// make sure to only pass id when necessary so that we don't inject
// duplicate tags when multiple components import the same css file
const idQuery = `&id=${scopeId}`;
const srcQuery = style.src ? `&src` : ``;
const query = `?vue&type=style&index=${i}${srcQuery}${idQuery}`;
const styleRequest = src + query + attrsQuery;
const styleRequestWithoutModule = src + query + attrsQueryWithoutModule;
if (style.module) {
if (!hasCSSModules) {
stylesCode += `\nconst cssModules = script.__cssModules = {}`;
hasCSSModules = true;
}
stylesCode += genCSSModulesCode(i, styleRequest, styleRequestWithoutModule, style.module);
}
else {
stylesCode += `\nimport ${JSON.stringify(styleRequest)}`;
}
// TODO SSR critical CSS collection
});
}
return stylesCode;
}
function getCustomBlock(descriptor, filter) {
let code = '';
descriptor.customBlocks.forEach((block, index) => {
if (filter(block.type)) {
const src = block.src || descriptor.filename;
const attrsQuery = attrsToQuery(block.attrs, block.type);
const srcQuery = block.src ? `&src` : ``;
const query = `?vue&type=${block.type}&index=${index}${srcQuery}${attrsQuery}`;
const request = JSON.stringify(src + query);
code += `import block${index} from ${request}\n`;
code += `if (typeof block${index} === 'function') block${index}(script)\n`;
}
});
return code;
}
function genCSSModulesCode(index, request, requestWithoutModule, moduleName) {
const styleVar = `style${index}`;
let code =
// first import the CSS for extraction
`\nimport ${JSON.stringify(requestWithoutModule)}` +
// then import the json file to expose to component...
`\nimport ${styleVar} from ${JSON.stringify(request + '.js')}`;
// inject variable
const name = typeof moduleName === 'string' ? moduleName : '$style';
code += `\ncssModules["${name}"] = ${styleVar}`;
return code;
}
// these are built-in query parameters so should be ignored
// if the user happen to add them as attrs
const ignoreList = ['id', 'index', 'src', 'type', 'lang'];
function attrsToQuery(attrs, langFallback, forceLangFallback = false) {
let query = ``;
for (const name in attrs) {
const value = attrs[name];
if (!ignoreList.includes(name)) {
query += `&${querystring_1.default.escape(name)}${value ? `=${querystring_1.default.escape(String(value))}` : ``}`;
}
}
if (langFallback || attrs.lang) {
query +=
`lang` in attrs
? forceLangFallback
? `&lang.${langFallback}`
: `&lang.${attrs.lang}`
: `&lang.${langFallback}`;
}
return query;
}
;