@pactor-app/module-pack 0.0.0-stage → 1.55.0

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/dist/index.js ADDED
@@ -0,0 +1,1169 @@
1
+ // src/build.ts
2
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, renameSync, rmSync as rmSync2 } from "fs";
3
+ import { dirname as dirname3, join as join9, resolve as resolve4 } from "path";
4
+
5
+ // src/bundle-client.ts
6
+ import { existsSync, mkdirSync, readFileSync } from "fs";
7
+ import { dirname, join, resolve } from "path";
8
+ import { buildSync } from "esbuild";
9
+ var CLIENT_EXTERNALS = [
10
+ "react",
11
+ "react-dom",
12
+ "react-dom/client",
13
+ "next",
14
+ "next/*",
15
+ "pactor-app",
16
+ "pactor-app/*",
17
+ "@pactor-app/*"
18
+ ];
19
+ function bundleClient(options) {
20
+ const entry = join(options.projectDir, options.clientEntry);
21
+ if (!existsSync(entry)) {
22
+ return {
23
+ bundled: false,
24
+ css: false,
25
+ message: `client \u5165\u53E3\u4E0D\u5B58\u5728: ${options.clientEntry}\uFF08\u6309\u7EAF DSL \u6A21\u5757\u5904\u7406\uFF0C\u8DF3\u8FC7 client \u6253\u5305\uFF09`
26
+ };
27
+ }
28
+ mkdirSync(options.outDir, { recursive: true });
29
+ buildSync({
30
+ entryPoints: [entry],
31
+ bundle: true,
32
+ format: "esm",
33
+ platform: "browser",
34
+ outdir: options.outDir,
35
+ entryNames: "index",
36
+ sourcemap: true,
37
+ alias: tsconfigAliases(options.projectDir),
38
+ external: [...CLIENT_EXTERNALS],
39
+ logLevel: "silent"
40
+ });
41
+ return {
42
+ bundled: true,
43
+ css: existsSync(join(options.outDir, "index.css"))
44
+ };
45
+ }
46
+ function tsconfigAliases(projectDir) {
47
+ const file = join(projectDir, "tsconfig.json");
48
+ if (!existsSync(file)) return {};
49
+ const compilerOptions = readTsconfig(file);
50
+ const paths = compilerOptions.paths ?? {};
51
+ const baseUrl = compilerOptions.baseUrl ?? ".";
52
+ const alias = {};
53
+ for (const [key, targets] of Object.entries(paths)) {
54
+ const target = targets[0];
55
+ if (typeof target !== "string") continue;
56
+ const name = key.endsWith("/*") ? key.slice(0, -2) : key;
57
+ const rel = target.endsWith("/*") ? target.slice(0, -2) : target;
58
+ alias[name] = resolve(dirname(file), baseUrl, rel);
59
+ }
60
+ return alias;
61
+ }
62
+ function readTsconfig(file, seen = /* @__PURE__ */ new Set()) {
63
+ if (seen.has(file) || !existsSync(file)) return {};
64
+ seen.add(file);
65
+ let raw;
66
+ try {
67
+ raw = parseJsonc(readFileSync(file, "utf8"));
68
+ } catch {
69
+ return {};
70
+ }
71
+ let base2 = {};
72
+ if (typeof raw.extends === "string" && raw.extends.startsWith(".")) {
73
+ base2 = readTsconfig(resolve(dirname(file), raw.extends), seen);
74
+ }
75
+ const own = raw.compilerOptions ?? {};
76
+ return {
77
+ baseUrl: own.baseUrl ?? base2.baseUrl,
78
+ paths: { ...base2.paths ?? {}, ...own.paths ?? {} }
79
+ };
80
+ }
81
+ function parseJsonc(text) {
82
+ let out = "";
83
+ let inString = false;
84
+ let i = 0;
85
+ while (i < text.length) {
86
+ const c = text[i];
87
+ if (inString) {
88
+ out += c;
89
+ if (c === "\\") {
90
+ out += text[i + 1] ?? "";
91
+ i += 2;
92
+ continue;
93
+ }
94
+ if (c === '"') inString = false;
95
+ i += 1;
96
+ continue;
97
+ }
98
+ if (c === '"') {
99
+ inString = true;
100
+ out += c;
101
+ i += 1;
102
+ continue;
103
+ }
104
+ if (c === "/" && text[i + 1] === "/") {
105
+ while (i < text.length && text[i] !== "\n") i += 1;
106
+ continue;
107
+ }
108
+ if (c === "/" && text[i + 1] === "*") {
109
+ i += 2;
110
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i += 1;
111
+ i += 2;
112
+ continue;
113
+ }
114
+ out += c;
115
+ i += 1;
116
+ }
117
+ return JSON.parse(out.replace(/,(\s*[}\]])/g, "$1"));
118
+ }
119
+
120
+ // src/collect.ts
121
+ import { readdirSync } from "fs";
122
+ import { join as join2, relative } from "path";
123
+ function collectModuleSource(yamlRoot) {
124
+ const pages = /* @__PURE__ */ new Map();
125
+ const layouts = [];
126
+ const fragments = [];
127
+ const warnings = [];
128
+ const walk = (dir) => {
129
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
130
+ const full = join2(dir, entry.name);
131
+ if (entry.isDirectory()) {
132
+ walk(full);
133
+ continue;
134
+ }
135
+ if (!entry.name.endsWith(".yaml") || entry.name === "sitemap.yaml") {
136
+ continue;
137
+ }
138
+ const rel = relative(yamlRoot, full);
139
+ if (entry.name.endsWith(".layout.yaml")) {
140
+ layouts.push(rel);
141
+ continue;
142
+ }
143
+ if (entry.name.endsWith(".fragment.yaml")) {
144
+ fragments.push(rel);
145
+ continue;
146
+ }
147
+ let id = entry.name.slice(0, -".yaml".length);
148
+ if (id.endsWith(".page")) {
149
+ id = id.slice(0, -".page".length);
150
+ }
151
+ if (pages.has(id)) {
152
+ warnings.push(`\u9875\u9762 id \u91CD\u590D: ${id}\uFF08${rel} \u8986\u76D6 ${pages.get(id)}\uFF09`);
153
+ }
154
+ pages.set(id, rel);
155
+ }
156
+ };
157
+ walk(yamlRoot);
158
+ layouts.sort();
159
+ fragments.sort();
160
+ return { yamlRoot, pages, layouts, fragments, warnings };
161
+ }
162
+
163
+ // src/compile-styles.ts
164
+ import { execFileSync } from "child_process";
165
+ import {
166
+ appendFileSync,
167
+ existsSync as existsSync2,
168
+ mkdirSync as mkdirSync2,
169
+ mkdtempSync,
170
+ readFileSync as readFileSync2,
171
+ rmSync,
172
+ writeFileSync
173
+ } from "fs";
174
+ import { tmpdir } from "os";
175
+ import { dirname as dirname2, join as join3 } from "path";
176
+ function compileTailwind(options) {
177
+ const warnings = [];
178
+ if (!options.tailwind.enabled) {
179
+ return { compiled: false, warnings };
180
+ }
181
+ const work = mkdtempSync(join3(tmpdir(), "pactor-tailwind-"));
182
+ try {
183
+ let input;
184
+ if (options.tailwind.input !== void 0) {
185
+ input = join3(options.projectDir, options.tailwind.input);
186
+ if (!existsSync2(input)) {
187
+ warnings.push(`tailwind.input \u4E0D\u5B58\u5728: ${options.tailwind.input}\uFF0C\u8DF3\u8FC7\u9884\u7F16\u8BD1`);
188
+ return { compiled: false, warnings };
189
+ }
190
+ } else {
191
+ const scan = (options.tailwind.scan ?? options.scanDirs).map(
192
+ (dir) => `@source "${dir.replace(/\\/g, "/")}";`
193
+ );
194
+ input = join3(work, "input.css");
195
+ writeFileSync(input, `@import "tailwindcss";
196
+ ${scan.join("\n")}
197
+ `, "utf8");
198
+ }
199
+ const out = join3(work, "tailwind.css");
200
+ execFileSync(
201
+ "npx",
202
+ ["--no-install", "tailwindcss", "--input", input, "--output", out],
203
+ { cwd: options.projectDir, stdio: "pipe" }
204
+ );
205
+ if (existsSync2(out)) {
206
+ const css = readFileSync2(out, "utf8");
207
+ if (css.trim().length > 0) {
208
+ mkdirSync2(dirname2(options.cssFile), { recursive: true });
209
+ appendFileSync(
210
+ options.cssFile,
211
+ `
212
+ /* tailwind utilities\uFF08pactor module build \u9884\u7F16\u8BD1\uFF09 */
213
+ ${css}`,
214
+ "utf8"
215
+ );
216
+ return { compiled: true, warnings };
217
+ }
218
+ }
219
+ warnings.push("tailwind \u9884\u7F16\u8BD1\u4EA7\u7269\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7");
220
+ return { compiled: false, warnings };
221
+ } catch (error) {
222
+ const firstLine = error.message.split("\n")[0] ?? "\u672A\u77E5\u9519\u8BEF";
223
+ warnings.push(`tailwind \u4E0D\u53EF\u7528\u6216\u7F16\u8BD1\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u8DF3\u8FC7\u9884\u7F16\u8BD1\uFF08${firstLine}\uFF09`);
224
+ return { compiled: false, warnings };
225
+ } finally {
226
+ rmSync(work, { recursive: true, force: true });
227
+ }
228
+ }
229
+
230
+ // src/config.ts
231
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
232
+ import { join as join4, resolve as resolve2 } from "path";
233
+
234
+ // src/profiles/innospots-web-shell.ts
235
+ var base = {
236
+ name: "innospots-web-shell",
237
+ inlineStringLayouts: true,
238
+ stripMenuPaths: true,
239
+ rewriteNavigation: { basePath: true, excludeApiUrls: true },
240
+ rewriteAssetUrls: (id, version) => `/modules/${id}/${version}/`,
241
+ forbidRootBasePath: true
242
+ };
243
+ var innospotsWebShellV1 = {
244
+ ...base,
245
+ version: 1,
246
+ forceInlinePages: true
247
+ };
248
+ var innospotsWebShellV2 = {
249
+ ...base,
250
+ version: 2,
251
+ forceInlinePages: false
252
+ };
253
+
254
+ // src/profiles/index.ts
255
+ var ProfileError = class extends Error {
256
+ };
257
+ var registry = /* @__PURE__ */ new Map([
258
+ [`${innospotsWebShellV1.name}@${innospotsWebShellV1.version}`, innospotsWebShellV1],
259
+ [`${innospotsWebShellV2.name}@${innospotsWebShellV2.version}`, innospotsWebShellV2]
260
+ ]);
261
+ var DEFAULT_TARGET = `${innospotsWebShellV2.name}@${innospotsWebShellV2.version}`;
262
+ function listProfiles() {
263
+ return [...registry.keys()];
264
+ }
265
+ function getProfile(id) {
266
+ const profile = registry.get(id);
267
+ if (!profile) {
268
+ throw new ProfileError(`\u672A\u77E5 target profile: ${id}\uFF08\u53EF\u7528: ${listProfiles().join(", ")}\uFF09`);
269
+ }
270
+ return profile;
271
+ }
272
+
273
+ // src/config.ts
274
+ var ConfigError = class extends Error {
275
+ };
276
+ var CONFIG_FILE = "pactor.module.json";
277
+ var CONFIG_DEFAULTS = {
278
+ yamlRoot: "src/server/yaml",
279
+ clientEntry: "module/client.ts",
280
+ publicDir: "public",
281
+ target: DEFAULT_TARGET,
282
+ outDir: "dist-module"
283
+ };
284
+ function loadModuleConfig(projectDir, configPath) {
285
+ const file = configPath ? resolve2(projectDir, configPath) : join4(projectDir, CONFIG_FILE);
286
+ if (!existsSync3(file)) {
287
+ throw new ConfigError(`\u7F3A\u5C11\u914D\u7F6E\u6587\u4EF6: ${file}\uFF08\u53EF\u5148\u6267\u884C pactor module init \u751F\u6210\uFF09`);
288
+ }
289
+ let raw;
290
+ try {
291
+ raw = JSON.parse(readFileSync3(file, "utf8"));
292
+ } catch (error) {
293
+ throw new ConfigError(`\u914D\u7F6E\u89E3\u6790\u5931\u8D25: ${file}: ${error.message}`);
294
+ }
295
+ if (!isRecord(raw)) {
296
+ throw new ConfigError(`${file}: \u914D\u7F6E\u5FC5\u987B\u662F JSON \u5BF9\u8C61`);
297
+ }
298
+ const id = readString(raw, "id", file);
299
+ if (!id) {
300
+ throw new ConfigError(`${file}: \u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5 id\uFF08\u6A21\u5757 id\uFF0C\u8FDB\u5165 globalPageId \u524D\u7F00\uFF09`);
301
+ }
302
+ const name = readString(raw, "name", file) ?? id;
303
+ let basePath = readString(raw, "basePath", file) ?? `/${id}`;
304
+ if (!basePath.startsWith("/")) basePath = `/${basePath}`;
305
+ if (basePath.length > 1 && basePath.endsWith("/")) basePath = basePath.slice(0, -1);
306
+ const actions = raw.actions === void 0 ? [] : readStringArray(raw.actions, "actions", file);
307
+ const demo = isRecord(raw.demo) ? raw.demo : {};
308
+ const tailwind = isRecord(raw.tailwind) ? raw.tailwind : {};
309
+ const rewrite = isRecord(raw.rewrite) ? raw.rewrite : {};
310
+ return {
311
+ id,
312
+ name,
313
+ basePath,
314
+ yamlRoot: readString(raw, "yamlRoot", file) ?? CONFIG_DEFAULTS.yamlRoot,
315
+ clientEntry: readString(raw, "clientEntry", file) ?? CONFIG_DEFAULTS.clientEntry,
316
+ actions,
317
+ publicDir: readString(raw, "publicDir", file) ?? CONFIG_DEFAULTS.publicDir,
318
+ target: readString(raw, "target", file) ?? CONFIG_DEFAULTS.target,
319
+ demo: { public: readBoolean(demo, "public", file, "demo") ?? false },
320
+ tailwind: {
321
+ enabled: readBoolean(tailwind, "enabled", file, "tailwind") ?? false,
322
+ input: readString(tailwind, "input", file, "tailwind"),
323
+ scan: tailwind.scan === void 0 ? void 0 : readStringArray(tailwind.scan, "tailwind.scan", file)
324
+ },
325
+ rewrite: {
326
+ absoluteApiToRelative: readBoolean(rewrite, "absoluteApiToRelative", file, "rewrite") ?? true
327
+ },
328
+ outDir: readString(raw, "outDir", file) ?? CONFIG_DEFAULTS.outDir
329
+ };
330
+ }
331
+ function isRecord(value) {
332
+ return value !== null && typeof value === "object" && !Array.isArray(value);
333
+ }
334
+ function readString(record, key, file, parent) {
335
+ const value = record[key];
336
+ if (value === void 0) return void 0;
337
+ const label = parent ? `${parent}.${key}` : key;
338
+ if (typeof value !== "string" || value.length === 0) {
339
+ throw new ConfigError(`${file}: ${label} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
340
+ }
341
+ return value;
342
+ }
343
+ function readBoolean(record, key, file, parent) {
344
+ const value = record[key];
345
+ if (value === void 0) return void 0;
346
+ if (typeof value !== "boolean") {
347
+ throw new ConfigError(`${file}: ${parent}.${key} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
348
+ }
349
+ return value;
350
+ }
351
+ function readStringArray(value, label, file) {
352
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
353
+ throw new ConfigError(`${file}: ${label} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);
354
+ }
355
+ return value;
356
+ }
357
+
358
+ // src/emit-manifest.ts
359
+ import { createHash } from "crypto";
360
+ import {
361
+ cpSync,
362
+ existsSync as existsSync4,
363
+ mkdirSync as mkdirSync3,
364
+ readdirSync as readdirSync2,
365
+ readFileSync as readFileSync4,
366
+ statSync,
367
+ writeFileSync as writeFileSync2
368
+ } from "fs";
369
+ import { createRequire } from "module";
370
+ import { join as join5 } from "path";
371
+ import { stringify } from "yaml";
372
+ function emitModule(input) {
373
+ const dslResult = emitDsl(input);
374
+ const manifest = writeManifest(input);
375
+ return { ...dslResult, manifest };
376
+ }
377
+ function emitDsl(input) {
378
+ const { config, projectDir, outDir, site, pages } = input;
379
+ const dslDir = join5(outDir, "dsl");
380
+ mkdirSync3(dslDir, { recursive: true });
381
+ writeFileSync2(join5(dslDir, "sitemap.yaml"), stringify(site, { lineWidth: 0 }), "utf8");
382
+ for (const [id, dsl] of pages) {
383
+ writeFileSync2(join5(dslDir, `${id}.yaml`), stringify(dsl, { lineWidth: 0 }), "utf8");
384
+ }
385
+ const publicDir = join5(projectDir, config.publicDir);
386
+ const assetsDir = join5(outDir, "assets");
387
+ if (existsSync4(publicDir)) {
388
+ cpSync(publicDir, assetsDir, { recursive: true });
389
+ } else {
390
+ mkdirSync3(assetsDir, { recursive: true });
391
+ }
392
+ const assets = hashAssets(assetsDir, assetsDir);
393
+ writeFileSync2(
394
+ join5(outDir, "module-assets.json"),
395
+ `${JSON.stringify({ module: config.id, assets }, null, 2)}
396
+ `,
397
+ "utf8"
398
+ );
399
+ return { assetCount: assets.length };
400
+ }
401
+ function writeManifest(input) {
402
+ const { config, projectDir, outDir } = input;
403
+ const version = readProjectVersion(projectDir);
404
+ const manifest = {
405
+ schemaVersion: "1",
406
+ id: config.id,
407
+ name: config.name,
408
+ version,
409
+ pactorVersionRange: resolvePactorVersionRange(projectDir, input.warnings),
410
+ basePath: config.basePath,
411
+ sitemap: "dsl/sitemap.yaml",
412
+ dslRoot: "dsl",
413
+ assetsRoot: "assets",
414
+ ...input.clientEntry ? { clientEntry: input.clientEntry } : {},
415
+ ...input.styles ? { styles: input.styles } : {}
416
+ };
417
+ writeFileSync2(
418
+ join5(outDir, "manifest.json"),
419
+ `${JSON.stringify(manifest, null, 2)}
420
+ `,
421
+ "utf8"
422
+ );
423
+ return manifest;
424
+ }
425
+ function readProjectVersion(projectDir) {
426
+ const pkgFile = join5(projectDir, "package.json");
427
+ if (!existsSync4(pkgFile)) return "0.0.0";
428
+ try {
429
+ const pkg = JSON.parse(readFileSync4(pkgFile, "utf8"));
430
+ return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "0.0.0";
431
+ } catch {
432
+ return "0.0.0";
433
+ }
434
+ }
435
+ function resolvePactorVersionRange(projectDir, warnings) {
436
+ try {
437
+ const require2 = createRequire(join5(projectDir, "package.json"));
438
+ for (const name of ["pactor-app/package.json", "@pactor-app/app/package.json"]) {
439
+ try {
440
+ const pkgFile = require2.resolve(name);
441
+ const pkg2 = JSON.parse(readFileSync4(pkgFile, "utf8"));
442
+ if (typeof pkg2.version === "string" && pkg2.version.length > 0) {
443
+ return `^${pkg2.version}`;
444
+ }
445
+ } catch {
446
+ }
447
+ }
448
+ const pkg = JSON.parse(readFileSync4(join5(projectDir, "package.json"), "utf8"));
449
+ for (const section of ["dependencies", "peerDependencies", "devDependencies"]) {
450
+ const range = pkg[section]?.["pactor-app"];
451
+ if (typeof range === "string" && range.length > 0) {
452
+ return range;
453
+ }
454
+ }
455
+ } catch {
456
+ }
457
+ warnings.push("\u672A\u80FD\u63A8\u5BFC\u76EE\u6807\u5DE5\u7A0B\u7684 pactor-app \u7248\u672C\uFF0CpactorVersionRange \u56DE\u9000\u4E3A ^1.54.0");
458
+ return "^1.54.0";
459
+ }
460
+ function hashAssets(dir, root) {
461
+ const entries = [];
462
+ if (!existsSync4(dir)) return entries;
463
+ for (const entry of readdirSync2(dir, { withFileTypes: true }).sort(
464
+ (a, b) => a.name.localeCompare(b.name)
465
+ )) {
466
+ const full = join5(dir, entry.name);
467
+ if (entry.isDirectory()) {
468
+ entries.push(...hashAssets(full, root));
469
+ continue;
470
+ }
471
+ if (!entry.isFile()) continue;
472
+ const content = readFileSync4(full);
473
+ entries.push({
474
+ path: full.slice(root.length + 1).split("\\").join("/"),
475
+ sha256: createHash("sha256").update(content).digest("hex"),
476
+ size: statSync(full).size
477
+ });
478
+ }
479
+ return entries;
480
+ }
481
+
482
+ // src/expand-include.ts
483
+ import { readFileSync as readFileSync5 } from "fs";
484
+ import { join as join6 } from "path";
485
+ import { parseDocument } from "yaml";
486
+ var INCLUDE_KEY = "$include";
487
+ var IncludeError = class extends Error {
488
+ };
489
+ function parseYamlFile(file) {
490
+ const doc = parseDocument(readFileSync5(file, "utf8"));
491
+ if (doc.errors.length > 0) {
492
+ throw new IncludeError(
493
+ `${file}: ${doc.errors.map((error) => error.message).join("; ")}`
494
+ );
495
+ }
496
+ return doc.toJS();
497
+ }
498
+ function loadYamlExpanded(yamlRoot, relPath, stack = []) {
499
+ if (stack.includes(relPath)) {
500
+ throw new IncludeError(`\u5FAA\u73AF\u5F15\u7528: ${[...stack, relPath].join(" -> ")}`);
501
+ }
502
+ const value = parseYamlFile(join6(yamlRoot, relPath));
503
+ return expandIncludes(value, { yamlRoot, current: relPath, stack: [...stack, relPath] });
504
+ }
505
+ function expandIncludes(value, ctx) {
506
+ if (Array.isArray(value)) {
507
+ return value.map((item) => expandIncludes(item, ctx));
508
+ }
509
+ if (value === null || typeof value !== "object") {
510
+ return value;
511
+ }
512
+ const record = value;
513
+ if (INCLUDE_KEY in record) {
514
+ if (Object.keys(record).length > 1) {
515
+ throw new IncludeError(`${ctx.current}: $include \u4E0D\u80FD\u4E0E\u5176\u4ED6\u5B57\u6BB5\u5E76\u5217`);
516
+ }
517
+ const target = record[INCLUDE_KEY];
518
+ if (typeof target !== "string") {
519
+ throw new IncludeError(`${ctx.current}: $include \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
520
+ }
521
+ return loadYamlExpanded(ctx.yamlRoot, target, ctx.stack);
522
+ }
523
+ return Object.fromEntries(
524
+ Object.entries(record).map(([key, item]) => [key, expandIncludes(item, ctx)])
525
+ );
526
+ }
527
+
528
+ // src/rewrite-urls.ts
529
+ import { existsSync as existsSync5, statSync as statSync2 } from "fs";
530
+ import { join as join7 } from "path";
531
+ var NAVIGATION_ACTIONS = /* @__PURE__ */ new Set(["navigate", "app.reload"]);
532
+ var ABSOLUTE_API_RE = /^https?:\/\/[^/]+(\/api\/.+)$/;
533
+ function rewriteDslUrls(value, ctx) {
534
+ const warnings = [];
535
+ const walk = (node) => {
536
+ if (typeof node === "string") {
537
+ return rewriteString(node, ctx, warnings);
538
+ }
539
+ if (Array.isArray(node)) {
540
+ return node.map((item) => walk(item));
541
+ }
542
+ if (node === null || typeof node !== "object") {
543
+ return node;
544
+ }
545
+ const record = node;
546
+ const next = {};
547
+ for (const [key, item] of Object.entries(record)) {
548
+ next[key] = walk(item);
549
+ }
550
+ if (ctx.rewriteNavigation && isNavigationAction(record)) {
551
+ const params = next.params;
552
+ if (isRecord2(params) && typeof params.to === "string") {
553
+ const rewritten = rewriteNavigationTo(params.to, ctx);
554
+ if (rewritten !== void 0) {
555
+ params.to = rewritten;
556
+ }
557
+ }
558
+ }
559
+ return next;
560
+ };
561
+ return { value: walk(value), warnings };
562
+ }
563
+ function isNavigationAction(record) {
564
+ return typeof record.action === "string" && NAVIGATION_ACTIONS.has(record.action);
565
+ }
566
+ function rewriteNavigationTo(to, ctx) {
567
+ if (!to.startsWith("/") || to.startsWith("//")) return void 0;
568
+ if (to.startsWith("/api/")) return void 0;
569
+ if (to === ctx.basePath || to.startsWith(`${ctx.basePath}/`)) return void 0;
570
+ const hit = [ctx.loginPath, ...ctx.pagePaths].some(
571
+ (pattern) => pattern.length > 0 && matchPathPattern(pattern, to)
572
+ );
573
+ if (!hit) return void 0;
574
+ return `${ctx.basePath}${to}`;
575
+ }
576
+ function matchPathPattern(pattern, value) {
577
+ const patternSegs = pattern.split("?")[0].split("/").filter(Boolean);
578
+ const valueSegs = value.split("?")[0].split("/").filter(Boolean);
579
+ if (patternSegs.length !== valueSegs.length) return false;
580
+ return patternSegs.every((seg, index) => seg.startsWith(":") || seg === valueSegs[index]);
581
+ }
582
+ function rewriteString(value, ctx, warnings) {
583
+ const absoluteApi = ABSOLUTE_API_RE.exec(value);
584
+ if (absoluteApi) {
585
+ warnings.push(
586
+ `\u7EDD\u5BF9 API \u5730\u5740: ${value}${ctx.absoluteApiToRelative ? "\uFF08\u5DF2\u91CD\u5199\u4E3A\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u9700\u5E95\u5EA7 BFF/\u4EE3\u7406\u627F\u63A5\uFF09" : ""}`
587
+ );
588
+ return ctx.absoluteApiToRelative ? absoluteApi[1] : value;
589
+ }
590
+ if (!ctx.rewriteAssets) return value;
591
+ if (!value.startsWith("/") || value.startsWith("//")) return value;
592
+ if (value.startsWith("/api/")) return value;
593
+ if (value === ctx.basePath || value.startsWith(`${ctx.basePath}/`)) return value;
594
+ const isPagePath = [ctx.loginPath, ...ctx.pagePaths].some(
595
+ (pattern) => pattern.length > 0 && matchPathPattern(pattern, value)
596
+ );
597
+ if (isPagePath) return value;
598
+ if (!isPublicFile(ctx.publicDir, value)) return value;
599
+ return `${ctx.assetPrefix}${value}`;
600
+ }
601
+ function isPublicFile(publicDir, urlPath) {
602
+ const file = join7(publicDir, urlPath);
603
+ try {
604
+ return existsSync5(file) && statSync2(file).isFile();
605
+ } catch {
606
+ return false;
607
+ }
608
+ }
609
+ function isRecord2(value) {
610
+ return value !== null && typeof value === "object" && !Array.isArray(value);
611
+ }
612
+
613
+ // src/transform-sitemap.ts
614
+ function transformSitemap(site, options) {
615
+ const { profile, demoPublic, loadLayout } = options;
616
+ const next = { ...site };
617
+ const rawLayouts = isRecord3(site.layouts) ? site.layouts : {};
618
+ const layouts = {};
619
+ for (const [name, def] of Object.entries(rawLayouts)) {
620
+ layouts[name] = typeof def === "string" && profile.inlineStringLayouts ? loadLayout(def) : def;
621
+ }
622
+ next.layouts = layouts;
623
+ const rawPages = Array.isArray(site.pages) ? site.pages : [];
624
+ next.pages = rawPages.map((page) => {
625
+ const nextPage = { ...page };
626
+ if (profile.forceInlinePages) {
627
+ delete nextPage.lazy;
628
+ }
629
+ if (demoPublic) {
630
+ nextPage.public = true;
631
+ }
632
+ return nextPage;
633
+ });
634
+ const rawMenus = Array.isArray(site.menus) ? site.menus : [];
635
+ const transformMenu = (menu) => {
636
+ const nextMenu = { ...menu };
637
+ if (profile.stripMenuPaths) {
638
+ delete nextMenu.path;
639
+ }
640
+ if (demoPublic) {
641
+ nextMenu.public = true;
642
+ }
643
+ if (Array.isArray(nextMenu.children)) {
644
+ nextMenu.children = nextMenu.children.map(
645
+ (child) => isRecord3(child) ? transformMenu(child) : child
646
+ );
647
+ }
648
+ return nextMenu;
649
+ };
650
+ next.menus = rawMenus.map((menu) => isRecord3(menu) ? transformMenu(menu) : menu);
651
+ return {
652
+ site: next,
653
+ layoutCount: Object.keys(layouts).length,
654
+ pageCount: rawPages.length,
655
+ menuCount: rawMenus.length
656
+ };
657
+ }
658
+ function isRecord3(value) {
659
+ return value !== null && typeof value === "object" && !Array.isArray(value);
660
+ }
661
+
662
+ // src/validate.ts
663
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
664
+ import { join as join8, relative as relative2, resolve as resolve3 } from "path";
665
+ import { parseDocument as parseDocument2 } from "yaml";
666
+ var REQUIRED_MANIFEST_FIELDS = [
667
+ "schemaVersion",
668
+ "id",
669
+ "name",
670
+ "version",
671
+ "pactorVersionRange",
672
+ "basePath",
673
+ "sitemap",
674
+ "dslRoot",
675
+ "assetsRoot"
676
+ ];
677
+ function validateModule(options = {}) {
678
+ const projectDir = resolve3(options.dir ?? process.cwd());
679
+ const errors = [];
680
+ const warnings = [];
681
+ let config;
682
+ if (options.moduleDir === void 0) {
683
+ try {
684
+ config = loadModuleConfig(projectDir, options.config);
685
+ } catch (error) {
686
+ errors.push(error.message);
687
+ return { ok: false, errors, warnings };
688
+ }
689
+ } else {
690
+ try {
691
+ config = loadModuleConfig(projectDir, options.config);
692
+ } catch {
693
+ config = void 0;
694
+ }
695
+ }
696
+ const moduleDir = options.moduleDir ?? join8(projectDir, config?.outDir ?? CONFIG_DEFAULTS.outDir);
697
+ const manifestFile = join8(moduleDir, "manifest.json");
698
+ if (!existsSync6(manifestFile)) {
699
+ errors.push(`\u4EA7\u7269\u4E0D\u5B58\u5728: ${manifestFile}\uFF08\u5148\u6267\u884C pactor module build\uFF09`);
700
+ return { ok: false, errors, warnings };
701
+ }
702
+ let manifest;
703
+ try {
704
+ manifest = JSON.parse(readFileSync6(manifestFile, "utf8"));
705
+ } catch (error) {
706
+ errors.push(`manifest.json \u89E3\u6790\u5931\u8D25: ${error.message}`);
707
+ return { ok: false, errors, warnings };
708
+ }
709
+ for (const field of REQUIRED_MANIFEST_FIELDS) {
710
+ if (!(field in manifest)) {
711
+ errors.push(`manifest \u7F3A\u5C11\u5B57\u6BB5: ${field}`);
712
+ }
713
+ }
714
+ if (manifest.schemaVersion !== "1") {
715
+ errors.push(`manifest.schemaVersion \u5FC5\u987B\u4E3A "1"\uFF08\u5F53\u524D: ${JSON.stringify(manifest.schemaVersion)}\uFF09`);
716
+ }
717
+ const dslRoot = typeof manifest.dslRoot === "string" ? manifest.dslRoot : "dsl";
718
+ const sitemapRel = typeof manifest.sitemap === "string" ? manifest.sitemap : "dsl/sitemap.yaml";
719
+ const sitemapFile = join8(moduleDir, sitemapRel);
720
+ if (!existsSync6(sitemapFile)) {
721
+ errors.push(`sitemap \u4E0D\u5B58\u5728: ${sitemapRel}`);
722
+ } else {
723
+ const doc = parseDocument2(readFileSync6(sitemapFile, "utf8"));
724
+ if (doc.errors.length > 0) {
725
+ errors.push(`sitemap YAML \u89E3\u6790\u5931\u8D25: ${doc.errors.map((e) => e.message).join("; ")}`);
726
+ } else {
727
+ const site = doc.toJS();
728
+ const pages = Array.isArray(site.pages) ? site.pages : [];
729
+ for (const page of pages) {
730
+ const id = page.id;
731
+ if (typeof id !== "string") {
732
+ errors.push("sitemap.pages \u5B58\u5728\u7F3A\u5C11 id \u7684\u9875\u9762\u9879");
733
+ continue;
734
+ }
735
+ if (findPageDslFile(join8(moduleDir, dslRoot), id) === null) {
736
+ errors.push(`\u9875\u9762 ${id} \u7F3A\u5C11 DSL \u6587\u4EF6\uFF08${dslRoot}/ \u4E0B\u672A\u627E\u5230 ${id}.yaml\uFF09`);
737
+ }
738
+ }
739
+ const layouts = site.layouts;
740
+ if (layouts !== null && typeof layouts === "object" && !Array.isArray(layouts)) {
741
+ for (const [name, def] of Object.entries(layouts)) {
742
+ if (typeof def === "string") {
743
+ errors.push(`\u5E03\u5C40 ${name} \u4ECD\u662F\u5B57\u7B26\u4E32\u5F15\u7528\uFF08target \u8981\u6C42\u5185\u8054\u4E3A\u5BF9\u8C61\uFF09`);
744
+ }
745
+ }
746
+ }
747
+ }
748
+ }
749
+ const dslDir = join8(moduleDir, dslRoot);
750
+ for (const file of listFiles(dslDir)) {
751
+ if (readFileSync6(file, "utf8").includes(INCLUDE_KEY)) {
752
+ errors.push(`$include \u6B8B\u7559: ${relative2(moduleDir, file)}`);
753
+ }
754
+ }
755
+ if (typeof manifest.clientEntry === "string") {
756
+ const clientFile = join8(moduleDir, manifest.clientEntry);
757
+ if (!existsSync6(clientFile)) {
758
+ errors.push(`client bundle \u4E0D\u5B58\u5728: ${manifest.clientEntry}`);
759
+ } else {
760
+ const text = readFileSync6(clientFile, "utf8");
761
+ if (/\bvar React\b|react\.production\.min|__REACT_DEVTOOLS_GLOBAL_HOOK__/.test(text)) {
762
+ errors.push("client bundle \u7591\u4F3C\u5185\u5D4C react\uFF08react \u5FC5\u987B\u4FDD\u6301 external\uFF0C\u907F\u514D\u53CC\u5B9E\u4F8B\uFF09");
763
+ }
764
+ if (/node_modules\/@pactor-app\//.test(text) || /node_modules\/pactor-app\//.test(text)) {
765
+ errors.push("client bundle \u7591\u4F3C\u5185\u5D4C pactor \u8FD0\u884C\u65F6\u4EE3\u7801\uFF08@pactor-app/* \u5FC5\u987B\u4FDD\u6301 external\uFF09");
766
+ }
767
+ const sizeKb = Math.round(statSync3(clientFile).size / 1024);
768
+ if (sizeKb > 512) {
769
+ warnings.push(`client bundle \u4F53\u79EF\u8F83\u5927\uFF08${sizeKb}KB\uFF09\uFF0C\u8BF7\u786E\u8BA4 react / pactor \u5DF2 external`);
770
+ }
771
+ }
772
+ }
773
+ const pkgFile = join8(projectDir, "package.json");
774
+ if (existsSync6(pkgFile)) {
775
+ try {
776
+ const pkg = JSON.parse(readFileSync6(pkgFile, "utf8"));
777
+ const exportsMap = pkg.exports ?? {};
778
+ if (exportsMap["./manifest"] === void 0) {
779
+ errors.push('package.json exports \u7F3A\u5C11 "./manifest"\uFF08Shell \u88C5\u914D\u5668\u7ECF require.resolve("<pkg>/manifest") \u5B9A\u4F4D\uFF09');
780
+ }
781
+ if (typeof manifest.clientEntry === "string" && exportsMap["./client"] === void 0) {
782
+ errors.push('package.json exports \u7F3A\u5C11 "./client"\uFF08client-registry \u9759\u6001 import "<pkg>/client"\uFF09');
783
+ }
784
+ if (typeof pkg.version === "string" && typeof manifest.version === "string" && pkg.version !== manifest.version) {
785
+ warnings.push(
786
+ `manifest.version (${manifest.version}) \u4E0E package.json version (${pkg.version}) \u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 build`
787
+ );
788
+ }
789
+ } catch {
790
+ warnings.push("package.json \u89E3\u6790\u5931\u8D25\uFF0C\u8DF3\u8FC7 exports \u68C0\u67E5");
791
+ }
792
+ } else {
793
+ warnings.push("\u672A\u627E\u5230 package.json\uFF0C\u8DF3\u8FC7 exports \u68C0\u67E5");
794
+ }
795
+ return { ok: errors.length === 0, errors, warnings };
796
+ }
797
+ function findPageDslFile(dslRootDir, pageId) {
798
+ if (!existsSync6(dslRootDir)) return null;
799
+ const queue = [dslRootDir];
800
+ while (queue.length > 0) {
801
+ const dir = queue.pop();
802
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
803
+ const full = join8(dir, entry.name);
804
+ if (entry.isDirectory()) {
805
+ queue.push(full);
806
+ continue;
807
+ }
808
+ if (entry.name === `${pageId}.yaml`) return full;
809
+ }
810
+ }
811
+ return null;
812
+ }
813
+ function listFiles(dir) {
814
+ if (!existsSync6(dir)) return [];
815
+ const files = [];
816
+ const walk = (target) => {
817
+ for (const entry of readdirSync3(target, { withFileTypes: true })) {
818
+ const full = join8(target, entry.name);
819
+ if (entry.isDirectory()) {
820
+ walk(full);
821
+ } else if (entry.isFile()) {
822
+ files.push(full);
823
+ }
824
+ }
825
+ };
826
+ walk(dir);
827
+ return files;
828
+ }
829
+
830
+ // src/build.ts
831
+ var ModuleBuildError = class extends Error {
832
+ constructor(message, errors) {
833
+ super(message);
834
+ this.errors = errors;
835
+ }
836
+ };
837
+ function buildModule(options = {}) {
838
+ const projectDir = resolve4(options.dir ?? process.cwd());
839
+ const config = loadModuleConfig(projectDir, options.config);
840
+ const profile = getProfile(config.target);
841
+ if (profile.forbidRootBasePath && config.basePath === "/") {
842
+ throw new ModuleBuildError(
843
+ `target ${config.target} \u7981\u6B62 basePath: "/"\uFF08Shell joinPublicPath \u4F1A\u4EA7\u751F "//login" \u8FD9\u7C7B\u574F\u8DEF\u5F84\uFF09`,
844
+ [`basePath: "/" \u4E0E profile ${config.target} \u7684 forbidRootBasePath \u51B2\u7A81\uFF0C\u8BF7\u6539\u7528\u5982 /${config.id} \u7684\u524D\u7F00`]
845
+ );
846
+ }
847
+ const yamlRoot = join9(projectDir, config.yamlRoot);
848
+ if (!existsSync7(yamlRoot)) {
849
+ throw new ModuleBuildError(`yamlRoot \u4E0D\u5B58\u5728: ${config.yamlRoot}`, [
850
+ `yamlRoot \u4E0D\u5B58\u5728: ${yamlRoot}`
851
+ ]);
852
+ }
853
+ const warnings = [];
854
+ const collected = collectModuleSource(yamlRoot);
855
+ warnings.push(...collected.warnings);
856
+ let site;
857
+ try {
858
+ site = loadYamlExpanded(yamlRoot, "sitemap.yaml");
859
+ } catch (error) {
860
+ const message = `sitemap \u5C55\u5F00\u5931\u8D25: ${error.message}`;
861
+ throw new ModuleBuildError(message, [message]);
862
+ }
863
+ const sitePages = Array.isArray(site.pages) ? site.pages : [];
864
+ const errors = [];
865
+ const pageDsls = /* @__PURE__ */ new Map();
866
+ for (const page of sitePages) {
867
+ const id = page.id;
868
+ if (typeof id !== "string") {
869
+ errors.push("sitemap.pages \u5B58\u5728\u7F3A\u5C11 id \u7684\u9875\u9762\u9879");
870
+ continue;
871
+ }
872
+ const rel = collected.pages.get(id);
873
+ if (rel === void 0) {
874
+ errors.push(`\u9875\u9762 DSL \u7F3A\u5931: ${id}\uFF08${config.yamlRoot} \u4E0B\u672A\u627E\u5230 ${id}.yaml / ${id}.page.yaml\uFF09`);
875
+ continue;
876
+ }
877
+ try {
878
+ pageDsls.set(id, loadYamlExpanded(yamlRoot, rel));
879
+ } catch (error) {
880
+ errors.push(`\u9875\u9762 ${id} \u5C55\u5F00\u5931\u8D25: ${error.message}`);
881
+ }
882
+ }
883
+ if (errors.length > 0) {
884
+ throw new ModuleBuildError(`\u6A21\u5757\u6784\u5EFA\u5931\u8D25\uFF08${errors.length} \u4E2A\u9519\u8BEF\uFF09`, errors);
885
+ }
886
+ const transformed = transformSitemap(site, {
887
+ profile,
888
+ demoPublic: config.demo.public,
889
+ loadLayout: (rel) => loadYamlExpanded(yamlRoot, rel)
890
+ });
891
+ const version = readProjectVersion(projectDir);
892
+ const siteAuth = site.auth !== null && typeof site.auth === "object" && !Array.isArray(site.auth) ? site.auth : {};
893
+ const rewriteCtx = {
894
+ basePath: config.basePath,
895
+ pagePaths: sitePages.map((page) => page.path).filter((path) => typeof path === "string"),
896
+ loginPath: typeof siteAuth.loginPath === "string" ? siteAuth.loginPath : "/login",
897
+ publicDir: join9(projectDir, config.publicDir),
898
+ rewriteNavigation: profile.rewriteNavigation.basePath,
899
+ rewriteAssets: true,
900
+ assetPrefix: profile.rewriteAssetUrls(config.id, version).replace(/\/+$/, ""),
901
+ absoluteApiToRelative: config.rewrite.absoluteApiToRelative
902
+ };
903
+ const sitemapRewrite = rewriteDslUrls(transformed.site, rewriteCtx);
904
+ warnings.push(...sitemapRewrite.warnings);
905
+ const rewrittenPages = /* @__PURE__ */ new Map();
906
+ for (const [id, dsl] of pageDsls) {
907
+ const result = rewriteDslUrls(dsl, rewriteCtx);
908
+ warnings.push(...result.warnings.map((warning) => `${id}: ${warning}`));
909
+ rewrittenPages.set(id, result.value);
910
+ }
911
+ const outDir = join9(projectDir, config.outDir);
912
+ const temp = mkdtempSync2(join9(projectDir, ".module-pack-"));
913
+ try {
914
+ const bundle = bundleClient({
915
+ projectDir,
916
+ clientEntry: config.clientEntry,
917
+ outDir: join9(temp, "client")
918
+ });
919
+ if (bundle.message !== void 0) {
920
+ warnings.push(bundle.message);
921
+ }
922
+ const emitted = emitDsl({
923
+ config,
924
+ projectDir,
925
+ outDir: temp,
926
+ site: sitemapRewrite.value,
927
+ pages: rewrittenPages
928
+ });
929
+ if (bundle.bundled && config.tailwind.enabled) {
930
+ const tailwind = compileTailwind({
931
+ projectDir,
932
+ tailwind: config.tailwind,
933
+ scanDirs: [join9(temp, "dsl"), dirname3(join9(projectDir, config.clientEntry))],
934
+ cssFile: join9(temp, "client", "index.css")
935
+ });
936
+ warnings.push(...tailwind.warnings);
937
+ }
938
+ const hasCss = existsSync7(join9(temp, "client", "index.css"));
939
+ writeManifest({
940
+ config,
941
+ projectDir,
942
+ outDir: temp,
943
+ clientEntry: bundle.bundled ? "client/index.js" : void 0,
944
+ styles: bundle.bundled && hasCss ? ["client/index.css"] : void 0,
945
+ warnings
946
+ });
947
+ const validation = validateModule({ dir: projectDir, config: options.config, moduleDir: temp });
948
+ warnings.push(...validation.warnings);
949
+ if (!validation.ok) {
950
+ throw new ModuleBuildError(
951
+ `\u4EA7\u7269\u6821\u9A8C\u5931\u8D25\uFF08${validation.errors.length} \u4E2A\u9519\u8BEF\uFF09`,
952
+ validation.errors
953
+ );
954
+ }
955
+ rmSync2(outDir, { recursive: true, force: true });
956
+ mkdirSync4(dirname3(outDir), { recursive: true });
957
+ renameSync(temp, outDir);
958
+ return {
959
+ outDir,
960
+ stats: {
961
+ pages: transformed.pageCount,
962
+ menus: transformed.menuCount,
963
+ layouts: transformed.layoutCount,
964
+ assets: emitted.assetCount
965
+ },
966
+ clientBundled: bundle.bundled,
967
+ warnings
968
+ };
969
+ } catch (error) {
970
+ rmSync2(temp, { recursive: true, force: true });
971
+ throw error;
972
+ }
973
+ }
974
+
975
+ // src/init.ts
976
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync4, writeFileSync as writeFileSync3 } from "fs";
977
+ import { join as join10, resolve as resolve5 } from "path";
978
+ function initModule(options = {}) {
979
+ const projectDir = resolve5(options.dir ?? process.cwd());
980
+ const result = { created: [], skipped: [], warnings: [] };
981
+ const pkgFile = join10(projectDir, "package.json");
982
+ if (!existsSync8(pkgFile)) {
983
+ throw new Error(`\u672A\u627E\u5230 package.json: ${projectDir}\uFF08\u8BF7\u5728\u76EE\u6807\u5DE5\u7A0B\u6839\u6267\u884C\uFF09`);
984
+ }
985
+ const pkg = JSON.parse(readFileSync7(pkgFile, "utf8"));
986
+ const id = deriveModuleId(pkg.name);
987
+ const actions = discoverActions(projectDir);
988
+ const hasComponents = discoverComponents(projectDir);
989
+ const configFile = join10(projectDir, CONFIG_FILE);
990
+ if (existsSync8(configFile)) {
991
+ result.skipped.push(`${CONFIG_FILE}\uFF08\u5DF2\u5B58\u5728\uFF09`);
992
+ } else {
993
+ const config = {
994
+ id,
995
+ name: typeof pkg.name === "string" ? pkg.name : id,
996
+ basePath: `/${id}`,
997
+ yamlRoot: CONFIG_DEFAULTS.yamlRoot,
998
+ clientEntry: CONFIG_DEFAULTS.clientEntry,
999
+ actions: actions.map((action) => action.specifier.replace(/^\.\.\//, "")),
1000
+ publicDir: CONFIG_DEFAULTS.publicDir,
1001
+ target: DEFAULT_TARGET,
1002
+ demo: { public: false }
1003
+ };
1004
+ writeFileSync3(configFile, `${JSON.stringify(config, null, 2)}
1005
+ `, "utf8");
1006
+ result.created.push(CONFIG_FILE);
1007
+ }
1008
+ const clientFile = join10(projectDir, CONFIG_DEFAULTS.clientEntry);
1009
+ if (existsSync8(clientFile)) {
1010
+ result.skipped.push(`${CONFIG_DEFAULTS.clientEntry}\uFF08\u5DF2\u5B58\u5728\uFF09`);
1011
+ } else {
1012
+ mkdirSync5(join10(projectDir, "module"), { recursive: true });
1013
+ writeFileSync3(clientFile, renderClientEntry(hasComponents, actions), "utf8");
1014
+ result.created.push(CONFIG_DEFAULTS.clientEntry);
1015
+ }
1016
+ injectPackageJson(pkg, result);
1017
+ writeFileSync3(pkgFile, `${JSON.stringify(pkg, null, 2)}
1018
+ `, "utf8");
1019
+ result.created.push("package.json\uFF08\u6CE8\u5165 files / exports / scripts / peerDependencies\uFF09");
1020
+ return result;
1021
+ }
1022
+ function deriveModuleId(name) {
1023
+ const base2 = typeof name === "string" && name.length > 0 ? name : "module";
1024
+ const withoutScope = base2.startsWith("@") ? base2.slice(base2.indexOf("/") + 1) : base2;
1025
+ const kebab = withoutScope.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1026
+ return kebab.length > 0 ? kebab : "module";
1027
+ }
1028
+ function discoverActions(projectDir) {
1029
+ const dir = join10(projectDir, "src/pactor");
1030
+ if (!existsSync8(dir)) return [];
1031
+ const sources = [];
1032
+ for (const entry of readdirSync4(dir, { withFileTypes: true }).sort(
1033
+ (a, b) => a.name.localeCompare(b.name)
1034
+ )) {
1035
+ if (!entry.isFile() || !entry.name.endsWith("-actions.ts")) continue;
1036
+ const file = join10(dir, entry.name);
1037
+ const content = readFileSync7(file, "utf8");
1038
+ const exports = [...content.matchAll(/export\s+const\s+([A-Za-z_$][\w$]*)/g)].map(
1039
+ (match) => match[1]
1040
+ );
1041
+ if (exports.length === 0) continue;
1042
+ sources.push({
1043
+ specifier: `../src/pactor/${entry.name.replace(/\.ts$/, "")}`,
1044
+ exports
1045
+ });
1046
+ }
1047
+ return sources;
1048
+ }
1049
+ function discoverComponents(projectDir) {
1050
+ const file = join10(projectDir, "src/pactor/components/index.ts");
1051
+ if (!existsSync8(file)) return false;
1052
+ return /export\s+(const|\{[^}]*)\s*pactorComponents/.test(readFileSync7(file, "utf8"));
1053
+ }
1054
+ function renderClientEntry(hasComponents, actions) {
1055
+ const imports = [];
1056
+ const spreads = [];
1057
+ if (hasComponents) {
1058
+ imports.push("import { pactorComponents } from '../src/pactor/components';");
1059
+ }
1060
+ for (const action of actions) {
1061
+ imports.push(
1062
+ `import { ${action.exports.join(", ")} } from '${action.specifier}';`
1063
+ );
1064
+ spreads.push(...action.exports.map((name) => `...${name}`));
1065
+ }
1066
+ const components = hasComponents ? "pactorComponents" : "{}";
1067
+ const actionsExpr = spreads.length > 0 ? `{ ${spreads.join(", ")} }` : "{}";
1068
+ return `// \u7531 \`pactor module init\` \u751F\u6210\uFF1A\u6A21\u5757 client \u5165\u53E3\u3002
1069
+ // Shell \u88C5\u914D\u5668\u751F\u6210 client-registry.ts \u65F6\u9759\u6001 import "<pkg>/client"\uFF0C
1070
+ // \u8981\u6C42\u6B64\u5904\u5177\u540D\u5BFC\u51FA extensions\uFF08components / actions \u6CE8\u518C\u8868\uFF09\u3002
1071
+ ${imports.join("\n")}
1072
+
1073
+ export const extensions = {
1074
+ components: ${components},
1075
+ actions: ${actionsExpr},
1076
+ };
1077
+ `;
1078
+ }
1079
+ function injectPackageJson(pkg, result) {
1080
+ const outDir = CONFIG_DEFAULTS.outDir;
1081
+ const files = Array.isArray(pkg.files) ? pkg.files : [];
1082
+ if (!files.includes(outDir)) {
1083
+ pkg.files = [...files, outDir];
1084
+ }
1085
+ const exportsMap = isRecord4(pkg.exports) ? { ...pkg.exports } : {};
1086
+ const wantedExports = {
1087
+ "./manifest": `./${outDir}/manifest.json`,
1088
+ "./client": `./${outDir}/client/index.js`,
1089
+ "./styles.css": `./${outDir}/client/index.css`
1090
+ };
1091
+ for (const [key, value] of Object.entries(wantedExports)) {
1092
+ if (exportsMap[key] === void 0) {
1093
+ exportsMap[key] = value;
1094
+ } else if (exportsMap[key] !== value) {
1095
+ result.warnings.push(`package.json exports["${key}"] \u5DF2\u5B58\u5728\u4E14\u4E0E\u5EFA\u8BAE\u503C\u4E0D\u540C\uFF0C\u672A\u8986\u76D6\uFF08\u5F53\u524D: ${JSON.stringify(exportsMap[key])}\uFF09`);
1096
+ }
1097
+ }
1098
+ pkg.exports = exportsMap;
1099
+ const scripts = isRecord4(pkg.scripts) ? { ...pkg.scripts } : {};
1100
+ const wantedScripts = {
1101
+ "build:module": "pactor module build",
1102
+ "pack:module": "pactor module pack"
1103
+ };
1104
+ for (const [key, value] of Object.entries(wantedScripts)) {
1105
+ if (scripts[key] === void 0) {
1106
+ scripts[key] = value;
1107
+ } else if (scripts[key] !== value) {
1108
+ result.warnings.push(`package.json scripts["${key}"] \u5DF2\u5B58\u5728\uFF0C\u672A\u8986\u76D6\uFF08\u5F53\u524D: ${JSON.stringify(scripts[key])}\uFF09`);
1109
+ }
1110
+ }
1111
+ pkg.scripts = scripts;
1112
+ const peers = isRecord4(pkg.peerDependencies) ? { ...pkg.peerDependencies } : {};
1113
+ const dependencies = isRecord4(pkg.dependencies) ? pkg.dependencies : {};
1114
+ const defaults = {
1115
+ react: "^18.0.0 || ^19.0.0",
1116
+ "react-dom": "^18.0.0 || ^19.0.0",
1117
+ next: "^14.0.0 || ^15.0.0",
1118
+ "pactor-app": "^1.54.0"
1119
+ };
1120
+ for (const [name, fallback] of Object.entries(defaults)) {
1121
+ if (peers[name] !== void 0) continue;
1122
+ const declared = dependencies[name];
1123
+ peers[name] = typeof declared === "string" && declared.length > 0 ? declared : fallback;
1124
+ }
1125
+ pkg.peerDependencies = peers;
1126
+ }
1127
+ function isRecord4(value) {
1128
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1129
+ }
1130
+ export {
1131
+ CLIENT_EXTERNALS,
1132
+ CONFIG_DEFAULTS,
1133
+ CONFIG_FILE,
1134
+ ConfigError,
1135
+ DEFAULT_TARGET,
1136
+ INCLUDE_KEY,
1137
+ IncludeError,
1138
+ ModuleBuildError,
1139
+ ProfileError,
1140
+ buildModule,
1141
+ bundleClient,
1142
+ collectModuleSource,
1143
+ compileTailwind,
1144
+ deriveModuleId,
1145
+ discoverActions,
1146
+ discoverComponents,
1147
+ emitDsl,
1148
+ emitModule,
1149
+ expandIncludes,
1150
+ getProfile,
1151
+ initModule,
1152
+ innospotsWebShellV1,
1153
+ innospotsWebShellV2,
1154
+ listProfiles,
1155
+ loadModuleConfig,
1156
+ loadYamlExpanded,
1157
+ matchPathPattern,
1158
+ parseJsonc,
1159
+ parseYamlFile,
1160
+ readProjectVersion,
1161
+ renderClientEntry,
1162
+ resolvePactorVersionRange,
1163
+ rewriteDslUrls,
1164
+ rewriteNavigationTo,
1165
+ transformSitemap,
1166
+ tsconfigAliases,
1167
+ validateModule,
1168
+ writeManifest
1169
+ };