@checkdigit/eslint-plugin 6.6.0-PR.75-f33d → 6.6.0-PR.75-9891

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.
@@ -0,0 +1,315 @@
1
+ // src/fixture/no-fixture.ts
2
+ import "eslint";
3
+ import { getEnclosingScopeNode, getEnclosingStatement, getParent } from "../ast/tree.mjs";
4
+ import { analyzeResponseReferences } from "./response-reference.mjs";
5
+ import { strict as assert } from "node:assert";
6
+ import getDocumentationUrl from "../get-documentation-url.mjs";
7
+ import { getIndentation } from "../ast/format.mjs";
8
+ import { getResponseBodyRetrievalText } from "./fetch.mjs";
9
+ import { isValidPropertyName } from "./variable.mjs";
10
+ import { replaceEndpointUrlPrefixWithBasePath } from "./url.mjs";
11
+ var ruleId = "no-fixture";
12
+ function analyzeFixtureCall(call, results, sourceCode) {
13
+ const parent = getParent(call);
14
+ assert.ok(parent, "parent should exist for fixture/supertest call node");
15
+ let nextCall;
16
+ if (parent.type === "ReturnStatement") {
17
+ results.fixtureNode = call;
18
+ results.rootNode = parent;
19
+ } else if (parent.type === "AwaitExpression") {
20
+ results.fixtureNode = call;
21
+ const enclosingStatement = getEnclosingStatement(parent);
22
+ assert.ok(enclosingStatement);
23
+ const awaitParent = getParent(parent);
24
+ if (awaitParent?.type === "MemberExpression") {
25
+ results.rootNode = parent;
26
+ results.inlineStatementNode = enclosingStatement;
27
+ if (awaitParent.property.type === "Identifier" && awaitParent.property.name === "body") {
28
+ results.inlineBodyReference = awaitParent;
29
+ }
30
+ } else if (enclosingStatement.type === "VariableDeclaration") {
31
+ results.variableDeclaration = enclosingStatement;
32
+ results.rootNode = enclosingStatement;
33
+ } else {
34
+ results.rootNode = parent;
35
+ }
36
+ } else if (parent.type === "MemberExpression" && parent.property.type === "Identifier") {
37
+ if (parent.property.name === "expect") {
38
+ const assertionCall = getParent(parent);
39
+ assert.ok(assertionCall && assertionCall.type === "CallExpression");
40
+ results.assertions = [...results.assertions ?? [], assertionCall.arguments];
41
+ nextCall = assertionCall;
42
+ } else if (parent.property.name === "send") {
43
+ const sendRequestBodyCall = getParent(parent);
44
+ assert.ok(sendRequestBodyCall && sendRequestBodyCall.type === "CallExpression");
45
+ results.requestBody = sendRequestBodyCall.arguments[0];
46
+ nextCall = sendRequestBodyCall;
47
+ } else if (parent.property.name === "set") {
48
+ const setRequestHeaderCall = getParent(parent);
49
+ assert.ok(setRequestHeaderCall && setRequestHeaderCall.type === "CallExpression");
50
+ const [name, value] = setRequestHeaderCall.arguments;
51
+ results.requestHeaders = [...results.requestHeaders ?? [], { name, value }];
52
+ nextCall = setRequestHeaderCall;
53
+ }
54
+ } else {
55
+ throw new Error(`Unexpected expression in fixture/supertest call ${sourceCode.getText(parent)}.`);
56
+ }
57
+ if (nextCall) {
58
+ analyzeFixtureCall(nextCall, results, sourceCode);
59
+ }
60
+ }
61
+ function createResponseAssertions(fixtureCallInformation, sourceCode, responseVariableName, destructuringResponseHeadersVariable) {
62
+ let statusAssertion;
63
+ const nonStatusAssertions = [];
64
+ for (const expectArguments of fixtureCallInformation.assertions ?? []) {
65
+ if (expectArguments.length === 1) {
66
+ const [assertionArgument] = expectArguments;
67
+ assert.ok(assertionArgument);
68
+ if (assertionArgument.type === "MemberExpression" && assertionArgument.object.type === "Identifier" && assertionArgument.object.name === "StatusCodes" || assertionArgument.type === "Literal" || sourceCode.getText(assertionArgument).includes("StatusCodes.")) {
69
+ statusAssertion = `assert.equal(${responseVariableName}.status, ${sourceCode.getText(assertionArgument)})`;
70
+ } else if (assertionArgument.type === "ArrowFunctionExpression") {
71
+ let functionBody = sourceCode.getText(assertionArgument.body);
72
+ const [originalResponseArgument] = assertionArgument.params;
73
+ assert.ok(originalResponseArgument?.type === "Identifier");
74
+ const originalResponseArgumentName = originalResponseArgument.name;
75
+ if (originalResponseArgumentName !== responseVariableName) {
76
+ functionBody = functionBody.replace(
77
+ new RegExp(`\\b${originalResponseArgumentName}\\b`, "ug"),
78
+ responseVariableName
79
+ );
80
+ }
81
+ nonStatusAssertions.push(`assert.ok(${functionBody})`);
82
+ } else if (assertionArgument.type === "Identifier") {
83
+ nonStatusAssertions.push(`assert.ok(${sourceCode.getText(assertionArgument)}(${responseVariableName}))`);
84
+ } else if (assertionArgument.type === "ObjectExpression" || assertionArgument.type === "CallExpression") {
85
+ nonStatusAssertions.push(
86
+ `assert.deepEqual(await ${responseVariableName}.json(), ${sourceCode.getText(assertionArgument)})`
87
+ );
88
+ } else {
89
+ throw new Error(`Unexpected Supertest assertion argument: ".expect(${sourceCode.getText(assertionArgument)})`);
90
+ }
91
+ } else if (expectArguments.length === 2) {
92
+ const [headerName, headerValue] = expectArguments;
93
+ assert.ok(headerName && headerValue);
94
+ const headersReference = destructuringResponseHeadersVariable !== void 0 ? destructuringResponseHeadersVariable.name : `${responseVariableName}.headers`;
95
+ if (headerValue.type === "Literal" && headerValue.value instanceof RegExp) {
96
+ nonStatusAssertions.push(
97
+ `assert.ok(${headersReference}.get(${sourceCode.getText(headerName)}).match(${sourceCode.getText(headerValue)}))`
98
+ );
99
+ } else {
100
+ nonStatusAssertions.push(
101
+ `assert.equal(${headersReference}.get(${sourceCode.getText(headerName)}), ${sourceCode.getText(headerValue)})`
102
+ );
103
+ }
104
+ }
105
+ }
106
+ return {
107
+ statusAssertion,
108
+ nonStatusAssertions
109
+ };
110
+ }
111
+ function getResponseVariableNameToUse(scopeManager, fixtureCallInformation, scopeVariablesMap) {
112
+ if (fixtureCallInformation.variableDeclaration) {
113
+ const firstDeclaration = fixtureCallInformation.variableDeclaration.declarations[0];
114
+ if (firstDeclaration && firstDeclaration.id.type === "Identifier") {
115
+ return firstDeclaration.id.name;
116
+ }
117
+ }
118
+ const enclosingScopeNode = getEnclosingScopeNode(fixtureCallInformation.rootNode);
119
+ scopeManager.getDeclaredVariables(fixtureCallInformation.rootNode);
120
+ assert.ok(enclosingScopeNode);
121
+ const scope = scopeManager.acquire(enclosingScopeNode);
122
+ assert.ok(scope !== null);
123
+ let scopeVariables = scopeVariablesMap.get(scope);
124
+ if (!scopeVariables) {
125
+ scopeVariables = [...scope.set.keys()];
126
+ scopeVariablesMap.set(scope, scopeVariables);
127
+ }
128
+ let responseVariableCounter = 0;
129
+ let responseVariableNameToUse;
130
+ while (responseVariableNameToUse === void 0) {
131
+ responseVariableCounter++;
132
+ responseVariableNameToUse = `response${responseVariableCounter === 1 ? "" : responseVariableCounter.toString()}`;
133
+ if (scopeVariables.includes(responseVariableNameToUse)) {
134
+ responseVariableNameToUse = void 0;
135
+ }
136
+ }
137
+ scopeVariables.push(responseVariableNameToUse);
138
+ return responseVariableNameToUse;
139
+ }
140
+ function isResponseBodyRedefinition(responseBodyReference) {
141
+ const parent = getParent(responseBodyReference);
142
+ return parent?.type === "VariableDeclarator" && parent.id.type === "Identifier";
143
+ }
144
+ var rule = {
145
+ meta: {
146
+ type: "suggestion",
147
+ docs: {
148
+ description: "Prefer native fetch API over customized fixture API.",
149
+ url: getDocumentationUrl(ruleId)
150
+ },
151
+ messages: {
152
+ preferNativeFetch: "Prefer native fetch API over customized fixture API.",
153
+ unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}. Please manually convert the fixture API call to fetch API call.'
154
+ },
155
+ fixable: "code",
156
+ schema: []
157
+ },
158
+ // eslint-disable-next-line max-lines-per-function
159
+ create(context) {
160
+ const sourceCode = context.sourceCode;
161
+ const scopeManager = sourceCode.scopeManager;
162
+ const scopeVariablesMap = /* @__PURE__ */ new Map();
163
+ return {
164
+ // eslint-disable-next-line max-lines-per-function
165
+ 'CallExpression[callee.object.object.name="fixture"][callee.object.property.name="api"]': (fixtureCall) => {
166
+ try {
167
+ assert.ok(fixtureCall.type === "CallExpression");
168
+ const fixtureFunction = fixtureCall.callee;
169
+ assert.ok(fixtureFunction.type === "MemberExpression");
170
+ const indentation = getIndentation(fixtureCall, sourceCode);
171
+ const [urlArgumentNode] = fixtureCall.arguments;
172
+ assert.ok(urlArgumentNode !== void 0);
173
+ const fixtureCallInformation = {};
174
+ analyzeFixtureCall(fixtureCall, fixtureCallInformation, sourceCode);
175
+ const {
176
+ variable: responseVariable,
177
+ bodyReferences: responseBodyReferences,
178
+ headersReferences: responseHeadersReferences,
179
+ statusReferences: responseStatusReferences,
180
+ destructuringBodyVariable: destructuringResponseBodyVariable,
181
+ destructuringHeadersVariable: destructuringResponseHeadersVariable
182
+ } = analyzeResponseReferences(fixtureCallInformation.variableDeclaration, scopeManager);
183
+ const originalUrlArgumentText = sourceCode.getText(urlArgumentNode);
184
+ const fetchUrlArgumentText = replaceEndpointUrlPrefixWithBasePath(originalUrlArgumentText);
185
+ const methodNode = fixtureFunction.property;
186
+ assert.ok(methodNode.type === "Identifier");
187
+ const fetchRequestArgumentLines = [
188
+ "{",
189
+ ` method: '${methodNode.name.toUpperCase()}',`,
190
+ ...fixtureCallInformation.requestBody ? [` body: JSON.stringify(${sourceCode.getText(fixtureCallInformation.requestBody)}),`] : [],
191
+ ...fixtureCallInformation.requestHeaders ? [
192
+ ` headers: {`,
193
+ ...fixtureCallInformation.requestHeaders.map(
194
+ ({ name, value }) => (
195
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, no-nested-ternary, sonarjs/no-nested-template-literals
196
+ ` ${name.type === "Literal" ? isValidPropertyName(name.value) ? name.value : `'${name.value}'` : `[${sourceCode.getText(name)}]`}: ${sourceCode.getText(value)},`
197
+ )
198
+ ),
199
+ ` },`
200
+ ] : [],
201
+ "}"
202
+ ].join(`
203
+ ${indentation}`);
204
+ const responseVariableNameToUse = getResponseVariableNameToUse(
205
+ scopeManager,
206
+ fixtureCallInformation,
207
+ scopeVariablesMap
208
+ );
209
+ const isResponseBodyVariableRedefinitionNeeded = destructuringResponseBodyVariable !== void 0 || fixtureCallInformation.inlineBodyReference !== void 0 || responseBodyReferences.length > 0 && !responseBodyReferences.some(isResponseBodyRedefinition);
210
+ const redefineResponseBodyVariableName = `${responseVariableNameToUse}Body`;
211
+ const isResponseVariableRedefinitionNeeded = responseVariable === void 0 && fixtureCallInformation.assertions !== void 0 || isResponseBodyVariableRedefinitionNeeded;
212
+ const responseBodyHeadersVariableRedefineLines = isResponseVariableRedefinitionNeeded ? [
213
+ // eslint-disable-next-line no-nested-ternary
214
+ ...destructuringResponseBodyVariable ? [
215
+ `const ${destructuringResponseBodyVariable.name} = ${getResponseBodyRetrievalText(responseVariableNameToUse)}`
216
+ ] : isResponseBodyVariableRedefinitionNeeded ? [
217
+ `const ${redefineResponseBodyVariableName} = ${getResponseBodyRetrievalText(responseVariableNameToUse)}`
218
+ ] : [],
219
+ ...destructuringResponseHeadersVariable ? [`const ${destructuringResponseHeadersVariable.name} = ${responseVariableNameToUse}.headers`] : []
220
+ ] : [];
221
+ const { statusAssertion, nonStatusAssertions } = createResponseAssertions(
222
+ fixtureCallInformation,
223
+ sourceCode,
224
+ responseVariableNameToUse,
225
+ destructuringResponseHeadersVariable
226
+ );
227
+ const fetchCallText = `fetch(${fetchUrlArgumentText}, ${fetchRequestArgumentLines})`;
228
+ const fetchStatementText = !isResponseVariableRedefinitionNeeded ? fetchCallText : `const ${responseVariableNameToUse} = await ${fetchCallText}`;
229
+ const nodeToReplace = isResponseVariableRedefinitionNeeded ? fixtureCallInformation.rootNode : fixtureCallInformation.fixtureNode;
230
+ const appendingAssignmentAndAssertionText = [
231
+ "",
232
+ ...statusAssertion !== void 0 ? [statusAssertion] : [],
233
+ ...responseBodyHeadersVariableRedefineLines,
234
+ ...nonStatusAssertions
235
+ ].join(`;
236
+ ${indentation}`);
237
+ context.report({
238
+ node: fixtureCall,
239
+ messageId: "preferNativeFetch",
240
+ // eslint-disable-next-line sonarjs/cognitive-complexity
241
+ *fix(fixer) {
242
+ if (fixtureCallInformation.inlineStatementNode) {
243
+ const preInlineDeclaration = [
244
+ fetchStatementText,
245
+ `${appendingAssignmentAndAssertionText};
246
+ ${indentation}`
247
+ ].join(``);
248
+ yield fixer.insertTextBefore(fixtureCallInformation.inlineStatementNode, preInlineDeclaration);
249
+ } else {
250
+ yield fixer.replaceText(nodeToReplace, fetchStatementText);
251
+ const needEndingSemiColon = sourceCode.getText(nodeToReplace).endsWith(";");
252
+ yield fixer.insertTextAfter(
253
+ nodeToReplace,
254
+ needEndingSemiColon ? `${appendingAssignmentAndAssertionText};` : appendingAssignmentAndAssertionText
255
+ );
256
+ }
257
+ for (const responseBodyReference of responseBodyReferences) {
258
+ yield fixer.replaceText(
259
+ responseBodyReference,
260
+ isResponseBodyVariableRedefinitionNeeded || !isResponseBodyRedefinition(responseBodyReference) ? redefineResponseBodyVariableName : getResponseBodyRetrievalText(responseVariableNameToUse)
261
+ );
262
+ }
263
+ if (fixtureCallInformation.inlineBodyReference) {
264
+ yield fixer.replaceText(fixtureCallInformation.inlineBodyReference, redefineResponseBodyVariableName);
265
+ }
266
+ for (const responseHeadersReference of responseHeadersReferences) {
267
+ const parent = getParent(responseHeadersReference);
268
+ assert.ok(parent);
269
+ let headerName;
270
+ if (parent.type === "MemberExpression") {
271
+ const headerNameNode = parent.property;
272
+ headerName = // eslint-disable-next-line no-nested-ternary, @typescript-eslint/restrict-template-expressions
273
+ parent.computed ? sourceCode.getText(headerNameNode) : `'${sourceCode.getText(headerNameNode)}'`;
274
+ } else if (parent.type === "CallExpression") {
275
+ const headerNameNode = parent.arguments[0];
276
+ headerName = sourceCode.getText(headerNameNode);
277
+ }
278
+ assert.ok(headerName !== void 0);
279
+ yield fixer.replaceText(parent, `${responseVariableNameToUse}.headers.get(${headerName})`);
280
+ }
281
+ for (const responseStatusReference of responseStatusReferences) {
282
+ if (responseStatusReference.property.type === "Identifier" && responseStatusReference.property.name === "statusCode") {
283
+ yield fixer.replaceText(responseStatusReference.property, `status`);
284
+ }
285
+ }
286
+ if (fixtureCallInformation.rootNode.type === "ReturnStatement" && fixtureCallInformation.assertions !== void 0) {
287
+ yield fixer.insertTextAfter(
288
+ fixtureCallInformation.rootNode,
289
+ `
290
+ ${indentation}return ${responseVariableNameToUse};`
291
+ );
292
+ }
293
+ }
294
+ });
295
+ } catch (error) {
296
+ console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
297
+ context.report({
298
+ node: fixtureCall,
299
+ messageId: "unknownError",
300
+ data: {
301
+ fileName: context.filename,
302
+ error: error instanceof Error ? error.toString() : JSON.stringify(error)
303
+ }
304
+ });
305
+ }
306
+ }
307
+ };
308
+ }
309
+ };
310
+ var no_fixture_default = rule;
311
+ export {
312
+ no_fixture_default as default,
313
+ ruleId
314
+ };
315
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvbm8tZml4dHVyZS50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFrQkEsT0FBa0Q7QUFDbEQsU0FBUyx1QkFBdUIsdUJBQXVCLGlCQUFpQjtBQUN4RSxTQUFTLGlDQUFpQztBQUMxQyxTQUFTLFVBQVUsY0FBYztBQUNqQyxPQUFPLHlCQUF5QjtBQUNoQyxTQUFTLHNCQUFzQjtBQUMvQixTQUFTLG9DQUFvQztBQUM3QyxTQUFTLDJCQUEyQjtBQUNwQyxTQUFTLDRDQUE0QztBQUU5QyxJQUFNLFNBQVM7QUFjdEIsU0FBUyxtQkFBbUIsTUFBNEIsU0FBaUMsWUFBd0I7QUFDL0csUUFBTSxTQUFTLFVBQVUsSUFBSTtBQUM3QixTQUFPLEdBQUcsUUFBUSxxREFBcUQ7QUFFdkUsTUFBSTtBQUNKLE1BQUksT0FBTyxTQUFTLG1CQUFtQjtBQUVyQyxZQUFRLGNBQWM7QUFDdEIsWUFBUSxXQUFXO0FBQUEsRUFDckIsV0FBVyxPQUFPLFNBQVMsbUJBQW1CO0FBQzVDLFlBQVEsY0FBYztBQUN0QixVQUFNLHFCQUFxQixzQkFBc0IsTUFBTTtBQUN2RCxXQUFPLEdBQUcsa0JBQWtCO0FBQzVCLFVBQU0sY0FBYyxVQUFVLE1BQU07QUFDcEMsUUFBSSxhQUFhLFNBQVMsb0JBQW9CO0FBQzVDLGNBQVEsV0FBVztBQUNuQixjQUFRLHNCQUFzQjtBQUM5QixVQUFJLFlBQVksU0FBUyxTQUFTLGdCQUFnQixZQUFZLFNBQVMsU0FBUyxRQUFRO0FBQ3RGLGdCQUFRLHNCQUFzQjtBQUFBLE1BQ2hDO0FBQUEsSUFDRixXQUFXLG1CQUFtQixTQUFTLHVCQUF1QjtBQUM1RCxjQUFRLHNCQUFzQjtBQUM5QixjQUFRLFdBQVc7QUFBQSxJQUNyQixPQUFPO0FBQ0wsY0FBUSxXQUFXO0FBQUEsSUFDckI7QUFBQSxFQUNGLFdBQVcsT0FBTyxTQUFTLHNCQUFzQixPQUFPLFNBQVMsU0FBUyxjQUFjO0FBQ3RGLFFBQUksT0FBTyxTQUFTLFNBQVMsVUFBVTtB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
@@ -0,0 +1,56 @@
1
+ // src/fixture/response-reference.ts
2
+ import "eslint";
3
+ import { strict as assert } from "node:assert";
4
+ import { getParent } from "../ast/tree.mjs";
5
+ function analyzeResponseReferences(variableDeclaration, scopeManager) {
6
+ const results = {
7
+ bodyReferences: [],
8
+ headersReferences: [],
9
+ statusReferences: []
10
+ };
11
+ if (!variableDeclaration) {
12
+ return results;
13
+ }
14
+ const responseVariables = scopeManager.getDeclaredVariables(variableDeclaration);
15
+ for (const responseVariable of responseVariables) {
16
+ const identifier = responseVariable.identifiers[0];
17
+ assert.ok(identifier);
18
+ const identifierParent = getParent(identifier);
19
+ assert.ok(identifierParent);
20
+ if (identifierParent.type === "VariableDeclarator") {
21
+ results.variable = responseVariable;
22
+ const responseReferences = responseVariable.references.map(
23
+ (responseReference) => getParent(responseReference.identifier)
24
+ );
25
+ results.bodyReferences = responseReferences.filter(
26
+ (node) => node !== null && node !== void 0 && node.type === "MemberExpression" && node.property.type === "Identifier" && node.property.name === "body"
27
+ );
28
+ results.headersReferences = responseReferences.filter(
29
+ (node) => node !== null && node !== void 0 && node.type === "MemberExpression" && node.property.type === "Identifier" && (node.property.name === "header" || node.property.name === "headers" || node.property.name === "get")
30
+ );
31
+ results.statusReferences = responseReferences.filter(
32
+ (node) => node !== null && node !== void 0 && node.type === "MemberExpression" && node.property.type === "Identifier" && (node.property.name === "status" || node.property.name === "statusCode")
33
+ );
34
+ } else if (
35
+ // body reference through destruction/renaming, e.g. "const { body } = ..."
36
+ identifierParent.type === "Property" && identifierParent.key.type === "Identifier" && identifierParent.key.name === "body"
37
+ ) {
38
+ results.destructuringBodyVariable = responseVariable;
39
+ } else if (
40
+ // header reference through destruction/renaming, e.g. "const { headers } = ..."
41
+ identifierParent.type === "Property" && identifierParent.key.type === "Identifier" && identifierParent.key.name === "headers"
42
+ ) {
43
+ results.destructuringHeadersVariable = responseVariable;
44
+ results.destructuringHeadersReferences = responseVariable.references.map((reference) => reference.identifier).map(getParent).filter(
45
+ (parent) => parent?.type === "MemberExpression" && parent.property.type === "Identifier" && parent.property.name !== "get" && getParent(parent)?.type !== "CallExpression"
46
+ );
47
+ } else {
48
+ throw new Error(`Unknown response variable reference: ${responseVariable.name}`);
49
+ }
50
+ }
51
+ return results;
52
+ }
53
+ export {
54
+ analyzeResponseReferences
55
+ };
56
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvcmVzcG9uc2UtcmVmZXJlbmNlLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVNBLE9BQTJCO0FBQzNCLFNBQVMsVUFBVSxjQUFjO0FBQ2pDLFNBQVMsaUJBQWlCO0FBT25CLFNBQVMsMEJBQ2QscUJBQ0EsY0FDQTtBQUNBLFFBQU0sVUFRRjtBQUFBLElBQ0YsZ0JBQWdCLENBQUM7QUFBQSxJQUNqQixtQkFBbUIsQ0FBQztBQUFBLElBQ3BCLGtCQUFrQixDQUFDO0FBQUEsRUFDckI7QUFDQSxNQUFJLENBQUMscUJBQXFCO0FBQ3hCLFdBQU87QUFBQSxFQUNUO0FBRUEsUUFBTSxvQkFBb0IsYUFBYSxxQkFBcUIsbUJBQW1CO0FBQy9FLGFBQVcsb0JBQW9CLG1CQUFtQjtBQUNoRCxVQUFNLGFBQWEsaUJBQWlCLFlBQVksQ0FBQztBQUNqRCxXQUFPLEdBQUcsVUFBVTtBQUNwQixVQUFNLG1CQUFtQixVQUFVLFVBQVU7QUFDN0MsV0FBTyxHQUFHLGdCQUFnQjtBQUMxQixRQUFJLGlCQUFpQixTQUFTLHNCQUFzQjtBQUVsRCxjQUFRLFdBQVc7QUFDbkIsWUFBTSxxQkFBcUIsaUJBQWlCLFdBQVc7QUFBQSxRQUFJLENBQUMsc0JBQzFELFVBQVUsa0JBQWtCLFVBQVU7QUFBQSxNQUN4QztBQUVBLGNBQVEsaUJBQWlCLG1CQUFtQjtBQUFBLFFBQzFDLENBQUMsU0FDQyxTQUFTLFFBQ1QsU0FBUyxVQUNULEtBQUssU0FBUyxzQkFDZCxLQUFLLFNBQVMsU0FBUyxnQkFDdkIsS0FBSyxTQUFTLFNBQVM7QUFBQSxNQUMzQjtBQUVBLGNBQVEsb0JBQW9CLG1CQUFtQjtBQUFBLFFBQzdDLENBQUMsU0FDQyxTQUFTLFFBQ1QsU0FBUyxVQUNULEtBQUssU0FBUyxzQkFDZCxLQUFLLFNBQVMsU0FBUyxpQkFDdEIsS0FBSyxTQUFTLFNBQVMsWUFBWSxLQUFLLFNBQVMsU0FBUyxhQUFhLEtBQUssU0FBUyxTQUFTO0FBQUEsTUFDbkc7QUFFQSxjQUFRLG1CQUFtQixtQkFBbUI7QUFBQSxRQUM1QyxDQUFDLFNBQ0MsU0FBUyxRQUNULFNBQVMsVUFDVCxLQUFLLFNBQVMsc0JBQ2QsS0FBSyxTQUFTLFNBQVMsaUJBQ3RCLEtBQUssU0FBUyxTQUFTLFlBQVksS0FBSyxTQUFTLFNBQVM7QUFBQSxNQUMvRDtBQUFBLElBQ0Y7QUFBQTtBQUFBLE1BRUUsaUJBQWlCLFNBQVMsY0FDMUIsaUJBQWlCLElBQUksU0FBUyxnQkFDOUIsaUJBQWlCLElBQUksU0FBUztBQUFBLE1BQzlCO0FBQ0EsY0FBUSw0QkFBNEI7QUFBQSxJQUN0QztBQUFBO0FBQUEsTUFFRSxpQkFBaUIsU0FBUyxjQUMxQixpQkFBaUIsSUFBSSxTQUFTLGdCQUM5QixpQkFBaUIsSUFBSSxTQUFTO0FBQUEsTUFDOUI7QUFDQSxjQUFRLCtCQUErQjtBQUN2QyxjQUFRLGlDQUFpQyxpQkFBaUIsV0FDdkQsSUFBSSxDQUFDLGNBQWMsVUFBVSxVQUFVLEVBQ3ZDLElBQUksU0FBUyxFQUNiO0FBQUEsUUFDQyxDQUFDLFdBQ0MsUUFBUSxTQUFTLHNCQUNqQixPQUFPLFNBQVMsU0FBUyxnQkFDekIsT0FBTyxTQUFTLFNBQVMsU0FDekIsVUFBVSxNQUFNLEdBQUcsU0FBUztBQUFBLE1BQ2hDO0FBQUEsSUFDSixPQUFPO0FBQ0wsWUFBTSxJQUFJLE1BQU0sd0NBQXdDLGlCQUFpQixJQUFJLEVBQUU7QUFBQSxJQUNqRjtBQUFBLEVBQ0Y7QUFDQSxTQUFPO0FBQ1Q7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,8 @@
1
+ // src/fixture/url.ts
2
+ function replaceEndpointUrlPrefixWithBasePath(url) {
3
+ return url.replace(/`\/\w+(?<parts>-\w+)*\/v\d+\//u, "`${BASE_PATH}/");
4
+ }
5
+ export {
6
+ replaceEndpointUrlPrefixWithBasePath
7
+ };
8
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvdXJsLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQUVPLFNBQVMscUNBQXFDLEtBQWE7QUFFaEUsU0FBTyxJQUFJLFFBQVEsa0NBQWtDLGdCQUFnQjtBQUN2RTsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,8 @@
1
+ // src/fixture/variable.ts
2
+ function isValidPropertyName(name) {
3
+ return typeof name === "string" && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/u.test(name);
4
+ }
5
+ export {
6
+ isValidPropertyName
7
+ };
8
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvdmFyaWFibGUudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRU8sU0FBUyxvQkFBb0IsTUFBZTtBQUNqRCxTQUFPLE9BQU8sU0FBUyxZQUFZLDhCQUE4QixLQUFLLElBQUk7QUFDNUU7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -1,7 +1,8 @@
1
1
  // src/index.ts
2
+ import concurrentPromises, { ruleId as concurrentPromisesRuleId } from "./fixture/concurrent-promises.mjs";
3
+ import fetchHeaderGetter, { ruleId as fetchHeaderGetterRuleId } from "./fixture/fetch-header-getter.mjs";
2
4
  import invalidJsonStringify, { ruleId as invalidJsonStringifyRuleId } from "./invalid-json-stringify.mjs";
3
- import noFixture, { ruleId as noFixtureRuleId } from "./no-fixture.mjs";
4
- import noFixtureHeaders, { ruleId as noFixtureHeadersRuleId } from "./no-fixture-headers.mjs";
5
+ import noFixture, { ruleId as noFixtureRuleId } from "./fixture/no-fixture.mjs";
5
6
  import noPromiseInstanceMethod, { ruleId as noPromiseInstanceMethodRuleId } from "./no-promise-instance-method.mjs";
6
7
  import filePathComment from "./file-path-comment.mjs";
7
8
  import noCardNumbers from "./no-card-numbers.mjs";
@@ -26,7 +27,8 @@ var src_default = {
26
27
  [invalidJsonStringifyRuleId]: invalidJsonStringify,
27
28
  [noPromiseInstanceMethodRuleId]: noPromiseInstanceMethod,
28
29
  [noFixtureRuleId]: noFixture,
29
- [noFixtureHeadersRuleId]: noFixtureHeaders
30
+ [fetchHeaderGetterRuleId]: fetchHeaderGetter,
31
+ [concurrentPromisesRuleId]: concurrentPromises
30
32
  },
31
33
  configs: {
32
34
  all: {
@@ -43,7 +45,8 @@ var src_default = {
43
45
  [`@checkdigit/${invalidJsonStringifyRuleId}`]: "error",
44
46
  [`@checkdigit/${noPromiseInstanceMethodRuleId}`]: "error",
45
47
  [`@checkdigit/${noFixtureRuleId}`]: "error",
46
- [`@checkdigit/${noFixtureHeadersRuleId}`]: "error"
48
+ [`@checkdigit/${fetchHeaderGetterRuleId}`]: "error",
49
+ [`@checkdigit/${concurrentPromisesRuleId}`]: "error"
47
50
  }
48
51
  },
49
52
  recommended: {
@@ -66,4 +69,4 @@ var src_default = {
66
69
  export {
67
70
  src_default as default
68
71
  };
69
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLE9BQU8sd0JBQXdCLFVBQVUsa0NBQWtDO0FBQzNFLE9BQU8sYUFBYSxVQUFVLHVCQUF1QjtBQUNyRCxPQUFPLG9CQUFvQixVQUFVLDhCQUE4QjtBQUNuRSxPQUFPLDJCQUEyQixVQUFVLHFDQUFxQztBQUNqRixPQUFPLHFCQUFxQjtBQUM1QixPQUFPLG1CQUFtQjtBQUMxQixPQUFPLGtCQUFrQjtBQUN6QixPQUFPLFlBQVk7QUFDbkIsT0FBTyxzQkFBc0I7QUFDN0IsT0FBTywyQkFBMkI7QUFDbEMsT0FBTyxrQkFBa0I7QUFDekIsT0FBTyx5Q0FBeUM7QUFDaEQsT0FBTyx5QkFBeUI7QUFFaEMsSUFBTyxjQUFRO0FBQUEsRUFDYixPQUFPO0FBQUEsSUFDTCxxQkFBcUI7QUFBQSxJQUNyQixtQkFBbUI7QUFBQSxJQUNuQixXQUFXO0FBQUEsSUFDWCx5QkFBeUI7QUFBQSxJQUN6QixrQkFBa0I7QUFBQSxJQUNsQixzQkFBc0I7QUFBQSxJQUN0Qiw4QkFBOEI7QUFBQSxJQUM5QiwyQ0FBMkM7QUFBQSxJQUMzQywyQkFBMkI7QUFBQSxJQUMzQixDQUFDLDBCQUEwQixHQUFHO0FBQUEsSUFDOUIsQ0FBQyw2QkFBNkIsR0FBRztBQUFBLElBQ2pDLENBQUMsZUFBZSxHQUFHO0FBQUEsSUFDbkIsQ0FBQyxzQkFBc0IsR0FBRztBQUFBLEVBQzVCO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxLQUFLO0FBQUEsTUFDSCxPQUFPO0FBQUEsUUFDTCwrQkFBK0I7QUFBQSxRQUMvQixpQ0FBaUM7QUFBQSxRQUNqQyx1QkFBdUI7QUFBQSxRQUN2QixxQ0FBcUM7QUFBQSxRQUNyQyxrQ0FBa0M7QUFBQSxRQUNsQywwQ0FBMEM7QUFBQSxRQUMxQyx1REFBdUQ7QUFBQSxRQUN2RCx1Q0FBdUM7QUFBQSxRQUN2Qyw4QkFBOEI7QUFBQSxRQUM5QixDQUFDLGVBQWUsMEJBQTBCLEVBQUUsR0FBRztBQUFBLFFBQy9DLENBQUMsZUFBZSw2QkFBNkIsRUFBRSxHQUFHO0FBQUEsUUFDbEQsQ0FBQyxlQUFlLGVBQWUsRUFBRSxHQUFHO0FBQUEsUUFDcEMsQ0FBQyxlQUFlLHNCQUFzQixFQUFFLEdBQUc7QUFBQSxNQUM3QztBQUFBLElBQ0Y7QUFBQSxJQUNBLGFBQWE7QUFBQSxNQUNYLE9BQU87QUFBQSxRQUNMLCtCQUErQjtBQUFBLFFBQy9CLGlDQUFpQztBQUFBLFFBQ2pDLHVCQUF1QjtBQUFBLFFBQ3ZCLHFDQUFxQztBQUFBLFFBQ3JDLGtDQUFrQztBQUFBLFFBQ2xDLDBDQUEwQztBQUFBLFFBQzFDLHVEQUF1RDtBQUFBLFFBQ3ZELHVDQUF1QztBQUFBLFFBQ3ZDLDhCQUE4QjtBQUFBLFFBQzlCLENBQUMsZUFBZSwwQkFBMEIsRUFBRSxHQUFHO0FBQUEsUUFDL0MsQ0FBQyxlQUFlLDZCQUE2QixFQUFFLEdBQUc7QUFBQSxNQUNwRDtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
72
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLE9BQU8sc0JBQXNCLFVBQVUsZ0NBQWdDO0FBQ3ZFLE9BQU8scUJBQXFCLFVBQVUsK0JBQStCO0FBQ3JFLE9BQU8sd0JBQXdCLFVBQVUsa0NBQWtDO0FBQzNFLE9BQU8sYUFBYSxVQUFVLHVCQUF1QjtBQUNyRCxPQUFPLDJCQUEyQixVQUFVLHFDQUFxQztBQUNqRixPQUFPLHFCQUFxQjtBQUM1QixPQUFPLG1CQUFtQjtBQUMxQixPQUFPLGtCQUFrQjtBQUN6QixPQUFPLFlBQVk7QUFDbkIsT0FBTyxzQkFBc0I7QUFDN0IsT0FBTywyQkFBMkI7QUFDbEMsT0FBTyxrQkFBa0I7QUFDekIsT0FBTyx5Q0FBeUM7QUFDaEQsT0FBTyx5QkFBeUI7QUFFaEMsSUFBTyxjQUFRO0FBQUEsRUFDYixPQUFPO0FBQUEsSUFDTCxxQkFBcUI7QUFBQSxJQUNyQixtQkFBbUI7QUFBQSxJQUNuQixXQUFXO0FBQUEsSUFDWCx5QkFBeUI7QUFBQSxJQUN6QixrQkFBa0I7QUFBQSxJQUNsQixzQkFBc0I7QUFBQSxJQUN0Qiw4QkFBOEI7QUFBQSxJQUM5QiwyQ0FBMkM7QUFBQSxJQUMzQywyQkFBMkI7QUFBQSxJQUMzQixDQUFDLDBCQUEwQixHQUFHO0FBQUEsSUFDOUIsQ0FBQyw2QkFBNkIsR0FBRztBQUFBLElBQ2pDLENBQUMsZUFBZSxHQUFHO0FBQUEsSUFDbkIsQ0FBQyx1QkFBdUIsR0FBRztBQUFBLElBQzNCLENBQUMsd0JBQXdCLEdBQUc7QUFBQSxFQUM5QjtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1AsS0FBSztBQUFBLE1BQ0gsT0FBTztBQUFBLFFBQ0wsK0JBQStCO0FBQUEsUUFDL0IsaUNBQWlDO0FBQUEsUUFDakMsdUJBQXVCO0FBQUEsUUFDdkIscUNBQXFDO0FBQUEsUUFDckMsa0NBQWtDO0FBQUEsUUFDbEMsMENBQTBDO0FBQUEsUUFDMUMsdURBQXVEO0FBQUEsUUFDdkQsdUNBQXVDO0FBQUEsUUFDdkMsOEJBQThCO0FBQUEsUUFDOUIsQ0FBQyxlQUFlLDBCQUEwQixFQUFFLEdBQUc7QUFBQSxRQUMvQyxDQUFDLGVBQWUsNkJBQTZCLEVBQUUsR0FBRztBQUFBLFFBQ2xELENBQUMsZUFBZSxlQUFlLEVBQUUsR0FBRztBQUFBLFFBQ3BDLENBQUMsZUFBZSx1QkFBdUIsRUFBRSxHQUFHO0FBQUEsUUFDNUMsQ0FBQyxlQUFlLHdCQUF3QixFQUFFLEdBQUc7QUFBQSxNQUMvQztBQUFBLElBQ0Y7QUFBQSxJQUNBLGFBQWE7QUFBQSxNQUNYLE9BQU87QUFBQSxRQUNMLCtCQUErQjtBQUFBLFFBQy9CLGlDQUFpQztBQUFBLFFBQ2pDLHVCQUF1QjtBQUFBLFFBQ3ZCLHFDQUFxQztBQUFBLFFBQ3JDLGtDQUFrQztBQUFBLFFBQ2xDLDBDQUEwQztBQUFBLFFBQzFDLHVEQUF1RDtBQUFBLFFBQ3ZELHVDQUF1QztBQUFBLFFBQ3ZDLDhCQUE4QjtBQUFBLFFBQzlCLENBQUMsZUFBZSwwQkFBMEIsRUFBRSxHQUFHO0FBQUEsUUFDL0MsQ0FBQyxlQUFlLDZCQUE2QixFQUFFLEdBQUc7QUFBQSxNQUNwRDtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -1,4 +1,4 @@
1
1
  import { type Rule } from 'eslint';
2
- export declare const ruleId = "no-fixture-headers";
2
+ export declare const ruleId = "concurrent-promises";
3
3
  declare const rule: Rule.RuleModule;
4
4
  export default rule;
@@ -0,0 +1,4 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const ruleId = "fetch-header-getter";
3
+ declare const rule: Rule.RuleModule;
4
+ export default rule;
@@ -0,0 +1 @@
1
+ export declare function getResponseBodyRetrievalText(responseVariableName: string): string;
@@ -0,0 +1,16 @@
1
+ import type { MemberExpression, VariableDeclaration } from 'estree';
2
+ import { type Scope } from 'eslint';
3
+ /**
4
+ * analyze response related variables and their references
5
+ * the implementation is for fixture API, but it can be used for fetch API as well since the tree structure is similar
6
+ * @param variableDeclaration - variable declaration node
7
+ */
8
+ export declare function analyzeResponseReferences(variableDeclaration: VariableDeclaration | undefined, scopeManager: Scope.ScopeManager): {
9
+ variable?: Scope.Variable;
10
+ bodyReferences: MemberExpression[];
11
+ headersReferences: MemberExpression[];
12
+ statusReferences: MemberExpression[];
13
+ destructuringBodyVariable?: Scope.Variable;
14
+ destructuringHeadersVariable?: Scope.Variable;
15
+ destructuringHeadersReferences?: MemberExpression[] | undefined;
16
+ };
@@ -0,0 +1 @@
1
+ export declare function replaceEndpointUrlPrefixWithBasePath(url: string): string;
@@ -0,0 +1 @@
1
+ export declare function isValidPropertyName(name: unknown): boolean;
@@ -12,7 +12,8 @@ declare const _default: {
12
12
  "invalid-json-stringify": import("eslint").Rule.RuleModule;
13
13
  "no-promise-instance-method": import("eslint").Rule.RuleModule;
14
14
  "no-fixture": import("eslint").Rule.RuleModule;
15
- "no-fixture-headers": import("eslint").Rule.RuleModule;
15
+ "fetch-header-getter": import("eslint").Rule.RuleModule;
16
+ "concurrent-promises": import("eslint").Rule.RuleModule;
16
17
  };
17
18
  configs: {
18
19
  all: {
@@ -29,7 +30,8 @@ declare const _default: {
29
30
  "@checkdigit/invalid-json-stringify": string;
30
31
  "@checkdigit/no-promise-instance-method": string;
31
32
  "@checkdigit/no-fixture": string;
32
- "@checkdigit/no-fixture-headers": string;
33
+ "@checkdigit/fetch-header-getter": string;
34
+ "@checkdigit/concurrent-promises": string;
33
35
  };
34
36
  };
35
37
  recommended: {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@checkdigit/eslint-plugin","version":"6.6.0-PR.75-f33d","description":"Check Digit eslint plugins","keywords":["eslint","eslintplugin"],"homepage":"https://github.com/checkdigit/eslint-plugin#readme","bugs":{"url":"https://github.com/checkdigit/eslint-plugin/issues"},"repository":{"type":"git","url":"https://github.com/checkdigit/eslint-plugin"},"license":"MIT","author":"Check Digit, LLC","sideEffects":false,"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","require":"./dist-cjs/index.cjs","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-cjs","dist-mjs","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-cjs/**/*.test.cjs","!dist-cjs/**/*.spec.cjs","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"build:dist-cjs":"rimraf dist-cjs && npx builder --type=commonjs --sourceMap --entryPoint=index.ts --outDir=dist-cjs --outFile=index.cjs --external=espree && echo \"module.exports = module.exports.default;\" >> dist-cjs/index.cjs","build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","ci:compile":"tsc --noEmit","ci:coverage":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=true","ci:lint":"npm run lint","ci:style":"npm run prettier","ci:test":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","lint":"eslint --max-warnings 0 --ignore-path .gitignore .","lint:fix":"eslint --ignore-path .gitignore . --fix","prepublishOnly":"npm run build:dist-types && npm run build:dist-cjs && npm run build:dist-mjs","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style"},"prettier":"@checkdigit/prettier-config","jest":{"preset":"@checkdigit/jest-config"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^5.5.0","@checkdigit/typescript-config":"6.0.0","@types/eslint":"^8.56.10","@typescript-eslint/eslint-plugin":"^7.16.1","@typescript-eslint/parser":"^7.16.1","eslint-config-prettier":"^9.1.0","eslint-plugin-eslint-plugin":"^6.2.0","eslint-plugin-import":"^2.29.1","eslint-plugin-no-only-tests":"^3.1.0","eslint-plugin-no-secrets":"^1.0.2","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"0.24.0"},"peerDependencies":{"eslint":">=8 <9"},"engines":{"node":">=20.14"}}
1
+ {"name":"@checkdigit/eslint-plugin","version":"6.6.0-PR.75-9891","description":"Check Digit eslint plugins","keywords":["eslint","eslintplugin"],"homepage":"https://github.com/checkdigit/eslint-plugin#readme","bugs":{"url":"https://github.com/checkdigit/eslint-plugin/issues"},"repository":{"type":"git","url":"https://github.com/checkdigit/eslint-plugin"},"license":"MIT","author":"Check Digit, LLC","sideEffects":false,"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","require":"./dist-cjs/index.cjs","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-cjs","dist-mjs","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-cjs/**/*.test.cjs","!dist-cjs/**/*.spec.cjs","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"build:dist-cjs":"rimraf dist-cjs && npx builder --type=commonjs --sourceMap --entryPoint=index.ts --outDir=dist-cjs --outFile=index.cjs --external=espree && echo \"module.exports = module.exports.default;\" >> dist-cjs/index.cjs","build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","ci:compile":"tsc --noEmit","ci:coverage":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=true","ci:lint":"npm run lint","ci:style":"npm run prettier","ci:test":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","lint":"eslint --max-warnings 0 --ignore-path .gitignore .","lint:fix":"eslint --ignore-path .gitignore . --fix","prepublishOnly":"npm run build:dist-types && npm run build:dist-cjs && npm run build:dist-mjs","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style"},"prettier":"@checkdigit/prettier-config","jest":{"preset":"@checkdigit/jest-config"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^5.5.0","@checkdigit/typescript-config":"6.0.0","@types/eslint":"^8.56.10","@typescript-eslint/eslint-plugin":"^7.16.1","@typescript-eslint/parser":"^7.16.1","eslint-config-prettier":"^9.1.0","eslint-plugin-eslint-plugin":"^6.2.0","eslint-plugin-import":"^2.29.1","eslint-plugin-no-only-tests":"^3.1.0","eslint-plugin-no-secrets":"^1.0.2","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"0.24.0"},"peerDependencies":{"eslint":">=8 <9"},"engines":{"node":">=20.14"}}
package/src/ast/tree.ts CHANGED
@@ -47,6 +47,6 @@ export function getEnclosingStatement(node: Node) {
47
47
 
48
48
  export function getEnclosingScopeNode(node: Node) {
49
49
  return getAncestor(node, (parentNode) =>
50
- ['FunctionExpression', 'FunctionDeclaration', 'ArrowFunctionExpression'].includes(parentNode.type),
50
+ ['FunctionExpression', 'FunctionDeclaration', 'ArrowFunctionExpression', 'Program'].includes(parentNode.type),
51
51
  );
52
52
  }