codegen.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.genExpr = exports.genBabelExpr = exports.generate = void 0;
  7. const shared_1 = require("@vue/shared");
  8. const compiler_core_1 = require("@vue/compiler-core");
  9. const generator_1 = __importDefault(require("@babel/generator"));
  10. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  11. function generate(ast, options) {
  12. const context = createCodegenContext(ast, options);
  13. const { mode, push, indent, deindent, newline, prefixIdentifiers } = context;
  14. const helpers = Array.from(ast.helpers);
  15. const hasHelpers = helpers.length > 0;
  16. const useWithBlock = !prefixIdentifiers && mode !== 'module';
  17. const isSetupInlined = !!options.inline;
  18. // preambles
  19. // in setup() inline mode, the preamble is generated in a sub context
  20. // and returned separately.
  21. const preambleContext = isSetupInlined
  22. ? createCodegenContext(ast, options)
  23. : context;
  24. if (mode === 'module') {
  25. genModulePreamble(ast, preambleContext, isSetupInlined);
  26. }
  27. else {
  28. genFunctionPreamble(ast, preambleContext);
  29. }
  30. // enter render function
  31. const functionName = `render`;
  32. const args = ['_ctx', '_cache'];
  33. if (options.bindingMetadata && !options.inline) {
  34. // binding optimization args
  35. args.push('$props', '$setup', '$data', '$options');
  36. }
  37. const signature = options.isTS
  38. ? args.map((arg) => `${arg}: any`).join(',')
  39. : args.join(', ');
  40. if (isSetupInlined) {
  41. push(`(${signature}) => {`);
  42. }
  43. else {
  44. push(`function ${functionName}(${signature}) {`);
  45. }
  46. indent();
  47. if (useWithBlock) {
  48. push(`with (_ctx) {`);
  49. indent();
  50. if (hasHelpers) {
  51. push(`const { ${helpers
  52. .map((s) => `${compiler_core_1.helperNameMap[s]}: _${compiler_core_1.helperNameMap[s]}`)
  53. .join(', ')} } = _Vue`);
  54. push(`\n`);
  55. newline();
  56. }
  57. }
  58. push(`return `);
  59. push(genBabelExpr(ast.renderData, options.generatorOpts));
  60. if (useWithBlock) {
  61. deindent();
  62. push(`}`);
  63. }
  64. deindent();
  65. push(`}`);
  66. return {
  67. code: context.code,
  68. preamble: isSetupInlined ? preambleContext.code : ``,
  69. // SourceMapGenerator does have toJSON() method but it's not in the types
  70. map: context.map ? context.map.toJSON() : undefined,
  71. };
  72. }
  73. exports.generate = generate;
  74. function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', filename = `template.vue.html`, scopeId = null, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, isTS = false, sourceMap = false, }) {
  75. const context = {
  76. mode,
  77. prefixIdentifiers,
  78. filename,
  79. scopeId,
  80. runtimeGlobalName,
  81. runtimeModuleName,
  82. bindingComponents: ast.bindingComponents,
  83. isTS,
  84. source: ast.loc.source,
  85. code: ``,
  86. column: 1,
  87. line: 1,
  88. offset: 0,
  89. indentLevel: 0,
  90. push(code, node) {
  91. context.code += code;
  92. if (context.map) {
  93. if (node) {
  94. let name;
  95. if (node.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ && !node.isStatic) {
  96. const content = node.content.replace(/^_ctx\./, '');
  97. if (content !== node.content && (0, compiler_core_1.isSimpleIdentifier)(content)) {
  98. name = content;
  99. }
  100. }
  101. addMapping(node.loc.start, name);
  102. }
  103. (0, compiler_core_1.advancePositionWithMutation)(context, code);
  104. if (node && node.loc !== compiler_core_1.locStub) {
  105. addMapping(node.loc.end);
  106. }
  107. }
  108. },
  109. indent() {
  110. newline(++context.indentLevel);
  111. },
  112. deindent(withoutNewLine = false) {
  113. if (withoutNewLine) {
  114. --context.indentLevel;
  115. }
  116. else {
  117. newline(--context.indentLevel);
  118. }
  119. },
  120. newline() {
  121. newline(context.indentLevel);
  122. },
  123. };
  124. function newline(n) {
  125. context.push('\n' + ` `.repeat(n));
  126. }
  127. function addMapping(loc, name) {
  128. context.map.addMapping({
  129. name,
  130. source: context.filename || '',
  131. original: {
  132. line: loc.line,
  133. column: loc.column - 1, // source-map column is 0 based
  134. },
  135. generated: {
  136. line: context.line,
  137. column: context.column - 1,
  138. },
  139. });
  140. }
  141. // 暂时无需提供 sourcemap 支持
  142. // if (sourceMap) {
  143. // // lazy require source-map implementation
  144. // context.map = new SourceMapGenerator()
  145. // context.map!.setSourceContent(filename, context.source)
  146. // }
  147. return context;
  148. }
  149. function genComponentImports(bindingComponents, { push, newline }) {
  150. const tags = Object.keys(bindingComponents);
  151. const importDeclarations = [];
  152. // 仅记录easycom和setup组件
  153. const components = [];
  154. tags.forEach((tag) => {
  155. const { name, type } = bindingComponents[tag];
  156. if (type === "unknown" /* BindingComponentTypes.UNKNOWN */) {
  157. const source = (0, uni_cli_shared_1.matchEasycom)(tag);
  158. if (source) {
  159. // 调整为easycom命名
  160. const easycomName = name.replace('component', 'easycom');
  161. bindingComponents[tag].name = easycomName;
  162. components.push(easycomName);
  163. (0, uni_cli_shared_1.addImportDeclaration)(importDeclarations, easycomName, source);
  164. }
  165. }
  166. else if (type === "setup" /* BindingComponentTypes.SETUP */) {
  167. components.push(name);
  168. }
  169. });
  170. if (tags.length) {
  171. push(`const __BINDING_COMPONENTS__ = '` +
  172. JSON.stringify(bindingComponents) +
  173. `'`);
  174. const resolveComponents = [];
  175. const names = [];
  176. Object.keys(bindingComponents).forEach((id) => {
  177. const { type, name } = bindingComponents[id];
  178. if (type === "unknown" /* BindingComponentTypes.UNKNOWN */) {
  179. resolveComponents.push(`const ${name} = _${compiler_core_1.helperNameMap[compiler_core_1.RESOLVE_COMPONENT]}("${id}");`);
  180. names.push(name);
  181. }
  182. });
  183. if (resolveComponents.length) {
  184. newline();
  185. push(`if (!Array) {`);
  186. resolveComponents.forEach((code) => {
  187. push(code);
  188. });
  189. push(`(${names.join('+')})()`);
  190. push(`}`);
  191. }
  192. newline();
  193. importDeclarations.forEach((str) => push(str));
  194. if (importDeclarations.length) {
  195. newline();
  196. }
  197. if (components.length) {
  198. push(`if (!Math) {`);
  199. push(` (${components.map((name) => name).join('+')})() `);
  200. push(`}`);
  201. newline();
  202. }
  203. }
  204. }
  205. function genFunctionPreamble(ast, context) {
  206. const { prefixIdentifiers, push, newline, runtimeGlobalName, bindingComponents, } = context;
  207. const VueBinding = runtimeGlobalName;
  208. const aliasHelper = (s) => `${compiler_core_1.helperNameMap[s]}: _${compiler_core_1.helperNameMap[s]}`;
  209. const helpers = Array.from(ast.helpers);
  210. if (helpers.length > 0) {
  211. if (prefixIdentifiers) {
  212. push(`const { ${helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`);
  213. }
  214. else {
  215. push(`const _Vue = ${VueBinding}\n`);
  216. }
  217. }
  218. genComponentImports(bindingComponents, context);
  219. newline();
  220. push(`return `);
  221. }
  222. function genModulePreamble(ast, context, inline) {
  223. const { push, newline, runtimeModuleName, bindingComponents } = context;
  224. const helpers = Array.from(ast.helpers);
  225. if (helpers.length) {
  226. push(`import { ${helpers
  227. .map((s) => `${compiler_core_1.helperNameMap[s]} as _${compiler_core_1.helperNameMap[s]}`)
  228. .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`);
  229. }
  230. if (ast.imports.length) {
  231. genImports(ast.imports, context);
  232. }
  233. genComponentImports(bindingComponents, context);
  234. newline();
  235. if (!inline) {
  236. push(`export `);
  237. }
  238. }
  239. function genImports(importsOptions, { push, newline }) {
  240. if (!importsOptions.length) {
  241. return;
  242. }
  243. importsOptions.forEach((imports) => {
  244. push(`import `);
  245. push(genExpr(imports.exp));
  246. push(` from '${imports.path}'`);
  247. newline();
  248. });
  249. }
  250. function createGenNodeContext() {
  251. const context = {
  252. code: '',
  253. helper(key) {
  254. return `_${compiler_core_1.helperNameMap[key]}`;
  255. },
  256. push(code) {
  257. context.code += code;
  258. },
  259. };
  260. return context;
  261. }
  262. function genBabelExpr(expr, opts = {}) {
  263. if (!(0, shared_1.hasOwn)(opts, 'jsescOption')) {
  264. opts.jsescOption = {};
  265. }
  266. opts.jsescOption.quotes = 'single';
  267. return (0, generator_1.default)(expr, opts).code;
  268. }
  269. exports.genBabelExpr = genBabelExpr;
  270. function genExpr(node, context) {
  271. return genNode(node, context).code;
  272. }
  273. exports.genExpr = genExpr;
  274. function genNode(node, context) {
  275. if (!context) {
  276. context = createGenNodeContext();
  277. }
  278. if ((0, shared_1.isString)(node)) {
  279. context.push(node);
  280. return context;
  281. }
  282. if ((0, shared_1.isSymbol)(node)) {
  283. context.push(context.helper(node));
  284. return context;
  285. }
  286. switch (node.type) {
  287. case 2 /* NodeTypes.TEXT */:
  288. genText(node, context);
  289. break;
  290. case 4 /* NodeTypes.SIMPLE_EXPRESSION */:
  291. genExpression(node, context);
  292. break;
  293. case 5 /* NodeTypes.INTERPOLATION */:
  294. genInterpolation(node, context);
  295. break;
  296. case 8 /* NodeTypes.COMPOUND_EXPRESSION */:
  297. genCompoundExpression(node, context);
  298. break;
  299. }
  300. return context;
  301. }
  302. function genText(node, context) {
  303. context.push(JSON.stringify(node.content), node);
  304. }
  305. function genExpression(node, context) {
  306. const { content, isStatic } = node;
  307. context.push(isStatic ? JSON.stringify(content) : content, node);
  308. }
  309. function genInterpolation(node, context) {
  310. const { push, helper } = context;
  311. push(`${helper(compiler_core_1.TO_DISPLAY_STRING)}(`);
  312. genExpr(node.content, context);
  313. push(`)`);
  314. }
  315. function genCompoundExpression(node, context) {
  316. for (let i = 0; i < node.children.length; i++) {
  317. const child = node.children[i];
  318. if ((0, shared_1.isString)(child)) {
  319. context.push(child);
  320. }
  321. else {
  322. genExpr(child, context);
  323. }
  324. }
  325. }