@fragno-dev/cli 0.2.1 → 0.2.3
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/.turbo/turbo-build.log +11 -7
- package/CHANGELOG.md +71 -0
- package/dist/cli.d.ts +4 -52
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +55 -719
- package/dist/cli.js.map +1 -1
- package/dist/find-fragno-databases-Depht1jV.js +184 -0
- package/dist/find-fragno-databases-Depht1jV.js.map +1 -0
- package/dist/serve-eh3Tpjhc.js +87 -0
- package/dist/serve-eh3Tpjhc.js.map +1 -0
- package/package.json +28 -30
- package/src/cli.ts +8 -15
- package/src/commands/db/generate.ts +3 -1
- package/src/commands/db/info.ts +2 -0
- package/src/commands/db/migrate.ts +3 -1
- package/src/commands/search.ts +1 -0
- package/src/commands/serve.ts +5 -2
- package/src/utils/find-fragno-databases.ts +11 -6
- package/src/utils/load-config.test.ts +42 -36
- package/src/utils/load-config.ts +5 -9
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
- package/src/commands/corpus.test.ts +0 -1129
- package/src/commands/corpus.ts +0 -632
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-eh3Tpjhc.js","names":["allFragments: FragnoInstantiatedFragment<\n [],\n unknown,\n Record<string, unknown>,\n Record<string, unknown>,\n Record<string, unknown>,\n unknown,\n Record<string, unknown>\n >[]"],"sources":["../src/commands/serve.ts"],"sourcesContent":["import { createServer, type Server } from \"node:http\";\nimport { resolve, relative } from \"node:path\";\n\nimport { define } from \"gunshi\";\n\nimport type { FragnoInstantiatedFragment } from \"@fragno-dev/core\";\nimport { toNodeHandler } from \"@fragno-dev/node\";\n\nimport { findFragnoFragments } from \"../utils/find-fragno-databases\";\nimport { loadConfig } from \"../utils/load-config\";\n\nexport const serveCommand = define({\n name: \"serve\",\n description: \"Start a local HTTP server to serve one or more Fragno fragments\",\n args: {\n port: {\n type: \"number\",\n short: \"p\",\n description: \"Port to listen on\",\n default: 8080,\n },\n host: {\n type: \"string\",\n short: \"H\",\n description: \"Host to bind to\",\n default: \"localhost\",\n },\n },\n run: async (ctx) => {\n const targets = ctx.positionals;\n const port = ctx.values.port ?? 8080;\n const host = ctx.values.host ?? \"localhost\";\n const cwd = process.cwd();\n\n if (targets.length === 0) {\n throw new Error(\n \"No fragment files specified.\\n\\n\" +\n \"Usage: fragno-cli serve <fragment-file> [fragment-file...]\\n\\n\" +\n \"Example: fragno-cli serve ./src/my-fragment.ts\",\n );\n }\n\n const targetPaths = targets.map((target) => resolve(cwd, target));\n\n // Import all fragment files and find instantiated fragments\n const allFragments: FragnoInstantiatedFragment<\n [],\n unknown,\n Record<string, unknown>,\n Record<string, unknown>,\n Record<string, unknown>,\n unknown,\n Record<string, unknown>\n >[] = [];\n\n for (const targetPath of targetPaths) {\n const relativePath = relative(cwd, targetPath);\n const config = await loadConfig(targetPath);\n const fragments = findFragnoFragments(config);\n\n if (fragments.length === 0) {\n console.warn(\n `Warning: No instantiated fragments found in ${relativePath}.\\n` +\n `Make sure you export an instantiated fragment (e.g., the return value of createMyFragment()).\\n`,\n );\n continue;\n }\n\n allFragments.push(...fragments);\n console.log(\n ` Found ${fragments.length} fragment(s) in ${relativePath}: ${fragments.map((f) => f.name).join(\", \")}`,\n );\n }\n\n if (allFragments.length === 0) {\n throw new Error(\n \"No instantiated fragments found in any of the specified files.\\n\" +\n \"Make sure your files export instantiated fragments.\",\n );\n }\n\n // Build handlers mapped by mountRoute\n const handlers = allFragments.map((fragment) => ({\n mountRoute: fragment.mountRoute,\n handler: toNodeHandler(fragment.handler.bind(fragment)),\n fragment,\n }));\n\n const server = createServer((req, res) => {\n const url = req.url ?? \"\";\n\n for (const { mountRoute, handler } of handlers) {\n if (url.startsWith(mountRoute)) {\n return handler(req, res);\n }\n }\n\n res.statusCode = 404;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: \"Not Found\",\n availableRoutes: handlers.map((h) => h.mountRoute),\n }),\n );\n });\n\n server.listen(port, host, () => {\n const hostStr = addressToString(server);\n console.log(`\\nFragno server is running on: ${hostStr}\\n`);\n\n for (const { fragment } of handlers) {\n console.log(`Fragment: ${fragment.name}`);\n console.log(` Mount: ${hostStr}${fragment.mountRoute}`);\n\n const routes = fragment.routes as unknown as { method: string; path: string }[];\n if (routes.length > 0) {\n console.log(\" Routes:\");\n for (const route of routes) {\n console.log(` ${route.method} ${fragment.mountRoute}${route.path}`);\n }\n }\n\n console.log(\"\");\n }\n });\n },\n});\n\nfunction addressToString(server: Server, protocol: \"http\" | \"https\" = \"http\"): string {\n const addr = server.address();\n if (!addr) {\n throw new Error(\"Address invalid\");\n }\n\n if (typeof addr === \"string\") {\n return addr;\n }\n\n let host = addr.address;\n\n if (host === \"::\" || host === \"0.0.0.0\") {\n host = \"localhost\";\n }\n\n if (addr.family === \"IPv6\" && host !== \"localhost\") {\n host = `[${host}]`;\n }\n\n return `${protocol}://${host}:${addr.port}`;\n}\n"],"mappings":";;;;;;;AAWA,MAAa,eAAe,OAAO;CACjC,MAAM;CACN,aAAa;CACb,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACD,MAAM;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACF;CACD,KAAK,OAAO,QAAQ;EAClB,MAAM,UAAU,IAAI;EACpB,MAAM,OAAO,IAAI,OAAO,QAAQ;EAChC,MAAM,OAAO,IAAI,OAAO,QAAQ;EAChC,MAAM,MAAM,QAAQ,KAAK;AAEzB,MAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,+IAGD;EAGH,MAAM,cAAc,QAAQ,KAAK,WAAW,QAAQ,KAAK,OAAO,CAAC;EAGjE,MAAMA,eAQA,EAAE;AAER,OAAK,MAAM,cAAc,aAAa;GACpC,MAAM,eAAe,SAAS,KAAK,WAAW;GAE9C,MAAM,YAAY,oBADH,MAAM,WAAW,WAAW,CACE;AAE7C,OAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,KACN,+CAA+C,aAAa,oGAE7D;AACD;;AAGF,gBAAa,KAAK,GAAG,UAAU;AAC/B,WAAQ,IACN,WAAW,UAAU,OAAO,kBAAkB,aAAa,IAAI,UAAU,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,GACvG;;AAGH,MAAI,aAAa,WAAW,EAC1B,OAAM,IAAI,MACR,sHAED;EAIH,MAAM,WAAW,aAAa,KAAK,cAAc;GAC/C,YAAY,SAAS;GACrB,SAAS,cAAc,SAAS,QAAQ,KAAK,SAAS,CAAC;GACvD;GACD,EAAE;EAEH,MAAM,SAAS,cAAc,KAAK,QAAQ;GACxC,MAAM,MAAM,IAAI,OAAO;AAEvB,QAAK,MAAM,EAAE,YAAY,aAAa,SACpC,KAAI,IAAI,WAAW,WAAW,CAC5B,QAAO,QAAQ,KAAK,IAAI;AAI5B,OAAI,aAAa;AACjB,OAAI,UAAU,gBAAgB,mBAAmB;AACjD,OAAI,IACF,KAAK,UAAU;IACb,OAAO;IACP,iBAAiB,SAAS,KAAK,MAAM,EAAE,WAAW;IACnD,CAAC,CACH;IACD;AAEF,SAAO,OAAO,MAAM,YAAY;GAC9B,MAAM,UAAU,gBAAgB,OAAO;AACvC,WAAQ,IAAI,kCAAkC,QAAQ,IAAI;AAE1D,QAAK,MAAM,EAAE,cAAc,UAAU;AACnC,YAAQ,IAAI,aAAa,SAAS,OAAO;AACzC,YAAQ,IAAI,YAAY,UAAU,SAAS,aAAa;IAExD,MAAM,SAAS,SAAS;AACxB,QAAI,OAAO,SAAS,GAAG;AACrB,aAAQ,IAAI,YAAY;AACxB,UAAK,MAAM,SAAS,OAClB,SAAQ,IAAI,OAAO,MAAM,OAAO,GAAG,SAAS,aAAa,MAAM,OAAO;;AAI1E,YAAQ,IAAI,GAAG;;IAEjB;;CAEL,CAAC;AAEF,SAAS,gBAAgB,QAAgB,WAA6B,QAAgB;CACpF,MAAM,OAAO,OAAO,SAAS;AAC7B,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,kBAAkB;AAGpC,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAI,OAAO,KAAK;AAEhB,KAAI,SAAS,QAAQ,SAAS,UAC5B,QAAO;AAGT,KAAI,KAAK,WAAW,UAAU,SAAS,YACrC,QAAO,IAAI,KAAK;AAGlB,QAAO,GAAG,SAAS,KAAK,KAAK,GAAG,KAAK"}
|
package/package.json
CHANGED
|
@@ -1,50 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"homepage": "https://fragno.dev",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/rejot-dev/fragno.git",
|
|
9
|
+
"directory": "apps/fragno-cli"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"fragno-cli": "./bin/run.js"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/cli.js",
|
|
16
|
+
"module": "./dist/cli.js",
|
|
17
|
+
"types": "./dist/cli.d.ts",
|
|
4
18
|
"exports": {
|
|
5
19
|
".": {
|
|
6
|
-
"development": "./src/cli.ts",
|
|
7
20
|
"types": "./dist/cli.d.ts",
|
|
8
21
|
"default": "./dist/cli.js"
|
|
9
22
|
}
|
|
10
23
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@clack/prompts": "^0.11.0",
|
|
26
|
+
"c12": "^3.3.3",
|
|
27
|
+
"gunshi": "^0.26.3",
|
|
28
|
+
"jsonc-parser": "^3.3.1",
|
|
29
|
+
"@fragno-dev/core": "0.2.2",
|
|
30
|
+
"@fragno-dev/db": "0.4.1",
|
|
31
|
+
"@fragno-dev/node": "0.0.9"
|
|
17
32
|
},
|
|
18
33
|
"devDependencies": {
|
|
19
34
|
"@types/node": "^22.19.7",
|
|
20
|
-
"@vitest/coverage-istanbul": "^
|
|
35
|
+
"@vitest/coverage-istanbul": "^4.1.0",
|
|
21
36
|
"@fragno-private/typescript-config": "0.0.1",
|
|
22
37
|
"@fragno-private/vitest-config": "0.0.0"
|
|
23
38
|
},
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"c12": "^3.3.3",
|
|
27
|
-
"gunshi": "^0.26.3",
|
|
28
|
-
"marked": "^15.0.12",
|
|
29
|
-
"marked-terminal": "^7.3.0",
|
|
30
|
-
"@fragno-dev/core": "0.2.0",
|
|
31
|
-
"@fragno-dev/corpus": "0.0.7",
|
|
32
|
-
"@fragno-dev/db": "0.3.0",
|
|
33
|
-
"@fragno-dev/node": "0.0.8"
|
|
34
|
-
},
|
|
35
|
-
"main": "./dist/cli.js",
|
|
36
|
-
"module": "./dist/cli.js",
|
|
37
|
-
"types": "./dist/cli.d.ts",
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "https://github.com/rejot-dev/fragno.git",
|
|
41
|
-
"directory": "apps/fragno-cli"
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22"
|
|
42
41
|
},
|
|
43
|
-
"homepage": "https://fragno.dev",
|
|
44
|
-
"license": "MIT",
|
|
45
42
|
"scripts": {
|
|
46
43
|
"build": "tsdown",
|
|
47
44
|
"build:watch": "tsdown --watch",
|
|
48
|
-
"types:check": "tsc --noEmit"
|
|
45
|
+
"types:check": "tsc --noEmit",
|
|
46
|
+
"test": "vitest run"
|
|
49
47
|
}
|
|
50
48
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
3
7
|
import { cli, define } from "gunshi";
|
|
8
|
+
|
|
4
9
|
import { generateCommand } from "./commands/db/generate.js";
|
|
5
|
-
import { migrateCommand } from "./commands/db/migrate.js";
|
|
6
10
|
import { infoCommand } from "./commands/db/info.js";
|
|
11
|
+
import { migrateCommand } from "./commands/db/migrate.js";
|
|
7
12
|
import { searchCommand } from "./commands/search.js";
|
|
8
|
-
import { corpusCommand } from "./commands/corpus.js";
|
|
9
|
-
import { serveCommand } from "./commands/serve.js";
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { dirname, join } from "node:path";
|
|
13
13
|
|
|
14
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
@@ -44,14 +44,9 @@ export async function run() {
|
|
|
44
44
|
name: "fragno-cli search",
|
|
45
45
|
version,
|
|
46
46
|
});
|
|
47
|
-
} else if (args[0] === "corpus") {
|
|
48
|
-
// Run corpus command directly
|
|
49
|
-
await cli(args.slice(1), corpusCommand, {
|
|
50
|
-
name: "fragno-cli corpus",
|
|
51
|
-
version,
|
|
52
|
-
});
|
|
53
47
|
} else if (args[0] === "serve") {
|
|
54
48
|
// Run serve command directly
|
|
49
|
+
const { serveCommand } = await import("./commands/serve.js");
|
|
55
50
|
await cli(args.slice(1), serveCommand, {
|
|
56
51
|
name: "fragno-cli serve",
|
|
57
52
|
version,
|
|
@@ -112,13 +107,11 @@ export async function run() {
|
|
|
112
107
|
console.log(" serve Start a local HTTP server to serve fragments");
|
|
113
108
|
console.log(" db Database management commands");
|
|
114
109
|
console.log(" search Search the Fragno documentation");
|
|
115
|
-
console.log(" corpus View code examples and documentation for Fragno");
|
|
116
110
|
console.log("");
|
|
117
111
|
console.log("For more info, run any command with the `--help` flag:");
|
|
118
112
|
console.log(" fragno-cli serve --help");
|
|
119
113
|
console.log(" fragno-cli db --help");
|
|
120
114
|
console.log(" fragno-cli search --help");
|
|
121
|
-
console.log(" fragno-cli corpus --help");
|
|
122
115
|
console.log("");
|
|
123
116
|
console.log("OPTIONS:");
|
|
124
117
|
console.log(" -h, --help Display this help message");
|
|
@@ -142,4 +135,4 @@ if (import.meta.main) {
|
|
|
142
135
|
await run();
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
export { generateCommand, migrateCommand, infoCommand, searchCommand
|
|
138
|
+
export { generateCommand, migrateCommand, infoCommand, searchCommand };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
2
2
|
import { resolve, dirname } from "node:path";
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import { generateSchemaArtifacts } from "@fragno-dev/db/generation-engine";
|
|
5
|
+
import { define } from "gunshi";
|
|
6
|
+
|
|
5
7
|
import { importFragmentFiles } from "../../utils/find-fragno-databases";
|
|
6
8
|
|
|
7
9
|
// Define the db generate command with type safety
|
package/src/commands/db/info.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { executeMigrations, type ExecuteMigrationResult } from "@fragno-dev/db/generation-engine";
|
|
2
4
|
import { define } from "gunshi";
|
|
5
|
+
|
|
3
6
|
import { importFragmentFiles } from "../../utils/find-fragno-databases";
|
|
4
|
-
import { executeMigrations, type ExecuteMigrationResult } from "@fragno-dev/db/generation-engine";
|
|
5
7
|
|
|
6
8
|
export const migrateCommand = define({
|
|
7
9
|
name: "migrate",
|
package/src/commands/search.ts
CHANGED
package/src/commands/serve.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { createServer, type Server } from "node:http";
|
|
2
2
|
import { resolve, relative } from "node:path";
|
|
3
|
+
|
|
3
4
|
import { define } from "gunshi";
|
|
4
|
-
|
|
5
|
+
|
|
5
6
|
import type { FragnoInstantiatedFragment } from "@fragno-dev/core";
|
|
6
|
-
import {
|
|
7
|
+
import { toNodeHandler } from "@fragno-dev/node";
|
|
8
|
+
|
|
7
9
|
import { findFragnoFragments } from "../utils/find-fragno-databases";
|
|
10
|
+
import { loadConfig } from "../utils/load-config";
|
|
8
11
|
|
|
9
12
|
export const serveCommand = define({
|
|
10
13
|
name: "serve",
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { relative } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { instantiatedFragmentFakeSymbol } from "@fragno-dev/core/internal/symbols";
|
|
2
4
|
import {
|
|
3
5
|
fragnoDatabaseAdapterNameFakeSymbol,
|
|
4
6
|
fragnoDatabaseAdapterVersionFakeSymbol,
|
|
5
7
|
} from "@fragno-dev/db/adapters";
|
|
6
8
|
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
7
|
-
|
|
9
|
+
|
|
8
10
|
import { type FragnoInstantiatedFragment } from "@fragno-dev/core";
|
|
11
|
+
import { isFragnoDatabase, type DatabaseAdapter, FragnoDatabase } from "@fragno-dev/db";
|
|
12
|
+
|
|
9
13
|
import { loadConfig } from "./load-config";
|
|
10
|
-
import { relative } from "node:path";
|
|
11
14
|
|
|
12
15
|
export async function importFragmentFile(path: string): Promise<Record<string, unknown>> {
|
|
13
16
|
// Enable dry run mode for database schema extraction
|
|
@@ -201,13 +204,15 @@ export function findFragnoDatabases(
|
|
|
201
204
|
const options = internal.options as Record<string, unknown>;
|
|
202
205
|
|
|
203
206
|
// Check if this is a database fragment by looking for implicit database dependencies
|
|
204
|
-
if (!deps["
|
|
207
|
+
if (!deps["schema"]) {
|
|
205
208
|
continue;
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
const schema = deps["schema"] as AnySchema;
|
|
209
|
-
const namespace = deps["namespace"] as string;
|
|
210
|
-
const databaseAdapter =
|
|
212
|
+
const namespace = deps["namespace"] as string | null;
|
|
213
|
+
const databaseAdapter =
|
|
214
|
+
(deps["databaseAdapter"] as DatabaseAdapter | undefined) ??
|
|
215
|
+
(options["databaseAdapter"] as DatabaseAdapter | undefined);
|
|
211
216
|
|
|
212
217
|
if (!databaseAdapter) {
|
|
213
218
|
console.warn(
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
|
|
5
|
+
import { stripJsonComments, convertTsconfigPathsToJitiAlias } from "./load-config";
|
|
6
|
+
|
|
5
7
|
describe("stripJsonComments", () => {
|
|
6
8
|
it("should strip single-line comments", () => {
|
|
7
9
|
const input = `{
|
|
8
10
|
// This is a comment
|
|
9
11
|
"key": "value"
|
|
10
12
|
}`;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const result = stripJsonComments(input);
|
|
14
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
15
|
+
expect(JSON.parse(result)).toEqual({ key: "value" });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("does not strip double slashes in strings", () => {
|
|
19
|
+
const input = `{
|
|
20
|
+
"key": "https://example.com/foo",
|
|
21
|
+
"another": "value"
|
|
14
22
|
}`;
|
|
15
|
-
expect(stripJsonComments(input)).toBe(
|
|
23
|
+
expect(stripJsonComments(input)).toBe(input);
|
|
16
24
|
});
|
|
17
25
|
|
|
18
26
|
it("should strip multiple single-line comments", () => {
|
|
@@ -22,13 +30,21 @@ describe("stripJsonComments", () => {
|
|
|
22
30
|
// Second comment
|
|
23
31
|
"key2": "value2"
|
|
24
32
|
}`;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
const result = stripJsonComments(input);
|
|
34
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
35
|
+
expect(JSON.parse(result)).toEqual({ key1: "value1", key2: "value2" });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("does not strip multi line comments chars in strings", () => {
|
|
39
|
+
const input = `{
|
|
40
|
+
"compilerOptions": {
|
|
41
|
+
"paths": {
|
|
42
|
+
"~/*": ["./src/*"]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
46
|
+
}`;
|
|
47
|
+
expect(stripJsonComments(input)).toBe(input);
|
|
32
48
|
});
|
|
33
49
|
|
|
34
50
|
it("should strip multi-line comments", () => {
|
|
@@ -37,11 +53,9 @@ describe("stripJsonComments", () => {
|
|
|
37
53
|
multi-line comment */
|
|
38
54
|
"key": "value"
|
|
39
55
|
}`;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}`;
|
|
44
|
-
expect(stripJsonComments(input)).toBe(expected);
|
|
56
|
+
const result = stripJsonComments(input);
|
|
57
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
58
|
+
expect(JSON.parse(result)).toEqual({ key: "value" });
|
|
45
59
|
});
|
|
46
60
|
|
|
47
61
|
it("should strip multiple multi-line comments", () => {
|
|
@@ -52,13 +66,9 @@ describe("stripJsonComments", () => {
|
|
|
52
66
|
spanning lines */
|
|
53
67
|
"key2": "value2"
|
|
54
68
|
}`;
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"key2": "value2"
|
|
60
|
-
}`;
|
|
61
|
-
expect(stripJsonComments(input)).toBe(expected);
|
|
69
|
+
const result = stripJsonComments(input);
|
|
70
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
71
|
+
expect(JSON.parse(result)).toEqual({ key1: "value1", key2: "value2" });
|
|
62
72
|
});
|
|
63
73
|
|
|
64
74
|
it("should strip both single-line and multi-line comments", () => {
|
|
@@ -69,13 +79,9 @@ describe("stripJsonComments", () => {
|
|
|
69
79
|
comment */
|
|
70
80
|
"key2": "value2" // Another single line
|
|
71
81
|
}`;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"key2": "value2"
|
|
77
|
-
}`;
|
|
78
|
-
expect(stripJsonComments(input)).toBe(expected);
|
|
82
|
+
const result = stripJsonComments(input);
|
|
83
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
84
|
+
expect(JSON.parse(result)).toEqual({ key1: "value1", key2: "value2" });
|
|
79
85
|
});
|
|
80
86
|
|
|
81
87
|
it("should handle strings with comment-like content", () => {
|
|
@@ -83,12 +89,12 @@ describe("stripJsonComments", () => {
|
|
|
83
89
|
"url": "https://example.com",
|
|
84
90
|
"comment": "This // is not a comment"
|
|
85
91
|
}`;
|
|
86
|
-
// Note: This is a known limitation - the simple regex approach
|
|
87
|
-
// will strip what looks like comments even inside strings
|
|
88
|
-
// For tsconfig.json files this is typically fine since URLs/strings
|
|
89
|
-
// with comment syntax are rare
|
|
90
92
|
const result = stripJsonComments(input);
|
|
91
|
-
expect(result).
|
|
93
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
94
|
+
expect(JSON.parse(result)).toEqual({
|
|
95
|
+
url: "https://example.com",
|
|
96
|
+
comment: "This // is not a comment",
|
|
97
|
+
});
|
|
92
98
|
});
|
|
93
99
|
|
|
94
100
|
it("should handle empty input", () => {
|
package/src/utils/load-config.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
2
|
import { readFile, access } from "node:fs/promises";
|
|
3
3
|
import { dirname, resolve, join } from "node:path";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { loadConfig as c12LoadConfig } from "c12";
|
|
6
|
+
import { stripComments } from "jsonc-parser";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Checks if a file exists using async API.
|
|
@@ -37,13 +39,7 @@ async function findTsconfig(startPath: string): Promise<string | null> {
|
|
|
37
39
|
* Strips comments from JSONC (JSON with Comments) content.
|
|
38
40
|
*/
|
|
39
41
|
export function stripJsonComments(jsonc: string): string {
|
|
40
|
-
|
|
41
|
-
let result = jsonc.replace(/\/\/[^\n]*/g, "");
|
|
42
|
-
|
|
43
|
-
// Remove multi-line comments (/* ... */)
|
|
44
|
-
result = result.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
45
|
-
|
|
46
|
-
return result;
|
|
42
|
+
return stripComments(jsonc);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
/**
|
package/tsconfig.json
CHANGED