@opensip-cli/lang-typescript 0.1.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.
Files changed (60) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/adapter.test.d.ts +2 -0
  5. package/dist/__tests__/adapter.test.d.ts.map +1 -0
  6. package/dist/__tests__/adapter.test.js +56 -0
  7. package/dist/__tests__/adapter.test.js.map +1 -0
  8. package/dist/__tests__/ast-utilities.test.d.ts +2 -0
  9. package/dist/__tests__/ast-utilities.test.d.ts.map +1 -0
  10. package/dist/__tests__/ast-utilities.test.js +442 -0
  11. package/dist/__tests__/ast-utilities.test.js.map +1 -0
  12. package/dist/__tests__/filter.test.d.ts +2 -0
  13. package/dist/__tests__/filter.test.d.ts.map +1 -0
  14. package/dist/__tests__/filter.test.js +183 -0
  15. package/dist/__tests__/filter.test.js.map +1 -0
  16. package/dist/__tests__/query.test.d.ts +2 -0
  17. package/dist/__tests__/query.test.d.ts.map +1 -0
  18. package/dist/__tests__/query.test.js +76 -0
  19. package/dist/__tests__/query.test.js.map +1 -0
  20. package/dist/__tests__/workspace-units.test.d.ts +2 -0
  21. package/dist/__tests__/workspace-units.test.d.ts.map +1 -0
  22. package/dist/__tests__/workspace-units.test.js +94 -0
  23. package/dist/__tests__/workspace-units.test.js.map +1 -0
  24. package/dist/adapter.d.ts +6 -0
  25. package/dist/adapter.d.ts.map +1 -0
  26. package/dist/adapter.js +17 -0
  27. package/dist/adapter.js.map +1 -0
  28. package/dist/ast-utilities.d.ts +76 -0
  29. package/dist/ast-utilities.d.ts.map +1 -0
  30. package/dist/ast-utilities.js +212 -0
  31. package/dist/ast-utilities.js.map +1 -0
  32. package/dist/filter.d.ts +39 -0
  33. package/dist/filter.d.ts.map +1 -0
  34. package/dist/filter.js +263 -0
  35. package/dist/filter.js.map +1 -0
  36. package/dist/function-scope.d.ts +70 -0
  37. package/dist/function-scope.d.ts.map +1 -0
  38. package/dist/function-scope.js +142 -0
  39. package/dist/function-scope.js.map +1 -0
  40. package/dist/index.d.ts +13 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +24 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/parse.d.ts +10 -0
  45. package/dist/parse.d.ts.map +1 -0
  46. package/dist/parse.js +19 -0
  47. package/dist/parse.js.map +1 -0
  48. package/dist/query.d.ts +4 -0
  49. package/dist/query.d.ts.map +1 -0
  50. package/dist/query.js +74 -0
  51. package/dist/query.js.map +1 -0
  52. package/dist/strip.d.ts +25 -0
  53. package/dist/strip.d.ts.map +1 -0
  54. package/dist/strip.js +30 -0
  55. package/dist/strip.js.map +1 -0
  56. package/dist/workspace-units.d.ts +19 -0
  57. package/dist/workspace-units.d.ts.map +1 -0
  58. package/dist/workspace-units.js +78 -0
  59. package/dist/workspace-units.js.map +1 -0
  60. package/package.json +50 -0
package/dist/filter.js ADDED
@@ -0,0 +1,263 @@
1
+ // @fitness-ignore-file file-length-limit -- framework/content-filter complexity requires single-file cohesion
2
+ // @fitness-ignore-file toctou-race-condition -- filterContent cache.get + cache.set on a per-RunScope Map; both operations are synchronous, no async gap, safe in single-threaded Node.js
3
+ /**
4
+ * @fileoverview TypeScript scanner-based content filtering
5
+ *
6
+ * Uses the TypeScript scanner (not full AST parser) to identify string literal
7
+ * and comment regions. String content is replaced with spaces of equal length,
8
+ * preserving line/column positions for accurate violation reporting.
9
+ */
10
+ import { logger, currentScope } from '@opensip-cli/core';
11
+ import { buildLineStarts } from '@opensip-cli/core/languages';
12
+ import ts from 'typescript';
13
+ // =============================================================================
14
+ // HELPERS
15
+ // =============================================================================
16
+ /**
17
+ * Build a set of 1-based line numbers from a list of regions.
18
+ * A line is included if any part of it falls within a region.
19
+ *
20
+ * Reuses `buildLineStarts` from `@opensip-cli/core/languages` so UTF-16
21
+ * surrogate-pair / BOM / CRLF handling stays in one place across language
22
+ * adapters.
23
+ */
24
+ function linesToSet(content, regions) {
25
+ if (regions.length === 0)
26
+ return new Set();
27
+ const lineStarts = buildLineStarts(content);
28
+ const result = new Set();
29
+ for (const region of regions) {
30
+ for (let lineIdx = 0; lineIdx < lineStarts.length; lineIdx++) {
31
+ const lineStart = lineStarts[lineIdx];
32
+ const lineEnd = lineIdx + 1 < lineStarts.length ? lineStarts[lineIdx + 1] - 1 : content.length;
33
+ if (lineStart > region.end)
34
+ break;
35
+ if (lineEnd >= region.start) {
36
+ result.add(lineIdx + 1); // 1-based
37
+ }
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Check if a (1-based line, 0-based column) offset falls within any region.
44
+ */
45
+ function isInRegions(content, regions, line, column) {
46
+ if (regions.length === 0)
47
+ return false;
48
+ // Convert line/column to byte offset
49
+ let currentLine = 1;
50
+ let lineStart = 0;
51
+ // eslint-disable-next-line unicorn/no-for-loop -- offset-bearing scan: captures UTF-16 line start
52
+ for (let i = 0; i < content.length; i++) {
53
+ if (currentLine === line) {
54
+ lineStart = i;
55
+ break;
56
+ }
57
+ if (content[i] === '\n')
58
+ currentLine++;
59
+ }
60
+ if (currentLine !== line)
61
+ return false;
62
+ const offset = lineStart + column;
63
+ for (const region of regions) {
64
+ if (offset >= region.start && offset < region.end)
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ /**
70
+ * Replace characters in the given range with spaces, preserving newlines.
71
+ * Records the range as a string region.
72
+ */
73
+ function replaceCharsInRange(chars, start, end, stringRegions) {
74
+ stringRegions.push({ start, end });
75
+ for (let i = start; i < end; i++) {
76
+ if (chars[i] !== '\n')
77
+ chars[i] = ' ';
78
+ }
79
+ }
80
+ // =============================================================================
81
+ // MAIN
82
+ // =============================================================================
83
+ /**
84
+ * Scan content using TypeScript's scanner to identify string and comment regions.
85
+ *
86
+ * String literals are replaced with spaces of equal length, preserving
87
+ * line/column positions. Comments are tracked but not removed (directives
88
+ * live in comments and must be preserved).
89
+ */
90
+ // Module-level cache to avoid re-running the TS scanner on the same content.
91
+ // Bounded by an idle timer (10 min, matching parse-cache.ts) so long-lived
92
+ // embedders don't accumulate cached filter results across runs forever. The
93
+ // timer resets each time filterContent runs, so an active session never
94
+ // loses its cache.
95
+ //
96
+ // Filter-content caching now rides on the current `RunScope`'s
97
+ // `parseCache.filteredContent` Map (Phase 6 Task 6.4). The previous
98
+ // design kept a separate module-level `filterCache` Map + 10-min
99
+ // idle timer, which had three failure modes: (1) two tests in the
100
+ // same process couldn't isolate state without a `clearFilterCache()`
101
+ // call; (2) the timer kept the process alive in environments where
102
+ // it wasn't `unref`'d correctly; (3) lifetime drift vs the parse
103
+ // cache meant a `clearParseCache()` call left stale filter entries.
104
+ // Folding into `RunScope` means the test/run lifecycle owns both
105
+ // caches together — one `scope.dispose()` clears them both.
106
+ //
107
+ // Calling `filterContent(content)` outside any `runWithScope` (e.g.
108
+ // a direct unit test of the filter) just bypasses the cache; the
109
+ // filtered output is computed every call. That's a documented
110
+ // fallback, not a hot-path concern, because production paths always
111
+ // run inside a scope established by the CLI's pre-action-hook.
112
+ /** Strips TS comments and string literals; result is cached per-content on the active scope. */
113
+ export function filterContent(content) {
114
+ const scope = currentScope();
115
+ if (scope) {
116
+ const cached = scope.parseCache.filteredContent.get(content);
117
+ if (cached)
118
+ return cached;
119
+ }
120
+ try {
121
+ const result = filterContentImpl(content);
122
+ if (scope)
123
+ scope.parseCache.filteredContent.set(content, result);
124
+ return result;
125
+ /* v8 ignore start -- defensive: TypeScript scanner is robust and recovers from malformed input rather than throwing; this fallback exists for theoretical scanner exceptions */
126
+ }
127
+ catch {
128
+ /*
129
+ * Silent degradation — by design.
130
+ *
131
+ * The TypeScript scanner is best-effort: it CAN throw on resource
132
+ * exhaustion, malformed input the scanner doesn't recognise, or
133
+ * unsupported character classes. When that happens, raising would
134
+ * terminate the entire fitness run because filterContent is called
135
+ * from every TS check that needs string/comment masking.
136
+ *
137
+ * The fallback returns raw content with stub `isInString` /
138
+ * `isInComment` predicates that always return `false`. This is the
139
+ * safest default: callers that pattern-match identifiers will see
140
+ * un-stripped source (so a banned-call reference inside a string or
141
+ * comment may produce a false positive), but they will never
142
+ * see a SILENT TRUNCATION of legitimate code (which is what would
143
+ * happen if the scanner desynced and we returned partially-stripped
144
+ * output).
145
+ *
146
+ * The audit (2026-05-23 F-M1) flagged this as a P3 — the trade-off
147
+ * is intentional but the only signal today is `logger.debug`. A
148
+ * future revision SHOULD widen `FilteredContent` with a
149
+ * `degraded: boolean` flag so callers can branch on it; until then,
150
+ * the debug log line below is the only operator-visible signal.
151
+ */
152
+ logger.debug('Content filter fell back to raw content', {
153
+ evt: 'fitness.content_filter.fallback',
154
+ module: 'fitness:framework',
155
+ });
156
+ const fallback = {
157
+ code: content,
158
+ codeNoComments: content,
159
+ raw: content,
160
+ commentLines: new Set(),
161
+ isInString: () => false,
162
+ isInComment: () => false,
163
+ };
164
+ if (scope)
165
+ scope.parseCache.filteredContent.set(content, fallback);
166
+ return fallback;
167
+ }
168
+ /* v8 ignore stop */
169
+ }
170
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- TS scanner driver: token-by-token loop with per-kind handling; flatter shape would scatter token classification
171
+ function filterContentImpl(content) {
172
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.Standard, content);
173
+ const stringRegions = [];
174
+ const commentRegions = [];
175
+ // Use UTF-16 code-unit array (split('')) so that scanner.getTokenStart()/getTokenEnd()
176
+ // (which are UTF-16 code unit offsets) align with indices, even for astral (non-BMP)
177
+ // characters. Using spread [...] produces a code-point array and desynchronizes
178
+ // after the first emoji/etc, corrupting all subsequent positions, signals, SARIF,
179
+ // baselines, suppression matching, etc.
180
+ // eslint-disable-next-line unicorn/prefer-spread -- correctness: must be UTF-16 code units to match TS scanner offsets; spread would be code points and corrupt token ranges for astral chars.
181
+ const chars = content.split('');
182
+ // Depth counter, not a boolean — a `${ `inner` }` construct nests two templates
183
+ // and each `}` that closes a template-expression must be rescanned. A plain
184
+ // boolean flipped off by the inner TemplateTail would leave the outer unrescanned
185
+ // and desync the scanner for the rest of the file (which silently wipes real
186
+ // code to whitespace). Incremented at TemplateHead, decremented at TemplateTail.
187
+ let templateDepth = 0;
188
+ while (true) {
189
+ let token = scanner.scan();
190
+ // @fitness-ignore-next-line unsafe-secret-comparison -- comparing TypeScript SyntaxKind enum, not a secret
191
+ if (token === ts.SyntaxKind.EndOfFileToken)
192
+ break;
193
+ // After a CloseBraceToken inside ANY template expression, rescan to get TemplateMiddle/TemplateTail
194
+ // @fitness-ignore-next-line unsafe-secret-comparison -- comparing TypeScript SyntaxKind enum, not a secret
195
+ if (token === ts.SyntaxKind.CloseBraceToken && templateDepth > 0) {
196
+ token = scanner.reScanTemplateToken(false);
197
+ }
198
+ const start = scanner.getTokenStart();
199
+ const end = scanner.getTokenEnd();
200
+ switch (token) {
201
+ case ts.SyntaxKind.StringLiteral:
202
+ case ts.SyntaxKind.NoSubstitutionTemplateLiteral: {
203
+ // Replace content inside quotes/backticks (keep delimiters)
204
+ replaceCharsInRange(chars, start + 1, end - 1, stringRegions);
205
+ break;
206
+ }
207
+ case ts.SyntaxKind.TemplateHead: {
208
+ // `text ${ — replace text between ` and ${
209
+ templateDepth++;
210
+ replaceCharsInRange(chars, start + 1, end - 2, stringRegions);
211
+ break;
212
+ }
213
+ case ts.SyntaxKind.TemplateMiddle: {
214
+ // }text ${ — replace text between } and ${
215
+ replaceCharsInRange(chars, start + 1, end - 2, stringRegions);
216
+ break;
217
+ }
218
+ case ts.SyntaxKind.TemplateTail: {
219
+ // }text` — replace text between } and `
220
+ templateDepth--;
221
+ replaceCharsInRange(chars, start + 1, end - 1, stringRegions);
222
+ break;
223
+ }
224
+ case ts.SyntaxKind.SingleLineCommentTrivia:
225
+ case ts.SyntaxKind.MultiLineCommentTrivia: {
226
+ // Track comment regions but don't modify content
227
+ commentRegions.push({ start, end });
228
+ break;
229
+ }
230
+ // RegularExpressionLiteral — leave unchanged, regex is code
231
+ default: {
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ const code = chars.join('');
237
+ const commentLines = linesToSet(content, commentRegions);
238
+ // Compute `codeNoComments` ... (same UTF-16 alignment requirement as above)
239
+ // eslint-disable-next-line unicorn/prefer-spread -- correctness: must be UTF-16 code units to match TS scanner offsets; spread would be code points and corrupt token ranges for astral chars.
240
+ const charsNoComments = content.split('');
241
+ for (const region of stringRegions) {
242
+ for (let i = region.start; i < region.end; i++) {
243
+ if (charsNoComments[i] !== '\n')
244
+ charsNoComments[i] = ' ';
245
+ }
246
+ }
247
+ for (const region of commentRegions) {
248
+ for (let i = region.start; i < region.end; i++) {
249
+ if (charsNoComments[i] !== '\n')
250
+ charsNoComments[i] = ' ';
251
+ }
252
+ }
253
+ const codeNoComments = charsNoComments.join('');
254
+ return {
255
+ code,
256
+ codeNoComments,
257
+ raw: content,
258
+ commentLines,
259
+ isInString: (line, column) => isInRegions(content, stringRegions, line, column),
260
+ isInComment: (line, column) => isInRegions(content, commentRegions, line, column),
261
+ };
262
+ }
263
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAAA,8GAA8G;AAC9G,0LAA0L;AAC1L;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,MAAM,YAAY,CAAC;AAmC5B,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,OAAe,EAAE,OAA0B;IAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAE3C,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;YAC7D,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,OAAO,GACX,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;YACjF,IAAI,SAAS,GAAG,MAAM,CAAC,GAAG;gBAAE,MAAM;YAClC,IAAI,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAClB,OAAe,EACf,OAA0B,EAC1B,IAAY,EACZ,MAAc;IAEd,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvC,qCAAqC;IACrC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,kGAAkG;IAClG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,SAAS,GAAG,CAAC,CAAC;YACd,MAAM;QACR,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,WAAW,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEvC,MAAM,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAClC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAC1B,KAAe,EACf,KAAa,EACb,GAAW,EACX,aAAuB;IAEvB,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACxC,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,OAAO;AACP,gFAAgF;AAEhF;;;;;;GAMG;AACH,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,mBAAmB;AACnB,EAAE;AACF,+DAA+D;AAC/D,oEAAoE;AACpE,iEAAiE;AACjE,kEAAkE;AAClE,qEAAqE;AACrE,mEAAmE;AACnE,iEAAiE;AACjE,oEAAoE;AACpE,iEAAiE;AACjE,4DAA4D;AAC5D,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,8DAA8D;AAC9D,oEAAoE;AACpE,+DAA+D;AAE/D,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAgC,CAAC;QAC5F,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,KAAK;YAAE,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACjE,OAAO,MAAM,CAAC;QACd,gLAAgL;IAClL,CAAC;IAAC,MAAM,CAAC;QACP;;;;;;;;;;;;;;;;;;;;;;;WAuBG;QACH,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE;YACtD,GAAG,EAAE,iCAAiC;YACtC,MAAM,EAAE,mBAAmB;SAC5B,CAAC,CAAC;QACH,MAAM,QAAQ,GAAoB;YAChC,IAAI,EAAE,OAAO;YACb,cAAc,EAAE,OAAO;YACvB,GAAG,EAAE,OAAO;YACZ,YAAY,EAAE,IAAI,GAAG,EAAE;YACvB,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK;YACvB,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;SACzB,CAAC;QACF,IAAI,KAAK;YAAE,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,oBAAoB;AACtB,CAAC;AAED,2KAA2K;AAC3K,SAAS,iBAAiB,CAAC,OAAe;IACxC,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAC9B,EAAE,CAAC,YAAY,CAAC,MAAM,EACtB,KAAK,EACL,EAAE,CAAC,eAAe,CAAC,QAAQ,EAC3B,OAAO,CACR,CAAC;IAEF,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,uFAAuF;IACvF,qFAAqF;IACrF,gFAAgF;IAChF,kFAAkF;IAClF,wCAAwC;IACxC,+LAA+L;IAC/L,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAEhC,gFAAgF;IAChF,4EAA4E;IAC5E,kFAAkF;IAClF,6EAA6E;IAC7E,iFAAiF;IACjF,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC3B,2GAA2G;QAC3G,IAAI,KAAK,KAAK,EAAE,CAAC,UAAU,CAAC,cAAc;YAAE,MAAM;QAElD,oGAAoG;QACpG,2GAA2G;QAC3G,IAAI,KAAK,KAAK,EAAE,CAAC,UAAU,CAAC,eAAe,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACjE,KAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAElC,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC;YACjC,KAAK,EAAE,CAAC,UAAU,CAAC,6BAA6B,CAAC,CAAC,CAAC;gBACjD,4DAA4D;gBAC5D,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC9D,MAAM;YACR,CAAC;YAED,KAAK,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;gBAChC,2CAA2C;gBAC3C,aAAa,EAAE,CAAC;gBAChB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC9D,MAAM;YACR,CAAC;YAED,KAAK,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;gBAClC,2CAA2C;gBAC3C,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC9D,MAAM;YACR,CAAC;YAED,KAAK,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;gBAChC,wCAAwC;gBACxC,aAAa,EAAE,CAAC;gBAChB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC9D,MAAM;YACR,CAAC;YAED,KAAK,EAAE,CAAC,UAAU,CAAC,uBAAuB,CAAC;YAC3C,KAAK,EAAE,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBAC1C,iDAAiD;gBACjD,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBACpC,MAAM;YACR,CAAC;YAED,4DAA4D;YAC5D,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAEzD,4EAA4E;IAC5E,+LAA+L;IAC/L,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1C,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,IAAI,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,eAAe,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,IAAI,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,eAAe,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,MAAM,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEhD,OAAO;QACL,IAAI;QACJ,cAAc;QACd,GAAG,EAAE,OAAO;QACZ,YAAY;QACZ,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC;QAC/E,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC;KAClF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview Function-scope AST helpers.
3
+ *
4
+ * Walking helpers that answer "what function am I inside?" / "is this in an
5
+ * async context?" / "is this in a conditional branch?" — used by checks that
6
+ * need to reason about scope boundaries (lifecycle cleanup, async waterfall
7
+ * detection, etc.).
8
+ *
9
+ * Lives in its own module so consumers reading these helpers don't scroll
10
+ * past unrelated parsing / inspection / comment-detection code, and so the
11
+ * next round of scope helpers has a sensible home rather than landing in
12
+ * the general-purpose `ast-utilities.ts` module.
13
+ */
14
+ import * as ts from 'typescript';
15
+ /**
16
+ * Function-like nodes the helpers below treat as a "function boundary":
17
+ * regular declarations, methods, function expressions, arrow functions, and
18
+ * constructor declarations. The helpers stop their upward walk at any of
19
+ * these.
20
+ */
21
+ export type FunctionLikeNode = ts.FunctionDeclaration | ts.MethodDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.ConstructorDeclaration;
22
+ /**
23
+ * Walk up the AST from a node and return the nearest enclosing function-like
24
+ * declaration. Includes constructors. Returns null when the node sits at
25
+ * module scope.
26
+ */
27
+ export declare function findEnclosingFunction(node: ts.Node): FunctionLikeNode | null;
28
+ /**
29
+ * Walk up the AST from a node and return the BODY of the nearest enclosing
30
+ * function-like declaration when that body is a {@link ts.Block}. Returns
31
+ * null when there is no enclosing function, or when the function uses an
32
+ * expression body (e.g. an arrow function `() => x`) rather than a block.
33
+ */
34
+ export declare function findEnclosingFunctionBody(node: ts.Node): ts.Block | null;
35
+ /**
36
+ * Return the textual name of the nearest enclosing named function-like, or
37
+ * null when the enclosing function is anonymous or there is no enclosing
38
+ * function. Walks past anonymous arrow functions to the next named ancestor —
39
+ * e.g. for a node inside `class Foo { bar() { (() => baz())() } }`, this
40
+ * returns `'bar'`, not `null`.
41
+ */
42
+ export declare function getEnclosingFunctionName(node: ts.Node, sourceFile: ts.SourceFile): string | null;
43
+ /**
44
+ * Walk up the AST from a node and return the nearest function-like ancestor
45
+ * OR the enclosing SourceFile. Differs from {@link findEnclosingFunction} in
46
+ * that it always returns a node (never null) — the SourceFile acts as the
47
+ * top-level scope.
48
+ */
49
+ export declare function findEnclosingScope(node: ts.Node): ts.Node;
50
+ /**
51
+ * Return true when `node` carries the `async` modifier. Uses the modern
52
+ * `canHaveModifiers` + `getModifiers` API so it is safe to call on any node
53
+ * kind, not just function-likes.
54
+ */
55
+ export declare function isAsync(node: ts.Node): boolean;
56
+ /**
57
+ * Return true when `node` is nested inside an `async` function-like ancestor.
58
+ * Walks up until the first function-like is found (returns false if none),
59
+ * then asks {@link isAsync} of that function. Module-top-level code returns
60
+ * false — there is no enclosing async context.
61
+ */
62
+ export declare function isInAsyncContext(node: ts.Node): boolean;
63
+ /**
64
+ * Return true when `node` is nested inside a conditional construct — `if`,
65
+ * `else`, `switch` case, or a ternary expression — within its enclosing
66
+ * function. Stops at function boundaries (does NOT cross into outer
67
+ * functions).
68
+ */
69
+ export declare function isInsideConditionalBlock(node: ts.Node): boolean;
70
+ //# sourceMappingURL=function-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-scope.d.ts","sourceRoot":"","sources":["../src/function-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAMjC;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GACxB,EAAE,CAAC,mBAAmB,GACtB,EAAE,CAAC,iBAAiB,GACpB,EAAE,CAAC,kBAAkB,GACrB,EAAE,CAAC,aAAa,GAChB,EAAE,CAAC,sBAAsB,CAAC;AAgB9B;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,gBAAgB,GAAG,IAAI,CAO5E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMxE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI,CAkBhG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAOzD;AAMD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAG9C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAIvD;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAW/D"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @fileoverview Function-scope AST helpers.
3
+ *
4
+ * Walking helpers that answer "what function am I inside?" / "is this in an
5
+ * async context?" / "is this in a conditional branch?" — used by checks that
6
+ * need to reason about scope boundaries (lifecycle cleanup, async waterfall
7
+ * detection, etc.).
8
+ *
9
+ * Lives in its own module so consumers reading these helpers don't scroll
10
+ * past unrelated parsing / inspection / comment-detection code, and so the
11
+ * next round of scope helpers has a sensible home rather than landing in
12
+ * the general-purpose `ast-utilities.ts` module.
13
+ */
14
+ import * as ts from 'typescript';
15
+ function isFunctionLike(node) {
16
+ return (ts.isFunctionDeclaration(node) ||
17
+ ts.isMethodDeclaration(node) ||
18
+ ts.isFunctionExpression(node) ||
19
+ ts.isArrowFunction(node) ||
20
+ ts.isConstructorDeclaration(node));
21
+ }
22
+ // =============================================================================
23
+ // ENCLOSING-FUNCTION WALKERS
24
+ // =============================================================================
25
+ /**
26
+ * Walk up the AST from a node and return the nearest enclosing function-like
27
+ * declaration. Includes constructors. Returns null when the node sits at
28
+ * module scope.
29
+ */
30
+ export function findEnclosingFunction(node) {
31
+ let current = node.parent;
32
+ while (current && !ts.isSourceFile(current)) {
33
+ if (isFunctionLike(current))
34
+ return current;
35
+ current = current.parent;
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Walk up the AST from a node and return the BODY of the nearest enclosing
41
+ * function-like declaration when that body is a {@link ts.Block}. Returns
42
+ * null when there is no enclosing function, or when the function uses an
43
+ * expression body (e.g. an arrow function `() => x`) rather than a block.
44
+ */
45
+ export function findEnclosingFunctionBody(node) {
46
+ const fn = findEnclosingFunction(node);
47
+ if (!fn)
48
+ return null;
49
+ const body = fn.body;
50
+ if (body && ts.isBlock(body))
51
+ return body;
52
+ return null;
53
+ }
54
+ /**
55
+ * Return the textual name of the nearest enclosing named function-like, or
56
+ * null when the enclosing function is anonymous or there is no enclosing
57
+ * function. Walks past anonymous arrow functions to the next named ancestor —
58
+ * e.g. for a node inside `class Foo { bar() { (() => baz())() } }`, this
59
+ * returns `'bar'`, not `null`.
60
+ */
61
+ export function getEnclosingFunctionName(node, sourceFile) {
62
+ let current = node.parent;
63
+ while (current && !ts.isSourceFile(current)) {
64
+ if (ts.isMethodDeclaration(current)) {
65
+ return current.name.getText(sourceFile);
66
+ }
67
+ if (ts.isFunctionDeclaration(current) && current.name) {
68
+ return current.name.getText(sourceFile);
69
+ }
70
+ // Named function expression: `const x = function namedFn() { … }`
71
+ // — the name is part of the FunctionExpression, not its parent. Without
72
+ // this branch the walker would skip past namedFn to its outer scope.
73
+ if (ts.isFunctionExpression(current) && current.name) {
74
+ return current.name.getText(sourceFile);
75
+ }
76
+ current = current.parent;
77
+ }
78
+ return null;
79
+ }
80
+ /**
81
+ * Walk up the AST from a node and return the nearest function-like ancestor
82
+ * OR the enclosing SourceFile. Differs from {@link findEnclosingFunction} in
83
+ * that it always returns a node (never null) — the SourceFile acts as the
84
+ * top-level scope.
85
+ */
86
+ export function findEnclosingScope(node) {
87
+ let current = node.parent;
88
+ while (current) {
89
+ if (isFunctionLike(current) || ts.isSourceFile(current))
90
+ return current;
91
+ current = current.parent;
92
+ }
93
+ return node.getSourceFile();
94
+ }
95
+ // =============================================================================
96
+ // ASYNC / CONDITIONAL CONTEXT
97
+ // =============================================================================
98
+ /**
99
+ * Return true when `node` carries the `async` modifier. Uses the modern
100
+ * `canHaveModifiers` + `getModifiers` API so it is safe to call on any node
101
+ * kind, not just function-likes.
102
+ */
103
+ export function isAsync(node) {
104
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
105
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
106
+ }
107
+ /**
108
+ * Return true when `node` is nested inside an `async` function-like ancestor.
109
+ * Walks up until the first function-like is found (returns false if none),
110
+ * then asks {@link isAsync} of that function. Module-top-level code returns
111
+ * false — there is no enclosing async context.
112
+ */
113
+ export function isInAsyncContext(node) {
114
+ const fn = findEnclosingFunction(node);
115
+ if (!fn)
116
+ return false;
117
+ return isAsync(fn);
118
+ }
119
+ /**
120
+ * Return true when `node` is nested inside a conditional construct — `if`,
121
+ * `else`, `switch` case, or a ternary expression — within its enclosing
122
+ * function. Stops at function boundaries (does NOT cross into outer
123
+ * functions).
124
+ */
125
+ export function isInsideConditionalBlock(node) {
126
+ let current = node.parent;
127
+ while (current && !ts.isSourceFile(current)) {
128
+ if (isFunctionLike(current))
129
+ return false;
130
+ if (ts.isIfStatement(current))
131
+ return true;
132
+ if (ts.isSwitchStatement(current))
133
+ return true;
134
+ if (ts.isCaseClause(current))
135
+ return true;
136
+ if (ts.isConditionalExpression(current))
137
+ return true;
138
+ current = current.parent;
139
+ }
140
+ return false;
141
+ }
142
+ //# sourceMappingURL=function-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-scope.js","sourceRoot":"","sources":["../src/function-scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import * as ts from 'typescript';
2
+ export { ts };
3
+ export { typescriptAdapter, adapters } from './adapter.js';
4
+ export { parseSource } from './parse.js';
5
+ export { typescriptQuery } from './query.js';
6
+ export { stripStrings, stripComments } from './strip.js';
7
+ export { filterContent } from './filter.js';
8
+ export type { FilteredContent } from './filter.js';
9
+ export { discoverTypescriptWorkspaceUnits } from './workspace-units.js';
10
+ export { findEnclosingFunction, findEnclosingFunctionBody, getEnclosingFunctionName, findEnclosingScope, isAsync, isInAsyncContext, isInsideConditionalBlock, } from './function-scope.js';
11
+ export type { FunctionLikeNode } from './function-scope.js';
12
+ export { getSharedSourceFile, walkNodes, getIdentifierName, getPropertyChain, getLineNumber, getColumn, isPropertyAccess, isLiteral, isInStringLiteral, findCallExpressions, findBinaryExpressions, findTemplateLiterals, isInComment, countUnescapedBackticks, } from './ast-utilities.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,EAAE,EAAE,CAAC;AAEd,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAC;AAKxE,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACxB,kBAAkB,EAClB,OAAO,EACP,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM5D,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ // TypeScript LanguageAdapter for opensip-cli
2
+ // Re-export the TypeScript namespace as a first-class top-level export so the
3
+ // barrel surface is the single source of truth for `ts` access. Done as a
4
+ // namespace import + named export because `export * as ts from 'typescript'`
5
+ // is invalid against typescript's `export =` shape.
6
+ import * as ts from 'typescript';
7
+ // eslint-disable-next-line unicorn/prefer-export-from -- `export * as from 'typescript'` is invalid (typescript uses `export =`); the namespace import + named export form is the only working shape
8
+ export { ts };
9
+ export { typescriptAdapter, adapters } from './adapter.js';
10
+ export { parseSource } from './parse.js';
11
+ export { typescriptQuery } from './query.js';
12
+ export { stripStrings, stripComments } from './strip.js';
13
+ export { filterContent } from './filter.js';
14
+ export { discoverTypescriptWorkspaceUnits } from './workspace-units.js';
15
+ // Function-scope helpers — extracted from `ast-utilities.ts` into a
16
+ // concern-named module. New scope helpers go in `./function-scope.ts`,
17
+ // NOT in `ast-utilities.ts`.
18
+ export { findEnclosingFunction, findEnclosingFunctionBody, getEnclosingFunctionName, findEnclosingScope, isAsync, isInAsyncContext, isInsideConditionalBlock, } from './function-scope.js';
19
+ // Canonical TS AST helpers — the compiler-API utilities check packs use.
20
+ // Re-exported so TS checks import them from @opensip-cli/lang-typescript.
21
+ // The `ts` re-export from this module is intentionally NOT re-surfaced here
22
+ // (it now lives at the top of the barrel above).
23
+ export { getSharedSourceFile, walkNodes, getIdentifierName, getPropertyChain, getLineNumber, getColumn, isPropertyAccess, isLiteral, isInStringLiteral, findCallExpressions, findBinaryExpressions, findTemplateLiterals, isInComment, countUnescapedBackticks, } from './ast-utilities.js';
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAE7C,8EAA8E;AAC9E,0EAA0E;AAC1E,6EAA6E;AAC7E,oDAAoD;AACpD,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,qMAAqM;AACrM,OAAO,EAAE,EAAE,EAAE,CAAC;AAEd,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAC;AAExE,oEAAoE;AACpE,uEAAuE;AACvE,6BAA6B;AAC7B,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACxB,kBAAkB,EAClB,OAAO,EACP,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAG7B,yEAAyE;AACzE,0EAA0E;AAC1E,4EAA4E;AAC5E,iDAAiD;AACjD,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import ts from 'typescript';
2
+ /**
3
+ * Parse TypeScript/JavaScript source into a SourceFile.
4
+ * Returns null on parse failure.
5
+ *
6
+ * Uses ts.ScriptKind.TSX so the same parse path handles .ts and .tsx
7
+ * (and is permissive enough for .js / .jsx).
8
+ */
9
+ export declare function parseSource(content: string, filePath: string): ts.SourceFile | null;
10
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAAC,UAAU,GAAG,IAAI,CAanF"}
package/dist/parse.js ADDED
@@ -0,0 +1,19 @@
1
+ import ts from 'typescript';
2
+ /**
3
+ * Parse TypeScript/JavaScript source into a SourceFile.
4
+ * Returns null on parse failure.
5
+ *
6
+ * Uses ts.ScriptKind.TSX so the same parse path handles .ts and .tsx
7
+ * (and is permissive enough for .js / .jsx).
8
+ */
9
+ export function parseSource(content, filePath) {
10
+ try {
11
+ return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest,
12
+ /* setParentNodes */ true, ts.ScriptKind.TSX);
13
+ }
14
+ catch {
15
+ // @fitness-ignore-next-line error-handling-quality -- defensive parse-or-null helper; ts.createSourceFile is permissive (recovers from syntax errors) and effectively does not throw on real input, so caller's null-check is belt-and-suspenders.
16
+ return null;
17
+ }
18
+ }
19
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,QAAgB;IAC3D,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,gBAAgB,CACxB,QAAQ,EACR,OAAO,EACP,EAAE,CAAC,YAAY,CAAC,MAAM;QACtB,oBAAoB,CAAC,IAAI,EACzB,EAAE,CAAC,UAAU,CAAC,GAAG,CAClB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,mPAAmP;QACnP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import ts from 'typescript';
2
+ import type { LanguageQueryAPI } from '@opensip-cli/core/languages';
3
+ export declare const typescriptQuery: LanguageQueryAPI<ts.SourceFile, ts.Node>;
4
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAGV,gBAAgB,EAEjB,MAAM,6BAA6B,CAAC;AAYrC,eAAO,MAAM,eAAe,EAAE,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CA4DpE,CAAC"}