@dimina/compiler 1.0.12-beta.9 → 1.0.13

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,537 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ //#region src/common/art.js
6
+ var artCode = `
7
+ ██████╗ ██╗███╗ ███╗██╗███╗ ██╗ █████╗
8
+ ██╔══██╗██║████╗ ████║██║████╗ ██║██╔══██╗
9
+ ██║ ██║██║██╔████╔██║██║██╔██╗ ██║███████║
10
+ ██║ ██║██║██║╚██╔╝██║██║██║╚██╗██║██╔══██║
11
+ ██████╔╝██║██║ ╚═╝ ██║██║██║ ╚████║██║ ██║
12
+ ╚═════╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
13
+ `;
14
+ function art_default() {
15
+ console.log(artCode);
16
+ }
17
+ //#endregion
18
+ //#region src/common/utils.js
19
+ function hasCompileInfo(modulePath, list, preList) {
20
+ const mergeList = Array.isArray(preList) ? [...preList, ...list] : list;
21
+ for (const element of mergeList) if (element.path === modulePath) return true;
22
+ return false;
23
+ }
24
+ function getAbsolutePath(workPath, pagePath, src) {
25
+ if (src.startsWith("/")) return path.join(workPath, src);
26
+ if (pagePath.includes("/miniprogram_npm/")) {
27
+ const componentFullPath = workPath + pagePath.split("/").slice(0, -1).join("/");
28
+ return path.resolve(componentFullPath, src);
29
+ }
30
+ const relativePath = pagePath.split("/").filter((part) => part !== "").slice(0, -1).join("/");
31
+ return path.resolve(workPath, relativePath, src);
32
+ }
33
+ var assetsMap = {};
34
+ /**
35
+ * 将静态资源存储到 static 文件夹
36
+ */
37
+ function collectAssets(workPath, pagePath, src, targetPath, appId) {
38
+ if (src.startsWith("http") || src.startsWith("//")) return src;
39
+ if (!/\.(?:png|jpe?g|gif|svg)(?:\?.*)?$/.test(src)) return src;
40
+ const relativePath = pagePath.split("/").slice(0, -1).join("/");
41
+ const absolutePath = src.startsWith("/") ? workPath + src : path.resolve(workPath, relativePath, src);
42
+ if (assetsMap[absolutePath]) return assetsMap[absolutePath];
43
+ try {
44
+ const ext = `.${src.split(".").pop()}`;
45
+ const dirPath = absolutePath.split(path.sep).slice(0, -1).join("/");
46
+ const prefix = uuid();
47
+ const targetStatic = `${targetPath}/main/static`;
48
+ if (!fs.existsSync(targetStatic)) fs.mkdirSync(targetStatic, { recursive: true });
49
+ getFilesWithExtension(dirPath, ext).forEach((file) => {
50
+ fs.copyFileSync(path.resolve(dirPath, file), `${targetStatic}/${prefix}_${file}`);
51
+ });
52
+ const filename = src.split("/").pop();
53
+ assetsMap[absolutePath] = `${process.env.ASSETS_PATH_PREFIX ? "" : "/"}${appId}/main/static/${prefix}_${filename}`;
54
+ } catch (error) {
55
+ console.log(error);
56
+ }
57
+ return assetsMap[absolutePath] || src;
58
+ }
59
+ function getFilesWithExtension(directory, extension) {
60
+ return fs.readdirSync(directory).filter((file) => path.extname(file) === extension);
61
+ }
62
+ function isObjectEmpty(objectName) {
63
+ if (!objectName) return true;
64
+ return Object.keys(objectName).length === 0 && objectName.constructor === Object;
65
+ }
66
+ function isString(o) {
67
+ return Object.prototype.toString.call(o) === "[object String]";
68
+ }
69
+ function transformRpx(styleText) {
70
+ if (!isString(styleText)) return styleText;
71
+ return styleText.replace(/([+-]?\d+(?:\.\d+)?)rpx/g, (_, pixel) => {
72
+ return `${Number(pixel)}rem`;
73
+ });
74
+ }
75
+ function uuid() {
76
+ return Math.random().toString(36).slice(2, 7);
77
+ }
78
+ var tagWhiteList = [
79
+ "page",
80
+ "wrapper",
81
+ "block",
82
+ "button",
83
+ "camera",
84
+ "checkbox-group",
85
+ "checkbox",
86
+ "cover-image",
87
+ "cover-view",
88
+ "form",
89
+ "icon",
90
+ "image",
91
+ "input",
92
+ "keyboard-accessory",
93
+ "label",
94
+ "map",
95
+ "movable-area",
96
+ "movable-view",
97
+ "navigation-bar",
98
+ "navigator",
99
+ "open-data",
100
+ "page-meta",
101
+ "picker-view-column",
102
+ "picker-view",
103
+ "picker",
104
+ "progress",
105
+ "radio-group",
106
+ "radio",
107
+ "rich-text",
108
+ "root-portal",
109
+ "scroll-view",
110
+ "slider",
111
+ "swiper-item",
112
+ "swiper",
113
+ "switch",
114
+ "template",
115
+ "text",
116
+ "textarea",
117
+ "video",
118
+ "view",
119
+ "web-view"
120
+ ];
121
+ //#endregion
122
+ //#region src/common/npm-resolver.js
123
+ /**
124
+ * npm 组件解析器
125
+ * 根据微信小程序 npm 支持规范实现组件寻址
126
+ * https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html
127
+ */
128
+ var NpmResolver = class {
129
+ constructor(workPath) {
130
+ this.workPath = workPath;
131
+ this.miniprogramNpmCache = /* @__PURE__ */ new Map();
132
+ this.packageCache = /* @__PURE__ */ new Map();
133
+ }
134
+ /**
135
+ * 解析组件路径,支持 npm 包组件
136
+ * @param {string} componentPath 组件路径
137
+ * @param {string} pageFilePath 页面文件路径
138
+ * @returns {string} 解析后的组件路径
139
+ */
140
+ resolveComponentPath(componentPath, pageFilePath) {
141
+ if (componentPath.startsWith("./") || componentPath.startsWith("../") || componentPath.startsWith("/")) return this.resolveRelativePath(componentPath, pageFilePath);
142
+ const npmPath = this.resolveNpmComponent(componentPath, pageFilePath);
143
+ if (npmPath) return npmPath;
144
+ return this.resolveRelativePath(componentPath, pageFilePath);
145
+ }
146
+ /**
147
+ * 解析相对路径组件
148
+ * @param {string} componentPath 组件路径
149
+ * @param {string} pageFilePath 页面文件路径
150
+ * @returns {string} 解析后的路径
151
+ */
152
+ resolveRelativePath(componentPath, pageFilePath) {
153
+ const lastIndex = pageFilePath.lastIndexOf("/");
154
+ const newPath = pageFilePath.slice(0, lastIndex);
155
+ return path.resolve(newPath, componentPath).replace(this.workPath, "");
156
+ }
157
+ /**
158
+ * 解析 npm 组件
159
+ * @param {string} componentName 组件名称
160
+ * @param {string} pageFilePath 页面文件路径
161
+ * @returns {string|null} 解析后的组件路径,如果找不到返回 null
162
+ */
163
+ resolveNpmComponent(componentName, pageFilePath) {
164
+ const searchPaths = this.generateSearchPaths(pageFilePath);
165
+ for (const searchPath of searchPaths) {
166
+ const componentPath = this.findComponentInMiniprogramNpm(componentName, searchPath);
167
+ if (componentPath) return componentPath;
168
+ }
169
+ return null;
170
+ }
171
+ /**
172
+ * 解析脚本模块路径,支持微信小程序 npm 逐级寻址和 package.json 入口
173
+ * @param {string} specifier 模块导入路径
174
+ * @param {string} modulePath 当前模块绝对文件路径
175
+ * @param {(moduleId: string) => string | null} resolveExistingModuleId 解析真实存在模块的回调
176
+ * @returns {string|null} 解析后的模块 id
177
+ */
178
+ resolveScriptModule(specifier, modulePath, resolveExistingModuleId) {
179
+ if (!specifier || !resolveExistingModuleId) return null;
180
+ for (const searchPath of this.generateSearchPaths(modulePath)) {
181
+ const resolvedModuleId = resolveExistingModuleId(this.normalizeModuleId(`/${searchPath}/${specifier}`));
182
+ if (resolvedModuleId) return resolvedModuleId;
183
+ }
184
+ return null;
185
+ }
186
+ /**
187
+ * 生成 miniprogram_npm 搜索路径
188
+ * 按照微信小程序的寻址顺序生成搜索路径
189
+ * @param {string} pageFilePath 页面文件路径
190
+ * @returns {string[]} 搜索路径数组
191
+ */
192
+ generateSearchPaths(pageFilePath) {
193
+ const pathParts = pageFilePath.replace(this.workPath, "").replace(/^\//, "").split("/").slice(0, -1);
194
+ const searchPaths = [];
195
+ for (let i = pathParts.length; i >= 0; i--) {
196
+ const currentPath = pathParts.slice(0, i).join("/");
197
+ const miniprogramNpmPath = currentPath ? `${currentPath}/miniprogram_npm` : "miniprogram_npm";
198
+ searchPaths.push(miniprogramNpmPath);
199
+ }
200
+ return searchPaths;
201
+ }
202
+ /**
203
+ * 在指定的 miniprogram_npm 目录中查找组件
204
+ * @param {string} componentName 组件名称
205
+ * @param {string} miniprogramNpmPath miniprogram_npm 路径
206
+ * @returns {string|null} 组件路径,如果找不到返回 null
207
+ */
208
+ findComponentInMiniprogramNpm(componentName, miniprogramNpmPath) {
209
+ const fullMiniprogramNpmPath = path.join(this.workPath, miniprogramNpmPath);
210
+ if (!fs.existsSync(fullMiniprogramNpmPath)) return null;
211
+ const cacheKey = `${miniprogramNpmPath}/${componentName}`;
212
+ if (this.miniprogramNpmCache.has(cacheKey)) return this.miniprogramNpmCache.get(cacheKey);
213
+ const candidatePaths = [componentName, `${componentName}/index`];
214
+ for (const candidatePath of candidatePaths) {
215
+ const componentDir = path.join(fullMiniprogramNpmPath, candidatePath);
216
+ if (this.isValidComponent(componentDir)) {
217
+ const resolvedPath = `/${miniprogramNpmPath}/${candidatePath}`.replace(/\/+/g, "/");
218
+ this.miniprogramNpmCache.set(cacheKey, resolvedPath);
219
+ return resolvedPath;
220
+ }
221
+ }
222
+ this.miniprogramNpmCache.set(cacheKey, null);
223
+ return null;
224
+ }
225
+ normalizeModuleId(moduleId) {
226
+ let normalized = moduleId.replace(/\.(js|ts)$/, "").replace(/\\/g, "/");
227
+ if (!normalized.startsWith("/")) normalized = `/${normalized}`;
228
+ return normalized;
229
+ }
230
+ /**
231
+ * 检查是否为有效的组件
232
+ * @param {string} componentPath 组件路径
233
+ * @returns {boolean} 是否为有效组件
234
+ */
235
+ isValidComponent(componentPath) {
236
+ const requiredFiles = [".json", ".js"];
237
+ if (!requiredFiles.some((ext) => {
238
+ return fs.existsSync(`${componentPath}${ext}`);
239
+ })) {
240
+ if (!requiredFiles.some((ext) => {
241
+ return fs.existsSync(path.join(componentPath, `index${ext}`));
242
+ })) return false;
243
+ const indexJsonFile = path.join(componentPath, "index.json");
244
+ if (fs.existsSync(indexJsonFile)) try {
245
+ return JSON.parse(fs.readFileSync(indexJsonFile, "utf-8")).component === true;
246
+ } catch (e) {
247
+ return false;
248
+ }
249
+ return true;
250
+ }
251
+ const jsonFile = `${componentPath}.json`;
252
+ if (fs.existsSync(jsonFile)) try {
253
+ return JSON.parse(fs.readFileSync(jsonFile, "utf-8")).component === true;
254
+ } catch (e) {
255
+ return false;
256
+ }
257
+ return true;
258
+ }
259
+ /**
260
+ * 获取 npm 包信息
261
+ * @param {string} packageName 包名
262
+ * @param {string} searchPath 搜索路径
263
+ * @returns {object|null} 包信息,如果找不到返回 null
264
+ */
265
+ getPackageInfo(packageName, searchPath) {
266
+ const cacheKey = `${searchPath}/${packageName}`;
267
+ if (this.packageCache.has(cacheKey)) return this.packageCache.get(cacheKey);
268
+ const packageJsonPath = path.join(this.workPath, searchPath, packageName, "package.json");
269
+ if (!fs.existsSync(packageJsonPath)) {
270
+ this.packageCache.set(cacheKey, null);
271
+ return null;
272
+ }
273
+ try {
274
+ const packageInfo = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
275
+ this.packageCache.set(cacheKey, packageInfo);
276
+ return packageInfo;
277
+ } catch (e) {
278
+ this.packageCache.set(cacheKey, null);
279
+ return null;
280
+ }
281
+ }
282
+ /**
283
+ * 清除缓存
284
+ */
285
+ clearCache() {
286
+ this.miniprogramNpmCache.clear();
287
+ this.packageCache.clear();
288
+ }
289
+ };
290
+ //#endregion
291
+ //#region src/env.js
292
+ var pathInfo = {};
293
+ var configInfo = {};
294
+ var npmResolver = null;
295
+ /**
296
+ * 持久化编译过程的上下文
297
+ */
298
+ function storeInfo(workPath) {
299
+ storePathInfo(workPath);
300
+ storeProjectConfig();
301
+ storeAppConfig();
302
+ storePageConfig();
303
+ return {
304
+ pathInfo,
305
+ configInfo
306
+ };
307
+ }
308
+ function resetStoreInfo(opts) {
309
+ pathInfo = opts.pathInfo;
310
+ configInfo = opts.configInfo;
311
+ if (pathInfo.workPath) npmResolver = new NpmResolver(pathInfo.workPath);
312
+ }
313
+ function storePathInfo(workPath) {
314
+ pathInfo.workPath = workPath;
315
+ if (process.env.TARGET_PATH) pathInfo.targetPath = process.env.TARGET_PATH;
316
+ else {
317
+ const tempDir = process.env.GITHUB_WORKSPACE || os.tmpdir();
318
+ const targetDir = path.join(tempDir, `dimina-fe-dist-${Date.now()}`);
319
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
320
+ pathInfo.targetPath = targetDir;
321
+ }
322
+ npmResolver = new NpmResolver(workPath);
323
+ }
324
+ function storeProjectConfig() {
325
+ const privateConfigPath = `${pathInfo.workPath}/project.private.config.json`;
326
+ const defaultConfigPath = `${pathInfo.workPath}/project.config.json`;
327
+ let privateConfig = {};
328
+ let defaultConfig = {};
329
+ if (fs.existsSync(defaultConfigPath)) try {
330
+ defaultConfig = parseContentByPath(defaultConfigPath);
331
+ } catch (e) {
332
+ console.warn("Failed to parse project.config.json:", e.message);
333
+ }
334
+ if (fs.existsSync(privateConfigPath)) try {
335
+ privateConfig = parseContentByPath(privateConfigPath);
336
+ } catch (e) {
337
+ console.warn("Failed to parse project.private.config.json:", e.message);
338
+ }
339
+ configInfo.projectInfo = {
340
+ ...defaultConfig,
341
+ ...privateConfig
342
+ };
343
+ }
344
+ function storeAppConfig() {
345
+ const content = parseContentByPath(`${pathInfo.workPath}/app.json`);
346
+ const newObj = {};
347
+ for (const key in content) if (Object.hasOwn(content, key)) if (key === "subpackages") newObj.subPackages = content[key];
348
+ else newObj[key] = content[key];
349
+ configInfo.appInfo = newObj;
350
+ }
351
+ function getContentByPath(path) {
352
+ return fs.readFileSync(path, { encoding: "utf-8" });
353
+ }
354
+ function parseContentByPath(path) {
355
+ return JSON.parse(getContentByPath(path));
356
+ }
357
+ /**
358
+ * 收集页面 json 信息
359
+ */
360
+ function storePageConfig() {
361
+ const { pages, subPackages } = configInfo.appInfo;
362
+ configInfo.pageInfo = {};
363
+ configInfo.componentInfo = {};
364
+ if (configInfo.appInfo.usingComponents) {
365
+ const appFilePath = `${pathInfo.workPath}/app.json`;
366
+ storeComponentConfig(configInfo.appInfo, appFilePath);
367
+ }
368
+ collectionPageJson(pages);
369
+ if (subPackages) subPackages.forEach((subPkg) => {
370
+ collectionPageJson(subPkg.pages, subPkg.root);
371
+ });
372
+ }
373
+ /**
374
+ * 匹配页面和对应的配置信息
375
+ * @param {*} pages
376
+ */
377
+ function collectionPageJson(pages, root) {
378
+ pages.forEach((pagePath) => {
379
+ let np = pagePath;
380
+ if (root) {
381
+ if (!root.endsWith("/")) root += "/";
382
+ np = root + np;
383
+ }
384
+ const pageFilePath = `${pathInfo.workPath}/${np}.json`;
385
+ if (fs.existsSync(pageFilePath)) {
386
+ const pageJsonContent = parseContentByPath(pageFilePath);
387
+ if (root) pageJsonContent.root = transSubDir(root);
388
+ configInfo.pageInfo[np] = pageJsonContent;
389
+ storeComponentConfig(pageJsonContent, pageFilePath);
390
+ }
391
+ });
392
+ }
393
+ /**
394
+ * 按页面收集组件 json 信息
395
+ * @param {*} pageJsonContent
396
+ * @param {*} pageFilePath
397
+ */
398
+ function storeComponentConfig(pageJsonContent, pageFilePath) {
399
+ if (isObjectEmpty(pageJsonContent.usingComponents)) return;
400
+ for (const [componentName, componentPath] of Object.entries(pageJsonContent.usingComponents)) {
401
+ const moduleId = getModuleId(componentPath, pageFilePath);
402
+ pageJsonContent.usingComponents[componentName] = moduleId;
403
+ if (configInfo.componentInfo[moduleId]) continue;
404
+ let componentFilePath = path.resolve(getWorkPath(), `./${moduleId}.json`);
405
+ let cContent = null;
406
+ if (fs.existsSync(componentFilePath)) cContent = parseContentByPath(componentFilePath);
407
+ else {
408
+ const indexJsonPath = path.resolve(getWorkPath(), `./${moduleId}/index.json`);
409
+ if (fs.existsSync(indexJsonPath)) {
410
+ componentFilePath = indexJsonPath;
411
+ cContent = parseContentByPath(componentFilePath);
412
+ } else if (moduleId.includes("/miniprogram_npm/")) {
413
+ console.log(`[env] 为 npm 组件创建默认配置: ${moduleId}`);
414
+ cContent = {
415
+ component: true,
416
+ usingComponents: {}
417
+ };
418
+ } else {
419
+ console.warn(`[env] 组件配置文件不存在: ${componentFilePath}`);
420
+ continue;
421
+ }
422
+ }
423
+ const cUsing = cContent.usingComponents || {};
424
+ const isComponent = cContent.component || false;
425
+ const cComponents = Object.keys(cUsing).reduce((acc, key) => {
426
+ acc[key] = getModuleId(cUsing[key], componentFilePath);
427
+ return acc;
428
+ }, {});
429
+ configInfo.componentInfo[moduleId] = {
430
+ id: uuid(),
431
+ path: moduleId,
432
+ component: isComponent,
433
+ usingComponents: cComponents
434
+ };
435
+ if (cContent.usingComponents && Object.keys(cContent.usingComponents).length > 0) storeComponentConfig(configInfo.componentInfo[moduleId], componentFilePath);
436
+ }
437
+ }
438
+ /**
439
+ * 转化为相对小程序根目录的绝对路径,作为模块唯一性 id
440
+ * 支持 npm 组件解析
441
+ * @param {string} src
442
+ */
443
+ function getModuleId(src, pageFilePath) {
444
+ const resolvedAlias = resolveAppAlias(src);
445
+ if (resolvedAlias) return resolvedAlias;
446
+ if (!npmResolver) {
447
+ const lastIndex = pageFilePath.lastIndexOf("/");
448
+ const newPath = pageFilePath.slice(0, lastIndex);
449
+ const workPath = getWorkPath();
450
+ return path.resolve(newPath, src).replace(workPath, "");
451
+ }
452
+ return npmResolver.resolveComponentPath(src, pageFilePath);
453
+ }
454
+ function resolveAppAlias(src) {
455
+ const resolveAlias = configInfo.appInfo?.resolveAlias;
456
+ if (!resolveAlias || typeof src !== "string") return null;
457
+ for (const [alias, target] of Object.entries(resolveAlias)) if (alias.endsWith("/*") && target.endsWith("/*")) {
458
+ const aliasPrefix = alias.slice(0, -1);
459
+ const targetPrefix = target.slice(0, -1);
460
+ if (src.startsWith(aliasPrefix)) return src.replace(aliasPrefix, targetPrefix);
461
+ } else if (src === alias) return target;
462
+ return null;
463
+ }
464
+ function getTargetPath() {
465
+ return pathInfo.targetPath;
466
+ }
467
+ function getComponent(src) {
468
+ return configInfo.componentInfo[src];
469
+ }
470
+ function getPageConfigInfo() {
471
+ return configInfo.pageInfo;
472
+ }
473
+ function getAppConfigInfo() {
474
+ return configInfo.appInfo;
475
+ }
476
+ function getWorkPath() {
477
+ return pathInfo.workPath;
478
+ }
479
+ function getNpmResolver() {
480
+ return npmResolver;
481
+ }
482
+ function getAppId() {
483
+ return configInfo.projectInfo.appid;
484
+ }
485
+ function getAppName() {
486
+ if (configInfo.projectInfo.projectname) return decodeURIComponent(configInfo.projectInfo.projectname);
487
+ return getAppId();
488
+ }
489
+ function transSubDir(name) {
490
+ return `sub_${name.replace(/\/$/, "")}`;
491
+ }
492
+ /**
493
+ * 获取页面及其配置信息,并生成id(输出的 json 文件没有 id)
494
+ */
495
+ function getPages() {
496
+ const { pages, subPackages = [], usingComponents: globalComponents = {} } = getAppConfigInfo();
497
+ const pageInfo = getPageConfigInfo();
498
+ const mainPages = pages.map((path) => {
499
+ const pageComponents = pageInfo[path]?.usingComponents || {};
500
+ const mergedComponents = {
501
+ ...globalComponents,
502
+ ...pageComponents
503
+ };
504
+ return {
505
+ id: uuid(),
506
+ path,
507
+ usingComponents: mergedComponents
508
+ };
509
+ });
510
+ const subPages = {};
511
+ subPackages.forEach((subPkg) => {
512
+ const rootPath = subPkg.root.endsWith("/") ? subPkg.root : `${subPkg.root}/`;
513
+ const independent = subPkg.independent ? subPkg.independent : false;
514
+ subPages[transSubDir(rootPath)] = {
515
+ independent,
516
+ info: subPkg.pages.map((path) => {
517
+ const fullPath = rootPath + path;
518
+ const pageComponents = pageInfo[fullPath]?.usingComponents || {};
519
+ const mergedComponents = {
520
+ ...globalComponents,
521
+ ...pageComponents
522
+ };
523
+ return {
524
+ id: uuid(),
525
+ path: fullPath,
526
+ usingComponents: mergedComponents
527
+ };
528
+ })
529
+ };
530
+ });
531
+ return {
532
+ mainPages,
533
+ subPages
534
+ };
535
+ }
536
+ //#endregion
537
+ export { tagWhiteList as _, getContentByPath as a, getPages as c, resetStoreInfo as d, resolveAppAlias as f, hasCompileInfo as g, getAbsolutePath as h, getComponent as i, getTargetPath as l, collectAssets as m, getAppId as n, getNpmResolver as o, storeInfo as p, getAppName as r, getPageConfigInfo as s, getAppConfigInfo as t, getWorkPath as u, transformRpx as v, art_default as y };