@c4a/extract-ts 0.6.0-beta.4 → 0.6.0-beta.5

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 (3) hide show
  1. package/README.md +5 -0
  2. package/index.js +222 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,6 +27,11 @@ packages.
27
27
  - package kind classification: `lib`, `cli`, or `service`
28
28
  - package version propagation into `ExtractionResult.package.version`
29
29
 
30
+ The Context project may override auto-detection with source-relative
31
+ `extractTs.entries`, or use `mode: "scan"` to make every `include`-matched file
32
+ an extraction root. These settings live in the Context workspace; consumers do
33
+ not need to modify the analyzed package solely to declare extraction entries.
34
+
30
35
  Entry files are returned as module-relative paths. The repository runner later prefixes them to repo-relative paths in raw snapshots.
31
36
 
32
37
  ### Symbol Extraction
package/index.js CHANGED
@@ -13496,7 +13496,188 @@ var UPLOAD_ALLOWED_EXTENSIONS = collectExtensions(() => true);
13496
13496
  var TEXT_EXTENSIONS = collectExtensions((definition) => definition.cas.encoding === "utf8");
13497
13497
  var UPLOAD_MAX_FILE_SIZE = 5 * 1024 * 1024;
13498
13498
  // src/pathUtils.ts
13499
+ import { posix as posix2 } from "node:path";
13500
+
13501
+ // src/tsconfigPaths.ts
13499
13502
  import { posix } from "node:path";
13503
+ function stripJsonComments(input) {
13504
+ let output = "";
13505
+ let inString = false;
13506
+ let escaped = false;
13507
+ let lineComment = false;
13508
+ let blockComment = false;
13509
+ for (let index = 0;index < input.length; index++) {
13510
+ const char = input[index] ?? "";
13511
+ const next = input[index + 1] ?? "";
13512
+ if (lineComment) {
13513
+ if (char === `
13514
+ `) {
13515
+ lineComment = false;
13516
+ output += char;
13517
+ } else {
13518
+ output += " ";
13519
+ }
13520
+ continue;
13521
+ }
13522
+ if (blockComment) {
13523
+ if (char === "*" && next === "/") {
13524
+ blockComment = false;
13525
+ output += " ";
13526
+ index++;
13527
+ } else {
13528
+ output += char === `
13529
+ ` ? `
13530
+ ` : " ";
13531
+ }
13532
+ continue;
13533
+ }
13534
+ if (inString) {
13535
+ output += char;
13536
+ if (escaped)
13537
+ escaped = false;
13538
+ else if (char === "\\")
13539
+ escaped = true;
13540
+ else if (char === '"')
13541
+ inString = false;
13542
+ continue;
13543
+ }
13544
+ if (char === '"') {
13545
+ inString = true;
13546
+ output += char;
13547
+ continue;
13548
+ }
13549
+ if (char === "/" && next === "/") {
13550
+ lineComment = true;
13551
+ output += " ";
13552
+ index++;
13553
+ continue;
13554
+ }
13555
+ if (char === "/" && next === "*") {
13556
+ blockComment = true;
13557
+ output += " ";
13558
+ index++;
13559
+ continue;
13560
+ }
13561
+ output += char;
13562
+ }
13563
+ return output;
13564
+ }
13565
+ function stripTrailingCommas(input) {
13566
+ let output = "";
13567
+ let inString = false;
13568
+ let escaped = false;
13569
+ for (let index = 0;index < input.length; index++) {
13570
+ const char = input[index] ?? "";
13571
+ if (inString) {
13572
+ output += char;
13573
+ if (escaped)
13574
+ escaped = false;
13575
+ else if (char === "\\")
13576
+ escaped = true;
13577
+ else if (char === '"')
13578
+ inString = false;
13579
+ continue;
13580
+ }
13581
+ if (char === '"') {
13582
+ inString = true;
13583
+ output += char;
13584
+ continue;
13585
+ }
13586
+ if (char === ",") {
13587
+ let cursor = index + 1;
13588
+ while (/\s/u.test(input[cursor] ?? ""))
13589
+ cursor++;
13590
+ if (input[cursor] === "}" || input[cursor] === "]")
13591
+ continue;
13592
+ }
13593
+ output += char;
13594
+ }
13595
+ return output;
13596
+ }
13597
+ function parseJsonConfig(raw) {
13598
+ try {
13599
+ const parsed = JSON.parse(stripTrailingCommas(stripJsonComments(raw)));
13600
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
13601
+ } catch {
13602
+ return;
13603
+ }
13604
+ }
13605
+ function normalizedPath(value) {
13606
+ return posix.normalize(value.replace(/\\/gu, "/")).replace(/^\.\//u, "");
13607
+ }
13608
+ function pathMappings(input, baseUrl) {
13609
+ if (input === null || typeof input !== "object" || Array.isArray(input))
13610
+ return [];
13611
+ const mappings = [];
13612
+ for (const [pattern, rawTargets] of Object.entries(input)) {
13613
+ if (!Array.isArray(rawTargets))
13614
+ continue;
13615
+ const wildcard = pattern.indexOf("*");
13616
+ const targets = rawTargets.filter((target) => typeof target === "string" && target.trim().length > 0).map((target) => normalizedPath(posix.join(baseUrl, target)));
13617
+ if (targets.length === 0)
13618
+ continue;
13619
+ mappings.push({
13620
+ pattern,
13621
+ prefix: wildcard < 0 ? pattern : pattern.slice(0, wildcard),
13622
+ suffix: wildcard < 0 ? "" : pattern.slice(wildcard + 1),
13623
+ targets
13624
+ });
13625
+ }
13626
+ return mappings.sort((left, right) => {
13627
+ const exactDifference = Number(!right.pattern.includes("*")) - Number(!left.pattern.includes("*"));
13628
+ return exactDifference || right.prefix.length - left.prefix.length;
13629
+ });
13630
+ }
13631
+ async function loadTsConfigPathResolver(fs2) {
13632
+ const configPath = await fs2.exists("tsconfig.json") ? "tsconfig.json" : await fs2.exists("jsconfig.json") ? "jsconfig.json" : undefined;
13633
+ return configPath === undefined ? { mappings: [] } : loadResolverFromConfig(fs2, configPath, new Set);
13634
+ }
13635
+ async function resolveExtendsPath(fs2, configPath, value) {
13636
+ const configDir = posix.dirname(configPath);
13637
+ const base = value.startsWith(".") ? normalizedPath(posix.join(configDir, value)) : normalizedPath(posix.join("node_modules", value));
13638
+ const candidates = [base, `${base}.json`, posix.join(base, "tsconfig.json")];
13639
+ for (const candidate of candidates) {
13640
+ if (await fs2.exists(candidate))
13641
+ return candidate;
13642
+ }
13643
+ return;
13644
+ }
13645
+ async function loadResolverFromConfig(fs2, configPath, seen) {
13646
+ if (seen.has(configPath))
13647
+ return { mappings: [] };
13648
+ seen.add(configPath);
13649
+ const config = parseJsonConfig(await fs2.readFile(configPath));
13650
+ const extended = typeof config?.extends === "string" ? await resolveExtendsPath(fs2, configPath, config.extends) : undefined;
13651
+ const parent = extended === undefined ? { mappings: [] } : await loadResolverFromConfig(fs2, extended, seen);
13652
+ const compilerOptions = config?.compilerOptions;
13653
+ if (compilerOptions === null || typeof compilerOptions !== "object" || Array.isArray(compilerOptions)) {
13654
+ return parent;
13655
+ }
13656
+ const options = compilerOptions;
13657
+ const configDir = posix.dirname(configPath);
13658
+ const baseUrl = typeof options.baseUrl === "string" && options.baseUrl.trim().length > 0 ? normalizedPath(posix.join(configDir, options.baseUrl)) : parent.baseUrl;
13659
+ const mappingBase = baseUrl ?? normalizedPath(configDir);
13660
+ return {
13661
+ ...baseUrl !== undefined ? { baseUrl } : {},
13662
+ mappings: options.paths === undefined ? parent.mappings : pathMappings(options.paths, mappingBase)
13663
+ };
13664
+ }
13665
+ function resolveTsConfigCandidates(specifier, resolver) {
13666
+ for (const mapping of resolver.mappings) {
13667
+ if (!mapping.pattern.includes("*")) {
13668
+ if (specifier === mapping.pattern)
13669
+ return mapping.targets;
13670
+ continue;
13671
+ }
13672
+ if (!specifier.startsWith(mapping.prefix) || !specifier.endsWith(mapping.suffix))
13673
+ continue;
13674
+ const wildcard = specifier.slice(mapping.prefix.length, specifier.length - mapping.suffix.length);
13675
+ return mapping.targets.map((target) => target.replace("*", wildcard));
13676
+ }
13677
+ return resolver.baseUrl === undefined ? [] : [normalizedPath(posix.join(resolver.baseUrl, specifier))];
13678
+ }
13679
+
13680
+ // src/pathUtils.ts
13500
13681
  var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
13501
13682
  var BUILD_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs"];
13502
13683
  var DECLARATION_EXTENSIONS = [".d.ts", ".d.mts", ".d.cts"];
@@ -13532,12 +13713,12 @@ var BUILD_FORMAT_DIRS = new Set([
13532
13713
  ]);
13533
13714
  var unique = (items) => [...new Set(items)];
13534
13715
  var normalizeRelativePath = (value) => {
13535
- const normalized = posix.normalize(value.replace(/\\/g, "/"));
13716
+ const normalized = posix2.normalize(value.replace(/\\/g, "/"));
13536
13717
  return normalized.replace(/^\.\//, "");
13537
13718
  };
13538
13719
  var stripResourceQuery = (value) => value.split(/[?#]/u)[0] ?? value;
13539
13720
  var hasUnsupportedExplicitExtension = (value) => {
13540
- const basename = posix.basename(value);
13721
+ const basename = posix2.basename(value);
13541
13722
  if (!basename.includes("."))
13542
13723
  return false;
13543
13724
  return !KNOWN_CODE_SUFFIXES.some((extension) => value.endsWith(extension));
@@ -13582,7 +13763,7 @@ var createCandidatePaths = (value, options = {}) => {
13582
13763
  }
13583
13764
  for (const extension of SOURCE_EXTENSIONS) {
13584
13765
  candidates.add(`${base}${extension}`);
13585
- candidates.add(posix.join(base, `index${extension}`));
13766
+ candidates.add(posix2.join(base, `index${extension}`));
13586
13767
  }
13587
13768
  if (BUILD_EXTENSIONS.some((extension) => normalized.endsWith(extension))) {
13588
13769
  for (const extension of SOURCE_EXTENSIONS) {
@@ -13600,19 +13781,28 @@ var createCandidatePaths = (value, options = {}) => {
13600
13781
  var isRelativeModuleSpecifier = (value) => value.startsWith("./") || value.startsWith("../");
13601
13782
  var resolveEntrySourcePath = async (packageDir, targetPath, fs2, options = {}) => {
13602
13783
  for (const candidate of createCandidatePaths(targetPath, options)) {
13603
- const fullPath = packageDir ? posix.join(packageDir, candidate) : candidate;
13784
+ const fullPath = packageDir ? posix2.join(packageDir, candidate) : candidate;
13604
13785
  if (await fs2.exists(fullPath)) {
13605
13786
  return fullPath;
13606
13787
  }
13607
13788
  }
13608
13789
  return null;
13609
13790
  };
13610
- var resolveImportSourcePath = async (fromFile, specifier, fs2) => {
13611
- if (!isRelativeModuleSpecifier(specifier))
13791
+ var resolveImportSourcePath = async (fromFile, specifier, fs2, resolver) => {
13792
+ if (!isRelativeModuleSpecifier(specifier)) {
13793
+ if (resolver === undefined)
13794
+ return null;
13795
+ for (const target of resolveTsConfigCandidates(specifier, resolver)) {
13796
+ for (const candidate of createCandidatePaths(target, { allowIndexFallback: false })) {
13797
+ if (await fs2.exists(candidate))
13798
+ return normalizeRelativePath(candidate);
13799
+ }
13800
+ }
13612
13801
  return null;
13613
- const baseDir = posix.dirname(fromFile);
13802
+ }
13803
+ const baseDir = posix2.dirname(fromFile);
13614
13804
  for (const candidate of createCandidatePaths(specifier)) {
13615
- const fullPath = baseDir === "." ? candidate : posix.join(baseDir, candidate);
13805
+ const fullPath = baseDir === "." ? candidate : posix2.join(baseDir, candidate);
13616
13806
  if (await fs2.exists(fullPath)) {
13617
13807
  return normalizeRelativePath(fullPath);
13618
13808
  }
@@ -13987,6 +14177,11 @@ var pluginSpecSchema = exports_external.object({
13987
14177
  package: exports_external.string().min(1),
13988
14178
  exportName: exports_external.string().min(1).optional()
13989
14179
  });
14180
+ var entrySelectionSchema = exports_external.discriminatedUnion("mode", [
14181
+ exports_external.object({ mode: exports_external.literal("auto") }),
14182
+ exports_external.object({ mode: exports_external.literal("configured"), entries: exports_external.array(exports_external.string().min(1)) }),
14183
+ exports_external.object({ mode: exports_external.literal("scan") })
14184
+ ]);
13990
14185
  var codeExtractRunnerInputSchema = exports_external.object({
13991
14186
  repoPath: exports_external.string().min(1),
13992
14187
  modules: exports_external.array(exports_external.string().min(1)).optional(),
@@ -13994,6 +14189,7 @@ var codeExtractRunnerInputSchema = exports_external.object({
13994
14189
  commitHash: exports_external.string().min(1).nullable().optional(),
13995
14190
  moduleCommits: exports_external.record(exports_external.string().nullable()).optional(),
13996
14191
  pathFilter: PathFilterConfigSchema.optional(),
14192
+ entrySelection: entrySelectionSchema.optional(),
13997
14193
  plugins: exports_external.array(pluginSpecSchema).min(1),
13998
14194
  snapshot: exports_external.object({
13999
14195
  sourceId: exports_external.string().min(1),
@@ -14148,8 +14344,6 @@ var collectImportBindings = (root) => {
14148
14344
  if (!stringNode)
14149
14345
  continue;
14150
14346
  const source2 = stripQuotes(stringNode.text);
14151
- if (!isRelativeModuleSpecifier(source2))
14152
- continue;
14153
14347
  const clause = node2.namedChildren.find((child) => child.type === "import_clause");
14154
14348
  for (const part of clause?.namedChildren ?? []) {
14155
14349
  if (part.type === "identifier") {
@@ -14179,7 +14373,7 @@ var uniqueExports = (exportsList) => {
14179
14373
  });
14180
14374
  };
14181
14375
  var traceImportedBinding = async (filePath, binding, exportedName, fs2, state) => {
14182
- const targetPath = await resolveImportSourcePath(filePath, binding.source, fs2);
14376
+ const targetPath = await resolveImportSourcePath(filePath, binding.source, fs2, state.resolver);
14183
14377
  if (!targetPath)
14184
14378
  return [];
14185
14379
  const traced = await traceFile(targetPath, fs2, state);
@@ -14233,8 +14427,8 @@ var traceFile = async (filePath, fs2, state) => {
14233
14427
  continue;
14234
14428
  }
14235
14429
  if (!exportClause) {
14236
- if (stringNode && node2.text.startsWith("export *") && isRelativeModuleSpecifier(stripQuotes(stringNode.text))) {
14237
- const targetPath2 = await resolveImportSourcePath(filePath, stripQuotes(stringNode.text), fs2);
14430
+ if (stringNode && node2.text.startsWith("export *")) {
14431
+ const targetPath2 = await resolveImportSourcePath(filePath, stripQuotes(stringNode.text), fs2, state.resolver);
14238
14432
  if (targetPath2) {
14239
14433
  exportsList.push(...await traceFile(targetPath2, fs2, state));
14240
14434
  }
@@ -14261,9 +14455,7 @@ var traceFile = async (filePath, fs2, state) => {
14261
14455
  }
14262
14456
  continue;
14263
14457
  }
14264
- if (!isRelativeModuleSpecifier(stripQuotes(stringNode.text)))
14265
- continue;
14266
- const targetPath = await resolveImportSourcePath(filePath, stripQuotes(stringNode.text), fs2);
14458
+ const targetPath = await resolveImportSourcePath(filePath, stripQuotes(stringNode.text), fs2, state.resolver);
14267
14459
  if (!targetPath)
14268
14460
  continue;
14269
14461
  const traced = await traceFile(targetPath, fs2, state);
@@ -14287,11 +14479,12 @@ var traceFile = async (filePath, fs2, state) => {
14287
14479
  state.cache.set(filePath, promise);
14288
14480
  return promise;
14289
14481
  };
14290
- var traceExports = async (entryPath, fs2) => {
14482
+ var traceExports = async (entryPath, fs2, resolver = { mappings: [] }) => {
14291
14483
  const state = {
14292
14484
  files: new Set,
14293
14485
  cache: new Map,
14294
- inFlight: new Set
14486
+ inFlight: new Set,
14487
+ resolver
14295
14488
  };
14296
14489
  const exportsList = await traceFile(entryPath, fs2, state);
14297
14490
  return {
@@ -14783,14 +14976,16 @@ var analyzeEnumDeclaration = (node2, name, symbolDoc, filePath, declarations) =>
14783
14976
  }
14784
14977
  });
14785
14978
  };
14786
- var collectImportBindings2 = (root, relations) => {
14979
+ var collectImportBindings2 = async (root, filePath, fs2, resolver, relations) => {
14787
14980
  const bindings = new Map;
14788
14981
  for (const node2 of root.namedChildren.filter((child) => child.type === "import_statement")) {
14789
14982
  const specifierNode = node2.namedChildren.find((child) => child.type === "string");
14790
14983
  if (!specifierNode)
14791
14984
  continue;
14792
14985
  const specifier = specifierNode.text.replace(/^['"]/, "").replace(/['"]$/, "");
14793
- const isExternal = !specifier.startsWith(".");
14986
+ const resolvedAlias = isRelativeModuleSpecifier(specifier) ? null : await resolveImportSourcePath(filePath, specifier, fs2, resolver);
14987
+ const isExternal = !isRelativeModuleSpecifier(specifier) && resolvedAlias === null;
14988
+ const relationTarget = resolvedAlias ?? specifier;
14794
14989
  const statementTypeOnly = node2.text.startsWith("import type ");
14795
14990
  let hasValueImport = !statementTypeOnly;
14796
14991
  let hasTypeImport = statementTypeOnly;
@@ -14821,15 +15016,15 @@ var collectImportBindings2 = (root, relations) => {
14821
15016
  }
14822
15017
  }
14823
15018
  if (hasValueImport) {
14824
- relations.push(createRelation("imports" /* Imports */, "", specifier, isExternal, getLine(node2)));
15019
+ relations.push(createRelation("imports" /* Imports */, "", relationTarget, isExternal, getLine(node2)));
14825
15020
  }
14826
15021
  if (hasTypeImport) {
14827
- relations.push(createRelation("imports_type" /* ImportsType */, "", specifier, isExternal, getLine(node2)));
15022
+ relations.push(createRelation("imports_type" /* ImportsType */, "", relationTarget, isExternal, getLine(node2)));
14828
15023
  }
14829
15024
  }
14830
15025
  return bindings;
14831
15026
  };
14832
- var analyzeFile = async (filePath, fs2) => {
15027
+ var analyzeFile = async (filePath, fs2, resolver = { mappings: [] }) => {
14833
15028
  const source2 = await fs2.readFile(filePath);
14834
15029
  const tree = await parseFile(source2, filePath.endsWith(".tsx"));
14835
15030
  if (!tree) {
@@ -14842,7 +15037,7 @@ var analyzeFile = async (filePath, fs2) => {
14842
15037
  }
14843
15038
  const root = tree.rootNode;
14844
15039
  const relations = [];
14845
- const importBindings = collectImportBindings2(root, relations);
15040
+ const importBindings = await collectImportBindings2(root, filePath, fs2, resolver, relations);
14846
15041
  const declarations = new Map;
14847
15042
  for (const relation of relations) {
14848
15043
  if (relation.from === "") {
@@ -14871,6 +15066,7 @@ var analyzeFile = async (filePath, fs2) => {
14871
15066
 
14872
15067
  // src/symbolExtractor.ts
14873
15068
  var extractSymbols = async (entries, fs2, options) => {
15069
+ const resolver = await loadTsConfigPathResolver(fs2);
14874
15070
  const exportedSymbols = [];
14875
15071
  const internalSymbols = [];
14876
15072
  const relations = [];
@@ -14878,11 +15074,11 @@ var extractSymbols = async (entries, fs2, options) => {
14878
15074
  const exportedDeclarationKeys = new Set;
14879
15075
  const analyses = new Map;
14880
15076
  for (const entry of entries) {
14881
- const traced = await traceExports(entry.path, fs2);
15077
+ const traced = await traceExports(entry.path, fs2, resolver);
14882
15078
  traced.files.forEach((filePath) => filePaths.add(filePath));
14883
15079
  for (const filePath of traced.files) {
14884
15080
  if (!analyses.has(filePath)) {
14885
- analyses.set(filePath, await analyzeFile(filePath, fs2));
15081
+ analyses.set(filePath, await analyzeFile(filePath, fs2, resolver));
14886
15082
  }
14887
15083
  }
14888
15084
  for (const tracedExport of traced.exports) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/extract-ts",
3
- "version": "0.6.0-beta.4",
3
+ "version": "0.6.0-beta.5",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "web-tree-sitter": "^0.20.8"