@aprimediet/codewalker 1.1.0 → 1.2.0
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/package.json +1 -1
- package/src/db.test.ts +148 -2
- package/src/db.ts +196 -29
- package/src/format.test.ts +52 -0
- package/src/format.ts +6 -0
- package/src/index.contract.test.ts +46 -1
- package/src/index.ts +66 -17
- package/src/libs/cards.test.ts +86 -0
- package/src/libs/cards.ts +53 -0
- package/src/libs/dts.test.ts +269 -0
- package/src/libs/dts.ts +213 -0
- package/src/libs/indexer.test.ts +236 -0
- package/src/libs/indexer.ts +291 -0
- package/src/libs/resolve.test.ts +218 -0
- package/src/libs/resolve.ts +120 -0
- package/src/project.ts +3 -1
- package/src/query.test.ts +72 -1
- package/src/query.ts +23 -7
- package/src/types.ts +28 -0
package/src/libs/dts.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .d.ts symbol extraction — regex/line-based, PURE (no I/O).
|
|
3
|
+
*
|
|
4
|
+
* Extracts top-level exported declarations from a `.d.ts` source string.
|
|
5
|
+
* Modeled on src/extract/regex.ts. Returns LibSymbol[] for a given library.
|
|
6
|
+
*
|
|
7
|
+
* Handles these export forms:
|
|
8
|
+
* export declare function|const|class|enum|namespace
|
|
9
|
+
* export function|const|class|interface|type|enum|namespace
|
|
10
|
+
* export abstract class
|
|
11
|
+
* export { a, b as c } from "..." (reexport)
|
|
12
|
+
* export { a, b } (local reexport)
|
|
13
|
+
* export * from "..." (star reexport → name: "*")
|
|
14
|
+
* export default … (name: "default")
|
|
15
|
+
*
|
|
16
|
+
* Non-exported declarations are ignored. Leading JSDoc is captured.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { LibSymbol, SymbolKind } from "../types.ts";
|
|
20
|
+
import { extractDocComment } from "../extract/docs.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Extract top-level exported symbols from a .d.ts source string.
|
|
24
|
+
*
|
|
25
|
+
* @param source - Full .d.ts file content.
|
|
26
|
+
* @param lib - Library name.
|
|
27
|
+
* @param version - Installed version.
|
|
28
|
+
* @returns Array of extracted LibSymbol objects.
|
|
29
|
+
*/
|
|
30
|
+
export function extractDtsSymbols(
|
|
31
|
+
source: string,
|
|
32
|
+
lib: string,
|
|
33
|
+
version: string,
|
|
34
|
+
): LibSymbol[] {
|
|
35
|
+
const lines = source.split("\n");
|
|
36
|
+
const symbols: LibSymbol[] = [];
|
|
37
|
+
let inBlockComment = false;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
const rawLine = lines[i] as string;
|
|
41
|
+
const trimmed = rawLine.trim();
|
|
42
|
+
|
|
43
|
+
// Track block comments
|
|
44
|
+
if (inBlockComment) {
|
|
45
|
+
if (trimmed.includes("*/")) inBlockComment = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (trimmed.startsWith("/*") || trimmed.startsWith("/**")) {
|
|
49
|
+
if (!trimmed.includes("*/")) inBlockComment = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (trimmed.startsWith("//")) continue;
|
|
53
|
+
if (!trimmed) continue;
|
|
54
|
+
|
|
55
|
+
// ── Named exports: export [declare] [abstract] <kind> <name> ──
|
|
56
|
+
const namedExport = tryExtractNamedExport(trimmed, lines, i, lib, version, source);
|
|
57
|
+
if (namedExport) {
|
|
58
|
+
symbols.push(namedExport);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── export default … (name always "default") ──
|
|
63
|
+
const defaultExport = tryExtractDefaultExport(trimmed, lines, i, lib, version, source);
|
|
64
|
+
if (defaultExport) {
|
|
65
|
+
symbols.push(defaultExport);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Re-exports: export { … } or export * from … ──
|
|
70
|
+
const reExport = tryExtractReexport(trimmed, lines, i, lib, version, source);
|
|
71
|
+
if (reExport) {
|
|
72
|
+
symbols.push(...reExport);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return symbols;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Try to extract a named export declaration (function, const, class, interface, type, enum, namespace).
|
|
82
|
+
*/
|
|
83
|
+
function tryExtractNamedExport(
|
|
84
|
+
trimmed: string,
|
|
85
|
+
lines: string[],
|
|
86
|
+
i: number,
|
|
87
|
+
lib: string,
|
|
88
|
+
version: string,
|
|
89
|
+
source: string,
|
|
90
|
+
): LibSymbol | null {
|
|
91
|
+
// Order matters: more specific before less specific.
|
|
92
|
+
// Each pattern: regex with named-kind capture, and a name capture group index.
|
|
93
|
+
const patterns: Array<{ regex: RegExp; kind: SymbolKind; nameIdx: number }> = [
|
|
94
|
+
// function: export [declare] [abstract] [async] function [<T>] name
|
|
95
|
+
{ regex: /^export\s+(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?function\s+(?:<[^>]+>\s+)?(\w+)/, kind: "function", nameIdx: 1 },
|
|
96
|
+
// class: export [declare] [abstract] class name
|
|
97
|
+
{ regex: /^export\s+(?:declare\s+)?(?:abstract\s+)?class\s+(\w+)/, kind: "class", nameIdx: 1 },
|
|
98
|
+
// interface: export interface name
|
|
99
|
+
{ regex: /^export\s+interface\s+(\w+)/, kind: "interface", nameIdx: 1 },
|
|
100
|
+
// type: export type name [<...>] =
|
|
101
|
+
{ regex: /^export\s+type\s+(\w+)/, kind: "type", nameIdx: 1 },
|
|
102
|
+
// enum: export [declare] enum name
|
|
103
|
+
{ regex: /^export\s+(?:declare\s+)?enum\s+(\w+)/, kind: "enum", nameIdx: 1 },
|
|
104
|
+
// namespace: export [declare] namespace name
|
|
105
|
+
{ regex: /^export\s+(?:declare\s+)?namespace\s+(\w+)/, kind: "namespace", nameIdx: 1 },
|
|
106
|
+
// const/let/var: export [declare] const name
|
|
107
|
+
{ regex: /^export\s+(?:declare\s+)?(?:const|let|var)\s+(\w+)/, kind: "const", nameIdx: 1 },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
for (const p of patterns) {
|
|
111
|
+
const m = trimmed.match(p.regex);
|
|
112
|
+
if (m?.[p.nameIdx]) {
|
|
113
|
+
return makeSymbol(lines, i, p.kind, m[p.nameIdx]!, lib, version, source);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Try to extract `export default …`.
|
|
122
|
+
* Name is always "default". Kind is determined by what follows:
|
|
123
|
+
* function → "function", class → "class", object/expr → "const".
|
|
124
|
+
*/
|
|
125
|
+
function tryExtractDefaultExport(
|
|
126
|
+
trimmed: string,
|
|
127
|
+
lines: string[],
|
|
128
|
+
i: number,
|
|
129
|
+
lib: string,
|
|
130
|
+
version: string,
|
|
131
|
+
source: string,
|
|
132
|
+
): LibSymbol | null {
|
|
133
|
+
if (!trimmed.startsWith("export default")) return null;
|
|
134
|
+
|
|
135
|
+
let kind: SymbolKind = "const";
|
|
136
|
+
|
|
137
|
+
if (/^export\s+default\s+(?:async\s+)?function\s/.test(trimmed)) {
|
|
138
|
+
kind = "function";
|
|
139
|
+
} else if (/^export\s+default\s+(?:abstract\s+)?class\s/.test(trimmed)) {
|
|
140
|
+
kind = "class";
|
|
141
|
+
} else if (/^export\s+default\s+interface\s/.test(trimmed)) {
|
|
142
|
+
kind = "interface";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return makeSymbol(lines, i, kind, "default", lib, version, source);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Try to extract re-export declarations:
|
|
150
|
+
* export { a, b as c } [from "..."]
|
|
151
|
+
* export * from "..."
|
|
152
|
+
*/
|
|
153
|
+
function tryExtractReexport(
|
|
154
|
+
trimmed: string,
|
|
155
|
+
lines: string[],
|
|
156
|
+
i: number,
|
|
157
|
+
lib: string,
|
|
158
|
+
version: string,
|
|
159
|
+
source: string,
|
|
160
|
+
): LibSymbol[] | null {
|
|
161
|
+
// export { a, b as c } [from "..."]
|
|
162
|
+
const braceReexport = trimmed.match(/^export\s+\{([^}]+)\}(?:\s+from\s+["'][^"']*["'])?\s*;?\s*$/);
|
|
163
|
+
if (braceReexport) {
|
|
164
|
+
const names = braceReexport[1]!.split(",").map(s => s.trim()).filter(Boolean);
|
|
165
|
+
return names.map((entry) => {
|
|
166
|
+
const aliasMatch = entry.match(/^(\w+)\s+as\s+(\w+)$/);
|
|
167
|
+
const name = aliasMatch?.[2] ?? entry;
|
|
168
|
+
return makeSymbol(lines, i, "reexport", name, lib, version, source);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// export * from "..."
|
|
173
|
+
if (/^export\s+\*\s+from\s+["'][^"']*["']\s*;?\s*$/.test(trimmed)) {
|
|
174
|
+
return [makeSymbol(lines, i, "reexport", "*", lib, version, source)];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Build a LibSymbol from a declaration line.
|
|
182
|
+
* Signature is the declaration line with the body (from `{` onward) stripped.
|
|
183
|
+
*/
|
|
184
|
+
function makeSymbol(
|
|
185
|
+
lines: string[],
|
|
186
|
+
lineIndex: number,
|
|
187
|
+
kind: SymbolKind,
|
|
188
|
+
name: string,
|
|
189
|
+
lib: string,
|
|
190
|
+
version: string,
|
|
191
|
+
source: string,
|
|
192
|
+
): LibSymbol {
|
|
193
|
+
const rawLine = (lines[lineIndex] as string).trim();
|
|
194
|
+
|
|
195
|
+
// Signature: first line, strip everything from the first `{` onward
|
|
196
|
+
const sigStart = rawLine.indexOf("{");
|
|
197
|
+
const signature = (sigStart >= 0 ? rawLine.slice(0, sigStart) : rawLine).trim();
|
|
198
|
+
|
|
199
|
+
// Capture JSDoc
|
|
200
|
+
const doc = extractDocComment(source, lineIndex + 1);
|
|
201
|
+
const summary = (doc.split("\n")[0] || "").trim();
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
lib,
|
|
205
|
+
version,
|
|
206
|
+
name,
|
|
207
|
+
kind,
|
|
208
|
+
signature,
|
|
209
|
+
doc,
|
|
210
|
+
summary,
|
|
211
|
+
card_path: "",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for libs/indexer.ts — library indexer.
|
|
3
|
+
*
|
|
4
|
+
* Integration tests using a fixture project with a fake node_modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import * as os from "node:os";
|
|
11
|
+
import { indexLibraries, rebuildLibDbFromCards } from "./indexer.ts";
|
|
12
|
+
import { openDb, searchLibSymbols, getMeta } from "../db.ts";
|
|
13
|
+
|
|
14
|
+
describe("indexLibraries", () => {
|
|
15
|
+
let tmpDir: string;
|
|
16
|
+
let projectRoot: string;
|
|
17
|
+
let libsDir: string;
|
|
18
|
+
let dbPath: string;
|
|
19
|
+
let nodeModulesDir: string;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "cw-idx-"));
|
|
23
|
+
projectRoot = path.join(tmpDir, "project");
|
|
24
|
+
libsDir = path.join(tmpDir, "codewalker", "entries", "libs");
|
|
25
|
+
dbPath = path.join(tmpDir, "codewalker", "index.db");
|
|
26
|
+
nodeModulesDir = path.join(projectRoot, "node_modules");
|
|
27
|
+
|
|
28
|
+
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
|
29
|
+
fs.mkdirSync(libsDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Write a project package.json
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
path.join(projectRoot, "package.json"),
|
|
34
|
+
JSON.stringify({
|
|
35
|
+
name: "test-project",
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
dependencies: {
|
|
38
|
+
"typed-pkg": "^1.0.0",
|
|
39
|
+
"no-dts-pkg": "^2.0.0",
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function installTypedPkg(): void {
|
|
50
|
+
const pkgDir = path.join(nodeModulesDir, "typed-pkg");
|
|
51
|
+
fs.mkdirSync(path.join(pkgDir, "dist"), { recursive: true });
|
|
52
|
+
fs.writeFileSync(
|
|
53
|
+
path.join(pkgDir, "package.json"),
|
|
54
|
+
JSON.stringify({ name: "typed-pkg", version: "1.2.0", types: "dist/index.d.ts" }),
|
|
55
|
+
);
|
|
56
|
+
fs.writeFileSync(
|
|
57
|
+
path.join(pkgDir, "dist", "index.d.ts"),
|
|
58
|
+
[
|
|
59
|
+
"/** A typed greeting function. */",
|
|
60
|
+
"export declare function greet(name: string): string;",
|
|
61
|
+
"/** Configuration options. */",
|
|
62
|
+
"export interface Config { port: number; }",
|
|
63
|
+
"export const VERSION: string;",
|
|
64
|
+
].join("\n"),
|
|
65
|
+
);
|
|
66
|
+
fs.writeFileSync(pkgDir + "/README.md", "# typed-pkg\nA typed package for testing.\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function installNoDtsPkg(): void {
|
|
70
|
+
const pkgDir = path.join(nodeModulesDir, "no-dts-pkg");
|
|
71
|
+
fs.mkdirSync(pkgDir);
|
|
72
|
+
fs.writeFileSync(
|
|
73
|
+
path.join(pkgDir, "package.json"),
|
|
74
|
+
JSON.stringify({ name: "no-dts-pkg", version: "2.1.0", main: "index.js" }),
|
|
75
|
+
);
|
|
76
|
+
fs.writeFileSync(path.join(pkgDir, "index.js"), "module.exports = {};\n");
|
|
77
|
+
fs.writeFileSync(path.join(pkgDir, "README.md"), "# no-dts-pkg\nA JS-only package.\n");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
it("indexes typed dependency: writes cards and populates DB", async () => {
|
|
81
|
+
installTypedPkg();
|
|
82
|
+
|
|
83
|
+
const result = await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
84
|
+
expect(result.indexed).toBe(1);
|
|
85
|
+
expect(result.symbols).toBe(3);
|
|
86
|
+
expect(result.errors).toBe(0);
|
|
87
|
+
|
|
88
|
+
// Check cards exist
|
|
89
|
+
const pkgCardDir = path.join(libsDir, "typed-pkg@1.2.0");
|
|
90
|
+
expect(fs.existsSync(pkgCardDir)).toBe(true);
|
|
91
|
+
const cardFiles = fs.readdirSync(pkgCardDir);
|
|
92
|
+
// greet, Config, VERSION
|
|
93
|
+
expect(cardFiles).toHaveLength(3);
|
|
94
|
+
|
|
95
|
+
// Check DB
|
|
96
|
+
const db = openDb(dbPath);
|
|
97
|
+
const symbols = searchLibSymbols(db, "", undefined, 10);
|
|
98
|
+
expect(symbols).toHaveLength(3);
|
|
99
|
+
expect(symbols.map(s => s.name)).toContain("greet");
|
|
100
|
+
expect(symbols.map(s => s.name)).toContain("Config");
|
|
101
|
+
expect(symbols.map(s => s.name)).toContain("VERSION");
|
|
102
|
+
expect(symbols[0]!.lib).toBe("typed-pkg");
|
|
103
|
+
expect(symbols[0]!.version).toBe("1.2.0");
|
|
104
|
+
db.close();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("indexes a README-only dependency (no .d.ts)", async () => {
|
|
108
|
+
installNoDtsPkg();
|
|
109
|
+
|
|
110
|
+
const result = await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
111
|
+
// Only no-dts-pkg is installed; typed-pkg missing from node_modules so skipped
|
|
112
|
+
expect(result.indexed).toBe(1);
|
|
113
|
+
expect(result.symbols).toBeGreaterThanOrEqual(1); // at least the module card
|
|
114
|
+
expect(result.errors).toBe(0);
|
|
115
|
+
|
|
116
|
+
// Check README-only package got a module card
|
|
117
|
+
const pkgCardDir = path.join(libsDir, "no-dts-pkg@2.1.0");
|
|
118
|
+
expect(fs.existsSync(pkgCardDir)).toBe(true);
|
|
119
|
+
const cardFiles = fs.readdirSync(pkgCardDir);
|
|
120
|
+
// Should have a module card (README summary)
|
|
121
|
+
expect(cardFiles.length).toBeGreaterThanOrEqual(1);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("is idempotent: re-running produces no duplicates", async () => {
|
|
125
|
+
installTypedPkg();
|
|
126
|
+
|
|
127
|
+
await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
128
|
+
await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
129
|
+
|
|
130
|
+
const db = openDb(dbPath);
|
|
131
|
+
const symbols = searchLibSymbols(db, "", undefined, 10);
|
|
132
|
+
expect(symbols).toHaveLength(3);
|
|
133
|
+
db.close();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("handles missing node_modules gracefully", async () => {
|
|
137
|
+
// Remove node_modules
|
|
138
|
+
fs.rmSync(nodeModulesDir, { recursive: true, force: true });
|
|
139
|
+
|
|
140
|
+
const result = await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
141
|
+
expect(result.indexed).toBe(0);
|
|
142
|
+
expect(result.symbols).toBe(0);
|
|
143
|
+
expect(result.errors).toBe(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("handles a dep that is missing from node_modules with a logged note", async () => {
|
|
147
|
+
// Only install one of the two deps
|
|
148
|
+
installTypedPkg();
|
|
149
|
+
// no-dts-pkg is missing from node_modules
|
|
150
|
+
|
|
151
|
+
const result = await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
152
|
+
// Should index typed-pkg, skip no-dts-pkg
|
|
153
|
+
expect(result.indexed).toBe(1);
|
|
154
|
+
expect(result.errors).toBe(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("includes devDependencies when includeDev=true", async () => {
|
|
158
|
+
// Add a dev dependency
|
|
159
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf-8"));
|
|
160
|
+
pkgJson.devDependencies = { "dev-pkg": "^3.0.0" };
|
|
161
|
+
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(pkgJson));
|
|
162
|
+
|
|
163
|
+
// Install dev-pkg (typed)
|
|
164
|
+
const devPkgDir = path.join(nodeModulesDir, "dev-pkg");
|
|
165
|
+
fs.mkdirSync(devPkgDir);
|
|
166
|
+
fs.writeFileSync(
|
|
167
|
+
path.join(devPkgDir, "package.json"),
|
|
168
|
+
JSON.stringify({ name: "dev-pkg", version: "3.0.0" }),
|
|
169
|
+
);
|
|
170
|
+
fs.writeFileSync(path.join(devPkgDir, "index.d.ts"), "export const devVar: number;\n");
|
|
171
|
+
|
|
172
|
+
// Install other deps too
|
|
173
|
+
installTypedPkg();
|
|
174
|
+
|
|
175
|
+
const result = await indexLibraries({ projectRoot, libsDir, dbPath, includeDev: true });
|
|
176
|
+
// typed-pkg + dev-pkg (no-dts-pkg missing from node_modules so skipped silently)
|
|
177
|
+
expect(result.indexed).toBe(2);
|
|
178
|
+
expect(result.symbols).toBeGreaterThanOrEqual(4); // 3 from typed-pkg + 1 from dev-pkg
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("rebuildLibDbFromCards repopulates lib_symbols from cards", async () => {
|
|
182
|
+
installTypedPkg();
|
|
183
|
+
await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
184
|
+
|
|
185
|
+
// Delete the DB and recreate from cards
|
|
186
|
+
const oldDb = openDb(dbPath);
|
|
187
|
+
oldDb.exec("DELETE FROM lib_symbols; DELETE FROM lib_symbols_fts; DELETE FROM libraries;");
|
|
188
|
+
oldDb.close();
|
|
189
|
+
|
|
190
|
+
rebuildLibDbFromCards(dbPath, libsDir);
|
|
191
|
+
|
|
192
|
+
const db = openDb(dbPath);
|
|
193
|
+
const symbols = searchLibSymbols(db, "", undefined, 10);
|
|
194
|
+
expect(symbols).toHaveLength(3);
|
|
195
|
+
db.close();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("version bump prunes old cards+rows and adds new", async () => {
|
|
199
|
+
// Install v1
|
|
200
|
+
const pkgDir = path.join(nodeModulesDir, "typed-pkg");
|
|
201
|
+
fs.mkdirSync(path.join(pkgDir, "dist"), { recursive: true });
|
|
202
|
+
fs.writeFileSync(
|
|
203
|
+
path.join(pkgDir, "package.json"),
|
|
204
|
+
JSON.stringify({ name: "typed-pkg", version: "1.0.0", types: "dist/index.d.ts" }),
|
|
205
|
+
);
|
|
206
|
+
fs.writeFileSync(path.join(pkgDir, "dist", "index.d.ts"), "export const OLD: number;\n");
|
|
207
|
+
|
|
208
|
+
await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
209
|
+
|
|
210
|
+
// Check v1 card exists
|
|
211
|
+
expect(fs.existsSync(path.join(libsDir, "typed-pkg@1.0.0"))).toBe(true);
|
|
212
|
+
|
|
213
|
+
// Now "upgrade" to v2
|
|
214
|
+
fs.writeFileSync(
|
|
215
|
+
path.join(pkgDir, "package.json"),
|
|
216
|
+
JSON.stringify({ name: "typed-pkg", version: "2.0.0", types: "dist/index.d.ts" }),
|
|
217
|
+
);
|
|
218
|
+
fs.writeFileSync(path.join(pkgDir, "dist", "index.d.ts"), "export const NEW: number;\n");
|
|
219
|
+
|
|
220
|
+
await indexLibraries({ projectRoot, libsDir, dbPath });
|
|
221
|
+
|
|
222
|
+
// Old card dir should be gone
|
|
223
|
+
expect(fs.existsSync(path.join(libsDir, "typed-pkg@1.0.0"))).toBe(false);
|
|
224
|
+
|
|
225
|
+
// New card dir exists
|
|
226
|
+
expect(fs.existsSync(path.join(libsDir, "typed-pkg@2.0.0"))).toBe(true);
|
|
227
|
+
|
|
228
|
+
// DB has new symbols only
|
|
229
|
+
const db = openDb(dbPath);
|
|
230
|
+
const symbols = searchLibSymbols(db, "", undefined, 10);
|
|
231
|
+
expect(symbols).toHaveLength(1);
|
|
232
|
+
expect(symbols[0]!.name).toBe("NEW");
|
|
233
|
+
expect(symbols[0]!.version).toBe("2.0.0");
|
|
234
|
+
db.close();
|
|
235
|
+
});
|
|
236
|
+
});
|