@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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/adapter.test.d.ts +2 -0
- package/dist/__tests__/adapter.test.d.ts.map +1 -0
- package/dist/__tests__/adapter.test.js +56 -0
- package/dist/__tests__/adapter.test.js.map +1 -0
- package/dist/__tests__/ast-utilities.test.d.ts +2 -0
- package/dist/__tests__/ast-utilities.test.d.ts.map +1 -0
- package/dist/__tests__/ast-utilities.test.js +442 -0
- package/dist/__tests__/ast-utilities.test.js.map +1 -0
- package/dist/__tests__/filter.test.d.ts +2 -0
- package/dist/__tests__/filter.test.d.ts.map +1 -0
- package/dist/__tests__/filter.test.js +183 -0
- package/dist/__tests__/filter.test.js.map +1 -0
- package/dist/__tests__/query.test.d.ts +2 -0
- package/dist/__tests__/query.test.d.ts.map +1 -0
- package/dist/__tests__/query.test.js +76 -0
- package/dist/__tests__/query.test.js.map +1 -0
- package/dist/__tests__/workspace-units.test.d.ts +2 -0
- package/dist/__tests__/workspace-units.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-units.test.js +94 -0
- package/dist/__tests__/workspace-units.test.js.map +1 -0
- package/dist/adapter.d.ts +6 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +17 -0
- package/dist/adapter.js.map +1 -0
- package/dist/ast-utilities.d.ts +76 -0
- package/dist/ast-utilities.d.ts.map +1 -0
- package/dist/ast-utilities.js +212 -0
- package/dist/ast-utilities.js.map +1 -0
- package/dist/filter.d.ts +39 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +263 -0
- package/dist/filter.js.map +1 -0
- package/dist/function-scope.d.ts +70 -0
- package/dist/function-scope.d.ts.map +1 -0
- package/dist/function-scope.js +142 -0
- package/dist/function-scope.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +10 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +19 -0
- package/dist/parse.js.map +1 -0
- package/dist/query.d.ts +4 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +74 -0
- package/dist/query.js.map +1 -0
- package/dist/strip.d.ts +25 -0
- package/dist/strip.d.ts.map +1 -0
- package/dist/strip.js +30 -0
- package/dist/strip.js.map +1 -0
- package/dist/workspace-units.d.ts +19 -0
- package/dist/workspace-units.d.ts.map +1 -0
- package/dist/workspace-units.js +78 -0
- package/dist/workspace-units.js.map +1 -0
- 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":"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"}
|
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/parse.d.ts
ADDED
|
@@ -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"}
|
package/dist/query.d.ts
ADDED
|
@@ -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"}
|