@akanjs/devkit 2.1.1-rc.1 → 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/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 { copyFile, mkdir, readdir as readDirEntries, stat } from "node:fs/promises";
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 { AkanAppConfig, AkanLibConfig, decreaseBuildNum, increaseBuildNum } from "./akanConfig";
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) => (/^[\w@%+=:,./-]+$/.test(value) ? value : JSON.stringify(value));
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 ? `signal: ${signal}` : `exit code: ${code ?? "unknown"}`;
114
+ const status = signal
115
+ ? `signal: ${signal}`
116
+ : `exit code: ${code ?? "unknown"}`;
104
117
  const output = (stderr || stdout).trim();
105
- super([`Command failed: ${displayCommand}`, `cwd: ${cwd}`, status, output ? `\n${output}` : ""].join("\n"), {
106
- cause,
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 ") ? trimmed.slice("export ".length).trim() : trimmed;
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 ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
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(["default", "pageConfig", "head", "generateHead", "Loading"]);
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(["default", "head", "generateHead", "Loading"]);
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(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
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" ? PAGE_ROUTE_EXPORTS : options.rootLayout ? ROOT_LAYOUT_EXPORTS : LAYOUT_ROUTE_EXPORTS;
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(`[route-convention] unsupported export "${name}" in ${filePath}`);
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 (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) continue;
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) throw new Error(`[route-convention] export * is not allowed in route modules: ${filePath}`);
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) ? ts.getModifiers(statement) : undefined;
201
- const isExported = modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
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 = modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
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)) assertExport(declaration.name.text);
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(`[route-convention] head and generateHead cannot both be exported in ${filePath}`);
281
+ throw new Error(
282
+ `[route-convention] head and generateHead cannot both be exported in ${filePath}`,
283
+ );
221
284
  }
222
285
  }
223
286
 
@@ -261,16 +324,40 @@ export class Executor {
261
324
  });
262
325
  return new Promise((resolve, reject) => {
263
326
  proc.on("error", (error) => {
264
- reject(new CommandExecutionError({ command, cwd, code: null, signal: null, stdout, stderr, cause: error }));
327
+ reject(
328
+ new CommandExecutionError({
329
+ command,
330
+ cwd,
331
+ code: null,
332
+ signal: null,
333
+ stdout,
334
+ stderr,
335
+ cause: error,
336
+ }),
337
+ );
265
338
  });
266
339
  proc.on("exit", (code, signal) => {
267
- if (!!code || signal) reject(new CommandExecutionError({ command, cwd, code, signal, stdout, stderr }));
340
+ if (!!code || signal)
341
+ reject(
342
+ new CommandExecutionError({
343
+ command,
344
+ cwd,
345
+ code,
346
+ signal,
347
+ stdout,
348
+ stderr,
349
+ }),
350
+ );
268
351
  else resolve({ code, signal });
269
352
  });
270
353
  });
271
354
  }
272
355
 
273
- spawn(command: string, args: string[] = [], options: SpawnOptions = {}): Promise<string> {
356
+ spawn(
357
+ command: string,
358
+ args: string[] = [],
359
+ options: SpawnOptions = {},
360
+ ): Promise<string> {
274
361
  const cwd = options.cwd?.toString() ?? this.cwdPath;
275
362
  const proc = spawn(command, args, {
276
363
  cwd: this.cwdPath,
@@ -293,17 +380,40 @@ export class Executor {
293
380
  return new Promise((resolve, reject) => {
294
381
  proc.on("error", (error) => {
295
382
  reject(
296
- new CommandExecutionError({ command, args, cwd, code: null, signal: null, stdout, stderr, cause: error }),
383
+ new CommandExecutionError({
384
+ command,
385
+ args,
386
+ cwd,
387
+ code: null,
388
+ signal: null,
389
+ stdout,
390
+ stderr,
391
+ cause: error,
392
+ }),
297
393
  );
298
394
  });
299
395
  proc.on("close", (code, signal) => {
300
396
  if (code !== 0 || signal)
301
- reject(new CommandExecutionError({ command, args, cwd, code, signal, stdout, stderr }));
397
+ reject(
398
+ new CommandExecutionError({
399
+ command,
400
+ args,
401
+ cwd,
402
+ code,
403
+ signal,
404
+ stdout,
405
+ stderr,
406
+ }),
407
+ );
302
408
  else resolve(stdout);
303
409
  });
304
410
  });
305
411
  }
306
- spawnSync(command: string, args: string[] = [], options: SpawnOptions = {}): ChildProcess {
412
+ spawnSync(
413
+ command: string,
414
+ args: string[] = [],
415
+ options: SpawnOptions = {},
416
+ ): ChildProcess {
307
417
  const proc = spawn(command, args, {
308
418
  cwd: this.cwdPath,
309
419
  // stdio: "inherit",
@@ -345,7 +455,17 @@ export class Executor {
345
455
  });
346
456
  proc.on("exit", (code, signal) => {
347
457
  if (!!code || signal)
348
- reject(new CommandExecutionError({ command: modulePath, args, cwd, code, signal, stdout, stderr }));
458
+ reject(
459
+ new CommandExecutionError({
460
+ command: modulePath,
461
+ args,
462
+ cwd,
463
+ code,
464
+ signal,
465
+ stdout,
466
+ stderr,
467
+ }),
468
+ );
349
469
  else resolve({ code, signal });
350
470
  });
351
471
  });
@@ -374,7 +494,8 @@ export class Executor {
374
494
  }
375
495
  async mkdir(dirPath: string) {
376
496
  const writePath = this.getPath(dirPath);
377
- if (!(await FileSys.dirExists(writePath))) await mkdir(writePath, { recursive: true });
497
+ if (!(await FileSys.dirExists(writePath)))
498
+ await mkdir(writePath, { recursive: true });
378
499
  this.logger.verbose(`Make directory ${writePath}`);
379
500
  return this;
380
501
  }
@@ -387,16 +508,27 @@ export class Executor {
387
508
  return [];
388
509
  }
389
510
  }
390
- async getAllFiles(pattern = "**/*", { cwd }: { cwd?: string } = {}): Promise<string[]> {
511
+ async getAllFiles(
512
+ pattern = "**/*",
513
+ { cwd }: { cwd?: string } = {},
514
+ ): Promise<string[]> {
391
515
  const glob = new Bun.Glob(pattern);
392
- return Array.from(glob.scanSync({ cwd: cwd ?? this.cwdPath, onlyFiles: true }));
516
+ return Array.from(
517
+ glob.scanSync({ cwd: cwd ?? this.cwdPath, onlyFiles: true }),
518
+ );
393
519
  }
394
- async getFilesAndDirs(dirPath: string): Promise<{ files: string[]; dirs: string[] }> {
520
+ async getFilesAndDirs(
521
+ dirPath: string,
522
+ ): Promise<{ files: string[]; dirs: string[] }> {
395
523
  const fullDirPath = this.getPath(dirPath);
396
524
  const fileGlob = new Bun.Glob("*");
397
- const files = Array.from(fileGlob.scanSync({ cwd: fullDirPath, onlyFiles: true }));
525
+ const files = Array.from(
526
+ fileGlob.scanSync({ cwd: fullDirPath, onlyFiles: true }),
527
+ );
398
528
  const dirGlob = new Bun.Glob("*");
399
- const allEntries = Array.from(dirGlob.scanSync({ cwd: fullDirPath, onlyFiles: false }));
529
+ const allEntries = Array.from(
530
+ dirGlob.scanSync({ cwd: fullDirPath, onlyFiles: false }),
531
+ );
400
532
  const dirs = allEntries.filter((entry) => !files.includes(entry));
401
533
  return { files, dirs };
402
534
  }
@@ -424,7 +556,8 @@ export class Executor {
424
556
  const writePath = this.getPath(filePath);
425
557
  const dir = path.dirname(writePath);
426
558
  if (!(await FileSys.dirExists(dir))) await mkdir(dir, { recursive: true });
427
- let contentStr = typeof content === "string" ? content : JSON.stringify(content, null, 2);
559
+ let contentStr =
560
+ typeof content === "string" ? content : JSON.stringify(content, null, 2);
428
561
 
429
562
  if (await FileSys.fileExists(writePath)) {
430
563
  const currentContent = await FileSys.readText(writePath);
@@ -433,7 +566,8 @@ export class Executor {
433
566
  contentStr = currentContent;
434
567
  } else {
435
568
  await FileSys.writeText(writePath, contentStr);
436
- if (Logger.isVerbose()) this.logger.rawLog(chalk.yellow(`File Update: ${filePath}`));
569
+ if (Logger.isVerbose())
570
+ this.logger.rawLog(chalk.yellow(`File Update: ${filePath}`));
437
571
  }
438
572
  } else {
439
573
  await FileSys.writeText(writePath, contentStr);
@@ -445,7 +579,9 @@ export class Executor {
445
579
  await this.writeFile(filePath, content);
446
580
  }
447
581
  async getLocalFile(targetPath: string) {
448
- const filePath = path.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
582
+ const filePath = path.isAbsolute(targetPath)
583
+ ? targetPath
584
+ : targetPath.replace(this.cwdPath, "");
449
585
  const content = await this.readFile(filePath);
450
586
  return { filePath, content };
451
587
  }
@@ -462,7 +598,8 @@ export class Executor {
462
598
  const dest = this.getPath(destPath);
463
599
  if (!(await FileSys.exists(src))) return;
464
600
  const isDirectory = (await stat(src)).isDirectory();
465
- if (!(await FileSys.exists(dest)) && isDirectory) await mkdir(dest, { recursive: true });
601
+ if (!(await FileSys.exists(dest)) && isDirectory)
602
+ await mkdir(dest, { recursive: true });
466
603
  await $`cp -r ${src}${isDirectory ? "/." : ""} ${dest}`;
467
604
  }
468
605
  log(msg: string) {
@@ -477,12 +614,22 @@ export class Executor {
477
614
  this.logger.debug(msg);
478
615
  return this;
479
616
  }
480
- spinning(msg: string, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
617
+ spinning(
618
+ msg: string,
619
+ {
620
+ prefix = `${this.emoji}${this.name}`,
621
+ indent = 0,
622
+ enableSpin = !Executor.verbose,
623
+ } = {},
624
+ ) {
481
625
  return new Spinner(msg, { prefix, indent, enableSpin }).start();
482
626
  }
483
627
 
484
628
  #tsconfig: TsConfigJson | null = null;
485
- async getTsConfig(pathname = "tsconfig.json", { refresh }: { refresh?: boolean } = {}): Promise<TsConfigJson> {
629
+ async getTsConfig(
630
+ pathname = "tsconfig.json",
631
+ { refresh }: { refresh?: boolean } = {},
632
+ ): Promise<TsConfigJson> {
486
633
  if (this.#tsconfig && !refresh) return this.#tsconfig;
487
634
  const tsconfig = (await this.readJson(pathname)) as TsConfigJson;
488
635
  if (tsconfig.extends) {
@@ -490,7 +637,10 @@ export class Executor {
490
637
  const result = {
491
638
  ...extendsTsconfig,
492
639
  ...tsconfig,
493
- compilerOptions: { ...extendsTsconfig.compilerOptions, ...tsconfig.compilerOptions },
640
+ compilerOptions: {
641
+ ...extendsTsconfig.compilerOptions,
642
+ ...tsconfig.compilerOptions,
643
+ },
494
644
  } as TsConfigJson;
495
645
  this.#tsconfig = result;
496
646
  return result;
@@ -504,7 +654,11 @@ export class Executor {
504
654
  }
505
655
 
506
656
  #packageJson: PackageJson | null = null;
507
- async getPackageJson({ refresh }: { refresh?: boolean } = {}): Promise<PackageJson> {
657
+ async getPackageJson({
658
+ refresh,
659
+ }: {
660
+ refresh?: boolean;
661
+ } = {}): Promise<PackageJson> {
508
662
  if (this.#packageJson && !refresh) return this.#packageJson;
509
663
  const packageJson = (await this.readJson("package.json")) as PackageJson;
510
664
  this.#packageJson = packageJson;
@@ -551,37 +705,55 @@ export class Executor {
551
705
  };
552
706
  const result = await getContent.default(scanInfo ?? null, dict, options);
553
707
  if (result === null) return null;
554
- const filename = typeof result === "object" ? result.filename : path.basename(targetPath).replace(".js", ".ts");
708
+ const filename =
709
+ typeof result === "object"
710
+ ? result.filename
711
+ : path.basename(targetPath).replace(".js", ".ts");
555
712
  const content = typeof result === "object" ? result.content : result;
556
713
  const dirname = path.dirname(targetPath);
557
714
  const convertedTargetPath = Object.entries(dict).reduce(
558
- (path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value),
715
+ (path, [key, value]) =>
716
+ path.replace(new RegExp(`__${key}__`, "g"), value),
559
717
  `${dirname}/${filename}`,
560
718
  );
561
- this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
719
+ this.logger.verbose(
720
+ `Apply template ${templatePath} to ${convertedTargetPath}`,
721
+ );
562
722
  return this.writeFile(convertedTargetPath, content, { overwrite });
563
723
  } else if (targetPath.endsWith(".template")) {
564
724
  const content = await FileSys.readText(templatePath);
565
725
  const convertedTargetPath = Object.entries(dict).reduce(
566
- (path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value),
726
+ (path, [key, value]) =>
727
+ path.replace(new RegExp(`__${key}__`, "g"), value),
567
728
  targetPath.slice(0, -9),
568
729
  );
569
730
  const convertedContent = Object.entries(dict).reduce(
570
- (data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value),
731
+ (data, [key, value]) =>
732
+ data.replace(new RegExp(`<%= ${key} %>`, "g"), value),
571
733
  content,
572
734
  );
573
- this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
574
- return this.writeFile(convertedTargetPath, convertedContent, { overwrite });
575
- } else if (staticTemplateFileExtensions.has(path.extname(targetPath).toLowerCase())) {
735
+ this.logger.verbose(
736
+ `Apply template ${templatePath} to ${convertedTargetPath}`,
737
+ );
738
+ return this.writeFile(convertedTargetPath, convertedContent, {
739
+ overwrite,
740
+ });
741
+ } else if (
742
+ staticTemplateFileExtensions.has(path.extname(targetPath).toLowerCase())
743
+ ) {
576
744
  const convertedTargetPath = Object.entries(dict).reduce(
577
- (path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value),
745
+ (path, [key, value]) =>
746
+ path.replace(new RegExp(`__${key}__`, "g"), value),
578
747
  targetPath,
579
748
  );
580
749
  const writePath = this.getPath(convertedTargetPath);
581
750
  const dirname = path.dirname(writePath);
582
- if (!(await FileSys.dirExists(dirname))) await mkdir(dirname, { recursive: true });
751
+ if (!(await FileSys.dirExists(dirname)))
752
+ await mkdir(dirname, { recursive: true });
583
753
  await copyFile(templatePath, writePath);
584
- this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
754
+ this.logger.verbose(
755
+ `Apply template ${templatePath} to ${convertedTargetPath}`,
756
+ );
585
757
  return { filePath: writePath, content: "" };
586
758
  } else return null;
587
759
  }
@@ -606,7 +778,12 @@ export class Executor {
606
778
  if ((await stat(prefixTemplatePath)).isFile()) {
607
779
  const filename = path.basename(prefixTemplatePath);
608
780
  const fileContent = await this.#applyTemplateFile(
609
- { templatePath: prefixTemplatePath, targetPath: path.join(basePath, filename), scanInfo, overwrite },
781
+ {
782
+ templatePath: prefixTemplatePath,
783
+ targetPath: path.join(basePath, filename),
784
+ scanInfo,
785
+ overwrite,
786
+ },
610
787
  dict,
611
788
  options,
612
789
  );
@@ -619,7 +796,12 @@ export class Executor {
619
796
  const subpath = path.join(templatePath, subdir);
620
797
  if ((await stat(subpath)).isFile()) {
621
798
  const fileContent = await this.#applyTemplateFile(
622
- { templatePath: subpath, targetPath: path.join(basePath, subdir), scanInfo, overwrite },
799
+ {
800
+ templatePath: subpath,
801
+ targetPath: path.join(basePath, subdir),
802
+ scanInfo,
803
+ overwrite,
804
+ },
623
805
  dict,
624
806
  options,
625
807
  );
@@ -659,7 +841,10 @@ export class Executor {
659
841
  const dict = {
660
842
  ...(options.dict ?? {}),
661
843
  ...Object.fromEntries(
662
- Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]),
844
+ Object.entries(options.dict ?? {}).map(([key, value]) => [
845
+ capitalize(key),
846
+ capitalize(value),
847
+ ]),
663
848
  ),
664
849
  };
665
850
  return this._applyTemplate({ ...options, dict });
@@ -671,10 +856,69 @@ export class Executor {
671
856
  typeCheck(filePath: string) {
672
857
  const path = this.getPath(filePath);
673
858
  const typeChecker = this.getTypeChecker();
674
- const { fileDiagnostics, fileErrors, fileWarnings } = typeChecker.check(path);
859
+ const { fileDiagnostics, fileErrors, fileWarnings } =
860
+ typeChecker.check(path);
675
861
  const message = typeChecker.formatDiagnostics(fileDiagnostics);
676
862
  return { fileDiagnostics, fileErrors, fileWarnings, message };
677
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
+ }
678
922
  getLinter() {
679
923
  this.linter ??= new Linter(this.cwdPath);
680
924
  return this.linter;
@@ -690,7 +934,10 @@ export class Executor {
690
934
  }> {
691
935
  const path = this.getPath(filePath);
692
936
  const linter = this.getLinter();
693
- const { results, errors, warnings } = await linter.lint(path, { fix, dryRun });
937
+ const { results, errors, warnings } = await linter.lint(path, {
938
+ fix,
939
+ dryRun,
940
+ });
694
941
  const message = linter.formatLintResults(results);
695
942
  return { results, message, errors, warnings };
696
943
  }
@@ -718,14 +965,20 @@ export class WorkspaceExecutor extends Executor {
718
965
  workspaceRoot?: string;
719
966
  repoName?: string;
720
967
  } = {}) {
721
- return WorkspaceExecutor.#execs.get(repoName) ?? new WorkspaceExecutor({ workspaceRoot, repoName });
968
+ return (
969
+ WorkspaceExecutor.#execs.get(repoName) ??
970
+ new WorkspaceExecutor({ workspaceRoot, repoName })
971
+ );
722
972
  }
723
973
  static getBaseDevEnv(envPath?: string) {
724
974
  // Bun auto-loads .env, so we use process.env directly
725
- const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
975
+ const sourceEnv = envPath
976
+ ? { ...process.env, ...parseEnvFile(envPath) }
977
+ : process.env;
726
978
 
727
979
  const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
728
980
  const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
981
+ const workspaceId = sourceEnv.AKAN_WORKSPACE_ID;
729
982
 
730
983
  const repoName = sourceEnv.AKAN_PUBLIC_REPO_NAME;
731
984
  if (!repoName) throw new Error("AKAN_PUBLIC_REPO_NAME is not set");
@@ -743,29 +996,63 @@ export class WorkspaceExecutor extends Executor {
743
996
  | "local"
744
997
  | undefined;
745
998
  if (!env) throw new Error("AKAN_PUBLIC_ENV is not set");
746
- return { ...(appName ? { appName } : {}), workspaceRoot, repoName, serveDomain, env, portOffset };
999
+ return {
1000
+ ...(appName ? { appName } : {}),
1001
+ workspaceRoot,
1002
+ repoName,
1003
+ serveDomain,
1004
+ env,
1005
+ portOffset,
1006
+ workspaceId,
1007
+ };
1008
+ }
1009
+ getWorkspaceId<AllowEmpty extends boolean = false>({
1010
+ allowEmpty,
1011
+ }: {
1012
+ allowEmpty?: AllowEmpty;
1013
+ } = {}): AllowEmpty extends true ? string | undefined : string {
1014
+ const { workspaceId } = WorkspaceExecutor.getBaseDevEnv();
1015
+ if (!workspaceId && !allowEmpty)
1016
+ throw new Error("Workspace ID is not found");
1017
+ return workspaceId as AllowEmpty extends true ? string | undefined : string;
747
1018
  }
748
1019
  async scan(): Promise<WorkspaceInfo> {
749
1020
  return await WorkspaceInfo.fromExecutor(this);
750
1021
  }
751
1022
  async getApps() {
752
1023
  if (!(await FileSys.dirExists(`${this.workspaceRoot}/apps`))) return [];
753
- return await this.#getDirHasFile(`${this.workspaceRoot}/apps`, "akan.config.ts");
1024
+ return await this.#getDirHasFile(
1025
+ `${this.workspaceRoot}/apps`,
1026
+ "akan.config.ts",
1027
+ );
754
1028
  }
755
1029
  async getLibs() {
756
1030
  if (!(await FileSys.dirExists(`${this.workspaceRoot}/libs`))) return [];
757
- return await this.#getDirHasFile(`${this.workspaceRoot}/libs`, "akan.config.ts");
1031
+ return await this.#getDirHasFile(
1032
+ `${this.workspaceRoot}/libs`,
1033
+ "akan.config.ts",
1034
+ );
758
1035
  }
759
1036
  async getSyss() {
760
- const [appNames, libNames] = await Promise.all([this.getApps(), this.getLibs()]);
1037
+ const [appNames, libNames] = await Promise.all([
1038
+ this.getApps(),
1039
+ this.getLibs(),
1040
+ ]);
761
1041
  return [appNames, libNames] as [string[], string[]];
762
1042
  }
763
1043
  async getPkgs() {
764
1044
  if (!(await FileSys.dirExists(`${this.workspaceRoot}/pkgs`))) return [];
765
- return await this.#getDirHasFile(`${this.workspaceRoot}/pkgs`, "package.json");
1045
+ return await this.#getDirHasFile(
1046
+ `${this.workspaceRoot}/pkgs`,
1047
+ "package.json",
1048
+ );
766
1049
  }
767
1050
  async getExecs() {
768
- const [appNames, libNames, pkgNames] = await Promise.all([this.getApps(), this.getLibs(), this.getPkgs()]);
1051
+ const [appNames, libNames, pkgNames] = await Promise.all([
1052
+ this.getApps(),
1053
+ this.getLibs(),
1054
+ this.getPkgs(),
1055
+ ]);
769
1056
  return [appNames, libNames, pkgNames] as [string[], string[], string[]];
770
1057
  }
771
1058
  async setPkgTsPaths(name: string) {
@@ -774,7 +1061,11 @@ export class WorkspaceExecutor extends Executor {
774
1061
  rootTsConfig.compilerOptions.paths[name] = [`./pkgs/${name}/index.ts`];
775
1062
  rootTsConfig.compilerOptions.paths[`${name}/*`] = [`./pkgs/${name}/*`];
776
1063
  if (rootTsConfig.references) {
777
- if (!rootTsConfig.references.some((ref) => ref.path === `./pkgs/${name}/tsconfig.json`))
1064
+ if (
1065
+ !rootTsConfig.references.some(
1066
+ (ref) => ref.path === `./pkgs/${name}/tsconfig.json`,
1067
+ )
1068
+ )
778
1069
  rootTsConfig.references.push({ path: `./pkgs/${name}/tsconfig.json` });
779
1070
  }
780
1071
  await this.writeJson("tsconfig.json", rootTsConfig);
@@ -782,11 +1073,14 @@ export class WorkspaceExecutor extends Executor {
782
1073
  }
783
1074
  async unsetPkgTsPaths(name: string) {
784
1075
  const rootTsConfig = (await this.readJson("tsconfig.json")) as TsConfigJson;
785
- const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter(
786
- (key) => key !== name && key !== `${name}/*`,
787
- );
1076
+ const filteredKeys = Object.keys(
1077
+ rootTsConfig.compilerOptions.paths ?? {},
1078
+ ).filter((key) => key !== name && key !== `${name}/*`);
788
1079
  rootTsConfig.compilerOptions.paths = Object.fromEntries(
789
- filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths?.[key] ?? []]),
1080
+ filteredKeys.map((key) => [
1081
+ key,
1082
+ rootTsConfig.compilerOptions.paths?.[key] ?? [],
1083
+ ]),
790
1084
  );
791
1085
  if (rootTsConfig.references) {
792
1086
  rootTsConfig.references = rootTsConfig.references.filter(
@@ -798,7 +1092,12 @@ export class WorkspaceExecutor extends Executor {
798
1092
  }
799
1093
  async getDirInModule(basePath: string, name: string) {
800
1094
  const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
801
- const getDirs = async (dirname: string, maxDepth = 3, results: string[] = [], prefix = "") => {
1095
+ const getDirs = async (
1096
+ dirname: string,
1097
+ maxDepth = 3,
1098
+ results: string[] = [],
1099
+ prefix = "",
1100
+ ) => {
802
1101
  const dirs = await this.readdir(dirname);
803
1102
  await Promise.all(
804
1103
  dirs.map(async (dir) => {
@@ -806,7 +1105,8 @@ export class WorkspaceExecutor extends Executor {
806
1105
  const dirPath = path.join(dirname, dir);
807
1106
  if ((await stat(dirPath)).isDirectory()) {
808
1107
  results.push(`${prefix}${dir}`);
809
- if (maxDepth > 0) await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
1108
+ if (maxDepth > 0)
1109
+ await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
810
1110
  }
811
1111
  }),
812
1112
  );
@@ -814,23 +1114,34 @@ export class WorkspaceExecutor extends Executor {
814
1114
  };
815
1115
  return await getDirs(basePath);
816
1116
  }
817
- async commit(message: string, { init = false, add = true }: { init?: boolean; add?: boolean } = {}) {
1117
+ async commit(
1118
+ message: string,
1119
+ { init = false, add = true }: { init?: boolean; add?: boolean } = {},
1120
+ ) {
818
1121
  if (init) await this.exec(`git init --quiet`);
819
1122
  if (add) await this.exec(`git add .`);
820
1123
  await this.exec(`git commit --quiet -m "${message}"`);
821
1124
  }
822
1125
  async #getDirHasFile(basePath: string, targetFilename: string) {
823
1126
  const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
824
- const getDirs = async (dirname: string, maxDepth = 3, results: string[] = [], prefix = "") => {
1127
+ const getDirs = async (
1128
+ dirname: string,
1129
+ maxDepth = 3,
1130
+ results: string[] = [],
1131
+ prefix = "",
1132
+ ) => {
825
1133
  const dirs = await this.readdir(dirname);
826
1134
  await Promise.all(
827
1135
  dirs.map(async (dir) => {
828
1136
  if (AVOID_DIRS.includes(dir)) return;
829
1137
  const dirPath = path.join(dirname, dir);
830
1138
  if ((await stat(dirPath)).isDirectory()) {
831
- const hasTargetFile = await FileSys.fileExists(path.join(dirPath, targetFilename));
1139
+ const hasTargetFile = await FileSys.fileExists(
1140
+ path.join(dirPath, targetFilename),
1141
+ );
832
1142
  if (hasTargetFile) results.push(`${prefix}${dir}`);
833
- if (maxDepth > 0) await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
1143
+ if (maxDepth > 0)
1144
+ await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
834
1145
  }
835
1146
  }),
836
1147
  );
@@ -843,10 +1154,18 @@ export class WorkspaceExecutor extends Executor {
843
1154
  const [appNames, libNames] = await this.getSyss();
844
1155
  const scalarConstantExampleFiles = [
845
1156
  ...(
846
- await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getScalarConstantFiles()))
1157
+ await Promise.all(
1158
+ appNames.map((appName) =>
1159
+ AppExecutor.from(this, appName).getScalarConstantFiles(),
1160
+ ),
1161
+ )
847
1162
  ).flat(),
848
1163
  ...(
849
- await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getScalarConstantFiles()))
1164
+ await Promise.all(
1165
+ libNames.map((libName) =>
1166
+ LibExecutor.from(this, libName).getScalarConstantFiles(),
1167
+ ),
1168
+ )
850
1169
  ).flat(),
851
1170
  ];
852
1171
  return scalarConstantExampleFiles;
@@ -854,24 +1173,60 @@ export class WorkspaceExecutor extends Executor {
854
1173
  async getConstantFiles() {
855
1174
  const [appNames, libNames] = await this.getSyss();
856
1175
  const moduleConstantExampleFiles = [
857
- ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getConstantFiles()))).flat(),
858
- ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getConstantFiles()))).flat(),
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(),
859
1190
  ];
860
1191
  return moduleConstantExampleFiles;
861
1192
  }
862
1193
  async getDictionaryFiles() {
863
1194
  const [appNames, libNames] = await this.getSyss();
864
1195
  const moduleDictionaryExampleFiles = [
865
- ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getDictionaryFiles()))).flat(),
866
- ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getDictionaryFiles()))).flat(),
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(),
867
1210
  ];
868
1211
  return moduleDictionaryExampleFiles;
869
1212
  }
870
1213
  async getViewFiles() {
871
1214
  const [appNames, libNames] = await this.getSyss();
872
1215
  const viewExampleFiles = [
873
- ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getViewsSourceCode()))).flat(),
874
- ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getViewsSourceCode()))).flat(),
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(),
875
1230
  ];
876
1231
  return viewExampleFiles;
877
1232
  }
@@ -890,7 +1245,11 @@ export class SysExecutor extends Executor {
890
1245
  override name: string;
891
1246
  type: "app" | "lib";
892
1247
  override emoji: string;
893
- constructor({ workspace = WorkspaceExecutor.fromRoot(), name, type }: SysExecutorOptions) {
1248
+ constructor({
1249
+ workspace = WorkspaceExecutor.fromRoot(),
1250
+ name,
1251
+ type,
1252
+ }: SysExecutorOptions) {
894
1253
  super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
895
1254
  this.workspace = workspace;
896
1255
  this.name = name;
@@ -907,7 +1266,8 @@ export class SysExecutor extends Executor {
907
1266
  return this.#akanConfig;
908
1267
  }
909
1268
  async getModules() {
910
- const path = this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
1269
+ const path =
1270
+ this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
911
1271
  return await this.workspace.getDirInModule(path, this.name);
912
1272
  }
913
1273
 
@@ -919,17 +1279,26 @@ export class SysExecutor extends Executor {
919
1279
  allowEmpty,
920
1280
  }: {
921
1281
  allowEmpty?: AllowEmpty;
922
- } = {}): AllowEmpty extends true ? AppInfo | LibInfo | null : AppInfo | LibInfo {
923
- if (!this.hasScanInfo() && !allowEmpty) throw new Error("Scan info is not available");
924
- return this.#scanInfo as AllowEmpty extends true ? AppInfo | LibInfo | null : AppInfo | LibInfo;
925
- }
926
- #getScanTemplateTasks(scanInfo: AppInfo | LibInfo): (Promise<FileContent[]> | null)[] {
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)[] {
927
1294
  return [
928
1295
  this._applyTemplate({ basePath: "env", template: "env", scanInfo }),
929
1296
  this._applyTemplate({ basePath: "lib", template: "lib", scanInfo }),
930
1297
  this._applyTemplate({ basePath: ".", template: "server.ts", scanInfo }),
931
1298
  this._applyTemplate({ basePath: ".", template: "client.ts", scanInfo }),
932
- this.type === "lib" ? this._applyTemplate({ basePath: ".", template: "index.ts", scanInfo }) : null,
1299
+ this.type === "lib"
1300
+ ? this._applyTemplate({ basePath: ".", template: "index.ts", scanInfo })
1301
+ : null,
933
1302
  ...scanFacetDirs.map((facet) =>
934
1303
  this._applyTemplate({
935
1304
  basePath: facet,
@@ -976,8 +1345,12 @@ export class SysExecutor extends Executor {
976
1345
  if (this.#scanInfo && !refresh) return this.#scanInfo;
977
1346
  const scanInfo =
978
1347
  this.type === "app"
979
- ? await AppInfo.fromExecutor(this as unknown as AppExecutor, { refresh })
980
- : await LibInfo.fromExecutor(this as unknown as LibExecutor, { refresh });
1348
+ ? await AppInfo.fromExecutor(this as unknown as AppExecutor, {
1349
+ refresh,
1350
+ })
1351
+ : await LibInfo.fromExecutor(this as unknown as LibExecutor, {
1352
+ refresh,
1353
+ });
981
1354
  if (write) {
982
1355
  await Promise.all(this.#getScanTemplateTasks(scanInfo));
983
1356
  await this.writeJson(`akan.${this.type}.json`, scanInfo.getScanResult());
@@ -986,7 +1359,11 @@ export class SysExecutor extends Executor {
986
1359
  if (writeLib) {
987
1360
  const libInfos = [...scanInfo.getLibInfos().values()];
988
1361
  await this.#updateDependencies(scanInfo);
989
- await Promise.all(libInfos.flatMap((libInfo) => libInfo.exec.#getScanTemplateTasks(libInfo)));
1362
+ await Promise.all(
1363
+ libInfos.flatMap((libInfo) =>
1364
+ libInfo.exec.#getScanTemplateTasks(libInfo),
1365
+ ),
1366
+ );
990
1367
  }
991
1368
  }
992
1369
  this.#scanInfo = scanInfo;
@@ -1003,7 +1380,9 @@ export class SysExecutor extends Executor {
1003
1380
  ...libPackageJson,
1004
1381
  dependencies: {
1005
1382
  ...Object.fromEntries(
1006
- Object.entries(libPackageJson.dependencies ?? {}).filter(([dep]) => !devDependencySet.has(dep)),
1383
+ Object.entries(libPackageJson.dependencies ?? {}).filter(
1384
+ ([dep]) => !devDependencySet.has(dep),
1385
+ ),
1007
1386
  ),
1008
1387
  ...(Object.fromEntries(
1009
1388
  dependencies
@@ -1014,82 +1393,134 @@ export class SysExecutor extends Executor {
1014
1393
  },
1015
1394
  devDependencies: {
1016
1395
  ...Object.fromEntries(
1017
- Object.entries(libPackageJson.devDependencies ?? {}).filter(([dep]) => !dependencySet.has(dep)),
1396
+ Object.entries(libPackageJson.devDependencies ?? {}).filter(
1397
+ ([dep]) => !dependencySet.has(dep),
1398
+ ),
1018
1399
  ),
1019
1400
  ...(Object.fromEntries(
1020
1401
  devDependencies
1021
- .filter((dep) => rootPackageJson.dependencies?.[dep] || rootPackageJson.devDependencies?.[dep])
1402
+ .filter(
1403
+ (dep) =>
1404
+ rootPackageJson.dependencies?.[dep] ||
1405
+ rootPackageJson.devDependencies?.[dep],
1406
+ )
1022
1407
  .sort()
1023
- .map((dep) => [dep, rootPackageJson.devDependencies?.[dep] ?? rootPackageJson.dependencies?.[dep]]),
1408
+ .map((dep) => [
1409
+ dep,
1410
+ rootPackageJson.devDependencies?.[dep] ??
1411
+ rootPackageJson.dependencies?.[dep],
1412
+ ]),
1024
1413
  ) as Record<string, string>),
1025
1414
  },
1026
1415
  };
1027
1416
  await this.setPackageJson(libPkgJsonWithDeps);
1028
1417
  }
1029
1418
  override async getLocalFile(targetPath: string) {
1030
- const filePath = path.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
1419
+ const filePath = path.isAbsolute(targetPath)
1420
+ ? targetPath
1421
+ : `${this.type}s/${this.name}/${targetPath}`;
1031
1422
  const content = await this.workspace.readFile(filePath);
1032
1423
  return { filePath, content };
1033
1424
  }
1034
1425
 
1035
1426
  async getDatabaseModules() {
1036
1427
  const databaseModules = (await this.readdir("lib"))
1037
- .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1038
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.constant.ts`).exists());
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
+ );
1039
1437
  return databaseModules;
1040
1438
  }
1041
1439
 
1042
1440
  async getServiceModules() {
1043
1441
  const serviceModules = (await this.readdir("lib"))
1044
1442
  .filter((name) => name.startsWith("_") && !name.startsWith("__"))
1045
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.service.ts`).exists());
1443
+ .filter((name) =>
1444
+ Bun.file(`${this.cwdPath}/lib/${name}/${name}.service.ts`).exists(),
1445
+ );
1046
1446
  return serviceModules;
1047
1447
  }
1048
1448
 
1049
1449
  async getScalarModules() {
1050
1450
  const scalarModules = (await this.readdir("lib/__scalar"))
1051
1451
  .filter((name) => !name.startsWith("_"))
1052
- .filter((name) => Bun.file(`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`).exists());
1452
+ .filter((name) =>
1453
+ Bun.file(
1454
+ `${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`,
1455
+ ).exists(),
1456
+ );
1053
1457
  return scalarModules;
1054
1458
  }
1055
1459
 
1056
1460
  async getViewComponents() {
1057
1461
  const viewComponents = (await this.readdir("lib"))
1058
- .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1059
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
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
+ );
1060
1471
  return viewComponents;
1061
1472
  }
1062
1473
 
1063
1474
  async getUnitComponents() {
1064
1475
  const unitComponents = (await this.readdir("lib"))
1065
- .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1066
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
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
+ );
1067
1485
  return unitComponents;
1068
1486
  }
1069
1487
  async getTemplateComponents() {
1070
1488
  const templateComponents = (await this.readdir("lib"))
1071
- .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1072
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
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
+ );
1073
1498
  return templateComponents;
1074
1499
  }
1075
1500
 
1076
1501
  async getViewsSourceCode() {
1077
1502
  const viewComponents = await this.getViewComponents();
1078
1503
  return Promise.all(
1079
- viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)),
1504
+ viewComponents.map((viewComponent) =>
1505
+ this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`),
1506
+ ),
1080
1507
  );
1081
1508
  }
1082
1509
  async getUnitsSourceCode() {
1083
1510
  const unitComponents = await this.getUnitComponents();
1084
1511
  return Promise.all(
1085
- unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)),
1512
+ unitComponents.map((unitComponent) =>
1513
+ this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`),
1514
+ ),
1086
1515
  );
1087
1516
  }
1088
1517
  async getTemplatesSourceCode() {
1089
1518
  const templateComponents = await this.getTemplateComponents();
1090
1519
  return Promise.all(
1091
1520
  templateComponents.map((templateComponent) =>
1092
- this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`),
1521
+ this.getLocalFile(
1522
+ `lib/${templateComponent}/${templateComponent}.Template.tsx`,
1523
+ ),
1093
1524
  ),
1094
1525
  );
1095
1526
  }
@@ -1098,7 +1529,9 @@ export class SysExecutor extends Executor {
1098
1529
  const scalarModules = await this.getScalarModules();
1099
1530
  return Promise.all(
1100
1531
  scalarModules.map((scalarModule) =>
1101
- this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.constant.ts`),
1532
+ this.getLocalFile(
1533
+ `lib/__scalar/${scalarModule}/${scalarModule}.constant.ts`,
1534
+ ),
1102
1535
  ),
1103
1536
  );
1104
1537
  }
@@ -1106,13 +1539,19 @@ export class SysExecutor extends Executor {
1106
1539
  async getScalarDictionaryFiles() {
1107
1540
  const scalarModules = await this.getScalarModules();
1108
1541
  return Promise.all(
1109
- scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`)),
1542
+ scalarModules.map((scalarModule) =>
1543
+ this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`),
1544
+ ),
1110
1545
  );
1111
1546
  }
1112
1547
 
1113
1548
  async getConstantFiles() {
1114
1549
  const modules = await this.getModules();
1115
- return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.constant.ts`)));
1550
+ return Promise.all(
1551
+ modules.map((module) =>
1552
+ this.getLocalFile(`lib/${module}/${module}.constant.ts`),
1553
+ ),
1554
+ );
1116
1555
  }
1117
1556
  async getConstantFilesWithLibs() {
1118
1557
  const scanInfo =
@@ -1128,11 +1567,19 @@ export class SysExecutor extends Executor {
1128
1567
  ...(await LibExecutor.from(this, lib).getScalarConstantFiles()),
1129
1568
  ]),
1130
1569
  );
1131
- return [...sysContantFiles, ...sysScalarConstantFiles, ...libConstantFiles.flat()];
1570
+ return [
1571
+ ...sysContantFiles,
1572
+ ...sysScalarConstantFiles,
1573
+ ...libConstantFiles.flat(),
1574
+ ];
1132
1575
  }
1133
1576
  async getDictionaryFiles() {
1134
1577
  const modules = await this.getModules();
1135
- return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.dictionary.ts`)));
1578
+ return Promise.all(
1579
+ modules.map((module) =>
1580
+ this.getLocalFile(`lib/${module}/${module}.dictionary.ts`),
1581
+ ),
1582
+ );
1136
1583
  }
1137
1584
  override async applyTemplate(options: {
1138
1585
  basePath: string;
@@ -1143,11 +1590,18 @@ export class SysExecutor extends Executor {
1143
1590
  const dict = {
1144
1591
  ...(options.dict ?? {}),
1145
1592
  ...Object.fromEntries(
1146
- Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]),
1593
+ Object.entries(options.dict ?? {}).map(([key, value]) => [
1594
+ capitalize(key),
1595
+ capitalize(value),
1596
+ ]),
1147
1597
  ),
1148
1598
  };
1149
1599
  const scanInfo = await this.scan();
1150
- const fileContents = await this._applyTemplate({ ...options, scanInfo, dict });
1600
+ const fileContents = await this._applyTemplate({
1601
+ ...options,
1602
+ scanInfo,
1603
+ dict,
1604
+ });
1151
1605
  await this.scan();
1152
1606
  return fileContents;
1153
1607
  }
@@ -1162,13 +1616,17 @@ export class AppExecutor extends SysExecutor {
1162
1616
  override emoji = execEmoji.app;
1163
1617
  constructor({ workspace, name }: AppExecutorOptions) {
1164
1618
  super({ workspace, name, type: "app" });
1165
- this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
1619
+ this.dist = new Executor(
1620
+ `dist/${name}`,
1621
+ `${this.workspace.workspaceRoot}/dist/apps/${name}`,
1622
+ );
1166
1623
  }
1167
1624
  static #execs = new Map<string, AppExecutor>();
1168
1625
  static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
1169
1626
  const exec = AppExecutor.#execs.get(name);
1170
1627
  if (exec) return exec;
1171
- else if (executor instanceof WorkspaceExecutor) return new AppExecutor({ workspace: executor, name });
1628
+ else if (executor instanceof WorkspaceExecutor)
1629
+ return new AppExecutor({ workspace: executor, name });
1172
1630
  else return new AppExecutor({ workspace: executor.workspace, name });
1173
1631
  }
1174
1632
  getEnv() {
@@ -1178,7 +1636,9 @@ export class AppExecutor extends SysExecutor {
1178
1636
  const basePort = 8282;
1179
1637
  const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
1180
1638
  const PORT = basePort ? (basePort + portOffset).toString() : undefined;
1181
- const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
1639
+ const AKAN_PUBLIC_SERVER_PORT = portOffset
1640
+ ? (8282 + portOffset).toString()
1641
+ : undefined;
1182
1642
  return {
1183
1643
  ...process.env,
1184
1644
  AKAN_PUBLIC_APP_NAME: this.name,
@@ -1191,15 +1651,22 @@ export class AppExecutor extends SysExecutor {
1191
1651
  }
1192
1652
  async prepareCommand(type: "build" | "start") {
1193
1653
  const akanConfig = await this.getConfig();
1194
- const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig.defaultDatabaseMode ?? "single";
1654
+ const databaseMode =
1655
+ process.env.AKAN_DATABASE_MODE ??
1656
+ akanConfig.defaultDatabaseMode ??
1657
+ "single";
1195
1658
  const routeEnv = {
1196
1659
  AKAN_PUBLIC_BASE_PATHS: [...akanConfig.basePaths].join(","),
1197
1660
  AKAN_DATABASE_MODE: databaseMode,
1198
1661
  };
1199
1662
  Object.assign(process.env, routeEnv);
1200
1663
  if (type === "build") {
1201
- if (await this.exists(this.dist.cwdPath)) await this.dist.exec(`rm -rf ${this.dist.cwdPath}`);
1202
- await Promise.all([this.dist.mkdir("private"), this.dist.mkdir("public")]);
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
+ ]);
1203
1670
  await Promise.all([
1204
1671
  this.cp("private", `${this.dist.cwdPath}/private`),
1205
1672
  this.cp("public", `${this.dist.cwdPath}/public`),
@@ -1237,7 +1704,11 @@ export class AppExecutor extends SysExecutor {
1237
1704
  }
1238
1705
 
1239
1706
  #pageKeys: string[] | null = null;
1240
- async getPageKeys({ refresh }: { refresh?: boolean } = {}): Promise<string[]> {
1707
+ async getPageKeys({
1708
+ refresh,
1709
+ }: {
1710
+ refresh?: boolean;
1711
+ } = {}): Promise<string[]> {
1241
1712
  if (this.#pageKeys && !refresh) return this.#pageKeys;
1242
1713
  const akanConfig = await this.getConfig();
1243
1714
  const glob = new Bun.Glob("**/*");
@@ -1247,7 +1718,11 @@ export class AppExecutor extends SysExecutor {
1247
1718
  this.#pageKeys = [];
1248
1719
  return this.#pageKeys;
1249
1720
  }
1250
- for await (const rel of glob.scan({ cwd: pageDir, absolute: false, onlyFiles: true })) {
1721
+ for await (const rel of glob.scan({
1722
+ cwd: pageDir,
1723
+ absolute: false,
1724
+ onlyFiles: true,
1725
+ })) {
1251
1726
  const segments = rel.split(path.sep);
1252
1727
  if (segments.some((s) => s === "node_modules")) continue;
1253
1728
  const posix = segments.join("/");
@@ -1255,13 +1730,24 @@ export class AppExecutor extends SysExecutor {
1255
1730
  validatePageSourceFile(posix, { filePath: absPath });
1256
1731
  if (!isRouteSourceFile(posix)) continue;
1257
1732
  const key = `./${posix}`;
1258
- validateSubRoutePageKey(key, akanConfig.basePaths, { appName: this.name, filePath: absPath });
1733
+ validateSubRoutePageKey(key, akanConfig.basePaths, {
1734
+ appName: this.name,
1735
+ filePath: absPath,
1736
+ });
1259
1737
  const parsed = parseRouteModuleKey(key);
1260
1738
  if (parsed.isInternalRootLayout) {
1261
- throw new Error(`[route-convention] __root_layout is reserved for Akan.js generated root layout: ${absPath}`);
1739
+ throw new Error(
1740
+ `[route-convention] __root_layout is reserved for Akan.js generated root layout: ${absPath}`,
1741
+ );
1262
1742
  }
1263
- const isRootLayout = parsed.kind === "layout" && parsed.moduleSegments.at(-1) === "_layout";
1264
- validateRouteSourceExports(await Bun.file(absPath).text(), absPath, parsed.kind, { rootLayout: isRootLayout });
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
+ );
1265
1751
  pageKeys.push(key);
1266
1752
  }
1267
1753
  pageKeys.sort();
@@ -1277,28 +1763,61 @@ export class AppExecutor extends SysExecutor {
1277
1763
  const projectAssetsPath = `${this.cwdPath}/private`;
1278
1764
  const projectPublicLibPath = `${projectPublicPath}/libs`;
1279
1765
  const projectAssetsLibPath = `${projectAssetsPath}/libs`;
1280
- await Promise.all([this.removeDir(projectPublicLibPath), this.removeDir(projectAssetsLibPath)]);
1766
+ await Promise.all([
1767
+ this.removeDir(projectPublicLibPath),
1768
+ this.removeDir(projectAssetsLibPath),
1769
+ ]);
1281
1770
  const targetPublicDeps = [] as string[];
1282
1771
  for (const dep of libDeps) {
1283
- if (await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/public`)) targetPublicDeps.push(dep);
1772
+ if (
1773
+ await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/public`)
1774
+ )
1775
+ targetPublicDeps.push(dep);
1284
1776
  }
1285
1777
  const targetAssetsDeps = [] as string[];
1286
1778
  for (const dep of libDeps) {
1287
- if (await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/private`)) targetAssetsDeps.push(dep);
1779
+ if (
1780
+ await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/private`)
1781
+ )
1782
+ targetAssetsDeps.push(dep);
1288
1783
  }
1289
- await Promise.all(targetPublicDeps.map((dep) => this.mkdir(`${projectPublicLibPath}/${dep}`)));
1290
- await Promise.all(targetAssetsDeps.map((dep) => this.mkdir(`${projectAssetsLibPath}/${dep}`)));
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
+ );
1291
1794
  await Promise.all([
1292
1795
  ...targetPublicDeps.map((dep) =>
1293
- this.cp(`${this.workspace.workspaceRoot}/libs/${dep}/public`, `${projectPublicLibPath}/${dep}`),
1796
+ this.cp(
1797
+ `${this.workspace.workspaceRoot}/libs/${dep}/public`,
1798
+ `${projectPublicLibPath}/${dep}`,
1799
+ ),
1294
1800
  ),
1295
1801
  ...targetAssetsDeps.map((dep) =>
1296
- this.cp(`${this.workspace.workspaceRoot}/libs/${dep}/private`, `${projectAssetsLibPath}/${dep}`),
1802
+ this.cp(
1803
+ `${this.workspace.workspaceRoot}/libs/${dep}/private`,
1804
+ `${projectAssetsLibPath}/${dep}`,
1805
+ ),
1297
1806
  ),
1298
1807
  ]);
1299
1808
  }
1300
- async scanSync({ refresh = false, write = true }: { refresh?: boolean; write?: boolean } = {}) {
1301
- const scanInfo = (await this.scan({ refresh, write, writeLib: write })) as AppInfo;
1809
+ async scanSync({
1810
+ refresh = false,
1811
+ write = true,
1812
+ }: {
1813
+ refresh?: boolean;
1814
+ write?: boolean;
1815
+ } = {}) {
1816
+ const scanInfo = (await this.scan({
1817
+ refresh,
1818
+ write,
1819
+ writeLib: write,
1820
+ })) as AppInfo;
1302
1821
  if (write) await this.syncAssets(scanInfo.getScanResult().libDeps);
1303
1822
  return scanInfo;
1304
1823
  }
@@ -1318,13 +1837,17 @@ export class LibExecutor extends SysExecutor {
1318
1837
  override emoji = execEmoji.lib;
1319
1838
  constructor({ workspace, name }: LibExecutorOptions) {
1320
1839
  super({ workspace, name, type: "lib" });
1321
- this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
1840
+ this.dist = new Executor(
1841
+ `dist/${name}`,
1842
+ `${this.workspace.workspaceRoot}/dist/libs/${name}`,
1843
+ );
1322
1844
  }
1323
1845
  static #execs = new Map<string, LibExecutor>();
1324
1846
  static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
1325
1847
  const exec = LibExecutor.#execs.get(name);
1326
1848
  if (exec) return exec;
1327
- else if (executor instanceof WorkspaceExecutor) return new LibExecutor({ workspace: executor, name });
1849
+ else if (executor instanceof WorkspaceExecutor)
1850
+ return new LibExecutor({ workspace: executor, name });
1328
1851
  else return new LibExecutor({ workspace: executor.workspace, name });
1329
1852
  }
1330
1853
 
@@ -1345,14 +1868,21 @@ export class PkgExecutor extends Executor {
1345
1868
  override name: string;
1346
1869
  dist: Executor;
1347
1870
  override emoji = execEmoji.pkg;
1348
- constructor({ workspace = WorkspaceExecutor.fromRoot(), name }: PkgExecutorOptions) {
1871
+ constructor({
1872
+ workspace = WorkspaceExecutor.fromRoot(),
1873
+ name,
1874
+ }: PkgExecutorOptions) {
1349
1875
  super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
1350
1876
  this.workspace = workspace;
1351
1877
  this.name = name;
1352
- this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
1878
+ this.dist = new Executor(
1879
+ `dist/${name}`,
1880
+ `${this.workspace.workspaceRoot}/dist/pkgs/${name}`,
1881
+ );
1353
1882
  }
1354
1883
  static from(executor: SysExecutor | WorkspaceExecutor, name: string) {
1355
- if (executor instanceof WorkspaceExecutor) return new PkgExecutor({ workspace: executor, name });
1884
+ if (executor instanceof WorkspaceExecutor)
1885
+ return new PkgExecutor({ workspace: executor, name });
1356
1886
  return new PkgExecutor({ workspace: executor.workspace, name });
1357
1887
  }
1358
1888
 
@@ -1364,16 +1894,28 @@ export class PkgExecutor extends Executor {
1364
1894
  this.#scanInfo = scanInfo;
1365
1895
  return scanInfo;
1366
1896
  }
1367
- async #getDependencyVersion(rootPackageJson: PackageJson, dep: string): Promise<string | undefined> {
1368
- const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
1897
+ async #getDependencyVersion(
1898
+ rootPackageJson: PackageJson,
1899
+ dep: string,
1900
+ ): Promise<string | undefined> {
1901
+ const rootDeps = {
1902
+ ...rootPackageJson.dependencies,
1903
+ ...rootPackageJson.devDependencies,
1904
+ };
1369
1905
  const rootVersion = rootDeps[dep];
1370
1906
  if (rootVersion) return rootVersion;
1371
1907
 
1372
1908
  try {
1373
1909
  const packageJsonPath = `pkgs/${dep}/package.json`;
1374
- if (!(await Bun.file(path.join(this.workspace.workspaceRoot, packageJsonPath)).exists())) return undefined;
1910
+ if (
1911
+ !(await Bun.file(
1912
+ path.join(this.workspace.workspaceRoot, packageJsonPath),
1913
+ ).exists())
1914
+ )
1915
+ return undefined;
1375
1916
  const packageJson = await this.workspace.readJson(packageJsonPath);
1376
- if ((packageJson as PackageJson).name === dep) return (packageJson as PackageJson).version;
1917
+ if ((packageJson as PackageJson).name === dep)
1918
+ return (packageJson as PackageJson).version;
1377
1919
  } catch {
1378
1920
  return undefined;
1379
1921
  }
@@ -1384,7 +1926,9 @@ export class PkgExecutor extends Executor {
1384
1926
  devDependencies: string[] = [],
1385
1927
  ): Promise<Pick<PackageJson, "dependencies" | "devDependencies">> {
1386
1928
  const dependencyNames = [...new Set(dependencies)].sort();
1387
- const devDependencyNames = [...new Set(devDependencies)].filter((dep) => !dependencyNames.includes(dep)).sort();
1929
+ const devDependencyNames = [...new Set(devDependencies)]
1930
+ .filter((dep) => !dependencyNames.includes(dep))
1931
+ .sort();
1388
1932
  const dependencyVersions = new Map<string, string>();
1389
1933
  const missingDeps: string[] = [];
1390
1934
  for (const dep of [...dependencyNames, ...devDependencyNames]) {
@@ -1393,26 +1937,40 @@ export class PkgExecutor extends Executor {
1393
1937
  else missingDeps.push(dep);
1394
1938
  }
1395
1939
  if (missingDeps.length > 0)
1396
- throw new Error(`Missing dependency versions in root package.json: ${missingDeps.join(", ")}`);
1940
+ throw new Error(
1941
+ `Missing dependency versions in root package.json: ${missingDeps.join(", ")}`,
1942
+ );
1397
1943
 
1398
1944
  const toDependencyEntries = (names: string[]) =>
1399
1945
  names.map((dep) => {
1400
1946
  const version = dependencyVersions.get(dep);
1401
- if (!version) throw new Error(`Missing dependency versions in root package.json: ${dep}`);
1947
+ if (!version)
1948
+ throw new Error(
1949
+ `Missing dependency versions in root package.json: ${dep}`,
1950
+ );
1402
1951
  return [dep, version] as const;
1403
1952
  });
1404
1953
 
1405
1954
  return {
1406
1955
  dependencies: Object.fromEntries(toDependencyEntries(dependencyNames)),
1407
- devDependencies: Object.fromEntries(toDependencyEntries(devDependencyNames)),
1956
+ devDependencies: Object.fromEntries(
1957
+ toDependencyEntries(devDependencyNames),
1958
+ ),
1408
1959
  };
1409
1960
  }
1410
1961
  async updatePackageJsonDependencies(
1411
1962
  dependencies: string[] = [],
1412
1963
  devDependencies: string[] = [],
1413
1964
  ): Promise<PackageJson> {
1414
- const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
1415
- const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
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
+ );
1416
1974
  const newPkgJson = {
1417
1975
  ...pkgJson,
1418
1976
  ...dependencyMaps,
@@ -1420,17 +1978,37 @@ export class PkgExecutor extends Executor {
1420
1978
  await this.writeJson("package.json", newPkgJson);
1421
1979
  return newPkgJson;
1422
1980
  }
1423
- async generateDistPackageJson(dependencies: string[] = [], devDependencies: string[] = []): Promise<PackageJson> {
1424
- const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
1425
- const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
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
+ );
1426
1994
  const distPkgJson: PackageJson = {
1427
1995
  ...pkgJson,
1428
1996
  type: "module",
1429
- exports: { ...pkgJson.exports, ".": { import: "./index.ts", types: "./index.ts", default: "./index.ts" } },
1997
+ exports: {
1998
+ ...pkgJson.exports,
1999
+ ".": {
2000
+ import: "./index.ts",
2001
+ types: "./index.ts",
2002
+ default: "./index.ts",
2003
+ },
2004
+ },
1430
2005
  engines: { bun: ">=1.3.13" },
1431
2006
  ...dependencyMaps,
1432
2007
  };
1433
- await Promise.all([this.dist.writeJson("package.json", distPkgJson), this.writeJson("package.json", distPkgJson)]);
2008
+ await Promise.all([
2009
+ this.dist.writeJson("package.json", distPkgJson),
2010
+ this.writeJson("package.json", distPkgJson),
2011
+ ]);
1434
2012
  return distPkgJson;
1435
2013
  }
1436
2014
  async build(): Promise<void> {
@@ -1443,7 +2021,10 @@ export class PkgExecutor extends Executor {
1443
2021
  await this.cp(`${this.cwdPath}/dist`, this.dist.cwdPath);
1444
2022
  }
1445
2023
  async generateTsconfigJson(): Promise<TsConfigJson> {
1446
- const [rootTsconfig, pkgTsconfig] = await Promise.all([this.workspace.getTsConfig(), this.getTsConfig()]);
2024
+ const [rootTsconfig, pkgTsconfig] = await Promise.all([
2025
+ this.workspace.getTsConfig(),
2026
+ this.getTsConfig(),
2027
+ ]);
1447
2028
  const tsconfig: TsConfigJson = {
1448
2029
  ...rootTsconfig,
1449
2030
  ...pkgTsconfig,
@@ -1466,7 +2047,10 @@ export class ModuleExecutor extends Executor {
1466
2047
  sys: SysExecutor;
1467
2048
  override emoji = execEmoji.module;
1468
2049
  constructor({ sys, name }: ModuleExecutorOptions) {
1469
- super(name, `${sys.workspace.workspaceRoot}/${sys.type}s/${sys.name}/lib/${name}`);
2050
+ super(
2051
+ name,
2052
+ `${sys.workspace.workspaceRoot}/${sys.type}s/${sys.name}/lib/${name}`,
2053
+ );
1470
2054
  this.sys = sys;
1471
2055
  }
1472
2056
  static from(sysExecutor: SysExecutor, name: string) {