1+ const tsconfigUtils = require ( '@typescript-eslint/tsconfig-utils' ) ;
12const babelParser = require ( '@babel/eslint-parser' ) ;
23const { registerParsedFile } = require ( '../preprocessor/noop' ) ;
34const {
@@ -15,8 +16,119 @@ const { transformForLint, preprocessGlimmerTemplates, convertAst } = require('./
1516 * 3. preprocesses the templates info and prepares the Glimmer AST
1617 * 4. converts the js/ts AST so that it includes the Glimmer AST at the right locations, replacing the original
1718 */
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+
18131/**
19- *
20132 * @type {import('eslint').ParserModule }
21133 */
22134module . exports = {
@@ -26,7 +138,13 @@ module.exports = {
26138 } ,
27139
28140 parseForESLint ( code , options ) {
29- patchTs ( ) ;
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+ }
30148 registerParsedFile ( options . filePath ) ;
31149 let jsCode = code ;
32150 const info = transformForLint ( code , options . filePath ) ;
@@ -73,7 +191,21 @@ module.exports = {
73191 result . isTypescript = isTypescript || useTypescript ;
74192 convertAst ( result , preprocessedResult , visitorKeys ) ;
75193 if ( result . services ?. program ) {
76- syncMtsGtsSourceFiles ( result . services ?. program ) ;
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 ) ;
77209 }
78210 return { ...result , visitorKeys } ;
79211 } catch ( e ) {
0 commit comments