@checkdigit/eslint-plugin 6.6.0-PR.75-a513 → 6.6.0-PR.75-bbf4

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/dist-cjs/index.cjs +968 -1029
  2. package/dist-cjs/metafile.json +144 -202
  3. package/dist-mjs/{fixture → agent}/add-url-domain.mjs +2 -2
  4. package/dist-mjs/{fixture → agent}/fetch-response-body-json.mjs +2 -2
  5. package/dist-mjs/agent/fetch-response-header-getter.mjs +117 -0
  6. package/dist-mjs/{fixture → agent}/fetch-then.mjs +5 -5
  7. package/dist-mjs/{fixture → agent}/fetch.mjs +3 -3
  8. package/dist-mjs/{fixture → agent}/no-fixture.mjs +5 -5
  9. package/dist-mjs/{fixture → agent}/no-full-response.mjs +3 -3
  10. package/dist-mjs/{fixture → agent}/no-service-wrapper.mjs +4 -4
  11. package/dist-mjs/{fixture → agent}/no-status-code.mjs +2 -2
  12. package/dist-mjs/{fixture → agent}/response-reference.mjs +3 -3
  13. package/dist-mjs/{fixture → agent}/url.mjs +2 -2
  14. package/dist-mjs/index.mjs +13 -16
  15. package/dist-mjs/library/format.mjs +14 -0
  16. package/dist-mjs/{ast → library}/tree.mjs +2 -2
  17. package/dist-mjs/library/ts-tree.mjs +72 -0
  18. package/dist-mjs/{fixture → library}/variable.mjs +2 -2
  19. package/dist-types/index.d.ts +0 -2
  20. package/dist-types/{ast → library}/ts-tree.d.ts +1 -0
  21. package/package.json +1 -1
  22. package/src/{fixture → agent}/add-url-domain.ts +2 -2
  23. package/src/{fixture/fetch-response-header-getter-ts.ts → agent/fetch-response-header-getter.ts} +32 -21
  24. package/src/{fixture → agent}/fetch-then.ts +3 -3
  25. package/src/{fixture → agent}/fetch.ts +1 -1
  26. package/src/{fixture → agent}/no-fixture.ts +3 -3
  27. package/src/{fixture → agent}/no-full-response.ts +1 -1
  28. package/src/{fixture → agent}/no-service-wrapper.ts +2 -2
  29. package/src/{fixture → agent}/response-reference.ts +1 -1
  30. package/src/index.ts +12 -15
  31. package/src/{ast → library}/ts-tree.ts +11 -0
  32. package/dist-mjs/ast/format.mjs +0 -14
  33. package/dist-mjs/ast/ts-tree.mjs +0 -65
  34. package/dist-mjs/fixture/fetch-header-getter.mjs +0 -71
  35. package/dist-mjs/fixture/fetch-response-header-getter-ts.mjs +0 -110
  36. package/dist-mjs/fixture/ts-tree.mjs +0 -12
  37. package/dist-types/fixture/fetch-header-getter.d.ts +0 -4
  38. package/dist-types/fixture/ts-tree.d.ts +0 -2
  39. package/src/fixture/fetch-header-getter.ts +0 -91
  40. package/src/fixture/ts-tree.ts +0 -14
  41. /package/dist-types/{fixture → agent}/add-url-domain.d.ts +0 -0
  42. /package/dist-types/{fixture → agent}/fetch-response-body-json.d.ts +0 -0
  43. /package/dist-types/{fixture/fetch-response-header-getter-ts.d.ts → agent/fetch-response-header-getter.d.ts} +0 -0
  44. /package/dist-types/{fixture → agent}/fetch-then.d.ts +0 -0
  45. /package/dist-types/{fixture → agent}/fetch.d.ts +0 -0
  46. /package/dist-types/{fixture → agent}/no-fixture.d.ts +0 -0
  47. /package/dist-types/{fixture → agent}/no-full-response.d.ts +0 -0
  48. /package/dist-types/{fixture → agent}/no-service-wrapper.d.ts +0 -0
  49. /package/dist-types/{fixture → agent}/no-status-code.d.ts +0 -0
  50. /package/dist-types/{fixture → agent}/response-reference.d.ts +0 -0
  51. /package/dist-types/{fixture → agent}/url.d.ts +0 -0
  52. /package/dist-types/{ast → library}/format.d.ts +0 -0
  53. /package/dist-types/{ast → library}/tree.d.ts +0 -0
  54. /package/dist-types/{fixture → library}/variable.d.ts +0 -0
  55. /package/src/{fixture → agent}/fetch-response-body-json.ts +0 -0
  56. /package/src/{fixture → agent}/no-status-code.ts +0 -0
  57. /package/src/{fixture → agent}/url.ts +0 -0
  58. /package/src/{ast → library}/format.ts +0 -0
  59. /package/src/{ast → library}/tree.ts +0 -0
  60. /package/src/{fixture → library}/variable.ts +0 -0
@@ -1,110 +0,0 @@
1
- // src/fixture/fetch-response-header-getter-ts.ts
2
- import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
3
- import getDocumentationUrl from "../get-documentation-url.mjs";
4
- var ruleId = "fetch-response-header-getter-ts";
5
- var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
6
- var rule = createRule({
7
- name: ruleId,
8
- meta: {
9
- type: "suggestion",
10
- docs: {
11
- description: 'Use "get()" method to get header value from the headers object of the fetch response.'
12
- },
13
- messages: {
14
- useGetter: 'Use "get()" method to get header value from the headers object of the fetch response.',
15
- unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.'
16
- },
17
- fixable: "code",
18
- schema: []
19
- },
20
- defaultOptions: [],
21
- create(context) {
22
- const parserServices = ESLintUtils.getParserServices(context);
23
- const typeChecker = parserServices.program.getTypeChecker();
24
- const sourceCode = context.sourceCode;
25
- return {
26
- 'MemberExpression[object.property.name="headers"]': (responseHeadersAccess) => {
27
- try {
28
- if (responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier && responseHeadersAccess.property.name === "get") {
29
- return;
30
- }
31
- const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseHeadersAccess.object);
32
- const responseType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
33
- const shouldReplace = responseType.getProperties().some((symbol) => symbol.name === "get");
34
- if (!shouldReplace) {
35
- return;
36
- }
37
- let replacementText;
38
- if (responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier) {
39
- replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`;
40
- } else if (responseHeadersAccess.property.type === AST_NODE_TYPES.TemplateLiteral) {
41
- replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`;
42
- } else if (responseHeadersAccess.property.type === AST_NODE_TYPES.Literal) {
43
- replacementText = responseHeadersAccess.computed ? `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})` : `${sourceCode.getText(responseHeadersAccess.object)}.get('${sourceCode.getText(responseHeadersAccess.property)}')`;
44
- } else {
45
- throw new Error(`Unexpected property type: ${responseHeadersAccess.property.type}`);
46
- }
47
- context.report({
48
- messageId: "useGetter",
49
- node: responseHeadersAccess.property,
50
- fix(fixer) {
51
- return fixer.replaceText(responseHeadersAccess, replacementText);
52
- }
53
- });
54
- } catch (error) {
55
- console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
56
- context.report({
57
- node: responseHeadersAccess,
58
- messageId: "unknownError",
59
- data: {
60
- fileName: context.filename,
61
- error: error instanceof Error ? error.toString() : JSON.stringify(error)
62
- }
63
- });
64
- }
65
- },
66
- 'CallExpression[callee.property.name="get"]:not([callee.object.name="request"])': (responseHeadersAccess) => {
67
- try {
68
- if (responseHeadersAccess.callee.type !== AST_NODE_TYPES.MemberExpression) {
69
- return;
70
- }
71
- const responseNode = responseHeadersAccess.callee.object;
72
- const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseNode);
73
- const responseType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
74
- const typeName = typeChecker.typeToString(responseType);
75
- if (typeName === "InboundContext" || typeName.endsWith("RequestType")) {
76
- return;
77
- }
78
- const hasHeadersProperty = responseType.getProperties().some((symbol) => symbol.name === "headers");
79
- if (!hasHeadersProperty) {
80
- return;
81
- }
82
- const replacementText = `${sourceCode.getText(responseNode)}.headers`;
83
- context.report({
84
- messageId: "useGetter",
85
- node: responseHeadersAccess,
86
- fix(fixer) {
87
- return fixer.replaceText(responseNode, replacementText);
88
- }
89
- });
90
- } catch (error) {
91
- console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
92
- context.report({
93
- node: responseHeadersAccess,
94
- messageId: "unknownError",
95
- data: {
96
- fileName: context.filename,
97
- error: error instanceof Error ? error.toString() : JSON.stringify(error)
98
- }
99
- });
100
- }
101
- }
102
- };
103
- }
104
- });
105
- var fetch_response_header_getter_ts_default = rule;
106
- export {
107
- fetch_response_header_getter_ts_default as default,
108
- ruleId
109
- };
110
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvZmV0Y2gtcmVzcG9uc2UtaGVhZGVyLWdldHRlci10cy50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFRQSxTQUFTLGdCQUFnQixtQkFBNkI7QUFDdEQsT0FBTyx5QkFBeUI7QUFFekIsSUFBTSxTQUFTO0FBRXRCLElBQU0sYUFBYSxZQUFZLFlBQVksQ0FBQyxTQUFTLG9CQUFvQixJQUFJLENBQUM7QUFFOUUsSUFBTSxPQUFPLFdBQVc7QUFBQSxFQUN0QixNQUFNO0FBQUEsRUFDTixNQUFNO0FBQUEsSUFDSixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsTUFDSixhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0EsVUFBVTtBQUFBLE1BQ1IsV0FBVztBQUFBLE1BQ1gsY0FBYztBQUFBLElBQ2hCO0FBQUEsSUFDQSxTQUFTO0FBQUEsSUFDVCxRQUFRLENBQUM7QUFBQSxFQUNYO0FBQUEsRUFDQSxnQkFBZ0IsQ0FBQztBQUFBLEVBQ2pCLE9BQU8sU0FBUztBQUNkLFVBQU0saUJBQWlCLFlBQVksa0JBQWtCLE9BQU87QUFDNUQsVUFBTSxjQUFjLGVBQWUsUUFBUSxlQUFlO0FBQzFELFVBQU0sYUFBYSxRQUFRO0FBRTNCLFdBQU87QUFBQSxNQUNMLG9EQUFvRCxDQUFDLDBCQUFxRDtBQUN4RyxZQUFJO0FBQ0YsY0FDRSxzQkFBc0IsU0FBUyxTQUFTLGVBQWUsY0FDdkQsc0JBQXNCLFNBQVMsU0FBUyxPQUN4QztBQUVBO0FBQUEsVUFDRjtBQUVBLGdCQUFNLHdCQUF3QixlQUFlLHNCQUFzQixJQUFJLHNCQUFzQixNQUFNO0FBQ25HLGdCQUFNLGVBQWUsWUFBWSxrQkFBa0IscUJBQXFCO0FBRXhFLGdCQUFNLGdCQUFnQixhQUFhLGNBQWMsRUFBRSxLQUFLLENBQUMsV0FBVyxPQUFPLFNBQVMsS0FBSztBQUN6RixjQUFJLENBQUMsZUFBZTtBQUNsQjtBQUFBLFVBQ0Y7QUFNQSxjQUFJO0FBQ0osY0FBSSxzQkFBc0IsU0FBUyxTQUFT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
@@ -1,12 +0,0 @@
1
- // src/fixture/ts-tree.ts
2
- import { AST_NODE_TYPES } from "@typescript-eslint/utils";
3
- function getTypeParentNode(node) {
4
- if (!node) {
5
- return void 0;
6
- }
7
- return node.type === AST_NODE_TYPES.TSTypeAnnotation || node.type === AST_NODE_TYPES.TSAsExpression ? node : getTypeParentNode(node.parent);
8
- }
9
- export {
10
- getTypeParentNode
11
- };
12
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2ZpeHR1cmUvdHMtdHJlZS50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFFQSxTQUFTLHNCQUFnQztBQUVsQyxTQUFTLGtCQUNkLE1BQ2lFO0FBQ2pFLE1BQUksQ0FBQyxNQUFNO0FBQ1QsV0FBTztBQUFBLEVBQ1Q7QUFDQSxTQUFPLEtBQUssU0FBUyxlQUFlLG9CQUFvQixLQUFLLFNBQVMsZUFBZSxpQkFDakYsT0FDQSxrQkFBa0IsS0FBSyxNQUFNO0FBQ25DOyIsCiAgIm5hbWVzIjogW10KfQo=
@@ -1,4 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- export declare const ruleId = "fetch-header-getter";
3
- declare const rule: Rule.RuleModule;
4
- export default rule;
@@ -1,2 +0,0 @@
1
- import { TSESTree } from '@typescript-eslint/utils';
2
- export declare function getTypeParentNode(node: TSESTree.Node | undefined): TSESTree.TSTypeAnnotation | TSESTree.TSAsExpression | undefined;
@@ -1,91 +0,0 @@
1
- // fixture/no-fixture.ts
2
-
3
- /*
4
- * Copyright (c) 2021-2024 Check Digit, LLC
5
- *
6
- * This code is licensed under the MIT license (see LICENSE.txt for details).
7
- */
8
-
9
- import type { Identifier, VariableDeclarator } from 'estree';
10
- import type { Rule } from 'eslint';
11
- import { analyzeResponseReferences } from './response-reference';
12
- import { strict as assert } from 'node:assert';
13
- import getDocumentationUrl from '../get-documentation-url';
14
- import { getParent } from '../ast/tree';
15
- import { isInvalidResponseHeadersAccess } from './fetch';
16
-
17
- export const ruleId = 'fetch-header-getter';
18
-
19
- const rule: Rule.RuleModule = {
20
- meta: {
21
- type: 'problem',
22
- docs: {
23
- description: 'Make sure getter is used to access response headers.',
24
- url: getDocumentationUrl(ruleId),
25
- },
26
- messages: {
27
- shouldUseHeaderGetter: 'Getter should be used to access response headers.',
28
- },
29
- fixable: 'code',
30
- schema: [],
31
- },
32
- create(context) {
33
- const sourceCode = context.sourceCode;
34
- const scopeManager = sourceCode.scopeManager;
35
-
36
- return {
37
- 'VariableDeclarator[init.argument.callee.name="fetch"]': (fetchCall: VariableDeclarator) => {
38
- const variableDeclaration = getParent(fetchCall);
39
- assert.ok(variableDeclaration?.type === 'VariableDeclaration');
40
- const { variable: responseVariable, headersReferences: responseHeadersReferences } = analyzeResponseReferences(
41
- variableDeclaration,
42
- scopeManager,
43
- );
44
- assert.ok(responseVariable);
45
-
46
- const directHeadersReferences = responseHeadersReferences.filter((headersReference) => {
47
- const headersAccess = getParent(headersReference);
48
- return headersAccess?.type !== 'VariableDeclarator';
49
- });
50
-
51
- const indirectHeadersReferences = responseHeadersReferences
52
- .map(getParent)
53
- .filter((parent): parent is VariableDeclarator => parent?.type === 'VariableDeclarator')
54
- .map((declarator) => (declarator.id as Identifier).name)
55
- .map((redefinedHeadersVariableName) => {
56
- const headersVariable = responseVariable.scope.variables.find((variable) => {
57
- const identifier = variable.identifiers[0];
58
- return identifier?.type === 'Identifier' && identifier.name === redefinedHeadersVariableName;
59
- });
60
- return headersVariable?.references.map((reference) => reference.identifier) ?? [];
61
- })
62
- .flat();
63
-
64
- const invalidHeadersReferences = [...directHeadersReferences, ...indirectHeadersReferences].filter(
65
- isInvalidResponseHeadersAccess,
66
- );
67
-
68
- invalidHeadersReferences.forEach((headersReference) => {
69
- const headerAccess = getParent(headersReference);
70
- if (headerAccess?.type === 'MemberExpression') {
71
- const headerNameNode = headerAccess.property;
72
- const headerName = headerAccess.computed
73
- ? sourceCode.getText(headerNameNode)
74
- : `'${sourceCode.getText(headerNameNode)}'`;
75
- const replacementText = `${sourceCode.getText(headerAccess.object)}.get(${headerName})`;
76
-
77
- context.report({
78
- node: headerAccess,
79
- messageId: 'shouldUseHeaderGetter',
80
- fix(fixer) {
81
- return fixer.replaceText(headerAccess, replacementText);
82
- },
83
- });
84
- }
85
- });
86
- },
87
- };
88
- },
89
- };
90
-
91
- export default rule;
@@ -1,14 +0,0 @@
1
- // fixture/ts-tree.ts
2
-
3
- import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
4
-
5
- export function getTypeParentNode(
6
- node: TSESTree.Node | undefined,
7
- ): TSESTree.TSTypeAnnotation | TSESTree.TSAsExpression | undefined {
8
- if (!node) {
9
- return undefined;
10
- }
11
- return node.type === AST_NODE_TYPES.TSTypeAnnotation || node.type === AST_NODE_TYPES.TSAsExpression
12
- ? node
13
- : getTypeParentNode(node.parent);
14
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes