@checkdigit/eslint-plugin 7.6.1 → 7.6.2-PR.108-2c27

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.
@@ -1,9 +1,11 @@
1
1
  // src/require-fixed-services-import.ts
2
- import { ESLintUtils } from "@typescript-eslint/utils";
2
+ import { strict as assert } from "node:assert";
3
+ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
3
4
  import getDocumentationUrl from "./get-documentation-url.mjs";
4
5
  var ruleId = "require-fixed-services-import";
5
6
  var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
6
- var SERVICE_TYPINGS_IMPORT_PATH_PREFIX = /(?<path>\.\.\/)+services(?!\/index(?:\.ts)?)\/.*/u;
7
+ var SERVICE_TYPINGS_IMPORT_PATH = /(?<path>\.\.\/)+services(?!\/index(?:\.ts)?)\/.*/u;
8
+ var SERVICE_TYPINGS_IMPORT_PATH_WITH_VERSION = /(?<path>\.\.\/)+services\/(?<service>\w+)\/(?<version>v\d+)(?<index>\/index(?:\.ts)?)?/u;
7
9
  var rule = createRule({
8
10
  name: ruleId,
9
11
  meta: {
@@ -12,28 +14,66 @@ var rule = createRule({
12
14
  description: 'Require fixed "from" with service typing imports from "src/services".'
13
15
  },
14
16
  messages: {
15
- updateServicesImportFrom: 'Update service typing imports to be from the fixed "src/services" path.'
17
+ updateServicesImportSpecifier: "Update service typing import specifiers to be from the corresponding service version namespace.",
18
+ updateServicesImportSource: 'Update service typing imports to be from the fixed "src/services" path.',
19
+ renameServiceTypeReference: "Rename service type reference using the corresponding service version namespace."
16
20
  },
17
21
  fixable: "code",
18
22
  schema: []
19
23
  },
20
24
  defaultOptions: [],
21
25
  create(context) {
26
+ const importedServiceTypeMapping = /* @__PURE__ */ new Map();
22
27
  return {
23
- ImportDeclaration(node) {
24
- const moduleName = node.source.value;
25
- if (SERVICE_TYPINGS_IMPORT_PATH_PREFIX.test(moduleName)) {
28
+ ImportDeclaration(importDeclaration) {
29
+ const moduleName = importDeclaration.source.value;
30
+ if (SERVICE_TYPINGS_IMPORT_PATH.test(moduleName)) {
31
+ const match = SERVICE_TYPINGS_IMPORT_PATH_WITH_VERSION.exec(moduleName);
32
+ if (match?.groups) {
33
+ const { service, version } = match.groups;
34
+ assert.ok(service !== void 0 && version !== void 0);
35
+ for (const specifier of importDeclaration.specifiers) {
36
+ if (specifier.type === AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === AST_NODE_TYPES.Identifier) {
37
+ importedServiceTypeMapping.set(specifier.imported.name, service);
38
+ }
39
+ }
40
+ const rangeStart = importDeclaration.specifiers[0]?.range[0];
41
+ assert.ok(rangeStart !== void 0);
42
+ const rangeEnd = importDeclaration.specifiers.at(-1)?.range[1];
43
+ assert.ok(rangeEnd !== void 0);
44
+ context.report({
45
+ messageId: "updateServicesImportSpecifier",
46
+ node: importDeclaration.source,
47
+ *fix(fixer) {
48
+ yield fixer.replaceTextRange([rangeStart, rangeEnd], `${service}V${version.slice(1)} as ${service}`);
49
+ }
50
+ });
51
+ }
26
52
  context.report({
27
- messageId: "updateServicesImportFrom",
28
- node: node.source,
53
+ messageId: "updateServicesImportSource",
54
+ node: importDeclaration.source,
29
55
  *fix(fixer) {
30
56
  yield fixer.replaceText(
31
- node.source,
32
- `'${moduleName.slice(0, moduleName.indexOf("../services") + "../services".length)}'`
57
+ importDeclaration.source,
58
+ `'${moduleName.slice(0, moduleName.indexOf("../services"))}../services/index.ts'`
33
59
  );
34
60
  }
35
61
  });
36
62
  }
63
+ },
64
+ TSTypeReference(typeReference) {
65
+ if (typeReference.typeName.type === AST_NODE_TYPES.Identifier && importedServiceTypeMapping.has(typeReference.typeName.name)) {
66
+ const referencedTypeName = typeReference.typeName.name;
67
+ const serviceNamespace = importedServiceTypeMapping.get(referencedTypeName);
68
+ assert.ok(serviceNamespace !== void 0);
69
+ context.report({
70
+ messageId: "renameServiceTypeReference",
71
+ node: typeReference.typeName,
72
+ fix(fixer) {
73
+ return fixer.replaceText(typeReference.typeName, `${serviceNamespace}.${referencedTypeName}`);
74
+ }
75
+ });
76
+ }
37
77
  }
38
78
  };
39
79
  }
@@ -43,4 +83,4 @@ export {
43
83
  require_fixed_services_import_default as default,
44
84
  ruleId
45
85
  };
46
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3JlcXVpcmUtZml4ZWQtc2VydmljZXMtaW1wb3J0LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLFNBQVMsbUJBQW1CO0FBRTVCLE9BQU8seUJBQXlCO0FBRXpCLElBQU0sU0FBUztBQUV0QixJQUFNLGFBQWEsWUFBWSxZQUFZLENBQUMsU0FBUyxvQkFBb0IsSUFBSSxDQUFDO0FBQzlFLElBQU0scUNBQXFDO0FBRTNDLElBQU0sT0FBMkQsV0FBVztBQUFBLEVBQzFFLE1BQU07QUFBQSxFQUNOLE1BQU07QUFBQSxJQUNKLE1BQU07QUFBQSxJQUNOLE1BQU07QUFBQSxNQUNKLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQSxVQUFVO0FBQUEsTUFDUiwwQkFBMEI7QUFBQSxJQUM1QjtBQUFBLElBQ0EsU0FBUztBQUFBLElBQ1QsUUFBUSxDQUFDO0FBQUEsRUFDWDtBQUFBLEVBQ0EsZ0JBQWdCLENBQUM7QUFBQSxFQUNqQixPQUFPLFNBQVM7QUFDZCxXQUFPO0FBQUEsTUFDTCxrQkFBa0IsTUFBTTtBQUN0QixjQUFNLGFBQWEsS0FBSyxPQUFPO0FBQy9CLFlBQUksbUNBQW1DLEtBQUssVUFBVSxHQUFHO0FBQ3ZELGtCQUFRLE9BQU87QUFBQSxZQUNiLFdBQVc7QUFBQSxZQUNYLE1BQU0sS0FBSztBQUFBLFlBQ1gsQ0FBQyxJQUFJLE9BQU87QUFDVixvQkFBTSxNQUFNO0FBQUEsZ0JBQ1YsS0FBSztBQUFBLGdCQUNMLElBQUksV0FBVyxNQUFNLEdBQUcsV0FBVyxRQUFRLGFBQWEsSUFBSSxjQUFjLE1BQU0sQ0FBQztBQUFBLGNBQ25GO0FBQUEsWUFDRjtBQUFBLFVBQ0YsQ0FBQztBQUFBLFFBQ0g7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDO0FBRUQsSUFBTyx3Q0FBUTsiLAogICJuYW1lcyI6IFtdCn0K
86
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3JlcXVpcmUtZml4ZWQtc2VydmljZXMtaW1wb3J0LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLFNBQVMsVUFBVSxjQUFjO0FBRWpDLFNBQVMsZ0JBQWdCLG1CQUE2QjtBQUV0RCxPQUFPLHlCQUF5QjtBQUV6QixJQUFNLFNBQVM7QUFFdEIsSUFBTSxhQUFhLFlBQVksWUFBWSxDQUFDLFNBQVMsb0JBQW9CLElBQUksQ0FBQztBQUM5RSxJQUFNLDhCQUE4QjtBQUNwQyxJQUFNLDJDQUNKO0FBRUYsSUFBTSxPQUVGLFdBQVc7QUFBQSxFQUNiLE1BQU07QUFBQSxFQUNOLE1BQU07QUFBQSxJQUNKLE1BQU07QUFBQSxJQUNOLE1BQU07QUFBQSxNQUNKLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQSxVQUFVO0FBQUEsTUFDUiwrQkFDRTtBQUFBLE1BQ0YsNEJBQTRCO0FBQUEsTUFDNUIsNEJBQTRCO0FBQUEsSUFDOUI7QUFBQSxJQUNBLFNBQVM7QUFBQSxJQUNULFFBQVEsQ0FBQztBQUFBLEVBQ1g7QUFBQSxFQUNBLGdCQUFnQixDQUFDO0FBQUEsRUFDakIsT0FBTyxTQUFTO0FBQ2QsVUFBTSw2QkFBNkIsb0JBQUksSUFBb0I7QUFFM0QsV0FBTztBQUFBLE1BQ0wsa0JBQWtCLG1CQUFtQjtBQUNuQyxjQUFNLGFBQWEsa0JBQWtCLE9BQU87QUFDNUMsWUFBSSw0QkFBNEIsS0FBSyxVQUFVLEdBQUc7QUFDaEQsZ0JBQU0sUUFBUSx5Q0FBeUMsS0FBSyxVQUFVO0FBQ3RFLGNBQUksT0FBTyxRQUFRO0FBRWpCLGtCQUFNLEVBQUUsU0FBUyxRQUFRLElBQUksTUFBTTtBQUNuQyxtQkFBTyxHQUFHLFlBQVksVUFBYSxZQUFZLE1BQVM7QUFHeEQsdUJBQVcsYUFBYSxrQkFBa0IsWUFBWTtBQUNwRCxrQkFDRSxVQUFVLFNBQVMsZUFBZSxtQkFDbEMsVUFBVSxTQUFTLFNBQVMsZUFBZSxZQUMzQztBQUNBLDJDQUEyQixJQUFJLFVBQVUsU0FBUyxNQUFNLE9BQU87QUFBQSxjQUNqRTtBQUFBLFlBQ0Y7QUFFQSxrQkFBTSxhQUFhLGtCQUFrQixXQUFXLENBQUMsR0FBRyxNQUFNLENBQUM7QUFDM0QsbUJBQU8sR0FBRyxlQUFlLE1BQVM7QUFDbEMsa0JBQU0sV0FBVyxrQkFBa0IsV0FBVyxHQUFHLEVBQUUsR0FBRyxNQUFNLENBQUM7QUFDN0QsbUJBQU8sR0FBRyxhQUFhLE1BQVM7QUFFaEMsb0JBQVEsT0FBTztBQUFBLGNBQ2IsV0FBVztBQUFBLGNBQ1gsTUFBTSxrQkFBa0I7QUFBQSxjQUN4QixDQUFDLElBQUksT0FBTztBQUNWLHNCQUFNLE1BQU0saUJBQWlCLENBQUMsWUFBWSxRQUFRLEdBQUcsR0FBRyxPQUFPLElBQUksUUFBUSxNQUFNLENBQUMsQ0FBQyxPQUFPLE9BQU8sRUFBRTtBQUFBLGNBQ3JHO0FBQUEsWUFDRixDQUFDO0FBQUEsVUFDSDtBQUdBLGtCQUFRLE9BQU87QUFBQSxZQUNiLFdBQVc7QUFBQSxZQUNYLE1BQU0sa0JBQWtCO0FBQUEsWUFDeEIsQ0FBQyxJQUFJLE9BQU87QUFDVixvQkFBTSxNQUFNO0FBQUEsZ0JBQ1Ysa0JBQWtCO0FBQUEsZ0JBQ2xCLElBQUksV0FBVyxNQUFNLEdBQUcsV0FBVyxRQUFRLGFBQWEsQ0FBQyxDQUFDO0FBQUEsY0FDNUQ7QUFBQSxZQUNGO0FBQUEsVUFDRixDQUFDO0FBQUEsUUFDSDtBQUFBLE1BQ0Y7QUFBQSxNQUVBLGdCQUFnQixlQUF5QztBQUN2RCxZQUNFLGNBQWMsU0FBUyxTQUFTLGVBQWUsY0FDL0MsMkJBQTJCLElBQUksY0FBYyxTQUFTLElBQUksR0FDMUQ7QUFDQSxnQkFBTSxxQkFBcUIsY0FBYyxTQUFTO0FBQ2xELGdCQUFNLG1CQUFtQiwyQkFBMkIsSUFBSSxrQkFBa0I7QUFDMUUsaUJBQU8sR0FBRyxxQkFBcUIsTUFBUztBQUN4QyxrQkFBUSxPQUFPO0FBQUEsWUFDYixXQUFXO0FBQUEsWUFDWCxNQUFNLGNBQWM7QUFBQSxZQUNwQixJQUFJLE9BQU87QUFDVCxxQkFBTyxNQUFNLFlBQVksY0FBYyxVQUFVLEdBQUcsZ0JBQWdCLElBQUksa0JBQWtCLEVBQUU7QUFBQSxZQUM5RjtBQUFBLFVBQ0YsQ0FBQztBQUFBLFFBQ0g7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDO0FBRUQsSUFBTyx3Q0FBUTsiLAogICJuYW1lcyI6IFtdCn0K
@@ -1,4 +1,4 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
2
  export declare const ruleId = "require-fixed-services-import";
3
- declare const rule: ESLintUtils.RuleModule<'updateServicesImportFrom'>;
3
+ declare const rule: ESLintUtils.RuleModule<'updateServicesImportSpecifier' | 'updateServicesImportSource' | 'renameServiceTypeReference'>;
4
4
  export default rule;
package/package.json CHANGED
@@ -1,96 +1 @@
1
- {
2
- "name": "@checkdigit/eslint-plugin",
3
- "version": "7.6.1",
4
- "description": "Check Digit eslint plugins",
5
- "keywords": [
6
- "eslint",
7
- "eslintplugin"
8
- ],
9
- "homepage": "https://github.com/checkdigit/eslint-plugin#readme",
10
- "bugs": {
11
- "url": "https://github.com/checkdigit/eslint-plugin/issues"
12
- },
13
- "repository": {
14
- "type": "git",
15
- "url": "https://github.com/checkdigit/eslint-plugin"
16
- },
17
- "license": "MIT",
18
- "author": "Check Digit, LLC",
19
- "sideEffects": false,
20
- "type": "module",
21
- "exports": {
22
- ".": {
23
- "types": "./dist-types/index.d.ts",
24
- "import": "./dist-mjs/index.mjs",
25
- "default": "./dist-mjs/index.mjs"
26
- }
27
- },
28
- "files": [
29
- "src",
30
- "dist-types",
31
- "dist-mjs",
32
- "!src/**/test/**",
33
- "!src/**/*.test.ts",
34
- "!src/**/*.spec.ts",
35
- "!dist-types/**/test/**",
36
- "!dist-types/**/*.test.d.ts",
37
- "!dist-types/**/*.spec.d.ts",
38
- "!dist-mjs/**/test/**",
39
- "!dist-mjs/**/*.test.mjs",
40
- "!dist-mjs/**/*.spec.mjs",
41
- "SECURITY.md"
42
- ],
43
- "scripts": {
44
- "build:dist-mjs": "rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs",
45
- "build:dist-types": "rimraf dist-types && npx builder --type=types --outDir=dist-types",
46
- "ci:compile": "tsc --noEmit",
47
- "ci:coverage": "NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=true",
48
- "ci:lint": "npm run lint",
49
- "ci:style": "npm run prettier",
50
- "ci:test": "NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false",
51
- "lint": "eslint --max-warnings 0 .",
52
- "lint:fix": "eslint --max-warnings 0 --fix .",
53
- "prepare": "",
54
- "prepublishOnly": "npm run build:dist-types && npm run build:dist-mjs",
55
- "prettier": "prettier --ignore-path .gitignore --list-different .",
56
- "prettier:fix": "prettier --ignore-path .gitignore --write .",
57
- "test": "npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style"
58
- },
59
- "prettier": "@checkdigit/prettier-config",
60
- "jest": {
61
- "preset": "@checkdigit/jest-config"
62
- },
63
- "dependencies": {
64
- "@typescript-eslint/type-utils": "^8.18.0",
65
- "@typescript-eslint/utils": "^8.18.0",
66
- "ts-api-utils": "^2.0.0"
67
- },
68
- "devDependencies": {
69
- "@checkdigit/jest-config": "^6.0.2",
70
- "@checkdigit/prettier-config": "^6.0.0",
71
- "@checkdigit/typescript-config": "^9.0.0",
72
- "@eslint/js": "^9.16.0",
73
- "@types/eslint": "^9.6.1",
74
- "@types/eslint-config-prettier": "^6.11.3",
75
- "@typescript-eslint/parser": "^8.18.0",
76
- "@typescript-eslint/rule-tester": "^8.18.0",
77
- "eslint": "^9.16.0",
78
- "eslint-config-prettier": "^9.1.0",
79
- "eslint-import-resolver-typescript": "^3.7.0",
80
- "eslint-plugin-eslint-plugin": "^6.3.2",
81
- "eslint-plugin-import": "^2.31.0",
82
- "eslint-plugin-no-only-tests": "^3.3.0",
83
- "eslint-plugin-no-secrets": "^2.1.1",
84
- "eslint-plugin-node": "^11.1.0",
85
- "eslint-plugin-sonarjs": "1.0.4",
86
- "http-status-codes": "^2.3.0",
87
- "rimraf": "^6.0.1",
88
- "typescript-eslint": "^8.18.0"
89
- },
90
- "peerDependencies": {
91
- "eslint": ">=9 <10"
92
- },
93
- "engines": {
94
- "node": ">=20.17"
95
- }
96
- }
1
+ {"name":"@checkdigit/eslint-plugin","version":"7.6.2-PR.108-2c27","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","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-mjs","!src/**/test/**","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/test/**","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-mjs/**/test/**","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"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 .","lint:fix":"eslint --max-warnings 0 --fix .","prepare":"","prepublishOnly":"npm run build:dist-types && 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"},"dependencies":{"@typescript-eslint/type-utils":"^8.18.0","@typescript-eslint/utils":"^8.18.0","ts-api-utils":"^2.0.0"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^6.0.0","@checkdigit/typescript-config":"^9.0.0","@eslint/js":"^9.16.0","@types/eslint":"^9.6.1","@types/eslint-config-prettier":"^6.11.3","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/rule-tester":"^8.18.0","eslint":"^9.16.0","eslint-config-prettier":"^9.1.0","eslint-import-resolver-typescript":"^3.7.0","eslint-plugin-eslint-plugin":"^6.3.2","eslint-plugin-import":"^2.31.0","eslint-plugin-no-only-tests":"^3.3.0","eslint-plugin-no-secrets":"^2.1.1","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"1.0.4","http-status-codes":"^2.3.0","rimraf":"^6.0.1","typescript-eslint":"^8.18.0"},"peerDependencies":{"eslint":">=9 <10"},"engines":{"node":">=20.17"}}
@@ -6,16 +6,22 @@
6
6
  * This code is licensed under the MIT license (see LICENSE.txt for details).
7
7
  */
8
8
 
9
- import { ESLintUtils } from '@typescript-eslint/utils';
9
+ import { strict as assert } from 'node:assert';
10
+
11
+ import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
10
12
 
11
13
  import getDocumentationUrl from './get-documentation-url.ts';
12
14
 
13
15
  export const ruleId = 'require-fixed-services-import';
14
16
 
15
17
  const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
16
- const SERVICE_TYPINGS_IMPORT_PATH_PREFIX = /(?<path>\.\.\/)+services(?!\/index(?:\.ts)?)\/.*/u;
18
+ const SERVICE_TYPINGS_IMPORT_PATH = /(?<path>\.\.\/)+services(?!\/index(?:\.ts)?)\/.*/u;
19
+ const SERVICE_TYPINGS_IMPORT_PATH_WITH_VERSION =
20
+ /(?<path>\.\.\/)+services\/(?<service>\w+)\/(?<version>v\d+)(?<index>\/index(?:\.ts)?)?/u;
17
21
 
18
- const rule: ESLintUtils.RuleModule<'updateServicesImportFrom'> = createRule({
22
+ const rule: ESLintUtils.RuleModule<
23
+ 'updateServicesImportSpecifier' | 'updateServicesImportSource' | 'renameServiceTypeReference'
24
+ > = createRule({
19
25
  name: ruleId,
20
26
  meta: {
21
27
  type: 'suggestion',
@@ -23,29 +29,83 @@ const rule: ESLintUtils.RuleModule<'updateServicesImportFrom'> = createRule({
23
29
  description: 'Require fixed "from" with service typing imports from "src/services".',
24
30
  },
25
31
  messages: {
26
- updateServicesImportFrom: 'Update service typing imports to be from the fixed "src/services" path.',
32
+ updateServicesImportSpecifier:
33
+ 'Update service typing import specifiers to be from the corresponding service version namespace.',
34
+ updateServicesImportSource: 'Update service typing imports to be from the fixed "src/services" path.',
35
+ renameServiceTypeReference: 'Rename service type reference using the corresponding service version namespace.',
27
36
  },
28
37
  fixable: 'code',
29
38
  schema: [],
30
39
  },
31
40
  defaultOptions: [],
32
41
  create(context) {
42
+ const importedServiceTypeMapping = new Map<string, string>();
43
+
33
44
  return {
34
- ImportDeclaration(node) {
35
- const moduleName = node.source.value;
36
- if (SERVICE_TYPINGS_IMPORT_PATH_PREFIX.test(moduleName)) {
45
+ ImportDeclaration(importDeclaration) {
46
+ const moduleName = importDeclaration.source.value;
47
+ if (SERVICE_TYPINGS_IMPORT_PATH.test(moduleName)) {
48
+ const match = SERVICE_TYPINGS_IMPORT_PATH_WITH_VERSION.exec(moduleName);
49
+ if (match?.groups) {
50
+ // need to import the service typings from the fixed path, and also apply the namespace to the referenced types
51
+ const { service, version } = match.groups; /*?*/
52
+ assert.ok(service !== undefined && version !== undefined);
53
+
54
+ // remember the type name and the corresponding service, which will be used to rename the references
55
+ for (const specifier of importDeclaration.specifiers) {
56
+ if (
57
+ specifier.type === AST_NODE_TYPES.ImportSpecifier &&
58
+ specifier.imported.type === AST_NODE_TYPES.Identifier
59
+ ) {
60
+ importedServiceTypeMapping.set(specifier.imported.name, service);
61
+ }
62
+ }
63
+
64
+ const rangeStart = importDeclaration.specifiers[0]?.range[0];
65
+ assert.ok(rangeStart !== undefined);
66
+ const rangeEnd = importDeclaration.specifiers.at(-1)?.range[1];
67
+ assert.ok(rangeEnd !== undefined);
68
+ // import the service typings using our naming convension
69
+ context.report({
70
+ messageId: 'updateServicesImportSpecifier',
71
+ node: importDeclaration.source,
72
+ *fix(fixer) {
73
+ yield fixer.replaceTextRange([rangeStart, rangeEnd], `${service}V${version.slice(1)} as ${service}`);
74
+ },
75
+ });
76
+ }
77
+
78
+ // update the imported source to be the fixed path
37
79
  context.report({
38
- messageId: 'updateServicesImportFrom',
39
- node: node.source,
80
+ messageId: 'updateServicesImportSource',
81
+ node: importDeclaration.source,
40
82
  *fix(fixer) {
41
83
  yield fixer.replaceText(
42
- node.source,
43
- `'${moduleName.slice(0, moduleName.indexOf('../services') + '../services'.length)}'`,
84
+ importDeclaration.source,
85
+ `'${moduleName.slice(0, moduleName.indexOf('../services'))}../services/index.ts'`,
44
86
  );
45
87
  },
46
88
  });
47
89
  }
48
90
  },
91
+
92
+ TSTypeReference(typeReference: TSESTree.TSTypeReference) {
93
+ if (
94
+ typeReference.typeName.type === AST_NODE_TYPES.Identifier &&
95
+ importedServiceTypeMapping.has(typeReference.typeName.name)
96
+ ) {
97
+ const referencedTypeName = typeReference.typeName.name;
98
+ const serviceNamespace = importedServiceTypeMapping.get(referencedTypeName);
99
+ assert.ok(serviceNamespace !== undefined);
100
+ context.report({
101
+ messageId: 'renameServiceTypeReference',
102
+ node: typeReference.typeName,
103
+ fix(fixer) {
104
+ return fixer.replaceText(typeReference.typeName, `${serviceNamespace}.${referencedTypeName}`);
105
+ },
106
+ });
107
+ }
108
+ },
49
109
  };
50
110
  },
51
111
  });