8000 fix gts type aware by emulating them to ts files · ember-cli/eslint-plugin-ember@dd72c1c · GitHub
[go: up one dir, main page]

Skip to content

Commit dd72c1c

Browse files
committed
fix gts type aware by emulating them to ts files
set our own ts.sys with ts.setSys typescript-eslint has handling for a lot of scenarios for file changes and project changes etc use mts extension to keep same offsets
1 parent 90d1fc4 commit dd72c1c

File tree

10 files changed

+760
-519
lines changed

10 files changed

+760
-519
lines changed

lib/parsers/gjs-gts-parser.js

Lines changed: 10 additions & 514 deletions
Large diffs are not rendered by default.

lib/parsers/transform.js

Lines changed: 518 additions & 0 deletions
Large diffs are not rendered by default.

lib/parsers/ts-utils.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const fs = require('node:fs');
2+
const ts = require('typescript');
3+
const { transformForLint } = require('./transform');
4+
const babel = require('@babel/core');
5+
const { replaceRange } = require('./transform');
6+
7+
module.exports.patchTs = function patchTs() {
8+
const sys = { ...ts.sys };
9+
const newSys = {
10+
...ts.sys,
11+
readDirectory(...args) {
12+
const results = sys.readDirectory.call(this, ...args);
13+
return results.map((f) => f.replace(/\.gts$/, '.mts'));
14+
},
15+
fileExists(fileName) {
16+
return fs.existsSync(fileName.replace(/\.mts$/, '.gts')) || fs.existsSync(fileName);
17+
},
18+
readFile(fname) {
19+
let fileName = fname;
20+
let content = '';
21+
try {
22+
content = fs.readFileSync(fileName).toString();
23+
} catch {
24+
fileName = fileName.replace(/\.mts$/, '.gts');
25+
content = fs.readFileSync(fileName).toString();
26+
}
27+
if (fileName.endsWith('.gts')) {
28+
content = transformForLint(content).output;
29+
}
30+
if ((!fileName.endsWith('.d.ts') && fileName.endsWith('.ts')) || fileName.endsWith('.gts')) {
31+
content = replaceExtensions(content);
32+
}
33+
return content;
34+
},
35+
};
36+
ts.setSys(newSys);
37+
};
38+
39+
function replaceExtensions(code) {
40+
let jsCode = code;
41+
const babelParseResult = babel.parse(jsCode, {
42+
parserOpts: { ranges: true, plugins: ['typescript'] },
43+
});
44+
const length = jsCode.length;
45+
for (const b of babelParseResult.program.body) {
46+
if (b.type === 'ImportDeclaration' && b.source.value.endsWith('.gts')) {
47+
const value = b.source.value.replace(/\.gts$/, '.mts');
48+
const strWrapper = jsCode[b.source.start];
49+
jsCode = replaceRange(jsCode, b.source.start, b.source.end, strWrapper + value + strWrapper);
50+
}
51+
}
52+
if (length !== jsCode.length) {
53+
throw new Error('bad replacement');
54+
}
55+
return jsCode;
56+
}
57+
58+
module.exports.replaceExtensions = replaceExtensions;

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
]
6969
},
7070
"dependencies": {
71+
"@babel/core": "^7.23.3",
7172
"@babel/eslint-parser": "^7.22.15",
7273
"@ember-data/rfc395-data": "^0.0.4",
7374
"@glimmer/syntax": "^0.85.12",
@@ -83,7 +84,8 @@
8384
"lodash.camelcase": "^4.3.0",
8485
"lodash.kebabcase": "^4.1.1",
8586
"requireindex": "^1.2.0",
86-
"snake-case": "^3.0.3"
87+
"snake-case": "^3.0.3",
88+
"typescript": "^5.2.2"
8789
},
8890
"devDependencies": {
8991
"@babel/plugin-proposal-class-properties": "^7.18.6",
@@ -112,8 +114,7 @@
112114
"npm-run-all": "^4.1.5",
113115
"prettier": "^3.0.3",
114116
"release-it": "^16.2.1",
115-
"sort-package-json": "^2.6.0",
116-
"typescript": "^5.2.2"
117+
"sort-package-json": "^2.6.0"
117118
},
118119
"peerDependencies": {
119120
"eslint": ">= 8"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const fortyTwoFromGTS = '42';
2+
3+
<template>
4+
{{fortyTwoFromGTS}}
5+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const fortyTwoFromTS = '42';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { fortyTwoFromGTS } from './bar.gts';
2+
import { fortyTwoFromTS } from './baz.ts';
3+
4+
export const fortyTwoLocal = '42';
5+
6+
const helloWorldFromTS = fortyTwoFromTS[0] === '4' ? 'hello' : 'world';
7+
const helloWorldFromGTS = fortyTwoFromGTS[0] === '4' ? 'hello' : 'world';
8+
const helloWorld = fortyTwoLocal[0] === '4' ? 'hello' : 'world';
9+
//
10+
<template>
11+
{{helloWorldFromGTS}}
12+
{{helloWorldFromTS}}
13+
{{helloWorld}}
14+
</template>

tests/lib/rules-preprocessor/gjs-gts-parser-test.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,4 +761,80 @@ describe('multiple tokens in same file', () => {
761761
expect(resultErrors[2].message).toBe("'bar' is not defined.");
762762
expect(resultErrors[2].line).toBe(17);
763763
});
764+
765+
it('lints while being type aware', async () => {
766+
const eslint = new ESLint({
767+
ignore: false,
768+
useEslintrc: false,
769+
plugins: { ember: plugin },
770+
overrideConfig: {
771+
root: true,
772+
env: {
773+
browser: true,
774+
},
775+
plugins: ['ember'],
776+
extends: ['plugin:ember/recommended'],
777+
overrides: [
778+
{
779+
files: ['**/*.gts'],
780+
parser: 'eslint-plugin-ember/gjs-gts-parser',
781+
parserOptions: {
782+
project: './tsconfig.eslint.json',
783+
tsconfigRootDir: __dirname,
784+
extraFileExtensions: ['.gts'],
785+
},
786+
extends: [
787+
'plugin:@typescript-eslint/recommended-requiring-type-checking',
788+
'plugin:ember/recommended',
789+
],
790+
rules: {
791+
'no-trailing-spaces': 'error',
792+
'@typescript-eslint/prefer-string-starts-ends-with': 'error',
793+
},
794+
},
795+
{
796+
files: ['**/*.ts'],
797+
parser: '@typescript-eslint/parser',
798+
parserOptions: {
799+
project: './tsconfig.eslint.json',
800+
tsconfigRootDir: __dirname,
801+
extraFileExtensions: ['.gts'],
802+
},
803+
extends: [
804+
'plugin:@typescript-eslint/recommended-requiring-type-checking',
805+
'plugin:ember/recommended',
806+
],
807+
rules: {
808+
'no-trailing-spaces': 'error',
809+
},
810+
},
811+
],
812+
rules: {
813+
quotes: ['error', 'single'],
814+
semi: ['error', 'always'],
815+
'object-curly-spacing': ['error', 'always'],
816+
'lines-between-class-members': 'error',
817+
'no-undef': 'error',
818+
'no-unused-vars': 'error',
819+
'ember/no-get': 'off',
820+
'ember/no-array-prototype-extensions': 'error',
821+
'ember/no-unused-services': 'error',
822+
},
823+
},
824+
});
825+
826+
const results = await eslint.lintFiles(['**/*.gts', '**/*.ts']);
827+
828+
const resultErrors = results.flatMap((result) => result.messages);
829+
expect(resultErrors).toHaveLength(3);
830+
831+
expect(resultErrors[0].message).toBe("Use 'String#startsWith' method instead."); // Actual result is "Unsafe member access [0] on an `any` value."
832+
expect(resultErrors[0].line).toBe(6);
833+
834+
expect(resultErrors[1].line).toBe(7);
835+
expect(resultErrors[1].message).toBe("Use 'String#startsWith' method instead.");
836+
837+
expect(resultErrors[2].line).toBe(8);
838+
expect(resultErrors[2].message).toBe("Use 'String#startsWith' method instead.");
839+
});
764840
});

tests/lib/rules-preprocessor/tsconfig.eslint.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"strictNullChecks": true
66
},
77
"include": [
8-
"*"
9-
]
8+
"**/*"
9+
],
1010
}

yarn.lock

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@
4949
json5 "^2.2.3"
5050
semver "^6.3.1"
5151

52+
"@babel/core@^7.23.3":
53+
version "7.23.3"
54+
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9"
55+
integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==
56+
dependencies:
57+
"@ampproject/remapping" "^2.2.0"
58+
"@babel/code-frame" "^7.22.13"
59+
"@babel/generator" "^7.23.3"
60+
"@babel/helper-compilation-targets" "^7.22.15"
61+
"@babel/helper-module-transforms" "^7.23.3"
62+
"@babel/helpers" "^7.23.2"
63+
"@babel/parser" "^7.23.3"
64+
"@babel/template" "^7.22.15"
65+
"@babel/traverse" "^7.23.3"
66+
"@babel/types" "^7.23.3"
67+
convert-source-map "^2.0.0"
68+
debug "^4.1.0"
69+
gensync "^1.0.0-beta.2"
70+
json5 "^2.2.3"
71+
semver "^6.3.1"
72+
5273
"@babel/eslint-parser@^7.22.15":
5374
version "7.22.15"
5475
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.15.tgz#263f059c476e29ca4972481a17b8b660cb025a34"
@@ -68,6 +89,16 @@
6889
"@jridgewell/trace-mapping" "^0.3.17"
6990
jsesc "^2.5.1"
7091

92+
"@babel/generator@^7.23.3":
93+
version "7.23.3"
94+
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e"
95+
integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==
96+
dependencies:
97+
"@babel/types" "^7.23.3"
98+
"@jridgewell/gen-mapping" "^0.3.2"
99+
"@jridgewell/trace-mapping" "^0.3.17"
100+
jsesc "^2.5.1"
101+
71102
"@babel/helper-annotate-as-pure@^7.22.5":
72103
version "7.22.5"
73104
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
@@ -146,6 +177,17 @@
146177
"@babel/helper-split-export-declaration" "^7.22.6"
147178
"@babel/helper-validator-identifier" "^7.22.20"
148179

180+
"@babel/helper-module-transforms@^7.23.3":
181+
version "7.23.3"
182+
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
183+
integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
184+
dependencies:
185+
"@babel/helper-environment-visitor" "^7.22.20"
186+
"@babel/helper-module-imports" "^7.22.15"
187+
"@babel/helper-simple-access" "^7.22.5"
188+
"@babel/helper-split-export-declaration" "^7.22.6"
189+
"@babel/helper-validator-identifier" "^7.22.20"
190+
149191
"@babel/helper-optimise-call-expression@^7.22.5":
150192
version "7.22.5"
151193
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e"
@@ -226,6 +268,11 @@
226268
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
227269
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
228270

271+
"@babel/parser@^7.23.3":
272+
version "7.23.3"
273+
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
274+
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
275+
229276
"@babel/plugin-proposal-class-properties@^7.18.6":
230277
version "7.18.6"
231278
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3"
@@ -375,6 +422,22 @@
375422
debug "^4.1.0"
376423
globals "^11.1.0"
377424

425+
"@babel/traverse@^7.23.3":
426+
version "7.23.3"
427+
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b"
428+
integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==
429+
dependencies:
430+
"@babel/code-frame" "^7.22.13"
431+
"@babel/generator" "^7.23.3"
432+
"@babel/helper-environment-visitor" "^7.22.20"
433+
"@babel/helper-function-name" "^7.23.0"
434+
"@babel/helper-hoist-variables" "^7.22.5"
435+
"@babel/helper-split-export-declaration" "^7.22.6"
436+
"@babel/parser" "^7.23.3"
437+
"@babel/types" "^7.23.3"
438+
debug "^4.1.0"
439+
globals "^11.1.0"
440+
378441
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.3.3":
379442
version "7.23.0"
380443
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
@@ -384,6 +447,15 @@
384447
"@babel/helper-validator-identifier" "^7.22.20"
385448
to-fast-properties "^2.0.0"
386449

450+
"@babel/types@^7.23.3":
451+
version "7.23.3"
452+
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598"
453+
integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==
454+
dependencies:
455+
"@babel/helper-string-parser" "^7.22.5"
456+
"@babel/helper-validator-identifier" "^7.22.20"
457+
to-fast-properties "^2.0.0"
458+
387459
"@bcoe/v8-coverage@^0.2.3":
388460
version "0.2.3"
389461
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"

0 commit comments

Comments
 (0)
0