@dimina/compiler 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,634 @@
1
+ import fs from "node:fs";
2
+ import { isMainThread, parentPort } from "node:worker_threads";
3
+ import babel from "@babel/core";
4
+ import types from "@babel/types";
5
+ import _traverse from "@babel/traverse";
6
+ import { compileTemplate } from "@vue/compiler-sfc";
7
+ import * as cheerio from "cheerio";
8
+ import * as htmlparser2 from "htmlparser2";
9
+ import { transform } from "esbuild";
10
+ import { r as resetStoreInfo, g as getTargetPath, a as getComponent, b as getContentByPath, c as getAbsolutePath, d as collectAssets, e as getAppId, f as getWorkPath, t as transformRpx, h as tagWhiteList } from "../env-CezfCSQz.js";
11
+ const traverse = _traverse.default ? _traverse.default : _traverse;
12
+ const fileType = [".wxml", ".ddml"];
13
+ const compileResCache = /* @__PURE__ */ new Map();
14
+ const processedModules = /* @__PURE__ */ new Set();
15
+ if (!isMainThread) {
16
+ parentPort.on("message", async ({ pages, storeInfo }) => {
17
+ try {
18
+ resetStoreInfo(storeInfo);
19
+ const progress = {
20
+ _completedTasks: 0,
21
+ get completedTasks() {
22
+ return this._completedTasks;
23
+ },
24
+ set completedTasks(value) {
25
+ this._completedTasks = value;
26
+ parentPort.postMessage({ completedTasks: this._completedTasks });
27
+ }
28
+ };
29
+ await compileML(pages.mainPages, null, progress);
30
+ for (const [root, subPages] of Object.entries(pages.subPages)) {
31
+ await compileML(subPages.info, root, progress);
32
+ }
33
+ parentPort.postMessage({ success: true });
34
+ } catch (error) {
35
+ parentPort.postMessage({ success: false, error: error.message });
36
+ }
37
+ });
38
+ }
39
+ async function compileML(pages, root, progress) {
40
+ for (const page of pages) {
41
+ const scriptRes = /* @__PURE__ */ new Map();
42
+ buildCompileView(page, false, scriptRes, []);
43
+ let mergeRender = "";
44
+ for (const [key, value] of scriptRes.entries()) {
45
+ const amdFormat = `modDefine('${key}', function(require, module, exports) {
46
+ ${value}
47
+ });`;
48
+ const { code: minifiedCode } = await transform(amdFormat, {
49
+ minify: true,
50
+ target: ["es2020"],
51
+ platform: "browser"
52
+ });
53
+ mergeRender += minifiedCode;
54
+ }
55
+ const filename = `${page.path.replace(/\//g, "_")}`;
56
+ if (root) {
57
+ const subDir = `${getTargetPath()}/${root}`;
58
+ if (!fs.existsSync(subDir)) {
59
+ fs.mkdirSync(subDir);
60
+ }
61
+ fs.writeFileSync(`${subDir}/${filename}.js`, mergeRender);
62
+ } else {
63
+ const mainDir = `${getTargetPath()}/main`;
64
+ if (!fs.existsSync(mainDir)) {
65
+ fs.mkdirSync(mainDir);
66
+ }
67
+ fs.writeFileSync(`${mainDir}/${filename}.js`, mergeRender);
68
+ }
69
+ progress.completedTasks++;
70
+ }
71
+ }
72
+ function buildCompileView(module, isComponent = false, scriptRes, depthChain = []) {
73
+ const currentPath = module.path;
74
+ if (depthChain.includes(currentPath)) {
75
+ console.warn(`检测到循环依赖: ${[...depthChain, currentPath].join(" -> ")}`);
76
+ }
77
+ if (depthChain.length > 100) {
78
+ console.warn(`检测到深度依赖: ${[...depthChain, currentPath].join(" -> ")}`);
79
+ }
80
+ depthChain = [...depthChain, currentPath];
81
+ compileModule(module, isComponent, scriptRes);
82
+ if (module.usingComponents) {
83
+ for (const componentInfo of Object.values(module.usingComponents)) {
84
+ const componentModule = getComponent(componentInfo);
85
+ if (!componentModule) {
86
+ continue;
87
+ }
88
+ buildCompileView(componentModule, true, scriptRes, depthChain);
89
+ }
90
+ }
91
+ processedModules.add(currentPath);
92
+ }
93
+ function compileModule(module, isComponent, scriptRes) {
94
+ const { tpl, instruction } = toCompileTemplate(isComponent, module.path, module.usingComponents, module.componentPlaceholder);
95
+ if (!tpl) {
96
+ return;
97
+ }
98
+ if (!scriptRes.has(module.path) && compileResCache.has(module.path)) {
99
+ const res = compileResCache.get(module.path);
100
+ scriptRes.set(module.path, res);
101
+ for (const sm of instruction.scriptModule) {
102
+ if (!scriptRes.has(sm.path)) {
103
+ scriptRes.set(sm.path, sm.code);
104
+ }
105
+ }
106
+ return;
107
+ }
108
+ const processedTpl = tpl.replace(/\bthis\./g, "_ctx.");
109
+ const tplCode = compileTemplate({
110
+ source: processedTpl,
111
+ filename: module.path,
112
+ // 用于错误提示
113
+ id: `data-v-${module.id}`,
114
+ scoped: true,
115
+ compilerOptions: {
116
+ // https://template-explorer.vuejs.org/
117
+ prefixIdentifiers: true,
118
+ hoistStatic: false,
119
+ cacheHandlers: true,
120
+ scopeId: `data-v-${module.id}`,
121
+ mode: "function",
122
+ inline: true
123
+ }
124
+ });
125
+ let tplComponents = "{";
126
+ for (const tm of instruction.templateModule) {
127
+ tplComponents += `'${tm.path}':${compileTemplate({
128
+ source: tm.tpl,
129
+ filename: tm.path,
130
+ id: `data-v-${module.id}`,
131
+ scoped: true,
132
+ compilerOptions: {
133
+ prefixIdentifiers: true,
134
+ hoistStatic: false,
135
+ cacheHandlers: true,
136
+ scopeId: `data-v-${module.id}`,
137
+ mode: "function",
138
+ inline: true
139
+ }
140
+ }).code},`;
141
+ }
142
+ tplComponents += "}";
143
+ const tplAst = babel.parseSync(tplCode.code);
144
+ for (const sm of instruction.scriptModule) {
145
+ if (!scriptRes.has(sm.path)) {
146
+ scriptRes.set(sm.path, sm.code);
147
+ }
148
+ const assignmentExpression = types.assignmentExpression(
149
+ "=",
150
+ // 创建赋值表达式
151
+ types.memberExpression(
152
+ types.identifier("_ctx"),
153
+ // 对象标识符
154
+ types.identifier(sm.path),
155
+ // 属性标识符
156
+ false
157
+ // 是否是计算属性
158
+ ),
159
+ // 创建require调用表达式
160
+ types.callExpression(
161
+ types.identifier("require"),
162
+ // 函数标识符
163
+ [types.stringLiteral(sm.path)]
164
+ // 参数列表,这里传入字符串字面量'foo'
165
+ )
166
+ );
167
+ const expressionStatement = types.expressionStatement(assignmentExpression);
168
+ tplAst.program.body[0].expression.body.body.splice(0, 0, expressionStatement);
169
+ }
170
+ const { code: transCode } = babel.transformFromAstSync(tplAst, "", {
171
+ comments: false
172
+ });
173
+ const code = `Module({
174
+ path: '${module.path}',
175
+ id: '${module.id}',
176
+ render: ${transCode.replace(/;$/, "").replace(/^"use strict";\s*/, "")},
177
+ usingComponents: ${JSON.stringify(module.usingComponents)},
178
+ tplComponents: ${tplComponents},
179
+ });`;
180
+ compileResCache.set(module.path, code);
181
+ scriptRes.set(module.path, code);
182
+ }
183
+ function toCompileTemplate(isComponent, path, components, componentPlaceholder) {
184
+ const workPath = getWorkPath();
185
+ const fullPath = getViewPath(workPath, path);
186
+ if (!fullPath) {
187
+ return { tpl: void 0 };
188
+ }
189
+ let content = getContentByPath(fullPath).trim();
190
+ if (!content) {
191
+ content = "<block></block>";
192
+ } else {
193
+ if (isComponent) {
194
+ content = `<wrapper>${content}</wrapper>`;
195
+ }
196
+ }
197
+ const templateModule = [];
198
+ const $ = cheerio.load(content, {
199
+ xmlMode: true,
200
+ decodeEntities: false
201
+ });
202
+ const includeNodes = $("include");
203
+ includeNodes.each((_, elem) => {
204
+ const src = $(elem).attr("src");
205
+ if (src) {
206
+ const includeContent = getContentByPath(getAbsolutePath(workPath, path, src)).trim();
207
+ if (includeContent) {
208
+ const $includeContent = cheerio.load(includeContent, {
209
+ xmlMode: true,
210
+ decodeEntities: false
211
+ });
212
+ $includeContent("template").remove();
213
+ $includeContent("wxs").remove();
214
+ $includeContent("dds").remove();
215
+ $(elem).html($includeContent.html());
216
+ }
217
+ }
218
+ });
219
+ transTagTemplate($, templateModule, path, components);
220
+ const importNodes = $("import");
221
+ importNodes.each((_, elem) => {
222
+ const src = $(elem).attr("src");
223
+ if (src) {
224
+ const importContent = getContentByPath(getAbsolutePath(workPath, path, src)).trim();
225
+ if (importContent) {
226
+ transTagTemplate(
227
+ cheerio.load(importContent, {
228
+ xmlMode: true,
229
+ decodeEntities: false
230
+ }),
231
+ templateModule,
232
+ path,
233
+ components
234
+ );
235
+ }
236
+ }
237
+ });
238
+ importNodes.remove();
239
+ const scriptModule = [];
240
+ let wxsNodes = $("wxs");
241
+ if (wxsNodes.length === 0) {
242
+ wxsNodes = $("dds");
243
+ }
244
+ wxsNodes.each((_, elem) => {
245
+ const smName = $(elem).attr("module");
246
+ if (smName) {
247
+ let wxsContent;
248
+ if (compileResCache.has(smName)) {
249
+ wxsContent = compileResCache.get(smName);
250
+ } else {
251
+ const src = $(elem).attr("src");
252
+ if (src) {
253
+ wxsContent = getContentByPath(getAbsolutePath(workPath, path, src)).trim();
254
+ } else {
255
+ wxsContent = $(elem).html();
256
+ }
257
+ const wxsAst = babel.parseSync(wxsContent);
258
+ traverse(wxsAst, {
259
+ CallExpression(path2) {
260
+ if (path2.node.callee.name === "getRegExp" || path2.node.callee.name === "getDate") {
261
+ const args = [];
262
+ for (let i = 0; i < path2.node.arguments.length; i++) {
263
+ args.push(path2.node.arguments[i]);
264
+ }
265
+ const newExpr = types.newExpression(types.identifier(path2.node.callee.name.substring(3)), args);
266
+ path2.replaceWith(newExpr);
267
+ }
268
+ }
269
+ });
270
+ wxsContent = babel.transformFromAstSync(wxsAst, "", {
271
+ comments: false
272
+ }).code;
273
+ compileResCache.set(smName, wxsContent);
274
+ }
275
+ if (wxsContent) {
276
+ scriptModule.push({
277
+ path: smName,
278
+ code: wxsContent
279
+ });
280
+ }
281
+ }
282
+ });
283
+ wxsNodes.remove();
284
+ transAsses($, $("image"), path);
285
+ const res = [];
286
+ transHtmlTag($.html(), res, components);
287
+ return {
288
+ tpl: res.join(""),
289
+ instruction: {
290
+ templateModule,
291
+ scriptModule
292
+ }
293
+ };
294
+ }
295
+ function transTagTemplate($, templateModule, path, components, componentPlaceholder) {
296
+ const templateNodes = $("template[name]");
297
+ templateNodes.each((_, elem) => {
298
+ const name = $(elem).attr("name");
299
+ const templateContent = $(elem);
300
+ templateContent.find("template").remove();
301
+ templateContent.find("import").remove();
302
+ templateContent.find("include").remove();
303
+ templateContent.find("wxs").remove();
304
+ templateContent.find("dds").remove();
305
+ transAsses($, templateContent.find("image"), path);
306
+ const res = [];
307
+ transHtmlTag(templateContent.html(), res, components);
308
+ templateModule.push({
309
+ path: `tpl-${name}`,
310
+ tpl: res.join("")
311
+ });
312
+ });
313
+ templateNodes.remove();
314
+ }
315
+ function transAsses($, imageNodes, path) {
316
+ imageNodes.each((_, elem) => {
317
+ const imgSrc = $(elem).attr("src").trim();
318
+ if (!imgSrc.startsWith("{{")) {
319
+ $(elem).attr("src", collectAssets(getWorkPath(), path, imgSrc, getTargetPath(), getAppId()));
320
+ }
321
+ });
322
+ }
323
+ function transHtmlTag(html, res, components, componentPlaceholder) {
324
+ const attrsList = [];
325
+ const parser = new htmlparser2.Parser(
326
+ {
327
+ onopentag(tag, attrs) {
328
+ attrsList.push(attrs);
329
+ res.push(transTag({ isStart: true, tag, attrs, components }));
330
+ },
331
+ ontext(text) {
332
+ res.push(text);
333
+ },
334
+ onclosetag(tag) {
335
+ res.push(transTag({ tag, attrs: attrsList.pop(), components }));
336
+ },
337
+ onerror(error) {
338
+ console.error(error);
339
+ }
340
+ },
341
+ { xmlMode: true }
342
+ );
343
+ parser.write(html);
344
+ parser.end();
345
+ }
346
+ function transTag(opts) {
347
+ const { isStart, tag, attrs, components } = opts;
348
+ let res;
349
+ if (tag === "slot") {
350
+ res = tag;
351
+ } else if (components && components[tag]) {
352
+ res = `dd-${tag}`;
353
+ } else if (tag === "component" || tag === "canvas") {
354
+ res = tag;
355
+ } else if (!tagWhiteList.includes(tag)) {
356
+ res = "dd-text";
357
+ } else {
358
+ res = `dd-${tag}`;
359
+ }
360
+ let tagRes;
361
+ const propsAry = getProps(attrs, tag);
362
+ const multipleSlots = attrs?.slot;
363
+ if (attrs?.slot) {
364
+ const withVIf = [];
365
+ const withoutVIf = [];
366
+ for (let i = 0; i < propsAry.length; i++) {
367
+ const prop = propsAry[i];
368
+ if (prop.includes("v-if") || prop.includes("v-else-if") || prop.includes("v-else")) {
369
+ withVIf.push(prop);
370
+ } else {
371
+ withoutVIf.push(prop);
372
+ }
373
+ }
374
+ if (isStart) {
375
+ tagRes = `<template ${`${withVIf.join(" ")}`} #${multipleSlots}><${res}${` ${withoutVIf.join(" ")}`}>`;
376
+ } else {
377
+ tagRes = `</${res}></template>`;
378
+ }
379
+ } else {
380
+ if (isStart) {
381
+ const props = propsAry.join(" ");
382
+ tagRes = props ? `<${res} ${props}>` : `<${res}>`;
383
+ } else {
384
+ tagRes = `</${res}>`;
385
+ }
386
+ }
387
+ return tagRes;
388
+ }
389
+ function getProps(attrs, tag) {
390
+ const attrsList = [];
391
+ Object.entries(attrs).forEach(([name, value]) => {
392
+ if (name.endsWith(":if")) {
393
+ attrsList.push({
394
+ name: "v-if",
395
+ value: parseBraceExp(value)
396
+ });
397
+ } else if (name.endsWith(":elif")) {
398
+ attrsList.push({
399
+ name: "v-else-if",
400
+ value: parseBraceExp(value)
401
+ });
402
+ } else if (name.endsWith(":else")) {
403
+ attrsList.push({
404
+ name: "v-else",
405
+ value: ""
406
+ });
407
+ } else if (name.endsWith(":for") || name.endsWith(":for-items")) {
408
+ attrsList.push({
409
+ name: "v-for",
410
+ value: parseForExp(value, attrs)
411
+ });
412
+ } else if (name.endsWith(":for-item") || name.endsWith(":for-index")) ;
413
+ else if (name.endsWith(":key")) {
414
+ let tranValue;
415
+ if (/\*this/.test(value)) {
416
+ tranValue = JSON.stringify("item");
417
+ } else if (/item/.test(value)) {
418
+ tranValue = getForItemName(attrs);
419
+ } else {
420
+ tranValue = parseKeyExpression(value, getForItemName(attrs));
421
+ }
422
+ attrsList.push({
423
+ name: ":key",
424
+ value: tranValue
425
+ });
426
+ } else if (name === "style") {
427
+ attrsList.push({
428
+ name: "v-c-style",
429
+ value: transformRpx(parseBraceExp(value))
430
+ });
431
+ } else if (name === "class") {
432
+ if (isWrappedByBraces(value)) {
433
+ attrsList.push({
434
+ name: ":class",
435
+ value: parseClassRules(value)
436
+ });
437
+ } else {
438
+ attrsList.push({
439
+ name: "class",
440
+ value
441
+ });
442
+ }
443
+ attrsList.push({
444
+ name: "v-c-class",
445
+ value: ""
446
+ });
447
+ } else if (name === "is" && tag === "component") {
448
+ attrsList.push({
449
+ name: ":is",
450
+ value: `'dd-'+${parseBraceExp(value)}`
451
+ });
452
+ } else if (name === "animation" && (tag !== "movable-view" && tagWhiteList.includes(tag))) {
453
+ attrsList.push({
454
+ name: "v-c-animation",
455
+ value: parseBraceExp(value)
456
+ });
457
+ } else if (name === "value" && (tag === "input" || tag === "textarea") || (name === "x" || name === "y") && tag === "movable-view") {
458
+ attrsList.push({
459
+ name: `v-model:${name}`,
460
+ value: parseBraceExp(value)
461
+ });
462
+ } else if (name.startsWith("data-")) {
463
+ if (isWrappedByBraces(value)) {
464
+ attrsList.push({
465
+ name: "v-c-data",
466
+ value: ""
467
+ });
468
+ attrsList.push({
469
+ name: `:${name}`,
470
+ value: parseBraceExp(value)
471
+ });
472
+ } else {
473
+ attrsList.push({
474
+ name,
475
+ value
476
+ });
477
+ }
478
+ } else if (isWrappedByBraces(value)) {
479
+ let pVal = parseBraceExp(value);
480
+ if (tag === "template" && name === "data") {
481
+ pVal = `{${pVal}}`;
482
+ }
483
+ attrsList.push({
484
+ name: `:${name}`,
485
+ value: pVal
486
+ });
487
+ } else if (name !== "slot") {
488
+ attrsList.push({
489
+ name,
490
+ value
491
+ });
492
+ }
493
+ });
494
+ const propsRes = [];
495
+ attrsList.forEach((attr) => {
496
+ const { name, value } = attr;
497
+ if (value === "") {
498
+ propsRes.push(`${name}`);
499
+ } else if (/\$\{[^}]*\}/.test(value)) {
500
+ propsRes.push(`:${name}="\`${value}\`"`);
501
+ } else {
502
+ propsRes.push(`${name}="${escapeQuotes(value)}"`);
503
+ }
504
+ });
505
+ return propsRes;
506
+ }
507
+ function parseKeyExpression(exp, itemName = "item") {
508
+ exp = exp.trim();
509
+ if (!exp.includes("{{")) {
510
+ return exp.startsWith(itemName) ? `'${exp}'` : `'${itemName}.${exp}'`;
511
+ }
512
+ if (exp.startsWith("{{") && exp.endsWith("}}")) {
513
+ const content = exp.slice(2, -2).trim();
514
+ return content.startsWith(itemName) ? `'${content}'` : `'${itemName}.${content}'`;
515
+ }
516
+ const parts = exp.split(/(\{\{.*?\}\})/);
517
+ const result = parts.map((part) => {
518
+ if (part.startsWith("{{") && part.endsWith("}}")) {
519
+ const content = part.slice(2, -2).trim();
520
+ return content.startsWith(itemName) ? content : `${itemName}.${content}`;
521
+ }
522
+ return `'${part}'`;
523
+ }).join("+");
524
+ return result.endsWith("+''") ? result.slice(0, -3) : result;
525
+ }
526
+ function getViewPath(workPath, src) {
527
+ const aSrc = src.startsWith("/") ? src : `/${src}`;
528
+ for (const mlType of fileType) {
529
+ const mlFullPath = `${workPath}${aSrc}${mlType}`;
530
+ if (fs.existsSync(mlFullPath)) {
531
+ return mlFullPath;
532
+ }
533
+ }
534
+ }
535
+ function escapeQuotes(input) {
536
+ return input.replace(/"/g, "'");
537
+ }
538
+ function isWrappedByBraces(str) {
539
+ return /\{\{.*\}\}/.test(str);
540
+ }
541
+ function splitWithBraces(str) {
542
+ const result = [];
543
+ let temp = "";
544
+ let inBraces = false;
545
+ for (let i = 0; i < str.length; i++) {
546
+ const char = str[i];
547
+ if (char === "{" && i + 1 < str.length && str[i + 1] === "{") {
548
+ inBraces = true;
549
+ temp += "{{";
550
+ i++;
551
+ } else if (char === "}" && i + 1 < str.length && str[i + 1] === "}") {
552
+ inBraces = false;
553
+ temp += "}}";
554
+ i++;
555
+ } else if (!inBraces && char === " ") {
556
+ if (temp) {
557
+ result.push(temp);
558
+ temp = "";
559
+ }
560
+ } else {
561
+ temp += char;
562
+ }
563
+ }
564
+ if (temp) {
565
+ result.push(temp);
566
+ }
567
+ return result;
568
+ }
569
+ function parseClassRules(cssRule) {
570
+ let list = splitWithBraces(cssRule);
571
+ list = list.map((item) => {
572
+ return parseBraceExp(item);
573
+ });
574
+ if (list.length === 1) {
575
+ return list.pop();
576
+ }
577
+ return `[${list.join(",")}]`;
578
+ }
579
+ function getForItemName(attrs) {
580
+ for (const key in attrs) {
581
+ if (key.endsWith(":for-item")) {
582
+ return attrs[key];
583
+ }
584
+ }
585
+ return "item";
586
+ }
587
+ function getForIndexName(attrs) {
588
+ for (const key in attrs) {
589
+ if (key.endsWith(":for-index")) {
590
+ return attrs[key];
591
+ }
592
+ }
593
+ return "index";
594
+ }
595
+ function parseForExp(exp, attrs) {
596
+ const item = getForItemName(attrs);
597
+ const index = getForIndexName(attrs);
598
+ const listVariableName = parseBraceExp(exp);
599
+ return `(${item}, ${index}) in ${listVariableName}`;
600
+ }
601
+ const braceRegex = /(\{\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}\})|([^{}]+)/g;
602
+ const noBraceRegex = /\{\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}\}/;
603
+ const ternaryRegex = /[^?]+\?.+:.+/;
604
+ function parseBraceExp(exp) {
605
+ let result;
606
+ const group = [];
607
+ while (result = braceRegex.exec(exp)) {
608
+ if (result[1]) {
609
+ const matchResult = result[1].match(noBraceRegex);
610
+ if (matchResult) {
611
+ const statement = matchResult[1].trim();
612
+ if (ternaryRegex.test(statement)) {
613
+ group.push(`(${statement})`);
614
+ } else {
615
+ group.push(statement);
616
+ }
617
+ }
618
+ }
619
+ if (result[2]) {
620
+ group.push(`+'${result[2].replace(/'/g, "\\'")}'+`);
621
+ }
622
+ }
623
+ return group.join("").replace(/^\+|\+$/g, "");
624
+ }
625
+ const viewCompiler = {
626
+ splitWithBraces,
627
+ parseClassRules,
628
+ parseBraceExp,
629
+ parseKeyExpression,
630
+ compileML
631
+ };
632
+ export {
633
+ viewCompiler as default
634
+ };