ember-eslint-parser 0.5.9 → 0.5.11
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.
- package/package.json +4 -3
- package/src/parser/gjs-gts-parser.js +135 -3
- package/src/parser/ts-patch.js +52 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-eslint-parser",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@babel/eslint-parser": "^7.23.10",
|
|
22
|
-
"@glimmer/syntax": "
|
|
22
|
+
"@glimmer/syntax": ">= 0.92.0",
|
|
23
|
+
"@typescript-eslint/tsconfig-utils": "^8.38.0",
|
|
23
24
|
"content-tag": "^2.0.1",
|
|
24
25
|
"eslint-scope": "^7.2.2",
|
|
25
26
|
"html-tags": "^3.3.1",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"fs-extra": "^11.2.0",
|
|
44
45
|
"prettier": "^3.2.5",
|
|
45
46
|
"publint": "^0.2.6",
|
|
46
|
-
"release-plan": "^0.
|
|
47
|
+
"release-plan": "^0.16.0",
|
|
47
48
|
"typescript": "^5.3.3",
|
|
48
49
|
"vite": "^5.0.12",
|
|
49
50
|
"vitest": "^1.2.2"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const tsconfigUtils = require('@typescript-eslint/tsconfig-utils');
|
|
1
2
|
const babelParser = require('@babel/eslint-parser');
|
|
2
3
|
const { registerParsedFile } = require('../preprocessor/noop');
|
|
3
4
|
const {
|
|
@@ -15,8 +16,119 @@ const { transformForLint, preprocessGlimmerTemplates, convertAst } = require('./
|
|
|
15
16
|
* 3. preprocesses the templates info and prepares the Glimmer AST
|
|
16
17
|
* 4. converts the js/ts AST so that it includes the Glimmer AST at the right locations, replacing the original
|
|
17
18
|
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} tsconfigPath
|
|
22
|
+
* @param {string} rootDir
|
|
23
|
+
* @returns {boolean|undefined}
|
|
24
|
+
*/
|
|
25
|
+
function parseAllowJsFromTsconfig(tsconfigPath, rootDir) {
|
|
26
|
+
try {
|
|
27
|
+
const parserPath = require.resolve('@typescript-eslint/parser');
|
|
28
|
+
// eslint-disable-next-line n/no-unpublished-require
|
|
29
|
+
const tsPath = require.resolve('typescript', { paths: [parserPath] });
|
|
30
|
+
const ts = require(tsPath);
|
|
31
|
+
const parsed = tsconfigUtils.getParsedConfigFile(ts, tsconfigPath, rootDir);
|
|
32
|
+
return parsed?.options?.allowJs;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.warn('[ember-eslint-parser] Failed to parse tsconfig:', tsconfigPath, e);
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {Array<boolean|undefined>} values
|
|
42
|
+
* @param {string} source
|
|
43
|
+
* @returns {boolean|null}
|
|
44
|
+
*/
|
|
45
|
+
function resolveAllowJs(values, source) {
|
|
46
|
+
const filtered = values.filter((val) => typeof val !== 'undefined');
|
|
47
|
+
if (filtered.length > 0) {
|
|
48
|
+
const uniqueValues = [...new Set(filtered)];
|
|
49
|
+
if (uniqueValues.length > 1) {
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.warn(
|
|
52
|
+
`[ember-eslint-parser] Conflicting allowJs values in ${source}. Defaulting allowGjs to false.`
|
|
53
|
+
);
|
|
54
|
+
return false;
|
|
55
|
+
} else {
|
|
56
|
+
return uniqueValues[0];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Array<{getCompilerOptions?: Function}>|undefined} programs
|
|
64
|
+
* @returns {boolean|null}
|
|
65
|
+
*/
|
|
66
|
+
function getAllowJsFromPrograms(programs) {
|
|
67
|
+
if (!Array.isArray(programs) || programs.length === 0) return null;
|
|
68
|
+
const allowJsValues = programs
|
|
69
|
+
.map((p) => p.getCompilerOptions?.())
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.map((opts) => opts.allowJs);
|
|
72
|
+
return resolveAllowJs(allowJsValues, 'programs');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {boolean|object|undefined} projectService
|
|
77
|
+
* @returns {string|null}
|
|
78
|
+
*/
|
|
79
|
+
function getProjectServiceTsconfigPath(projectService) {
|
|
80
|
+
if (!projectService) return null;
|
|
81
|
+
|
|
82
|
+
// If projectService is true, use default behavior (nearest tsconfig.json, allowJs from config)
|
|
83
|
+
if (projectService === true) {
|
|
84
|
+
return 'tsconfig.json';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// If projectService is an object, handle ProjectServiceOptions
|
|
88
|
+
if (typeof projectService === 'object') {
|
|
89
|
+
if (typeof projectService.allowDefaultProject !== 'undefined') {
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.warn(
|
|
92
|
+
'[ember-eslint-parser] projectService.allowDefaultProject is specified. Behavior may differ depending on default project config.'
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return projectService.defaultProject ?? 'tsconfig.json';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Returns the resolved allowJs value based on priority: programs > projectService > project/tsconfig
|
|
103
|
+
*/
|
|
104
|
+
function getAllowJs(options) {
|
|
105
|
+
const allowJsFromPrograms = getAllowJsFromPrograms(options.programs);
|
|
106
|
+
if (allowJsFromPrograms !== null) return allowJsFromPrograms;
|
|
107
|
+
|
|
108
|
+
const rootDir = options.tsconfigRootDir || process.cwd();
|
|
109
|
+
|
|
110
|
+
const projectServiceTsconfigPath = getProjectServiceTsconfigPath(options.projectService);
|
|
111
|
+
if (projectServiceTsconfigPath) {
|
|
112
|
+
return parseAllowJsFromTsconfig(projectServiceTsconfigPath, rootDir);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let tsconfigPaths = [];
|
|
116
|
+
if (Array.isArray(options.project)) {
|
|
117
|
+
tsconfigPaths = options.project;
|
|
118
|
+
} else if (typeof options.project === 'string') {
|
|
119
|
+
tsconfigPaths = [options.project];
|
|
120
|
+
} else if (options.project) {
|
|
121
|
+
tsconfigPaths = ['tsconfig.json'];
|
|
122
|
+
}
|
|
123
|
+
if (tsconfigPaths.length > 0) {
|
|
124
|
+
const allowJsValues = tsconfigPaths.map((cfg) => parseAllowJsFromTsconfig(cfg, rootDir));
|
|
125
|
+
return resolveAllowJs(allowJsValues, 'project');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
18
131
|
/**
|
|
19
|
-
*
|
|
20
132
|
* @type {import('eslint').ParserModule}
|
|
21
133
|
*/
|
|
22
134
|
module.exports = {
|
|
@@ -26,7 +138,13 @@ module.exports = {
|
|
|
26
138
|
},
|
|
27
139
|
|
|
28
140
|
parseForESLint(code, options) {
|
|
29
|
-
|
|
141
|
+
const allowGjsWasSet = options.allowGjs !== undefined;
|
|
142
|
+
const allowGjs = allowGjsWasSet ? options.allowGjs : getAllowJs(options);
|
|
143
|
+
let actualAllowGjs;
|
|
144
|
+
// Only patch TypeScript if we actually need it.
|
|
145
|
+
if (options.programs || options.projectService || options.project) {
|
|
146
|
+
({ allowGjs: actualAllowGjs } = patchTs({ allowGjs }));
|
|
147
|
+
}
|
|
30
148
|
registerParsedFile(options.filePath);
|
|
31
149
|
let jsCode = code;
|
|
32
150
|
const info = transformForLint(code, options.filePath);
|
|
@@ -73,7 +191,21 @@ module.exports = {
|
|
|
73
191
|
result.isTypescript = isTypescript || useTypescript;
|
|
74
192
|
convertAst(result, preprocessedResult, visitorKeys);
|
|
75
193
|
if (result.services?.program) {
|
|
76
|
-
|
|
194
|
+
// Compare allowJs with the actual program's compiler options
|
|
195
|
+
const programAllowJs = result.services.program.getCompilerOptions?.()?.allowJs;
|
|
196
|
+
if (
|
|
197
|
+
!allowGjsWasSet &&
|
|
198
|
+
programAllowJs !== undefined &&
|
|
199
|
+
actualAllowGjs !== undefined &&
|
|
200
|
+
actualAllowGjs !== programAllowJs
|
|
201
|
+
) {
|
|
202
|
+
// eslint-disable-next-line no-console
|
|
203
|
+
console.warn(
|
|
204
|
+
'[ember-eslint-parser] allowJs does not match the actual program. Consider setting allowGjs explicitly.\n' +
|
|
205
|
+
` Current: ${allowGjs}, Program: ${programAllowJs}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
syncMtsGtsSourceFiles(result.services.program);
|
|
77
209
|
}
|
|
78
210
|
return { ...result, visitorKeys };
|
|
79
211
|
} catch (e) {
|
package/src/parser/ts-patch.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('node:fs');
|
|
|
2
2
|
const { transformForLint } = require('./transforms');
|
|
3
3
|
const { replaceRange } = require('./transforms');
|
|
4
4
|
|
|
5
|
-
let patchTs, replaceExtensions, syncMtsGtsSourceFiles, typescriptParser, isPatched;
|
|
5
|
+
let patchTs, replaceExtensions, syncMtsGtsSourceFiles, typescriptParser, isPatched, allowGjs;
|
|
6
6
|
|
|
7
7
|
try {
|
|
8
8
|
const parserPath = require.resolve('@typescript-eslint/parser');
|
|
@@ -10,23 +10,27 @@ try {
|
|
|
10
10
|
const tsPath = require.resolve('typescript', { paths: [parserPath] });
|
|
11
11
|
const ts = require(tsPath);
|
|
12
12
|
typescriptParser = require('@typescript-eslint/parser');
|
|
13
|
-
patchTs = function patchTs() {
|
|
14
|
-
if (isPatched) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
13
|
+
patchTs = function patchTs(options = {}) {
|
|
14
|
+
if (isPatched) return { allowGjs };
|
|
17
15
|
isPatched = true;
|
|
16
|
+
allowGjs = options.allowGjs !== undefined ? options.allowGjs : true;
|
|
18
17
|
const sys = { ...ts.sys };
|
|
19
18
|
const newSys = {
|
|
20
19
|
...ts.sys,
|
|
21
20
|
readDirectory(...args) {
|
|
22
21
|
const results = sys.readDirectory.call(this, ...args);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const gtsVirtuals = results
|
|
23
|
+
.filter((x) => x.endsWith('.gts'))
|
|
24
|
+
.map((f) => f.replace(/\.gts$/, '.mts'));
|
|
25
|
+
const gjsVirtuals = allowGjs
|
|
26
|
+
? results.filter((x) => x.endsWith('.gjs')).map((f) => f.replace(/\.gjs$/, '.mjs'))
|
|
27
|
+
: [];
|
|
28
|
+
return results.concat(gtsVirtuals, gjsVirtuals);
|
|
27
29
|
},
|
|
28
30
|
fileExists(fileName) {
|
|
29
|
-
|
|
31
|
+
const gtsExists = fs.existsSync(fileName.replace(/\.m?ts$/, '.gts'));
|
|
32
|
+
const gjsExists = allowGjs ? fs.existsSync(fileName.replace(/\.m?js$/, '.gjs')) : false;
|
|
33
|
+
return gtsExists || gjsExists || fs.existsSync(fileName);
|
|
30
34
|
},
|
|
31
35
|
readFile(fname) {
|
|
32
36
|
let fileName = fname;
|
|
@@ -38,25 +42,30 @@ try {
|
|
|
38
42
|
try {
|
|
39
43
|
content = fs.readFileSync(fileName).toString();
|
|
40
44
|
} catch {
|
|
41
|
-
|
|
45
|
+
if (fileName.match(/\.m?ts$/)) {
|
|
46
|
+
fileName = fileName.replace(/\.m?ts$/, '.gts');
|
|
47
|
+
} else if (allowGjs && fileName.match(/\.m?js$/)) {
|
|
48
|
+
fileName = fileName.replace(/\.m?js$/, '.gjs');
|
|
49
|
+
}
|
|
42
50
|
content = fs.readFileSync(fileName).toString();
|
|
43
51
|
}
|
|
44
|
-
if (fileName.endsWith('.gts')) {
|
|
52
|
+
if (fileName.endsWith('.gts') || (allowGjs && fileName.endsWith('.gjs'))) {
|
|
45
53
|
try {
|
|
46
54
|
content = transformForLint(content).output;
|
|
47
55
|
} catch (e) {
|
|
48
|
-
console.error('failed to transformForLint for gts processing');
|
|
56
|
+
console.error('failed to transformForLint for gts/gjs processing');
|
|
49
57
|
console.error(e);
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
if (
|
|
53
61
|
(!fileName.endsWith('.d.ts') && fileName.endsWith('.ts')) ||
|
|
54
|
-
fileName.endsWith('.gts')
|
|
62
|
+
fileName.endsWith('.gts') ||
|
|
63
|
+
(allowGjs && fileName.endsWith('.gjs'))
|
|
55
64
|
) {
|
|
56
65
|
try {
|
|
57
66
|
content = replaceExtensions(content);
|
|
58
67
|
} catch (e) {
|
|
59
|
-
console.error('failed to replace extensions for gts processing');
|
|
68
|
+
console.error('failed to replace extensions for gts/gjs processing');
|
|
60
69
|
console.error(e);
|
|
61
70
|
}
|
|
62
71
|
}
|
|
@@ -64,6 +73,7 @@ try {
|
|
|
64
73
|
},
|
|
65
74
|
};
|
|
66
75
|
ts.setSys(newSys);
|
|
76
|
+
return { allowGjs };
|
|
67
77
|
};
|
|
68
78
|
|
|
69
79
|
replaceExtensions = function replaceExtensions(code) {
|
|
@@ -88,35 +98,42 @@ try {
|
|
|
88
98
|
*/
|
|
89
99
|
syncMtsGtsSourceFiles = function syncMtsGtsSourceFiles(program) {
|
|
90
100
|
const sourceFiles = program.getSourceFiles();
|
|
91
|
-
|
|
92
|
-
// check for deleted
|
|
93
|
-
if (sourceFile.path.match(
|
|
94
|
-
const
|
|
95
|
-
|
|
101
|
+
function syncVirtualFile(sourceFile, ext, virtualExt, virtualFlag) {
|
|
102
|
+
// check for deleted files, need to remove virtual as well
|
|
103
|
+
if (sourceFile.path.match(new RegExp(`\\.m?${virtualExt}$`)) && sourceFile[virtualFlag]) {
|
|
104
|
+
const origFile = program.getSourceFile(
|
|
105
|
+
sourceFile.path.replace(new RegExp(`\\.m?${virtualExt}$`), `.${ext}`)
|
|
106
|
+
);
|
|
107
|
+
if (!origFile) {
|
|
96
108
|
sourceFile.version = null;
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
|
-
if (sourceFile.path.endsWith(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
if (sourceFile.path.endsWith(`.${ext}`)) {
|
|
112
|
+
let virtualSourceFile = program.getSourceFile(
|
|
113
|
+
sourceFile.path.replace(new RegExp(`\\.${ext}$`), `.${virtualExt}`)
|
|
114
|
+
);
|
|
115
|
+
if (!virtualSourceFile) {
|
|
116
|
+
virtualSourceFile = program.getSourceFile(
|
|
117
|
+
sourceFile.path.replace(new RegExp(`\\.${ext}$`), virtualExt === 'mts' ? '.ts' : '.js')
|
|
118
|
+
);
|
|
106
119
|
}
|
|
107
|
-
if (
|
|
120
|
+
if (virtualSourceFile) {
|
|
108
121
|
const keep = {
|
|
109
|
-
fileName:
|
|
110
|
-
path:
|
|
111
|
-
originalFileName:
|
|
112
|
-
resolvedPath:
|
|
113
|
-
impliedNodeFormat:
|
|
122
|
+
fileName: virtualSourceFile.fileName,
|
|
123
|
+
path: virtualSourceFile.path,
|
|
124
|
+
originalFileName: virtualSourceFile.originalFileName,
|
|
125
|
+
resolvedPath: virtualSourceFile.resolvedPath,
|
|
126
|
+
impliedNodeFormat: virtualSourceFile.impliedNodeFormat,
|
|
114
127
|
};
|
|
115
|
-
Object.assign(
|
|
116
|
-
|
|
128
|
+
Object.assign(virtualSourceFile, sourceFile, keep);
|
|
129
|
+
virtualSourceFile[virtualFlag] = true;
|
|
117
130
|
}
|
|
118
131
|
}
|
|
119
132
|
}
|
|
133
|
+
for (const sourceFile of sourceFiles) {
|
|
134
|
+
syncVirtualFile(sourceFile, 'gts', 'mts', 'isVirtualGts');
|
|
135
|
+
syncVirtualFile(sourceFile, 'gjs', 'mjs', 'isVirtualGjs');
|
|
136
|
+
}
|
|
120
137
|
};
|
|
121
138
|
} catch /* istanbul ignore next */ {
|
|
122
139
|
// typescript not available
|