openxiangda 1.0.43 → 1.0.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.43",
3
+ "version": "1.0.44",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -18,9 +18,79 @@ async function fileExists(targetPath) {
18
18
  }
19
19
  }
20
20
 
21
+ async function resolveExistingModulePath(basePath) {
22
+ const candidates = [
23
+ basePath,
24
+ `${basePath}.ts`,
25
+ `${basePath}.tsx`,
26
+ `${basePath}.js`,
27
+ `${basePath}.jsx`,
28
+ `${basePath}.mjs`,
29
+ `${basePath}.cjs`,
30
+ path.join(basePath, "index.ts"),
31
+ path.join(basePath, "index.tsx"),
32
+ path.join(basePath, "index.js"),
33
+ path.join(basePath, "index.jsx"),
34
+ path.join(basePath, "index.mjs"),
35
+ path.join(basePath, "index.cjs"),
36
+ ];
37
+
38
+ for (const candidate of candidates) {
39
+ if (await fileExists(candidate)) return candidate;
40
+ }
41
+ throw new Error(`无法解析工作区别名导入: @/${path.relative(srcRoot, basePath)}`);
42
+ }
43
+
44
+ async function rewriteWorkspaceAliasImports(source) {
45
+ const importSpecifierPattern =
46
+ /(\bfrom\s*["']|^\s*import\s*["']|import\s*\(\s*["'])(@\/[^"']+)(["'])/gm;
47
+ const replacements = [];
48
+ let match;
49
+ while ((match = importSpecifierPattern.exec(source))) {
50
+ const [, prefix, specifier, suffix] = match;
51
+ const targetPath = await resolveExistingModulePath(
52
+ path.join(srcRoot, specifier.slice(2)),
53
+ );
54
+ replacements.push({
55
+ start: match.index,
56
+ end: match.index + match[0].length,
57
+ value: `${prefix}${pathToFileURL(targetPath).href}${suffix}`,
58
+ });
59
+ }
60
+
61
+ if (replacements.length === 0) return source;
62
+
63
+ let cursor = 0;
64
+ let next = "";
65
+ for (const replacement of replacements) {
66
+ next += source.slice(cursor, replacement.start);
67
+ next += replacement.value;
68
+ cursor = replacement.end;
69
+ }
70
+ next += source.slice(cursor);
71
+ return next;
72
+ }
73
+
21
74
  async function loadTypeScriptModule(filePath) {
22
- const loaded = await import(pathToFileURL(filePath).href);
23
- return loaded.default || loaded;
75
+ const source = await fs.readFile(filePath, "utf8");
76
+ const rewritten = await rewriteWorkspaceAliasImports(source);
77
+ let importPath = filePath;
78
+ if (rewritten !== source) {
79
+ importPath = path.join(
80
+ path.dirname(filePath),
81
+ `.page.config.openxiangda-loader-${process.pid}-${Date.now()}.ts`,
82
+ );
83
+ await fs.writeFile(importPath, rewritten, "utf8");
84
+ }
85
+
86
+ try {
87
+ const loaded = await import(`${pathToFileURL(importPath).href}?t=${Date.now()}`);
88
+ return loaded.default || loaded;
89
+ } finally {
90
+ if (importPath !== filePath) {
91
+ await fs.rm(importPath, { force: true });
92
+ }
93
+ }
24
94
  }
25
95
 
26
96
  export async function discoverPages(filterName = "") {
@@ -70,6 +70,60 @@ describe("register payload helpers", () => {
70
70
  }
71
71
  });
72
72
 
73
+ it("loads page configs that import workspace @ alias modules", async () => {
74
+ const workspaceRoot = await mkdtemp(
75
+ join(process.cwd(), ".tmp-openxiangda-pages-alias-test-"),
76
+ );
77
+ const previousWorkspaceRoot = process.env.LOWCODE_WORKSPACE_ROOT;
78
+
79
+ try {
80
+ const typesDir = join(workspaceRoot, "src/types");
81
+ const pageDir = join(workspaceRoot, "src/pages/runtime-workbench");
82
+ await mkdir(typesDir, { recursive: true });
83
+ await mkdir(pageDir, { recursive: true });
84
+ await writeFile(
85
+ join(typesDir, "app-workspace.types.ts"),
86
+ `export function definePageConfig(config) { return config; }\n`,
87
+ "utf8",
88
+ );
89
+ await writeFile(join(pageDir, "index.tsx"), "export {};\n", "utf8");
90
+ await writeFile(
91
+ join(pageDir, "App.tsx"),
92
+ "export default function App() { return null; }\n",
93
+ "utf8",
94
+ );
95
+ await writeFile(
96
+ join(pageDir, "page.config.ts"),
97
+ `import { definePageConfig } from "@/types/app-workspace.types";
98
+
99
+ export default definePageConfig({
100
+ code: "runtime-workbench",
101
+ name: "运行时工作台",
102
+ route: { pathKey: "runtime_workbench" },
103
+ });
104
+ `,
105
+ "utf8",
106
+ );
107
+
108
+ process.env.LOWCODE_WORKSPACE_ROOT = workspaceRoot;
109
+ vi.resetModules();
110
+ const { discoverPages } = await import("./pages.mjs");
111
+ const pages = await discoverPages("runtime-workbench");
112
+
113
+ expect(pages).toHaveLength(1);
114
+ expect(pages[0].config.code).toBe("runtime-workbench");
115
+ expect(pages[0].config.route.pathKey).toBe("runtime_workbench");
116
+ } finally {
117
+ if (previousWorkspaceRoot === undefined) {
118
+ delete process.env.LOWCODE_WORKSPACE_ROOT;
119
+ } else {
120
+ process.env.LOWCODE_WORKSPACE_ROOT = previousWorkspaceRoot;
121
+ }
122
+ vi.resetModules();
123
+ await rm(workspaceRoot, { recursive: true, force: true });
124
+ }
125
+ });
126
+
73
127
  it("builds stable form and page asset URLs from one build id", () => {
74
128
  expect(getFormBundleUrl(config, "customer-info", "index.js")).toBe(
75
129
  "https://bucket.oss-cn-hangzhou.aliyuncs.com/lowcode/app-workspace/dev/1.2.3/BUILD_001/forms/customer-info/index.js",