2024-01-29 09:26:07 +08:00

2562 lines
101 KiB
JavaScript

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var CompilerDOM = require('@vue/compiler-dom');
var sourceMap = require('source-map');
var hash = require('hash-sum');
var path = require('path');
var compilerCore = require('@vue/compiler-core');
var url = require('url');
var shared = require('@vue/shared');
var CompilerSSR = require('@vue/compiler-ssr');
var postcss = require('postcss');
var selectorParser = require('postcss-selector-parser');
var merge = require('merge-source-map');
var MagicString = require('magic-string');
var parser = require('@babel/parser');
var estreeWalker = require('estree-walker');
var refTransform = require('@vue/ref-transform');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e['default'] : e; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
n[k] = e[k];
});
}
n['default'] = e;
return Object.freeze(n);
}
var CompilerDOM__namespace = /*#__PURE__*/_interopNamespace(CompilerDOM);
var hash__default = /*#__PURE__*/_interopDefaultLegacy(hash);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var CompilerSSR__namespace = /*#__PURE__*/_interopNamespace(CompilerSSR);
var postcss__default = /*#__PURE__*/_interopDefaultLegacy(postcss);
var selectorParser__default = /*#__PURE__*/_interopDefaultLegacy(selectorParser);
var merge__default = /*#__PURE__*/_interopDefaultLegacy(merge);
var MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString);
const CSS_VARS_HELPER = `useCssVars`;
const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g;
function genCssVarsFromList(vars, id, isProd) {
return `{\n ${vars
.map(key => `"${genVarName(id, key, isProd)}": (${key})`)
.join(',\n ')}\n}`;
}
function genVarName(id, raw, isProd) {
if (isProd) {
return hash__default(id + raw);
}
else {
return `${id}-${raw.replace(/([^\w-])/g, '_')}`;
}
}
function parseCssVars(sfc) {
const vars = [];
sfc.styles.forEach(style => {
let match;
// ignore v-bind() in comments /* ... */
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '');
while ((match = cssVarRE.exec(content))) {
const variable = match[1] || match[2] || match[3];
if (!vars.includes(variable)) {
vars.push(variable);
}
}
});
return vars;
}
const cssVarsPlugin = opts => {
const { id, isProd } = opts;
return {
postcssPlugin: 'vue-sfc-vars',
Declaration(decl) {
// rewrite CSS variables
if (cssVarRE.test(decl.value)) {
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
return `var(--${genVarName(id, $1 || $2 || $3, isProd)})`;
});
}
}
};
};
cssVarsPlugin.postcss = true;
function genCssVarsCode(vars, bindings, id, isProd) {
const varsExp = genCssVarsFromList(vars, id, isProd);
const exp = CompilerDOM.createSimpleExpression(varsExp, false);
const context = CompilerDOM.createTransformContext(CompilerDOM.createRoot([]), {
prefixIdentifiers: true,
inline: true,
bindingMetadata: bindings.__isScriptSetup === false ? undefined : bindings
});
const transformed = CompilerDOM.processExpression(exp, context);
const transformedString = transformed.type === 4 /* SIMPLE_EXPRESSION */
? transformed.content
: transformed.children
.map(c => {
return typeof c === 'string'
? c
: c.content;
})
.join('');
return `_${CSS_VARS_HELPER}(_ctx => (${transformedString}))`;
}
// <script setup> already gets the calls injected as part of the transform
// this is only for single normal <script>
function genNormalScriptCssVarsCode(cssVars, bindings, id, isProd) {
return (`\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` +
`const __injectCSSVars__ = () => {\n${genCssVarsCode(cssVars, bindings, id, isProd)}}\n` +
`const __setup__ = __default__.setup\n` +
`__default__.setup = __setup__\n` +
` ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` +
` : __injectCSSVars__\n`);
}
function createCache(size = 500) {
return new (require('lru-cache'))(size);
}
const sourceToSFC = createCache();
function parse(source, { sourceMap = true, filename = 'anonymous.vue', sourceRoot = '', pad = false, ignoreEmpty = true, compiler = CompilerDOM__namespace } = {}) {
const sourceKey = source + sourceMap + filename + sourceRoot + pad + compiler.parse;
const cache = sourceToSFC.get(sourceKey);
if (cache) {
return cache;
}
const descriptor = {
filename,
source,
template: null,
script: null,
scriptSetup: null,
styles: [],
customBlocks: [],
cssVars: [],
slotted: false
};
const errors = [];
const ast = compiler.parse(source, {
// there are no components at SFC parsing level
isNativeTag: () => true,
// preserve all whitespaces
isPreTag: () => true,
getTextMode: ({ tag, props }, parent) => {
// all top level elements except <template> are parsed as raw text
// containers
if ((!parent && tag !== 'template') ||
// <template lang="xxx"> should also be treated as raw text
(tag === 'template' &&
props.some(p => p.type === 6 /* ATTRIBUTE */ &&
p.name === 'lang' &&
p.value &&
p.value.content &&
p.value.content !== 'html'))) {
return 2 /* RAWTEXT */;
}
else {
return 0 /* DATA */;
}
},
onError: e => {
errors.push(e);
}
});
ast.children.forEach(node => {
if (node.type !== 1 /* ELEMENT */) {
return;
}
// we only want to keep the nodes that are not empty (when the tag is not a template)
if (ignoreEmpty &&
node.tag !== 'template' &&
isEmpty(node) &&
!hasSrc(node)) {
return;
}
switch (node.tag) {
case 'template':
if (!descriptor.template) {
const templateBlock = (descriptor.template = createBlock(node, source, false));
templateBlock.ast = node;
// warn against 2.x <template functional>
if (templateBlock.attrs.functional) {
const err = new SyntaxError(`<template functional> is no longer supported in Vue 3, since ` +
`functional components no longer have significant performance ` +
`difference from stateful ones. Just use a normal <template> ` +
`instead.`);
err.loc = node.props.find(p => p.name === 'functional').loc;
errors.push(err);
}
}
else {
errors.push(createDuplicateBlockError(node));
}
break;
case 'script':
const scriptBlock = createBlock(node, source, pad);
const isSetup = !!scriptBlock.attrs.setup;
if (isSetup && !descriptor.scriptSetup) {
descriptor.scriptSetup = scriptBlock;
break;
}
if (!isSetup && !descriptor.script) {
descriptor.script = scriptBlock;
break;
}
errors.push(createDuplicateBlockError(node, isSetup));
break;
case 'style':
const styleBlock = createBlock(node, source, pad);
if (styleBlock.attrs.vars) {
errors.push(new SyntaxError(`<style vars> has been replaced by a new proposal: ` +
`https://github.com/vuejs/rfcs/pull/231`));
}
descriptor.styles.push(styleBlock);
break;
default:
descriptor.customBlocks.push(createBlock(node, source, pad));
break;
}
});
if (descriptor.scriptSetup) {
if (descriptor.scriptSetup.src) {
errors.push(new SyntaxError(`<script setup> cannot use the "src" attribute because ` +
`its syntax will be ambiguous outside of the component.`));
descriptor.scriptSetup = null;
}
if (descriptor.script && descriptor.script.src) {
errors.push(new SyntaxError(`<script> cannot use the "src" attribute when <script setup> is ` +
`also present because they must be processed together.`));
descriptor.script = null;
}
}
if (sourceMap) {
const genMap = (block) => {
if (block && !block.src) {
block.map = generateSourceMap(filename, source, block.content, sourceRoot, !pad || block.type === 'template' ? block.loc.start.line - 1 : 0);
}
};
genMap(descriptor.template);
genMap(descriptor.script);
descriptor.styles.forEach(genMap);
descriptor.customBlocks.forEach(genMap);
}
// parse CSS vars
descriptor.cssVars = parseCssVars(descriptor);
// check if the SFC uses :slotted
const slottedRE = /(?:::v-|:)slotted\(/;
descriptor.slotted = descriptor.styles.some(s => s.scoped && slottedRE.test(s.content));
const result = {
descriptor,
errors
};
sourceToSFC.set(sourceKey, result);
return result;
}
function createDuplicateBlockError(node, isScriptSetup = false) {
const err = new SyntaxError(`Single file component can contain only one <${node.tag}${isScriptSetup ? ` setup` : ``}> element`);
err.loc = node.loc;
return err;
}
function createBlock(node, source, pad) {
const type = node.tag;
let { start, end } = node.loc;
let content = '';
if (node.children.length) {
start = node.children[0].loc.start;
end = node.children[node.children.length - 1].loc.end;
content = source.slice(start.offset, end.offset);
}
else {
const offset = node.loc.source.indexOf(`</`);
if (offset > -1) {
start = {
line: start.line,
column: start.column + offset,
offset: start.offset + offset
};
}
end = Object.assign({}, start);
}
const loc = {
source: content,
start,
end
};
const attrs = {};
const block = {
type,
content,
loc,
attrs
};
if (pad) {
block.content = padContent(source, block, pad) + block.content;
}
node.props.forEach(p => {
if (p.type === 6 /* ATTRIBUTE */) {
attrs[p.name] = p.value ? p.value.content || true : true;
if (p.name === 'lang') {
block.lang = p.value && p.value.content;
}
else if (p.name === 'src') {
block.src = p.value && p.value.content;
}
else if (type === 'style') {
if (p.name === 'scoped') {
block.scoped = true;
}
else if (p.name === 'module') {
block.module = attrs[p.name];
}
}
else if (type === 'script' && p.name === 'setup') {
block.setup = attrs.setup;
}
}
});
return block;
}
const splitRE = /\r?\n/g;
const emptyRE = /^(?:\/\/)?\s*$/;
const replaceRE = /./g;
function generateSourceMap(filename, source, generated, sourceRoot, lineOffset) {
const map = new sourceMap.SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
sourceRoot: sourceRoot.replace(/\\/g, '/')
});
map.setSourceContent(filename, source);
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
const originalLine = index + 1 + lineOffset;
const generatedLine = index + 1;
for (let i = 0; i < line.length; i++) {
if (!/\s/.test(line[i])) {
map.addMapping({
source: filename,
original: {
line: originalLine,
column: i
},
generated: {
line: generatedLine,
column: i
}
});
}
}
}
});
return JSON.parse(map.toString());
}
function padContent(content, block, pad) {
content = content.slice(0, block.loc.start.offset);
if (pad === 'space') {
return content.replace(replaceRE, ' ');
}
else {
const offset = content.split(splitRE).length;
const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n';
return Array(offset).join(padChar);
}
}
function hasSrc(node) {
return node.props.some(p => {
if (p.type !== 6 /* ATTRIBUTE */) {
return false;
}
return p.name === 'src';
});
}
/**
* Returns true if the node has no children
* once the empty text nodes (trimmed content) have been filtered out.
*/
function isEmpty(node) {
return (node.children.filter(child => child.type !== 2 /* TEXT */ || child.content.trim() !== '').length === 0);
}
function isRelativeUrl(url) {
const firstChar = url.charAt(0);
return firstChar === '.' || firstChar === '~' || firstChar === '@';
}
const externalRE = /^https?:\/\//;
function isExternalUrl(url) {
return externalRE.test(url);
}
const dataUrlRE = /^\s*data:/i;
function isDataUrl(url) {
return dataUrlRE.test(url);
}
/**
* Parses string url into URL object.
*/
function parseUrl(url) {
const firstChar = url.charAt(0);
if (firstChar === '~') {
const secondChar = url.charAt(1);
url = url.slice(secondChar === '/' ? 2 : 1);
}
return parseUriParts(url);
}
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
* @param urlString an url as a string
*/
function parseUriParts(urlString) {
// A TypeError is thrown if urlString is not a string
// @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
return url.parse(shared.isString(urlString) ? urlString : '', false, true);
}
const defaultAssetUrlOptions = {
base: null,
includeAbsolute: false,
tags: {
video: ['src', 'poster'],
source: ['src'],
img: ['src'],
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
}
};
const normalizeOptions = (options) => {
if (Object.keys(options).some(key => shared.isArray(options[key]))) {
// legacy option format which directly passes in tags config
return Object.assign(Object.assign({}, defaultAssetUrlOptions), { tags: options });
}
return Object.assign(Object.assign({}, defaultAssetUrlOptions), options);
};
const createAssetUrlTransformWithOptions = (options) => {
return (node, context) => transformAssetUrl(node, context, options);
};
/**
* A `@vue/compiler-core` plugin that transforms relative asset urls into
* either imports or absolute urls.
*
* ``` js
* // Before
* createVNode('img', { src: './logo.png' })
*
* // After
* import _imports_0 from './logo.png'
* createVNode('img', { src: _imports_0 })
* ```
*/
const transformAssetUrl = (node, context, options = defaultAssetUrlOptions) => {
if (node.type === 1 /* ELEMENT */) {
if (!node.props.length) {
return;
}
const tags = options.tags || defaultAssetUrlOptions.tags;
const attrs = tags[node.tag];
const wildCardAttrs = tags['*'];
if (!attrs && !wildCardAttrs) {
return;
}
const assetAttrs = (attrs || []).concat(wildCardAttrs || []);
node.props.forEach((attr, index) => {
if (attr.type !== 6 /* ATTRIBUTE */ ||
!assetAttrs.includes(attr.name) ||
!attr.value ||
isExternalUrl(attr.value.content) ||
isDataUrl(attr.value.content) ||
attr.value.content[0] === '#' ||
(!options.includeAbsolute && !isRelativeUrl(attr.value.content))) {
return;
}
const url = parseUrl(attr.value.content);
if (options.base && attr.value.content[0] === '.') {
// explicit base - directly rewrite relative urls into absolute url
// to avoid generating extra imports
// Allow for full hostnames provided in options.base
const base = parseUrl(options.base);
const protocol = base.protocol || '';
const host = base.host ? protocol + '//' + base.host : '';
const basePath = base.path || '/';
// when packaged in the browser, path will be using the posix-
// only version provided by rollup-plugin-node-builtins.
attr.value.content =
host +
(path__default.posix || path__default).join(basePath, url.path + (url.hash || ''));
return;
}
// otherwise, transform the url into an import.
// this assumes a bundler will resolve the import into the correct
// absolute url (e.g. webpack file-loader)
const exp = getImportsExpressionExp(url.path, url.hash, attr.loc, context);
node.props[index] = {
type: 7 /* DIRECTIVE */,
name: 'bind',
arg: compilerCore.createSimpleExpression(attr.name, true, attr.loc),
exp,
modifiers: [],
loc: attr.loc
};
});
}
};
function getImportsExpressionExp(path, hash, loc, context) {
if (path) {
const existing = context.imports.find(i => i.path === path);
if (existing) {
return existing.exp;
}
const name = `_imports_${context.imports.length}`;
const exp = compilerCore.createSimpleExpression(name, false, loc, 2 /* CAN_HOIST */);
context.imports.push({ exp, path });
if (hash && path) {
return context.hoist(compilerCore.createSimpleExpression(`${name} + '${hash}'`, false, loc, 2 /* CAN_HOIST */));
}
else {
return exp;
}
}
else {
return compilerCore.createSimpleExpression(`''`, false, loc, 2 /* CAN_HOIST */);
}
}
const srcsetTags = ['img', 'source'];
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g;
const createSrcsetTransformWithOptions = (options) => {
return (node, context) => transformSrcset(node, context, options);
};
const transformSrcset = (node, context, options = defaultAssetUrlOptions) => {
if (node.type === 1 /* ELEMENT */) {
if (srcsetTags.includes(node.tag) && node.props.length) {
node.props.forEach((attr, index) => {
if (attr.name === 'srcset' && attr.type === 6 /* ATTRIBUTE */) {
if (!attr.value)
return;
const value = attr.value.content;
if (!value)
return;
const imageCandidates = value.split(',').map(s => {
// The attribute value arrives here with all whitespace, except
// normal spaces, represented by escape sequences
const [url, descriptor] = s
.replace(escapedSpaceCharacters, ' ')
.trim()
.split(' ', 2);
return { url, descriptor };
});
// data urls contains comma after the ecoding so we need to re-merge
// them
for (let i = 0; i < imageCandidates.length; i++) {
const { url } = imageCandidates[i];
if (isDataUrl(url)) {
imageCandidates[i + 1].url =
url + ',' + imageCandidates[i + 1].url;
imageCandidates.splice(i, 1);
}
}
const hasQualifiedUrl = imageCandidates.some(({ url }) => {
return (!isExternalUrl(url) &&
!isDataUrl(url) &&
(options.includeAbsolute || isRelativeUrl(url)));
});
// When srcset does not contain any qualified URLs, skip transforming
if (!hasQualifiedUrl) {
return;
}
if (options.base) {
const base = options.base;
const set = [];
imageCandidates.forEach(({ url, descriptor }) => {
descriptor = descriptor ? ` ${descriptor}` : ``;
if (isRelativeUrl(url)) {
set.push((path__default.posix || path__default).join(base, url) + descriptor);
}
else {
set.push(url + descriptor);
}
});
attr.value.content = set.join(', ');
return;
}
const compoundExpression = compilerCore.createCompoundExpression([], attr.loc);
imageCandidates.forEach(({ url, descriptor }, index) => {
if (!isExternalUrl(url) &&
!isDataUrl(url) &&
(options.includeAbsolute || isRelativeUrl(url))) {
const { path } = parseUrl(url);
let exp;
if (path) {
const existingImportsIndex = context.imports.findIndex(i => i.path === path);
if (existingImportsIndex > -1) {
exp = compilerCore.createSimpleExpression(`_imports_${existingImportsIndex}`, false, attr.loc, 2 /* CAN_HOIST */);
}
else {
exp = compilerCore.createSimpleExpression(`_imports_${context.imports.length}`, false, attr.loc, 2 /* CAN_HOIST */);
context.imports.push({ exp, path });
}
compoundExpression.children.push(exp);
}
}
else {
const exp = compilerCore.createSimpleExpression(`"${url}"`, false, attr.loc, 2 /* CAN_HOIST */);
compoundExpression.children.push(exp);
}
const isNotLast = imageCandidates.length - 1 > index;
if (descriptor && isNotLast) {
compoundExpression.children.push(` + ' ${descriptor}, ' + `);
}
else if (descriptor) {
compoundExpression.children.push(` + ' ${descriptor}'`);
}
else if (isNotLast) {
compoundExpression.children.push(` + ', ' + `);
}
});
const hoisted = context.hoist(compoundExpression);
hoisted.constType = 2 /* CAN_HOIST */;
node.props[index] = {
type: 7 /* DIRECTIVE */,
name: 'bind',
arg: compilerCore.createSimpleExpression('srcset', true, attr.loc),
exp: hoisted,
modifiers: [],
loc: attr.loc
};
}
});
}
}
};
const hasWarned = {};
function warnOnce(msg) {
const isNodeProd = typeof process !== 'undefined' && process.env.NODE_ENV === 'production';
if (!isNodeProd && !false && !hasWarned[msg]) {
hasWarned[msg] = true;
warn(msg);
}
}
function warn(msg) {
console.warn(`\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`);
}
function preprocess({ source, filename, preprocessOptions }, preprocessor) {
// Consolidate exposes a callback based API, but the callback is in fact
// called synchronously for most templating engines. In our case, we have to
// expose a synchronous API so that it is usable in Jest transforms (which
// have to be sync because they are applied via Node.js require hooks)
let res = '';
let err = null;
preprocessor.render(source, Object.assign({ filename }, preprocessOptions), (_err, _res) => {
if (_err)
err = _err;
res = _res;
});
if (err)
throw err;
return res;
}
function compileTemplate(options) {
const { preprocessLang, preprocessCustomRequire } = options;
const preprocessor = preprocessLang
? preprocessCustomRequire
? preprocessCustomRequire(preprocessLang)
: require('consolidate')[preprocessLang]
: false;
if (preprocessor) {
try {
return doCompileTemplate(Object.assign(Object.assign({}, options), { source: preprocess(options, preprocessor) }));
}
catch (e) {
return {
code: `export default function render() {}`,
source: options.source,
tips: [],
errors: [e]
};
}
}
else if (preprocessLang) {
return {
code: `export default function render() {}`,
source: options.source,
tips: [
`Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
],
errors: [
`Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
]
};
}
else {
return doCompileTemplate(options);
}
}
function doCompileTemplate({ filename, id, scoped, slotted, inMap, source, ssr = false, ssrCssVars, isProd = false, compiler = ssr ? CompilerSSR__namespace : CompilerDOM__namespace, compilerOptions = {}, transformAssetUrls }) {
const errors = [];
const warnings = [];
let nodeTransforms = [];
if (shared.isObject(transformAssetUrls)) {
const assetOptions = normalizeOptions(transformAssetUrls);
nodeTransforms = [
createAssetUrlTransformWithOptions(assetOptions),
createSrcsetTransformWithOptions(assetOptions)
];
}
else if (transformAssetUrls !== false) {
nodeTransforms = [transformAssetUrl, transformSrcset];
}
if (ssr && !ssrCssVars) {
warnOnce(`compileTemplate is called with \`ssr: true\` but no ` +
`corresponding \`cssVars\` option.\`.`);
}
if (!id) {
warnOnce(`compileTemplate now requires the \`id\` option.\`.`);
id = '';
}
const shortId = id.replace(/^data-v-/, '');
const longId = `data-v-${shortId}`;
let { code, ast, preamble, map } = compiler.compile(source, Object.assign(Object.assign({ mode: 'module', prefixIdentifiers: true, hoistStatic: true, cacheHandlers: true, ssrCssVars: ssr && ssrCssVars && ssrCssVars.length
? genCssVarsFromList(ssrCssVars, shortId, isProd)
: '', scopeId: scoped ? longId : undefined, slotted }, compilerOptions), { nodeTransforms: nodeTransforms.concat(compilerOptions.nodeTransforms || []), filename, sourceMap: true, onError: e => errors.push(e), onWarn: w => warnings.push(w) }));
// inMap should be the map produced by ./parse.ts which is a simple line-only
// mapping. If it is present, we need to adjust the final map and errors to
// reflect the original line numbers.
if (inMap) {
if (map) {
map = mapLines(inMap, map);
}
if (errors.length) {
patchErrors(errors, source, inMap);
}
}
const tips = warnings.map(w => {
let msg = w.message;
if (w.loc) {
msg += `\n${shared.generateCodeFrame(source, w.loc.start.offset, w.loc.end.offset)}`;
}
return msg;
});
return { code, ast, preamble, source, errors, tips, map };
}
function mapLines(oldMap, newMap) {
if (!oldMap)
return newMap;
if (!newMap)
return oldMap;
const oldMapConsumer = new sourceMap.SourceMapConsumer(oldMap);
const newMapConsumer = new sourceMap.SourceMapConsumer(newMap);
const mergedMapGenerator = new sourceMap.SourceMapGenerator();
newMapConsumer.eachMapping(m => {
if (m.originalLine == null) {
return;
}
const origPosInOldMap = oldMapConsumer.originalPositionFor({
line: m.originalLine,
column: m.originalColumn
});
if (origPosInOldMap.source == null) {
return;
}
mergedMapGenerator.addMapping({
generated: {
line: m.generatedLine,
column: m.generatedColumn
},
original: {
line: origPosInOldMap.line,
// use current column, since the oldMap produced by @vue/compiler-sfc
// does not
column: m.originalColumn
},
source: origPosInOldMap.source,
name: origPosInOldMap.name
});
});
// source-map's type definition is incomplete
const generator = mergedMapGenerator;
oldMapConsumer.sources.forEach((sourceFile) => {
generator._sources.add(sourceFile);
const sourceContent = oldMapConsumer.sourceContentFor(sourceFile);
if (sourceContent != null) {
mergedMapGenerator.setSourceContent(sourceFile, sourceContent);
}
});
generator._sourceRoot = oldMap.sourceRoot;
generator._file = oldMap.file;
return generator.toJSON();
}
function patchErrors(errors, source, inMap) {
const originalSource = inMap.sourcesContent[0];
const offset = originalSource.indexOf(source);
const lineOffset = originalSource.slice(0, offset).split(/\r?\n/).length - 1;
errors.forEach(err => {
if (err.loc) {
err.loc.start.line += lineOffset;
err.loc.start.offset += offset;
if (err.loc.end !== err.loc.start) {
err.loc.end.line += lineOffset;
err.loc.end.offset += offset;
}
}
});
}
const trimPlugin = () => {
return {
postcssPlugin: 'vue-sfc-trim',
Once(root) {
root.walk(({ type, raws }) => {
if (type === 'rule' || type === 'atrule') {
if (raws.before)
raws.before = '\n';
if ('after' in raws && raws.after)
raws.after = '\n';
}
});
}
};
};
trimPlugin.postcss = true;
const animationNameRE = /^(-\w+-)?animation-name$/;
const animationRE = /^(-\w+-)?animation$/;
const scopedPlugin = (id = '') => {
const keyframes = Object.create(null);
const shortId = id.replace(/^data-v-/, '');
return {
postcssPlugin: 'vue-sfc-scoped',
Rule(rule) {
processRule(id, rule);
},
AtRule(node) {
if (/-?keyframes$/.test(node.name) &&
!node.params.endsWith(`-${shortId}`)) {
// register keyframes
keyframes[node.params] = node.params = node.params + '-' + shortId;
}
},
OnceExit(root) {
if (Object.keys(keyframes).length) {
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
// individual animation-name declaration
root.walkDecls(decl => {
if (animationNameRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',');
}
// shorthand
if (animationRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/);
const i = vals.findIndex(val => keyframes[val]);
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]]);
return vals.join(' ');
}
else {
return v;
}
})
.join(',');
}
});
}
}
};
};
const processedRules = new WeakSet();
function processRule(id, rule) {
if (processedRules.has(rule) ||
(rule.parent &&
rule.parent.type === 'atrule' &&
/-?keyframes$/.test(rule.parent.name))) {
return;
}
processedRules.add(rule);
rule.selector = selectorParser__default(selectorRoot => {
selectorRoot.each(selector => {
rewriteSelector(id, selector, selectorRoot);
});
}).processSync(rule.selector);
}
function rewriteSelector(id, selector, selectorRoot, slotted = false) {
let node = null;
let shouldInject = true;
// find the last child node to insert attribute selector
selector.each(n => {
// DEPRECATED ">>>" and "/deep/" combinator
if (n.type === 'combinator' &&
(n.value === '>>>' || n.value === '/deep/')) {
n.value = ' ';
n.spaces.before = n.spaces.after = '';
warn(`the >>> and /deep/ combinators have been deprecated. ` +
`Use :deep() instead.`);
return false;
}
if (n.type === 'pseudo') {
const { value } = n;
// deep: inject [id] attribute at the node before the ::v-deep
// combinator.
if (value === ':deep' || value === '::v-deep') {
if (n.nodes.length) {
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
// replace the current node with ::v-deep's inner selector
let last = n;
n.nodes[0].each(ss => {
selector.insertAfter(last, ss);
last = ss;
});
// insert a space combinator before if it doesn't already have one
const prev = selector.at(selector.index(n) - 1);
if (!prev || !isSpaceCombinator(prev)) {
selector.insertAfter(n, selectorParser__default.combinator({
value: ' '
}));
}
selector.removeChild(n);
}
else {
// DEPRECATED usage
// .foo ::v-deep .bar -> .foo[xxxxxxx] .bar
warn(`::v-deep usage as a combinator has ` +
`been deprecated. Use :deep(<inner-selector>) instead.`);
const prev = selector.at(selector.index(n) - 1);
if (prev && isSpaceCombinator(prev)) {
selector.removeChild(prev);
}
selector.removeChild(n);
}
return false;
}
// slot: use selector inside `::v-slotted` and inject [id + '-s']
// instead.
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
if (value === ':slotted' || value === '::v-slotted') {
rewriteSelector(id, n.nodes[0], selectorRoot, true /* slotted */);
let last = n;
n.nodes[0].each(ss => {
selector.insertAfter(last, ss);
last = ss;
});
// selector.insertAfter(n, n.nodes[0])
selector.removeChild(n);
// since slotted attribute already scopes the selector there's no
// need for the non-slot attribute.
shouldInject = false;
return false;
}
// global: replace with inner selector and do not inject [id].
// ::v-global(.foo) -> .foo
if (value === ':global' || value === '::v-global') {
selectorRoot.insertAfter(selector, n.nodes[0]);
selectorRoot.removeChild(selector);
return false;
}
}
if (n.type !== 'pseudo' && n.type !== 'combinator') {
node = n;
}
});
if (node) {
node.spaces.after = '';
}
else {
// For deep selectors & standalone pseudo selectors,
// the attribute selectors are prepended rather than appended.
// So all leading spaces must be eliminated to avoid problems.
selector.first.spaces.before = '';
}
if (shouldInject) {
const idToAdd = slotted ? id + '-s' : id;
selector.insertAfter(
// If node is null it means we need to inject [id] at the start
// insertAfter can handle `null` here
node, selectorParser__default.attribute({
attribute: idToAdd,
value: idToAdd,
raws: {},
quoteMark: `"`
}));
}
}
function isSpaceCombinator(node) {
return node.type === 'combinator' && /^\s+$/.test(node.value);
}
scopedPlugin.postcss = true;
// .scss/.sass processor
const scss = (source, map, options, load = require) => {
const nodeSass = load('sass');
const finalOptions = Object.assign(Object.assign({}, options), { data: getSource(source, options.filename, options.additionalData), file: options.filename, outFile: options.filename, sourceMap: !!map });
try {
const result = nodeSass.renderSync(finalOptions);
const dependencies = result.stats.includedFiles;
if (map) {
return {
code: result.css.toString(),
map: merge__default(map, JSON.parse(result.map.toString())),
errors: [],
dependencies
};
}
return { code: result.css.toString(), errors: [], dependencies };
}
catch (e) {
return { code: '', errors: [e], dependencies: [] };
}
};
const sass = (source, map, options, load) => scss(source, map, Object.assign(Object.assign({}, options), { indentedSyntax: true }), load);
// .less
const less = (source, map, options, load = require) => {
const nodeLess = load('less');
let result;
let error = null;
nodeLess.render(getSource(source, options.filename, options.additionalData), Object.assign(Object.assign({}, options), { syncImport: true }), (err, output) => {
error = err;
result = output;
});
if (error)
return { code: '', errors: [error], dependencies: [] };
const dependencies = result.imports;
if (map) {
return {
code: result.css.toString(),
map: merge__default(map, result.map),
errors: [],
dependencies: dependencies
};
}
return {
code: result.css.toString(),
errors: [],
dependencies: dependencies
};
};
// .styl
const styl = (source, map, options, load = require) => {
const nodeStylus = load('stylus');
try {
const ref = nodeStylus(source);
Object.keys(options).forEach(key => ref.set(key, options[key]));
if (map)
ref.set('sourcemap', { inline: false, comment: false });
const result = ref.render();
const dependencies = ref.deps();
if (map) {
return {
code: result,
map: merge__default(map, ref.sourcemap),
errors: [],
dependencies
};
}
return { code: result, errors: [], dependencies };
}
catch (e) {
return { code: '', errors: [e], dependencies: [] };
}
};
function getSource(source, filename, additionalData) {
if (!additionalData)
return source;
if (shared.isFunction(additionalData)) {
return additionalData(source, filename);
}
return additionalData + source;
}
const processors = {
less,
sass,
scss,
styl,
stylus: styl
};
function compileStyle(options) {
return doCompileStyle(Object.assign(Object.assign({}, options), { isAsync: false }));
}
function compileStyleAsync(options) {
return doCompileStyle(Object.assign(Object.assign({}, options), { isAsync: true }));
}
function doCompileStyle(options) {
const { filename, id, scoped = false, trim = true, isProd = false, modules = false, modulesOptions = {}, preprocessLang, postcssOptions, postcssPlugins } = options;
const preprocessor = preprocessLang && processors[preprocessLang];
const preProcessedSource = preprocessor && preprocess$1(options, preprocessor);
const map = preProcessedSource
? preProcessedSource.map
: options.inMap || options.map;
const source = preProcessedSource ? preProcessedSource.code : options.source;
const shortId = id.replace(/^data-v-/, '');
const longId = `data-v-${shortId}`;
const plugins = (postcssPlugins || []).slice();
plugins.unshift(cssVarsPlugin({ id: shortId, isProd }));
if (trim) {
plugins.push(trimPlugin());
}
if (scoped) {
plugins.push(scopedPlugin(longId));
}
let cssModules;
if (modules) {
if (!options.isAsync) {
throw new Error('[@vue/compiler-sfc] `modules` option can only be used with compileStyleAsync().');
}
plugins.push(require('postcss-modules')(Object.assign(Object.assign({}, modulesOptions), { getJSON: (_cssFileName, json) => {
cssModules = json;
} })));
}
const postCSSOptions = Object.assign(Object.assign({}, postcssOptions), { to: filename, from: filename });
if (map) {
postCSSOptions.map = {
inline: false,
annotation: false,
prev: map
};
}
let result;
let code;
let outMap;
// stylus output include plain css. so need remove the repeat item
const dependencies = new Set(preProcessedSource ? preProcessedSource.dependencies : []);
// sass has filename self when provided filename option
dependencies.delete(filename);
const errors = [];
if (preProcessedSource && preProcessedSource.errors.length) {
errors.push(...preProcessedSource.errors);
}
const recordPlainCssDependencies = (messages) => {
messages.forEach(msg => {
if (msg.type === 'dependency') {
// postcss output path is absolute position path
dependencies.add(msg.file);
}
});
return dependencies;
};
try {
result = postcss__default(plugins).process(source, postCSSOptions);
// In async mode, return a promise.
if (options.isAsync) {
return result
.then(result => ({
code: result.css || '',
map: result.map && result.map.toJSON(),
errors,
modules: cssModules,
rawResult: result,
dependencies: recordPlainCssDependencies(result.messages)
}))
.catch(error => ({
code: '',
map: undefined,
errors: [...errors, error],
rawResult: undefined,
dependencies
}));
}
recordPlainCssDependencies(result.messages);
// force synchronous transform (we know we only have sync plugins)
code = result.css;
outMap = result.map;
}
catch (e) {
errors.push(e);
}
return {
code: code || ``,
map: outMap && outMap.toJSON(),
errors,
rawResult: result,
dependencies
};
}
function preprocess$1(options, preprocessor) {
return preprocessor(options.source, options.inMap || options.map, Object.assign({ filename: options.filename }, options.preprocessOptions), options.preprocessCustomRequire);
}
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/;
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/s;
const exportDefaultClassRE = /((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/;
/**
* Utility for rewriting `export default` in a script block into a variable
* declaration so that we can inject things into it
*/
function rewriteDefault(input, as, parserPlugins) {
if (!hasDefaultExport(input)) {
return input + `\nconst ${as} = {}`;
}
let replaced;
const classMatch = input.match(exportDefaultClassRE);
if (classMatch) {
replaced =
input.replace(exportDefaultClassRE, '$1class $2') +
`\nconst ${as} = ${classMatch[2]}`;
}
else {
replaced = input.replace(defaultExportRE, `$1const ${as} =`);
}
if (!hasDefaultExport(replaced)) {
return replaced;
}
// if the script somehow still contains `default export`, it probably has
// multi-line comments or template strings. fallback to a full parse.
const s = new MagicString__default(input);
const ast = parser.parse(input, {
sourceType: 'module',
plugins: parserPlugins
}).program.body;
ast.forEach(node => {
if (node.type === 'ExportDefaultDeclaration') {
s.overwrite(node.start, node.declaration.start, `const ${as} = `);
}
if (node.type === 'ExportNamedDeclaration') {
node.specifiers.forEach(specifier => {
if (specifier.type === 'ExportSpecifier' &&
specifier.exported.type === 'Identifier' &&
specifier.exported.name === 'default') {
const end = specifier.end;
s.overwrite(specifier.start, input.charAt(end) === ',' ? end + 1 : end, ``);
s.append(`\nconst ${as} = ${specifier.local.name}`);
}
});
}
});
return s.toString();
}
function hasDefaultExport(input) {
return defaultExportRE.test(input) || namedDefaultExportRE.test(input);
}
// Special compiler macros
const DEFINE_PROPS = 'defineProps';
const DEFINE_EMITS = 'defineEmits';
const DEFINE_EXPOSE = 'defineExpose';
const WITH_DEFAULTS = 'withDefaults';
const isBuiltInDir = shared.makeMap(`once,memo,if,else,else-if,slot,text,html,on,bind,model,show,cloak,is`);
/**
* Compile `<script setup>`
* It requires the whole SFC descriptor because we need to handle and merge
* normal `<script>` + `<script setup>` if both are present.
*/
function compileScript(sfc, options) {
let { script, scriptSetup, source, filename } = sfc;
// feature flags
const enableRefTransform = !!options.refSugar || !!options.refTransform;
let refBindings;
// for backwards compat
if (!options) {
options = { id: '' };
}
if (!options.id) {
warnOnce(`compileScript now requires passing the \`id\` option.\n` +
`Upgrade your vite or vue-loader version for compatibility with ` +
`the latest experimental proposals.`);
}
const scopeId = options.id ? options.id.replace(/^data-v-/, '') : '';
const cssVars = sfc.cssVars;
const scriptLang = script && script.lang;
const scriptSetupLang = scriptSetup && scriptSetup.lang;
const isTS = scriptLang === 'ts' ||
scriptLang === 'tsx' ||
scriptSetupLang === 'ts' ||
scriptSetupLang === 'tsx';
const plugins = [...shared.babelParserDefaultPlugins];
if (!isTS || scriptLang === 'tsx' || scriptSetupLang === 'tsx') {
plugins.push('jsx');
}
if (options.babelParserPlugins)
plugins.push(...options.babelParserPlugins);
if (isTS)
plugins.push('typescript', 'decorators-legacy');
if (!scriptSetup) {
if (!script) {
throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`);
}
if (scriptLang && !isTS && scriptLang !== 'jsx') {
// do not process non js/ts script blocks
return script;
}
try {
let content = script.content;
let map = script.map;
const scriptAst = parser.parse(content, {
plugins,
sourceType: 'module'
}).program;
const bindings = analyzeScriptBindings(scriptAst.body);
if (enableRefTransform && refTransform.shouldTransform(content)) {
const s = new MagicString__default(source);
const startOffset = script.loc.start.offset;
const endOffset = script.loc.end.offset;
const { importedHelpers } = refTransform.transformAST(scriptAst, s, startOffset);
if (importedHelpers.length) {
s.prepend(`import { ${importedHelpers
.map(h => `${h} as _${h}`)
.join(', ')} } from 'vue'\n`);
}
s.remove(0, startOffset);
s.remove(endOffset, source.length);
content = s.toString();
map = s.generateMap({
source: filename,
hires: true,
includeContent: true
});
}
if (cssVars.length) {
content = rewriteDefault(content, `__default__`, plugins);
content += genNormalScriptCssVarsCode(cssVars, bindings, scopeId, !!options.isProd);
content += `\nexport default __default__`;
}
return Object.assign(Object.assign({}, script), { content,
map,
bindings, scriptAst: scriptAst.body });
}
catch (e) {
// silently fallback if parse fails since user may be using custom
// babel syntax
return script;
}
}
if (script && scriptLang !== scriptSetupLang) {
throw new Error(`[@vue/compiler-sfc] <script> and <script setup> must have the same ` +
`language type.`);
}
if (scriptSetupLang && !isTS && scriptSetupLang !== 'jsx') {
// do not process non js/ts script blocks
return scriptSetup;
}
// metadata that needs to be returned
const bindingMetadata = {};
const defaultTempVar = `__default__`;
const helperImports = new Set();
const userImports = Object.create(null);
const userImportAlias = Object.create(null);
const setupBindings = Object.create(null);
let defaultExport;
let hasDefinePropsCall = false;
let hasDefineEmitCall = false;
let hasDefineExposeCall = false;
let propsRuntimeDecl;
let propsRuntimeDefaults;
let propsTypeDecl;
let propsTypeDeclRaw;
let propsIdentifier;
let emitsRuntimeDecl;
let emitsTypeDecl;
let emitsTypeDeclRaw;
let emitIdentifier;
let hasAwait = false;
let hasInlinedSsrRenderFn = false;
// props/emits declared via types
const typeDeclaredProps = {};
const typeDeclaredEmits = new Set();
// record declared types for runtime props type generation
const declaredTypes = {};
// magic-string state
const s = new MagicString__default(source);
const startOffset = scriptSetup.loc.start.offset;
const endOffset = scriptSetup.loc.end.offset;
const scriptStartOffset = script && script.loc.start.offset;
const scriptEndOffset = script && script.loc.end.offset;
function helper(key) {
helperImports.add(key);
return `_${key}`;
}
function parse(input, options, offset) {
try {
return parser.parse(input, options).program;
}
catch (e) {
e.message = `[@vue/compiler-sfc] ${e.message}\n\n${sfc.filename}\n${shared.generateCodeFrame(source, e.pos + offset, e.pos + offset + 1)}`;
throw e;
}
}
function error(msg, node, end = node.end + startOffset) {
throw new Error(`[@vue/compiler-sfc] ${msg}\n\n${sfc.filename}\n${shared.generateCodeFrame(source, node.start + startOffset, end)}`);
}
function registerUserImport(source, local, imported, isType, isFromSetup) {
if (source === 'vue' && imported) {
userImportAlias[imported] = local;
}
let isUsedInTemplate = true;
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
isUsedInTemplate = new RegExp(
// #4274 escape $ since it's a special char in regex
// (and is the only regex special char that is valid in identifiers)
`[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`).test(resolveTemplateUsageCheckString(sfc));
}
userImports[local] = {
isType,
imported: imported || 'default',
source,
isFromSetup,
isUsedInTemplate
};
}
function processDefineProps(node) {
if (!isCallOf(node, DEFINE_PROPS)) {
return false;
}
if (hasDefinePropsCall) {
error(`duplicate ${DEFINE_PROPS}() call`, node);
}
hasDefinePropsCall = true;
propsRuntimeDecl = node.arguments[0];
// call has type parameters - infer runtime types from it
if (node.typeParameters) {
if (propsRuntimeDecl) {
error(`${DEFINE_PROPS}() cannot accept both type and non-type arguments ` +
`at the same time. Use one or the other.`, node);
}
propsTypeDeclRaw = node.typeParameters.params[0];
propsTypeDecl = resolveQualifiedType(propsTypeDeclRaw, node => node.type === 'TSTypeLiteral');
if (!propsTypeDecl) {
error(`type argument passed to ${DEFINE_PROPS}() must be a literal type, ` +
`or a reference to an interface or literal type.`, propsTypeDeclRaw);
}
}
return true;
}
function processWithDefaults(node) {
if (!isCallOf(node, WITH_DEFAULTS)) {
return false;
}
if (processDefineProps(node.arguments[0])) {
if (propsRuntimeDecl) {
error(`${WITH_DEFAULTS} can only be used with type-based ` +
`${DEFINE_PROPS} declaration.`, node);
}
propsRuntimeDefaults = node.arguments[1];
if (!propsRuntimeDefaults ||
propsRuntimeDefaults.type !== 'ObjectExpression') {
error(`The 2nd argument of ${WITH_DEFAULTS} must be an object literal.`, propsRuntimeDefaults || node);
}
}
else {
error(`${WITH_DEFAULTS}' first argument must be a ${DEFINE_PROPS} call.`, node.arguments[0] || node);
}
return true;
}
function processDefineEmits(node) {
if (!isCallOf(node, DEFINE_EMITS)) {
return false;
}
if (hasDefineEmitCall) {
error(`duplicate ${DEFINE_EMITS}() call`, node);
}
hasDefineEmitCall = true;
emitsRuntimeDecl = node.arguments[0];
if (node.typeParameters) {
if (emitsRuntimeDecl) {
error(`${DEFINE_EMITS}() cannot accept both type and non-type arguments ` +
`at the same time. Use one or the other.`, node);
}
emitsTypeDeclRaw = node.typeParameters.params[0];
emitsTypeDecl = resolveQualifiedType(emitsTypeDeclRaw, node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral');
if (!emitsTypeDecl) {
error(`type argument passed to ${DEFINE_EMITS}() must be a function type, ` +
`a literal type with call signatures, or a reference to the above types.`, emitsTypeDeclRaw);
}
}
return true;
}
function resolveQualifiedType(node, qualifier) {
if (qualifier(node)) {
return node;
}
if (node.type === 'TSTypeReference' &&
node.typeName.type === 'Identifier') {
const refName = node.typeName.name;
const isQualifiedType = (node) => {
if (node.type === 'TSInterfaceDeclaration' &&
node.id.name === refName) {
return node.body;
}
else if (node.type === 'TSTypeAliasDeclaration' &&
node.id.name === refName &&
qualifier(node.typeAnnotation)) {
return node.typeAnnotation;
}
else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
return isQualifiedType(node.declaration);
}
};
for (const node of scriptSetupAst.body) {
const qualified = isQualifiedType(node);
if (qualified) {
return qualified;
}
}
}
}
function processDefineExpose(node) {
if (isCallOf(node, DEFINE_EXPOSE)) {
if (hasDefineExposeCall) {
error(`duplicate ${DEFINE_EXPOSE}() call`, node);
}
hasDefineExposeCall = true;
return true;
}
return false;
}
function checkInvalidScopeReference(node, method) {
if (!node)
return;
CompilerDOM.walkIdentifiers(node, id => {
if (setupBindings[id.name]) {
error(`\`${method}()\` in <script setup> cannot reference locally ` +
`declared variables because it will be hoisted outside of the ` +
`setup() function. If your component options requires initialization ` +
`in the module scope, use a separate normal <script> to export ` +
`the options instead.`, id);
}
});
}
/**
* await foo()
* -->
* (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
*/
function processAwait(node, isStatement) {
s.overwrite(node.start + startOffset, node.argument.start + startOffset, `${isStatement ? `;` : ``}(([__temp,__restore]=${helper(`withAsyncContext`)}(()=>(`);
s.appendLeft(node.end + startOffset, `))),__temp=await __temp,__restore()${isStatement ? `` : `,__temp`})`);
}
/**
* check defaults. If the default object is an object literal with only
* static properties, we can directly generate more optimzied default
* decalrations. Otherwise we will have to fallback to runtime merging.
*/
function checkStaticDefaults() {
return (propsRuntimeDefaults &&
propsRuntimeDefaults.type === 'ObjectExpression' &&
propsRuntimeDefaults.properties.every(node => (node.type === 'ObjectProperty' && !node.computed) ||
node.type === 'ObjectMethod'));
}
function genRuntimeProps(props) {
const keys = Object.keys(props);
if (!keys.length) {
return ``;
}
const hasStaticDefaults = checkStaticDefaults();
const scriptSetupSource = scriptSetup.content;
let propsDecls = `{
${keys
.map(key => {
let defaultString;
if (hasStaticDefaults) {
const prop = propsRuntimeDefaults.properties.find((node) => node.key.name === key);
if (prop) {
if (prop.type === 'ObjectProperty') {
// prop has corresponding static default value
defaultString = `default: ${scriptSetupSource.slice(prop.value.start, prop.value.end)}`;
}
else {
defaultString = `default() ${scriptSetupSource.slice(prop.body.start, prop.body.end)}`;
}
}
}
{
const { type, required } = props[key];
return `${key}: { type: ${toRuntimeTypeString(type)}, required: ${required}${defaultString ? `, ${defaultString}` : ``} }`;
}
})
.join(',\n ')}\n }`;
if (propsRuntimeDefaults && !hasStaticDefaults) {
propsDecls = `${helper('mergeDefaults')}(${propsDecls}, ${source.slice(propsRuntimeDefaults.start + startOffset, propsRuntimeDefaults.end + startOffset)})`;
}
return `\n props: ${propsDecls},`;
}
function genSetupPropsType(node) {
const scriptSetupSource = scriptSetup.content;
if (checkStaticDefaults()) {
// if withDefaults() is used, we need to remove the optional flags
// on props that have default values
let res = `{ `;
const members = node.type === 'TSTypeLiteral' ? node.members : node.body;
for (const m of members) {
if ((m.type === 'TSPropertySignature' ||
m.type === 'TSMethodSignature') &&
m.typeAnnotation &&
m.key.type === 'Identifier') {
if (propsRuntimeDefaults.properties.some((p) => p.key.name === m.key.name)) {
res +=
m.key.name +
(m.type === 'TSMethodSignature' ? '()' : '') +
scriptSetupSource.slice(m.typeAnnotation.start, m.typeAnnotation.end) +
', ';
}
else {
res += scriptSetupSource.slice(m.start, m.end) + `, `;
}
}
}
return (res.length ? res.slice(0, -2) : res) + ` }`;
}
else {
return scriptSetupSource.slice(node.start, node.end);
}
}
// 1. process normal <script> first if it exists
let scriptAst;
if (script) {
// import dedupe between <script> and <script setup>
scriptAst = parse(script.content, {
plugins,
sourceType: 'module'
}, scriptStartOffset);
for (const node of scriptAst.body) {
if (node.type === 'ImportDeclaration') {
// record imports for dedupe
for (const specifier of node.specifiers) {
const imported = specifier.type === 'ImportSpecifier' &&
specifier.imported.type === 'Identifier' &&
specifier.imported.name;
registerUserImport(node.source.value, specifier.local.name, imported, node.importKind === 'type', false);
}
}
else if (node.type === 'ExportDefaultDeclaration') {
// export default
defaultExport = node;
const start = node.start + scriptStartOffset;
const end = node.declaration.start + scriptStartOffset;
s.overwrite(start, end, `const ${defaultTempVar} = `);
}
else if (node.type === 'ExportNamedDeclaration' && node.specifiers) {
const defaultSpecifier = node.specifiers.find(s => s.exported.type === 'Identifier' && s.exported.name === 'default');
if (defaultSpecifier) {
defaultExport = node;
// 1. remove specifier
if (node.specifiers.length > 1) {
s.remove(defaultSpecifier.start + scriptStartOffset, defaultSpecifier.end + scriptStartOffset);
}
else {
s.remove(node.start + scriptStartOffset, node.end + scriptStartOffset);
}
if (node.source) {
// export { x as default } from './x'
// rewrite to `import { x as __default__ } from './x'` and
// add to top
s.prepend(`import { ${defaultSpecifier.local.name} as ${defaultTempVar} } from '${node.source.value}'\n`);
}
else {
// export { x as default }
// rewrite to `const __default__ = x` and move to end
s.append(`\nconst ${defaultTempVar} = ${defaultSpecifier.local.name}\n`);
}
}
}
else if ((node.type === 'VariableDeclaration' ||
node.type === 'FunctionDeclaration' ||
node.type === 'ClassDeclaration') &&
!node.declare) {
walkDeclaration(node, setupBindings, userImportAlias);
}
}
// apply ref transform
if (enableRefTransform && refTransform.shouldTransform(script.content)) {
const { rootVars, importedHelpers } = refTransform.transformAST(scriptAst, s, scriptStartOffset);
refBindings = rootVars;
for (const h of importedHelpers) {
helperImports.add(h);
}
}
}
// 2. parse <script setup> and walk over top level statements
const scriptSetupAst = parse(scriptSetup.content, {
plugins: [
...plugins,
// allow top level await but only inside <script setup>
'topLevelAwait'
],
sourceType: 'module'
}, startOffset);
for (const node of scriptSetupAst.body) {
const start = node.start + startOffset;
let end = node.end + startOffset;
// locate comment
if (node.trailingComments && node.trailingComments.length > 0) {
const lastCommentNode = node.trailingComments[node.trailingComments.length - 1];
end = lastCommentNode.end + startOffset;
}
// locate the end of whitespace between this statement and the next
while (end <= source.length) {
if (!/\s/.test(source.charAt(end))) {
break;
}
end++;
}
// (Dropped) `ref: x` bindings
if (node.type === 'LabeledStatement' &&
node.label.name === 'ref' &&
node.body.type === 'ExpressionStatement') {
error(`ref sugar using the label syntax was an experimental proposal and ` +
`has been dropped based on community feedback. Please check out ` +
`the new proposal at https://github.com/vuejs/rfcs/discussions/369`, node);
}
if (node.type === 'ImportDeclaration') {
// import declarations are moved to top
s.move(start, end, 0);
// dedupe imports
let removed = 0;
const removeSpecifier = (i) => {
const removeLeft = i > removed;
removed++;
const current = node.specifiers[i];
const next = node.specifiers[i + 1];
s.remove(removeLeft
? node.specifiers[i - 1].end + startOffset
: current.start + startOffset, next && !removeLeft
? next.start + startOffset
: current.end + startOffset);
};
for (let i = 0; i < node.specifiers.length; i++) {
const specifier = node.specifiers[i];
const local = specifier.local.name;
const imported = specifier.type === 'ImportSpecifier' &&
specifier.imported.type === 'Identifier' &&
specifier.imported.name;
const source = node.source.value;
const existing = userImports[local];
if (source === 'vue' &&
(imported === DEFINE_PROPS ||
imported === DEFINE_EMITS ||
imported === DEFINE_EXPOSE)) {
warnOnce(`\`${imported}\` is a compiler macro and no longer needs to be imported.`);
removeSpecifier(i);
}
else if (existing) {
if (existing.source === source && existing.imported === imported) {
// already imported in <script setup>, dedupe
removeSpecifier(i);
}
else {
error(`different imports aliased to same local name.`, specifier);
}
}
else {
registerUserImport(source, local, imported, node.importKind === 'type', true);
}
}
if (node.specifiers.length && removed === node.specifiers.length) {
s.remove(node.start + startOffset, node.end + startOffset);
}
}
if (node.type === 'ExpressionStatement') {
// process `defineProps` and `defineEmit(s)` calls
if (processDefineProps(node.expression) ||
processDefineEmits(node.expression) ||
processWithDefaults(node.expression)) {
s.remove(node.start + startOffset, node.end + startOffset);
}
else if (processDefineExpose(node.expression)) {
// defineExpose({}) -> expose({})
const callee = node.expression.callee;
s.overwrite(callee.start + startOffset, callee.end + startOffset, 'expose');
}
}
if (node.type === 'VariableDeclaration' && !node.declare) {
const total = node.declarations.length;
let left = total;
for (let i = 0; i < total; i++) {
const decl = node.declarations[i];
if (decl.init) {
// defineProps / defineEmits
const isDefineProps = processDefineProps(decl.init) || processWithDefaults(decl.init);
if (isDefineProps) {
propsIdentifier = scriptSetup.content.slice(decl.id.start, decl.id.end);
}
const isDefineEmits = processDefineEmits(decl.init);
if (isDefineEmits) {
emitIdentifier = scriptSetup.content.slice(decl.id.start, decl.id.end);
}
if (isDefineProps || isDefineEmits) {
if (left === 1) {
s.remove(node.start + startOffset, node.end + startOffset);
}
else {
let start = decl.start + startOffset;
let end = decl.end + startOffset;
if (i < total - 1) {
// not the last one, locate the start of the next
end = node.declarations[i + 1].start + startOffset;
}
else {
// last one, locate the end of the prev
start = node.declarations[i - 1].end + startOffset;
}
s.remove(start, end);
left--;
}
}
}
}
}
// walk decalrations to record declared bindings
if ((node.type === 'VariableDeclaration' ||
node.type === 'FunctionDeclaration' ||
node.type === 'ClassDeclaration') &&
!node.declare) {
walkDeclaration(node, setupBindings, userImportAlias);
}
// walk statements & named exports / variable declarations for top level
// await
if ((node.type === 'VariableDeclaration' && !node.declare) ||
node.type.endsWith('Statement')) {
estreeWalker.walk(node, {
enter(child, parent) {
if (CompilerDOM.isFunctionType(child)) {
this.skip();
}
if (child.type === 'AwaitExpression') {
hasAwait = true;
processAwait(child, parent.type === 'ExpressionStatement');
}
}
});
}
if ((node.type === 'ExportNamedDeclaration' && node.exportKind !== 'type') ||
node.type === 'ExportAllDeclaration' ||
node.type === 'ExportDefaultDeclaration') {
error(`<script setup> cannot contain ES module exports. ` +
`If you are using a previous version of <script setup>, please ` +
`consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.`, node);
}
if (isTS) {
// runtime enum
if (node.type === 'TSEnumDeclaration') {
registerBinding(setupBindings, node.id, "setup-const" /* SETUP_CONST */);
}
// move all Type declarations to outer scope
if (node.type.startsWith('TS') ||
(node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'type') ||
(node.type === 'VariableDeclaration' && node.declare)) {
recordType(node, declaredTypes);
s.move(start, end, 0);
}
}
}
// 3. Apply ref sugar transform
if (enableRefTransform && refTransform.shouldTransform(scriptSetup.content)) {
const { rootVars, importedHelpers } = refTransform.transformAST(scriptSetupAst, s, startOffset, refBindings);
refBindings = refBindings ? [...refBindings, ...rootVars] : rootVars;
for (const h of importedHelpers) {
helperImports.add(h);
}
}
// 4. extract runtime props/emits code from setup context type
if (propsTypeDecl) {
extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes);
}
if (emitsTypeDecl) {
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits);
}
// 5. check useOptions args to make sure it doesn't reference setup scope
// variables
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS);
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS);
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_PROPS);
// 6. remove non-script content
if (script) {
if (startOffset < scriptStartOffset) {
// <script setup> before <script>
s.remove(0, startOffset);
s.remove(endOffset, scriptStartOffset);
s.remove(scriptEndOffset, source.length);
}
else {
// <script> before <script setup>
s.remove(0, scriptStartOffset);
s.remove(scriptEndOffset, startOffset);
s.remove(endOffset, source.length);
}
}
else {
// only <script setup>
s.remove(0, startOffset);
s.remove(endOffset, source.length);
}
// 7. analyze binding metadata
if (scriptAst) {
Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst.body));
}
if (propsRuntimeDecl) {
for (const key of getObjectOrArrayExpressionKeys(propsRuntimeDecl)) {
bindingMetadata[key] = "props" /* PROPS */;
}
}
for (const key in typeDeclaredProps) {
bindingMetadata[key] = "props" /* PROPS */;
}
for (const [key, { isType, imported, source }] of Object.entries(userImports)) {
if (isType)
continue;
bindingMetadata[key] =
(imported === 'default' && source.endsWith('.vue')) || source === 'vue'
? "setup-const" /* SETUP_CONST */
: "setup-maybe-ref" /* SETUP_MAYBE_REF */;
}
for (const key in setupBindings) {
bindingMetadata[key] = setupBindings[key];
}
// known ref bindings
if (refBindings) {
for (const key of refBindings) {
bindingMetadata[key] = "setup-ref" /* SETUP_REF */;
}
}
// 8. inject `useCssVars` calls
if (cssVars.length) {
helperImports.add(CSS_VARS_HELPER);
helperImports.add('unref');
s.prependRight(startOffset, `\n${genCssVarsCode(cssVars, bindingMetadata, scopeId, !!options.isProd)}\n`);
}
// 9. finalize setup() argument signature
let args = `__props`;
if (propsTypeDecl) {
// mark as any and only cast on assignment
// since the user defined complex types may be incompatible with the
// inferred type from generated runtime declarations
args += `: any`;
}
// inject user assignment of props
// we use a default __props so that template expressions referencing props
// can use it directly
if (propsIdentifier) {
s.prependRight(startOffset, `\nconst ${propsIdentifier} = __props${propsTypeDecl ? ` as ${genSetupPropsType(propsTypeDecl)}` : ``}`);
}
// inject temp variables for async context preservation
if (hasAwait) {
const any = isTS ? `: any` : ``;
s.prependRight(startOffset, `\nlet __temp${any}, __restore${any}\n`);
}
const destructureElements = hasDefineExposeCall || !options.inlineTemplate ? [`expose`] : [];
if (emitIdentifier) {
destructureElements.push(emitIdentifier === `emit` ? `emit` : `emit: ${emitIdentifier}`);
}
if (destructureElements.length) {
args += `, { ${destructureElements.join(', ')} }`;
if (emitsTypeDecl) {
args += `: { emit: (${scriptSetup.content.slice(emitsTypeDecl.start, emitsTypeDecl.end)}), expose: any, slots: any, attrs: any }`;
}
}
// 10. generate return statement
let returned;
if (options.inlineTemplate) {
if (sfc.template && !sfc.template.src) {
if (options.templateOptions && options.templateOptions.ssr) {
hasInlinedSsrRenderFn = true;
}
// inline render function mode - we are going to compile the template and
// inline it right here
const { code, ast, preamble, tips, errors } = compileTemplate(Object.assign(Object.assign({ filename, source: sfc.template.content, inMap: sfc.template.map }, options.templateOptions), { id: scopeId, scoped: sfc.styles.some(s => s.scoped), isProd: options.isProd, ssrCssVars: sfc.cssVars, compilerOptions: Object.assign(Object.assign({}, (options.templateOptions &&
options.templateOptions.compilerOptions)), { inline: true, isTS,
bindingMetadata }) }));
if (tips.length) {
tips.forEach(warnOnce);
}
const err = errors[0];
if (typeof err === 'string') {
throw new Error(err);
}
else if (err) {
if (err.loc) {
err.message +=
`\n\n` +
sfc.filename +
'\n' +
shared.generateCodeFrame(source, err.loc.start.offset, err.loc.end.offset) +
`\n`;
}
throw err;
}
if (preamble) {
s.prepend(preamble);
}
// avoid duplicated unref import
// as this may get injected by the render function preamble OR the
// css vars codegen
if (ast && ast.helpers.includes(CompilerDOM.UNREF)) {
helperImports.delete('unref');
}
returned = code;
}
else {
returned = `() => {}`;
}
}
else {
// return bindings from setup
const allBindings = Object.assign({}, setupBindings);
for (const key in userImports) {
if (!userImports[key].isType && userImports[key].isUsedInTemplate) {
allBindings[key] = true;
}
}
returned = `{ ${Object.keys(allBindings).join(', ')} }`;
}
if (!options.inlineTemplate && !false) {
// in non-inline mode, the `__isScriptSetup: true` flag is used by
// componentPublicInstance proxy to allow properties that start with $ or _
s.appendRight(endOffset, `\nconst __returned__ = ${returned}\n` +
`Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })\n` +
`return __returned__` +
`\n}\n\n`);
}
else {
s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`);
}
// 11. finalize default export
let runtimeOptions = ``;
if (hasInlinedSsrRenderFn) {
runtimeOptions += `\n __ssrInlineRender: true,`;
}
if (propsRuntimeDecl) {
runtimeOptions += `\n props: ${scriptSetup.content
.slice(propsRuntimeDecl.start, propsRuntimeDecl.end)
.trim()},`;
}
else if (propsTypeDecl) {
runtimeOptions += genRuntimeProps(typeDeclaredProps);
}
if (emitsRuntimeDecl) {
runtimeOptions += `\n emits: ${scriptSetup.content
.slice(emitsRuntimeDecl.start, emitsRuntimeDecl.end)
.trim()},`;
}
else if (emitsTypeDecl) {
runtimeOptions += genRuntimeEmits(typeDeclaredEmits);
}
// <script setup> components are closed by default. If the user did not
// explicitly call `defineExpose`, call expose() with no args.
const exposeCall = hasDefineExposeCall || options.inlineTemplate ? `` : ` expose()\n`;
if (isTS) {
// for TS, make sure the exported type is still valid type with
// correct props information
// we have to use object spread for types to be merged properly
// user's TS setting should compile it down to proper targets
const def = defaultExport ? `\n ...${defaultTempVar},` : ``;
// wrap setup code with function.
// export the content of <script setup> as a named export, `setup`.
// this allows `import { setup } from '*.vue'` for testing purposes.
if (defaultExport) {
s.prependLeft(startOffset, `\n${hasAwait ? `async ` : ``}function setup(${args}) {\n`);
s.append(`\nexport default /*#__PURE__*/${helper(`defineComponent`)}({${def}${runtimeOptions}\n setup})`);
}
else {
s.prependLeft(startOffset, `\nexport default /*#__PURE__*/${helper(`defineComponent`)}({${def}${runtimeOptions}\n ${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`);
s.appendRight(endOffset, `})`);
}
}
else {
if (defaultExport) {
// can't rely on spread operator in non ts mode
s.prependLeft(startOffset, `\n${hasAwait ? `async ` : ``}function setup(${args}) {\n`);
s.append(`\nexport default /*#__PURE__*/ Object.assign(${defaultTempVar}, {${runtimeOptions}\n setup\n})\n`);
}
else {
s.prependLeft(startOffset, `\nexport default {${runtimeOptions}\n ` +
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`);
s.appendRight(endOffset, `}`);
}
}
// 12. finalize Vue helper imports
if (helperImports.size > 0) {
s.prepend(`import { ${[...helperImports]
.map(h => `${h} as _${h}`)
.join(', ')} } from 'vue'\n`);
}
s.trim();
return Object.assign(Object.assign({}, scriptSetup), { bindings: bindingMetadata, content: s.toString(), map: s.generateMap({
source: filename,
hires: true,
includeContent: true
}), scriptAst: scriptAst === null || scriptAst === void 0 ? void 0 : scriptAst.body, scriptSetupAst: scriptSetupAst === null || scriptSetupAst === void 0 ? void 0 : scriptSetupAst.body });
}
function registerBinding(bindings, node, type) {
bindings[node.name] = type;
}
function walkDeclaration(node, bindings, userImportAlias) {
if (node.type === 'VariableDeclaration') {
const isConst = node.kind === 'const';
// export const foo = ...
for (const { id, init } of node.declarations) {
const isDefineCall = !!(isConst &&
isCallOf(init, c => c === DEFINE_PROPS || c === DEFINE_EMITS || c === WITH_DEFAULTS));
if (id.type === 'Identifier') {
let bindingType;
const userReactiveBinding = userImportAlias['reactive'] || 'reactive';
if (isCallOf(init, userReactiveBinding)) {
// treat reactive() calls as let since it's meant to be mutable
bindingType = "setup-let" /* SETUP_LET */;
}
else if (
// if a declaration is a const literal, we can mark it so that
// the generated render fn code doesn't need to unref() it
isDefineCall ||
(isConst && canNeverBeRef(init, userReactiveBinding))) {
bindingType = "setup-const" /* SETUP_CONST */;
}
else if (isConst) {
if (isCallOf(init, userImportAlias['ref'] || 'ref')) {
bindingType = "setup-ref" /* SETUP_REF */;
}
else {
bindingType = "setup-maybe-ref" /* SETUP_MAYBE_REF */;
}
}
else {
bindingType = "setup-let" /* SETUP_LET */;
}
registerBinding(bindings, id, bindingType);
}
else if (id.type === 'ObjectPattern') {
walkObjectPattern(id, bindings, isConst, isDefineCall);
}
else if (id.type === 'ArrayPattern') {
walkArrayPattern(id, bindings, isConst, isDefineCall);
}
}
}
else if (node.type === 'FunctionDeclaration' ||
node.type === 'ClassDeclaration') {
// export function foo() {} / export class Foo {}
// export declarations must be named.
bindings[node.id.name] = "setup-const" /* SETUP_CONST */;
}
}
function walkObjectPattern(node, bindings, isConst, isDefineCall = false) {
for (const p of node.properties) {
if (p.type === 'ObjectProperty') {
// key can only be Identifier in ObjectPattern
if (p.key.type === 'Identifier') {
if (p.key === p.value) {
// const { x } = ...
const type = isDefineCall
? "setup-const" /* SETUP_CONST */
: isConst
? "setup-maybe-ref" /* SETUP_MAYBE_REF */
: "setup-let" /* SETUP_LET */;
registerBinding(bindings, p.key, type);
}
else {
walkPattern(p.value, bindings, isConst, isDefineCall);
}
}
}
else {
// ...rest
// argument can only be identifer when destructuring
const type = isConst ? "setup-const" /* SETUP_CONST */ : "setup-let" /* SETUP_LET */;
registerBinding(bindings, p.argument, type);
}
}
}
function walkArrayPattern(node, bindings, isConst, isDefineCall = false) {
for (const e of node.elements) {
e && walkPattern(e, bindings, isConst, isDefineCall);
}
}
function walkPattern(node, bindings, isConst, isDefineCall = false) {
if (node.type === 'Identifier') {
const type = isDefineCall
? "setup-const" /* SETUP_CONST */
: isConst
? "setup-maybe-ref" /* SETUP_MAYBE_REF */
: "setup-let" /* SETUP_LET */;
registerBinding(bindings, node, type);
}
else if (node.type === 'RestElement') {
// argument can only be identifer when destructuring
const type = isConst ? "setup-const" /* SETUP_CONST */ : "setup-let" /* SETUP_LET */;
registerBinding(bindings, node.argument, type);
}
else if (node.type === 'ObjectPattern') {
walkObjectPattern(node, bindings, isConst);
}
else if (node.type === 'ArrayPattern') {
walkArrayPattern(node, bindings, isConst);
}
else if (node.type === 'AssignmentPattern') {
if (node.left.type === 'Identifier') {
const type = isDefineCall
? "setup-const" /* SETUP_CONST */
: isConst
? "setup-maybe-ref" /* SETUP_MAYBE_REF */
: "setup-let" /* SETUP_LET */;
registerBinding(bindings, node.left, type);
}
else {
walkPattern(node.left, bindings, isConst);
}
}
}
function recordType(node, declaredTypes) {
if (node.type === 'TSInterfaceDeclaration') {
declaredTypes[node.id.name] = [`Object`];
}
else if (node.type === 'TSTypeAliasDeclaration') {
declaredTypes[node.id.name] = inferRuntimeType(node.typeAnnotation, declaredTypes);
}
else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
recordType(node.declaration, declaredTypes);
}
}
function extractRuntimeProps(node, props, declaredTypes) {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body;
for (const m of members) {
if ((m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
m.key.type === 'Identifier') {
let type;
{
if (m.type === 'TSMethodSignature') {
type = ['Function'];
}
else if (m.typeAnnotation) {
type = inferRuntimeType(m.typeAnnotation.typeAnnotation, declaredTypes);
}
}
props[m.key.name] = {
key: m.key.name,
required: !m.optional,
type: type || [`null`]
};
}
}
}
function inferRuntimeType(node, declaredTypes) {
switch (node.type) {
case 'TSStringKeyword':
return ['String'];
case 'TSNumberKeyword':
return ['Number'];
case 'TSBooleanKeyword':
return ['Boolean'];
case 'TSObjectKeyword':
return ['Object'];
case 'TSTypeLiteral':
// TODO (nice to have) generate runtime property validation
return ['Object'];
case 'TSFunctionType':
return ['Function'];
case 'TSArrayType':
case 'TSTupleType':
// TODO (nice to have) generate runtime element type/length checks
return ['Array'];
case 'TSLiteralType':
switch (node.literal.type) {
case 'StringLiteral':
return ['String'];
case 'BooleanLiteral':
return ['Boolean'];
case 'NumericLiteral':
case 'BigIntLiteral':
return ['Number'];
default:
return [`null`];
}
case 'TSTypeReference':
if (node.typeName.type === 'Identifier') {
if (declaredTypes[node.typeName.name]) {
return declaredTypes[node.typeName.name];
}
switch (node.typeName.name) {
case 'Array':
case 'Function':
case 'Object':
case 'Set':
case 'Map':
case 'WeakSet':
case 'WeakMap':
return [node.typeName.name];
case 'Record':
case 'Partial':
case 'Readonly':
case 'Pick':
case 'Omit':
case 'Exclude':
case 'Extract':
case 'Required':
case 'InstanceType':
return ['Object'];
}
}
return [`null`];
case 'TSParenthesizedType':
return inferRuntimeType(node.typeAnnotation, declaredTypes);
case 'TSUnionType':
return [
...new Set([].concat(...node.types.map(t => inferRuntimeType(t, declaredTypes))))
];
case 'TSIntersectionType':
return ['Object'];
default:
return [`null`]; // no runtime check
}
}
function toRuntimeTypeString(types) {
return types.length > 1 ? `[${types.join(', ')}]` : types[0];
}
function extractRuntimeEmits(node, emits) {
if (node.type === 'TSTypeLiteral' || node.type === 'TSInterfaceBody') {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body;
for (let t of members) {
if (t.type === 'TSCallSignatureDeclaration') {
extractEventNames(t.parameters[0], emits);
}
}
return;
}
else {
extractEventNames(node.parameters[0], emits);
}
}
function extractEventNames(eventName, emits) {
if (eventName.type === 'Identifier' &&
eventName.typeAnnotation &&
eventName.typeAnnotation.type === 'TSTypeAnnotation') {
const typeNode = eventName.typeAnnotation.typeAnnotation;
if (typeNode.type === 'TSLiteralType') {
if (typeNode.literal.type !== 'UnaryExpression') {
emits.add(String(typeNode.literal.value));
}
}
else if (typeNode.type === 'TSUnionType') {
for (const t of typeNode.types) {
if (t.type === 'TSLiteralType' &&
t.literal.type !== 'UnaryExpression') {
emits.add(String(t.literal.value));
}
}
}
}
}
function genRuntimeEmits(emits) {
return emits.size
? `\n emits: [${Array.from(emits)
.map(p => JSON.stringify(p))
.join(', ')}],`
: ``;
}
function isCallOf(node, test) {
return !!(node &&
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
(typeof test === 'string'
? node.callee.name === test
: test(node.callee.name)));
}
function canNeverBeRef(node, userReactiveImport) {
if (isCallOf(node, userReactiveImport)) {
return true;
}
switch (node.type) {
case 'UnaryExpression':
case 'BinaryExpression':
case 'ArrayExpression':
case 'ObjectExpression':
case 'FunctionExpression':
case 'ArrowFunctionExpression':
case 'UpdateExpression':
case 'ClassExpression':
case 'TaggedTemplateExpression':
return true;
case 'SequenceExpression':
return canNeverBeRef(node.expressions[node.expressions.length - 1], userReactiveImport);
default:
if (node.type.endsWith('Literal')) {
return true;
}
return false;
}
}
/**
* Analyze bindings in normal `<script>`
* Note that `compileScriptSetup` already analyzes bindings as part of its
* compilation process so this should only be used on single `<script>` SFCs.
*/
function analyzeScriptBindings(ast) {
for (const node of ast) {
if (node.type === 'ExportDefaultDeclaration' &&
node.declaration.type === 'ObjectExpression') {
return analyzeBindingsFromOptions(node.declaration);
}
}
return {};
}
function analyzeBindingsFromOptions(node) {
const bindings = {};
// #3270, #3275
// mark non-script-setup so we don't resolve components/directives from these
Object.defineProperty(bindings, '__isScriptSetup', {
enumerable: false,
value: false
});
for (const property of node.properties) {
if (property.type === 'ObjectProperty' &&
!property.computed &&
property.key.type === 'Identifier') {
// props
if (property.key.name === 'props') {
// props: ['foo']
// props: { foo: ... }
for (const key of getObjectOrArrayExpressionKeys(property.value)) {
bindings[key] = "props" /* PROPS */;
}
}
// inject
else if (property.key.name === 'inject') {
// inject: ['foo']
// inject: { foo: {} }
for (const key of getObjectOrArrayExpressionKeys(property.value)) {
bindings[key] = "options" /* OPTIONS */;
}
}
// computed & methods
else if (property.value.type === 'ObjectExpression' &&
(property.key.name === 'computed' || property.key.name === 'methods')) {
// methods: { foo() {} }
// computed: { foo() {} }
for (const key of getObjectExpressionKeys(property.value)) {
bindings[key] = "options" /* OPTIONS */;
}
}
}
// setup & data
else if (property.type === 'ObjectMethod' &&
property.key.type === 'Identifier' &&
(property.key.name === 'setup' || property.key.name === 'data')) {
for (const bodyItem of property.body.body) {
// setup() {
// return {
// foo: null
// }
// }
if (bodyItem.type === 'ReturnStatement' &&
bodyItem.argument &&
bodyItem.argument.type === 'ObjectExpression') {
for (const key of getObjectExpressionKeys(bodyItem.argument)) {
bindings[key] =
property.key.name === 'setup'
? "setup-maybe-ref" /* SETUP_MAYBE_REF */
: "data" /* DATA */;
}
}
}
}
}
return bindings;
}
function getObjectExpressionKeys(node) {
const keys = [];
for (const prop of node.properties) {
if ((prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
!prop.computed) {
if (prop.key.type === 'Identifier') {
keys.push(prop.key.name);
}
else if (prop.key.type === 'StringLiteral') {
keys.push(prop.key.value);
}
}
}
return keys;
}
function getArrayExpressionKeys(node) {
const keys = [];
for (const element of node.elements) {
if (element && element.type === 'StringLiteral') {
keys.push(element.value);
}
}
return keys;
}
function getObjectOrArrayExpressionKeys(value) {
if (value.type === 'ArrayExpression') {
return getArrayExpressionKeys(value);
}
if (value.type === 'ObjectExpression') {
return getObjectExpressionKeys(value);
}
return [];
}
const templateUsageCheckCache = createCache();
function resolveTemplateUsageCheckString(sfc) {
const { content, ast } = sfc.template;
const cached = templateUsageCheckCache.get(content);
if (cached) {
return cached;
}
let code = '';
CompilerDOM.transform(CompilerDOM.createRoot([ast]), {
nodeTransforms: [
node => {
if (node.type === 1 /* ELEMENT */) {
if (!CompilerDOM.parserOptions.isNativeTag(node.tag) &&
!CompilerDOM.parserOptions.isBuiltInComponent(node.tag)) {
code += `,${shared.camelize(node.tag)},${shared.capitalize(shared.camelize(node.tag))}`;
}
for (let i = 0; i < node.props.length; i++) {
const prop = node.props[i];
if (prop.type === 7 /* DIRECTIVE */) {
if (!isBuiltInDir(prop.name)) {
code += `,v${shared.capitalize(shared.camelize(prop.name))}`;
}
if (prop.exp) {
code += `,${stripStrings(prop.exp.content)}`;
}
}
}
}
else if (node.type === 5 /* INTERPOLATION */) {
code += `,${stripStrings(node.content.content)}`;
}
}
]
});
code += ';';
templateUsageCheckCache.set(content, code);
return code;
}
function stripStrings(exp) {
return exp
.replace(/'[^']+'|"[^"]+"/g, '')
.replace(/`[^`]+`/g, stripTemplateString);
}
function stripTemplateString(str) {
const interpMatch = str.match(/\${[^}]+}/g);
if (interpMatch) {
return interpMatch.map(m => m.slice(2, -1)).join(',');
}
return '';
}
exports.extractIdentifiers = compilerCore.extractIdentifiers;
exports.generateCodeFrame = compilerCore.generateCodeFrame;
exports.isInDestructureAssignment = compilerCore.isInDestructureAssignment;
exports.isStaticProperty = compilerCore.isStaticProperty;
exports.walkIdentifiers = compilerCore.walkIdentifiers;
exports.MagicString = MagicString__default;
exports.babelParse = parser.parse;
exports.walk = estreeWalker.walk;
exports.shouldTransformRef = refTransform.shouldTransform;
exports.transformRef = refTransform.transform;
exports.transformRefAST = refTransform.transformAST;
exports.compileScript = compileScript;
exports.compileStyle = compileStyle;
exports.compileStyleAsync = compileStyleAsync;
exports.compileTemplate = compileTemplate;
exports.parse = parse;
exports.rewriteDefault = rewriteDefault;