@aiot-toolkit/aiotpack 2.1.0-prender.16 → 2.1.0-prender.17

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.
@@ -79,6 +79,29 @@ declare function stripDeadDefProp(code: string): string;
79
79
  */
80
80
  declare function dropUnusedUxImports(code: string): string;
81
81
  declare function buildComponentJs(compCode: string, compName: string, compSid: number, relPath: string): string;
82
+ /**
83
+ * Normalize rolldown output: drop its region markers and runtime helpers, and
84
+ * turn surviving `require("@system.*")` calls into `$app_require$`.
85
+ */
86
+ declare function stripRolldownArtifacts(code: string): string;
87
+ /**
88
+ * Move synthesized namespace objects to the top of the flattened scope.
89
+ *
90
+ * The `import * as ns` interop bridge reads `ns_exports` from a top-level statement,
91
+ * so with an import cycle DFS post-order can leave the bridge ahead of the object:
92
+ * `var` assignment is not hoisted, so it reads undefined and throws before any
93
+ * `$app_define$` runs. Only getter-only literals move — they have no side effects and
94
+ * resolve lazily, so relocating one cannot snapshot an undefined or reorder an effect.
95
+ */
96
+ declare function hoistNamespaceObjects(code: string): string;
97
+ /** Calls through a surviving thunk table are silently dropped, so refuse to emit one. */
98
+ declare function assertNamespaceObjectsInlined(code: string): void;
99
+ /**
100
+ * Reject a top-level `var` whose eager initializer reads an identifier assigned by a
101
+ * later top-level `var` — the read yields undefined, which is how an import cycle
102
+ * surfaces once module bodies are flattened into one scope.
103
+ */
104
+ declare function assertNoEagerForwardRead(code: string): void;
82
105
  declare function mergeStyleSheets(app: any, page: any): any;
83
106
  export declare const __test: {
84
107
  stripDeadDefProp: typeof stripDeadDefProp;
@@ -89,5 +112,9 @@ export declare const __test: {
89
112
  resolveImportSrc: typeof resolveImportSrc;
90
113
  findUnbundledRelativeImports: typeof findUnbundledRelativeImports;
91
114
  bundleComponentCode: typeof bundleComponentCode;
115
+ stripRolldownArtifacts: typeof stripRolldownArtifacts;
116
+ hoistNamespaceObjects: typeof hoistNamespaceObjects;
117
+ assertNamespaceObjectsInlined: typeof assertNamespaceObjectsInlined;
118
+ assertNoEagerForwardRead: typeof assertNoEagerForwardRead;
92
119
  };
93
120
  export default ViteCompiler;
@@ -962,8 +962,11 @@ function stripRolldownArtifacts(code) {
962
962
  // Inline __exportAll calls: __exportAll({key: () => value, ...}) -> {key: value, ...}
963
963
  // Match __exportAll(...) or /* @__PURE__ */ __exportAll(...)
964
964
  code = code.replace(/(?:\/\* @__PURE__ \*\/\s*)?__exportAll\(\s*\{([\s\S]*?)\}\s*\)/g, (_match, body) => {
965
- // Replace each "key: () => value" with "get key() { return value }" for proper live binding
966
- const inlined = body.replace(/(\w+)\s*:\s*\(\)\s*=>\s*([^,\n}]+?)([,\n])/g, 'get $1() { return $2; }$3');
965
+ // A surviving thunk is not a live binding but a function returning one, so
966
+ // `ns.fn(arg)` drops the argument and returns `fn`. The lookahead (rather than
967
+ // consuming the separator) is what converts the last property, which is followed
968
+ // by the closing brace.
969
+ const inlined = body.replace(/(\w+)\s*:\s*\(\)\s*=>\s*([^,\n}]+?)\s*(?=[,\n]|$)/g, 'get $1() { return $2; }');
967
970
  return `{${inlined}}`;
968
971
  });
969
972
  // Remove Object.defineProperty for Symbol.toStringTag
@@ -991,8 +994,177 @@ function stripRolldownArtifacts(code) {
991
994
  if (!/\b__require\b/.test(code)) {
992
995
  code = code.replace(/var __require\s*=\s*\/\*[^*]*\*\/\s*\(\(x\)\s*=>[\s\S]*?}\);\n?/g, '');
993
996
  }
997
+ assertNamespaceObjectsInlined(code);
998
+ code = hoistNamespaceObjects(code);
999
+ assertNoEagerForwardRead(code);
994
1000
  return code;
995
1001
  }
1002
+
1003
+ /** A getter-only object literal: `{ get a() { return a; }, get b() { return b; }}`. */
1004
+ function isGetterOnlyLiteral(literal) {
1005
+ const inner = literal.slice(1, -1).trim();
1006
+ if (!inner) return false;
1007
+ return inner.split(/,\s*(?=get\s)/).every(p => /^get\s+\w+\s*\(\)\s*\{\s*return\s+[^;{}]+;?\s*\}\s*,?$/.test(p.trim()));
1008
+ }
1009
+
1010
+ /**
1011
+ * Move synthesized namespace objects to the top of the flattened scope.
1012
+ *
1013
+ * The `import * as ns` interop bridge reads `ns_exports` from a top-level statement,
1014
+ * so with an import cycle DFS post-order can leave the bridge ahead of the object:
1015
+ * `var` assignment is not hoisted, so it reads undefined and throws before any
1016
+ * `$app_define$` runs. Only getter-only literals move — they have no side effects and
1017
+ * resolve lazily, so relocating one cannot snapshot an undefined or reorder an effect.
1018
+ */
1019
+ function hoistNamespaceObjects(code) {
1020
+ const hoisted = [];
1021
+ const out = code.replace(/^[ \t]*var\s+(\w+)\s*=\s*(\{[\s\S]*?\});[ \t]*$\n?/gm, (stmt, name, literal) => {
1022
+ if (!isGetterOnlyLiteral(literal)) return stmt;
1023
+ hoisted.push(`var ${name} = ${literal};`);
1024
+ return '';
1025
+ });
1026
+ return hoisted.length ? `${hoisted.join('\n')}\n${out}` : out;
1027
+ }
1028
+
1029
+ /** Calls through a surviving thunk table are silently dropped, so refuse to emit one. */
1030
+ function assertNamespaceObjectsInlined(code) {
1031
+ if (/\b__exportAll\s*\(/.test(code)) {
1032
+ throw new Error(`[js] a namespace object was emitted through the __exportAll helper instead of being ` + `inlined as getters. Calls through it are silently dropped. Offending code:\n` + code.match(/[^\n]*__exportAll\s*\([^\n]*/)[0]);
1033
+ }
1034
+ const thunk = code.match(/^[ \t]*var\s+\w+_exports\s*=\s*\{[^{}]*?\w+\s*:\s*\(\)\s*=>[^\n]*$/m);
1035
+ if (thunk) {
1036
+ throw new Error(`[js] namespace object still holds a thunk property instead of a getter; calls ` + `through it are silently dropped. Offending code:\n${thunk[0]}`);
1037
+ }
1038
+ }
1039
+
1040
+ /**
1041
+ * Reject a top-level `var` whose eager initializer reads an identifier assigned by a
1042
+ * later top-level `var` — the read yields undefined, which is how an import cycle
1043
+ * surfaces once module bodies are flattened into one scope.
1044
+ */
1045
+ function assertNoEagerForwardRead(code) {
1046
+ const stmts = topLevelVarStatements(code);
1047
+ const assignedAt = new Map();
1048
+ for (const s of stmts) if (!assignedAt.has(s.name)) assignedAt.set(s.name, s.at);
1049
+ for (const {
1050
+ name,
1051
+ init,
1052
+ at
1053
+ } of stmts) {
1054
+ if (isLazyInitializer(init)) continue;
1055
+ for (const ref of maskNonReferences(init).matchAll(/[A-Za-z_$][\w$]*/g)) {
1056
+ const refAt = assignedAt.get(ref[0]);
1057
+ if (ref[0] !== name && refAt !== undefined && refAt > at) {
1058
+ throw new Error(`[js] top-level \`var ${name}\` is evaluated before \`${ref[0]}\` is assigned ` + `(assignment appears later in the flattened scope), so it reads undefined. ` + `This is a module ordering bug, usually an import cycle.\n var ${name} = ${init};`);
1059
+ }
1060
+ }
1061
+ }
1062
+ }
1063
+ function isLazyInitializer(init) {
1064
+ // A `{` means a block or object literal, where the name may sit in a method or getter
1065
+ // body that runs later — or be a property key, which is no reference at all. Skipping
1066
+ // those trades coverage inside object literals for freedom from false positives.
1067
+ return init.includes('{') || /\bfunction\b|=>|\bclass\b/.test(init);
1068
+ }
1069
+
1070
+ /** Blank out what looks like an identifier but cannot be a binding reference. */
1071
+ function maskNonReferences(src) {
1072
+ return src.replace(/'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"|`(?:\\.|[^`\\])*`/g, '""').replace(/\.\s*[A-Za-z_$][\w$]*/g, '');
1073
+ }
1074
+
1075
+ /**
1076
+ * Split top-level `var NAME = INIT;` statements.
1077
+ *
1078
+ * Needs a real scan rather than a per-line regex: initializers do span lines in bundler
1079
+ * output (array literals, multi-line template literals), and a line-based split would
1080
+ * read template-literal text as identifiers.
1081
+ */
1082
+ function topLevelVarStatements(code) {
1083
+ const out = [];
1084
+ let depth = 0;
1085
+ let i = 0;
1086
+ while (i < code.length) {
1087
+ const c = code[i];
1088
+ if (c === '{' || c === '(' || c === '[') {
1089
+ depth++;
1090
+ i++;
1091
+ continue;
1092
+ }
1093
+ if (c === '}' || c === ')' || c === ']') {
1094
+ depth--;
1095
+ i++;
1096
+ continue;
1097
+ }
1098
+ if (c === '"' || c === "'" || c === '`') {
1099
+ i = skipString(code, i);
1100
+ continue;
1101
+ }
1102
+ if (c === '/' && code[i + 1] === '/') {
1103
+ i = code.indexOf('\n', i);
1104
+ if (i === -1) break;
1105
+ continue;
1106
+ }
1107
+ if (c === '/' && code[i + 1] === '*') {
1108
+ i = code.indexOf('*/', i);
1109
+ if (i === -1) break;
1110
+ i += 2;
1111
+ continue;
1112
+ }
1113
+ if (depth === 0 && code.startsWith('var ', i) && (i === 0 || /[\s;}]/.test(code[i - 1]))) {
1114
+ const m = /^var\s+([\w$]+)\s*=\s*/.exec(code.slice(i));
1115
+ if (m) {
1116
+ const initStart = i + m[0].length;
1117
+ const end = findStatementEnd(code, initStart);
1118
+ out.push({
1119
+ name: m[1],
1120
+ init: code.slice(initStart, end).trim(),
1121
+ at: i
1122
+ });
1123
+ i = end;
1124
+ continue;
1125
+ }
1126
+ }
1127
+ i++;
1128
+ }
1129
+ return out;
1130
+ }
1131
+ function skipString(code, start) {
1132
+ const quote = code[start];
1133
+ let i = start + 1;
1134
+ while (i < code.length) {
1135
+ if (code[i] === '\\') {
1136
+ i += 2;
1137
+ continue;
1138
+ }
1139
+ if (code[i] === quote) return i + 1;
1140
+ i++;
1141
+ }
1142
+ return i;
1143
+ }
1144
+ function findStatementEnd(code, start) {
1145
+ let depth = 0;
1146
+ let i = start;
1147
+ while (i < code.length) {
1148
+ const c = code[i];
1149
+ if (c === '{' || c === '(' || c === '[') {
1150
+ depth++;
1151
+ i++;
1152
+ continue;
1153
+ }
1154
+ if (c === '}' || c === ')' || c === ']') {
1155
+ depth--;
1156
+ i++;
1157
+ continue;
1158
+ }
1159
+ if (c === '"' || c === "'" || c === '`') {
1160
+ i = skipString(code, i);
1161
+ continue;
1162
+ }
1163
+ if (depth === 0 && (c === ';' || c === '\n')) return i;
1164
+ i++;
1165
+ }
1166
+ return i;
1167
+ }
996
1168
  function wrapOutput(code, entryName, isApp) {
997
1169
  const componentName = _path.default.parse(entryName).name;
998
1170
  const defineId = isApp ? '@app-component/app' : `@app-component/${componentName}`;
@@ -1084,6 +1256,10 @@ const __test = exports.__test = {
1084
1256
  collectReachableUx,
1085
1257
  resolveImportSrc,
1086
1258
  findUnbundledRelativeImports,
1087
- bundleComponentCode
1259
+ bundleComponentCode,
1260
+ stripRolldownArtifacts,
1261
+ hoistNamespaceObjects,
1262
+ assertNamespaceObjectsInlined,
1263
+ assertNoEagerForwardRead
1088
1264
  };
1089
1265
  var _default = exports.default = ViteCompiler;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/aiotpack",
3
- "version": "2.1.0-prender.16",
3
+ "version": "2.1.0-prender.17",
4
4
  "description": "The process tool for packaging aiot projects.",
5
5
  "keywords": [
6
6
  "aiotpack"
@@ -19,16 +19,16 @@
19
19
  "test": "node ./__tests__/aiotpack.test.js"
20
20
  },
21
21
  "dependencies": {
22
- "@aiot-toolkit/generator": "2.1.0-prender.16",
23
- "@aiot-toolkit/parser": "2.1.0-prender.16",
24
- "@aiot-toolkit/shared-utils": "2.1.0-prender.16",
22
+ "@aiot-toolkit/generator": "2.1.0-prender.17",
23
+ "@aiot-toolkit/parser": "2.1.0-prender.17",
24
+ "@aiot-toolkit/shared-utils": "2.1.0-prender.17",
25
25
  "@hap-toolkit/aaptjs": "^2.0.0",
26
26
  "@rspack/core": "^1.3.9",
27
27
  "acorn": "^8.16.0",
28
28
  "aiot-parse5": "^1.0.2",
29
29
  "astring": "^1.9.0",
30
30
  "babel-loader": "^9.1.3",
31
- "file-lane": "2.1.0-prender.16",
31
+ "file-lane": "2.1.0-prender.17",
32
32
  "file-loader": "^6.2.0",
33
33
  "fs-extra": "^11.2.0",
34
34
  "jsrsasign": "^11.1.0",