
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var MagicString = require('magic-string');
var estreeWalker = require('estree-walker');
var compilerCore = require('@vue/compiler-core');
var parser = require('@babel/parser');
var shared = require('@vue/shared');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e['default'] : e; }
var MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString);
const CONVERT_SYMBOL = '$';
const ESCAPE_SYMBOL = '$$';
const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef'];
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/;
function shouldTransform(src) {
return transformCheckRE.test(src);
}
function transform(src, { filename, sourceMap, parserPlugins, importHelpersFrom = 'vue' } = {}) {
const plugins = parserPlugins || [];
if (filename) {
if (/\.tsx?$/.test(filename)) {
plugins.push('typescript');
}
if (filename.endsWith('x')) {
plugins.push('jsx');
}
}
const ast = parser.parse(src, {
sourceType: 'module',
plugins
});
const s = new MagicString__default(src);
const res = transformAST(ast.program, s, 0);
// inject helper imports
if (res.importedHelpers.length) {
s.prepend(`import { ${res.importedHelpers
.map(h => `${h} as _${h}`)
.join(', ')} } from '${importHelpersFrom}'\n`);
}
return {
...res,
code: s.toString(),
map: sourceMap
? s.generateMap({
source: filename,
hires: true,
includeContent: true
})
: null
};
}
function transformAST(ast, s, offset = 0, knownRefs, knownProps) {
// TODO remove when out of experimental
warnExperimental();
let convertSymbol = CONVERT_SYMBOL;
let escapeSymbol = ESCAPE_SYMBOL;
// macro import handling
for (const node of ast.body) {
if (node.type === 'ImportDeclaration' &&
node.source.value === 'vue/macros') {
// remove macro imports
s.remove(node.start + offset, node.end + offset);
// check aliasing
for (const specifier of node.specifiers) {
if (specifier.type === 'ImportSpecifier') {
const imported = specifier.imported.name;
const local = specifier.local.name;
if (local !== imported) {
if (imported === ESCAPE_SYMBOL) {
escapeSymbol = local;
}
else if (imported === CONVERT_SYMBOL) {
convertSymbol = local;
}
else {
error(`macro imports for ref-creating methods do not support aliasing.`, specifier);
}
}
}
}
}
}
const importedHelpers = new Set();
const rootScope = {};
const scopeStack = [rootScope];
let currentScope = rootScope;
let escapeScope; // inside $$()
const excludedIds = new WeakSet();
const parentStack = [];
const propsLocalToPublicMap = Object.create(null);
if (knownRefs) {
for (const key of knownRefs) {
rootScope[key] = true;
}
}
if (knownProps) {
for (const key in knownProps) {
const { local } = knownProps[key];
rootScope[local] = 'prop';
propsLocalToPublicMap[local] = key;
}
}
function isRefCreationCall(callee) {
if (callee === convertSymbol) {
return convertSymbol;
}
if (callee[0] === '$' && shorthands.includes(callee.slice(1))) {
return callee;
}
return false;
}
function error(msg, node) {
const e = new Error(msg);
e.node = node;
throw e;
}
function helper(msg) {
importedHelpers.add(msg);
return `_${msg}`;
}
function registerBinding(id, isRef = false) {
excludedIds.add(id);
if (currentScope) {
currentScope[id.name] = isRef;
}
else {
error('registerBinding called without active scope, something is wrong.', id);
}
}
const registerRefBinding = (id) => registerBinding(id, true);
let tempVarCount = 0;
function genTempVar() {
return `__$temp_${++tempVarCount}`;
}
function snip(node) {
return s.original.slice(node.start + offset, node.end + offset);
}
function walkScope(node, isRoot = false) {
for (const stmt of node.body) {
if (stmt.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt, isRoot);
}
else if (stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration') {
if (stmt.declare || !stmt.id)
continue;
registerBinding(stmt.id);
}
else if ((stmt.type === 'ForOfStatement' || stmt.type === 'ForInStatement') &&
stmt.left.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.left);
}
else if (stmt.type === 'ExportNamedDeclaration' &&
stmt.declaration &&
stmt.declaration.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.declaration, isRoot);
}
else if (stmt.type === 'LabeledStatement' &&
stmt.body.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.body, isRoot);
}
}
}
function walkVariableDeclaration(stmt, isRoot = false) {
if (stmt.declare) {
return;
}
for (const decl of stmt.declarations) {
let refCall;
const isCall = decl.init &&
decl.init.type === 'CallExpression' &&
decl.init.callee.type === 'Identifier';
if (isCall &&
(refCall = isRefCreationCall(decl.init.callee.name))) {
processRefDeclaration(refCall, decl.id, decl.init);
}
else {
const isProps = isRoot && isCall && decl.init.callee.name === 'defineProps';
for (const id of compilerCore.extractIdentifiers(decl.id)) {
if (isProps) {
// for defineProps destructure, only exclude them since they
// are already passed in as knownProps
excludedIds.add(id);
}
else {
registerBinding(id);
}
}
}
}
}
function processRefDeclaration(method, id, call) {
excludedIds.add(call.callee);
if (method === convertSymbol) {
// $
// remove macro
s.remove(call.callee.start + offset, call.callee.end + offset);
if (id.type === 'Identifier') {
// single variable
registerRefBinding(id);
}
else if (id.type === 'ObjectPattern') {
processRefObjectPattern(id, call);
}
else if (id.type === 'ArrayPattern') {
processRefArrayPattern(id, call);
}
}
else {
// shorthands
if (id.type === 'Identifier') {
registerRefBinding(id);
// replace call
s.overwrite(call.start + offset, call.start + method.length + offset, helper(method.slice(1)));
}
else {
error(`${method}() cannot be used with destructure patterns.`, call);
}
}
}
function processRefObjectPattern(pattern, call, tempVar, path = []) {
if (!tempVar) {
tempVar = genTempVar();
// const { x } = $(useFoo()) --> const __$temp_1 = useFoo()
s.overwrite(pattern.start + offset, pattern.end + offset, tempVar);
}
for (const p of pattern.properties) {
let nameId;
let key;
let defaultValue;
if (p.type === 'ObjectProperty') {
if (p.key.start === p.value.start) {
// shorthand { foo }
nameId = p.key;
if (p.value.type === 'Identifier') {
// avoid shorthand value identifier from being processed
excludedIds.add(p.value);
}
else if (p.value.type === 'AssignmentPattern' &&
p.value.left.type === 'Identifier') {
// { foo = 1 }
excludedIds.add(p.value.left);
defaultValue = p.value.right;
}
}
else {
key = p.computed ? p.key : p.key.name;
if (p.value.type === 'Identifier') {
// { foo: bar }
nameId = p.value;
}
else if (p.value.type === 'ObjectPattern') {
processRefObjectPattern(p.value, call, tempVar, [...path, key]);
}
else if (p.value.type === 'ArrayPattern') {
processRefArrayPattern(p.value, call, tempVar, [...path, key]);
}
else if (p.value.type === 'AssignmentPattern') {
if (p.value.left.type === 'Identifier') {
// { foo: bar = 1 }
nameId = p.value.left;
defaultValue = p.value.right;
}
else if (p.value.left.type === 'ObjectPattern') {
processRefObjectPattern(p.value.left, call, tempVar, [
...path,
[key, p.value.right]
]);
}
else if (p.value.left.type === 'ArrayPattern') {
processRefArrayPattern(p.value.left, call, tempVar, [
...path,
[key, p.value.right]
]);
}
else ;
}
}
}
else {
// rest element { ...foo }
error(`reactivity destructure does not support rest elements.`, p);
}
if (nameId) {
registerRefBinding(nameId);
// inject toRef() after original replaced pattern
const source = pathToString(tempVar, path);
const keyStr = shared.isString(key)
? `'${key}'`
: key
? snip(key)
: `'${nameId.name}'`;
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``;
s.appendLeft(call.end + offset, `,\n ${nameId.name} = ${helper('toRef')}(${source}, ${keyStr}${defaultStr})`);
}
}
}
function processRefArrayPattern(pattern, call, tempVar, path = []) {
if (!tempVar) {
// const [x] = $(useFoo()) --> const __$temp_1 = useFoo()
tempVar = genTempVar();
s.overwrite(pattern.start + offset, pattern.end + offset, tempVar);
}
for (let i = 0; i < pattern.elements.length; i++) {
const e = pattern.elements[i];
if (!e)
continue;
let nameId;
let defaultValue;
if (e.type === 'Identifier') {
// [a] --> [__a]
nameId = e;
}
else if (e.type === 'AssignmentPattern') {
// [a = 1]
nameId = e.left;
defaultValue = e.right;
}
else if (e.type === 'RestElement') {
// [...a]
error(`reactivity destructure does not support rest elements.`, e);
}
else if (e.type === 'ObjectPattern') {
processRefObjectPattern(e, call, tempVar, [...path, i]);
}
else if (e.type === 'ArrayPattern') {
processRefArrayPattern(e, call, tempVar, [...path, i]);
}
if (nameId) {
registerRefBinding(nameId);
// inject toRef() after original replaced pattern
const source = pathToString(tempVar, path);
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``;
s.appendLeft(call.end + offset, `,\n ${nameId.name} = ${helper('toRef')}(${source}, ${i}${defaultStr})`);
}
}
}
function pathToString(source, path) {
if (path.length) {
for (const seg of path) {
if (shared.isArray(seg)) {
source = `(${source}${segToString(seg[0])} || ${snip(seg[1])})`;
}
else {
source += segToString(seg);
}
}
}
return source;
}
function segToString(seg) {
if (typeof seg === 'number') {
return `[${seg}]`;
}
else if (typeof seg === 'string') {
return `.${seg}`;
}
else {
return snip(seg);
}
}
function rewriteId(scope, id, parent, parentStack) {
if (shared.hasOwn(scope, id.name)) {
const bindingType = scope[id.name];
if (bindingType) {
const isProp = bindingType === 'prop';
if (compilerCore.isStaticProperty(parent) && parent.shorthand) {
// let binding used in a property shorthand
// skip for destructure patterns
if (!parent.inPattern ||
compilerCore.isInDestructureAssignment(parent, parentStack)) {
if (isProp) {
if (escapeScope) {
// prop binding in $$()
// { prop } -> { prop: __props_prop }
registerEscapedPropBinding(id);
s.appendLeft(id.end + offset, `: __props_${propsLocalToPublicMap[id.name]}`);
}
else {
// { prop } -> { prop: __props.prop }
s.appendLeft(id.end + offset, `: ${shared.genPropsAccessExp(propsLocalToPublicMap[id.name])}`);
}
}
else {
// { foo } -> { foo: foo.value }
s.appendLeft(id.end + offset, `: ${id.name}.value`);
}
}
}
else {
if (isProp) {
if (escapeScope) {
// x --> __props_x
registerEscapedPropBinding(id);
s.overwrite(id.start + offset, id.end + offset, `__props_${propsLocalToPublicMap[id.name]}`);
}
else {
// x --> __props.x
s.overwrite(id.start + offset, id.end + offset, shared.genPropsAccessExp(propsLocalToPublicMap[id.name]));
}
}
else {
// x --> x.value
s.appendLeft(id.end + offset, '.value');
}
}
}
return true;
}
return false;
}
const propBindingRefs = {};
function registerEscapedPropBinding(id) {
if (!propBindingRefs.hasOwnProperty(id.name)) {
propBindingRefs[id.name] = true;
const publicKey = propsLocalToPublicMap[id.name];
s.prependRight(offset, `const __props_${publicKey} = ${helper(`toRef`)}(__props, '${publicKey}')\n`);
}
}
// check root scope first
walkScope(ast, true);
estreeWalker.walk(ast, {
enter(node, parent) {
parent && parentStack.push(parent);
// function scopes
if (compilerCore.isFunctionType(node)) {
scopeStack.push((currentScope = {}));
compilerCore.walkFunctionParams(node, registerBinding);
if (node.body.type === 'BlockStatement') {
walkScope(node.body);
}
return;
}
// catch param
if (node.type === 'CatchClause') {
scopeStack.push((currentScope = {}));
if (node.param && node.param.type === 'Identifier') {
registerBinding(node.param);
}
walkScope(node.body);
return;
}
// non-function block scopes
if (node.type === 'BlockStatement' && !compilerCore.isFunctionType(parent)) {
scopeStack.push((currentScope = {}));
walkScope(node);
return;
}
// skip type nodes
if (parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion') {
return this.skip();
}
if (node.type === 'Identifier' &&
// if inside $$(), skip unless this is a destructured prop binding
!(escapeScope && rootScope[node.name] !== 'prop') &&
compilerCore.isReferencedIdentifier(node, parent, parentStack) &&
!excludedIds.has(node)) {
// walk up the scope chain to check if id should be appended .value
let i = scopeStack.length;
while (i--) {
if (rewriteId(scopeStack[i], node, parent, parentStack)) {
return;
}
}
}
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
const callee = node.callee.name;
const refCall = isRefCreationCall(callee);
if (refCall && (!parent || parent.type !== 'VariableDeclarator')) {
return error(`${refCall} can only be used as the initializer of ` +
`a variable declaration.`, node);
}
if (callee === escapeSymbol) {
s.remove(node.callee.start + offset, node.callee.end + offset);
escapeScope = node;
}
// TODO remove when out of experimental
if (callee === '$raw') {
error(`$raw() has been replaced by $$(). ` +
`See ${RFC_LINK} for latest updates.`, node);
}
if (callee === '$fromRef') {
error(`$fromRef() has been replaced by $(). ` +
`See ${RFC_LINK} for latest updates.`, node);
}
}
},
leave(node, parent) {
parent && parentStack.pop();
if ((node.type === 'BlockStatement' && !compilerCore.isFunctionType(parent)) ||
compilerCore.isFunctionType(node)) {
scopeStack.pop();
currentScope = scopeStack[scopeStack.length - 1] || null;
}
if (node === escapeScope) {
escapeScope = undefined;
}
}
});
return {
rootRefs: Object.keys(rootScope).filter(key => rootScope[key] === true),
importedHelpers: [...importedHelpers]
};
}
const RFC_LINK = `https://github.com/vuejs/rfcs/discussions/369`;
const hasWarned = {};
function warnExperimental() {
// eslint-disable-next-line
if (typeof window !== 'undefined') {
return;
}
warnOnce(`Reactivity transform is an experimental feature.\n` +
`Experimental features may change behavior between patch versions.\n` +
`It is recommended to pin your vue dependencies to exact versions to avoid breakage.\n` +
`You can follow the proposal's status at ${RFC_LINK}.`);
}
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/reactivity-transform]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`);
}
exports.shouldTransform = shouldTransform;
exports.transform = transform;
exports.transformAST = transformAST;