@akanjs/cli 2.1.0-rc.5 → 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 +23 -35
- package/index.js +26 -36
- 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 [
|
|
@@ -7087,20 +7081,15 @@ class SsrBaseArtifactBuilder {
|
|
|
7087
7081
|
return { rscClientUrl, vendorMap };
|
|
7088
7082
|
}
|
|
7089
7083
|
async#resolveAkanServerPath() {
|
|
7090
|
-
const candidates = [
|
|
7091
|
-
path30.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"),
|
|
7092
|
-
path30.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"),
|
|
7093
|
-
path30.join(path30.dirname(Bun.main), "node_modules/akanjs/server"),
|
|
7094
|
-
path30.join(path30.dirname(Bun.main), "../../akanjs/server"),
|
|
7095
|
-
path30.resolve(import.meta.dir, "../../server"),
|
|
7096
|
-
path30.resolve(import.meta.dir, "../server")
|
|
7097
|
-
];
|
|
7084
|
+
const candidates = [];
|
|
7098
7085
|
try {
|
|
7099
|
-
candidates.
|
|
7086
|
+
candidates.push(path30.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
7100
7087
|
} catch {}
|
|
7088
|
+
candidates.push(path30.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path30.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
7101
7089
|
try {
|
|
7102
|
-
candidates.
|
|
7090
|
+
candidates.push(path30.dirname(Bun.resolveSync("akanjs/server", path30.dirname(Bun.main))));
|
|
7103
7091
|
} catch {}
|
|
7092
|
+
candidates.push(path30.join(path30.dirname(Bun.main), "node_modules/akanjs/server"), path30.join(path30.dirname(Bun.main), "../../akanjs/server"), path30.resolve(import.meta.dir, "../../server"), path30.resolve(import.meta.dir, "../server"));
|
|
7104
7093
|
for (const candidate of candidates) {
|
|
7105
7094
|
if (await Bun.file(path30.join(candidate, "rscClient.tsx")).exists())
|
|
7106
7095
|
return candidate;
|
|
@@ -7182,22 +7171,6 @@ var SSR_RENDER_EXTERNALS = [
|
|
|
7182
7171
|
"react-server-dom-webpack/client.node",
|
|
7183
7172
|
"react-server-dom-webpack/client.browser"
|
|
7184
7173
|
];
|
|
7185
|
-
var TYPECHECK_WORKER_CODE = `
|
|
7186
|
-
import { TypeChecker } from "@akanjs/devkit";
|
|
7187
|
-
|
|
7188
|
-
try {
|
|
7189
|
-
const configPath = process.env.AKAN_TYPECHECK_TSCONFIG;
|
|
7190
|
-
if (!configPath) throw new Error("AKAN_TYPECHECK_TSCONFIG is required");
|
|
7191
|
-
const result = TypeChecker.checkProject(configPath);
|
|
7192
|
-
if (result.errors.length > 0) {
|
|
7193
|
-
console.error(result.message);
|
|
7194
|
-
process.exit(1);
|
|
7195
|
-
}
|
|
7196
|
-
} catch (error) {
|
|
7197
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
7198
|
-
process.exit(1);
|
|
7199
|
-
}
|
|
7200
|
-
`;
|
|
7201
7174
|
|
|
7202
7175
|
class ApplicationBuildRunner {
|
|
7203
7176
|
#app;
|
|
@@ -7341,7 +7314,8 @@ class ApplicationBuildRunner {
|
|
|
7341
7314
|
return { typecheckDir, tsconfigPath };
|
|
7342
7315
|
}
|
|
7343
7316
|
async#checkProjectInChildProcess(tsconfigPath) {
|
|
7344
|
-
const
|
|
7317
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
7318
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
7345
7319
|
cwd: this.#app.workspace.workspaceRoot,
|
|
7346
7320
|
env: this.#app.getCommandEnv({
|
|
7347
7321
|
AKAN_COMMAND_TYPE: "typecheck",
|
|
@@ -7358,6 +7332,18 @@ class ApplicationBuildRunner {
|
|
|
7358
7332
|
if (exitCode !== 0)
|
|
7359
7333
|
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
7360
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
|
+
}
|
|
7361
7347
|
async#buildOrThrow(label, config) {
|
|
7362
7348
|
const result = await Bun.build(config);
|
|
7363
7349
|
if (!result.success)
|
|
@@ -8126,7 +8112,7 @@ class CapacitorApp {
|
|
|
8126
8112
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8127
8113
|
const appInfoPath = path34.relative(this.app.cwdPath, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8128
8114
|
const content = `import type { AppScanResult } from "akanjs";
|
|
8129
|
-
import { withBase } from "
|
|
8115
|
+
import { withBase } from "akanjs/capacitor.base.config";
|
|
8130
8116
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
8131
8117
|
|
|
8132
8118
|
export default withBase(
|
|
@@ -9369,6 +9355,8 @@ import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
|
9369
9355
|
import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
9370
9356
|
"use client";
|
|
9371
9357
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
9358
|
+
import { Logger as Logger12 } from "akanjs/common";
|
|
9359
|
+
|
|
9372
9360
|
class IncrementalBuilder {
|
|
9373
9361
|
#logger = new Logger12("IncrementalBuilder");
|
|
9374
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 [
|
|
@@ -7084,20 +7079,15 @@ class SsrBaseArtifactBuilder {
|
|
|
7084
7079
|
return { rscClientUrl, vendorMap };
|
|
7085
7080
|
}
|
|
7086
7081
|
async#resolveAkanServerPath() {
|
|
7087
|
-
const candidates = [
|
|
7088
|
-
path30.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"),
|
|
7089
|
-
path30.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"),
|
|
7090
|
-
path30.join(path30.dirname(Bun.main), "node_modules/akanjs/server"),
|
|
7091
|
-
path30.join(path30.dirname(Bun.main), "../../akanjs/server"),
|
|
7092
|
-
path30.resolve(import.meta.dir, "../../server"),
|
|
7093
|
-
path30.resolve(import.meta.dir, "../server")
|
|
7094
|
-
];
|
|
7082
|
+
const candidates = [];
|
|
7095
7083
|
try {
|
|
7096
|
-
candidates.
|
|
7084
|
+
candidates.push(path30.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
7097
7085
|
} catch {}
|
|
7086
|
+
candidates.push(path30.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path30.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
7098
7087
|
try {
|
|
7099
|
-
candidates.
|
|
7088
|
+
candidates.push(path30.dirname(Bun.resolveSync("akanjs/server", path30.dirname(Bun.main))));
|
|
7100
7089
|
} catch {}
|
|
7090
|
+
candidates.push(path30.join(path30.dirname(Bun.main), "node_modules/akanjs/server"), path30.join(path30.dirname(Bun.main), "../../akanjs/server"), path30.resolve(import.meta.dir, "../../server"), path30.resolve(import.meta.dir, "../server"));
|
|
7101
7091
|
for (const candidate of candidates) {
|
|
7102
7092
|
if (await Bun.file(path30.join(candidate, "rscClient.tsx")).exists())
|
|
7103
7093
|
return candidate;
|
|
@@ -7179,22 +7169,6 @@ var SSR_RENDER_EXTERNALS = [
|
|
|
7179
7169
|
"react-server-dom-webpack/client.node",
|
|
7180
7170
|
"react-server-dom-webpack/client.browser"
|
|
7181
7171
|
];
|
|
7182
|
-
var TYPECHECK_WORKER_CODE = `
|
|
7183
|
-
import { TypeChecker } from "@akanjs/devkit";
|
|
7184
|
-
|
|
7185
|
-
try {
|
|
7186
|
-
const configPath = process.env.AKAN_TYPECHECK_TSCONFIG;
|
|
7187
|
-
if (!configPath) throw new Error("AKAN_TYPECHECK_TSCONFIG is required");
|
|
7188
|
-
const result = TypeChecker.checkProject(configPath);
|
|
7189
|
-
if (result.errors.length > 0) {
|
|
7190
|
-
console.error(result.message);
|
|
7191
|
-
process.exit(1);
|
|
7192
|
-
}
|
|
7193
|
-
} catch (error) {
|
|
7194
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
7195
|
-
process.exit(1);
|
|
7196
|
-
}
|
|
7197
|
-
`;
|
|
7198
7172
|
|
|
7199
7173
|
class ApplicationBuildRunner {
|
|
7200
7174
|
#app;
|
|
@@ -7338,7 +7312,8 @@ class ApplicationBuildRunner {
|
|
|
7338
7312
|
return { typecheckDir, tsconfigPath };
|
|
7339
7313
|
}
|
|
7340
7314
|
async#checkProjectInChildProcess(tsconfigPath) {
|
|
7341
|
-
const
|
|
7315
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
7316
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
7342
7317
|
cwd: this.#app.workspace.workspaceRoot,
|
|
7343
7318
|
env: this.#app.getCommandEnv({
|
|
7344
7319
|
AKAN_COMMAND_TYPE: "typecheck",
|
|
@@ -7355,6 +7330,18 @@ class ApplicationBuildRunner {
|
|
|
7355
7330
|
if (exitCode !== 0)
|
|
7356
7331
|
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
7357
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
|
+
}
|
|
7358
7345
|
async#buildOrThrow(label, config) {
|
|
7359
7346
|
const result = await Bun.build(config);
|
|
7360
7347
|
if (!result.success)
|
|
@@ -8123,7 +8110,7 @@ class CapacitorApp {
|
|
|
8123
8110
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8124
8111
|
const appInfoPath = path34.relative(this.app.cwdPath, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8125
8112
|
const content = `import type { AppScanResult } from "akanjs";
|
|
8126
|
-
import { withBase } from "
|
|
8113
|
+
import { withBase } from "akanjs/capacitor.base.config";
|
|
8127
8114
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
8128
8115
|
|
|
8129
8116
|
export default withBase(
|
|
@@ -10157,7 +10144,9 @@ class PackageRunner extends runner("package") {
|
|
|
10157
10144
|
await pkg.dist.mkdir(pkg.dist.cwdPath);
|
|
10158
10145
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
10159
10146
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
10160
|
-
const packageRuntimeDependencies = {
|
|
10147
|
+
const packageRuntimeDependencies = {
|
|
10148
|
+
"@akanjs/devkit": ["daisyui", "tailwind-scrollbar"]
|
|
10149
|
+
};
|
|
10161
10150
|
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome", "@types/bun"] };
|
|
10162
10151
|
if (pkg.name === "@akanjs/cli") {
|
|
10163
10152
|
const devkitPackageJson = await pkg.workspace.readJson("pkgs/@akanjs/devkit/package.json");
|
|
@@ -10472,6 +10461,7 @@ class CloudCommand extends command("cloud", [CloudScript], ({ public: target })
|
|
|
10472
10461
|
|
|
10473
10462
|
// pkgs/@akanjs/cli/guideline/guideline.prompt.ts
|
|
10474
10463
|
import { randomPicks } from "akanjs/common";
|
|
10464
|
+
|
|
10475
10465
|
class GuidelinePrompt extends Prompter {
|
|
10476
10466
|
workspace;
|
|
10477
10467
|
name;
|
|
@@ -11573,7 +11563,7 @@ class ScalarScript extends script("scalar", [ScalarRunner]) {
|
|
|
11573
11563
|
|
|
11574
11564
|
// pkgs/@akanjs/cli/scalar/scalar.command.ts
|
|
11575
11565
|
class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target }) => ({
|
|
11576
|
-
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) {
|
|
11577
11567
|
const name = lowerlize2(scalarName.replace(/ /g, ""));
|
|
11578
11568
|
if (ai)
|
|
11579
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
|
+
}
|