更新
This commit is contained in:
+528
@@ -0,0 +1,528 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createStructuralDirectiveTransform = exports.createTransformContext = exports.traverseChildren = exports.traverseNode = exports.transform = exports.isScopedSlotVFor = exports.isVForScope = exports.isVIfScope = exports.isRootScope = void 0;
|
||||
const shared_1 = require("@vue/shared");
|
||||
const types_1 = require("@babel/types");
|
||||
const compiler_core_1 = require("@vue/compiler-core");
|
||||
const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
|
||||
const identifier_1 = __importDefault(require("./identifier"));
|
||||
const runtimeHelpers_1 = require("./runtimeHelpers");
|
||||
const ast_1 = require("./ast");
|
||||
const utils_1 = require("./transforms/utils");
|
||||
const codegen_1 = require("./codegen");
|
||||
function isRootScope(scope) {
|
||||
return !isVIfScope(scope) && !isVForScope(scope);
|
||||
}
|
||||
exports.isRootScope = isRootScope;
|
||||
function isVIfScope(scope) {
|
||||
return (!!scope.condition ||
|
||||
scope.name === 'else');
|
||||
}
|
||||
exports.isVIfScope = isVIfScope;
|
||||
function isVForScope(scope) {
|
||||
return !!scope.source;
|
||||
}
|
||||
exports.isVForScope = isVForScope;
|
||||
function isScopedSlotVFor({ source }) {
|
||||
if (source.type !== compiler_core_1.NodeTypes.COMPOUND_EXPRESSION) {
|
||||
return false;
|
||||
}
|
||||
const first = source.children[0];
|
||||
return (first.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION &&
|
||||
first.content.includes(utils_1.SCOPED_SLOT_IDENTIFIER));
|
||||
}
|
||||
exports.isScopedSlotVFor = isScopedSlotVFor;
|
||||
function transform(root, options) {
|
||||
const context = createTransformContext(root, options);
|
||||
findRootNode(root, context);
|
||||
traverseNode(root, context);
|
||||
root.renderData = createRenderDataExpr(context.scope.properties, context);
|
||||
// finalize meta information
|
||||
root.helpers = new Set([...context.helpers.keys()]);
|
||||
root.components = [...context.components];
|
||||
root.imports = context.imports;
|
||||
root.cached = context.cached;
|
||||
return context;
|
||||
}
|
||||
exports.transform = transform;
|
||||
function findRootNode(root, context) {
|
||||
const children = root.children.filter((node) => node.type === compiler_core_1.NodeTypes.ELEMENT && node.tag !== 'template');
|
||||
if (children.length === 1) {
|
||||
context.rootNode = children[0];
|
||||
}
|
||||
}
|
||||
function traverseNode(node, context) {
|
||||
context.currentNode = node;
|
||||
// apply transform plugins
|
||||
const { nodeTransforms } = context;
|
||||
const exitFns = [];
|
||||
for (let i = 0; i < nodeTransforms.length; i++) {
|
||||
const onExit = nodeTransforms[i](node, context);
|
||||
if (onExit) {
|
||||
if ((0, shared_1.isArray)(onExit)) {
|
||||
exitFns.push(...onExit);
|
||||
}
|
||||
else {
|
||||
exitFns.push(onExit);
|
||||
}
|
||||
}
|
||||
if (!context.currentNode) {
|
||||
// node was removed
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// node may have been replaced
|
||||
node = context.currentNode;
|
||||
}
|
||||
}
|
||||
switch (node.type) {
|
||||
case compiler_core_1.NodeTypes.COMMENT:
|
||||
// context.helper(CREATE_COMMENT)
|
||||
break;
|
||||
case compiler_core_1.NodeTypes.INTERPOLATION:
|
||||
context.helper(compiler_core_1.TO_DISPLAY_STRING);
|
||||
break;
|
||||
// for container types, further traverse downwards
|
||||
case compiler_core_1.NodeTypes.IF:
|
||||
for (let i = 0; i < node.branches.length; i++) {
|
||||
traverseNode(node.branches[i], context);
|
||||
}
|
||||
break;
|
||||
case compiler_core_1.NodeTypes.IF_BRANCH:
|
||||
case compiler_core_1.NodeTypes.FOR:
|
||||
case compiler_core_1.NodeTypes.ELEMENT:
|
||||
case compiler_core_1.NodeTypes.ROOT:
|
||||
traverseChildren(node, context);
|
||||
break;
|
||||
}
|
||||
// exit transforms
|
||||
context.currentNode = node;
|
||||
let i = exitFns.length;
|
||||
while (i--) {
|
||||
exitFns[i]();
|
||||
}
|
||||
}
|
||||
exports.traverseNode = traverseNode;
|
||||
function traverseChildren(parent, context) {
|
||||
let i = 0;
|
||||
const nodeRemoved = () => {
|
||||
i--;
|
||||
};
|
||||
for (; i < parent.children.length; i++) {
|
||||
const child = parent.children[i];
|
||||
if ((0, shared_1.isString)(child))
|
||||
continue;
|
||||
context.parent = parent;
|
||||
context.childIndex = i;
|
||||
context.onNodeRemoved = nodeRemoved;
|
||||
traverseNode(child, context);
|
||||
}
|
||||
}
|
||||
exports.traverseChildren = traverseChildren;
|
||||
function defaultOnError(error) {
|
||||
throw error;
|
||||
}
|
||||
function defaultOnWarn(msg) {
|
||||
console.warn(`[Vue warn] ${msg.message}`);
|
||||
}
|
||||
function createTransformContext(rootNode, { isX = false, root = '', filename = '', isTS = false, inline = false, hashId = null, scopeId = null, filters = [], bindingCssVars = [], bindingMetadata = shared_1.EMPTY_OBJ, cacheHandlers = false, prefixIdentifiers = false, skipTransformIdentifier = false, renderDataSpread = false, nodeTransforms = [], directiveTransforms = {}, miniProgram = {
|
||||
class: {
|
||||
array: true,
|
||||
},
|
||||
slot: {
|
||||
fallbackContent: false,
|
||||
dynamicSlotNames: true,
|
||||
},
|
||||
directive: '',
|
||||
}, isBuiltInComponent = shared_1.NOOP, isCustomElement = shared_1.NOOP, expressionPlugins = [], onError = defaultOnError, onWarn = defaultOnWarn, }) {
|
||||
const rootScope = {
|
||||
id: new identifier_1.default(),
|
||||
identifiers: [],
|
||||
properties: [],
|
||||
parent: null,
|
||||
};
|
||||
function findVIfParentScope() {
|
||||
for (let i = scopes.length - 1; i >= 0; i--) {
|
||||
const scope = scopes[i];
|
||||
if (isVForScope(scope) || isRootScope(scope)) {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
return rootScope;
|
||||
}
|
||||
function createScope(id, initScope) {
|
||||
return (0, shared_1.extend)({
|
||||
id,
|
||||
properties: [],
|
||||
parent: scopes[scopes.length - 1],
|
||||
get identifiers() {
|
||||
return Object.keys(identifiers);
|
||||
},
|
||||
}, initScope);
|
||||
}
|
||||
const vueIds = [];
|
||||
const identifiers = Object.create(null);
|
||||
const scopes = [rootScope];
|
||||
const miniProgramComponents = (0, uni_cli_shared_1.findMiniProgramUsingComponents)({
|
||||
filename,
|
||||
componentsDir: miniProgram.component?.dir,
|
||||
inputDir: root,
|
||||
});
|
||||
// const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
|
||||
const context = {
|
||||
// options
|
||||
// 暂不提供根据文件名生成递归组件
|
||||
isX,
|
||||
selfName: '', //nameMatch && capitalize(camelize(nameMatch[1])),
|
||||
miniProgram,
|
||||
isTS,
|
||||
inline,
|
||||
hashId,
|
||||
scopeId,
|
||||
filters,
|
||||
autoImportFilters: [],
|
||||
bindingCssVars,
|
||||
bindingMetadata,
|
||||
cacheHandlers,
|
||||
prefixIdentifiers,
|
||||
nodeTransforms,
|
||||
directiveTransforms,
|
||||
expressionPlugins,
|
||||
skipTransformIdentifier,
|
||||
renderDataSpread,
|
||||
isBuiltInComponent,
|
||||
isCustomElement,
|
||||
onError,
|
||||
onWarn,
|
||||
// state
|
||||
parent: null,
|
||||
childIndex: 0,
|
||||
helpers: new Map(),
|
||||
components: new Set(),
|
||||
imports: [],
|
||||
bindingComponents: Object.create(null),
|
||||
cached: 0,
|
||||
identifiers,
|
||||
scope: rootScope,
|
||||
scopes: {
|
||||
vFor: 0,
|
||||
vueId: 0,
|
||||
},
|
||||
get currentScope() {
|
||||
return scopes[scopes.length - 1];
|
||||
},
|
||||
currentNode: rootNode,
|
||||
vueIds,
|
||||
get currentVueId() {
|
||||
return vueIds[vueIds.length - 1];
|
||||
},
|
||||
inVOnce: false,
|
||||
get inVFor() {
|
||||
let parent = scopes[scopes.length - 1];
|
||||
while (parent) {
|
||||
if (isVForScope(parent) && !isScopedSlotVFor(parent)) {
|
||||
return true;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// methods
|
||||
getScopeIndex(scope) {
|
||||
return scopes.indexOf(scope);
|
||||
},
|
||||
popScope() {
|
||||
return scopes.pop();
|
||||
},
|
||||
addVIfScope(initScope) {
|
||||
const vIfScope = createScope(scopes[scopes.length - 1].id, (0, shared_1.extend)(initScope, { parentScope: findVIfParentScope() }));
|
||||
scopes.push(vIfScope);
|
||||
return vIfScope;
|
||||
},
|
||||
addVForScope(initScope) {
|
||||
const vForScope = createScope(new identifier_1.default(), initScope);
|
||||
scopes.push(vForScope);
|
||||
return vForScope;
|
||||
},
|
||||
helper(name) {
|
||||
const count = context.helpers.get(name) || 0;
|
||||
context.helpers.set(name, count + 1);
|
||||
return name;
|
||||
},
|
||||
removeHelper(name) {
|
||||
const count = context.helpers.get(name);
|
||||
if (count) {
|
||||
const currentCount = count - 1;
|
||||
if (!currentCount) {
|
||||
context.helpers.delete(name);
|
||||
}
|
||||
else {
|
||||
context.helpers.set(name, currentCount);
|
||||
}
|
||||
}
|
||||
},
|
||||
helperString(name) {
|
||||
return `_${compiler_core_1.helperNameMap[context.helper(name)]}`;
|
||||
},
|
||||
replaceNode(node) {
|
||||
context.parent.children[context.childIndex] = context.currentNode = node;
|
||||
},
|
||||
removeNode(node) {
|
||||
if (!context.parent) {
|
||||
throw new Error(`Cannot remove root node.`);
|
||||
}
|
||||
const list = context.parent.children;
|
||||
const removalIndex = node
|
||||
? list.indexOf(node)
|
||||
: context.currentNode
|
||||
? context.childIndex
|
||||
: -1;
|
||||
/* istanbul ignore if */
|
||||
if (removalIndex < 0) {
|
||||
throw new Error(`node being removed is not a child of current parent`);
|
||||
}
|
||||
if (!node || node === context.currentNode) {
|
||||
// current node removed
|
||||
context.currentNode = null;
|
||||
context.onNodeRemoved();
|
||||
}
|
||||
else {
|
||||
// sibling node removed
|
||||
if (context.childIndex > removalIndex) {
|
||||
context.childIndex--;
|
||||
context.onNodeRemoved();
|
||||
}
|
||||
}
|
||||
context.parent.children.splice(removalIndex, 1);
|
||||
},
|
||||
onNodeRemoved: () => { },
|
||||
addIdentifiers(exp) {
|
||||
if ((0, shared_1.isString)(exp)) {
|
||||
addId(exp);
|
||||
}
|
||||
else if (exp.identifiers) {
|
||||
exp.identifiers.forEach(addId);
|
||||
}
|
||||
else if (exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
|
||||
addId(exp.content);
|
||||
}
|
||||
},
|
||||
removeIdentifiers(exp) {
|
||||
if ((0, shared_1.isString)(exp)) {
|
||||
removeId(exp);
|
||||
}
|
||||
else if (exp.identifiers) {
|
||||
exp.identifiers.forEach(removeId);
|
||||
}
|
||||
else if (exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
|
||||
removeId(exp.content);
|
||||
}
|
||||
},
|
||||
cache(exp, isVNode = false) {
|
||||
return createCacheExpression(context.cached++, exp, isVNode);
|
||||
},
|
||||
isMiniProgramComponent(name) {
|
||||
return miniProgramComponents[name];
|
||||
},
|
||||
rootNode: null,
|
||||
elementRefIndex: 0,
|
||||
};
|
||||
function addId(id) {
|
||||
const { identifiers } = context;
|
||||
if (identifiers[id] === undefined) {
|
||||
identifiers[id] = 0;
|
||||
}
|
||||
identifiers[id]++;
|
||||
}
|
||||
function removeId(id) {
|
||||
context.identifiers[id]--;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
exports.createTransformContext = createTransformContext;
|
||||
function createCacheExpression(index, value, isVNode = false) {
|
||||
return {
|
||||
type: compiler_core_1.NodeTypes.JS_CACHE_EXPRESSION,
|
||||
index,
|
||||
value,
|
||||
isVNode,
|
||||
loc: compiler_core_1.locStub,
|
||||
};
|
||||
}
|
||||
function createStructuralDirectiveTransform(name, fn) {
|
||||
const matches = (0, shared_1.isString)(name)
|
||||
? (n) => n === name
|
||||
: (n) => name.test(n);
|
||||
return (node, context) => {
|
||||
if (node.type === compiler_core_1.NodeTypes.ELEMENT) {
|
||||
const { props } = node;
|
||||
// structural directive transforms are not concerned with slots
|
||||
// as they are handled separately in vSlot.ts
|
||||
// if (node.tagType === ElementTypes.TEMPLATE && props.some(isVSlot)) {
|
||||
// return
|
||||
// }
|
||||
const exitFns = [];
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
const prop = props[i];
|
||||
if (prop.type === compiler_core_1.NodeTypes.DIRECTIVE && matches(prop.name)) {
|
||||
// structural directives are removed to avoid infinite recursion
|
||||
// also we remove them *before* applying so that it can further
|
||||
// traverse itself in case it moves the node around
|
||||
props.splice(i, 1);
|
||||
i--;
|
||||
const onExit = fn(node, prop, context);
|
||||
if (onExit)
|
||||
exitFns.push(onExit);
|
||||
}
|
||||
}
|
||||
return exitFns;
|
||||
}
|
||||
};
|
||||
}
|
||||
exports.createStructuralDirectiveTransform = createStructuralDirectiveTransform;
|
||||
function createRenderDataExpr(properties, context) {
|
||||
const objExpr = (0, ast_1.createObjectExpression)(properties);
|
||||
if (!hasSpreadElement(objExpr)) {
|
||||
return objExpr;
|
||||
}
|
||||
// filters: ['test']
|
||||
// v-if="text.aa()"
|
||||
if (context.filters.length) {
|
||||
transformFilterObjectSpreadExpr(objExpr, context);
|
||||
}
|
||||
if (context.renderDataSpread) {
|
||||
return objExpr;
|
||||
}
|
||||
return transformObjectSpreadExpr(objExpr, context);
|
||||
}
|
||||
function hasSpreadElement(expr) {
|
||||
return expr.properties.some((prop) => {
|
||||
if ((0, types_1.isSpreadElement)(prop)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
const returnStatement = parseReturnStatement(prop);
|
||||
if (returnStatement) {
|
||||
return hasSpreadElement(returnStatement.argument);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 目前硬编码识别 _f,应该读取 context.helperString
|
||||
const returnObjExprMap = {
|
||||
_f: 1, // _f(_ctx.items,()=>{return {}})
|
||||
_w: 0, // _w(()=>{return {}})
|
||||
};
|
||||
function parseReturnStatement(prop) {
|
||||
if ((0, types_1.isObjectProperty)(prop) &&
|
||||
(0, types_1.isCallExpression)(prop.value) &&
|
||||
(0, types_1.isIdentifier)(prop.value.callee)) {
|
||||
const { name } = prop.value.callee;
|
||||
if ((0, shared_1.hasOwn)(returnObjExprMap, name)) {
|
||||
return prop.value.arguments[returnObjExprMap[name]].body.body[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
function transformObjectPropertyExpr(prop, context) {
|
||||
// vFor,withScopedSlot
|
||||
const returnStatement = parseReturnStatement(prop);
|
||||
if (returnStatement) {
|
||||
const objExpr = returnStatement.argument;
|
||||
if (hasSpreadElement(objExpr)) {
|
||||
returnStatement.argument = transformObjectSpreadExpr(objExpr, context);
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
function transformObjectSpreadExpr(objExpr, context) {
|
||||
const properties = objExpr.properties;
|
||||
const args = [];
|
||||
let objExprProperties = [];
|
||||
properties.forEach((prop) => {
|
||||
if ((0, types_1.isObjectProperty)(prop)) {
|
||||
objExprProperties.push(transformObjectPropertyExpr(prop, context));
|
||||
}
|
||||
else {
|
||||
if (objExprProperties.length) {
|
||||
args.push((0, types_1.objectExpression)(objExprProperties));
|
||||
}
|
||||
args.push(transformConditionalExpression(prop.argument, context));
|
||||
objExprProperties = [];
|
||||
}
|
||||
});
|
||||
if (objExprProperties.length) {
|
||||
args.push((0, types_1.objectExpression)(objExprProperties));
|
||||
}
|
||||
if (args.length === 1) {
|
||||
return args[0];
|
||||
}
|
||||
return (0, types_1.callExpression)((0, types_1.identifier)(context.helperString(runtimeHelpers_1.EXTEND)), args);
|
||||
}
|
||||
function transformConditionalExpression(expr, context) {
|
||||
const { consequent, alternate } = expr;
|
||||
if ((0, types_1.isObjectExpression)(consequent) && hasSpreadElement(consequent)) {
|
||||
expr.consequent = transformObjectSpreadExpr(consequent, context);
|
||||
}
|
||||
if ((0, types_1.isObjectExpression)(alternate)) {
|
||||
if (hasSpreadElement(alternate)) {
|
||||
expr.alternate = transformObjectSpreadExpr(alternate, context);
|
||||
}
|
||||
}
|
||||
else if ((0, types_1.isConditionalExpression)(alternate)) {
|
||||
transformConditionalExpression(alternate, context);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
function transformFilterObjectSpreadExpr(objExpr, context) {
|
||||
const properties = objExpr.properties;
|
||||
properties.forEach((prop) => {
|
||||
if ((0, types_1.isObjectProperty)(prop)) {
|
||||
transformFilterObjectPropertyExpr(prop, context);
|
||||
}
|
||||
else {
|
||||
prop.argument = transformFilterConditionalExpression(prop.argument, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
function transformFilterObjectPropertyExpr(prop, context) {
|
||||
// vFor, withScopedSlot
|
||||
const returnStatement = parseReturnStatement(prop);
|
||||
if (returnStatement) {
|
||||
const objExpr = returnStatement.argument;
|
||||
if (hasSpreadElement(objExpr)) {
|
||||
transformFilterObjectSpreadExpr(objExpr, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
function transformFilterConditionalExpression(expr, context) {
|
||||
const { test, consequent, alternate } = expr;
|
||||
if ((0, types_1.isObjectExpression)(consequent) && hasSpreadElement(consequent)) {
|
||||
transformFilterObjectSpreadExpr(consequent, context);
|
||||
}
|
||||
if ((0, types_1.isObjectExpression)(alternate)) {
|
||||
if (hasSpreadElement(alternate)) {
|
||||
transformFilterObjectSpreadExpr(alternate, context);
|
||||
}
|
||||
}
|
||||
else if ((0, types_1.isConditionalExpression)(alternate)) {
|
||||
expr.alternate = transformFilterConditionalExpression(alternate, context);
|
||||
}
|
||||
const testCode = (0, codegen_1.genBabelExpr)(test);
|
||||
// filter test
|
||||
if (context.filters.find((filter) => testCode.includes(filter + '.'))) {
|
||||
// test.aa() ? {a:1} : {b:2} => {...{a:1},...{b:2}}
|
||||
const properties = [];
|
||||
if (!(0, types_1.isObjectExpression)(consequent) || consequent.properties.length) {
|
||||
properties.push((0, types_1.spreadElement)(consequent));
|
||||
}
|
||||
if (!(0, types_1.isObjectExpression)(expr.alternate) ||
|
||||
expr.alternate.properties.length) {
|
||||
properties.push((0, types_1.spreadElement)(expr.alternate));
|
||||
}
|
||||
return (0, types_1.objectExpression)(properties);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
Reference in New Issue
Block a user