436 lines
14 KiB
JavaScript
436 lines
14 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.genElementProps = exports.genNode = exports.generate = void 0;
|
||
const shared_1 = require("@vue/shared");
|
||
const uni_shared_1 = require("@dcloudio/uni-shared");
|
||
const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
|
||
const compiler_core_1 = require("@vue/compiler-core");
|
||
const codegen_1 = require("../codegen");
|
||
const vFor_1 = require("../transforms/vFor");
|
||
const vIf_1 = require("../transforms/vIf");
|
||
const vSlot_1 = require("../transforms/vSlot");
|
||
const utils_1 = require("../transforms/utils");
|
||
function generate({ children }, { slot, event, scopeId, emitFile, filename, directive, lazyElement, isBuiltInComponent, isMiniProgramComponent, component, }) {
|
||
const context = {
|
||
slot,
|
||
event,
|
||
code: '',
|
||
scopeId,
|
||
directive,
|
||
lazyElement,
|
||
component,
|
||
isBuiltInComponent,
|
||
isMiniProgramComponent,
|
||
push(code) {
|
||
context.code += code;
|
||
},
|
||
};
|
||
children.forEach((node) => {
|
||
genNode(node, context);
|
||
});
|
||
emitFile({ type: 'asset', fileName: filename, source: context.code });
|
||
}
|
||
exports.generate = generate;
|
||
function genNode(node, context) {
|
||
switch (node.type) {
|
||
case 9 /* NodeTypes.IF */:
|
||
return node.branches.forEach((node) => {
|
||
genNode(node, context);
|
||
});
|
||
case 2 /* NodeTypes.TEXT */:
|
||
return genText(node, context);
|
||
case 5 /* NodeTypes.INTERPOLATION */:
|
||
return genExpression(node.content, context);
|
||
case 1 /* NodeTypes.ELEMENT */:
|
||
if (node.tagType === 2 /* ElementTypes.SLOT */) {
|
||
return genSlot(node, context);
|
||
}
|
||
else if (node.tagType === 1 /* ElementTypes.COMPONENT */) {
|
||
return genComponent(node, context);
|
||
}
|
||
else if (node.tagType === 3 /* ElementTypes.TEMPLATE */) {
|
||
return genTemplate(node, context);
|
||
}
|
||
else if (isLazyElement(node, context)) {
|
||
return genLazyElement(node, context);
|
||
}
|
||
return genElement(node, context);
|
||
}
|
||
}
|
||
exports.genNode = genNode;
|
||
function genText(node, { push }) {
|
||
push(node.content);
|
||
}
|
||
function genExpression(node, { push }) {
|
||
push(`{{${(0, codegen_1.genExpr)(node)}}}`);
|
||
}
|
||
function genVIf(exp, { push, directive }) {
|
||
push(` ${directive}if="{{${exp}}}"`);
|
||
}
|
||
function genVElseIf(exp, { push, directive }) {
|
||
push(` ${directive}elif="{{${exp}}}"`);
|
||
}
|
||
function genVElse({ push, directive }) {
|
||
push(` ${directive}else`);
|
||
}
|
||
function genVFor(node, { push, directive }) {
|
||
const { sourceCode, valueAlias, indexAlias } = node.vFor;
|
||
push(` ${directive}for="${sourceCode}"`);
|
||
if (valueAlias) {
|
||
push(` ${directive}for-item="${valueAlias}"`);
|
||
}
|
||
if (valueAlias === 'index') {
|
||
push(` ${directive}for-index="${indexAlias}"`);
|
||
}
|
||
const keyProp = (0, compiler_core_1.findProp)(node, 'key', true);
|
||
if (keyProp) {
|
||
const key = keyProp.exp.content;
|
||
push(` ${directive}key="${key.includes('.') ? key.split('.')[1] : key}"`);
|
||
node.props.splice(node.props.indexOf(keyProp), 1);
|
||
}
|
||
}
|
||
function genSlot(node, context) {
|
||
// 移除掉所有非name属性,即移除作用域插槽的绑定指令
|
||
node.props = node.props.filter((prop) => {
|
||
if (prop.type === 6 /* NodeTypes.ATTRIBUTE */) {
|
||
return prop.name === 'name';
|
||
}
|
||
else if (prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
|
||
return prop.arg.content === 'name';
|
||
}
|
||
});
|
||
if (!node.children.length || context.slot.fallbackContent) {
|
||
// 无后备内容或支持后备内容
|
||
return genElement(node, context);
|
||
}
|
||
const { push } = context;
|
||
const isVIfSlot = (0, vIf_1.isIfElementNode)(node);
|
||
if (isVIfSlot) {
|
||
push(`<block`);
|
||
genVIfCode(node, context);
|
||
push(`>`);
|
||
delete node.vIf;
|
||
}
|
||
const children = node.children.slice();
|
||
node.children.length = 0;
|
||
push(`<block`);
|
||
const nameProp = (0, compiler_core_1.findProp)(node, 'name');
|
||
let name = uni_shared_1.SLOT_DEFAULT_NAME;
|
||
if (nameProp) {
|
||
if ((0, uni_cli_shared_1.isAttributeNode)(nameProp)) {
|
||
if (nameProp.value?.content) {
|
||
name = nameProp.value.content;
|
||
}
|
||
}
|
||
else {
|
||
if (nameProp.slotName) {
|
||
name = nameProp.slotName;
|
||
}
|
||
}
|
||
}
|
||
genVIf(`$slots.${name}`, context);
|
||
push(`>`);
|
||
genElement(node, context);
|
||
push(`</block>`);
|
||
push(`<block`);
|
||
genVElse(context);
|
||
push(`>`);
|
||
children.forEach((node) => {
|
||
genNode(node, context);
|
||
});
|
||
push(`</block>`);
|
||
if (isVIfSlot) {
|
||
push(`</block>`);
|
||
}
|
||
}
|
||
function genTemplate(node, context) {
|
||
const slotProp = node.props.find((prop) => prop.type === 7 /* NodeTypes.DIRECTIVE */ &&
|
||
(prop.name === 'slot' ||
|
||
(prop.name === 'bind' &&
|
||
prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
|
||
prop.arg.content === 'slot')));
|
||
// 为 bind 时,通常是作用域插槽生成的 vSlot.ts:197 createBindDirectiveNode('slot',...)
|
||
if (slotProp && (slotProp.name === 'bind' || (0, vSlot_1.findSlotName)(slotProp))) {
|
||
/**
|
||
* 仅百度、字节支持使用 block 作为命名插槽根节点
|
||
* 此处为了统一仅默认替换为view
|
||
* <template v-slot/> => <view slot="">
|
||
*/
|
||
node.tag = 'view';
|
||
}
|
||
else {
|
||
// <template/> => <block/>
|
||
node.tag = 'block';
|
||
}
|
||
// @ts-ignore
|
||
node.tagType = 0 /* ElementTypes.ELEMENT */;
|
||
// 仅单个子节点的命名插槽(非作用域),直接使用子节点作为插槽使用,避免多增加的 view 节点影响 flex 排版
|
||
if (slotProp &&
|
||
node.tag === 'view' &&
|
||
!(0, vFor_1.isForElementNode)(node) &&
|
||
node.children.length === 1) {
|
||
const child = node.children[0];
|
||
if ((0, uni_cli_shared_1.isElementNode)(child) &&
|
||
!(0, vFor_1.isForElementNode)(child) &&
|
||
!(0, compiler_core_1.isSlotOutlet)(child)) {
|
||
child.props.push(slotProp);
|
||
return genElement(child, context);
|
||
}
|
||
}
|
||
return genElement(node, context);
|
||
}
|
||
function genComponent(node, context) {
|
||
if (context.component?.getPropertySync) {
|
||
return genElement(node, context);
|
||
}
|
||
if ((0, vIf_1.isIfElementNode)(node) || (0, vFor_1.isForElementNode)(node)) {
|
||
return genElement(node, context);
|
||
}
|
||
// 小程序原生组件,补充 if(r0)
|
||
if (context.isMiniProgramComponent(node.tag)) {
|
||
;
|
||
node.vIf = {
|
||
name: 'if',
|
||
condition: 'r0',
|
||
};
|
||
return genElement(node, context);
|
||
}
|
||
const prop = (0, compiler_core_1.findProp)(node, utils_1.ATTR_VUE_PROPS);
|
||
if (!prop) {
|
||
return genElement(node, context);
|
||
}
|
||
;
|
||
node.vIf = {
|
||
name: 'if',
|
||
condition: prop.exp.content,
|
||
};
|
||
return genElement(node, context);
|
||
}
|
||
function isLazyElement(node, context) {
|
||
if (!context.lazyElement) {
|
||
return false;
|
||
}
|
||
let lazyProps;
|
||
if ((0, shared_1.isFunction)(context.lazyElement)) {
|
||
const res = context.lazyElement(node, context);
|
||
if (!(0, shared_1.isPlainObject)(res)) {
|
||
return res;
|
||
}
|
||
lazyProps = res[node.tag];
|
||
}
|
||
else {
|
||
lazyProps = context.lazyElement[node.tag];
|
||
}
|
||
if (lazyProps === true) {
|
||
return true;
|
||
}
|
||
if (!lazyProps) {
|
||
return;
|
||
}
|
||
return node.props.some((prop) => prop.type === 7 /* NodeTypes.DIRECTIVE */ &&
|
||
lazyProps.find((lazyProp) => {
|
||
return (prop.name === lazyProp.name &&
|
||
prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
|
||
lazyProp.arg.includes(prop.arg.content));
|
||
}));
|
||
}
|
||
/**
|
||
* 部分内置组件的部分事件在初始化时会立刻触发,但标准事件需要等首次渲染才能确认事件函数,故增加wx:if="{{r0}}"
|
||
* @param node
|
||
* @param context
|
||
*/
|
||
function genLazyElement(node, context) {
|
||
const { push } = context;
|
||
if (!(0, vIf_1.isIfElementNode)(node)) {
|
||
push(`<block`);
|
||
// r0 => ready 首次渲染
|
||
genVIf(`r0`, context);
|
||
push(`>`);
|
||
genElement(node, context);
|
||
push(`</block>`);
|
||
return;
|
||
}
|
||
// v-if,v-else-if 无需处理
|
||
if (node.vIf.name !== 'else') {
|
||
return genElement(node, context);
|
||
}
|
||
push(`<block`);
|
||
genVElse(context);
|
||
push(`>`);
|
||
node.vIf.name = 'if';
|
||
node.vIf.condition = 'r0';
|
||
genElement(node, context);
|
||
push(`</block>`);
|
||
}
|
||
function genVIfCode(node, context) {
|
||
const { name, condition } = node.vIf;
|
||
if (name === 'if') {
|
||
genVIf(condition, context);
|
||
}
|
||
else if (name === 'else-if') {
|
||
genVElseIf(condition, context);
|
||
}
|
||
else if (name === 'else') {
|
||
genVElse(context);
|
||
}
|
||
}
|
||
function genElement(node, context) {
|
||
const { children, isSelfClosing, props } = node;
|
||
let tag = node.tag;
|
||
// <template slot="left"/> => <block slot="left"/>
|
||
if (tag === 'template') {
|
||
if ((0, compiler_core_1.findProp)(node, 'slot')) {
|
||
tag = 'view';
|
||
}
|
||
else {
|
||
tag = 'block';
|
||
}
|
||
}
|
||
// 无用的 block
|
||
if (tag === 'block' &&
|
||
props.length === 0 &&
|
||
!(0, vIf_1.isIfElementNode)(node) &&
|
||
!(0, vFor_1.isForElementNode)(node)) {
|
||
return children.forEach((node) => {
|
||
genNode(node, context);
|
||
});
|
||
}
|
||
let virtualHost = false;
|
||
if ((0, uni_cli_shared_1.isUserComponent)(node, context)) {
|
||
tag = (0, shared_1.hyphenate)(tag);
|
||
if (context.component?.normalizeName) {
|
||
tag = context.component?.normalizeName(tag);
|
||
}
|
||
if (context.component?.mergeVirtualHostAttributes) {
|
||
virtualHost = true;
|
||
}
|
||
}
|
||
const { push } = context;
|
||
const hasVIf = (0, vIf_1.isIfElementNode)(node);
|
||
const hasVFor = (0, vFor_1.isForElementNode)(node);
|
||
const hasVIfAndVFor = hasVIf && hasVFor;
|
||
// 小程序中 wx:else wx:elif 不支持与 wx:for 同时使用
|
||
// 故 if 需要补充一层 block
|
||
if (hasVIfAndVFor) {
|
||
push(`<block`);
|
||
genVIfCode(node, context);
|
||
push(`>`);
|
||
}
|
||
push(`<${tag}`);
|
||
if (!hasVIfAndVFor && hasVIf) {
|
||
genVIfCode(node, context);
|
||
}
|
||
if (hasVFor) {
|
||
genVFor(node, context);
|
||
}
|
||
if (props.length) {
|
||
genElementProps(node, virtualHost, context);
|
||
}
|
||
if (isSelfClosing) {
|
||
push(`/>`);
|
||
}
|
||
else {
|
||
push(`>`);
|
||
children.forEach((node) => {
|
||
genNode(node, context);
|
||
});
|
||
push(`</${tag}>`);
|
||
}
|
||
if (hasVIfAndVFor) {
|
||
push(`</block>`);
|
||
}
|
||
}
|
||
function checkVirtualHostProps(name, virtualHost) {
|
||
const names = [name];
|
||
if (virtualHost) {
|
||
const obj = {
|
||
style: utils_1.VIRTUAL_HOST_STYLE,
|
||
class: utils_1.VIRTUAL_HOST_CLASS,
|
||
};
|
||
if (name in obj) {
|
||
// TODO 支付宝平台移除原有属性(支付宝小程序自定义组件外部属性始终无效)
|
||
names.push(obj[name]);
|
||
}
|
||
return names;
|
||
}
|
||
return names;
|
||
}
|
||
function genElementProps(node, virtualHost, context) {
|
||
node.props.forEach((prop) => {
|
||
if (prop.type === 6 /* NodeTypes.ATTRIBUTE */) {
|
||
const { value } = prop;
|
||
if (value) {
|
||
checkVirtualHostProps(prop.name, virtualHost).forEach((name) => {
|
||
context.push(` ${name}="${value.content}"`);
|
||
});
|
||
}
|
||
else {
|
||
context.push(` ${prop.name}`);
|
||
}
|
||
}
|
||
else {
|
||
const { name } = prop;
|
||
if (name === 'on') {
|
||
genOn(prop, node, context);
|
||
}
|
||
else {
|
||
genDirectiveNode(prop, node, virtualHost, context);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
exports.genElementProps = genElementProps;
|
||
function genOn(prop, node, { push, event, isBuiltInComponent }) {
|
||
const arg = prop.arg.content;
|
||
const exp = prop.exp;
|
||
const modifiers = prop.modifiers;
|
||
const name = (event?.format || uni_cli_shared_1.formatMiniProgramEvent)(arg, {
|
||
isCatch: modifiers.includes('stop') || modifiers.includes('prevent'),
|
||
isCapture: modifiers.includes('capture'),
|
||
isComponent: (0, uni_cli_shared_1.isUserComponent)(node, { isBuiltInComponent }),
|
||
});
|
||
if (exp.isStatic) {
|
||
push(` ${name}="${exp.content}"`);
|
||
}
|
||
else {
|
||
push(` ${name}="{{${exp.content}}}"`);
|
||
}
|
||
}
|
||
function genDirectiveNode(prop, node, virtualHost, context) {
|
||
const { push, component } = context;
|
||
if (prop.name === 'slot') {
|
||
if (prop.arg) {
|
||
const arg = prop.arg;
|
||
if (arg.isStatic) {
|
||
const slotName = (0, uni_shared_1.dynamicSlotName)(arg.content);
|
||
// 非作用域默认插槽不生成 slot 属性
|
||
if (slotName !== uni_shared_1.SLOT_DEFAULT_NAME) {
|
||
push(` slot="${slotName}"`);
|
||
}
|
||
}
|
||
else {
|
||
push(` slot="{{${arg.content}}}"`);
|
||
}
|
||
}
|
||
}
|
||
else if (prop.name === 'show') {
|
||
let hiddenPropName = 'hidden';
|
||
if ((0, uni_cli_shared_1.isUserComponent)(node, context) && component && component.vShow) {
|
||
hiddenPropName = component.vShow;
|
||
}
|
||
push(` ${hiddenPropName}="{{!${prop.exp.content}}}"`);
|
||
}
|
||
else if (prop.arg && prop.exp) {
|
||
const arg = prop.arg.content;
|
||
const exp = prop.exp.content;
|
||
checkVirtualHostProps(arg, virtualHost).forEach((arg) => {
|
||
push(` ${arg}="{{${exp}}}"`);
|
||
});
|
||
}
|
||
else {
|
||
if (prop.name !== 'bind') {
|
||
throw new Error(`unknown directive ` + JSON.stringify(prop));
|
||
}
|
||
}
|
||
}
|