declapract-typescript-ehmpathy 0.44.1 → 0.45.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.
@@ -0,0 +1,27 @@
1
+ this bad practice detects relative imports that traverse up directories (`../`) and can be replaced with the `@src/` path alias.
2
+
3
+ why this matters:
4
+ - relative paths are fragile and break when files are moved or reorganized
5
+ - `@src/` alias provides stable, absolute imports from the src root
6
+ - enables easier refactoring and code navigation
7
+ - imports remain correct regardless of file location
8
+
9
+ what it catches:
10
+ - imports using `../` (e.g., `from '../utils/helper'`)
11
+ - deeply nested relative imports (e.g., `from '../../../domain/objects'`)
12
+
13
+ what it ignores:
14
+ - same-directory imports (`./`) which are appropriate for local modules
15
+ - files outside `src/` directory
16
+ - imports already using `@src/` alias
17
+
18
+ the fix automatically rewrites:
19
+ ```typescript
20
+ // before
21
+ import { helper } from '../utils/helper';
22
+ import { thing } from '../../shared/thing';
23
+
24
+ // after
25
+ import { helper } from '@src/utils/helper';
26
+ import { thing } from '@src/shared/thing';
27
+ ```
@@ -0,0 +1,59 @@
1
+ import { type FileCheckFunction, type FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects relative imports that should use @src alias
5
+ * .why = relative paths are fragile and break when files move
6
+ */
7
+
8
+ // matches relative imports that go up directories (../)
9
+ const RELATIVE_IMPORT_PATTERN = /from\s+['"](\.\.\/)+(.*?)['"]/g;
10
+
11
+ export const check: FileCheckFunction = (contents, { relativeFilePath }) => {
12
+ if (!contents) throw new Error('does not match bad practice');
13
+
14
+ // skip if file is not in src/
15
+ if (!relativeFilePath?.startsWith('src/')) {
16
+ throw new Error('does not match bad practice');
17
+ }
18
+
19
+ // check for relative imports that go up directories (../)
20
+ const matches = contents.match(RELATIVE_IMPORT_PATTERN);
21
+ if (matches && matches.length > 0) {
22
+ return; // matches bad practice
23
+ }
24
+
25
+ throw new Error('does not match bad practice');
26
+ };
27
+
28
+ export const fix: FileFixFunction = (contents, { relativeFilePath }) => {
29
+ if (!contents) return {};
30
+ if (!relativeFilePath?.startsWith('src/')) return {};
31
+
32
+ // calculate the path parts of the current file
33
+ const pathParts = relativeFilePath.split('/');
34
+ const srcIndex = pathParts.indexOf('src');
35
+
36
+ // replace relative imports with @src imports
37
+ const fixed = contents.replace(
38
+ /from\s+['"]((\.\.\/)+)(.*?)['"]/g,
39
+ (match, dots, _, importPath) => {
40
+ // count how many ../ we have
41
+ const upCount = (dots.match(/\.\.\//g) || []).length;
42
+
43
+ // get the directories between src/ and the current file
44
+ const currentDirParts = pathParts.slice(srcIndex + 1, -1);
45
+
46
+ // if going up more levels than we have directories, just use @src/importPath
47
+ if (upCount >= currentDirParts.length) {
48
+ return `from '@src/${importPath}'`;
49
+ }
50
+
51
+ // otherwise, calculate the remaining path after going up
52
+ const remainingParts = currentDirParts.slice(0, -upCount);
53
+ const targetPath = [...remainingParts, importPath].join('/');
54
+ return `from '@src/${targetPath}'`;
55
+ },
56
+ );
57
+
58
+ return { contents: fixed };
59
+ };
@@ -8,3 +8,8 @@ namely:
8
8
  - strict mode on tsc prevents a _ton_ of errors
9
9
  - without it, `Type | null` -> `Type` -> so you have a bunch of bugs whenever a value could be null but you dont expect it to be / realize it could be
10
10
  - it defeats the point of typechecking, really
11
+ - absolute imports via @src/* path alias
12
+ - eliminates fragile relative paths (../../..)
13
+ - imports are stable regardless of file location
14
+ - easier refactoring and code navigation
15
+ - uses tsc-alias post-compilation to rewrite paths for node runtime
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "devDependencies": {
3
3
  "typescript": "@declapract{check.minVersion('5.4.5')}",
4
+ "tsc-alias": "@declapract{check.minVersion('1.8.10')}",
4
5
  "@tsconfig/node20": "@declapract{check.minVersion('20.1.5')}",
5
6
  "@tsconfig/strictest": "@declapract{check.minVersion('2.0.5')}",
6
7
  "@types/node": "@declapract{check.minVersion('22.15.21')}"
7
8
  },
8
9
  "scripts": {
9
10
  "build:clean": "rm dist/ -rf",
10
- "build:compile": "tsc -p ./tsconfig.build.json",
11
+ "build:compile": "tsc -p ./tsconfig.build.json && tsc-alias -p ./tsconfig.build.json",
11
12
  "build": "npm run build:clean && npm run build:compile && npm run build:artifact",
12
13
  "test:types": "tsc -p ./tsconfig.json --noEmit"
13
14
  }
@@ -4,6 +4,10 @@
4
4
  "@tsconfig/node20/tsconfig.json"
5
5
  ],
6
6
  "compilerOptions": {
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@src/*": ["src/*"]
10
+ },
7
11
  "importsNotUsedAsValues": "remove",
8
12
  "noPropertyAccessFromIndexSignature": false,
9
13
  "noUnusedLocals": false, // this is something a linter should warn on, not something a compiler should fail on
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "declapract-typescript-ehmpathy",
3
3
  "author": "ehmpathy",
4
4
  "description": "declapract best practices declarations for typescript",
5
- "version": "0.44.1",
5
+ "version": "0.45.0",
6
6
  "license": "MIT",
7
7
  "main": "src/index.js",
8
8
  "repository": "ehmpathy/declapract-typescript-ehmpathy",
@@ -39,7 +39,7 @@
39
39
  "preversion": "npm run prepush",
40
40
  "postversion": "git push origin HEAD --tags --no-verify",
41
41
  "prepare:husky": "npx husky install && chmod ug+x .husky/*",
42
- "prepare": "[ -d .git ] && npm run prepare:husky || exit 0"
42
+ "prepare": "[ -e .git ] && npm run prepare:husky || exit 0"
43
43
  },
44
44
  "dependencies": {
45
45
  "chalk": "4.1.2",
@@ -47,6 +47,8 @@
47
47
  "expect": "29.4.2",
48
48
  "flat": "5.0.2",
49
49
  "helpful-errors": "1.5.3",
50
+ "rhachet": "1.13.1",
51
+ "rhachet-roles-ehmpathy": "1.13.7",
50
52
  "simple-log-methods": "0.5.0",
51
53
  "yaml": "1.10.2"
52
54
  },