@akanjs/cli 2.1.0-rc.6 → 2.1.0-rc.7
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/incrementalBuilder.proc.js +18 -25
- package/index.js +21 -26
- package/package.json +2 -2
- package/templates/facetIndex/index.ts +1 -1
- package/typecheck.proc.js +208 -0
|
@@ -3,7 +3,6 @@ var __require = import.meta.require;
|
|
|
3
3
|
|
|
4
4
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
5
5
|
import path36 from "path";
|
|
6
|
-
import { Logger as Logger12 } from "akanjs/common";
|
|
7
6
|
|
|
8
7
|
// pkgs/@akanjs/devkit/aiEditor.ts
|
|
9
8
|
import { input, select } from "@inquirer/prompts";
|
|
@@ -5950,12 +5949,7 @@ class CssImportResolver {
|
|
|
5950
5949
|
}
|
|
5951
5950
|
}
|
|
5952
5951
|
#resolutionBases(fromBase) {
|
|
5953
|
-
return [
|
|
5954
|
-
fromBase,
|
|
5955
|
-
this.#workspaceRoot,
|
|
5956
|
-
path22.dirname(Bun.main),
|
|
5957
|
-
path22.resolve(path22.dirname(Bun.main), "../..")
|
|
5958
|
-
];
|
|
5952
|
+
return [fromBase, this.#workspaceRoot, path22.dirname(Bun.main), path22.resolve(path22.dirname(Bun.main), "../..")];
|
|
5959
5953
|
}
|
|
5960
5954
|
#packageJsonCandidates(pkgName) {
|
|
5961
5955
|
return [
|
|
@@ -7177,22 +7171,6 @@ var SSR_RENDER_EXTERNALS = [
|
|
|
7177
7171
|
"react-server-dom-webpack/client.node",
|
|
7178
7172
|
"react-server-dom-webpack/client.browser"
|
|
7179
7173
|
];
|
|
7180
|
-
var TYPECHECK_WORKER_CODE = `
|
|
7181
|
-
import { TypeChecker } from "@akanjs/devkit";
|
|
7182
|
-
|
|
7183
|
-
try {
|
|
7184
|
-
const configPath = process.env.AKAN_TYPECHECK_TSCONFIG;
|
|
7185
|
-
if (!configPath) throw new Error("AKAN_TYPECHECK_TSCONFIG is required");
|
|
7186
|
-
const result = TypeChecker.checkProject(configPath);
|
|
7187
|
-
if (result.errors.length > 0) {
|
|
7188
|
-
console.error(result.message);
|
|
7189
|
-
process.exit(1);
|
|
7190
|
-
}
|
|
7191
|
-
} catch (error) {
|
|
7192
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
7193
|
-
process.exit(1);
|
|
7194
|
-
}
|
|
7195
|
-
`;
|
|
7196
7174
|
|
|
7197
7175
|
class ApplicationBuildRunner {
|
|
7198
7176
|
#app;
|
|
@@ -7336,7 +7314,8 @@ class ApplicationBuildRunner {
|
|
|
7336
7314
|
return { typecheckDir, tsconfigPath };
|
|
7337
7315
|
}
|
|
7338
7316
|
async#checkProjectInChildProcess(tsconfigPath) {
|
|
7339
|
-
const
|
|
7317
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
7318
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
7340
7319
|
cwd: this.#app.workspace.workspaceRoot,
|
|
7341
7320
|
env: this.#app.getCommandEnv({
|
|
7342
7321
|
AKAN_COMMAND_TYPE: "typecheck",
|
|
@@ -7353,6 +7332,18 @@ class ApplicationBuildRunner {
|
|
|
7353
7332
|
if (exitCode !== 0)
|
|
7354
7333
|
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
7355
7334
|
}
|
|
7335
|
+
async#resolveTypecheckWorkerEntry() {
|
|
7336
|
+
const candidates = [
|
|
7337
|
+
path32.join(this.#app.workspace.workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
7338
|
+
path32.join(this.#app.workspace.workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
7339
|
+
path32.join(import.meta.dir, "typecheck.proc.js"),
|
|
7340
|
+
path32.join(import.meta.dir, "typecheck.proc.ts")
|
|
7341
|
+
];
|
|
7342
|
+
for (const candidate of candidates)
|
|
7343
|
+
if (await Bun.file(candidate).exists())
|
|
7344
|
+
return candidate;
|
|
7345
|
+
throw new Error(`[cli] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
7346
|
+
}
|
|
7356
7347
|
async#buildOrThrow(label, config) {
|
|
7357
7348
|
const result = await Bun.build(config);
|
|
7358
7349
|
if (!result.success)
|
|
@@ -8121,7 +8112,7 @@ class CapacitorApp {
|
|
|
8121
8112
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8122
8113
|
const appInfoPath = path34.relative(this.app.cwdPath, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8123
8114
|
const content = `import type { AppScanResult } from "akanjs";
|
|
8124
|
-
import { withBase } from "
|
|
8115
|
+
import { withBase } from "akanjs/capacitor.base.config";
|
|
8125
8116
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
8126
8117
|
|
|
8127
8118
|
export default withBase(
|
|
@@ -9364,6 +9355,8 @@ import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
|
9364
9355
|
import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
9365
9356
|
"use client";
|
|
9366
9357
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
9358
|
+
import { Logger as Logger12 } from "akanjs/common";
|
|
9359
|
+
|
|
9367
9360
|
class IncrementalBuilder {
|
|
9368
9361
|
#logger = new Logger12("IncrementalBuilder");
|
|
9369
9362
|
#app;
|
package/index.js
CHANGED
|
@@ -5947,12 +5947,7 @@ class CssImportResolver {
|
|
|
5947
5947
|
}
|
|
5948
5948
|
}
|
|
5949
5949
|
#resolutionBases(fromBase) {
|
|
5950
|
-
return [
|
|
5951
|
-
fromBase,
|
|
5952
|
-
this.#workspaceRoot,
|
|
5953
|
-
path22.dirname(Bun.main),
|
|
5954
|
-
path22.resolve(path22.dirname(Bun.main), "../..")
|
|
5955
|
-
];
|
|
5950
|
+
return [fromBase, this.#workspaceRoot, path22.dirname(Bun.main), path22.resolve(path22.dirname(Bun.main), "../..")];
|
|
5956
5951
|
}
|
|
5957
5952
|
#packageJsonCandidates(pkgName) {
|
|
5958
5953
|
return [
|
|
@@ -7174,22 +7169,6 @@ var SSR_RENDER_EXTERNALS = [
|
|
|
7174
7169
|
"react-server-dom-webpack/client.node",
|
|
7175
7170
|
"react-server-dom-webpack/client.browser"
|
|
7176
7171
|
];
|
|
7177
|
-
var TYPECHECK_WORKER_CODE = `
|
|
7178
|
-
import { TypeChecker } from "@akanjs/devkit";
|
|
7179
|
-
|
|
7180
|
-
try {
|
|
7181
|
-
const configPath = process.env.AKAN_TYPECHECK_TSCONFIG;
|
|
7182
|
-
if (!configPath) throw new Error("AKAN_TYPECHECK_TSCONFIG is required");
|
|
7183
|
-
const result = TypeChecker.checkProject(configPath);
|
|
7184
|
-
if (result.errors.length > 0) {
|
|
7185
|
-
console.error(result.message);
|
|
7186
|
-
process.exit(1);
|
|
7187
|
-
}
|
|
7188
|
-
} catch (error) {
|
|
7189
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
7190
|
-
process.exit(1);
|
|
7191
|
-
}
|
|
7192
|
-
`;
|
|
7193
7172
|
|
|
7194
7173
|
class ApplicationBuildRunner {
|
|
7195
7174
|
#app;
|
|
@@ -7333,7 +7312,8 @@ class ApplicationBuildRunner {
|
|
|
7333
7312
|
return { typecheckDir, tsconfigPath };
|
|
7334
7313
|
}
|
|
7335
7314
|
async#checkProjectInChildProcess(tsconfigPath) {
|
|
7336
|
-
const
|
|
7315
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
7316
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
7337
7317
|
cwd: this.#app.workspace.workspaceRoot,
|
|
7338
7318
|
env: this.#app.getCommandEnv({
|
|
7339
7319
|
AKAN_COMMAND_TYPE: "typecheck",
|
|
@@ -7350,6 +7330,18 @@ class ApplicationBuildRunner {
|
|
|
7350
7330
|
if (exitCode !== 0)
|
|
7351
7331
|
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
7352
7332
|
}
|
|
7333
|
+
async#resolveTypecheckWorkerEntry() {
|
|
7334
|
+
const candidates = [
|
|
7335
|
+
path32.join(this.#app.workspace.workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
7336
|
+
path32.join(this.#app.workspace.workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
7337
|
+
path32.join(import.meta.dir, "typecheck.proc.js"),
|
|
7338
|
+
path32.join(import.meta.dir, "typecheck.proc.ts")
|
|
7339
|
+
];
|
|
7340
|
+
for (const candidate of candidates)
|
|
7341
|
+
if (await Bun.file(candidate).exists())
|
|
7342
|
+
return candidate;
|
|
7343
|
+
throw new Error(`[cli] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
7344
|
+
}
|
|
7353
7345
|
async#buildOrThrow(label, config) {
|
|
7354
7346
|
const result = await Bun.build(config);
|
|
7355
7347
|
if (!result.success)
|
|
@@ -8118,7 +8110,7 @@ class CapacitorApp {
|
|
|
8118
8110
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8119
8111
|
const appInfoPath = path34.relative(this.app.cwdPath, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8120
8112
|
const content = `import type { AppScanResult } from "akanjs";
|
|
8121
|
-
import { withBase } from "
|
|
8113
|
+
import { withBase } from "akanjs/capacitor.base.config";
|
|
8122
8114
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
8123
8115
|
|
|
8124
8116
|
export default withBase(
|
|
@@ -10152,7 +10144,9 @@ class PackageRunner extends runner("package") {
|
|
|
10152
10144
|
await pkg.dist.mkdir(pkg.dist.cwdPath);
|
|
10153
10145
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
10154
10146
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
10155
|
-
const packageRuntimeDependencies = {
|
|
10147
|
+
const packageRuntimeDependencies = {
|
|
10148
|
+
"@akanjs/devkit": ["daisyui", "tailwind-scrollbar"]
|
|
10149
|
+
};
|
|
10156
10150
|
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome", "@types/bun"] };
|
|
10157
10151
|
if (pkg.name === "@akanjs/cli") {
|
|
10158
10152
|
const devkitPackageJson = await pkg.workspace.readJson("pkgs/@akanjs/devkit/package.json");
|
|
@@ -10467,6 +10461,7 @@ class CloudCommand extends command("cloud", [CloudScript], ({ public: target })
|
|
|
10467
10461
|
|
|
10468
10462
|
// pkgs/@akanjs/cli/guideline/guideline.prompt.ts
|
|
10469
10463
|
import { randomPicks } from "akanjs/common";
|
|
10464
|
+
|
|
10470
10465
|
class GuidelinePrompt extends Prompter {
|
|
10471
10466
|
workspace;
|
|
10472
10467
|
name;
|
|
@@ -11568,7 +11563,7 @@ class ScalarScript extends script("scalar", [ScalarRunner]) {
|
|
|
11568
11563
|
|
|
11569
11564
|
// pkgs/@akanjs/cli/scalar/scalar.command.ts
|
|
11570
11565
|
class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target }) => ({
|
|
11571
|
-
createScalar: target({ desc: "Create a new scalar type (simple data model without DB)" }).arg("scalarName", String, { desc: "name of scalar" }).option("ai", Boolean, { default: false, desc: "use ai to create scalar" }).
|
|
11566
|
+
createScalar: target({ desc: "Create a new scalar type (simple data model without DB)" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).option("ai", Boolean, { default: false, desc: "use ai to create scalar" }).exec(async function(scalarName, sys3, ai) {
|
|
11572
11567
|
const name = lowerlize2(scalarName.replace(/ /g, ""));
|
|
11573
11568
|
if (ai)
|
|
11574
11569
|
await this.scalarScript.createScalarWithAi(sys3, name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "2.1.0-rc.
|
|
3
|
+
"version": "2.1.0-rc.7",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@langchain/deepseek": "^1.0.26",
|
|
35
35
|
"@langchain/openai": "^1.4.6",
|
|
36
36
|
"@trapezedev/project": "^7.1.4",
|
|
37
|
-
"akanjs": "2.1.0-rc.
|
|
37
|
+
"akanjs": "2.1.0-rc.6",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"daisyui": "^5.5.20",
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
4
|
+
// pkgs/@akanjs/devkit/typeChecker.ts
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import * as ts from "typescript";
|
|
9
|
+
|
|
10
|
+
class TypeChecker {
|
|
11
|
+
configPath;
|
|
12
|
+
configFile;
|
|
13
|
+
config;
|
|
14
|
+
constructor(executor) {
|
|
15
|
+
const configPath = this.#findConfigFile(executor.cwdPath);
|
|
16
|
+
if (!configPath)
|
|
17
|
+
throw new Error("No tsconfig.json found in the project");
|
|
18
|
+
this.configPath = configPath;
|
|
19
|
+
this.configFile = ts.readConfigFile(this.configPath, (fileName) => ts.sys.readFile(fileName));
|
|
20
|
+
const parsedConfig = ts.parseJsonConfigFileContent(this.configFile.config, ts.sys, path.dirname(this.configPath), undefined, this.configPath);
|
|
21
|
+
if (parsedConfig.errors.length > 0) {
|
|
22
|
+
const errorMessages = parsedConfig.errors.map((error) => ts.flattenDiagnosticMessageText(error.messageText, `
|
|
23
|
+
`)).join(`
|
|
24
|
+
`);
|
|
25
|
+
throw new Error(`Error parsing tsconfig.json:
|
|
26
|
+
${errorMessages}`);
|
|
27
|
+
}
|
|
28
|
+
this.config = parsedConfig;
|
|
29
|
+
}
|
|
30
|
+
#findConfigFile(searchPath) {
|
|
31
|
+
return ts.findConfigFile(searchPath, (fileName) => ts.sys.fileExists(fileName), "tsconfig.json");
|
|
32
|
+
}
|
|
33
|
+
check(filePath) {
|
|
34
|
+
const program = ts.createProgram([filePath], this.config.options);
|
|
35
|
+
const diagnostics = [
|
|
36
|
+
...program.getSemanticDiagnostics(),
|
|
37
|
+
...program.getSyntacticDiagnostics(),
|
|
38
|
+
...this.config.options.declaration ? program.getDeclarationDiagnostics() : []
|
|
39
|
+
];
|
|
40
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
41
|
+
const warnings = diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Warning);
|
|
42
|
+
const fileDiagnostics = diagnostics.filter((diagnostic) => diagnostic.file?.fileName === filePath);
|
|
43
|
+
const fileErrors = fileDiagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
44
|
+
const fileWarnings = fileDiagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Warning);
|
|
45
|
+
return { diagnostics, errors, warnings, fileDiagnostics, fileErrors, fileWarnings };
|
|
46
|
+
}
|
|
47
|
+
formatDiagnostics(diagnostics) {
|
|
48
|
+
if (diagnostics.length === 0)
|
|
49
|
+
return chalk.bold("\u2705 No type errors found");
|
|
50
|
+
const output = [];
|
|
51
|
+
let errorCount = 0;
|
|
52
|
+
let warningCount = 0;
|
|
53
|
+
let suggestionCount = 0;
|
|
54
|
+
const diagnosticsByFile = new Map;
|
|
55
|
+
diagnostics.forEach((diagnostic) => {
|
|
56
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error)
|
|
57
|
+
errorCount++;
|
|
58
|
+
else if (diagnostic.category === ts.DiagnosticCategory.Warning)
|
|
59
|
+
warningCount++;
|
|
60
|
+
else if (diagnostic.category === ts.DiagnosticCategory.Suggestion)
|
|
61
|
+
suggestionCount++;
|
|
62
|
+
if (diagnostic.file) {
|
|
63
|
+
const fileName = diagnostic.file.fileName;
|
|
64
|
+
if (!diagnosticsByFile.has(fileName))
|
|
65
|
+
diagnosticsByFile.set(fileName, []);
|
|
66
|
+
const fileDiagnostics = diagnosticsByFile.get(fileName);
|
|
67
|
+
if (fileDiagnostics)
|
|
68
|
+
fileDiagnostics.push(diagnostic);
|
|
69
|
+
} else {
|
|
70
|
+
if (!diagnosticsByFile.has(""))
|
|
71
|
+
diagnosticsByFile.set("", []);
|
|
72
|
+
const fileDiagnostics = diagnosticsByFile.get("");
|
|
73
|
+
if (fileDiagnostics)
|
|
74
|
+
fileDiagnostics.push(diagnostic);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
diagnosticsByFile.forEach((fileDiagnostics, fileName) => {
|
|
78
|
+
if (fileName)
|
|
79
|
+
output.push(`
|
|
80
|
+
${chalk.cyan(fileName)}`);
|
|
81
|
+
fileDiagnostics.forEach((diagnostic) => {
|
|
82
|
+
const categoryText = diagnostic.category === ts.DiagnosticCategory.Error ? "error" : diagnostic.category === ts.DiagnosticCategory.Warning ? "warning" : "suggestion";
|
|
83
|
+
const categoryColor = diagnostic.category === ts.DiagnosticCategory.Error ? chalk.red : diagnostic.category === ts.DiagnosticCategory.Warning ? chalk.yellow : chalk.blue;
|
|
84
|
+
const icon = diagnostic.category === ts.DiagnosticCategory.Error ? "\u274C" : diagnostic.category === ts.DiagnosticCategory.Warning ? "\u26A0\uFE0F" : "\uD83D\uDCA1";
|
|
85
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
86
|
+
`);
|
|
87
|
+
const tsCode = chalk.dim(`(TS${diagnostic.code})`);
|
|
88
|
+
if (diagnostic.file && diagnostic.start !== undefined) {
|
|
89
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
90
|
+
output.push(`
|
|
91
|
+
${icon} ${categoryColor(categoryText)}: ${message} ${tsCode}`);
|
|
92
|
+
output.push(` ${chalk.gray("at")} ${fileName}:${chalk.bold(`${line + 1}:${character + 1}`)}`);
|
|
93
|
+
const sourceLines = diagnostic.file.text.split(`
|
|
94
|
+
`);
|
|
95
|
+
if (line < sourceLines.length) {
|
|
96
|
+
const sourceLine = sourceLines[line];
|
|
97
|
+
const lineNumber = (line + 1).toString().padStart(5, " ");
|
|
98
|
+
output.push(`
|
|
99
|
+
${chalk.dim(`${lineNumber} |`)} ${sourceLine}`);
|
|
100
|
+
const underlinePrefix = " ".repeat(character);
|
|
101
|
+
const length = diagnostic.length ?? 1;
|
|
102
|
+
const underline = "~".repeat(Math.max(1, length));
|
|
103
|
+
output.push(`${chalk.dim(`${" ".repeat(lineNumber.length)} |`)} ${underlinePrefix}${categoryColor(underline)}`);
|
|
104
|
+
}
|
|
105
|
+
} else
|
|
106
|
+
output.push(`
|
|
107
|
+
${icon} ${categoryColor(categoryText)}: ${message} ${tsCode}`);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
const summary = [];
|
|
111
|
+
if (errorCount > 0)
|
|
112
|
+
summary.push(chalk.red(`${errorCount} error(s)`));
|
|
113
|
+
if (warningCount > 0)
|
|
114
|
+
summary.push(chalk.yellow(`${warningCount} warning(s)`));
|
|
115
|
+
if (suggestionCount > 0)
|
|
116
|
+
summary.push(chalk.blue(`${suggestionCount} suggestion(s)`));
|
|
117
|
+
return `
|
|
118
|
+
${summary.join(", ")} found${output.join(`
|
|
119
|
+
`)}`;
|
|
120
|
+
}
|
|
121
|
+
getDetailedDiagnostics(filePath) {
|
|
122
|
+
const { diagnostics } = this.check(filePath);
|
|
123
|
+
const sourceFile = ts.createSourceFile(filePath, readFileSync(filePath, "utf8"), ts.ScriptTarget.Latest, true);
|
|
124
|
+
const details = diagnostics.map((diagnostic) => {
|
|
125
|
+
if (diagnostic.file && diagnostic.start !== undefined) {
|
|
126
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
127
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
128
|
+
`);
|
|
129
|
+
const lines = sourceFile.text.split(`
|
|
130
|
+
`);
|
|
131
|
+
const codeSnippet = line < lines.length ? lines[line] : undefined;
|
|
132
|
+
return { line: line + 1, column: character + 1, message, code: diagnostic.code, codeSnippet };
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
line: 0,
|
|
136
|
+
column: 0,
|
|
137
|
+
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
138
|
+
`),
|
|
139
|
+
code: diagnostic.code
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
return { diagnostics, details };
|
|
143
|
+
}
|
|
144
|
+
hasNoTypeErrors(filePath) {
|
|
145
|
+
try {
|
|
146
|
+
const { diagnostics } = this.check(filePath);
|
|
147
|
+
return diagnostics.length === 0;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
static checkProject(configPath) {
|
|
153
|
+
const parsedConfig = TypeChecker.parseConfig(configPath);
|
|
154
|
+
const host = ts.createIncrementalCompilerHost(parsedConfig.options);
|
|
155
|
+
const builderProgram = ts.createIncrementalProgram({
|
|
156
|
+
rootNames: parsedConfig.fileNames,
|
|
157
|
+
options: parsedConfig.options,
|
|
158
|
+
projectReferences: parsedConfig.projectReferences,
|
|
159
|
+
configFileParsingDiagnostics: parsedConfig.errors,
|
|
160
|
+
host
|
|
161
|
+
});
|
|
162
|
+
const program = builderProgram.getProgram();
|
|
163
|
+
const diagnostics = [...ts.getPreEmitDiagnostics(program), ...builderProgram.emit().diagnostics];
|
|
164
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
165
|
+
const warnings = diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Warning);
|
|
166
|
+
return {
|
|
167
|
+
configPath,
|
|
168
|
+
diagnostics,
|
|
169
|
+
errors,
|
|
170
|
+
warnings,
|
|
171
|
+
message: TypeChecker.formatDiagnosticMessages(diagnostics)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
static parseConfig(configPath) {
|
|
175
|
+
const configFile = ts.readConfigFile(configPath, (fileName) => ts.sys.readFile(fileName));
|
|
176
|
+
const configDiagnostics = configFile.error ? [configFile.error] : [];
|
|
177
|
+
if (!configFile.config) {
|
|
178
|
+
const message = TypeChecker.formatDiagnosticMessages(configDiagnostics);
|
|
179
|
+
throw new Error(message || `Error reading tsconfig.json: ${configPath}`);
|
|
180
|
+
}
|
|
181
|
+
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath), undefined, configPath);
|
|
182
|
+
if (parsedConfig.errors.length > 0)
|
|
183
|
+
throw new Error(TypeChecker.formatDiagnosticMessages(parsedConfig.errors));
|
|
184
|
+
return parsedConfig;
|
|
185
|
+
}
|
|
186
|
+
static formatDiagnosticMessages(diagnostics) {
|
|
187
|
+
return ts.formatDiagnosticsWithColorAndContext(diagnostics, {
|
|
188
|
+
getCanonicalFileName: (fileName) => fileName,
|
|
189
|
+
getCurrentDirectory: () => process.cwd(),
|
|
190
|
+
getNewLine: () => ts.sys.newLine
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts
|
|
196
|
+
try {
|
|
197
|
+
const configPath = process.env.AKAN_TYPECHECK_TSCONFIG;
|
|
198
|
+
if (!configPath)
|
|
199
|
+
throw new Error("AKAN_TYPECHECK_TSCONFIG is required");
|
|
200
|
+
const result = TypeChecker.checkProject(configPath);
|
|
201
|
+
if (result.errors.length > 0) {
|
|
202
|
+
console.error(result.message);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|