@aiot-toolkit/aiotpack 2.0.6-beta.8 → 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.
Files changed (43) hide show
  1. package/lib/afterCompile/ux/UxAfterCompile.d.ts +4 -0
  2. package/lib/afterCompile/ux/UxAfterCompile.js +90 -2
  3. package/lib/compiler/javascript/JavascriptCompiler.js +11 -4
  4. package/lib/compiler/javascript/TemplateCompiler.d.ts +29 -0
  5. package/lib/compiler/javascript/TemplateCompiler.js +564 -0
  6. package/lib/compiler/javascript/ViteCompiler.d.ts +13 -0
  7. package/lib/compiler/javascript/ViteCompiler.js +414 -0
  8. package/lib/compiler/javascript/interface/IJavascriptCompileOption.d.ts +26 -0
  9. package/lib/compiler/javascript/vela/VelaWebpackConfigurator.d.ts +3 -1
  10. package/lib/compiler/javascript/vela/VelaWebpackConfigurator.js +16 -1
  11. package/lib/compiler/javascript/vela/interface/IManifest.d.ts +12 -0
  12. package/lib/compiler/javascript/vela/plugin/WrapPlugin.d.ts +10 -1
  13. package/lib/compiler/javascript/vela/plugin/WrapPlugin.js +241 -57
  14. package/lib/compiler/javascript/vela/utils/UxCompileUtil.d.ts +3 -2
  15. package/lib/compiler/javascript/vela/utils/UxCompileUtil.js +12 -4
  16. package/lib/compiler/javascript/vela/utils/VruUtil.d.ts +50 -0
  17. package/lib/compiler/javascript/vela/utils/VruUtil.js +128 -0
  18. package/lib/compiler/javascript/vela/utils/ZipUtil.d.ts +9 -0
  19. package/lib/compiler/javascript/vela/utils/ZipUtil.js +112 -6
  20. package/lib/compiler/javascript/vela/utils/webpackLoader/WebpackJsLoader.js +1 -1
  21. package/lib/config/UxConfig.d.ts +12 -5
  22. package/lib/config/UxConfig.js +7 -6
  23. package/lib/loader/ux/JsLoader.d.ts +9 -0
  24. package/lib/loader/ux/JsLoader.js +47 -8
  25. package/lib/loader/ux/vela/HmlLoader.d.ts +6 -6
  26. package/lib/loader/ux/vela/HmlLoader.js +30 -13
  27. package/lib/prerender/PrerenderVM.d.ts +86 -0
  28. package/lib/prerender/PrerenderVM.js +677 -0
  29. package/lib/prerender/StyleSerializer.d.ts +18 -0
  30. package/lib/prerender/StyleSerializer.js +92 -0
  31. package/lib/prerender/TemplateSerializer.d.ts +26 -0
  32. package/lib/prerender/TemplateSerializer.js +122 -0
  33. package/lib/prerender/index.d.ts +20 -0
  34. package/lib/prerender/index.js +519 -0
  35. package/lib/prerender/interface/IPrerenderOption.d.ts +15 -0
  36. package/lib/prerender/interface/IPrerenderOption.js +1 -0
  37. package/lib/utils/BeforeCompileUtils.d.ts +1 -1
  38. package/lib/utils/BeforeCompileUtils.js +52 -9
  39. package/lib/utils/ux/ManifestSchema.js +0 -1
  40. package/lib/utils/ux/UxFileUtils.js +1 -1
  41. package/lib/utils/ux/UxLoaderUtils.d.ts +6 -0
  42. package/lib/utils/ux/UxLoaderUtils.js +22 -10
  43. package/package.json +9 -6
@@ -14,6 +14,7 @@ var _BuildNameFormatType = _interopRequireDefault(require("../enum/BuildNameForm
14
14
  var _Package = _interopRequireDefault(require("../model/Package"));
15
15
  var _UxCompileUtil = _interopRequireDefault(require("./UxCompileUtil"));
16
16
  var _SignUtil = _interopRequireDefault(require("./signature/SignUtil"));
17
+ var _VruUtil = require("./VruUtil");
17
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
19
  /**
19
20
  * Zip 用于将打包成功的 build 目录按特定规则压缩成 rpk
@@ -66,17 +67,122 @@ class ZipUtil {
66
67
  _sharedUtils.ColorConsole.error(`The build file is missing, stop generating the application package, please check carefully`);
67
68
  return;
68
69
  }
70
+ const signConfig = await _SignUtil.default.getProjectSignConfig(param);
71
+ const rpkFileName = this.getFileName(param, config, 'rpk');
72
+
73
+ // VRU mode: produce wrapped RPK (manifest + logo + CERT + inner-vru)
74
+ if (param.enableVru) {
75
+ const wrappedRpkBuffer = await this.createWrappedReleaseRpk(param, config, files, signConfig);
76
+ return await this.generateDistFile(wrappedRpkBuffer, param, rpkFileName);
77
+ }
69
78
 
70
- // 2
79
+ // Flat mode (default): all build files directly in RPK
71
80
  const {
72
81
  fullPackage
73
82
  } = await this.createPackagesDefinition(param, config, files);
74
-
75
- // 生产出带签名的rpk文件buffer
76
- const signConfig = await _SignUtil.default.getProjectSignConfig(param);
77
83
  const rpkBuffer = await ZipUtil.buildProjectAndOutput(fullPackage, signConfig);
78
- // 3
79
- return this.generateDistFile(rpkBuffer, param, this.getFileName(param, config, 'rpk'));
84
+ return await this.generateDistFile(rpkBuffer, param, rpkFileName);
85
+ }
86
+
87
+ /**
88
+ * Build the blueos-pack compatible "wrapped release" RPK.
89
+ * Outer RPK (zip) contains:
90
+ * - manifest.json (top-level metadata)
91
+ * - logo.<ext> (icon - kept as png since VUG conversion is out of scope)
92
+ * - META-INF/CERT (signature)
93
+ * - <package>.vru (multi-file VRU containing all build artifacts)
94
+ */
95
+ static async createWrappedReleaseRpk(param, config, files, signConfig) {
96
+ const {
97
+ projectPath,
98
+ outputPath
99
+ } = param;
100
+ const buildDir = _path.default.join(projectPath, outputPath);
101
+
102
+ // 1. Pack all build files into VRU
103
+ const vruEntries = [];
104
+ for (const f of files) {
105
+ // Skip top-level manifest.json and CERT — those go in outer rpk
106
+ if (f === 'manifest.json' || f === ZipUtil.CERT_PATH) continue;
107
+ const absPath = _path.default.join(buildDir, f);
108
+ vruEntries.push({
109
+ name: f,
110
+ data: _fsExtra.default.readFileSync(absPath)
111
+ });
112
+ }
113
+ // Add packageInfo.json for build metadata
114
+ const packageInfo = {
115
+ originType: 'native',
116
+ toolkit: 'aiot-toolkit',
117
+ buildTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
118
+ node: process.version,
119
+ platform: process.platform,
120
+ arch: process.arch
121
+ };
122
+ vruEntries.push({
123
+ name: 'packageInfo.json',
124
+ data: Buffer.from(JSON.stringify(packageInfo, null, 2), 'utf-8')
125
+ });
126
+ // Sort entries alphabetically (matches blueos-pack ordering)
127
+ vruEntries.sort((a, b) => a.name.localeCompare(b.name));
128
+ const vruBuffer = _VruUtil.VruUtil.pack(vruEntries, config.package);
129
+
130
+ // 2. Build the outer RPK
131
+ const outerPackage = new _Package.default({
132
+ filePrefix: config.package,
133
+ fileSuffix: 'rpk',
134
+ standalone: true,
135
+ comment: JSON.stringify(this.createComment(param))
136
+ });
137
+
138
+ // Add manifest.json
139
+ const manifestPath = _path.default.join(buildDir, 'manifest.json');
140
+ if (_fsExtra.default.existsSync(manifestPath)) {
141
+ const manifestBuf = _fsExtra.default.readFileSync(manifestPath);
142
+ outerPackage.addResource({
143
+ fileBuildPath: 'manifest.json',
144
+ fileContentBuffer: manifestBuf,
145
+ fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(manifestBuf)
146
+ });
147
+ }
148
+
149
+ // Add icon (as-is, no VUG conversion)
150
+ if (config.icon) {
151
+ const iconPath = _path.default.join(buildDir, config.icon.replace(/^\//, ''));
152
+ if (_fsExtra.default.existsSync(iconPath)) {
153
+ const iconBuf = _fsExtra.default.readFileSync(iconPath);
154
+ const iconExt = _path.default.extname(iconPath) || '.png';
155
+ outerPackage.addResource({
156
+ fileBuildPath: `logo${iconExt}`,
157
+ fileContentBuffer: iconBuf,
158
+ fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(iconBuf)
159
+ });
160
+ }
161
+ }
162
+
163
+ // Add inner VRU
164
+ outerPackage.addResource({
165
+ fileBuildPath: _VruUtil.VruUtil.getVruName(config.package),
166
+ fileContentBuffer: vruBuffer,
167
+ fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(vruBuffer)
168
+ });
169
+
170
+ // Add CERT if present
171
+ const certAbsPath = _path.default.join(buildDir, ZipUtil.CERT_PATH);
172
+ if (_fsExtra.default.existsSync(certAbsPath)) {
173
+ let certBuf = _fsExtra.default.readFileSync(certAbsPath);
174
+ const metaZip = await _jszip.default.loadAsync(certBuf);
175
+ certBuf = await metaZip.generateAsync({
176
+ ...ZipUtil.ZIP_OPTION,
177
+ comment: null
178
+ });
179
+ outerPackage.addResource({
180
+ fileBuildPath: ZipUtil.CERT_PATH,
181
+ fileContentBuffer: certBuf,
182
+ fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(certBuf)
183
+ });
184
+ }
185
+ return await ZipUtil.buildProjectAndOutput(outerPackage, signConfig);
80
186
  }
81
187
  static getFileName(param, config, ext) {
82
188
  const {
@@ -20,7 +20,7 @@ async function _default(source) {
20
20
  projectPath: this.rootContext,
21
21
  onLog: log => onLog?.([log]),
22
22
  projectType: _sharedUtils.ProjectType.VELA_UX
23
- }, compileParam, context).translate({
23
+ }, compileParam, context, true).translate({
24
24
  content: source
25
25
  }, []);
26
26
  callback(null, result.targetTree.getFullText());
@@ -5,6 +5,7 @@ import PngLoader from '../loader/ux/PngLoader';
5
5
  import AppUxLoader from '../loader/ux/vela/AppUxLoader';
6
6
  import HmlLoader from '../loader/ux/vela/HmlLoader';
7
7
  import UxLoader from '../loader/ux/vela/UxLoader';
8
+ import AndroidUxLoader from '../loader/ux/android/UxLoader';
8
9
  import UxBeforeWorks from '../beforeWorks/ux/UxBeforeWorks';
9
10
  import UxAfterWorks from '../afterWorks/ux/UxAfterWorks';
10
11
  import { IChangedFile } from 'file-lane/lib/interface/IChangedFile';
@@ -28,13 +29,15 @@ declare class UxConfig implements IFileLaneConfig<IJavascriptCompileOption> {
28
29
  beforeCompile: import("file-lane").PreWork<IJavascriptCompileOption>[];
29
30
  afterCompile: ({
30
31
  worker: import("file-lane").FollowWork<IJavascriptCompileOption>;
32
+ workDescribe: string;
31
33
  workerDescribe?: undefined;
32
34
  } | {
33
35
  worker: import("file-lane").FollowWork<IJavascriptCompileOption>;
34
36
  workerDescribe: string;
37
+ workDescribe?: undefined;
35
38
  })[];
36
39
  afterWorks: (typeof UxAfterWorks.cleanOutput)[];
37
- watchIgnores: RegExp[];
40
+ watchIgnores: string[];
38
41
  /**
39
42
  * 通过项目类型,返回模块配置
40
43
  */
@@ -46,11 +49,7 @@ declare class UxConfig implements IFileLaneConfig<IJavascriptCompileOption> {
46
49
  } | {
47
50
  test: RegExp[];
48
51
  exclude: RegExp[];
49
- loader: (typeof UxLoader)[];
50
- } | {
51
- test: RegExp[];
52
52
  loader: (typeof HmlLoader | typeof UxLoader)[];
53
- exclude?: undefined;
54
53
  } | {
55
54
  test: RegExp[];
56
55
  loader: (typeof JsLoader)[];
@@ -60,6 +59,14 @@ declare class UxConfig implements IFileLaneConfig<IJavascriptCompileOption> {
60
59
  loader: (typeof PngLoader)[];
61
60
  exclude?: undefined;
62
61
  })[];
62
+ } | {
63
+ rules: ({
64
+ test: RegExp[];
65
+ loader: (typeof AndroidUxLoader)[];
66
+ } | {
67
+ test: RegExp[];
68
+ loader: (typeof PngLoader)[];
69
+ })[];
63
70
  };
64
71
  /**
65
72
  * 判断项目类型
@@ -75,13 +75,17 @@ class UxConfig {
75
75
  beforeWorks = (() => [_UxBeforeWorks.default.cleanOutput])();
76
76
  beforeCompile = (() => [_UxBeforeCompile.default.validateManifest, _UxBeforeCompile.default.validateSitemap, _BeforeCompileUtils.default.clean, _BeforeCompileUtils.default.getEntries, _BeforeCompileUtils.default.getGlobalVar])();
77
77
  afterCompile = (() => [{
78
- worker: _UxAfterCompile.default.writeGitIgnore
78
+ worker: _UxAfterCompile.default.writeGitIgnore,
79
+ workDescribe: 'Write .gitignore'
79
80
  }, {
80
81
  worker: _UxAfterCompile.default.symlinkNodeModule,
81
82
  workerDescribe: 'Create a soft link to the node_modules folder'
82
83
  }, {
83
84
  worker: _UxAfterCompile.default.compileJavascript,
84
85
  workerDescribe: 'Compile javascript project'
86
+ }, {
87
+ worker: _UxAfterCompile.default.prerender,
88
+ workerDescribe: 'Prerender pages to template.json + css.json'
85
89
  }, {
86
90
  worker: _UxAfterCompile.default.copyResource,
87
91
  workerDescribe: 'Copy resource files'
@@ -111,7 +115,7 @@ class UxConfig {
111
115
  workerDescribe: 'Check resource'
112
116
  }])();
113
117
  afterWorks = (() => [_UxAfterWorks.default.cleanOutput])();
114
- watchIgnores = [/node_modules/, /build/, /dist/];
118
+ watchIgnores = ['**/node_modules/**', '**/build/**', '**/dist/**'];
115
119
 
116
120
  /**
117
121
  * 通过项目类型,返回模块配置
@@ -123,11 +127,8 @@ class UxConfig {
123
127
  test: ['app.ux'],
124
128
  loader: [_AppUxLoader.default]
125
129
  }, {
126
- test: [/.+\.ux$/],
130
+ test: [/.+\.(ux|hml)$/],
127
131
  exclude: [/app\.ux/],
128
- loader: [_UxLoader.default]
129
- }, {
130
- test: [/.+\.hml$/],
131
132
  loader: [_HmlLoader.default, _UxLoader.default]
132
133
  }, {
133
134
  test: [/.+\.js$/],
@@ -1,11 +1,20 @@
1
1
  import { IFileLaneContext, IFileParam, ILoader } from 'file-lane';
2
2
  import IJavascriptCompileOption from '../../compiler/javascript/interface/IJavascriptCompileOption';
3
+ import { ILog } from '@aiot-toolkit/shared-utils';
4
+ import FileLaneCompilation from 'file-lane/lib/FileLaneCompilation';
3
5
  /**
4
6
  * JsLoader
5
7
  */
6
8
  declare class JsLoader implements ILoader {
7
9
  context: IFileLaneContext;
8
10
  compilerOption: IJavascriptCompileOption;
11
+ compilation: FileLaneCompilation;
12
+ logs: ILog[];
13
+ /**
14
+ * 解析文件内容
15
+ * @param files files 待处理的文件数组
16
+ * @returns 处理后的文件数组
17
+ */
9
18
  parser(files: IFileParam<any>[]): Promise<IFileParam<any>[]>;
10
19
  }
11
20
  export default JsLoader;
@@ -6,13 +6,28 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _parser = require("@aiot-toolkit/parser");
8
8
  var _sharedUtils = require("@aiot-toolkit/shared-utils");
9
+ var _UxLoaderUtils = _interopRequireDefault(require("../../utils/ux/UxLoaderUtils"));
10
+ var _path = _interopRequireDefault(require("path"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
12
  /**
10
13
  * JsLoader
11
14
  */
12
15
  class JsLoader {
16
+ logs = [];
17
+ /**
18
+ * 解析文件内容
19
+ * @param files files 待处理的文件数组
20
+ * @returns 处理后的文件数组
21
+ */
13
22
  async parser(files) {
14
- const onLog = () => {};
15
23
  const result = [];
24
+ const {
25
+ projectPath,
26
+ widgetProvider
27
+ } = this.context;
28
+ const {
29
+ sourceRoot
30
+ } = this.compilerOption;
16
31
  for (const item of files) {
17
32
  if (!item.content) {
18
33
  result.push({
@@ -22,17 +37,41 @@ class JsLoader {
22
37
  } else {
23
38
  const options = {
24
39
  filePath: item.path,
25
- projectPath: this.context.projectPath,
40
+ projectPath,
26
41
  content: item.content.toString(),
27
42
  projectType: _sharedUtils.ProjectType.getProjectType(this.context.projectPath),
28
- onLog
43
+ onLog: log => {
44
+ this.logs.push(log);
45
+ }
29
46
  };
30
- result.push({
31
- path: item.path,
32
- content: (await new _parser.ScriptToTypescript(options, this.compilerOption, this.context).translate({
33
- content: new _parser.ScriptParser(options).parser(item.content.toString()).ast.content
34
- }, [])).targetTree.getFullText()
47
+ const scriptTree = await new _parser.ScriptToTypescript(options, this.compilerOption, this.context).translate({
48
+ content: new _parser.ScriptParser(options).parser(item.content.toString()).ast.content
49
+ }, []);
50
+ const isService = _UxLoaderUtils.default.isServiceFile(item.path, {
51
+ compilation: this.compilation,
52
+ compilerOption: this.compilerOption,
53
+ context: this.context
35
54
  });
55
+ if (isService) {
56
+ result.push({
57
+ path: item.path,
58
+ content: _UxLoaderUtils.default.createServiceWrapper(scriptTree.targetTree).filter(Boolean).join('\n')
59
+ });
60
+ } else {
61
+ const isWidgetProvider = widgetProvider?.includes(_path.default.relative(_path.default.join(projectPath, sourceRoot), item.path).replace(/\\/g, '/'));
62
+ let content = scriptTree.targetTree.getFullText();
63
+ if (isWidgetProvider) {
64
+ const {
65
+ newScriptTree,
66
+ scriptSourceMap
67
+ } = _parser.SourceMapUtil.splitSourceMap(scriptTree.targetTree);
68
+ content = _parser.SourceMapUtil.concatSourceMap(`${newScriptTree.getFullText()}\n$app_exports$.default = exports["default"]\n`, scriptSourceMap);
69
+ }
70
+ result.push({
71
+ path: item.path,
72
+ content: content
73
+ });
74
+ }
36
75
  }
37
76
  }
38
77
  return result;
@@ -9,16 +9,16 @@ declare class HmlLoader implements ILoader {
9
9
  /**
10
10
  * 包裹 hml 文件
11
11
  *
12
- * # 路径
13
- * 如果存在同路径的 ux 后缀,报错;否则转换为同路径的 ux 后缀
14
- *
15
12
  * # 内容
16
- * 1. 给hml的内容加上<template></template>
17
- * 2. 如果存在同路径同名的 script文件,则加到<script></script>中
18
- * 3. 如果存在同路径同名的 style文件,则加到<style></style>中
13
+ * 1. 全文模式(存在template、script、style任一节点),保留原内容不变
14
+ * 2. 否则,则进行如下包装
15
+ * 1. hml的内容: import放到最前,其它内容使用<template></template>包裹
16
+ * 2. 如果存在同路径同名的 script文件,则加到<script></script>中
17
+ * 3. 如果存在同路径同名的 style文件,则加到<style></style>中
19
18
  *
20
19
  * @param file
21
20
  */
22
21
  private wrapHml;
22
+ private nodesToString;
23
23
  }
24
24
  export default HmlLoader;
@@ -7,6 +7,9 @@ exports.default = void 0;
7
7
  var _path = _interopRequireDefault(require("path"));
8
8
  var _fs = _interopRequireDefault(require("fs"));
9
9
  var _parser = require("@aiot-toolkit/parser");
10
+ var parse5 = _interopRequireWildcard(require("aiot-parse5"));
11
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
12
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
10
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
14
  /**
12
15
  * HmlLoader
@@ -21,13 +24,12 @@ class HmlLoader {
21
24
  /**
22
25
  * 包裹 hml 文件
23
26
  *
24
- * # 路径
25
- * 如果存在同路径的 ux 后缀,报错;否则转换为同路径的 ux 后缀
26
- *
27
27
  * # 内容
28
- * 1. 给hml的内容加上<template></template>
29
- * 2. 如果存在同路径同名的 script文件,则加到<script></script>中
30
- * 3. 如果存在同路径同名的 style文件,则加到<style></style>中
28
+ * 1. 全文模式(存在template、script、style任一节点),保留原内容不变
29
+ * 2. 否则,则进行如下包装
30
+ * 1. hml的内容: import放到最前,其它内容使用<template></template>包裹
31
+ * 2. 如果存在同路径同名的 script文件,则加到<script></script>中
32
+ * 3. 如果存在同路径同名的 style文件,则加到<style></style>中
31
33
  *
32
34
  * @param file
33
35
  */
@@ -36,32 +38,47 @@ class HmlLoader {
36
38
  path,
37
39
  content
38
40
  } = file;
39
- const uxExt = _parser.ExtensionConfig.UX;
41
+ const uxAst = parse5.parseFragment(content?.toString() || '', {
42
+ scriptingEnabled: false,
43
+ sourceCodeLocationInfo: true
44
+ });
45
+ const fullMode = uxAst.childNodes.some(node => {
46
+ return ['template', 'script', 'style'].includes(node.nodeName);
47
+ });
48
+ if (fullMode) {
49
+ return file;
50
+ }
40
51
  const getFilePath = ext => {
41
52
  const pathParsed = _path.default.parse(path);
42
53
  pathParsed.ext = ext;
43
54
  pathParsed.base = pathParsed.name + ext;
44
55
  return _path.default.format(pathParsed);
45
56
  };
46
- const uxPath = getFilePath(uxExt);
47
- if (_fs.default.existsSync(uxPath)) {
48
- throw new Error(`${uxPath} already exists`);
49
- }
50
57
  const scriptExts = _parser.ExtensionConfig.SCRIPTS;
51
58
  const styleExts = _parser.ExtensionConfig.STYLES;
52
59
  const scriptPath = scriptExts.map(item => getFilePath(item)).find(item => _fs.default.existsSync(item));
53
60
  const stylePath = styleExts.map(item => getFilePath(item)).find(item => _fs.default.existsSync(item));
54
- let uxContent = ['<template>', content, '</template>'].join('\n');
61
+ const importNodes = uxAst.childNodes.filter(node => node.nodeName === 'import');
62
+ const otherNodes = uxAst.childNodes.filter(node => node.nodeName !== 'import');
63
+ let uxContent = this.nodesToString(importNodes);
64
+ uxContent += ['<template>', this.nodesToString(otherNodes), '</template>'].join('\n');
55
65
  if (scriptPath) {
56
66
  uxContent += ['<script>', _fs.default.readFileSync(scriptPath, 'utf-8'), '</script>'].join('\n');
57
67
  }
58
68
  if (stylePath) {
59
- uxContent += ['<style>', _fs.default.readFileSync(stylePath, 'utf-8'), '</style>'].join('\n');
69
+ const styleExt = _path.default.extname(stylePath).toLowerCase();
70
+ uxContent += [`<style lang="${styleExt.slice(1)}">`, _fs.default.readFileSync(stylePath, 'utf-8'), '</style>'].join('\n');
60
71
  }
61
72
  return {
62
73
  path,
63
74
  content: uxContent
64
75
  };
65
76
  }
77
+ nodesToString(nodes) {
78
+ return parse5.serialize({
79
+ nodeName: '#document-fragment',
80
+ childNodes: nodes
81
+ });
82
+ }
66
83
  }
67
84
  var _default = exports.default = HmlLoader;
@@ -0,0 +1,86 @@
1
+ import { ILog } from '@aiot-toolkit/shared-utils';
2
+ /** 预渲染 DOM 节点 */
3
+ export interface PrerenderNode {
4
+ type: string;
5
+ attr?: Record<string, any>;
6
+ class?: string;
7
+ style?: Record<string, string>;
8
+ styleObjectId?: number;
9
+ children?: PrerenderNode[];
10
+ events?: Record<string, string>;
11
+ bind?: number;
12
+ $repeat?: string;
13
+ repeat?: any[];
14
+ repeat_items?: PrerenderNode[];
15
+ import?: string;
16
+ }
17
+ /** 绑定标记:文本/属性绑定 */
18
+ export interface BindingMarker {
19
+ $value: string;
20
+ bind: 1;
21
+ _staticValue?: any;
22
+ [key: string]: any;
23
+ }
24
+ /** 循环标记 */
25
+ export interface RepeatMarker {
26
+ $repeat: string;
27
+ repeat: any[];
28
+ }
29
+ /** 事件标记 */
30
+ export interface EventMarker {
31
+ [key: `$${string}`]: string;
32
+ }
33
+ /**
34
+ * PrerenderVM - 在 Node.js VM 沙箱中执行编译后的模板函数,收集 DOM 树
35
+ */
36
+ export default class PrerenderVM {
37
+ private components;
38
+ private styleMap;
39
+ private bindCounter;
40
+ private styleSerializer;
41
+ private accessedKeys;
42
+ /** 创建 data Proxy,拦截访问并返回绑定标记(支持嵌套和数组) */
43
+ createDataProxy(data: Record<string, any>, parentPath?: string): Record<string, any>;
44
+ /** 获取已访问的 key 列表 */
45
+ getAccessedKeys(): string[];
46
+ /** Track binding markers accessed during a function call */
47
+ private _bindingAccess;
48
+ /** Create a binding marker with toString for string concatenation */
49
+ private createBindingMarker;
50
+ /** 文本绑定标记 */
51
+ static createTextBinding(key: string): BindingMarker;
52
+ /** 属性绑定标记 */
53
+ static createAttrBinding(attr: string, key: string): Record<string, any>;
54
+ /** 循环标记 */
55
+ static createRepeatMarker(listName: string): RepeatMarker;
56
+ /** 事件标记 */
57
+ static createEventMarker(event: string, handler: string): EventMarker;
58
+ /**
59
+ * 执行编译后的 JS,收集组件定义
60
+ */
61
+ execute(jsCode: string, onLog?: (logs: ILog[]) => void): {
62
+ tree: PrerenderNode | null;
63
+ styles: Record<number, any>;
64
+ };
65
+ private unwrapCode;
66
+ private createSandbox;
67
+ private collectStyles;
68
+ private normalizeStyleSheet;
69
+ private createMockVM;
70
+ private createNode;
71
+ private createConditionalNode;
72
+ private createForNode;
73
+ private createBlockNode;
74
+ private executeTemplate;
75
+ private parseInlineStyle;
76
+ private extractBindingExpression;
77
+ /** Replace $item.xxx references in event handler strings with actual values */
78
+ private bakeItemRefsInEvents;
79
+ /**
80
+ * Get all registered sub-component templates (excluding the bootstrapped main component)
81
+ */
82
+ getSubComponentTemplates(mainName?: string): Map<string, {
83
+ tree: PrerenderNode | null;
84
+ styles: Record<number, any>;
85
+ }>;
86
+ }