@c4a/extract-ts 0.7.1 → 0.7.4

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.
Files changed (4) hide show
  1. package/README.md +16 -36
  2. package/README.zh-CN.md +11 -28
  3. package/index.js +311 -38
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -4,14 +4,13 @@
4
4
 
5
5
  `@c4a/extract-ts` turns TypeScript, JavaScript, TSX, and JSX structure into deterministic code
6
6
  evidence for Context knowledge production. It implements the
7
- `ExtractionPlugin` protocol from `@c4a/extract` and is the default plugin used
8
- by the SDK `extractTs({ source, collection: "codegraph" })` phase for npm-style
9
- packages.
7
+ `ExtractionPlugin` protocol from `@c4a/extract` and is the community Code
8
+ Indexer Provider's ECMAScript parser for npm-style packages.
10
9
 
11
10
  It extracts code facts; it does not decide product meaning, write approved
12
11
  Markdown, or choose the user's source boundary. Knowledge-workspace users reach
13
- it through the Context Agent entry and a confirmed extraction phase. The direct
14
- APIs below are for reusable structural analysis and project-owned adapters.
12
+ it through the selected Code Indexer Provider. The direct APIs below are for
13
+ Provider authors and reusable structural analysis.
15
14
 
16
15
  ## Package Role
17
16
 
@@ -24,12 +23,12 @@ confirmed TypeScript/JavaScript boundary
24
23
 
25
24
  entry detection + export tracing + AST facts
26
25
 
27
- raw code snapshot review candidates → approved knowledge
26
+ Indexer factsreader-oriented Candidates → approved knowledge
28
27
  ```
29
28
 
30
29
  ## React Router structural facts
31
30
 
32
- Projects that use `extractCustom()` can reuse `extractReactRouterRoutes()` to
31
+ Indexer Providers can reuse `extractReactRouterRoutes()` to
33
32
  index JSX `<Route>` declarations and route-object arrays. It reports paths,
34
33
  components, redirects, conditions, import sources, notes, and source locations
35
34
  without classifying product meaning:
@@ -83,10 +82,9 @@ const moduleFacts = extractEcmaScriptModuleExports(source, "src/index.cjs");
83
82
  - package version propagation into `ExtractionResult.package.version`
84
83
  - authored `.js`, `.jsx`, `.mjs`, and `.cjs` entry files without remapping them to TypeScript
85
84
 
86
- The Context project may override auto-detection with source-relative
87
- `extractTs.entries`, or use `mode: "scan"` to make every `include`-matched file
88
- an extraction root. These settings live in the Context workspace; consumers do
89
- not need to modify the analyzed package solely to declare extraction entries.
85
+ The Code Indexer Provider may override auto-detection with source-relative
86
+ entries, or use scan mode to make every include-matched file an extraction
87
+ root. Consumers do not modify the analyzed package solely to configure parsing.
90
88
 
91
89
  Entry files are returned as module-relative paths. The repository runner later prefixes them to repo-relative paths in raw snapshots.
92
90
 
@@ -160,9 +158,8 @@ identities, per-file owner/disposition, explicit denominators, diagnostics, and
160
158
  the ordered parser receipt through `context.indexer.evidence-adapter-result/v1`.
161
159
  `ExtractionResult` remains available for the existing raw snapshot runner.
162
160
 
163
- The Context CLI consumes those rows during `context run extract:<source>:codegraph`
164
- to build review candidates for package/category/symbol knowledge Nodes. The
165
- important projection inputs are:
161
+ The Code Indexer Provider consumes those facts while producing the current
162
+ Indexer result. Important authoring inputs include:
166
163
 
167
164
  - stable package names and versions
168
165
  - stable exported symbol names
@@ -187,33 +184,16 @@ const registry = new ExtractionPluginRegistry();
187
184
  registry.register(new TypeScriptPlugin());
188
185
  ```
189
186
 
190
- Workspace usage normally goes through a declared project phase:
191
-
192
- ```ts
193
- import { defineProject, extractTs, reviewValidity, source } from "@c4a/context";
194
-
195
- const componentLib = source("component-lib");
196
-
197
- export default defineProject({
198
- sources: [componentLib],
199
- phases: [
200
- extractTs({ source: componentLib, collection: "codegraph" }),
201
- reviewValidity({ collection: "codegraph" }),
202
- ],
203
- packages: [],
204
- });
205
- ```
206
-
207
- The source boundary is registered first, then the phase is previewed or run:
187
+ Workspace usage registers the source boundary, then selects the Code Indexer
188
+ Provider through `src/indexers.yaml`:
208
189
 
209
190
  ```bash
210
191
  context source add repo component-lib --local ../packages/component-lib
211
- context run extract:component-lib:codegraph --dry-run
212
- context run extract:component-lib:codegraph
192
+ context status --format json
213
193
  ```
214
194
 
215
- Agents should not hand-build runner input or raw snapshots for normal Context
216
- workspace operation.
195
+ Agents should not hand-build parser input, raw snapshots, or Candidate files
196
+ for normal Context workspace operation.
217
197
 
218
198
  ## Development
219
199
 
package/README.zh-CN.md CHANGED
@@ -3,12 +3,12 @@
3
3
  [English](./README.md)
4
4
 
5
5
  `@c4a/extract-ts` 将 TypeScript、JavaScript、TSX 和 JSX 结构转化为 Context 知识生产可使用的确定性
6
- 代码证据。它实现 `@c4a/extract` 的 `ExtractionPlugin` 协议,也是 npm-style
7
- package 使用 `extractTs({ source, collection: "codegraph" })` 阶段时的默认插件。
6
+ 代码证据。它实现 `@c4a/extract` 的 `ExtractionPlugin` 协议,也是社区 Code
7
+ Indexer Provider 面向 npm-style package 使用的 ECMAScript 解析器。
8
8
 
9
9
  它只提取代码事实,不判断产品含义、不写正式 Markdown,也不替用户选择来源边界。
10
- 知识工作区用户通过 Context Agent 入口和已确认的提取阶段使用它;直接 API 面向
11
- 可复用结构分析和项目自有 Adapter。
10
+ 知识工作区用户通过选中的 Code Indexer Provider 使用它;直接 API 面向 Provider
11
+ 作者和可复用结构分析。
12
12
 
13
13
  ## 在知识生产链中的职责
14
14
 
@@ -17,7 +17,7 @@ package 使用 `extractTs({ source, collection: "codegraph" })` 阶段时的默
17
17
 
18
18
  入口检测 + 导出追踪 + AST 事实
19
19
 
20
- 原始代码快照审核候选 → 正式知识
20
+ Indexer 事实 面向读者的 Candidate → 正式知识
21
21
  ```
22
22
 
23
23
  `@c4a/extract-ts` 负责 npm package 入口检测和 ECMAScript-family AST 提取。它不直接写工作区;
@@ -29,7 +29,7 @@ package 使用 `extractTs({ source, collection: "codegraph" })` 阶段时的默
29
29
 
30
30
  ### React Router 结构事实
31
31
 
32
- 使用 `extractCustom()` 的项目可以复用 `extractReactRouterRoutes()`,索引 JSX
32
+ Indexer Provider 可以复用 `extractReactRouterRoutes()`,索引 JSX
33
33
  `<Route>` 声明和 route-object 数组:
34
34
 
35
35
  ```ts
@@ -83,9 +83,8 @@ const moduleFacts = extractEcmaScriptModuleExports(source, "src/index.cjs");
83
83
  - 将 package version 写入 `ExtractionResult.package.version`。
84
84
  - 直接识别源码目录中的 `.js`、`.jsx`、`.mjs` 和 `.cjs`,不会误映射为 TypeScript。
85
85
 
86
- Context 项目可以使用 source-relative `extractTs.entries` 覆盖自动检测,或用
87
- `mode: "scan"` 把所有 `include` 命中的文件作为提取根。这些设置属于知识工作区,
88
- 无需修改被分析 package。
86
+ Code Indexer Provider 可以使用 source-relative entries 覆盖自动检测,或用 scan
87
+ 模式把所有命中 include 的文件作为解析根;无需修改被分析 package。
89
88
 
90
89
  入口文件以 module-relative 路径返回,Repository Runner 再为原始快照补充
91
90
  repo-relative 前缀。
@@ -162,25 +161,9 @@ const registry = new ExtractionPluginRegistry();
162
161
  registry.register(new TypeScriptPlugin());
163
162
  ```
164
163
 
165
- 正常工作区在 `src/index.ts` 声明项目阶段:
166
-
167
- ```ts
168
- import { defineProject, extractTs, reviewValidity, source } from "@c4a/context";
169
-
170
- const componentLib = source("component-lib");
171
-
172
- export default defineProject({
173
- sources: [componentLib],
174
- phases: [
175
- extractTs({ source: componentLib, collection: "codegraph" }),
176
- reviewValidity({ collection: "codegraph" }),
177
- ],
178
- packages: [],
179
- });
180
- ```
181
-
182
- 实际提取由当前 Context Route 驱动。Agent 不应在正常知识生产中手工构造 Runner
183
- 输入或原始快照。
164
+ 正常工作区先登记来源,再由 `src/indexers.yaml` 选择 Code Indexer Provider。
165
+ 实际解析由当前 Context Route 驱动。Agent 不应在正常知识生产中手工构造 Runner
166
+ 输入、原始快照或 Candidate 文件。
184
167
 
185
168
  ## 开发
186
169
 
package/index.js CHANGED
@@ -11404,6 +11404,8 @@ function keyTokens(key) {
11404
11404
  return key.replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replace(/[^A-Za-z0-9]+/gu, "-").toLowerCase().split("-").filter(Boolean);
11405
11405
  }
11406
11406
  function sensitiveKey(key, value) {
11407
+ if (value === INDEXER_OUTPUT_REDACTION_MARKER)
11408
+ return false;
11407
11409
  const tokens = keyTokens(key);
11408
11410
  if (tokens.length === 0)
11409
11411
  return false;
@@ -11445,12 +11447,12 @@ function replaceWithCount(value, pattern, replacement, count) {
11445
11447
  function redactKnownText(value, count) {
11446
11448
  let output = value;
11447
11449
  output = replaceWithCount(output, /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/gu, INDEXER_OUTPUT_REDACTION_MARKER, count);
11448
- output = replaceWithCount(output, /(\bauthorization\s*:\s*(?:bearer|basic)\s+)[^\s,;]+/giu, (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
11450
+ output = replaceWithCount(output, new RegExp(`(\\bauthorization\\s*:\\s*(?:bearer|basic)\\s+)` + `(?!${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})[^\\s,;]+`, "giu"), (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
11449
11451
  output = replaceWithCount(output, /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/giu, (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}@`, count);
11450
- output = replaceWithCount(output, /([?&](?:access_token|refresh_token|api_key|password|secret)=)[^&#\s]+/giu, (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
11452
+ output = replaceWithCount(output, new RegExp(`([?&](?:access_token|refresh_token|api_key|password|secret)=)` + `(?!${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})[^&#\\s]+`, "giu"), (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
11451
11453
  const key = "(?:[A-Za-z0-9_.-]*(?:password|passwd|pwd|secret|token|credential|cookie)[A-Za-z0-9_.-]*|api[-_]?key|access[-_]?(?:key|token)|private[-_]?key|client[-_]?secret|authorization)";
11452
11454
  const assignment = `(?:=\\s*|:\\s+(?=\\S)|:\\s*(?=["']))`;
11453
- output = replaceWithCount(output, new RegExp(`((?:["']?${key}["']?)\\s*${assignment})(?:"(?:\\\\.|[^"])*"|'(?:\\\\.|[^'])*'|[^\\s,;}\\]]+)`, "giu"), (_match, prefix) => `${prefix}"${INDEXER_OUTPUT_REDACTION_MARKER}"`, count);
11455
+ output = replaceWithCount(output, new RegExp(`((?:["']?${key}["']?)\\s*${assignment})` + `(?!["']?${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})` + `(?:"(?:\\\\.|[^"])*"|'(?:\\\\.|[^'])*'|[^\\s,;}\\]]+)`, "giu"), (_match, prefix) => `${prefix}"${INDEXER_OUTPUT_REDACTION_MARKER}"`, count);
11454
11456
  return output;
11455
11457
  }
11456
11458
  function redactBlockedText(value, blocked, count) {
@@ -12312,6 +12314,7 @@ var buildToSourcePaths = (withoutExtension) => {
12312
12314
  }
12313
12315
  const fullRest = rest.join("/");
12314
12316
  results.push(`src/${fullRest}`);
12317
+ results.push(fullRest);
12315
12318
  return unique(results);
12316
12319
  };
12317
12320
  var createCandidatePaths = (value, options = {}) => {
@@ -12463,7 +12466,7 @@ var expandWorkspacePattern = async (rootDir, pattern, fs) => {
12463
12466
  if (pattern.endsWith("/*")) {
12464
12467
  const prefix = pattern.slice(0, -2);
12465
12468
  const baseDir = prefix ? `${rootDir}/${prefix}` : rootDir;
12466
- const entries = await fs.readdir(baseDir);
12469
+ const entries = (await fs.readdir(baseDir)).sort();
12467
12470
  return entries.map((entry) => `${baseDir}/${entry}`.replace(/^\.\//, ""));
12468
12471
  }
12469
12472
  return [`${rootDir}/${pattern}`.replace(/^\.\//, "")];
@@ -12814,6 +12817,10 @@ var codeExtractRunnerInputSchema = exports_external.object({
12814
12817
  });
12815
12818
  // ../extract/src/evidenceAdapter.ts
12816
12819
  function fact2(input) {
12820
+ const payload = redactIndexerOutput({
12821
+ channel: "success-payload",
12822
+ value: input.payload
12823
+ }).value;
12817
12824
  return createIndexerEvidenceAdapterFact({
12818
12825
  source_ref: input.sourceRef,
12819
12826
  module_ref: input.moduleRef,
@@ -12821,10 +12828,21 @@ function fact2(input) {
12821
12828
  qualified_item_path: input.qualifiedItemPath,
12822
12829
  kind: input.kind,
12823
12830
  signature: input.signature,
12824
- payload: input.payload,
12831
+ payload,
12825
12832
  denominator: input.denominator
12826
12833
  });
12827
12834
  }
12835
+ function uniqueFacts(facts) {
12836
+ const byRef = new Map;
12837
+ for (const value of facts) {
12838
+ const previous = byRef.get(value.fact_ref);
12839
+ if (previous !== undefined && indexerEvidenceAdapterProtocolDigest(previous) !== indexerEvidenceAdapterProtocolDigest(value)) {
12840
+ throw new TypeError(`ExtractionResult emits conflicting facts for ${value.fact_ref}`);
12841
+ }
12842
+ byRef.set(value.fact_ref, previous ?? value);
12843
+ }
12844
+ return [...byRef.values()];
12845
+ }
12828
12846
  function semanticExtractionPayload(extraction) {
12829
12847
  return {
12830
12848
  version: extraction.version,
@@ -12854,6 +12872,13 @@ function relationSourceFile(relation, symbols, filePaths) {
12854
12872
  }
12855
12873
  return null;
12856
12874
  }
12875
+ function relationQualifiedItemPath(relation) {
12876
+ const identityDigest = indexerEvidenceAdapterProtocolDigest({
12877
+ from: relation.from,
12878
+ to: relation.to
12879
+ });
12880
+ return `relation:${relation.type}@${relation.line ?? 0}#${identityDigest}`;
12881
+ }
12857
12882
  function diagnosticPayload(diagnostic) {
12858
12883
  return {
12859
12884
  code: diagnostic.code,
@@ -12960,7 +12985,7 @@ function extractionResultToEvidenceAdapterResult(extraction, invocation) {
12960
12985
  sourceRef: invocation.authorized_scope.source_ref,
12961
12986
  moduleRef: invocation.module_ref,
12962
12987
  normalizedPath: normalizedPath2,
12963
- qualifiedItemPath: `relation:${relation.type}:${relation.from}->${relation.to}@${relation.line ?? 0}`,
12988
+ qualifiedItemPath: relationQualifiedItemPath(relation),
12964
12989
  kind: "code-relation",
12965
12990
  signature: relation,
12966
12991
  payload: relation,
@@ -12976,7 +13001,7 @@ function extractionResultToEvidenceAdapterResult(extraction, invocation) {
12976
13001
  role,
12977
13002
  coverage_tier: coverage.tier,
12978
13003
  disposition: coverageFile.disposition,
12979
- facts
13004
+ facts: uniqueFacts(facts)
12980
13005
  };
12981
13006
  });
12982
13007
  const diagnostics = [
@@ -13017,6 +13042,184 @@ function extractionResultToEvidenceAdapterResult(extraction, invocation) {
13017
13042
  }]
13018
13043
  });
13019
13044
  }
13045
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
13046
+ var CharacterCodes;
13047
+ (function(CharacterCodes2) {
13048
+ CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
13049
+ CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
13050
+ CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
13051
+ CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
13052
+ CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
13053
+ CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
13054
+ CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
13055
+ CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
13056
+ CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
13057
+ CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
13058
+ CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
13059
+ CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
13060
+ CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
13061
+ CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
13062
+ CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
13063
+ CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
13064
+ CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
13065
+ CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
13066
+ CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
13067
+ CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
13068
+ CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
13069
+ CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
13070
+ CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
13071
+ CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
13072
+ CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
13073
+ CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
13074
+ CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
13075
+ CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
13076
+ CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
13077
+ CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
13078
+ CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
13079
+ CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
13080
+ CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
13081
+ CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
13082
+ CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
13083
+ CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
13084
+ CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
13085
+ CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
13086
+ CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
13087
+ CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
13088
+ CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
13089
+ CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
13090
+ CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
13091
+ CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
13092
+ CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
13093
+ CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
13094
+ CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
13095
+ CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
13096
+ CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
13097
+ CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
13098
+ CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
13099
+ CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
13100
+ CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
13101
+ CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
13102
+ CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
13103
+ CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
13104
+ CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
13105
+ CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
13106
+ CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
13107
+ CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
13108
+ CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
13109
+ CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
13110
+ CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
13111
+ CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
13112
+ CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
13113
+ CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
13114
+ CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
13115
+ CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
13116
+ CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
13117
+ CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
13118
+ CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
13119
+ CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
13120
+ CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
13121
+ CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
13122
+ CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
13123
+ CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
13124
+ CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
13125
+ CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
13126
+ CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
13127
+ CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
13128
+ })(CharacterCodes || (CharacterCodes = {}));
13129
+
13130
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/string-intern.js
13131
+ var cachedSpaces = new Array(20).fill(0).map((_, index) => {
13132
+ return " ".repeat(index);
13133
+ });
13134
+ var maxCachedValues = 200;
13135
+ var cachedBreakLinesWithSpaces = {
13136
+ " ": {
13137
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
13138
+ return `
13139
+ ` + " ".repeat(index);
13140
+ }),
13141
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
13142
+ return "\r" + " ".repeat(index);
13143
+ }),
13144
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
13145
+ return `\r
13146
+ ` + " ".repeat(index);
13147
+ })
13148
+ },
13149
+ "\t": {
13150
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
13151
+ return `
13152
+ ` + "\t".repeat(index);
13153
+ }),
13154
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
13155
+ return "\r" + "\t".repeat(index);
13156
+ }),
13157
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
13158
+ return `\r
13159
+ ` + "\t".repeat(index);
13160
+ })
13161
+ }
13162
+ };
13163
+
13164
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/parser.js
13165
+ var ParseOptions;
13166
+ (function(ParseOptions2) {
13167
+ ParseOptions2.DEFAULT = {
13168
+ allowTrailingComma: false
13169
+ };
13170
+ })(ParseOptions || (ParseOptions = {}));
13171
+
13172
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
13173
+ var ScanError;
13174
+ (function(ScanError2) {
13175
+ ScanError2[ScanError2["None"] = 0] = "None";
13176
+ ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
13177
+ ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
13178
+ ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
13179
+ ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
13180
+ ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
13181
+ ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
13182
+ })(ScanError || (ScanError = {}));
13183
+ var SyntaxKind;
13184
+ (function(SyntaxKind2) {
13185
+ SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
13186
+ SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
13187
+ SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
13188
+ SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
13189
+ SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
13190
+ SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
13191
+ SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
13192
+ SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
13193
+ SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
13194
+ SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
13195
+ SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
13196
+ SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
13197
+ SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
13198
+ SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
13199
+ SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
13200
+ SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
13201
+ SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
13202
+ })(SyntaxKind || (SyntaxKind = {}));
13203
+ var ParseErrorCode;
13204
+ (function(ParseErrorCode2) {
13205
+ ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
13206
+ ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
13207
+ ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
13208
+ ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
13209
+ ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
13210
+ ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
13211
+ ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
13212
+ ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
13213
+ ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
13214
+ ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
13215
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
13216
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
13217
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
13218
+ ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
13219
+ ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
13220
+ ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
13221
+ })(ParseErrorCode || (ParseErrorCode = {}));
13222
+
13020
13223
  // ../../node_modules/.bun/eslint-visitor-keys@5.0.1/node_modules/eslint-visitor-keys/lib/visitor-keys.js
13021
13224
  var KEYS = {
13022
13225
  ArrayExpression: ["elements"],
@@ -13505,6 +13708,24 @@ var scriptKind = (filePath) => {
13505
13708
  return ts.ScriptKind.TS;
13506
13709
  };
13507
13710
  var createEcmaScriptSourceFile = (source, filePath) => ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, scriptKind(filePath));
13711
+ var treeSitterCompatibleJsxSource = (sourceFile, source) => {
13712
+ const characters = source.split("");
13713
+ let changed = false;
13714
+ const visit2 = (node2) => {
13715
+ const jsxPresentationLiteral = ts.isJsxText(node2) || ts.isStringLiteral(node2) && ts.isJsxAttribute(node2.parent);
13716
+ if (jsxPresentationLiteral) {
13717
+ for (let index = node2.getStart(sourceFile);index < node2.getEnd(); index += 1) {
13718
+ if (characters[index] === "&") {
13719
+ characters[index] = " ";
13720
+ changed = true;
13721
+ }
13722
+ }
13723
+ }
13724
+ ts.forEachChild(node2, visit2);
13725
+ };
13726
+ visit2(sourceFile);
13727
+ return changed ? characters.join("") : source;
13728
+ };
13508
13729
  var syntaxDiagnostics = (sourceFile) => {
13509
13730
  const diagnostics = sourceFile.parseDiagnostics ?? [];
13510
13731
  return diagnostics.map((diagnostic) => {
@@ -13688,7 +13909,7 @@ var collectBindings = (sourceFile) => {
13688
13909
  };
13689
13910
  var collectDynamicDiagnostics = (sourceFile) => {
13690
13911
  const diagnostics = [];
13691
- const visit = (node2) => {
13912
+ const visit2 = (node2) => {
13692
13913
  if (ts2.isCallExpression(node2) && ts2.isIdentifier(node2.expression) && node2.expression.text === "require") {
13693
13914
  if (staticStringValue(node2.arguments[0]) === null) {
13694
13915
  const location = nodeLocation(sourceFile, node2);
@@ -13726,9 +13947,9 @@ var collectDynamicDiagnostics = (sourceFile) => {
13726
13947
  }
13727
13948
  }
13728
13949
  }
13729
- ts2.forEachChild(node2, visit);
13950
+ ts2.forEachChild(node2, visit2);
13730
13951
  };
13731
- visit(sourceFile);
13952
+ visit2(sourceFile);
13732
13953
  return diagnostics;
13733
13954
  };
13734
13955
  var analyzeCommonJsModule = (source, filePath) => {
@@ -13965,7 +14186,9 @@ var traceFile = async (filePath, fs, state) => {
13965
14186
  const commonJs = analyzeCommonJsModule(source, filePath);
13966
14187
  if (commonJs.diagnostics.length > 0)
13967
14188
  return [];
13968
- const tree = await parseFile(source, isJsxLikePath(filePath));
14189
+ const jsxLike = isJsxLikePath(filePath);
14190
+ const parserSource = jsxLike ? treeSitterCompatibleJsxSource(createEcmaScriptSourceFile(source, filePath), source) : source;
14191
+ const tree = await parseFile(parserSource, jsxLike);
13969
14192
  if (!tree)
13970
14193
  return [];
13971
14194
  const root = tree.rootNode;
@@ -14410,7 +14633,7 @@ var declarationName = (node2, sourceFile) => {
14410
14633
  var collectStaticCallRelations = (source, filePath, importBindings) => {
14411
14634
  const sourceFile = createEcmaScriptSourceFile(source, filePath);
14412
14635
  const relations = [];
14413
- const visit = (node2, owner) => {
14636
+ const visit2 = (node2, owner) => {
14414
14637
  const namedOwner = declarationName(node2, sourceFile) ?? owner;
14415
14638
  if (ts3.isCallExpression(node2) || ts3.isNewExpression(node2)) {
14416
14639
  const target = callTarget(node2.expression, sourceFile);
@@ -14420,9 +14643,9 @@ var collectStaticCallRelations = (source, filePath, importBindings) => {
14420
14643
  relations.push(createRelation("calls" /* Calls */, namedOwner, target, isExternal, nodeLocation(sourceFile, node2).line));
14421
14644
  }
14422
14645
  }
14423
- ts3.forEachChild(node2, (child) => visit(child, namedOwner));
14646
+ ts3.forEachChild(node2, (child) => visit2(child, namedOwner));
14424
14647
  };
14425
- visit(sourceFile, filePath);
14648
+ visit2(sourceFile, filePath);
14426
14649
  const seen = new Set;
14427
14650
  return relations.sort((left, right) => (left.line ?? 0) - (right.line ?? 0) || left.from.localeCompare(right.from) || left.to.localeCompare(right.to)).filter((relation) => {
14428
14651
  const key = `${relation.from}\x00${relation.to}\x00${relation.line ?? 0}`;
@@ -14724,7 +14947,9 @@ var analyzeFile = async (filePath, fs, resolver = { mappings: [] }) => {
14724
14947
  const source = await fs.readFile(filePath);
14725
14948
  const sourceFile = createEcmaScriptSourceFile(source, filePath);
14726
14949
  const commonJs = analyzeCommonJsModule(source, filePath);
14727
- const tree = await parseFile(source, isJsxLikePath(filePath));
14950
+ const jsxLike = isJsxLikePath(filePath);
14951
+ const parserSource = jsxLike ? treeSitterCompatibleJsxSource(sourceFile, source) : source;
14952
+ const tree = await parseFile(parserSource, jsxLike);
14728
14953
  if (!tree) {
14729
14954
  const diagnostics = syntaxDiagnostics(sourceFile);
14730
14955
  if (diagnostics.length === 0) {
@@ -14789,6 +15014,26 @@ var analyzeFile = async (filePath, fs, resolver = { mappings: [] }) => {
14789
15014
  };
14790
15015
 
14791
15016
  // src/symbolExtractor.ts
15017
+ var relationIdentity = (relation) => JSON.stringify([
15018
+ relation.type,
15019
+ relation.from,
15020
+ relation.to,
15021
+ relation.isExternal,
15022
+ relation.grounding,
15023
+ relation.confidence,
15024
+ relation.source,
15025
+ relation.line ?? null
15026
+ ]);
15027
+ var uniqueRelations = (relations) => {
15028
+ const seen = new Set;
15029
+ return relations.filter((relation) => {
15030
+ const identity = relationIdentity(relation);
15031
+ if (seen.has(identity))
15032
+ return false;
15033
+ seen.add(identity);
15034
+ return true;
15035
+ });
15036
+ };
14792
15037
  var extractSymbols = async (entries, fs, options) => {
14793
15038
  const resolver = await loadTsConfigPathResolver(fs);
14794
15039
  const exportedSymbols = [];
@@ -14797,15 +15042,19 @@ var extractSymbols = async (entries, fs, options) => {
14797
15042
  const filePaths = new Set;
14798
15043
  const exportedDeclarationKeys = new Set;
14799
15044
  const analyses = new Map;
15045
+ const analysisScope = options.analysisPaths === undefined ? null : new Set(options.analysisPaths);
15046
+ const isInScope = (path2) => analysisScope === null || analysisScope.has(path2);
14800
15047
  for (const entry of entries) {
14801
15048
  const traced = await traceExports(entry.path, fs, resolver);
14802
- traced.files.forEach((filePath) => filePaths.add(filePath));
14803
- for (const filePath of traced.files) {
15049
+ traced.files.filter(isInScope).forEach((filePath) => filePaths.add(filePath));
15050
+ for (const filePath of traced.files.filter(isInScope)) {
14804
15051
  if (!analyses.has(filePath)) {
14805
15052
  analyses.set(filePath, await analyzeFile(filePath, fs, resolver));
14806
15053
  }
14807
15054
  }
14808
15055
  for (const tracedExport of traced.exports) {
15056
+ if (!isInScope(tracedExport.declarationFile))
15057
+ continue;
14809
15058
  const analysis = analyses.get(tracedExport.declarationFile);
14810
15059
  const declaration = analysis?.declarations.get(tracedExport.localName);
14811
15060
  if (!declaration)
@@ -14823,6 +15072,12 @@ var extractSymbols = async (entries, fs, options) => {
14823
15072
  });
14824
15073
  }
14825
15074
  }
15075
+ for (const filePath of [...analysisScope ?? []].sort()) {
15076
+ filePaths.add(filePath);
15077
+ if (!analyses.has(filePath)) {
15078
+ analyses.set(filePath, await analyzeFile(filePath, fs, resolver));
15079
+ }
15080
+ }
14826
15081
  for (const [filePath, analysis] of analyses) {
14827
15082
  relations.push(...analysis.relations);
14828
15083
  for (const [name, declaration] of analysis.declarations) {
@@ -14863,6 +15118,7 @@ var extractSymbols = async (entries, fs, options) => {
14863
15118
  relations.push(createRelation("of_type" /* OfType */, sym.name, propsTypeName, false, sym.line));
14864
15119
  }
14865
15120
  }
15121
+ const deduplicatedRelations = uniqueRelations(relations);
14866
15122
  const files = [...filePaths].sort().map((filePath) => ({
14867
15123
  path: filePath,
14868
15124
  language: ecmaScriptLanguage(filePath),
@@ -14890,7 +15146,7 @@ var extractSymbols = async (entries, fs, options) => {
14890
15146
  package: options.packageInfo,
14891
15147
  files,
14892
15148
  symbols,
14893
- relations,
15149
+ relations: deduplicatedRelations,
14894
15150
  coverage: {
14895
15151
  tier: EXTRACT_TS_COVERAGE_TIER,
14896
15152
  capabilities: [...EXTRACT_TS_CAPABILITIES],
@@ -14902,7 +15158,7 @@ var extractSymbols = async (entries, fs, options) => {
14902
15158
  lines: files.reduce((sum, file) => sum + file.lines, 0),
14903
15159
  exportedSymbols: exportedSymbols.length,
14904
15160
  internalSymbols: internalSymbols.length,
14905
- relations: relations.length
15161
+ relations: deduplicatedRelations.length
14906
15162
  }
14907
15163
  };
14908
15164
  };
@@ -14929,12 +15185,29 @@ class TypeScriptPlugin {
14929
15185
  if (!packageInfo) {
14930
15186
  throw new Error("TypeScriptPlugin.extractSymbols requires detectEntries to run first");
14931
15187
  }
14932
- const result = await extractSymbols(entries, fs, {
14933
- packageInfo,
14934
- pluginId: this.id
14935
- });
14936
- this.#lastDetection = null;
14937
- return result;
15188
+ try {
15189
+ return await extractSymbols(entries, fs, {
15190
+ packageInfo,
15191
+ pluginId: this.id
15192
+ });
15193
+ } finally {
15194
+ this.#lastDetection = null;
15195
+ }
15196
+ }
15197
+ async extractSymbolsInScope(entries, analysisPaths, fs) {
15198
+ const packageInfo = this.#lastDetection?.package;
15199
+ if (!packageInfo) {
15200
+ throw new Error("TypeScriptPlugin.extractSymbolsInScope requires detectEntries to run first");
15201
+ }
15202
+ try {
15203
+ return await extractSymbols(entries, fs, {
15204
+ packageInfo,
15205
+ pluginId: this.id,
15206
+ analysisPaths
15207
+ });
15208
+ } finally {
15209
+ this.#lastDetection = null;
15210
+ }
14938
15211
  }
14939
15212
  }
14940
15213
  // src/reactRouter.ts
@@ -14957,16 +15230,16 @@ function scalarValue(node2) {
14957
15230
  }
14958
15231
  function findDynamicImport(node2) {
14959
15232
  let result;
14960
- const visit = (child) => {
15233
+ const visit2 = (child) => {
14961
15234
  if (result)
14962
15235
  return;
14963
15236
  if (ts4.isCallExpression(child) && child.expression.kind === ts4.SyntaxKind.ImportKeyword && child.arguments[0] && ts4.isStringLiteralLike(child.arguments[0])) {
14964
15237
  result = child.arguments[0].text;
14965
15238
  return;
14966
15239
  }
14967
- ts4.forEachChild(child, visit);
15240
+ ts4.forEachChild(child, visit2);
14968
15241
  };
14969
- visit(node2);
15242
+ visit2(node2);
14970
15243
  return result;
14971
15244
  }
14972
15245
  function parseSource(source, filePath) {
@@ -15054,7 +15327,7 @@ function jsxScalar(attribute, constants2) {
15054
15327
  }
15055
15328
  function descendantTags(node2) {
15056
15329
  const tags = new Set;
15057
- const visit = (child) => {
15330
+ const visit2 = (child) => {
15058
15331
  if (ts4.isJsxElement(child) || ts4.isJsxSelfClosingElement(child)) {
15059
15332
  const opening = ts4.isJsxElement(child) ? child.openingElement : child;
15060
15333
  const tag = opening.tagName.getText();
@@ -15063,14 +15336,14 @@ function descendantTags(node2) {
15063
15336
  if (!["Route", "Routes", "Suspense", "Fragment", "React.Fragment", "Navigate"].includes(tag))
15064
15337
  tags.add(tag);
15065
15338
  }
15066
- ts4.forEachChild(child, visit);
15339
+ ts4.forEachChild(child, visit2);
15067
15340
  };
15068
- visit(node2);
15341
+ visit2(node2);
15069
15342
  return [...tags];
15070
15343
  }
15071
15344
  function navigateTarget(node2, sourceFile) {
15072
15345
  let target;
15073
- const visit = (child) => {
15346
+ const visit2 = (child) => {
15074
15347
  if (target)
15075
15348
  return;
15076
15349
  if (ts4.isJsxElement(child) || ts4.isJsxSelfClosingElement(child)) {
@@ -15089,9 +15362,9 @@ function navigateTarget(node2, sourceFile) {
15089
15362
  }
15090
15363
  }
15091
15364
  }
15092
- ts4.forEachChild(child, visit);
15365
+ ts4.forEachChild(child, visit2);
15093
15366
  };
15094
- visit(node2);
15367
+ visit2(node2);
15095
15368
  return target;
15096
15369
  }
15097
15370
  function leadingNote(node2, sourceFile) {
@@ -15173,7 +15446,7 @@ function extractReactRouterRoutes(source, filePath, options = {}) {
15173
15446
  });
15174
15447
  }
15175
15448
  };
15176
- const visit = (node2, parentPath) => {
15449
+ const visit2 = (node2, parentPath) => {
15177
15450
  if (ts4.isJsxElement(node2) || ts4.isJsxSelfClosingElement(node2)) {
15178
15451
  const opening = ts4.isJsxElement(node2) ? node2.openingElement : node2;
15179
15452
  if (opening.tagName.getText() === "Route") {
@@ -15190,7 +15463,7 @@ function extractReactRouterRoutes(source, filePath, options = {}) {
15190
15463
  pushRoute({ node: node2, parentPath, path: typeof pathValue === "string" ? pathValue : "", index, ...component ? { component } : {}, candidates, ...redirectTo ? { redirectTo } : {} });
15191
15464
  if (ts4.isJsxElement(node2))
15192
15465
  for (const child of node2.children)
15193
- visit(child, fullPath);
15466
+ visit2(child, fullPath);
15194
15467
  return;
15195
15468
  }
15196
15469
  }
@@ -15200,9 +15473,9 @@ function extractReactRouterRoutes(source, filePath, options = {}) {
15200
15473
  visitRouteObjects(node2.arguments[0], mountPath);
15201
15474
  }
15202
15475
  }
15203
- ts4.forEachChild(node2, (child) => visit(child, parentPath));
15476
+ ts4.forEachChild(node2, (child) => visit2(child, parentPath));
15204
15477
  };
15205
- visit(parsed.sourceFile, mountPath);
15478
+ visit2(parsed.sourceFile, mountPath);
15206
15479
  return routes.sort((left, right) => left.fullPath.localeCompare(right.fullPath) || left.location.startLine - right.location.startLine || left.location.startColumn - right.location.startColumn);
15207
15480
  }
15208
15481
  // src/moduleExports.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/extract-ts",
3
- "version": "0.7.1",
3
+ "version": "0.7.4",
4
4
  "type": "module",
5
5
  "description": "TypeScript and JavaScript extraction plugin for the Context ExtractionResult v2 contract",
6
6
  "license": "MIT",