@aiot-toolkit/aiotpack 2.0.6-beta.9 → 2.1.0-prender.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.
- package/lib/afterCompile/ux/UxAfterCompile.d.ts +4 -0
- package/lib/afterCompile/ux/UxAfterCompile.js +90 -2
- package/lib/compiler/javascript/JavascriptCompiler.js +11 -4
- package/lib/compiler/javascript/TemplateCompiler.d.ts +29 -0
- package/lib/compiler/javascript/TemplateCompiler.js +564 -0
- package/lib/compiler/javascript/ViteCompiler.d.ts +13 -0
- package/lib/compiler/javascript/ViteCompiler.js +414 -0
- package/lib/compiler/javascript/interface/IJavascriptCompileOption.d.ts +26 -0
- package/lib/compiler/javascript/vela/VelaWebpackConfigurator.d.ts +3 -1
- package/lib/compiler/javascript/vela/VelaWebpackConfigurator.js +16 -1
- package/lib/compiler/javascript/vela/interface/IManifest.d.ts +12 -0
- package/lib/compiler/javascript/vela/plugin/WrapPlugin.d.ts +10 -1
- package/lib/compiler/javascript/vela/plugin/WrapPlugin.js +241 -57
- package/lib/compiler/javascript/vela/utils/UxCompileUtil.d.ts +3 -2
- package/lib/compiler/javascript/vela/utils/UxCompileUtil.js +12 -4
- package/lib/compiler/javascript/vela/utils/VruUtil.d.ts +50 -0
- package/lib/compiler/javascript/vela/utils/VruUtil.js +128 -0
- package/lib/compiler/javascript/vela/utils/ZipUtil.d.ts +9 -0
- package/lib/compiler/javascript/vela/utils/ZipUtil.js +112 -6
- package/lib/compiler/javascript/vela/utils/webpackLoader/WebpackJsLoader.js +1 -1
- package/lib/config/UxConfig.d.ts +12 -5
- package/lib/config/UxConfig.js +7 -6
- package/lib/loader/ux/JsLoader.d.ts +7 -0
- package/lib/loader/ux/JsLoader.js +38 -8
- package/lib/loader/ux/vela/HmlLoader.d.ts +6 -6
- package/lib/loader/ux/vela/HmlLoader.js +30 -13
- package/lib/prerender/PrerenderVM.d.ts +86 -0
- package/lib/prerender/PrerenderVM.js +677 -0
- package/lib/prerender/StyleSerializer.d.ts +18 -0
- package/lib/prerender/StyleSerializer.js +92 -0
- package/lib/prerender/TemplateSerializer.d.ts +26 -0
- package/lib/prerender/TemplateSerializer.js +122 -0
- package/lib/prerender/index.d.ts +20 -0
- package/lib/prerender/index.js +519 -0
- package/lib/prerender/interface/IPrerenderOption.d.ts +15 -0
- package/lib/prerender/interface/IPrerenderOption.js +1 -0
- package/lib/utils/BeforeCompileUtils.d.ts +1 -1
- package/lib/utils/BeforeCompileUtils.js +52 -9
- package/lib/utils/ux/ManifestSchema.js +0 -1
- package/lib/utils/ux/UxFileUtils.js +1 -1
- package/lib/utils/ux/UxLoaderUtils.js +8 -3
- package/package.json +9 -6
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "PrerenderVM", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _PrerenderVM.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "TemplateSerializer", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _TemplateSerializer.default;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
exports.prerender = prerender;
|
|
19
|
+
exports.watchChange = watchChange;
|
|
20
|
+
var _fsExtra = _interopRequireDefault(require("fs-extra"));
|
|
21
|
+
var _path = _interopRequireDefault(require("path"));
|
|
22
|
+
var _sharedUtils = require("@aiot-toolkit/shared-utils");
|
|
23
|
+
var _PrerenderVM = _interopRequireDefault(require("./PrerenderVM"));
|
|
24
|
+
var _TemplateSerializer = _interopRequireDefault(require("./TemplateSerializer"));
|
|
25
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
26
|
+
/** Cache: deviceType -> entryKey -> true (avoids re-prerendering same page per device) */
|
|
27
|
+
let prerenderEntryKeyCache = {};
|
|
28
|
+
/** Cache: resourcePath$$name -> true (avoids re-prerendering same component) */
|
|
29
|
+
let preRenderCache = {};
|
|
30
|
+
/** Cache: page path -> true (tracks pages whose css.json has been written) */
|
|
31
|
+
let cssCache = {};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Skip rules: app and services should not be prerendered
|
|
35
|
+
*/
|
|
36
|
+
function shouldSkip(name) {
|
|
37
|
+
return name === 'app' || name === 'services';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Recursively find and prerender sub-components (import nodes).
|
|
42
|
+
* Tracks ancestors to detect circular dependencies.
|
|
43
|
+
*/
|
|
44
|
+
function processSubComponents(node, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, ancestors) {
|
|
45
|
+
if (!prerenderSubComp) return;
|
|
46
|
+
if (node.import) {
|
|
47
|
+
const compPath = node.import;
|
|
48
|
+
const cacheKey = `${buildPath}$$${compPath}`;
|
|
49
|
+
if (!preRenderCache[cacheKey]) {
|
|
50
|
+
prerenderPage(compPath, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, ancestors);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (node.children) {
|
|
54
|
+
for (const child of node.children) {
|
|
55
|
+
processSubComponents(child, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, ancestors);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Prerender a single page/component entry.
|
|
62
|
+
* @param ancestors - Set of ancestor component paths for circular dependency detection
|
|
63
|
+
*/
|
|
64
|
+
function prerenderPage(page, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, ancestors) {
|
|
65
|
+
// Skip app and services
|
|
66
|
+
const name = page.split('/')[0];
|
|
67
|
+
if (shouldSkip(name)) return;
|
|
68
|
+
|
|
69
|
+
// Circular dependency detection
|
|
70
|
+
if (ancestors?.has(page)) {
|
|
71
|
+
onLog?.([{
|
|
72
|
+
level: _sharedUtils.Loglevel.WARN,
|
|
73
|
+
message: [`prerender: circular dependency detected for ${page}, skip`]
|
|
74
|
+
}]);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check component-level cache
|
|
79
|
+
const cacheKey = `${buildPath}$$${page}`;
|
|
80
|
+
if (preRenderCache[cacheKey]) return;
|
|
81
|
+
preRenderCache[cacheKey] = true;
|
|
82
|
+
|
|
83
|
+
// Check device-level entry cache
|
|
84
|
+
if (!prerenderEntryKeyCache[deviceType]) {
|
|
85
|
+
prerenderEntryKeyCache[deviceType] = {};
|
|
86
|
+
}
|
|
87
|
+
if (prerenderEntryKeyCache[deviceType][page]) return;
|
|
88
|
+
prerenderEntryKeyCache[deviceType][page] = true;
|
|
89
|
+
|
|
90
|
+
// Load sub-component JS
|
|
91
|
+
const jsPath = _path.default.join(buildPath, `${page}.js`);
|
|
92
|
+
if (!_fsExtra.default.existsSync(jsPath)) {
|
|
93
|
+
onLog?.([{
|
|
94
|
+
level: _sharedUtils.Loglevel.WARN,
|
|
95
|
+
message: [`prerender: ${jsPath} not found, skip`]
|
|
96
|
+
}]);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const jsCode = _fsExtra.default.readFileSync(jsPath, 'utf-8');
|
|
100
|
+
const {
|
|
101
|
+
tree,
|
|
102
|
+
styles
|
|
103
|
+
} = vm.execute(jsCode, onLog);
|
|
104
|
+
if (!tree) {
|
|
105
|
+
onLog?.([{
|
|
106
|
+
level: _sharedUtils.Loglevel.WARN,
|
|
107
|
+
message: [`prerender: failed to render ${page}, skip`]
|
|
108
|
+
}]);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Build css.json
|
|
113
|
+
const styleIds = Object.keys(styles).map(Number);
|
|
114
|
+
const mainStyleId = tree.styleObjectId ?? styleIds[styleIds.length - 1];
|
|
115
|
+
let cssJson = serializer.serializeCss(styles);
|
|
116
|
+
|
|
117
|
+
// Fix css.json keys: replace default "0" with actual styleObjectId from JS
|
|
118
|
+
const styleIdMatches = [...jsCode.matchAll(/let \$style\$(\d+)/g)];
|
|
119
|
+
if (styleIdMatches.length > 0 && cssJson['0']) {
|
|
120
|
+
const fixedCss = {};
|
|
121
|
+
const cssKeys = Object.keys(cssJson);
|
|
122
|
+
for (let i = 0; i < cssKeys.length; i++) {
|
|
123
|
+
const newId = styleIdMatches[i] ? styleIdMatches[i][1] : cssKeys[i];
|
|
124
|
+
fixedCss[newId] = cssJson[cssKeys[i]];
|
|
125
|
+
}
|
|
126
|
+
cssJson = fixedCss;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Merge all styleSheets for class -> style resolution
|
|
130
|
+
// Use css.json (generated by WrapPlugin) which has the actual style data
|
|
131
|
+
const mergedSheet = {};
|
|
132
|
+
const pageCssPath = _path.default.join(_path.default.dirname(jsPath), `${_path.default.basename(page)}.css.json`);
|
|
133
|
+
if (_fsExtra.default.existsSync(pageCssPath)) {
|
|
134
|
+
const cssData = _fsExtra.default.readJSONSync(pageCssPath);
|
|
135
|
+
for (const sheet of Object.values(cssData)) {
|
|
136
|
+
if (sheet && typeof sheet === 'object') {
|
|
137
|
+
Object.assign(mergedSheet, sheet);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
for (const sheet of Object.values(styles)) {
|
|
142
|
+
Object.assign(mergedSheet, sheet);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Serialize template.json
|
|
147
|
+
const templateJson = serializer.serializeTemplate(tree, mergedSheet, mainStyleId);
|
|
148
|
+
|
|
149
|
+
// Add import paths to custom component nodes
|
|
150
|
+
const importsPath = jsPath.replace(/\.js$/, '.imports.json');
|
|
151
|
+
let importMap = {};
|
|
152
|
+
if (_fsExtra.default.existsSync(importsPath)) {
|
|
153
|
+
importMap = _fsExtra.default.readJSONSync(importsPath);
|
|
154
|
+
addImportPaths(templateJson, importMap);
|
|
155
|
+
_fsExtra.default.removeSync(importsPath); // Clean up the temp file
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Write files
|
|
159
|
+
const dir = _path.default.dirname(jsPath);
|
|
160
|
+
const baseName = _path.default.basename(page);
|
|
161
|
+
const templatePath = _path.default.join(dir, `${baseName}.template.json`);
|
|
162
|
+
const cssPath = _path.default.join(dir, `${baseName}.css.json`);
|
|
163
|
+
_fsExtra.default.ensureDirSync(dir);
|
|
164
|
+
_fsExtra.default.writeJSONSync(templatePath, templateJson, {
|
|
165
|
+
spaces: 2
|
|
166
|
+
});
|
|
167
|
+
// Only write css.json if it doesn't already have content (WrapPlugin may have generated it)
|
|
168
|
+
if (!_fsExtra.default.existsSync(cssPath) || _fsExtra.default.readFileSync(cssPath, 'utf-8').trim() === '{}') {
|
|
169
|
+
_fsExtra.default.writeJSONSync(cssPath, cssJson, {
|
|
170
|
+
spaces: 2
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
cssCache[page] = true;
|
|
174
|
+
|
|
175
|
+
// Generate template.json for sub-components from VM's component registry
|
|
176
|
+
for (const [compName, compImportPath] of Object.entries(importMap)) {
|
|
177
|
+
const compTemplatePath = _path.default.join(buildPath, `${compImportPath}.template.json`);
|
|
178
|
+
if (_fsExtra.default.existsSync(compTemplatePath)) continue;
|
|
179
|
+
|
|
180
|
+
// Get sub-component templates from the VM (they were registered during $app_define$ execution)
|
|
181
|
+
const subTemplates = vm.getSubComponentTemplates();
|
|
182
|
+
const compDefineId = `@app-component/${compName}`;
|
|
183
|
+
const subData = subTemplates.get(compDefineId);
|
|
184
|
+
if (subData?.tree) {
|
|
185
|
+
// Get the correct styleObjectId from the css.json (matches the JS $style$<id>)
|
|
186
|
+
const cssJsonContent = _fsExtra.default.existsSync(cssPath) ? _fsExtra.default.readJSONSync(cssPath) : cssJson;
|
|
187
|
+
const cssKeys = Object.keys(cssJsonContent).map(Number);
|
|
188
|
+
// The sub-component's styleObjectId is the one that's NOT the main page's
|
|
189
|
+
const subStyleId = cssKeys.find(k => k !== mainStyleId) || cssKeys[0] || 0;
|
|
190
|
+
const subMergedSheet = {};
|
|
191
|
+
if (cssJsonContent[String(subStyleId)]) {
|
|
192
|
+
Object.assign(subMergedSheet, cssJsonContent[String(subStyleId)]);
|
|
193
|
+
}
|
|
194
|
+
const subTemplateJson = serializer.serializeTemplate(subData.tree, subMergedSheet, subStyleId);
|
|
195
|
+
_fsExtra.default.ensureDirSync(_path.default.dirname(compTemplatePath));
|
|
196
|
+
_fsExtra.default.writeJSONSync(compTemplatePath, subTemplateJson, {
|
|
197
|
+
spaces: 2
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
onLog?.([{
|
|
202
|
+
level: _sharedUtils.Loglevel.SUCCESS,
|
|
203
|
+
message: [`prerender: ${page} -> template.json + css.json`]
|
|
204
|
+
}]);
|
|
205
|
+
|
|
206
|
+
// Recursively prerender sub-components
|
|
207
|
+
const nextAncestors = new Set(ancestors);
|
|
208
|
+
nextAncestors.add(page);
|
|
209
|
+
processSubComponents(tree, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, nextAncestors);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 预渲染 app.ux 的全局样式,生成 app.css.json
|
|
214
|
+
* app.ux 只有样式没有模板,因此只需提取 styles 并写入 css.json
|
|
215
|
+
*/
|
|
216
|
+
function prerenderAppCss(buildPath, vm, serializer, onLog) {
|
|
217
|
+
const appJsPath = _path.default.join(buildPath, 'app.js');
|
|
218
|
+
if (!_fsExtra.default.existsSync(appJsPath)) return;
|
|
219
|
+
const jsCode = _fsExtra.default.readFileSync(appJsPath, 'utf-8');
|
|
220
|
+
const {
|
|
221
|
+
styles
|
|
222
|
+
} = vm.execute(jsCode, onLog);
|
|
223
|
+
const styleIds = Object.keys(styles);
|
|
224
|
+
if (!styleIds.length) return;
|
|
225
|
+
let cssJson = serializer.serializeCss(styles);
|
|
226
|
+
|
|
227
|
+
// Fix css.json keys: replace default "0" with actual styleObjectId from JS
|
|
228
|
+
const styleIdMatch = jsCode.match(/let \$style\$(\d+)/);
|
|
229
|
+
if (styleIdMatch && cssJson['0']) {
|
|
230
|
+
cssJson = {
|
|
231
|
+
[styleIdMatch[1]]: cssJson['0']
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const cssPath = _path.default.join(buildPath, 'app.css.json');
|
|
235
|
+
// Only write if not already generated by WrapPlugin
|
|
236
|
+
if (!_fsExtra.default.existsSync(cssPath) || _fsExtra.default.readFileSync(cssPath, 'utf-8').trim() === '{}') {
|
|
237
|
+
_fsExtra.default.writeJSONSync(cssPath, cssJson, {
|
|
238
|
+
spaces: 2
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Strip style array from app.js (replace with @info reference)
|
|
243
|
+
stripStyleFromFile(appJsPath);
|
|
244
|
+
onLog?.([{
|
|
245
|
+
level: _sharedUtils.Loglevel.SUCCESS,
|
|
246
|
+
message: ['prerender: app -> app.css.json']
|
|
247
|
+
}]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 预渲染入口
|
|
252
|
+
*
|
|
253
|
+
* 读取编译后的 JS 产物,在 VM 沙箱中执行模板函数,
|
|
254
|
+
* 将静态 DOM 结构序列化为 .template.json + .css.json
|
|
255
|
+
* 支持子组件递归预渲染和多级缓存
|
|
256
|
+
*/
|
|
257
|
+
async function prerender(option, onLog) {
|
|
258
|
+
const {
|
|
259
|
+
buildPath,
|
|
260
|
+
sourcePath,
|
|
261
|
+
pages,
|
|
262
|
+
prerenderSubComp = true,
|
|
263
|
+
deviceTypes
|
|
264
|
+
} = option;
|
|
265
|
+
const vm = new _PrerenderVM.default();
|
|
266
|
+
const serializer = new _TemplateSerializer.default();
|
|
267
|
+
const types = deviceTypes?.length ? deviceTypes : ['default'];
|
|
268
|
+
for (const deviceType of types) {
|
|
269
|
+
for (const page of pages) {
|
|
270
|
+
prerenderPage(page, buildPath, sourcePath, deviceType, prerenderSubComp, vm, serializer, onLog, new Set());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Generate app.css.json for global styles defined in app.ux
|
|
275
|
+
prerenderAppCss(buildPath, vm, serializer, onLog);
|
|
276
|
+
|
|
277
|
+
// Strip $app_template$ from JS files (template is now in template.json)
|
|
278
|
+
stripTemplatesFromJs(buildPath, pages, onLog);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Remove $app_template$ variable and module.exports.template assignment from JS files.
|
|
283
|
+
* After prerender generates template.json, the template function is no longer needed in JS.
|
|
284
|
+
*/
|
|
285
|
+
function stripTemplatesFromJs(buildPath, pages, onLog) {
|
|
286
|
+
for (const page of pages) {
|
|
287
|
+
const jsPath = _path.default.join(buildPath, `${page}.js`);
|
|
288
|
+
if (!_fsExtra.default.existsSync(jsPath)) continue;
|
|
289
|
+
|
|
290
|
+
// Only strip if template.json was generated for this page
|
|
291
|
+
const templateJsonPath = _path.default.join(_path.default.dirname(jsPath), `${_path.default.basename(page)}.template.json`);
|
|
292
|
+
if (!_fsExtra.default.existsSync(templateJsonPath)) continue;
|
|
293
|
+
let code = _fsExtra.default.readFileSync(jsPath, 'utf-8');
|
|
294
|
+
let modified = false;
|
|
295
|
+
|
|
296
|
+
// Remove all occurrences of: var $app_template$ = function(vm) { ... };
|
|
297
|
+
while (true) {
|
|
298
|
+
const templateStart = code.indexOf('var $app_template$');
|
|
299
|
+
if (templateStart === -1) break;
|
|
300
|
+
let end = templateStart;
|
|
301
|
+
let depth = 0;
|
|
302
|
+
let started = false;
|
|
303
|
+
for (let i = templateStart; i < code.length; i++) {
|
|
304
|
+
if (code[i] === '{') {
|
|
305
|
+
depth++;
|
|
306
|
+
started = true;
|
|
307
|
+
} else if (code[i] === '}') {
|
|
308
|
+
depth--;
|
|
309
|
+
}
|
|
310
|
+
if (started && depth === 0) {
|
|
311
|
+
end = code.indexOf(';', i);
|
|
312
|
+
if (end === -1) end = i;else end = end + 1;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
code = code.slice(0, templateStart) + code.slice(end);
|
|
317
|
+
modified = true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Remove: module.exports.template = $app_template$; or $app_module$.exports.template = $app_template$;
|
|
321
|
+
const newCode = code.replace(/\s*(?:module|\$app_module\$)\.exports\.template\s*=\s*\$app_template\$;?\s*/g, '\n');
|
|
322
|
+
if (newCode !== code) {
|
|
323
|
+
code = newCode;
|
|
324
|
+
modified = true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Replace $app_style$ or $app_style_<id>$ arrays with @info reference
|
|
328
|
+
while (true) {
|
|
329
|
+
const styleMatch = code.match(/var \$app_style_?(\d*)\$/);
|
|
330
|
+
if (!styleMatch) break;
|
|
331
|
+
const styleStart = code.indexOf(styleMatch[0]);
|
|
332
|
+
const eqIdx = code.indexOf('=', styleStart);
|
|
333
|
+
if (eqIdx === -1) break;
|
|
334
|
+
|
|
335
|
+
// Find the opening [ or {
|
|
336
|
+
let openIdx = -1;
|
|
337
|
+
let openChar = '';
|
|
338
|
+
let closeChar = '';
|
|
339
|
+
for (let i = eqIdx + 1; i < code.length; i++) {
|
|
340
|
+
if (code[i] === '[') {
|
|
341
|
+
openIdx = i;
|
|
342
|
+
openChar = '[';
|
|
343
|
+
closeChar = ']';
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
if (code[i] === '{') {
|
|
347
|
+
openIdx = i;
|
|
348
|
+
openChar = '{';
|
|
349
|
+
closeChar = '}';
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (openIdx === -1) break;
|
|
354
|
+
|
|
355
|
+
// Find matching close
|
|
356
|
+
let depth = 0;
|
|
357
|
+
let closeIdx = -1;
|
|
358
|
+
let inStr = null;
|
|
359
|
+
let esc = false;
|
|
360
|
+
for (let i = openIdx; i < code.length; i++) {
|
|
361
|
+
const ch = code[i];
|
|
362
|
+
if (esc) {
|
|
363
|
+
esc = false;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (ch === '\\') {
|
|
367
|
+
esc = true;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (inStr) {
|
|
371
|
+
if (ch === inStr) inStr = null;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
375
|
+
inStr = ch;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (ch === openChar) depth++;else if (ch === closeChar) {
|
|
379
|
+
depth--;
|
|
380
|
+
if (depth === 0) {
|
|
381
|
+
closeIdx = i;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (closeIdx === -1) break;
|
|
387
|
+
|
|
388
|
+
// Find the styleObjectId from the variable name ($app_style_<id>$)
|
|
389
|
+
const varId = styleMatch[1];
|
|
390
|
+
let styleId = varId;
|
|
391
|
+
if (!styleId) {
|
|
392
|
+
const idM = code.match(/let \$style\$(\d+)/);
|
|
393
|
+
styleId = idM ? idM[1] : '0';
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Find semicolon after
|
|
397
|
+
let semiIdx = code.indexOf(';', closeIdx);
|
|
398
|
+
if (semiIdx === -1) semiIdx = closeIdx;
|
|
399
|
+
code = code.slice(0, eqIdx + 1) + ` {"@info":{"styleObjectId":${styleId}}}` + code.slice(semiIdx);
|
|
400
|
+
modified = true;
|
|
401
|
+
break; // Only one $app_style$ per component block typically
|
|
402
|
+
}
|
|
403
|
+
if (modified) {
|
|
404
|
+
_fsExtra.default.writeFileSync(jsPath, code);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Replace $app_style$ array with @info reference in a JS file
|
|
411
|
+
*/
|
|
412
|
+
function stripStyleFromFile(jsPath) {
|
|
413
|
+
let code = _fsExtra.default.readFileSync(jsPath, 'utf-8');
|
|
414
|
+
const styleStart = code.indexOf('var $app_style$');
|
|
415
|
+
if (styleStart === -1) return;
|
|
416
|
+
const eqIdx = code.indexOf('=', styleStart);
|
|
417
|
+
if (eqIdx === -1) return;
|
|
418
|
+
|
|
419
|
+
// Find opening [ or {
|
|
420
|
+
let openIdx = -1,
|
|
421
|
+
openChar = '',
|
|
422
|
+
closeChar = '';
|
|
423
|
+
for (let i = eqIdx + 1; i < code.length; i++) {
|
|
424
|
+
if (code[i] === '[') {
|
|
425
|
+
openIdx = i;
|
|
426
|
+
openChar = '[';
|
|
427
|
+
closeChar = ']';
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
if (code[i] === '{') {
|
|
431
|
+
openIdx = i;
|
|
432
|
+
openChar = '{';
|
|
433
|
+
closeChar = '}';
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (openIdx === -1) return;
|
|
438
|
+
|
|
439
|
+
// Find matching close with brace counting
|
|
440
|
+
let depth = 0,
|
|
441
|
+
inStr = null,
|
|
442
|
+
esc = false,
|
|
443
|
+
closeIdx = -1;
|
|
444
|
+
for (let i = openIdx; i < code.length; i++) {
|
|
445
|
+
const ch = code[i];
|
|
446
|
+
if (esc) {
|
|
447
|
+
esc = false;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (ch === '\\') {
|
|
451
|
+
esc = true;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (inStr) {
|
|
455
|
+
if (ch === inStr) inStr = null;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
459
|
+
inStr = ch;
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (ch === openChar) depth++;else if (ch === closeChar) {
|
|
463
|
+
depth--;
|
|
464
|
+
if (depth === 0) {
|
|
465
|
+
closeIdx = i;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (closeIdx === -1) return;
|
|
471
|
+
|
|
472
|
+
// Get styleObjectId from the $style$ variable
|
|
473
|
+
const idMatch = code.match(/let \$style\$(\d+)/);
|
|
474
|
+
const styleId = idMatch ? idMatch[1] : '0';
|
|
475
|
+
let semiIdx = code.indexOf(';', closeIdx);
|
|
476
|
+
if (semiIdx === -1) semiIdx = closeIdx;
|
|
477
|
+
code = code.slice(0, eqIdx + 1) + ` {"@info":{"styleObjectId":${styleId}}}` + code.slice(semiIdx);
|
|
478
|
+
_fsExtra.default.writeFileSync(jsPath, code);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Add import paths to custom component nodes in template.json
|
|
483
|
+
*/
|
|
484
|
+
function addImportPaths(node, importMap) {
|
|
485
|
+
if (!node) return;
|
|
486
|
+
if (importMap[node.type]) {
|
|
487
|
+
node.import = importMap[node.type];
|
|
488
|
+
}
|
|
489
|
+
if (node.children) {
|
|
490
|
+
for (const child of node.children) {
|
|
491
|
+
addImportPaths(child, importMap);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Clear prerender caches for watch mode.
|
|
498
|
+
* - No args or empty array: full clear (e.g. style file changed)
|
|
499
|
+
* - With page paths: incremental clear for those pages only
|
|
500
|
+
*/
|
|
501
|
+
function watchChange(changedPages) {
|
|
502
|
+
if (!changedPages || changedPages.length === 0) {
|
|
503
|
+
prerenderEntryKeyCache = {};
|
|
504
|
+
preRenderCache = {};
|
|
505
|
+
cssCache = {};
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
for (const page of changedPages) {
|
|
509
|
+
for (const deviceEntries of Object.values(prerenderEntryKeyCache)) {
|
|
510
|
+
delete deviceEntries[page];
|
|
511
|
+
}
|
|
512
|
+
for (const key of Object.keys(preRenderCache)) {
|
|
513
|
+
if (key.endsWith(`$$${page}`)) {
|
|
514
|
+
delete preRenderCache[key];
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
delete cssCache[page];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 预渲染配置
|
|
3
|
+
*/
|
|
4
|
+
export default interface IPrerenderOption {
|
|
5
|
+
/** 编译产物目录(绝对路径) */
|
|
6
|
+
buildPath: string;
|
|
7
|
+
/** 源码目录(绝对路径) */
|
|
8
|
+
sourcePath: string;
|
|
9
|
+
/** 页面入口列表,相对 buildPath,如 ['pages/Demo/index'] */
|
|
10
|
+
pages: string[];
|
|
11
|
+
/** 是否递归预渲染子组件,默认 true */
|
|
12
|
+
prerenderSubComp?: boolean;
|
|
13
|
+
/** 设备类型列表,来自 manifest.json deviceTypeList,默认 ['default'] */
|
|
14
|
+
deviceTypes?: string[];
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -11,6 +11,7 @@ var _path = _interopRequireDefault(require("path"));
|
|
|
11
11
|
var _TranslateCache = _interopRequireDefault(require("@aiot-toolkit/parser/lib/ux/translate/vela/TranslateCache"));
|
|
12
12
|
var _UxFileUtils = _interopRequireDefault(require("./ux/UxFileUtils"));
|
|
13
13
|
var _IManifest = require("../compiler/javascript/vela/interface/IManifest");
|
|
14
|
+
var _UxUtil = _interopRequireDefault(require("@aiot-toolkit/parser/lib/ux/utils/UxUtil"));
|
|
14
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
16
|
const BinaryPlugin = require('@aiot-toolkit/parser/lib/ux/translate/vela/protobuf/BinaryPlugin');
|
|
16
17
|
|
|
@@ -28,7 +29,8 @@ class BeforeCompileUtils {
|
|
|
28
29
|
const {
|
|
29
30
|
context,
|
|
30
31
|
compilerOption,
|
|
31
|
-
compilation
|
|
32
|
+
compilation,
|
|
33
|
+
onLog
|
|
32
34
|
} = params;
|
|
33
35
|
const {
|
|
34
36
|
projectPath
|
|
@@ -45,8 +47,11 @@ class BeforeCompileUtils {
|
|
|
45
47
|
let serviceList = [];
|
|
46
48
|
const {
|
|
47
49
|
router,
|
|
48
|
-
services
|
|
50
|
+
services,
|
|
51
|
+
widgetProvider
|
|
49
52
|
} = manifestContent;
|
|
53
|
+
// 存储widgetProvider
|
|
54
|
+
let widgetProviderList = [];
|
|
50
55
|
if (router) {
|
|
51
56
|
const {
|
|
52
57
|
pages,
|
|
@@ -62,12 +67,14 @@ class BeforeCompileUtils {
|
|
|
62
67
|
entryList.push(_path.default.join(entryDir, entry));
|
|
63
68
|
} else {
|
|
64
69
|
// 路径不存在
|
|
65
|
-
|
|
70
|
+
onLog?.([{
|
|
71
|
+
level: _sharedUtils.Loglevel.THROW,
|
|
72
|
+
message: ['### manifest ### path', {
|
|
73
|
+
word: _path.default.join(entryDir, entryPages.join(' | '))
|
|
74
|
+
}, 'does not exist']
|
|
75
|
+
}]);
|
|
66
76
|
}
|
|
67
77
|
});
|
|
68
|
-
} else {
|
|
69
|
-
// 没有pages配置
|
|
70
|
-
_sharedUtils.ColorConsole.throw(`### manifest ### No pages configuration`);
|
|
71
78
|
}
|
|
72
79
|
//获取轻卡路由
|
|
73
80
|
if (widgets) {
|
|
@@ -78,23 +85,59 @@ class BeforeCompileUtils {
|
|
|
78
85
|
if (_fsExtra.default.existsSync(cardPath)) {
|
|
79
86
|
liteCardList.push(card + _path.default.posix.sep + cardContent.component);
|
|
80
87
|
} else {
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
onLog?.([{
|
|
89
|
+
level: _sharedUtils.Loglevel.THROW,
|
|
90
|
+
message: [`### manifest ### lite card path`, {
|
|
91
|
+
word: cardPath
|
|
92
|
+
}, `does not exist`]
|
|
93
|
+
}]);
|
|
83
94
|
}
|
|
84
95
|
}
|
|
85
96
|
});
|
|
86
97
|
}
|
|
87
98
|
} else {
|
|
88
99
|
// 没有router配置
|
|
89
|
-
|
|
100
|
+
onLog?.([{
|
|
101
|
+
level: _sharedUtils.Loglevel.THROW,
|
|
102
|
+
message: ['### manifest ### No router configuration']
|
|
103
|
+
}]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// e2e测试入口
|
|
107
|
+
if (compilerOption?.enableE2e && compilerOption?.e2eConfigPath) {
|
|
108
|
+
const e2eConfig = _UxUtil.default.getE2eConfig({
|
|
109
|
+
projectPath: context.projectPath,
|
|
110
|
+
e2eConfigPath: compilerOption.e2eConfigPath
|
|
111
|
+
});
|
|
112
|
+
entryList.push(_path.default.join(e2eConfig.dir, e2eConfig.entry.path));
|
|
90
113
|
}
|
|
91
114
|
if (services) {
|
|
92
115
|
serviceList = Array.isArray(services) ? services : Object.values(services);
|
|
93
116
|
}
|
|
117
|
+
if (widgetProvider) {
|
|
118
|
+
const EXTENSION_JS = '.js';
|
|
119
|
+
for (const providerItem of widgetProvider) {
|
|
120
|
+
const {
|
|
121
|
+
path
|
|
122
|
+
} = providerItem;
|
|
123
|
+
const itemPath = _path.default.join(srcPath, path + EXTENSION_JS);
|
|
124
|
+
if (_fsExtra.default.existsSync(itemPath)) {
|
|
125
|
+
widgetProviderList.push(path + EXTENSION_JS);
|
|
126
|
+
} else {
|
|
127
|
+
onLog?.([{
|
|
128
|
+
level: _sharedUtils.Loglevel.THROW,
|
|
129
|
+
message: ['### manifest ### widgetProvider path', {
|
|
130
|
+
word: itemPath
|
|
131
|
+
}, 'does not exist']
|
|
132
|
+
}]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
94
136
|
if (compilation) {
|
|
95
137
|
compilation['entries'] = entryList;
|
|
96
138
|
compilation['liteCards'] = liteCardList;
|
|
97
139
|
compilation['services'] = serviceList;
|
|
140
|
+
context['widgetProvider'] = widgetProviderList;
|
|
98
141
|
}
|
|
99
142
|
return Promise.resolve();
|
|
100
143
|
};
|