@akanjs/devkit 2.1.1-rc.2 → 2.1.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/aiEditor.test.ts +68 -0
- package/aiEditor.ts +82 -28
- package/executors.ts +625 -147
- package/index.ts +1 -1
- package/linter.ts +308 -97
- package/package.json +2 -2
- package/prompter.ts +17 -4
- package/typecheck/typecheck.proc.ts +21 -0
package/executors.ts
CHANGED
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
spawn,
|
|
9
9
|
} from "node:child_process";
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
copyFile,
|
|
13
|
+
mkdir,
|
|
14
|
+
readdir as readDirEntries,
|
|
15
|
+
stat,
|
|
16
|
+
} from "node:fs/promises";
|
|
12
17
|
import path from "node:path";
|
|
13
18
|
import {
|
|
14
19
|
capitalize,
|
|
@@ -21,7 +26,12 @@ import {
|
|
|
21
26
|
import { $ } from "bun";
|
|
22
27
|
import chalk from "chalk";
|
|
23
28
|
import ts from "typescript";
|
|
24
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
AkanAppConfig,
|
|
31
|
+
AkanLibConfig,
|
|
32
|
+
decreaseBuildNum,
|
|
33
|
+
increaseBuildNum,
|
|
34
|
+
} from "./akanConfig";
|
|
25
35
|
import { FileSys } from "./fileSys";
|
|
26
36
|
import { getDirname } from "./getDirname";
|
|
27
37
|
import { Linter } from "./linter";
|
|
@@ -64,7 +74,8 @@ const staticTemplateFileExtensions = new Set([
|
|
|
64
74
|
".xml",
|
|
65
75
|
]);
|
|
66
76
|
|
|
67
|
-
const formatCommandArg = (value: string) =>
|
|
77
|
+
const formatCommandArg = (value: string) =>
|
|
78
|
+
/^[\w@%+=:,./-]+$/.test(value) ? value : JSON.stringify(value);
|
|
68
79
|
|
|
69
80
|
const formatCommandForDisplay = (command: string, args: string[] = []) =>
|
|
70
81
|
[command, ...args].map(formatCommandArg).join(" ");
|
|
@@ -100,11 +111,21 @@ export class CommandExecutionError extends Error {
|
|
|
100
111
|
cause,
|
|
101
112
|
}: CommandExecutionErrorOptions) {
|
|
102
113
|
const displayCommand = formatCommandForDisplay(command, args);
|
|
103
|
-
const status = signal
|
|
114
|
+
const status = signal
|
|
115
|
+
? `signal: ${signal}`
|
|
116
|
+
: `exit code: ${code ?? "unknown"}`;
|
|
104
117
|
const output = (stderr || stdout).trim();
|
|
105
|
-
super(
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
super(
|
|
119
|
+
[
|
|
120
|
+
`Command failed: ${displayCommand}`,
|
|
121
|
+
`cwd: ${cwd}`,
|
|
122
|
+
status,
|
|
123
|
+
output ? `\n${output}` : "",
|
|
124
|
+
].join("\n"),
|
|
125
|
+
{
|
|
126
|
+
cause,
|
|
127
|
+
},
|
|
128
|
+
);
|
|
108
129
|
this.name = "CommandExecutionError";
|
|
109
130
|
this.command = command;
|
|
110
131
|
this.args = args;
|
|
@@ -138,12 +159,17 @@ const parseEnvFile = (envPath: string): Record<string, string> => {
|
|
|
138
159
|
for (const line of content.split(/\r?\n/)) {
|
|
139
160
|
const trimmed = line.trim();
|
|
140
161
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
141
|
-
const normalized = trimmed.startsWith("export ")
|
|
162
|
+
const normalized = trimmed.startsWith("export ")
|
|
163
|
+
? trimmed.slice("export ".length).trim()
|
|
164
|
+
: trimmed;
|
|
142
165
|
const separatorIndex = normalized.indexOf("=");
|
|
143
166
|
if (separatorIndex <= 0) continue;
|
|
144
167
|
const key = normalized.slice(0, separatorIndex).trim();
|
|
145
168
|
let value = normalized.slice(separatorIndex + 1).trim();
|
|
146
|
-
if (
|
|
169
|
+
if (
|
|
170
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
171
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
172
|
+
) {
|
|
147
173
|
value = value.slice(1, -1);
|
|
148
174
|
}
|
|
149
175
|
env[key] = value;
|
|
@@ -151,7 +177,13 @@ const parseEnvFile = (envPath: string): Record<string, string> => {
|
|
|
151
177
|
return env;
|
|
152
178
|
};
|
|
153
179
|
|
|
154
|
-
const PAGE_ROUTE_EXPORTS = new Set([
|
|
180
|
+
const PAGE_ROUTE_EXPORTS = new Set([
|
|
181
|
+
"default",
|
|
182
|
+
"pageConfig",
|
|
183
|
+
"head",
|
|
184
|
+
"generateHead",
|
|
185
|
+
"Loading",
|
|
186
|
+
]);
|
|
155
187
|
const ROOT_LAYOUT_EXPORTS = new Set([
|
|
156
188
|
"default",
|
|
157
189
|
"head",
|
|
@@ -164,7 +196,12 @@ const ROOT_LAYOUT_EXPORTS = new Set([
|
|
|
164
196
|
"gaTrackingId",
|
|
165
197
|
"Loading",
|
|
166
198
|
]);
|
|
167
|
-
const LAYOUT_ROUTE_EXPORTS = new Set([
|
|
199
|
+
const LAYOUT_ROUTE_EXPORTS = new Set([
|
|
200
|
+
"default",
|
|
201
|
+
"head",
|
|
202
|
+
"generateHead",
|
|
203
|
+
"Loading",
|
|
204
|
+
]);
|
|
168
205
|
|
|
169
206
|
function validateRouteSourceExports(
|
|
170
207
|
source: string,
|
|
@@ -172,23 +209,42 @@ function validateRouteSourceExports(
|
|
|
172
209
|
kind: "page" | "layout",
|
|
173
210
|
options: { rootLayout?: boolean } = {},
|
|
174
211
|
) {
|
|
175
|
-
const sourceFile = ts.createSourceFile(
|
|
212
|
+
const sourceFile = ts.createSourceFile(
|
|
213
|
+
filePath,
|
|
214
|
+
source,
|
|
215
|
+
ts.ScriptTarget.Latest,
|
|
216
|
+
true,
|
|
217
|
+
ts.ScriptKind.TSX,
|
|
218
|
+
);
|
|
176
219
|
const allowed =
|
|
177
|
-
kind === "page"
|
|
220
|
+
kind === "page"
|
|
221
|
+
? PAGE_ROUTE_EXPORTS
|
|
222
|
+
: options.rootLayout
|
|
223
|
+
? ROOT_LAYOUT_EXPORTS
|
|
224
|
+
: LAYOUT_ROUTE_EXPORTS;
|
|
178
225
|
const exported = new Set<string>();
|
|
179
226
|
const assertExport = (name: string) => {
|
|
180
227
|
if (!allowed.has(name)) {
|
|
181
|
-
throw new Error(
|
|
228
|
+
throw new Error(
|
|
229
|
+
`[route-convention] unsupported export "${name}" in ${filePath}`,
|
|
230
|
+
);
|
|
182
231
|
}
|
|
183
232
|
exported.add(name);
|
|
184
233
|
};
|
|
185
234
|
|
|
186
235
|
for (const statement of sourceFile.statements) {
|
|
187
|
-
if (
|
|
236
|
+
if (
|
|
237
|
+
ts.isInterfaceDeclaration(statement) ||
|
|
238
|
+
ts.isTypeAliasDeclaration(statement)
|
|
239
|
+
)
|
|
240
|
+
continue;
|
|
188
241
|
if (ts.isExportDeclaration(statement)) {
|
|
189
242
|
if (statement.isTypeOnly) continue;
|
|
190
243
|
const clause = statement.exportClause;
|
|
191
|
-
if (!clause)
|
|
244
|
+
if (!clause)
|
|
245
|
+
throw new Error(
|
|
246
|
+
`[route-convention] export * is not allowed in route modules: ${filePath}`,
|
|
247
|
+
);
|
|
192
248
|
if (ts.isNamedExports(clause)) {
|
|
193
249
|
for (const element of clause.elements) {
|
|
194
250
|
if (element.isTypeOnly) continue;
|
|
@@ -197,17 +253,22 @@ function validateRouteSourceExports(
|
|
|
197
253
|
}
|
|
198
254
|
continue;
|
|
199
255
|
}
|
|
200
|
-
const modifiers = ts.canHaveModifiers(statement)
|
|
201
|
-
|
|
256
|
+
const modifiers = ts.canHaveModifiers(statement)
|
|
257
|
+
? ts.getModifiers(statement)
|
|
258
|
+
: undefined;
|
|
259
|
+
const isExported =
|
|
260
|
+
modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
202
261
|
if (!isExported) continue;
|
|
203
|
-
const isDefault =
|
|
262
|
+
const isDefault =
|
|
263
|
+
modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
|
|
204
264
|
if (isDefault) {
|
|
205
265
|
assertExport("default");
|
|
206
266
|
continue;
|
|
207
267
|
}
|
|
208
268
|
if (ts.isVariableStatement(statement)) {
|
|
209
269
|
for (const declaration of statement.declarationList.declarations) {
|
|
210
|
-
if (ts.isIdentifier(declaration.name))
|
|
270
|
+
if (ts.isIdentifier(declaration.name))
|
|
271
|
+
assertExport(declaration.name.text);
|
|
211
272
|
}
|
|
212
273
|
continue;
|
|
213
274
|
}
|
|
@@ -217,7 +278,9 @@ function validateRouteSourceExports(
|
|
|
217
278
|
}
|
|
218
279
|
}
|
|
219
280
|
if (exported.has("head") && exported.has("generateHead")) {
|
|
220
|
-
throw new Error(
|
|
281
|
+
throw new Error(
|
|
282
|
+
`[route-convention] head and generateHead cannot both be exported in ${filePath}`,
|
|
283
|
+
);
|
|
221
284
|
}
|
|
222
285
|
}
|
|
223
286
|
|
|
@@ -290,7 +353,11 @@ export class Executor {
|
|
|
290
353
|
});
|
|
291
354
|
}
|
|
292
355
|
|
|
293
|
-
spawn(
|
|
356
|
+
spawn(
|
|
357
|
+
command: string,
|
|
358
|
+
args: string[] = [],
|
|
359
|
+
options: SpawnOptions = {},
|
|
360
|
+
): Promise<string> {
|
|
294
361
|
const cwd = options.cwd?.toString() ?? this.cwdPath;
|
|
295
362
|
const proc = spawn(command, args, {
|
|
296
363
|
cwd: this.cwdPath,
|
|
@@ -342,7 +409,11 @@ export class Executor {
|
|
|
342
409
|
});
|
|
343
410
|
});
|
|
344
411
|
}
|
|
345
|
-
spawnSync(
|
|
412
|
+
spawnSync(
|
|
413
|
+
command: string,
|
|
414
|
+
args: string[] = [],
|
|
415
|
+
options: SpawnOptions = {},
|
|
416
|
+
): ChildProcess {
|
|
346
417
|
const proc = spawn(command, args, {
|
|
347
418
|
cwd: this.cwdPath,
|
|
348
419
|
// stdio: "inherit",
|
|
@@ -423,7 +494,8 @@ export class Executor {
|
|
|
423
494
|
}
|
|
424
495
|
async mkdir(dirPath: string) {
|
|
425
496
|
const writePath = this.getPath(dirPath);
|
|
426
|
-
if (!(await FileSys.dirExists(writePath)))
|
|
497
|
+
if (!(await FileSys.dirExists(writePath)))
|
|
498
|
+
await mkdir(writePath, { recursive: true });
|
|
427
499
|
this.logger.verbose(`Make directory ${writePath}`);
|
|
428
500
|
return this;
|
|
429
501
|
}
|
|
@@ -436,16 +508,27 @@ export class Executor {
|
|
|
436
508
|
return [];
|
|
437
509
|
}
|
|
438
510
|
}
|
|
439
|
-
async getAllFiles(
|
|
511
|
+
async getAllFiles(
|
|
512
|
+
pattern = "**/*",
|
|
513
|
+
{ cwd }: { cwd?: string } = {},
|
|
514
|
+
): Promise<string[]> {
|
|
440
515
|
const glob = new Bun.Glob(pattern);
|
|
441
|
-
return Array.from(
|
|
516
|
+
return Array.from(
|
|
517
|
+
glob.scanSync({ cwd: cwd ?? this.cwdPath, onlyFiles: true }),
|
|
518
|
+
);
|
|
442
519
|
}
|
|
443
|
-
async getFilesAndDirs(
|
|
520
|
+
async getFilesAndDirs(
|
|
521
|
+
dirPath: string,
|
|
522
|
+
): Promise<{ files: string[]; dirs: string[] }> {
|
|
444
523
|
const fullDirPath = this.getPath(dirPath);
|
|
445
524
|
const fileGlob = new Bun.Glob("*");
|
|
446
|
-
const files = Array.from(
|
|
525
|
+
const files = Array.from(
|
|
526
|
+
fileGlob.scanSync({ cwd: fullDirPath, onlyFiles: true }),
|
|
527
|
+
);
|
|
447
528
|
const dirGlob = new Bun.Glob("*");
|
|
448
|
-
const allEntries = Array.from(
|
|
529
|
+
const allEntries = Array.from(
|
|
530
|
+
dirGlob.scanSync({ cwd: fullDirPath, onlyFiles: false }),
|
|
531
|
+
);
|
|
449
532
|
const dirs = allEntries.filter((entry) => !files.includes(entry));
|
|
450
533
|
return { files, dirs };
|
|
451
534
|
}
|
|
@@ -473,7 +556,8 @@ export class Executor {
|
|
|
473
556
|
const writePath = this.getPath(filePath);
|
|
474
557
|
const dir = path.dirname(writePath);
|
|
475
558
|
if (!(await FileSys.dirExists(dir))) await mkdir(dir, { recursive: true });
|
|
476
|
-
let contentStr =
|
|
559
|
+
let contentStr =
|
|
560
|
+
typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
477
561
|
|
|
478
562
|
if (await FileSys.fileExists(writePath)) {
|
|
479
563
|
const currentContent = await FileSys.readText(writePath);
|
|
@@ -482,7 +566,8 @@ export class Executor {
|
|
|
482
566
|
contentStr = currentContent;
|
|
483
567
|
} else {
|
|
484
568
|
await FileSys.writeText(writePath, contentStr);
|
|
485
|
-
if (Logger.isVerbose())
|
|
569
|
+
if (Logger.isVerbose())
|
|
570
|
+
this.logger.rawLog(chalk.yellow(`File Update: ${filePath}`));
|
|
486
571
|
}
|
|
487
572
|
} else {
|
|
488
573
|
await FileSys.writeText(writePath, contentStr);
|
|
@@ -494,7 +579,9 @@ export class Executor {
|
|
|
494
579
|
await this.writeFile(filePath, content);
|
|
495
580
|
}
|
|
496
581
|
async getLocalFile(targetPath: string) {
|
|
497
|
-
const filePath = path.isAbsolute(targetPath)
|
|
582
|
+
const filePath = path.isAbsolute(targetPath)
|
|
583
|
+
? targetPath
|
|
584
|
+
: targetPath.replace(this.cwdPath, "");
|
|
498
585
|
const content = await this.readFile(filePath);
|
|
499
586
|
return { filePath, content };
|
|
500
587
|
}
|
|
@@ -511,7 +598,8 @@ export class Executor {
|
|
|
511
598
|
const dest = this.getPath(destPath);
|
|
512
599
|
if (!(await FileSys.exists(src))) return;
|
|
513
600
|
const isDirectory = (await stat(src)).isDirectory();
|
|
514
|
-
if (!(await FileSys.exists(dest)) && isDirectory)
|
|
601
|
+
if (!(await FileSys.exists(dest)) && isDirectory)
|
|
602
|
+
await mkdir(dest, { recursive: true });
|
|
515
603
|
await $`cp -r ${src}${isDirectory ? "/." : ""} ${dest}`;
|
|
516
604
|
}
|
|
517
605
|
log(msg: string) {
|
|
@@ -526,12 +614,22 @@ export class Executor {
|
|
|
526
614
|
this.logger.debug(msg);
|
|
527
615
|
return this;
|
|
528
616
|
}
|
|
529
|
-
spinning(
|
|
617
|
+
spinning(
|
|
618
|
+
msg: string,
|
|
619
|
+
{
|
|
620
|
+
prefix = `${this.emoji}${this.name}`,
|
|
621
|
+
indent = 0,
|
|
622
|
+
enableSpin = !Executor.verbose,
|
|
623
|
+
} = {},
|
|
624
|
+
) {
|
|
530
625
|
return new Spinner(msg, { prefix, indent, enableSpin }).start();
|
|
531
626
|
}
|
|
532
627
|
|
|
533
628
|
#tsconfig: TsConfigJson | null = null;
|
|
534
|
-
async getTsConfig(
|
|
629
|
+
async getTsConfig(
|
|
630
|
+
pathname = "tsconfig.json",
|
|
631
|
+
{ refresh }: { refresh?: boolean } = {},
|
|
632
|
+
): Promise<TsConfigJson> {
|
|
535
633
|
if (this.#tsconfig && !refresh) return this.#tsconfig;
|
|
536
634
|
const tsconfig = (await this.readJson(pathname)) as TsConfigJson;
|
|
537
635
|
if (tsconfig.extends) {
|
|
@@ -556,7 +654,11 @@ export class Executor {
|
|
|
556
654
|
}
|
|
557
655
|
|
|
558
656
|
#packageJson: PackageJson | null = null;
|
|
559
|
-
async getPackageJson({
|
|
657
|
+
async getPackageJson({
|
|
658
|
+
refresh,
|
|
659
|
+
}: {
|
|
660
|
+
refresh?: boolean;
|
|
661
|
+
} = {}): Promise<PackageJson> {
|
|
560
662
|
if (this.#packageJson && !refresh) return this.#packageJson;
|
|
561
663
|
const packageJson = (await this.readJson("package.json")) as PackageJson;
|
|
562
664
|
this.#packageJson = packageJson;
|
|
@@ -603,39 +705,55 @@ export class Executor {
|
|
|
603
705
|
};
|
|
604
706
|
const result = await getContent.default(scanInfo ?? null, dict, options);
|
|
605
707
|
if (result === null) return null;
|
|
606
|
-
const filename =
|
|
708
|
+
const filename =
|
|
709
|
+
typeof result === "object"
|
|
710
|
+
? result.filename
|
|
711
|
+
: path.basename(targetPath).replace(".js", ".ts");
|
|
607
712
|
const content = typeof result === "object" ? result.content : result;
|
|
608
713
|
const dirname = path.dirname(targetPath);
|
|
609
714
|
const convertedTargetPath = Object.entries(dict).reduce(
|
|
610
|
-
(path, [key, value]) =>
|
|
715
|
+
(path, [key, value]) =>
|
|
716
|
+
path.replace(new RegExp(`__${key}__`, "g"), value),
|
|
611
717
|
`${dirname}/${filename}`,
|
|
612
718
|
);
|
|
613
|
-
this.logger.verbose(
|
|
719
|
+
this.logger.verbose(
|
|
720
|
+
`Apply template ${templatePath} to ${convertedTargetPath}`,
|
|
721
|
+
);
|
|
614
722
|
return this.writeFile(convertedTargetPath, content, { overwrite });
|
|
615
723
|
} else if (targetPath.endsWith(".template")) {
|
|
616
724
|
const content = await FileSys.readText(templatePath);
|
|
617
725
|
const convertedTargetPath = Object.entries(dict).reduce(
|
|
618
|
-
(path, [key, value]) =>
|
|
726
|
+
(path, [key, value]) =>
|
|
727
|
+
path.replace(new RegExp(`__${key}__`, "g"), value),
|
|
619
728
|
targetPath.slice(0, -9),
|
|
620
729
|
);
|
|
621
730
|
const convertedContent = Object.entries(dict).reduce(
|
|
622
|
-
(data, [key, value]) =>
|
|
731
|
+
(data, [key, value]) =>
|
|
732
|
+
data.replace(new RegExp(`<%= ${key} %>`, "g"), value),
|
|
623
733
|
content,
|
|
624
734
|
);
|
|
625
|
-
this.logger.verbose(
|
|
735
|
+
this.logger.verbose(
|
|
736
|
+
`Apply template ${templatePath} to ${convertedTargetPath}`,
|
|
737
|
+
);
|
|
626
738
|
return this.writeFile(convertedTargetPath, convertedContent, {
|
|
627
739
|
overwrite,
|
|
628
740
|
});
|
|
629
|
-
} else if (
|
|
741
|
+
} else if (
|
|
742
|
+
staticTemplateFileExtensions.has(path.extname(targetPath).toLowerCase())
|
|
743
|
+
) {
|
|
630
744
|
const convertedTargetPath = Object.entries(dict).reduce(
|
|
631
|
-
(path, [key, value]) =>
|
|
745
|
+
(path, [key, value]) =>
|
|
746
|
+
path.replace(new RegExp(`__${key}__`, "g"), value),
|
|
632
747
|
targetPath,
|
|
633
748
|
);
|
|
634
749
|
const writePath = this.getPath(convertedTargetPath);
|
|
635
750
|
const dirname = path.dirname(writePath);
|
|
636
|
-
if (!(await FileSys.dirExists(dirname)))
|
|
751
|
+
if (!(await FileSys.dirExists(dirname)))
|
|
752
|
+
await mkdir(dirname, { recursive: true });
|
|
637
753
|
await copyFile(templatePath, writePath);
|
|
638
|
-
this.logger.verbose(
|
|
754
|
+
this.logger.verbose(
|
|
755
|
+
`Apply template ${templatePath} to ${convertedTargetPath}`,
|
|
756
|
+
);
|
|
639
757
|
return { filePath: writePath, content: "" };
|
|
640
758
|
} else return null;
|
|
641
759
|
}
|
|
@@ -723,7 +841,10 @@ export class Executor {
|
|
|
723
841
|
const dict = {
|
|
724
842
|
...(options.dict ?? {}),
|
|
725
843
|
...Object.fromEntries(
|
|
726
|
-
Object.entries(options.dict ?? {}).map(([key, value]) => [
|
|
844
|
+
Object.entries(options.dict ?? {}).map(([key, value]) => [
|
|
845
|
+
capitalize(key),
|
|
846
|
+
capitalize(value),
|
|
847
|
+
]),
|
|
727
848
|
),
|
|
728
849
|
};
|
|
729
850
|
return this._applyTemplate({ ...options, dict });
|
|
@@ -735,10 +856,69 @@ export class Executor {
|
|
|
735
856
|
typeCheck(filePath: string) {
|
|
736
857
|
const path = this.getPath(filePath);
|
|
737
858
|
const typeChecker = this.getTypeChecker();
|
|
738
|
-
const { fileDiagnostics, fileErrors, fileWarnings } =
|
|
859
|
+
const { fileDiagnostics, fileErrors, fileWarnings } =
|
|
860
|
+
typeChecker.check(path);
|
|
739
861
|
const message = typeChecker.formatDiagnostics(fileDiagnostics);
|
|
740
862
|
return { fileDiagnostics, fileErrors, fileWarnings, message };
|
|
741
863
|
}
|
|
864
|
+
async typeCheckAsync(filePath: string) {
|
|
865
|
+
const path = this.getPath(filePath);
|
|
866
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
867
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
868
|
+
cwd: this.cwdPath,
|
|
869
|
+
env: {
|
|
870
|
+
...process.env,
|
|
871
|
+
AKAN_TYPECHECK_CWD: this.cwdPath,
|
|
872
|
+
AKAN_TYPECHECK_FILE: path,
|
|
873
|
+
},
|
|
874
|
+
stdout: "pipe",
|
|
875
|
+
stderr: "pipe",
|
|
876
|
+
});
|
|
877
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
878
|
+
new Response(proc.stdout).text(),
|
|
879
|
+
new Response(proc.stderr).text(),
|
|
880
|
+
proc.exited,
|
|
881
|
+
]);
|
|
882
|
+
if (exitCode !== 0)
|
|
883
|
+
throw new Error(
|
|
884
|
+
(stderr || stdout).trim() ||
|
|
885
|
+
`Typecheck failed with exit code ${exitCode}`,
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
const result = JSON.parse(stdout) as {
|
|
889
|
+
fileDiagnosticsCount: number;
|
|
890
|
+
fileErrorsCount: number;
|
|
891
|
+
fileWarningsCount: number;
|
|
892
|
+
message: string;
|
|
893
|
+
};
|
|
894
|
+
return {
|
|
895
|
+
fileDiagnostics: Array.from({ length: result.fileDiagnosticsCount }),
|
|
896
|
+
fileErrors: Array.from({ length: result.fileErrorsCount }),
|
|
897
|
+
fileWarnings: Array.from({ length: result.fileWarningsCount }),
|
|
898
|
+
message: result.message,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
async #resolveTypecheckWorkerEntry() {
|
|
902
|
+
const dirname = getDirname(import.meta.url);
|
|
903
|
+
const candidates = [
|
|
904
|
+
path.join(
|
|
905
|
+
process.cwd(),
|
|
906
|
+
"pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts",
|
|
907
|
+
),
|
|
908
|
+
path.join(
|
|
909
|
+
process.cwd(),
|
|
910
|
+
"node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts",
|
|
911
|
+
),
|
|
912
|
+
path.join(dirname, "typecheck/typecheck.proc.ts"),
|
|
913
|
+
path.join(dirname, "typecheck.proc.js"),
|
|
914
|
+
path.join(dirname, "typecheck.proc.ts"),
|
|
915
|
+
];
|
|
916
|
+
for (const candidate of candidates)
|
|
917
|
+
if (await Bun.file(candidate).exists()) return candidate;
|
|
918
|
+
throw new Error(
|
|
919
|
+
`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
742
922
|
getLinter() {
|
|
743
923
|
this.linter ??= new Linter(this.cwdPath);
|
|
744
924
|
return this.linter;
|
|
@@ -785,11 +965,16 @@ export class WorkspaceExecutor extends Executor {
|
|
|
785
965
|
workspaceRoot?: string;
|
|
786
966
|
repoName?: string;
|
|
787
967
|
} = {}) {
|
|
788
|
-
return
|
|
968
|
+
return (
|
|
969
|
+
WorkspaceExecutor.#execs.get(repoName) ??
|
|
970
|
+
new WorkspaceExecutor({ workspaceRoot, repoName })
|
|
971
|
+
);
|
|
789
972
|
}
|
|
790
973
|
static getBaseDevEnv(envPath?: string) {
|
|
791
974
|
// Bun auto-loads .env, so we use process.env directly
|
|
792
|
-
const sourceEnv = envPath
|
|
975
|
+
const sourceEnv = envPath
|
|
976
|
+
? { ...process.env, ...parseEnvFile(envPath) }
|
|
977
|
+
: process.env;
|
|
793
978
|
|
|
794
979
|
const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
|
|
795
980
|
const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
|
|
@@ -811,7 +996,15 @@ export class WorkspaceExecutor extends Executor {
|
|
|
811
996
|
| "local"
|
|
812
997
|
| undefined;
|
|
813
998
|
if (!env) throw new Error("AKAN_PUBLIC_ENV is not set");
|
|
814
|
-
return {
|
|
999
|
+
return {
|
|
1000
|
+
...(appName ? { appName } : {}),
|
|
1001
|
+
workspaceRoot,
|
|
1002
|
+
repoName,
|
|
1003
|
+
serveDomain,
|
|
1004
|
+
env,
|
|
1005
|
+
portOffset,
|
|
1006
|
+
workspaceId,
|
|
1007
|
+
};
|
|
815
1008
|
}
|
|
816
1009
|
getWorkspaceId<AllowEmpty extends boolean = false>({
|
|
817
1010
|
allowEmpty,
|
|
@@ -819,7 +1012,8 @@ export class WorkspaceExecutor extends Executor {
|
|
|
819
1012
|
allowEmpty?: AllowEmpty;
|
|
820
1013
|
} = {}): AllowEmpty extends true ? string | undefined : string {
|
|
821
1014
|
const { workspaceId } = WorkspaceExecutor.getBaseDevEnv();
|
|
822
|
-
if (!workspaceId && !allowEmpty)
|
|
1015
|
+
if (!workspaceId && !allowEmpty)
|
|
1016
|
+
throw new Error("Workspace ID is not found");
|
|
823
1017
|
return workspaceId as AllowEmpty extends true ? string | undefined : string;
|
|
824
1018
|
}
|
|
825
1019
|
async scan(): Promise<WorkspaceInfo> {
|
|
@@ -827,22 +1021,38 @@ export class WorkspaceExecutor extends Executor {
|
|
|
827
1021
|
}
|
|
828
1022
|
async getApps() {
|
|
829
1023
|
if (!(await FileSys.dirExists(`${this.workspaceRoot}/apps`))) return [];
|
|
830
|
-
return await this.#getDirHasFile(
|
|
1024
|
+
return await this.#getDirHasFile(
|
|
1025
|
+
`${this.workspaceRoot}/apps`,
|
|
1026
|
+
"akan.config.ts",
|
|
1027
|
+
);
|
|
831
1028
|
}
|
|
832
1029
|
async getLibs() {
|
|
833
1030
|
if (!(await FileSys.dirExists(`${this.workspaceRoot}/libs`))) return [];
|
|
834
|
-
return await this.#getDirHasFile(
|
|
1031
|
+
return await this.#getDirHasFile(
|
|
1032
|
+
`${this.workspaceRoot}/libs`,
|
|
1033
|
+
"akan.config.ts",
|
|
1034
|
+
);
|
|
835
1035
|
}
|
|
836
1036
|
async getSyss() {
|
|
837
|
-
const [appNames, libNames] = await Promise.all([
|
|
1037
|
+
const [appNames, libNames] = await Promise.all([
|
|
1038
|
+
this.getApps(),
|
|
1039
|
+
this.getLibs(),
|
|
1040
|
+
]);
|
|
838
1041
|
return [appNames, libNames] as [string[], string[]];
|
|
839
1042
|
}
|
|
840
1043
|
async getPkgs() {
|
|
841
1044
|
if (!(await FileSys.dirExists(`${this.workspaceRoot}/pkgs`))) return [];
|
|
842
|
-
return await this.#getDirHasFile(
|
|
1045
|
+
return await this.#getDirHasFile(
|
|
1046
|
+
`${this.workspaceRoot}/pkgs`,
|
|
1047
|
+
"package.json",
|
|
1048
|
+
);
|
|
843
1049
|
}
|
|
844
1050
|
async getExecs() {
|
|
845
|
-
const [appNames, libNames, pkgNames] = await Promise.all([
|
|
1051
|
+
const [appNames, libNames, pkgNames] = await Promise.all([
|
|
1052
|
+
this.getApps(),
|
|
1053
|
+
this.getLibs(),
|
|
1054
|
+
this.getPkgs(),
|
|
1055
|
+
]);
|
|
846
1056
|
return [appNames, libNames, pkgNames] as [string[], string[], string[]];
|
|
847
1057
|
}
|
|
848
1058
|
async setPkgTsPaths(name: string) {
|
|
@@ -851,7 +1061,11 @@ export class WorkspaceExecutor extends Executor {
|
|
|
851
1061
|
rootTsConfig.compilerOptions.paths[name] = [`./pkgs/${name}/index.ts`];
|
|
852
1062
|
rootTsConfig.compilerOptions.paths[`${name}/*`] = [`./pkgs/${name}/*`];
|
|
853
1063
|
if (rootTsConfig.references) {
|
|
854
|
-
if (
|
|
1064
|
+
if (
|
|
1065
|
+
!rootTsConfig.references.some(
|
|
1066
|
+
(ref) => ref.path === `./pkgs/${name}/tsconfig.json`,
|
|
1067
|
+
)
|
|
1068
|
+
)
|
|
855
1069
|
rootTsConfig.references.push({ path: `./pkgs/${name}/tsconfig.json` });
|
|
856
1070
|
}
|
|
857
1071
|
await this.writeJson("tsconfig.json", rootTsConfig);
|
|
@@ -859,11 +1073,14 @@ export class WorkspaceExecutor extends Executor {
|
|
|
859
1073
|
}
|
|
860
1074
|
async unsetPkgTsPaths(name: string) {
|
|
861
1075
|
const rootTsConfig = (await this.readJson("tsconfig.json")) as TsConfigJson;
|
|
862
|
-
const filteredKeys = Object.keys(
|
|
863
|
-
|
|
864
|
-
);
|
|
1076
|
+
const filteredKeys = Object.keys(
|
|
1077
|
+
rootTsConfig.compilerOptions.paths ?? {},
|
|
1078
|
+
).filter((key) => key !== name && key !== `${name}/*`);
|
|
865
1079
|
rootTsConfig.compilerOptions.paths = Object.fromEntries(
|
|
866
|
-
filteredKeys.map((key) => [
|
|
1080
|
+
filteredKeys.map((key) => [
|
|
1081
|
+
key,
|
|
1082
|
+
rootTsConfig.compilerOptions.paths?.[key] ?? [],
|
|
1083
|
+
]),
|
|
867
1084
|
);
|
|
868
1085
|
if (rootTsConfig.references) {
|
|
869
1086
|
rootTsConfig.references = rootTsConfig.references.filter(
|
|
@@ -875,7 +1092,12 @@ export class WorkspaceExecutor extends Executor {
|
|
|
875
1092
|
}
|
|
876
1093
|
async getDirInModule(basePath: string, name: string) {
|
|
877
1094
|
const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
|
|
878
|
-
const getDirs = async (
|
|
1095
|
+
const getDirs = async (
|
|
1096
|
+
dirname: string,
|
|
1097
|
+
maxDepth = 3,
|
|
1098
|
+
results: string[] = [],
|
|
1099
|
+
prefix = "",
|
|
1100
|
+
) => {
|
|
879
1101
|
const dirs = await this.readdir(dirname);
|
|
880
1102
|
await Promise.all(
|
|
881
1103
|
dirs.map(async (dir) => {
|
|
@@ -883,7 +1105,8 @@ export class WorkspaceExecutor extends Executor {
|
|
|
883
1105
|
const dirPath = path.join(dirname, dir);
|
|
884
1106
|
if ((await stat(dirPath)).isDirectory()) {
|
|
885
1107
|
results.push(`${prefix}${dir}`);
|
|
886
|
-
if (maxDepth > 0)
|
|
1108
|
+
if (maxDepth > 0)
|
|
1109
|
+
await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
|
|
887
1110
|
}
|
|
888
1111
|
}),
|
|
889
1112
|
);
|
|
@@ -891,23 +1114,34 @@ export class WorkspaceExecutor extends Executor {
|
|
|
891
1114
|
};
|
|
892
1115
|
return await getDirs(basePath);
|
|
893
1116
|
}
|
|
894
|
-
async commit(
|
|
1117
|
+
async commit(
|
|
1118
|
+
message: string,
|
|
1119
|
+
{ init = false, add = true }: { init?: boolean; add?: boolean } = {},
|
|
1120
|
+
) {
|
|
895
1121
|
if (init) await this.exec(`git init --quiet`);
|
|
896
1122
|
if (add) await this.exec(`git add .`);
|
|
897
1123
|
await this.exec(`git commit --quiet -m "${message}"`);
|
|
898
1124
|
}
|
|
899
1125
|
async #getDirHasFile(basePath: string, targetFilename: string) {
|
|
900
1126
|
const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
|
|
901
|
-
const getDirs = async (
|
|
1127
|
+
const getDirs = async (
|
|
1128
|
+
dirname: string,
|
|
1129
|
+
maxDepth = 3,
|
|
1130
|
+
results: string[] = [],
|
|
1131
|
+
prefix = "",
|
|
1132
|
+
) => {
|
|
902
1133
|
const dirs = await this.readdir(dirname);
|
|
903
1134
|
await Promise.all(
|
|
904
1135
|
dirs.map(async (dir) => {
|
|
905
1136
|
if (AVOID_DIRS.includes(dir)) return;
|
|
906
1137
|
const dirPath = path.join(dirname, dir);
|
|
907
1138
|
if ((await stat(dirPath)).isDirectory()) {
|
|
908
|
-
const hasTargetFile = await FileSys.fileExists(
|
|
1139
|
+
const hasTargetFile = await FileSys.fileExists(
|
|
1140
|
+
path.join(dirPath, targetFilename),
|
|
1141
|
+
);
|
|
909
1142
|
if (hasTargetFile) results.push(`${prefix}${dir}`);
|
|
910
|
-
if (maxDepth > 0)
|
|
1143
|
+
if (maxDepth > 0)
|
|
1144
|
+
await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
|
|
911
1145
|
}
|
|
912
1146
|
}),
|
|
913
1147
|
);
|
|
@@ -920,10 +1154,18 @@ export class WorkspaceExecutor extends Executor {
|
|
|
920
1154
|
const [appNames, libNames] = await this.getSyss();
|
|
921
1155
|
const scalarConstantExampleFiles = [
|
|
922
1156
|
...(
|
|
923
|
-
await Promise.all(
|
|
1157
|
+
await Promise.all(
|
|
1158
|
+
appNames.map((appName) =>
|
|
1159
|
+
AppExecutor.from(this, appName).getScalarConstantFiles(),
|
|
1160
|
+
),
|
|
1161
|
+
)
|
|
924
1162
|
).flat(),
|
|
925
1163
|
...(
|
|
926
|
-
await Promise.all(
|
|
1164
|
+
await Promise.all(
|
|
1165
|
+
libNames.map((libName) =>
|
|
1166
|
+
LibExecutor.from(this, libName).getScalarConstantFiles(),
|
|
1167
|
+
),
|
|
1168
|
+
)
|
|
927
1169
|
).flat(),
|
|
928
1170
|
];
|
|
929
1171
|
return scalarConstantExampleFiles;
|
|
@@ -931,24 +1173,60 @@ export class WorkspaceExecutor extends Executor {
|
|
|
931
1173
|
async getConstantFiles() {
|
|
932
1174
|
const [appNames, libNames] = await this.getSyss();
|
|
933
1175
|
const moduleConstantExampleFiles = [
|
|
934
|
-
...(
|
|
935
|
-
|
|
1176
|
+
...(
|
|
1177
|
+
await Promise.all(
|
|
1178
|
+
appNames.map((appName) =>
|
|
1179
|
+
AppExecutor.from(this, appName).getConstantFiles(),
|
|
1180
|
+
),
|
|
1181
|
+
)
|
|
1182
|
+
).flat(),
|
|
1183
|
+
...(
|
|
1184
|
+
await Promise.all(
|
|
1185
|
+
libNames.map((libName) =>
|
|
1186
|
+
LibExecutor.from(this, libName).getConstantFiles(),
|
|
1187
|
+
),
|
|
1188
|
+
)
|
|
1189
|
+
).flat(),
|
|
936
1190
|
];
|
|
937
1191
|
return moduleConstantExampleFiles;
|
|
938
1192
|
}
|
|
939
1193
|
async getDictionaryFiles() {
|
|
940
1194
|
const [appNames, libNames] = await this.getSyss();
|
|
941
1195
|
const moduleDictionaryExampleFiles = [
|
|
942
|
-
...(
|
|
943
|
-
|
|
1196
|
+
...(
|
|
1197
|
+
await Promise.all(
|
|
1198
|
+
appNames.map((appName) =>
|
|
1199
|
+
AppExecutor.from(this, appName).getDictionaryFiles(),
|
|
1200
|
+
),
|
|
1201
|
+
)
|
|
1202
|
+
).flat(),
|
|
1203
|
+
...(
|
|
1204
|
+
await Promise.all(
|
|
1205
|
+
libNames.map((libName) =>
|
|
1206
|
+
LibExecutor.from(this, libName).getDictionaryFiles(),
|
|
1207
|
+
),
|
|
1208
|
+
)
|
|
1209
|
+
).flat(),
|
|
944
1210
|
];
|
|
945
1211
|
return moduleDictionaryExampleFiles;
|
|
946
1212
|
}
|
|
947
1213
|
async getViewFiles() {
|
|
948
1214
|
const [appNames, libNames] = await this.getSyss();
|
|
949
1215
|
const viewExampleFiles = [
|
|
950
|
-
...(
|
|
951
|
-
|
|
1216
|
+
...(
|
|
1217
|
+
await Promise.all(
|
|
1218
|
+
appNames.map((appName) =>
|
|
1219
|
+
AppExecutor.from(this, appName).getViewsSourceCode(),
|
|
1220
|
+
),
|
|
1221
|
+
)
|
|
1222
|
+
).flat(),
|
|
1223
|
+
...(
|
|
1224
|
+
await Promise.all(
|
|
1225
|
+
libNames.map((libName) =>
|
|
1226
|
+
LibExecutor.from(this, libName).getViewsSourceCode(),
|
|
1227
|
+
),
|
|
1228
|
+
)
|
|
1229
|
+
).flat(),
|
|
952
1230
|
];
|
|
953
1231
|
return viewExampleFiles;
|
|
954
1232
|
}
|
|
@@ -967,7 +1245,11 @@ export class SysExecutor extends Executor {
|
|
|
967
1245
|
override name: string;
|
|
968
1246
|
type: "app" | "lib";
|
|
969
1247
|
override emoji: string;
|
|
970
|
-
constructor({
|
|
1248
|
+
constructor({
|
|
1249
|
+
workspace = WorkspaceExecutor.fromRoot(),
|
|
1250
|
+
name,
|
|
1251
|
+
type,
|
|
1252
|
+
}: SysExecutorOptions) {
|
|
971
1253
|
super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
|
|
972
1254
|
this.workspace = workspace;
|
|
973
1255
|
this.name = name;
|
|
@@ -984,7 +1266,8 @@ export class SysExecutor extends Executor {
|
|
|
984
1266
|
return this.#akanConfig;
|
|
985
1267
|
}
|
|
986
1268
|
async getModules() {
|
|
987
|
-
const path =
|
|
1269
|
+
const path =
|
|
1270
|
+
this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
|
|
988
1271
|
return await this.workspace.getDirInModule(path, this.name);
|
|
989
1272
|
}
|
|
990
1273
|
|
|
@@ -996,17 +1279,26 @@ export class SysExecutor extends Executor {
|
|
|
996
1279
|
allowEmpty,
|
|
997
1280
|
}: {
|
|
998
1281
|
allowEmpty?: AllowEmpty;
|
|
999
|
-
} = {}): AllowEmpty extends true
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1282
|
+
} = {}): AllowEmpty extends true
|
|
1283
|
+
? AppInfo | LibInfo | null
|
|
1284
|
+
: AppInfo | LibInfo {
|
|
1285
|
+
if (!this.hasScanInfo() && !allowEmpty)
|
|
1286
|
+
throw new Error("Scan info is not available");
|
|
1287
|
+
return this.#scanInfo as AllowEmpty extends true
|
|
1288
|
+
? AppInfo | LibInfo | null
|
|
1289
|
+
: AppInfo | LibInfo;
|
|
1290
|
+
}
|
|
1291
|
+
#getScanTemplateTasks(
|
|
1292
|
+
scanInfo: AppInfo | LibInfo,
|
|
1293
|
+
): (Promise<FileContent[]> | null)[] {
|
|
1004
1294
|
return [
|
|
1005
1295
|
this._applyTemplate({ basePath: "env", template: "env", scanInfo }),
|
|
1006
1296
|
this._applyTemplate({ basePath: "lib", template: "lib", scanInfo }),
|
|
1007
1297
|
this._applyTemplate({ basePath: ".", template: "server.ts", scanInfo }),
|
|
1008
1298
|
this._applyTemplate({ basePath: ".", template: "client.ts", scanInfo }),
|
|
1009
|
-
this.type === "lib"
|
|
1299
|
+
this.type === "lib"
|
|
1300
|
+
? this._applyTemplate({ basePath: ".", template: "index.ts", scanInfo })
|
|
1301
|
+
: null,
|
|
1010
1302
|
...scanFacetDirs.map((facet) =>
|
|
1011
1303
|
this._applyTemplate({
|
|
1012
1304
|
basePath: facet,
|
|
@@ -1067,7 +1359,11 @@ export class SysExecutor extends Executor {
|
|
|
1067
1359
|
if (writeLib) {
|
|
1068
1360
|
const libInfos = [...scanInfo.getLibInfos().values()];
|
|
1069
1361
|
await this.#updateDependencies(scanInfo);
|
|
1070
|
-
await Promise.all(
|
|
1362
|
+
await Promise.all(
|
|
1363
|
+
libInfos.flatMap((libInfo) =>
|
|
1364
|
+
libInfo.exec.#getScanTemplateTasks(libInfo),
|
|
1365
|
+
),
|
|
1366
|
+
);
|
|
1071
1367
|
}
|
|
1072
1368
|
}
|
|
1073
1369
|
this.#scanInfo = scanInfo;
|
|
@@ -1084,7 +1380,9 @@ export class SysExecutor extends Executor {
|
|
|
1084
1380
|
...libPackageJson,
|
|
1085
1381
|
dependencies: {
|
|
1086
1382
|
...Object.fromEntries(
|
|
1087
|
-
Object.entries(libPackageJson.dependencies ?? {}).filter(
|
|
1383
|
+
Object.entries(libPackageJson.dependencies ?? {}).filter(
|
|
1384
|
+
([dep]) => !devDependencySet.has(dep),
|
|
1385
|
+
),
|
|
1088
1386
|
),
|
|
1089
1387
|
...(Object.fromEntries(
|
|
1090
1388
|
dependencies
|
|
@@ -1095,82 +1393,134 @@ export class SysExecutor extends Executor {
|
|
|
1095
1393
|
},
|
|
1096
1394
|
devDependencies: {
|
|
1097
1395
|
...Object.fromEntries(
|
|
1098
|
-
Object.entries(libPackageJson.devDependencies ?? {}).filter(
|
|
1396
|
+
Object.entries(libPackageJson.devDependencies ?? {}).filter(
|
|
1397
|
+
([dep]) => !dependencySet.has(dep),
|
|
1398
|
+
),
|
|
1099
1399
|
),
|
|
1100
1400
|
...(Object.fromEntries(
|
|
1101
1401
|
devDependencies
|
|
1102
|
-
.filter(
|
|
1402
|
+
.filter(
|
|
1403
|
+
(dep) =>
|
|
1404
|
+
rootPackageJson.dependencies?.[dep] ||
|
|
1405
|
+
rootPackageJson.devDependencies?.[dep],
|
|
1406
|
+
)
|
|
1103
1407
|
.sort()
|
|
1104
|
-
.map((dep) => [
|
|
1408
|
+
.map((dep) => [
|
|
1409
|
+
dep,
|
|
1410
|
+
rootPackageJson.devDependencies?.[dep] ??
|
|
1411
|
+
rootPackageJson.dependencies?.[dep],
|
|
1412
|
+
]),
|
|
1105
1413
|
) as Record<string, string>),
|
|
1106
1414
|
},
|
|
1107
1415
|
};
|
|
1108
1416
|
await this.setPackageJson(libPkgJsonWithDeps);
|
|
1109
1417
|
}
|
|
1110
1418
|
override async getLocalFile(targetPath: string) {
|
|
1111
|
-
const filePath = path.isAbsolute(targetPath)
|
|
1419
|
+
const filePath = path.isAbsolute(targetPath)
|
|
1420
|
+
? targetPath
|
|
1421
|
+
: `${this.type}s/${this.name}/${targetPath}`;
|
|
1112
1422
|
const content = await this.workspace.readFile(filePath);
|
|
1113
1423
|
return { filePath, content };
|
|
1114
1424
|
}
|
|
1115
1425
|
|
|
1116
1426
|
async getDatabaseModules() {
|
|
1117
1427
|
const databaseModules = (await this.readdir("lib"))
|
|
1118
|
-
.filter(
|
|
1119
|
-
|
|
1428
|
+
.filter(
|
|
1429
|
+
(name) =>
|
|
1430
|
+
!name.startsWith("_") &&
|
|
1431
|
+
!name.startsWith("__") &&
|
|
1432
|
+
!name.endsWith(".ts"),
|
|
1433
|
+
)
|
|
1434
|
+
.filter((name) =>
|
|
1435
|
+
Bun.file(`${this.cwdPath}/lib/${name}/${name}.constant.ts`).exists(),
|
|
1436
|
+
);
|
|
1120
1437
|
return databaseModules;
|
|
1121
1438
|
}
|
|
1122
1439
|
|
|
1123
1440
|
async getServiceModules() {
|
|
1124
1441
|
const serviceModules = (await this.readdir("lib"))
|
|
1125
1442
|
.filter((name) => name.startsWith("_") && !name.startsWith("__"))
|
|
1126
|
-
.filter((name) =>
|
|
1443
|
+
.filter((name) =>
|
|
1444
|
+
Bun.file(`${this.cwdPath}/lib/${name}/${name}.service.ts`).exists(),
|
|
1445
|
+
);
|
|
1127
1446
|
return serviceModules;
|
|
1128
1447
|
}
|
|
1129
1448
|
|
|
1130
1449
|
async getScalarModules() {
|
|
1131
1450
|
const scalarModules = (await this.readdir("lib/__scalar"))
|
|
1132
1451
|
.filter((name) => !name.startsWith("_"))
|
|
1133
|
-
.filter((name) =>
|
|
1452
|
+
.filter((name) =>
|
|
1453
|
+
Bun.file(
|
|
1454
|
+
`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`,
|
|
1455
|
+
).exists(),
|
|
1456
|
+
);
|
|
1134
1457
|
return scalarModules;
|
|
1135
1458
|
}
|
|
1136
1459
|
|
|
1137
1460
|
async getViewComponents() {
|
|
1138
1461
|
const viewComponents = (await this.readdir("lib"))
|
|
1139
|
-
.filter(
|
|
1140
|
-
|
|
1462
|
+
.filter(
|
|
1463
|
+
(name) =>
|
|
1464
|
+
!name.startsWith("_") &&
|
|
1465
|
+
!name.startsWith("__") &&
|
|
1466
|
+
!name.endsWith(".ts"),
|
|
1467
|
+
)
|
|
1468
|
+
.filter((name) =>
|
|
1469
|
+
Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists(),
|
|
1470
|
+
);
|
|
1141
1471
|
return viewComponents;
|
|
1142
1472
|
}
|
|
1143
1473
|
|
|
1144
1474
|
async getUnitComponents() {
|
|
1145
1475
|
const unitComponents = (await this.readdir("lib"))
|
|
1146
|
-
.filter(
|
|
1147
|
-
|
|
1476
|
+
.filter(
|
|
1477
|
+
(name) =>
|
|
1478
|
+
!name.startsWith("_") &&
|
|
1479
|
+
!name.startsWith("__") &&
|
|
1480
|
+
!name.endsWith(".ts"),
|
|
1481
|
+
)
|
|
1482
|
+
.filter((name) =>
|
|
1483
|
+
Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists(),
|
|
1484
|
+
);
|
|
1148
1485
|
return unitComponents;
|
|
1149
1486
|
}
|
|
1150
1487
|
async getTemplateComponents() {
|
|
1151
1488
|
const templateComponents = (await this.readdir("lib"))
|
|
1152
|
-
.filter(
|
|
1153
|
-
|
|
1489
|
+
.filter(
|
|
1490
|
+
(name) =>
|
|
1491
|
+
!name.startsWith("_") &&
|
|
1492
|
+
!name.startsWith("__") &&
|
|
1493
|
+
!name.endsWith(".ts"),
|
|
1494
|
+
)
|
|
1495
|
+
.filter((name) =>
|
|
1496
|
+
Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists(),
|
|
1497
|
+
);
|
|
1154
1498
|
return templateComponents;
|
|
1155
1499
|
}
|
|
1156
1500
|
|
|
1157
1501
|
async getViewsSourceCode() {
|
|
1158
1502
|
const viewComponents = await this.getViewComponents();
|
|
1159
1503
|
return Promise.all(
|
|
1160
|
-
viewComponents.map((viewComponent) =>
|
|
1504
|
+
viewComponents.map((viewComponent) =>
|
|
1505
|
+
this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`),
|
|
1506
|
+
),
|
|
1161
1507
|
);
|
|
1162
1508
|
}
|
|
1163
1509
|
async getUnitsSourceCode() {
|
|
1164
1510
|
const unitComponents = await this.getUnitComponents();
|
|
1165
1511
|
return Promise.all(
|
|
1166
|
-
unitComponents.map((unitComponent) =>
|
|
1512
|
+
unitComponents.map((unitComponent) =>
|
|
1513
|
+
this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`),
|
|
1514
|
+
),
|
|
1167
1515
|
);
|
|
1168
1516
|
}
|
|
1169
1517
|
async getTemplatesSourceCode() {
|
|
1170
1518
|
const templateComponents = await this.getTemplateComponents();
|
|
1171
1519
|
return Promise.all(
|
|
1172
1520
|
templateComponents.map((templateComponent) =>
|
|
1173
|
-
this.getLocalFile(
|
|
1521
|
+
this.getLocalFile(
|
|
1522
|
+
`lib/${templateComponent}/${templateComponent}.Template.tsx`,
|
|
1523
|
+
),
|
|
1174
1524
|
),
|
|
1175
1525
|
);
|
|
1176
1526
|
}
|
|
@@ -1179,7 +1529,9 @@ export class SysExecutor extends Executor {
|
|
|
1179
1529
|
const scalarModules = await this.getScalarModules();
|
|
1180
1530
|
return Promise.all(
|
|
1181
1531
|
scalarModules.map((scalarModule) =>
|
|
1182
|
-
this.getLocalFile(
|
|
1532
|
+
this.getLocalFile(
|
|
1533
|
+
`lib/__scalar/${scalarModule}/${scalarModule}.constant.ts`,
|
|
1534
|
+
),
|
|
1183
1535
|
),
|
|
1184
1536
|
);
|
|
1185
1537
|
}
|
|
@@ -1187,13 +1539,19 @@ export class SysExecutor extends Executor {
|
|
|
1187
1539
|
async getScalarDictionaryFiles() {
|
|
1188
1540
|
const scalarModules = await this.getScalarModules();
|
|
1189
1541
|
return Promise.all(
|
|
1190
|
-
scalarModules.map((scalarModule) =>
|
|
1542
|
+
scalarModules.map((scalarModule) =>
|
|
1543
|
+
this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`),
|
|
1544
|
+
),
|
|
1191
1545
|
);
|
|
1192
1546
|
}
|
|
1193
1547
|
|
|
1194
1548
|
async getConstantFiles() {
|
|
1195
1549
|
const modules = await this.getModules();
|
|
1196
|
-
return Promise.all(
|
|
1550
|
+
return Promise.all(
|
|
1551
|
+
modules.map((module) =>
|
|
1552
|
+
this.getLocalFile(`lib/${module}/${module}.constant.ts`),
|
|
1553
|
+
),
|
|
1554
|
+
);
|
|
1197
1555
|
}
|
|
1198
1556
|
async getConstantFilesWithLibs() {
|
|
1199
1557
|
const scanInfo =
|
|
@@ -1209,11 +1567,19 @@ export class SysExecutor extends Executor {
|
|
|
1209
1567
|
...(await LibExecutor.from(this, lib).getScalarConstantFiles()),
|
|
1210
1568
|
]),
|
|
1211
1569
|
);
|
|
1212
|
-
return [
|
|
1570
|
+
return [
|
|
1571
|
+
...sysContantFiles,
|
|
1572
|
+
...sysScalarConstantFiles,
|
|
1573
|
+
...libConstantFiles.flat(),
|
|
1574
|
+
];
|
|
1213
1575
|
}
|
|
1214
1576
|
async getDictionaryFiles() {
|
|
1215
1577
|
const modules = await this.getModules();
|
|
1216
|
-
return Promise.all(
|
|
1578
|
+
return Promise.all(
|
|
1579
|
+
modules.map((module) =>
|
|
1580
|
+
this.getLocalFile(`lib/${module}/${module}.dictionary.ts`),
|
|
1581
|
+
),
|
|
1582
|
+
);
|
|
1217
1583
|
}
|
|
1218
1584
|
override async applyTemplate(options: {
|
|
1219
1585
|
basePath: string;
|
|
@@ -1224,7 +1590,10 @@ export class SysExecutor extends Executor {
|
|
|
1224
1590
|
const dict = {
|
|
1225
1591
|
...(options.dict ?? {}),
|
|
1226
1592
|
...Object.fromEntries(
|
|
1227
|
-
Object.entries(options.dict ?? {}).map(([key, value]) => [
|
|
1593
|
+
Object.entries(options.dict ?? {}).map(([key, value]) => [
|
|
1594
|
+
capitalize(key),
|
|
1595
|
+
capitalize(value),
|
|
1596
|
+
]),
|
|
1228
1597
|
),
|
|
1229
1598
|
};
|
|
1230
1599
|
const scanInfo = await this.scan();
|
|
@@ -1247,13 +1616,17 @@ export class AppExecutor extends SysExecutor {
|
|
|
1247
1616
|
override emoji = execEmoji.app;
|
|
1248
1617
|
constructor({ workspace, name }: AppExecutorOptions) {
|
|
1249
1618
|
super({ workspace, name, type: "app" });
|
|
1250
|
-
this.dist = new Executor(
|
|
1619
|
+
this.dist = new Executor(
|
|
1620
|
+
`dist/${name}`,
|
|
1621
|
+
`${this.workspace.workspaceRoot}/dist/apps/${name}`,
|
|
1622
|
+
);
|
|
1251
1623
|
}
|
|
1252
1624
|
static #execs = new Map<string, AppExecutor>();
|
|
1253
1625
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
|
|
1254
1626
|
const exec = AppExecutor.#execs.get(name);
|
|
1255
1627
|
if (exec) return exec;
|
|
1256
|
-
else if (executor instanceof WorkspaceExecutor)
|
|
1628
|
+
else if (executor instanceof WorkspaceExecutor)
|
|
1629
|
+
return new AppExecutor({ workspace: executor, name });
|
|
1257
1630
|
else return new AppExecutor({ workspace: executor.workspace, name });
|
|
1258
1631
|
}
|
|
1259
1632
|
getEnv() {
|
|
@@ -1263,7 +1636,9 @@ export class AppExecutor extends SysExecutor {
|
|
|
1263
1636
|
const basePort = 8282;
|
|
1264
1637
|
const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
|
|
1265
1638
|
const PORT = basePort ? (basePort + portOffset).toString() : undefined;
|
|
1266
|
-
const AKAN_PUBLIC_SERVER_PORT = portOffset
|
|
1639
|
+
const AKAN_PUBLIC_SERVER_PORT = portOffset
|
|
1640
|
+
? (8282 + portOffset).toString()
|
|
1641
|
+
: undefined;
|
|
1267
1642
|
return {
|
|
1268
1643
|
...process.env,
|
|
1269
1644
|
AKAN_PUBLIC_APP_NAME: this.name,
|
|
@@ -1276,15 +1651,22 @@ export class AppExecutor extends SysExecutor {
|
|
|
1276
1651
|
}
|
|
1277
1652
|
async prepareCommand(type: "build" | "start") {
|
|
1278
1653
|
const akanConfig = await this.getConfig();
|
|
1279
|
-
const databaseMode =
|
|
1654
|
+
const databaseMode =
|
|
1655
|
+
process.env.AKAN_DATABASE_MODE ??
|
|
1656
|
+
akanConfig.defaultDatabaseMode ??
|
|
1657
|
+
"single";
|
|
1280
1658
|
const routeEnv = {
|
|
1281
1659
|
AKAN_PUBLIC_BASE_PATHS: [...akanConfig.basePaths].join(","),
|
|
1282
1660
|
AKAN_DATABASE_MODE: databaseMode,
|
|
1283
1661
|
};
|
|
1284
1662
|
Object.assign(process.env, routeEnv);
|
|
1285
1663
|
if (type === "build") {
|
|
1286
|
-
if (await this.exists(this.dist.cwdPath))
|
|
1287
|
-
|
|
1664
|
+
if (await this.exists(this.dist.cwdPath))
|
|
1665
|
+
await this.dist.exec(`rm -rf ${this.dist.cwdPath}`);
|
|
1666
|
+
await Promise.all([
|
|
1667
|
+
this.dist.mkdir("private"),
|
|
1668
|
+
this.dist.mkdir("public"),
|
|
1669
|
+
]);
|
|
1288
1670
|
await Promise.all([
|
|
1289
1671
|
this.cp("private", `${this.dist.cwdPath}/private`),
|
|
1290
1672
|
this.cp("public", `${this.dist.cwdPath}/public`),
|
|
@@ -1322,7 +1704,11 @@ export class AppExecutor extends SysExecutor {
|
|
|
1322
1704
|
}
|
|
1323
1705
|
|
|
1324
1706
|
#pageKeys: string[] | null = null;
|
|
1325
|
-
async getPageKeys({
|
|
1707
|
+
async getPageKeys({
|
|
1708
|
+
refresh,
|
|
1709
|
+
}: {
|
|
1710
|
+
refresh?: boolean;
|
|
1711
|
+
} = {}): Promise<string[]> {
|
|
1326
1712
|
if (this.#pageKeys && !refresh) return this.#pageKeys;
|
|
1327
1713
|
const akanConfig = await this.getConfig();
|
|
1328
1714
|
const glob = new Bun.Glob("**/*");
|
|
@@ -1350,10 +1736,18 @@ export class AppExecutor extends SysExecutor {
|
|
|
1350
1736
|
});
|
|
1351
1737
|
const parsed = parseRouteModuleKey(key);
|
|
1352
1738
|
if (parsed.isInternalRootLayout) {
|
|
1353
|
-
throw new Error(
|
|
1739
|
+
throw new Error(
|
|
1740
|
+
`[route-convention] __root_layout is reserved for Akan.js generated root layout: ${absPath}`,
|
|
1741
|
+
);
|
|
1354
1742
|
}
|
|
1355
|
-
const isRootLayout =
|
|
1356
|
-
|
|
1743
|
+
const isRootLayout =
|
|
1744
|
+
parsed.kind === "layout" && parsed.moduleSegments.at(-1) === "_layout";
|
|
1745
|
+
validateRouteSourceExports(
|
|
1746
|
+
await Bun.file(absPath).text(),
|
|
1747
|
+
absPath,
|
|
1748
|
+
parsed.kind,
|
|
1749
|
+
{ rootLayout: isRootLayout },
|
|
1750
|
+
);
|
|
1357
1751
|
pageKeys.push(key);
|
|
1358
1752
|
}
|
|
1359
1753
|
pageKeys.sort();
|
|
@@ -1369,27 +1763,56 @@ export class AppExecutor extends SysExecutor {
|
|
|
1369
1763
|
const projectAssetsPath = `${this.cwdPath}/private`;
|
|
1370
1764
|
const projectPublicLibPath = `${projectPublicPath}/libs`;
|
|
1371
1765
|
const projectAssetsLibPath = `${projectAssetsPath}/libs`;
|
|
1372
|
-
await Promise.all([
|
|
1766
|
+
await Promise.all([
|
|
1767
|
+
this.removeDir(projectPublicLibPath),
|
|
1768
|
+
this.removeDir(projectAssetsLibPath),
|
|
1769
|
+
]);
|
|
1373
1770
|
const targetPublicDeps = [] as string[];
|
|
1374
1771
|
for (const dep of libDeps) {
|
|
1375
|
-
if (
|
|
1772
|
+
if (
|
|
1773
|
+
await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/public`)
|
|
1774
|
+
)
|
|
1775
|
+
targetPublicDeps.push(dep);
|
|
1376
1776
|
}
|
|
1377
1777
|
const targetAssetsDeps = [] as string[];
|
|
1378
1778
|
for (const dep of libDeps) {
|
|
1379
|
-
if (
|
|
1779
|
+
if (
|
|
1780
|
+
await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/private`)
|
|
1781
|
+
)
|
|
1782
|
+
targetAssetsDeps.push(dep);
|
|
1380
1783
|
}
|
|
1381
|
-
await Promise.all(
|
|
1382
|
-
|
|
1784
|
+
await Promise.all(
|
|
1785
|
+
targetPublicDeps.map((dep) =>
|
|
1786
|
+
this.mkdir(`${projectPublicLibPath}/${dep}`),
|
|
1787
|
+
),
|
|
1788
|
+
);
|
|
1789
|
+
await Promise.all(
|
|
1790
|
+
targetAssetsDeps.map((dep) =>
|
|
1791
|
+
this.mkdir(`${projectAssetsLibPath}/${dep}`),
|
|
1792
|
+
),
|
|
1793
|
+
);
|
|
1383
1794
|
await Promise.all([
|
|
1384
1795
|
...targetPublicDeps.map((dep) =>
|
|
1385
|
-
this.cp(
|
|
1796
|
+
this.cp(
|
|
1797
|
+
`${this.workspace.workspaceRoot}/libs/${dep}/public`,
|
|
1798
|
+
`${projectPublicLibPath}/${dep}`,
|
|
1799
|
+
),
|
|
1386
1800
|
),
|
|
1387
1801
|
...targetAssetsDeps.map((dep) =>
|
|
1388
|
-
this.cp(
|
|
1802
|
+
this.cp(
|
|
1803
|
+
`${this.workspace.workspaceRoot}/libs/${dep}/private`,
|
|
1804
|
+
`${projectAssetsLibPath}/${dep}`,
|
|
1805
|
+
),
|
|
1389
1806
|
),
|
|
1390
1807
|
]);
|
|
1391
1808
|
}
|
|
1392
|
-
async scanSync({
|
|
1809
|
+
async scanSync({
|
|
1810
|
+
refresh = false,
|
|
1811
|
+
write = true,
|
|
1812
|
+
}: {
|
|
1813
|
+
refresh?: boolean;
|
|
1814
|
+
write?: boolean;
|
|
1815
|
+
} = {}) {
|
|
1393
1816
|
const scanInfo = (await this.scan({
|
|
1394
1817
|
refresh,
|
|
1395
1818
|
write,
|
|
@@ -1414,13 +1837,17 @@ export class LibExecutor extends SysExecutor {
|
|
|
1414
1837
|
override emoji = execEmoji.lib;
|
|
1415
1838
|
constructor({ workspace, name }: LibExecutorOptions) {
|
|
1416
1839
|
super({ workspace, name, type: "lib" });
|
|
1417
|
-
this.dist = new Executor(
|
|
1840
|
+
this.dist = new Executor(
|
|
1841
|
+
`dist/${name}`,
|
|
1842
|
+
`${this.workspace.workspaceRoot}/dist/libs/${name}`,
|
|
1843
|
+
);
|
|
1418
1844
|
}
|
|
1419
1845
|
static #execs = new Map<string, LibExecutor>();
|
|
1420
1846
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
|
|
1421
1847
|
const exec = LibExecutor.#execs.get(name);
|
|
1422
1848
|
if (exec) return exec;
|
|
1423
|
-
else if (executor instanceof WorkspaceExecutor)
|
|
1849
|
+
else if (executor instanceof WorkspaceExecutor)
|
|
1850
|
+
return new LibExecutor({ workspace: executor, name });
|
|
1424
1851
|
else return new LibExecutor({ workspace: executor.workspace, name });
|
|
1425
1852
|
}
|
|
1426
1853
|
|
|
@@ -1441,14 +1868,21 @@ export class PkgExecutor extends Executor {
|
|
|
1441
1868
|
override name: string;
|
|
1442
1869
|
dist: Executor;
|
|
1443
1870
|
override emoji = execEmoji.pkg;
|
|
1444
|
-
constructor({
|
|
1871
|
+
constructor({
|
|
1872
|
+
workspace = WorkspaceExecutor.fromRoot(),
|
|
1873
|
+
name,
|
|
1874
|
+
}: PkgExecutorOptions) {
|
|
1445
1875
|
super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
|
|
1446
1876
|
this.workspace = workspace;
|
|
1447
1877
|
this.name = name;
|
|
1448
|
-
this.dist = new Executor(
|
|
1878
|
+
this.dist = new Executor(
|
|
1879
|
+
`dist/${name}`,
|
|
1880
|
+
`${this.workspace.workspaceRoot}/dist/pkgs/${name}`,
|
|
1881
|
+
);
|
|
1449
1882
|
}
|
|
1450
1883
|
static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
|
|
1451
|
-
if (executor instanceof WorkspaceExecutor)
|
|
1884
|
+
if (executor instanceof WorkspaceExecutor)
|
|
1885
|
+
return new PkgExecutor({ workspace: executor, name });
|
|
1452
1886
|
return new PkgExecutor({ workspace: executor.workspace, name });
|
|
1453
1887
|
}
|
|
1454
1888
|
|
|
@@ -1460,7 +1894,10 @@ export class PkgExecutor extends Executor {
|
|
|
1460
1894
|
this.#scanInfo = scanInfo;
|
|
1461
1895
|
return scanInfo;
|
|
1462
1896
|
}
|
|
1463
|
-
async #getDependencyVersion(
|
|
1897
|
+
async #getDependencyVersion(
|
|
1898
|
+
rootPackageJson: PackageJson,
|
|
1899
|
+
dep: string,
|
|
1900
|
+
): Promise<string | undefined> {
|
|
1464
1901
|
const rootDeps = {
|
|
1465
1902
|
...rootPackageJson.dependencies,
|
|
1466
1903
|
...rootPackageJson.devDependencies,
|
|
@@ -1470,9 +1907,15 @@ export class PkgExecutor extends Executor {
|
|
|
1470
1907
|
|
|
1471
1908
|
try {
|
|
1472
1909
|
const packageJsonPath = `pkgs/${dep}/package.json`;
|
|
1473
|
-
if (
|
|
1910
|
+
if (
|
|
1911
|
+
!(await Bun.file(
|
|
1912
|
+
path.join(this.workspace.workspaceRoot, packageJsonPath),
|
|
1913
|
+
).exists())
|
|
1914
|
+
)
|
|
1915
|
+
return undefined;
|
|
1474
1916
|
const packageJson = await this.workspace.readJson(packageJsonPath);
|
|
1475
|
-
if ((packageJson as PackageJson).name === dep)
|
|
1917
|
+
if ((packageJson as PackageJson).name === dep)
|
|
1918
|
+
return (packageJson as PackageJson).version;
|
|
1476
1919
|
} catch {
|
|
1477
1920
|
return undefined;
|
|
1478
1921
|
}
|
|
@@ -1483,7 +1926,9 @@ export class PkgExecutor extends Executor {
|
|
|
1483
1926
|
devDependencies: string[] = [],
|
|
1484
1927
|
): Promise<Pick<PackageJson, "dependencies" | "devDependencies">> {
|
|
1485
1928
|
const dependencyNames = [...new Set(dependencies)].sort();
|
|
1486
|
-
const devDependencyNames = [...new Set(devDependencies)]
|
|
1929
|
+
const devDependencyNames = [...new Set(devDependencies)]
|
|
1930
|
+
.filter((dep) => !dependencyNames.includes(dep))
|
|
1931
|
+
.sort();
|
|
1487
1932
|
const dependencyVersions = new Map<string, string>();
|
|
1488
1933
|
const missingDeps: string[] = [];
|
|
1489
1934
|
for (const dep of [...dependencyNames, ...devDependencyNames]) {
|
|
@@ -1492,26 +1937,40 @@ export class PkgExecutor extends Executor {
|
|
|
1492
1937
|
else missingDeps.push(dep);
|
|
1493
1938
|
}
|
|
1494
1939
|
if (missingDeps.length > 0)
|
|
1495
|
-
throw new Error(
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Missing dependency versions in root package.json: ${missingDeps.join(", ")}`,
|
|
1942
|
+
);
|
|
1496
1943
|
|
|
1497
1944
|
const toDependencyEntries = (names: string[]) =>
|
|
1498
1945
|
names.map((dep) => {
|
|
1499
1946
|
const version = dependencyVersions.get(dep);
|
|
1500
|
-
if (!version)
|
|
1947
|
+
if (!version)
|
|
1948
|
+
throw new Error(
|
|
1949
|
+
`Missing dependency versions in root package.json: ${dep}`,
|
|
1950
|
+
);
|
|
1501
1951
|
return [dep, version] as const;
|
|
1502
1952
|
});
|
|
1503
1953
|
|
|
1504
1954
|
return {
|
|
1505
1955
|
dependencies: Object.fromEntries(toDependencyEntries(dependencyNames)),
|
|
1506
|
-
devDependencies: Object.fromEntries(
|
|
1956
|
+
devDependencies: Object.fromEntries(
|
|
1957
|
+
toDependencyEntries(devDependencyNames),
|
|
1958
|
+
),
|
|
1507
1959
|
};
|
|
1508
1960
|
}
|
|
1509
1961
|
async updatePackageJsonDependencies(
|
|
1510
1962
|
dependencies: string[] = [],
|
|
1511
1963
|
devDependencies: string[] = [],
|
|
1512
1964
|
): Promise<PackageJson> {
|
|
1513
|
-
const [rootPackageJson, pkgJson] = await Promise.all([
|
|
1514
|
-
|
|
1965
|
+
const [rootPackageJson, pkgJson] = await Promise.all([
|
|
1966
|
+
this.workspace.getPackageJson(),
|
|
1967
|
+
this.getPackageJson(),
|
|
1968
|
+
]);
|
|
1969
|
+
const dependencyMaps = await this.#toDependencyMap(
|
|
1970
|
+
rootPackageJson,
|
|
1971
|
+
dependencies,
|
|
1972
|
+
devDependencies,
|
|
1973
|
+
);
|
|
1515
1974
|
const newPkgJson = {
|
|
1516
1975
|
...pkgJson,
|
|
1517
1976
|
...dependencyMaps,
|
|
@@ -1519,9 +1978,19 @@ export class PkgExecutor extends Executor {
|
|
|
1519
1978
|
await this.writeJson("package.json", newPkgJson);
|
|
1520
1979
|
return newPkgJson;
|
|
1521
1980
|
}
|
|
1522
|
-
async generateDistPackageJson(
|
|
1523
|
-
|
|
1524
|
-
|
|
1981
|
+
async generateDistPackageJson(
|
|
1982
|
+
dependencies: string[] = [],
|
|
1983
|
+
devDependencies: string[] = [],
|
|
1984
|
+
): Promise<PackageJson> {
|
|
1985
|
+
const [rootPackageJson, pkgJson] = await Promise.all([
|
|
1986
|
+
this.workspace.getPackageJson(),
|
|
1987
|
+
this.getPackageJson(),
|
|
1988
|
+
]);
|
|
1989
|
+
const dependencyMaps = await this.#toDependencyMap(
|
|
1990
|
+
rootPackageJson,
|
|
1991
|
+
dependencies,
|
|
1992
|
+
devDependencies,
|
|
1993
|
+
);
|
|
1525
1994
|
const distPkgJson: PackageJson = {
|
|
1526
1995
|
...pkgJson,
|
|
1527
1996
|
type: "module",
|
|
@@ -1536,7 +2005,10 @@ export class PkgExecutor extends Executor {
|
|
|
1536
2005
|
engines: { bun: ">=1.3.13" },
|
|
1537
2006
|
...dependencyMaps,
|
|
1538
2007
|
};
|
|
1539
|
-
await Promise.all([
|
|
2008
|
+
await Promise.all([
|
|
2009
|
+
this.dist.writeJson("package.json", distPkgJson),
|
|
2010
|
+
this.writeJson("package.json", distPkgJson),
|
|
2011
|
+
]);
|
|
1540
2012
|
return distPkgJson;
|
|
1541
2013
|
}
|
|
1542
2014
|
async build(): Promise<void> {
|
|
@@ -1549,7 +2021,10 @@ export class PkgExecutor extends Executor {
|
|
|
1549
2021
|
await this.cp(`${this.cwdPath}/dist`, this.dist.cwdPath);
|
|
1550
2022
|
}
|
|
1551
2023
|
async generateTsconfigJson(): Promise<TsConfigJson> {
|
|
1552
|
-
const [rootTsconfig, pkgTsconfig] = await Promise.all([
|
|
2024
|
+
const [rootTsconfig, pkgTsconfig] = await Promise.all([
|
|
2025
|
+
this.workspace.getTsConfig(),
|
|
2026
|
+
this.getTsConfig(),
|
|
2027
|
+
]);
|
|
1553
2028
|
const tsconfig: TsConfigJson = {
|
|
1554
2029
|
...rootTsconfig,
|
|
1555
2030
|
...pkgTsconfig,
|
|
@@ -1572,7 +2047,10 @@ export class ModuleExecutor extends Executor {
|
|
|
1572
2047
|
sys: SysExecutor;
|
|
1573
2048
|
override emoji = execEmoji.module;
|
|
1574
2049
|
constructor({ sys, name }: ModuleExecutorOptions) {
|
|
1575
|
-
super(
|
|
2050
|
+
super(
|
|
2051
|
+
name,
|
|
2052
|
+
`${sys.workspace.workspaceRoot}/${sys.type}s/${sys.name}/lib/${name}`,
|
|
2053
|
+
);
|
|
1576
2054
|
this.sys = sys;
|
|
1577
2055
|
}
|
|
1578
2056
|
static from(sysExecutor: SysExecutor, name: string) {
|