@akanjs/devkit 2.3.9-rc.0 → 2.3.9-rc.10

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.
@@ -130,16 +130,14 @@ describe("materializeCapacitorConfig", () => {
130
130
  describe("root capacitor config helpers", () => {
131
131
  test("clears root configs before writing the temporary json config", async () => {
132
132
  const root = await makeTempRoot();
133
- await Promise.all(
134
- rootCapacitorConfigFilenames.map((file) => writeFile(path.join(root, file), `old ${file}\n`)),
135
- );
133
+ await Promise.all(rootCapacitorConfigFilenames.map((file) => writeFile(path.join(root, file), `old ${file}\n`)));
136
134
 
137
- await writeRootCapacitorConfig(root, "{\n \"appId\": \"com.minimal.app\"\n}\n");
135
+ await writeRootCapacitorConfig(root, '{\n "appId": "com.minimal.app"\n}\n');
138
136
 
139
137
  expect(await Bun.file(path.join(root, "capacitor.config.ts")).exists()).toBe(false);
140
138
  expect(await Bun.file(path.join(root, "capacitor.config.js")).exists()).toBe(false);
141
139
  expect(await Bun.file(path.join(root, "capacitor.config.json")).text()).toBe(
142
- "{\n \"appId\": \"com.minimal.app\"\n}\n",
140
+ '{\n "appId": "com.minimal.app"\n}\n',
143
141
  );
144
142
 
145
143
  await clearRootCapacitorConfigs(root);
@@ -186,7 +184,9 @@ describe("iOS native run helpers", () => {
186
184
  },
187
185
  }),
188
186
  ),
189
- ).toEqual([{ id: "11111111-2222-3333-4444-555555555555", name: "iPhone 16", kind: "simulator", state: "Shutdown" }]);
187
+ ).toEqual([
188
+ { id: "11111111-2222-3333-4444-555555555555", name: "iPhone 16", kind: "simulator", state: "Shutdown" },
189
+ ]);
190
190
  });
191
191
 
192
192
  test("builds xcodebuild destinations and output paths by target kind", () => {
@@ -198,7 +198,9 @@ describe("iOS native run helpers", () => {
198
198
  });
199
199
  expect(deviceCommand.xcodebuildArgs).toContain("id=device-1");
200
200
  expect(deviceCommand.xcodebuildArgs).toContain("App QA");
201
- expect(deviceCommand.appPath).toBe("/repo/apps/minimal/ios/DerivedData/device-1/Build/Products/Debug-iphoneos/App.app");
201
+ expect(deviceCommand.appPath).toBe(
202
+ "/repo/apps/minimal/ios/DerivedData/device-1/Build/Products/Debug-iphoneos/App.app",
203
+ );
202
204
 
203
205
  const simulatorCommand = buildIosNativeRunCommand({
204
206
  appRoot: "/repo/apps/minimal",
@@ -212,7 +214,11 @@ describe("iOS native run helpers", () => {
212
214
 
213
215
  test("classifies iOS signing and device-state failures", () => {
214
216
  expect(classifyIosRunFailure("There are no accounts registered with Xcode").kind).toBe("apple-account");
215
- expect(classifyIosRunFailure('Failed Registering Bundle Identifier: The app identifier "com.minimal.app" cannot be registered to your development team').kind).toBe("bundle-identifier");
217
+ expect(
218
+ classifyIosRunFailure(
219
+ 'Failed Registering Bundle Identifier: The app identifier "com.minimal.app" cannot be registered to your development team',
220
+ ).kind,
221
+ ).toBe("bundle-identifier");
216
222
  expect(classifyIosRunFailure("No profiles for 'com.minimal.app' were found").kind).toBe("provisioning-profile");
217
223
  expect(classifyIosRunFailure("Developer Mode is disabled on this device").kind).toBe("device-state");
218
224
  expect(classifyIosRunFailure("arm64-apple-darwin20.0: error: unknown argument: '-index-store-path'").kind).toBe(
@@ -165,7 +165,7 @@ const assertCurrentDirectoryIsWorkspaceRoot = async () => {
165
165
  );
166
166
  };
167
167
 
168
- const getInternalArgumentValue = async (
168
+ export const getInternalArgumentValue = async (
169
169
  argMeta: InternalArgMeta,
170
170
  value: string | undefined,
171
171
  workspace: WorkspaceExecutor,
@@ -201,6 +201,7 @@ const getInternalArgumentValue = async (
201
201
  }
202
202
  } else if (sysType === "app") {
203
203
  if (value && appNames.includes(value)) return AppExecutor.from(workspace, value);
204
+ if (!value && appNames.length === 1 && appNames[0]) return AppExecutor.from(workspace, appNames[0]);
204
205
  const appName = await select<string>({ message: `Select the ${sysType} name`, choices: appNames });
205
206
  return AppExecutor.from(workspace, appName);
206
207
  } else if (sysType === "lib") {
@@ -1,5 +1,7 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test";
2
+ import { AppExecutor, WorkspaceExecutor } from "../executors";
2
3
  import { App, getArgMetas, Lib, Module, Pkg, Sys, Workspace } from "./argMeta";
4
+ import { getInternalArgumentValue } from "./command";
3
5
  import { command } from "./commandBuilder";
4
6
  import {
5
7
  assertUniqueDependencies,
@@ -127,6 +129,17 @@ describe("command helper metadata", () => {
127
129
  expect(commandHelp).toContain("[default: true]");
128
130
  expect(commandHelp).toContain("Fast, Full");
129
131
  });
132
+
133
+ test("resolves the only app without prompting for selection", async () => {
134
+ const workspace = new WorkspaceExecutor({ workspaceRoot: "/workspace", repoName: "repo" });
135
+ workspace.getExecs = async () => [["single-command-test-app"], [], []];
136
+
137
+ const app = await getInternalArgumentValue({ key: "", idx: 0, type: "App" }, undefined, workspace);
138
+
139
+ expect(app).toBeInstanceOf(AppExecutor);
140
+ expect(app.name).toBe("single-command-test-app");
141
+ expect(app.workspace).toBe(workspace);
142
+ });
130
143
  });
131
144
 
132
145
  describe("command helper dependency injection", () => {
@@ -269,11 +269,11 @@ describe("getModelFileData", () => {
269
269
  ].join("\n"),
270
270
  );
271
271
  await write(
272
- path.join(root, "apps/demo/lib/post/post.Unit.tsx"),
272
+ path.join(root, "apps/demo/lib/post/Post.Unit.tsx"),
273
273
  "export default function Unit() { return null; }\n",
274
274
  );
275
275
  await write(
276
- path.join(root, "apps/demo/lib/post/post.View.tsx"),
276
+ path.join(root, "apps/demo/lib/post/Post.View.tsx"),
277
277
  "export default function View() { return null; }\n",
278
278
  );
279
279
 
package/executors.test.ts CHANGED
@@ -143,11 +143,32 @@ describe("Executor filesystem helpers", () => {
143
143
  "Akan.js Workspace Rules",
144
144
  );
145
145
  expect(await readFile(path.join(root, "workspace/AGENTS.md"), "utf8")).toContain("sample Agent Guide");
146
+ expect(await readFile(path.join(root, "workspace/docs/AI-DEVELOPMENT.md"), "utf8")).toContain(
147
+ "AI Development Guide",
148
+ );
149
+ expect(await readFile(path.join(root, "workspace/docs/GENERATED.md"), "utf8")).toContain("Generated Akan Files");
146
150
  expect(await readFile(path.join(root, "workspace/biome.json"), "utf8")).toContain(
147
151
  "./node_modules/@akanjs/devkit/lint/no-import-client-functions.grit",
148
152
  );
149
153
  });
150
154
 
155
+ test("applies app sample signal test helpers", async () => {
156
+ const root = await makeTempRoot();
157
+ const exec = new Executor("fixture", root);
158
+
159
+ await exec.applyTemplate({
160
+ basePath: "app",
161
+ template: "appSample",
162
+ dict: { appName: "demo" },
163
+ options: { libs: [] },
164
+ });
165
+
166
+ await expect(readFile(path.join(root, "app/lib/task/task.service.test.ts"), "utf8")).rejects.toThrow();
167
+ expect(await readFile(path.join(root, "app/lib/task/task.signal.spec.ts"), "utf8")).toContain("getCompletedTask");
168
+ expect(await readFile(path.join(root, "app/lib/task/task.signal.test.ts"), "utf8")).toContain("Task signal smoke");
169
+ expect(await readFile(path.join(root, "app/lib/task/task.document.ts"), "utf8")).toContain('action: "started"');
170
+ });
171
+
151
172
  test("copies static files from CLI templates", async () => {
152
173
  const root = await makeTempRoot();
153
174
  const exec = new Executor("fixture", root);
package/executors.ts CHANGED
@@ -503,7 +503,7 @@ export class Executor {
503
503
  async writeFile(
504
504
  filePath: string,
505
505
  content: string | object,
506
- { overwrite = true }: { overwrite?: boolean } = {},
506
+ { overwrite = true, silent = false }: { overwrite?: boolean; silent?: boolean } = {},
507
507
  ): Promise<FileContent> {
508
508
  const writePath = this.getPath(filePath);
509
509
  const dir = path.dirname(writePath);
@@ -521,7 +521,7 @@ export class Executor {
521
521
  }
522
522
  } else {
523
523
  await FileSys.writeText(writePath, contentStr);
524
- this.logger.rawLog(chalk.green(`File Create: ${filePath}`));
524
+ if (!silent) this.logger.rawLog(chalk.green(`File Create: ${filePath}`));
525
525
  }
526
526
  return { filePath: writePath, content: contentStr };
527
527
  }
@@ -626,14 +626,14 @@ export class Executor {
626
626
  overwrite?: boolean;
627
627
  },
628
628
  dict: { [key: string]: string } = {},
629
- options: { [key: string]: any } = {},
629
+ options: { [key: string]: unknown } = {},
630
630
  ): Promise<FileContent | null> {
631
631
  if (targetPath.endsWith(".ts") || targetPath.endsWith(".tsx")) {
632
632
  const getContent = (await import(templatePath)) as {
633
633
  default: (
634
634
  scanInfo: AppInfo | LibInfo | null,
635
635
  dict: { [key: string]: string },
636
- options?: { [key: string]: any },
636
+ options?: { [key: string]: unknown },
637
637
  ) => Promise<string | null | { filename: string; content: string }>;
638
638
  };
639
639
  const result = await getContent.default(scanInfo ?? null, dict, options);
@@ -686,7 +686,7 @@ export class Executor {
686
686
  template: string;
687
687
  scanInfo?: AppInfo | LibInfo | null;
688
688
  dict?: { [key: string]: string };
689
- options?: { [key: string]: any };
689
+ options?: { [key: string]: unknown };
690
690
  overwrite?: boolean;
691
691
  }): Promise<FileContent[]> {
692
692
  const templateRoot = await this.#resolveTemplateRoot();
@@ -752,7 +752,7 @@ export class Executor {
752
752
  basePath: string;
753
753
  template: string;
754
754
  dict?: { [key: string]: string };
755
- options?: { [key: string]: any };
755
+ options?: { [key: string]: unknown };
756
756
  overwrite?: boolean;
757
757
  }): Promise<FileContent[]> {
758
758
  const dict = {
@@ -827,10 +827,10 @@ export class Executor {
827
827
  filePath: string,
828
828
  { fix = false, dryRun = false }: { fix?: boolean; dryRun?: boolean } = {},
829
829
  ): Promise<{
830
- results: any[]; // ESLint.LintResult[];
830
+ results: unknown[]; // ESLint.LintResult[];
831
831
  message: string;
832
- errors: any[]; // ESLintLinter.LintMessage[];
833
- warnings: any[]; // ESLintLinter.LintMessage[];
832
+ errors: unknown[]; // ESLintLinter.LintMessage[];
833
+ warnings: unknown[]; // ESLintLinter.LintMessage[];
834
834
  }> {
835
835
  const path = this.getPath(filePath);
836
836
  const linter = this.getLinter();
@@ -1225,40 +1225,44 @@ export class SysExecutor extends Executor {
1225
1225
  async getViewComponents() {
1226
1226
  const viewComponents = (await this.readdir("lib"))
1227
1227
  .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1228
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
1228
+ .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
1229
1229
  return viewComponents;
1230
1230
  }
1231
1231
 
1232
1232
  async getUnitComponents() {
1233
1233
  const unitComponents = (await this.readdir("lib"))
1234
1234
  .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1235
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
1235
+ .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
1236
1236
  return unitComponents;
1237
1237
  }
1238
1238
  async getTemplateComponents() {
1239
1239
  const templateComponents = (await this.readdir("lib"))
1240
1240
  .filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts"))
1241
- .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
1241
+ .filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
1242
1242
  return templateComponents;
1243
1243
  }
1244
1244
 
1245
1245
  async getViewsSourceCode() {
1246
1246
  const viewComponents = await this.getViewComponents();
1247
1247
  return Promise.all(
1248
- viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)),
1248
+ viewComponents.map((viewComponent) =>
1249
+ this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`),
1250
+ ),
1249
1251
  );
1250
1252
  }
1251
1253
  async getUnitsSourceCode() {
1252
1254
  const unitComponents = await this.getUnitComponents();
1253
1255
  return Promise.all(
1254
- unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)),
1256
+ unitComponents.map((unitComponent) =>
1257
+ this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`),
1258
+ ),
1255
1259
  );
1256
1260
  }
1257
1261
  async getTemplatesSourceCode() {
1258
1262
  const templateComponents = await this.getTemplateComponents();
1259
1263
  return Promise.all(
1260
1264
  templateComponents.map((templateComponent) =>
1261
- this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`),
1265
+ this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`),
1262
1266
  ),
1263
1267
  );
1264
1268
  }
@@ -1357,14 +1361,15 @@ export class AppExecutor extends SysExecutor {
1357
1361
  getCommandEnv(env: Record<string, string> = {}): Record<string, string> {
1358
1362
  const basePort = 8282;
1359
1363
  const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
1360
- const PORT = basePort ? (basePort + portOffset).toString() : undefined;
1364
+ const PORT = (basePort + portOffset).toString();
1361
1365
  const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
1362
1366
  return {
1363
1367
  ...process.env,
1364
1368
  AKAN_PUBLIC_APP_NAME: this.name,
1365
1369
  AKAN_WORKSPACE_ROOT: this.workspace.workspaceRoot,
1366
1370
  NODE_NO_WARNINGS: "1",
1367
- ...(PORT ? { PORT, AKAN_PUBLIC_CLIENT_PORT: PORT } : {}),
1371
+ PORT,
1372
+ AKAN_PUBLIC_CLIENT_PORT: PORT,
1368
1373
  ...(AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {}),
1369
1374
  ...env,
1370
1375
  };
@@ -1,3 +1,5 @@
1
+ import { capitalize } from "akanjs/common";
2
+
1
3
  interface ModelFileData {
2
4
  moduleType: "lib" | "app";
3
5
  moduleName: string;
@@ -16,9 +18,10 @@ interface ModelFileData {
16
18
  export const getModelFileData = async (modulePath: string, modelName: string): Promise<ModelFileData> => {
17
19
  const moduleType = modulePath.startsWith("apps") ? "app" : "lib";
18
20
  const moduleName = modulePath.split("/")[1] ?? "";
21
+ const modelComponentName = capitalize(modelName);
19
22
  const constantFilePath = `${modulePath}/lib/${modelName}/${modelName}.constant.ts`;
20
- const unitFilePath = `${modulePath}/lib/${modelName}/${modelName}.Unit.tsx`;
21
- const viewFilePath = `${modulePath}/lib/${modelName}/${modelName}.View.tsx`;
23
+ const unitFilePath = `${modulePath}/lib/${modelName}/${modelComponentName}.Unit.tsx`;
24
+ const viewFilePath = `${modulePath}/lib/${modelName}/${modelComponentName}.View.tsx`;
22
25
  const [constantFileStr, unitFileStr, viewFileStr] = await Promise.all([
23
26
  Bun.file(constantFilePath).text(),
24
27
  Bun.file(unitFilePath).text(),
package/index.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./aiEditor";
2
2
  export * from "./akanApp";
3
3
  export * from "./akanConfig";
4
4
  export * from "./akanContext";
5
+ export * from "./akanMcpContract";
5
6
  export * from "./applicationBuildReporter";
6
7
  export * from "./applicationBuildRunner";
7
8
  export * from "./applicationReleasePackager";
@@ -36,3 +37,4 @@ export * from "./types";
36
37
  export * from "./ui";
37
38
  export * from "./uploadRelease";
38
39
  export * from "./useStdoutDimensions";
40
+ export * from "./workflow";
package/linter.ts CHANGED
@@ -78,15 +78,12 @@ export class Linter {
78
78
 
79
79
  #toBiomePath(filePath: string): string {
80
80
  const relativePath = path.relative(this.lintRoot, filePath);
81
- if (!relativePath.startsWith("..") && !path.isAbsolute(relativePath))
82
- return relativePath;
81
+ if (!relativePath.startsWith("..") && !path.isAbsolute(relativePath)) return relativePath;
83
82
  return filePath;
84
83
  }
85
84
 
86
85
  #resolveFilePath(filePath: string): string {
87
- return path.isAbsolute(filePath)
88
- ? filePath
89
- : path.join(this.lintRoot, filePath);
86
+ return path.isAbsolute(filePath) ? filePath : path.join(this.lintRoot, filePath);
90
87
  }
91
88
 
92
89
  async #runBiome(args: string[], input?: string) {
@@ -125,9 +122,7 @@ export class Linter {
125
122
  #diagnosticFilePath(diagnostic: BiomeDiagnostic, fallbackFilePath: string) {
126
123
  const diagnosticPath = diagnostic.location?.path;
127
124
  if (!diagnosticPath) return fallbackFilePath;
128
- return path.isAbsolute(diagnosticPath)
129
- ? diagnosticPath
130
- : path.join(this.lintRoot, diagnosticPath);
125
+ return path.isAbsolute(diagnosticPath) ? diagnosticPath : path.join(this.lintRoot, diagnosticPath);
131
126
  }
132
127
 
133
128
  #createLintMessage(diagnostic: BiomeDiagnostic): LintMessage {
@@ -148,8 +143,7 @@ export class Linter {
148
143
  const resultsByPath = new Map<string, LintResult>();
149
144
 
150
145
  for (const diagnostic of report.diagnostics ?? []) {
151
- if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
152
- continue;
146
+ if (diagnostic.severity !== "error" && diagnostic.severity !== "warning") continue;
153
147
  const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
154
148
  const result =
155
149
  resultsByPath.get(diagnosticFilePath) ??
@@ -178,9 +172,7 @@ export class Linter {
178
172
  fixableErrorCount: 0,
179
173
  fixableWarningCount: 0,
180
174
  } satisfies LintResult),
181
- ...[...resultsByPath.entries()]
182
- .filter(([resultPath]) => resultPath !== filePath)
183
- .map(([, result]) => result),
175
+ ...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result),
184
176
  ];
185
177
  }
186
178
 
@@ -192,13 +184,8 @@ export class Linter {
192
184
  };
193
185
  }
194
186
 
195
- async #checkFile(
196
- filePath: string,
197
- { write = false }: { write?: boolean } = {},
198
- ): Promise<LintResponse> {
199
- const originalContent = existsSync(filePath)
200
- ? readFileSync(filePath, "utf8")
201
- : "";
187
+ async #checkFile(filePath: string, { write = false }: { write?: boolean } = {}): Promise<LintResponse> {
188
+ const originalContent = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
202
189
  const { stdout, stderr } = await this.#runBiome([
203
190
  "check",
204
191
  ...(write ? ["--write"] : []),
@@ -212,10 +199,7 @@ export class Linter {
212
199
  const report = this.#parseBiomeReport(stdout || stderr);
213
200
  const results = this.#toLintResults(report, filePath);
214
201
  const { errors, warnings } = this.#splitMessages(results);
215
- const output =
216
- write && existsSync(filePath)
217
- ? readFileSync(filePath, "utf8")
218
- : undefined;
202
+ const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
219
203
 
220
204
  return {
221
205
  fixed: write && output !== originalContent,
@@ -226,10 +210,7 @@ export class Linter {
226
210
  };
227
211
  }
228
212
 
229
- async lint(
230
- filePath: string,
231
- { fix = false, dryRun = false }: { fix?: boolean; dryRun?: boolean } = {},
232
- ) {
213
+ async lint(filePath: string, { fix = false, dryRun = false }: { fix?: boolean; dryRun?: boolean } = {}) {
233
214
  if (fix) return await this.fixFile(filePath, dryRun);
234
215
  return await this.lintFile(filePath);
235
216
  }
@@ -241,8 +222,7 @@ export class Linter {
241
222
  */
242
223
  async lintFile(filePath: string): Promise<LintResponse> {
243
224
  const resolvedFilePath = this.#resolveFilePath(filePath);
244
- if (!existsSync(resolvedFilePath))
245
- throw new Error(`File not found: ${filePath}`);
225
+ if (!existsSync(resolvedFilePath)) throw new Error(`File not found: ${filePath}`);
246
226
  return await this.#checkFile(resolvedFilePath);
247
227
  }
248
228
 
@@ -279,16 +259,10 @@ export class Linter {
279
259
  const type = message.severity === 2 ? "error" : "warning";
280
260
  const typeColor = message.severity === 2 ? chalk.red : chalk.yellow;
281
261
  const icon = message.severity === 2 ? "x" : "!";
282
- const ruleInfo = message.ruleId
283
- ? chalk.dim(` (${message.ruleId})`)
284
- : "";
262
+ const ruleInfo = message.ruleId ? chalk.dim(` (${message.ruleId})`) : "";
285
263
 
286
- output.push(
287
- `\n ${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`,
288
- );
289
- output.push(
290
- ` ${chalk.gray("at")} ${result.filePath}:${chalk.bold(`${message.line}:${message.column}`)}`,
291
- );
264
+ output.push(`\n ${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`);
265
+ output.push(` ${chalk.gray("at")} ${result.filePath}:${chalk.bold(`${message.line}:${message.column}`)}`);
292
266
 
293
267
  // Show source line with underline
294
268
  if (sourceLines.length > 0 && message.line <= sourceLines.length) {
@@ -299,28 +273,19 @@ export class Linter {
299
273
 
300
274
  // Create underline
301
275
  const underlinePrefix = " ".repeat(message.column - 1);
302
- const underlineLength = message.endColumn
303
- ? message.endColumn - message.column
304
- : 1;
276
+ const underlineLength = message.endColumn ? message.endColumn - message.column : 1;
305
277
  const underline = "^".repeat(Math.max(1, underlineLength));
306
278
 
307
- output.push(
308
- `${chalk.dim(`${" ".repeat(lineNumber.length)} |`)} ${underlinePrefix}${typeColor(underline)}`,
309
- );
279
+ output.push(`${chalk.dim(`${" ".repeat(lineNumber.length)} |`)} ${underlinePrefix}${typeColor(underline)}`);
310
280
  }
311
281
  });
312
282
  }
313
283
  });
314
284
 
315
- if (totalErrors === 0 && totalWarnings === 0)
316
- return chalk.bold("No Biome errors or warnings found");
285
+ if (totalErrors === 0 && totalWarnings === 0) return chalk.bold("No Biome errors or warnings found");
317
286
 
318
- const errorText =
319
- totalErrors > 0 ? chalk.red(`${totalErrors} error(s)`) : "0 errors";
320
- const warningText =
321
- totalWarnings > 0
322
- ? chalk.yellow(`${totalWarnings} warning(s)`)
323
- : "0 warnings";
287
+ const errorText = totalErrors > 0 ? chalk.red(`${totalErrors} error(s)`) : "0 errors";
288
+ const warningText = totalWarnings > 0 ? chalk.yellow(`${totalWarnings} warning(s)`) : "0 warnings";
324
289
  const summary = [`\n${errorText}, ${warningText} found`];
325
290
 
326
291
  return summary.concat(output).join("\n");
@@ -355,8 +320,7 @@ export class Linter {
355
320
  column: message.column,
356
321
  message: message.message,
357
322
  ruleId: message.ruleId,
358
- severity:
359
- message.severity === 2 ? ("error" as const) : ("warning" as const),
323
+ severity: message.severity === 2 ? ("error" as const) : ("warning" as const),
360
324
  })),
361
325
  );
362
326
 
@@ -365,8 +329,7 @@ export class Linter {
365
329
  errorCount: acc.errorCount + result.errorCount,
366
330
  warningCount: acc.warningCount + result.warningCount,
367
331
  fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
368
- fixableWarningCount:
369
- acc.fixableWarningCount + result.fixableWarningCount,
332
+ fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount,
370
333
  }),
371
334
  {
372
335
  errorCount: 0,
@@ -400,9 +363,7 @@ export class Linter {
400
363
  */
401
364
  async getErrors(filePath: string): Promise<LintMessage[]> {
402
365
  const { results } = await this.lintFile(filePath);
403
- return results.flatMap((result) =>
404
- result.messages.filter((message) => message.severity === 2),
405
- );
366
+ return results.flatMap((result) => result.messages.filter((message) => message.severity === 2));
406
367
  }
407
368
 
408
369
  /**
@@ -412,9 +373,7 @@ export class Linter {
412
373
  */
413
374
  async getWarnings(filePath: string): Promise<LintMessage[]> {
414
375
  const { results } = await this.lintFile(filePath);
415
- return results.flatMap((result) =>
416
- result.messages.filter((message) => message.severity === 1),
417
- );
376
+ return results.flatMap((result) => result.messages.filter((message) => message.severity === 1));
418
377
  }
419
378
 
420
379
  /**
@@ -425,11 +384,9 @@ export class Linter {
425
384
  */
426
385
  async fixFile(filePath: string, dryRun = false): Promise<LintResponse> {
427
386
  const resolvedFilePath = this.#resolveFilePath(filePath);
428
- if (!existsSync(resolvedFilePath))
429
- throw new Error(`File not found: ${filePath}`);
387
+ if (!existsSync(resolvedFilePath)) throw new Error(`File not found: ${filePath}`);
430
388
 
431
- if (!dryRun)
432
- return await this.#checkFile(resolvedFilePath, { write: true });
389
+ if (!dryRun) return await this.#checkFile(resolvedFilePath, { write: true });
433
390
 
434
391
  const source = readFileSync(resolvedFilePath, "utf8");
435
392
  const { stdout } = await this.#runBiome(
@@ -454,11 +411,8 @@ export class Linter {
454
411
  */
455
412
  async getConfigForFile(filePath: string): Promise<unknown> {
456
413
  const resolvedFilePath = this.#resolveFilePath(filePath);
457
- if (!existsSync(resolvedFilePath))
458
- throw new Error(`File not found: ${filePath}`);
459
- return JSON.parse(
460
- readFileSync(path.join(this.lintRoot, "biome.json"), "utf8"),
461
- ) as unknown;
414
+ if (!existsSync(resolvedFilePath)) throw new Error(`File not found: ${filePath}`);
415
+ return JSON.parse(readFileSync(path.join(this.lintRoot, "biome.json"), "utf8")) as unknown;
462
416
  }
463
417
 
464
418
  /**
@@ -472,8 +426,7 @@ export class Linter {
472
426
 
473
427
  results.forEach((result) => {
474
428
  result.messages.forEach((message) => {
475
- if (message.ruleId)
476
- ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
429
+ if (message.ruleId) ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
477
430
  });
478
431
  });
479
432
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/devkit",
3
- "version": "2.3.9-rc.0",
3
+ "version": "2.3.9-rc.10",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -32,7 +32,7 @@
32
32
  "@langchain/openai": "^1.4.6",
33
33
  "@tailwindcss/node": "^4.3.0",
34
34
  "@trapezedev/project": "^7.1.4",
35
- "akanjs": "2.3.9-rc.0",
35
+ "akanjs": "2.3.9-rc.10",
36
36
  "chalk": "^5.6.2",
37
37
  "commander": "^14.0.3",
38
38
  "daisyui": "5.5.23",
@@ -8,8 +8,7 @@ try {
8
8
  if (!cwdPath) throw new Error("AKAN_TYPECHECK_CWD is required");
9
9
 
10
10
  const typeChecker = new TypeChecker({ cwdPath } as never);
11
- const { fileDiagnostics, fileErrors, fileWarnings } =
12
- typeChecker.check(filePath);
11
+ const { fileDiagnostics, fileErrors, fileWarnings } = typeChecker.check(filePath);
13
12
  const message = typeChecker.formatDiagnostics(fileDiagnostics);
14
13
  Logger.rawLog(
15
14
  JSON.stringify({