You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
579 lines
19 KiB
579 lines
19 KiB
"use strict"; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.generate = void 0; |
|
const source_map_1 = require("source-map"); |
|
const compiler_core_1 = require("@vue/compiler-core"); |
|
const shared_1 = require("@vue/shared"); |
|
const utils_1 = require("./utils"); |
|
const runtimeHelpers_1 = require("./runtimeHelpers"); |
|
const utils_2 = require("./utils"); |
|
function createCodegenContext(ast, { targetLanguage, mode = 'default', prefixIdentifiers = false, bindingMetadata = {}, sourceMap = false, filename = '', matchEasyCom = shared_1.NOOP, parseUTSComponent = shared_1.NOOP, }) { |
|
const context = { |
|
targetLanguage, |
|
mode, |
|
prefixIdentifiers, |
|
bindingMetadata, |
|
sourceMap, |
|
filename, |
|
source: ast.loc.source, |
|
code: ``, |
|
importEasyComponents: [], |
|
importUTSComponents: [], |
|
column: 1, |
|
line: 1, |
|
offset: 0, |
|
indentLevel: 0, |
|
map: undefined, |
|
matchEasyCom, |
|
parseUTSComponent, |
|
helper(key) { |
|
return `${compiler_core_1.helperNameMap[key]}`; |
|
}, |
|
push(code, node) { |
|
context.code += code; |
|
if (context.map) { |
|
if (node) { |
|
let name; |
|
if (node.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) { |
|
const content = node.content.replace(/^_ctx\./, ''); |
|
if (content !== node.content && (0, compiler_core_1.isSimpleIdentifier)(content)) { |
|
name = content; |
|
} |
|
} |
|
addMapping(node.loc.start, name); |
|
} |
|
(0, compiler_core_1.advancePositionWithMutation)(context, code); |
|
if (node && node.loc !== compiler_core_1.locStub) { |
|
addMapping(node.loc.end); |
|
} |
|
} |
|
}, |
|
indent() { |
|
newline(++context.indentLevel); |
|
}, |
|
deindent(withoutNewLine = false) { |
|
if (withoutNewLine) { |
|
--context.indentLevel; |
|
} |
|
else { |
|
newline(--context.indentLevel); |
|
} |
|
}, |
|
newline() { |
|
newline(context.indentLevel); |
|
}, |
|
}; |
|
function newline(n) { |
|
context.push('\n' + ` `.repeat(n)); |
|
} |
|
function addMapping(loc, name) { |
|
context.map.addMapping({ |
|
name, |
|
source: context.filename, |
|
original: { |
|
line: loc.line, |
|
column: loc.column - 1, // source-map column is 0 based |
|
}, |
|
generated: { |
|
line: context.line, |
|
column: context.column - 1, |
|
}, |
|
}); |
|
} |
|
if (sourceMap) { |
|
// lazy require source-map implementation, only in non-browser builds |
|
context.map = new source_map_1.SourceMapGenerator(); |
|
context.map.setSourceContent(filename, context.source); |
|
} |
|
return context; |
|
} |
|
function generate(ast, options) { |
|
const context = createCodegenContext(ast, options); |
|
const { mode, deindent, indent, push, newline } = context; |
|
if (mode === 'function') { |
|
genEasyComImports(ast.components, context); |
|
push((0, utils_1.genRenderFunctionDecl)(options) + ` {`); |
|
// generate asset resolution statements |
|
if (ast.components.length) { |
|
newline(); |
|
genAssets(ast.components, 'component', context); |
|
if (ast.directives.length || ast.temps > 0) { |
|
newline(); |
|
} |
|
} |
|
if (ast.directives.length) { |
|
genAssets(ast.directives, 'directive', context); |
|
if (ast.temps > 0) { |
|
newline(); |
|
} |
|
} |
|
if (ast.components.length || ast.directives.length || ast.temps) { |
|
newline(); |
|
} |
|
indent(); |
|
push(`return `); |
|
} |
|
if (ast.codegenNode) { |
|
genNode(ast.codegenNode, context); |
|
} |
|
else { |
|
push(`null`); |
|
} |
|
if (mode === 'function') { |
|
deindent(); |
|
push(`}`); |
|
} |
|
return { |
|
code: context.code, |
|
importEasyComponents: context.importEasyComponents, |
|
importUTSComponents: context.importUTSComponents, |
|
// SourceMapGenerator does have toJSON() method but it's not in the types |
|
map: context.map ? context.map.toJSON() : undefined, |
|
}; |
|
} |
|
exports.generate = generate; |
|
function genEasyComImports(components, { push, newline, matchEasyCom, targetLanguage }) { |
|
for (let i = 0; i < components.length; i++) { |
|
let id = components[i]; |
|
const maybeSelfReference = id.endsWith('__self'); |
|
if (maybeSelfReference) { |
|
id = id.slice(0, -6); |
|
} |
|
const source = matchEasyCom(id, true); |
|
if (source) { |
|
const componentId = (0, compiler_core_1.toValidAssetId)(id, 'easycom'); |
|
push(`import ${componentId} from '${source}'`); |
|
newline(); |
|
} |
|
} |
|
} |
|
function genAssets(assets, type, { helper, push, newline, importEasyComponents, matchEasyCom }) { |
|
const resolver = helper(type === 'component' ? runtimeHelpers_1.RESOLVE_COMPONENT : runtimeHelpers_1.RESOLVE_DIRECTIVE); |
|
for (let i = 0; i < assets.length; i++) { |
|
let id = assets[i]; |
|
// potential component implicit self-reference inferred from SFC filename |
|
const maybeSelfReference = id.endsWith('__self'); |
|
if (maybeSelfReference) { |
|
id = id.slice(0, -6); |
|
} |
|
let assetCode = ''; |
|
if (type === 'component') { |
|
const source = matchEasyCom(id, false); |
|
if (source) { |
|
const easyComponentId = (0, compiler_core_1.toValidAssetId)(id, 'easycom'); |
|
const componentId = (0, compiler_core_1.toValidAssetId)(id, type); |
|
assetCode = `const ${componentId} = ${helper(runtimeHelpers_1.RESOLVE_EASY_COMPONENT)}(${JSON.stringify(id)},${easyComponentId}${maybeSelfReference ? `, true` : ``})`; |
|
const importCode = `import ${easyComponentId} from '${source}';`; |
|
if (!importEasyComponents.includes(importCode)) { |
|
importEasyComponents.push(importCode); |
|
} |
|
} |
|
} |
|
if (!assetCode) { |
|
assetCode = `const ${(0, compiler_core_1.toValidAssetId)(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})`; |
|
} |
|
push(assetCode); |
|
if (i < assets.length - 1) { |
|
newline(); |
|
} |
|
} |
|
} |
|
function isText(n) { |
|
return ((0, shared_1.isString)(n) || |
|
n.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ || |
|
n.type === 2 /* NodeTypes.TEXT */ || |
|
n.type === 5 /* NodeTypes.INTERPOLATION */ || |
|
n.type === 8 /* NodeTypes.COMPOUND_EXPRESSION */); |
|
} |
|
function genNodeListAsArray(nodes, context) { |
|
const multilines = nodes.length > 3 || nodes.some((n) => (0, shared_1.isArray)(n) || !isText(n)); |
|
context.push(`[`); |
|
multilines && context.indent(); |
|
genNodeList(nodes, context, multilines); |
|
multilines && context.deindent(); |
|
context.push(`]`); |
|
} |
|
function genNodeList(nodes, context, multilines = false, comma = true) { |
|
const { push, newline } = context; |
|
for (let i = 0; i < nodes.length; i++) { |
|
const node = nodes[i]; |
|
if ((0, shared_1.isString)(node)) { |
|
push(node); |
|
} |
|
else if ((0, shared_1.isArray)(node)) { |
|
genNodeListAsArray(node, context); |
|
} |
|
else { |
|
genNode(node, context); |
|
} |
|
if (i < nodes.length - 1) { |
|
if (multilines) { |
|
comma && push(','); |
|
newline(); |
|
} |
|
else { |
|
comma && push(', '); |
|
} |
|
} |
|
} |
|
} |
|
function genNode(node, context) { |
|
if ((0, shared_1.isString)(node)) { |
|
context.push(node); |
|
return; |
|
} |
|
if ((0, shared_1.isSymbol)(node)) { |
|
context.push(context.helper(node)); |
|
return; |
|
} |
|
switch (node.type) { |
|
case 1 /* NodeTypes.ELEMENT */: |
|
case 9 /* NodeTypes.IF */: |
|
case 11 /* NodeTypes.FOR */: |
|
genNode(node.codegenNode, context); |
|
break; |
|
case 2 /* NodeTypes.TEXT */: |
|
genText(node, context); |
|
break; |
|
case 4 /* NodeTypes.SIMPLE_EXPRESSION */: |
|
genExpression(node, context); |
|
break; |
|
case 5 /* NodeTypes.INTERPOLATION */: |
|
genInterpolation(node, context); |
|
break; |
|
case 12 /* NodeTypes.TEXT_CALL */: |
|
genNode(node.codegenNode, context); |
|
break; |
|
case 8 /* NodeTypes.COMPOUND_EXPRESSION */: |
|
genCompoundExpression(node, context); |
|
break; |
|
case 3 /* NodeTypes.COMMENT */: |
|
genComment(node, context); |
|
break; |
|
case 13 /* NodeTypes.VNODE_CALL */: |
|
genVNodeCall(node, context); |
|
break; |
|
case 14 /* NodeTypes.JS_CALL_EXPRESSION */: |
|
genCallExpression(node, context); |
|
break; |
|
case 15 /* NodeTypes.JS_OBJECT_EXPRESSION */: |
|
genObjectExpression(node, context); |
|
break; |
|
case 17 /* NodeTypes.JS_ARRAY_EXPRESSION */: |
|
genArrayExpression(node, context); |
|
break; |
|
case 18 /* NodeTypes.JS_FUNCTION_EXPRESSION */: |
|
genFunctionExpression(node, context); |
|
break; |
|
case 19 /* NodeTypes.JS_CONDITIONAL_EXPRESSION */: |
|
genConditionalExpression(node, context); |
|
break; |
|
case 20 /* NodeTypes.JS_CACHE_EXPRESSION */: |
|
genCacheExpression(node, context); |
|
break; |
|
/* istanbul ignore next */ |
|
case 10 /* NodeTypes.IF_BRANCH */: |
|
// noop |
|
break; |
|
default: |
|
} |
|
} |
|
function genText(node, context) { |
|
context.push(JSON.stringify(node.content), node); |
|
} |
|
function genExpression(node, context) { |
|
const { content, isStatic } = node; |
|
context.push(isStatic ? JSON.stringify(content) : content, node); |
|
} |
|
function genInterpolation(node, context) { |
|
const { push, helper } = context; |
|
push(`${helper(compiler_core_1.TO_DISPLAY_STRING)}(`); |
|
genNode(node.content, context); |
|
push(`)`); |
|
} |
|
function genCompoundExpression(node, context) { |
|
for (let i = 0; i < node.children.length; i++) { |
|
const child = node.children[i]; |
|
if ((0, shared_1.isString)(child)) { |
|
context.push(child); |
|
} |
|
else { |
|
genNode(child, context); |
|
} |
|
} |
|
} |
|
function genExpressionAsPropertyKey(node, context) { |
|
const { push } = context; |
|
if (node.type === 8 /* NodeTypes.COMPOUND_EXPRESSION */) { |
|
// dynamic arg genObjectExpression have added [] |
|
// push(`[`) |
|
genCompoundExpression(node, context); |
|
// push(`]`) |
|
} |
|
else if (node.isStatic) { |
|
// only quote keys if necessary |
|
const text = JSON.stringify(node.content); |
|
push(text, node); |
|
} |
|
else { |
|
// dynamic arg genObjectExpression have added [] |
|
// push(`[${node.content}]`, node) |
|
push(`${node.content}`, node); |
|
} |
|
} |
|
function genComment(node, context) { |
|
const { push, helper } = context; |
|
push(`${helper(compiler_core_1.CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node); |
|
} |
|
function parseTag(tag, { parseUTSComponent, targetLanguage, importUTSComponents }) { |
|
if ((0, shared_1.isString)(tag)) { |
|
// 原生UTS组件 |
|
const utsComponentOptions = parseUTSComponent(tag.slice(1, -1), targetLanguage); |
|
if (utsComponentOptions) { |
|
const importCode = `import '${utsComponentOptions.source}';`; |
|
if (!importUTSComponents.includes(importCode)) { |
|
importUTSComponents.push(importCode); |
|
} |
|
return (utsComponentOptions.namespace + |
|
'.' + |
|
utsComponentOptions.className + |
|
'.name'); |
|
} |
|
} |
|
return tag; |
|
} |
|
function genVNodeCall(node, context) { |
|
const { push, helper } = context; |
|
const { tag, props, children, patchFlag, dynamicProps, directives, |
|
// isBlock, |
|
disableTracking, isComponent, } = node; |
|
if (directives) { |
|
push(helper(compiler_core_1.WITH_DIRECTIVES) + `(`); |
|
} |
|
const isBlock = false; |
|
if (isBlock) { |
|
push(`(${helper(compiler_core_1.OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `); |
|
} |
|
const callHelper = isBlock |
|
? (0, compiler_core_1.getVNodeBlockHelper)(false, isComponent) |
|
: (0, compiler_core_1.getVNodeHelper)(false, isComponent); |
|
push(helper(callHelper) + `(`, node); |
|
genNodeList(genNullableArgs([ |
|
parseTag(tag, context), |
|
props, |
|
children, |
|
patchFlag, |
|
dynamicProps, |
|
]), context); |
|
push(`)`); |
|
if (isBlock) { |
|
push(`)`); |
|
} |
|
if (directives) { |
|
push(`, `); |
|
genNode(directives, context); |
|
push(`)`); |
|
} |
|
} |
|
function genNullableArgs(args) { |
|
let i = args.length; |
|
while (i--) { |
|
if (args[i] != null) |
|
break; |
|
} |
|
return args.slice(0, i + 1).map((arg) => arg || `null`); |
|
} |
|
// JavaScript |
|
function genCallExpression(node, context) { |
|
const { push, helper } = context; |
|
const callee = (0, shared_1.isString)(node.callee) ? node.callee : helper(node.callee); |
|
push(callee + `(`, node); |
|
if (callee === helper(runtimeHelpers_1.RENDER_LIST)) { |
|
genRenderList(node); |
|
} |
|
if (callee === helper(runtimeHelpers_1.TO_HANDLERS)) { |
|
genToHandlers(node, push); |
|
} |
|
genNodeList(node.arguments, context); |
|
push(`)`); |
|
} |
|
function genRenderList(node) { |
|
node.arguments.forEach((argument) => { |
|
if (argument.type === 18 /* NodeTypes.JS_FUNCTION_EXPRESSION */) { |
|
argument.returnType = 'VNode'; |
|
} |
|
}); |
|
} |
|
function genToHandlers(node, push) { |
|
push(`new Map<string, any | null>([`); |
|
const argument = node.arguments[0]; |
|
if (argument?.type === 8 /* NodeTypes.COMPOUND_EXPRESSION */) { |
|
; |
|
argument.children.forEach((child, index) => { |
|
if ((0, shared_1.isString)(child)) { |
|
if (index === |
|
argument.children.length - 1) { |
|
; |
|
argument.children[index] = |
|
child.replace('}', '])'); |
|
} |
|
else { |
|
; |
|
argument.children[index] = child |
|
.replace('{', '["') |
|
.replace(',', ',["') |
|
.replace(':', '",') |
|
.replaceAll(' ', ''); |
|
} |
|
} |
|
else { |
|
child.content = child.content += ']'; |
|
} |
|
}); |
|
} |
|
if (argument?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) { |
|
; |
|
argument.content = |
|
(0, utils_2.object2Map)(argument.content, false) + '])'; |
|
} |
|
} |
|
function genObjectExpression(node, context) { |
|
const { push, indent, deindent, newline } = context; |
|
const { properties } = node; |
|
if (!properties.length) { |
|
push(`new Map<string, any | null>()`, node); |
|
return; |
|
} |
|
const multilines = properties.length > 1 || |
|
properties.some((p) => p.value.type !== 4 /* NodeTypes.SIMPLE_EXPRESSION */); |
|
push(`new Map<string, any | null>([`); |
|
multilines && indent(); |
|
for (let i = 0; i < properties.length; i++) { |
|
const { key, value } = properties[i]; |
|
push(`[`); |
|
// key |
|
genExpressionAsPropertyKey(key, context); |
|
push(`, `); |
|
// value |
|
genNode(value, context); |
|
push(`]`); |
|
if (i < properties.length - 1) { |
|
// will only reach this if it's multilines |
|
push(`,`); |
|
newline(); |
|
} |
|
} |
|
multilines && deindent(); |
|
push(`])`); |
|
} |
|
function genArrayExpression(node, context) { |
|
genNodeListAsArray(node.elements, context); |
|
} |
|
function genFunctionExpression(node, context) { |
|
const { push, indent, deindent } = context; |
|
const { params, returns, body, newline, isSlot } = node; |
|
if (isSlot) { |
|
// wrap slot functions with owner context |
|
// push(`_${helperNameMap[WITH_CTX]}(`) |
|
push('('); |
|
} |
|
push(`(`, node); |
|
if ((0, shared_1.isArray)(params)) { |
|
genNodeList(params, context); |
|
} |
|
else if (params) { |
|
genNode(params, context); |
|
} |
|
if (node.returnType) { |
|
push(`):${node.returnType} => `); |
|
} |
|
else { |
|
if (isSlot) { |
|
// TODO: supplement types slots params type based on user-defined slots |
|
if (params) { |
|
push(`: Map<string, any | null>): any[] => `); |
|
} |
|
else { |
|
push(`): any[] => `); |
|
} |
|
} |
|
else { |
|
push(`) => `); |
|
} |
|
} |
|
if (newline || body) { |
|
push(`{`); |
|
indent(); |
|
} |
|
if (returns) { |
|
if (newline) { |
|
push(`return `); |
|
} |
|
if ((0, shared_1.isArray)(returns)) { |
|
genNodeListAsArray(returns, context); |
|
} |
|
else { |
|
genNode(returns, context); |
|
} |
|
} |
|
else if (body) { |
|
genNode(body, context); |
|
} |
|
if (newline || body) { |
|
deindent(); |
|
push(`}`); |
|
} |
|
if (isSlot) { |
|
push(`)`); |
|
} |
|
} |
|
function genConditionalExpression(node, context) { |
|
const { test, consequent, alternate, newline: needNewline } = node; |
|
const { push, indent, deindent, newline } = context; |
|
push(`${context.helper(runtimeHelpers_1.IS_TRUE)}(`); |
|
if (test.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) { |
|
genExpression(test, context); |
|
} |
|
else { |
|
genNode(test, context); |
|
} |
|
push(`)`); |
|
needNewline && indent(); |
|
context.indentLevel++; |
|
needNewline || push(` `); |
|
push(`? `); |
|
genNode(consequent, context); |
|
context.indentLevel--; |
|
needNewline && newline(); |
|
needNewline || push(` `); |
|
push(`: `); |
|
const isNested = alternate.type === 19 /* NodeTypes.JS_CONDITIONAL_EXPRESSION */; |
|
if (!isNested) { |
|
context.indentLevel++; |
|
} |
|
genNode(alternate, context); |
|
if (!isNested) { |
|
context.indentLevel--; |
|
} |
|
needNewline && deindent(true /* without newline */); |
|
} |
|
function genCacheExpression(node, context) { |
|
const { push, helper, indent, deindent, newline } = context; |
|
push(`_cache[${node.index}] || (`); |
|
if (node.isVNode) { |
|
indent(); |
|
push(`${helper(compiler_core_1.SET_BLOCK_TRACKING)}(-1),`); |
|
newline(); |
|
} |
|
push(`_cache[${node.index}] = `); |
|
genNode(node.value, context); |
|
if (node.isVNode) { |
|
push(`,`); |
|
newline(); |
|
push(`${helper(compiler_core_1.SET_BLOCK_TRACKING)}(1),`); |
|
newline(); |
|
push(`_cache[${node.index}]`); |
|
deindent(); |
|
} |
|
push(`)`); |
|
}
|
|
|