@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.
- package/aiEditor.test.ts +4 -12
- package/akanContext.ts +409 -15
- package/akanMcpContract.test.ts +37 -0
- package/akanMcpContract.ts +701 -0
- package/capacitorApp.test.ts +14 -8
- package/commandDecorators/command.ts +2 -1
- package/commandDecorators/commandDecorators.test.ts +13 -0
- package/devkitUtils.test.ts +2 -2
- package/executors.test.ts +21 -0
- package/executors.ts +22 -17
- package/getModelFileData.ts +5 -2
- package/index.ts +2 -0
- package/linter.ts +27 -74
- package/package.json +2 -2
- package/typecheck/typecheck.proc.ts +1 -2
- package/workflow/artifacts.ts +571 -0
- package/workflow/executor.ts +590 -0
- package/workflow/index.ts +11 -0
- package/workflow/moduleIndex.test.ts +296 -0
- package/workflow/moduleIndex.ts +504 -0
- package/workflow/plan.ts +429 -0
- package/workflow/primitive.ts +59 -0
- package/workflow/render.ts +335 -0
- package/workflow/rolloutGate.test.ts +109 -0
- package/workflow/rolloutGate.ts +141 -0
- package/workflow/source.test.ts +62 -0
- package/workflow/source.ts +864 -0
- package/workflow/types.ts +475 -0
- package/workflow/uiPolicy.ts +45 -0
- package/workflow/utils.ts +23 -0
package/capacitorApp.test.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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([
|
|
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(
|
|
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(
|
|
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", () => {
|
package/devkitUtils.test.ts
CHANGED
|
@@ -269,11 +269,11 @@ describe("getModelFileData", () => {
|
|
|
269
269
|
].join("\n"),
|
|
270
270
|
);
|
|
271
271
|
await write(
|
|
272
|
-
path.join(root, "apps/demo/lib/post/
|
|
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/
|
|
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]:
|
|
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]:
|
|
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]:
|
|
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]:
|
|
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:
|
|
830
|
+
results: unknown[]; // ESLint.LintResult[];
|
|
831
831
|
message: string;
|
|
832
|
-
errors:
|
|
833
|
-
warnings:
|
|
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) =>
|
|
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) =>
|
|
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 =
|
|
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
|
-
|
|
1371
|
+
PORT,
|
|
1372
|
+
AKAN_PUBLIC_CLIENT_PORT: PORT,
|
|
1368
1373
|
...(AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {}),
|
|
1369
1374
|
...env,
|
|
1370
1375
|
};
|
package/getModelFileData.ts
CHANGED
|
@@ -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}/${
|
|
21
|
-
const viewFilePath = `${modulePath}/lib/${modelName}/${
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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({
|