Skip to content

Commit

Permalink
fix gts type aware by emulating them to ts files
Browse files Browse the repository at this point in the history
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
  • Loading branch information
patricklx committed Dec 8, 2023
1 parent a08f0ab commit 35c39d3
Show file tree
Hide file tree
Showing 10 changed files with 768 additions and 519 deletions.
525 changes: 9 additions & 516 deletions lib/parsers/gjs-gts-parser.js

Large diffs are not rendered by default.

519 changes: 519 additions & 0 deletions lib/parsers/transform.js

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions lib/parsers/ts-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const fs = require('node:fs');
const ts = require('typescript');
const { transformForLint } = require('./transform');
const babel = require('@babel/core');
const { replaceRange } = require('./transform');

module.exports.patchTs = function patchTs() {
const sys = { ...ts.sys };
const newSys = {
...ts.sys,
readDirectory(...args) {
const results = sys.readDirectory.call(this, ...args);
return [
...results,
...results.filter((x) => x.endsWith('.gts')).map((f) => f.replace(/\.gts$/, '.mts')),
];
},
fileExists(fileName) {
return fs.existsSync(fileName.replace(/\.mts$/, '.gts')) || fs.existsSync(fileName);
},
readFile(fname) {
let fileName = fname;
let content = '';
try {
content = fs.readFileSync(fileName).toString();
} catch {
fileName = fileName.replace(/\.mts$/, '.gts');
content = fs.readFileSync(fileName).toString();
}
if (fileName.endsWith('.gts')) {
content = transformForLint(content).output;
}
if ((!fileName.endsWith('.d.ts') && fileName.endsWith('.ts')) || fileName.endsWith('.gts')) {
content = replaceExtensions(content);
}
return content;
},
};
ts.setSys(newSys);
};

function replaceExtensions(code) {
let jsCode = code;
const babelParseResult = babel.parse(jsCode, {
parserOpts: { ranges: true, plugins: ['typescript'] },
});
const length = jsCode.length;
for (const b of babelParseResult.program.body) {
if (b.type === 'ImportDeclaration' && b.source.value.endsWith('.gts')) {
const value = b.source.value.replace(/\.gts$/, '.mts');
const strWrapper = jsCode[b.source.start];
jsCode = replaceRange(jsCode, b.source.start, b.source.end, strWrapper + value + strWrapper);
}
}
if (length !== jsCode.length) {
throw new Error('bad replacement');
}
return jsCode;
}

module.exports.replaceExtensions = replaceExtensions;
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
]
},
"dependencies": {
"@babel/core": "^7.23.3",
"@babel/eslint-parser": "^7.22.15",
"@ember-data/rfc395-data": "^0.0.4",
"@glimmer/syntax": "^0.85.12",
Expand Down Expand Up @@ -116,7 +117,13 @@
"typescript": "^5.2.2"
},
"peerDependencies": {
"eslint": ">= 8"
"eslint": ">= 8",
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": "18.* || 20.* || >= 21"
Expand Down
5 changes: 5 additions & 0 deletions tests/lib/rules-preprocessor/ember_ts/bar.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const fortyTwoFromGTS = '42';

<template>
{{fortyTwoFromGTS}}
</template>
1 change: 1 addition & 0 deletions tests/lib/rules-preprocessor/ember_ts/baz.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const fortyTwoFromTS = '42';
14 changes: 14 additions & 0 deletions tests/lib/rules-preprocessor/ember_ts/foo.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fortyTwoFromGTS } from './bar.gts';
import { fortyTwoFromTS } from './baz.ts';

export const fortyTwoLocal = '42';

const helloWorldFromTS = fortyTwoFromTS[0] === '4' ? 'hello' : 'world';
const helloWorldFromGTS = fortyTwoFromGTS[0] === '4' ? 'hello' : 'world';
const helloWorld = fortyTwoLocal[0] === '4' ? 'hello' : 'world';
//
<template>
{{helloWorldFromGTS}}
{{helloWorldFromTS}}
{{helloWorld}}
</template>
76 changes: 76 additions & 0 deletions tests/lib/rules-preprocessor/gjs-gts-parser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,80 @@ describe('multiple tokens in same file', () => {
expect(resultErrors[2].message).toBe("'bar' is not defined.");
expect(resultErrors[2].line).toBe(17);
});

it('lints while being type aware', async () => {
const eslint = new ESLint({
ignore: false,
useEslintrc: false,
plugins: { ember: plugin },
overrideConfig: {
root: true,
env: {
browser: true,
},
plugins: ['ember'],
extends: ['plugin:ember/recommended'],
overrides: [
{
files: ['**/*.gts'],
parser: 'eslint-plugin-ember/gjs-gts-parser',
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
extraFileExtensions: ['.gts'],
},
extends: [
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:ember/recommended',
],
rules: {
'no-trailing-spaces': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'error',
},
},
{
files: ['**/*.ts'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
extraFileExtensions: ['.gts'],
},
extends: [
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:ember/recommended',
],
rules: {
'no-trailing-spaces': 'error',
},
},
],
rules: {
quotes: ['error', 'single'],
semi: ['error', 'always'],
'object-curly-spacing': ['error', 'always'],
'lines-between-class-members': 'error',
'no-undef': 'error',
'no-unused-vars': 'error',
'ember/no-get': 'off',
'ember/no-array-prototype-extensions': 'error',
'ember/no-unused-services': 'error',
},
},
});

const results = await eslint.lintFiles(['**/*.gts', '**/*.ts']);

const resultErrors = results.flatMap((result) => result.messages);
expect(resultErrors).toHaveLength(3);

expect(resultErrors[0].message).toBe("Use 'String#startsWith' method instead.");
expect(resultErrors[0].line).toBe(6);

expect(resultErrors[1].line).toBe(7);
expect(resultErrors[1].message).toBe("Use 'String#startsWith' method instead.");

expect(resultErrors[2].line).toBe(8);
expect(resultErrors[2].message).toBe("Use 'String#startsWith' method instead.");
});
});
5 changes: 3 additions & 2 deletions tests/lib/rules-preprocessor/tsconfig.eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"strictNullChecks": true
},
"include": [
"*"
]
"**/*.ts",
"**/*.gts"
],
}
72 changes: 72 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@
json5 "^2.2.3"
semver "^6.3.1"

"@babel/core@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9"
integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.3"
"@babel/helper-compilation-targets" "^7.22.15"
"@babel/helper-module-transforms" "^7.23.3"
"@babel/helpers" "^7.23.2"
"@babel/parser" "^7.23.3"
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.3"
"@babel/types" "^7.23.3"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.3"
semver "^6.3.1"

"@babel/eslint-parser@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.15.tgz#263f059c476e29ca4972481a17b8b660cb025a34"
Expand All @@ -68,6 +89,16 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"

"@babel/generator@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e"
integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==
dependencies:
"@babel/types" "^7.23.3"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"

"@babel/helper-annotate-as-pure@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
Expand Down Expand Up @@ -146,6 +177,17 @@
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/helper-validator-identifier" "^7.22.20"

"@babel/helper-module-transforms@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
dependencies:
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-module-imports" "^7.22.15"
"@babel/helper-simple-access" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/helper-validator-identifier" "^7.22.20"

"@babel/helper-optimise-call-expression@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e"
Expand Down Expand Up @@ -226,6 +268,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==

"@babel/parser@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==

"@babel/plugin-proposal-class-properties@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3"
Expand Down Expand Up @@ -375,6 +422,22 @@
debug "^4.1.0"
globals "^11.1.0"

"@babel/traverse@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b"
integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.3"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.3"
"@babel/types" "^7.23.3"
debug "^4.1.0"
globals "^11.1.0"

"@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":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
Expand All @@ -384,6 +447,15 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"

"@babel/types@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598"
integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"

"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
Expand Down

0 comments on commit 35c39d3

Please sign in to comment.