Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.8k
fix(typescript-estree): adds support for project services using extended config files#9306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Merged
JoshuaKGoldberg merged 22 commits intotypescript-eslint:v8fromhigherorderfunctor:fix/9205-project-service-extends-issueJul 28, 2024
Uh oh!
There was an error while loading.Please reload this page.
Merged
Changes fromall commits
Commits
Show all changes
22 commits Select commitHold shift + click to select a range
69aa3c9 refactor(typescript-estree): refactors default project config and add…
higherorderfunctord32c1e5 test(typescript-estree): fix original failing unit tests
higherorderfunctor148a9d0 chore(typescript-estree): cleans up PR submission
higherorderfunctor8bc4b40 chore: merge remote-tracking branch 'upstream/v8' into fix/9205-proje…
higherorderfunctorb6718a1 chore(typescript-estree): fixes test names to use the new function name
higherorderfunctorbde1a26 test(typescript-estree): adds additional tests for coverage
higherorderfunctor3315345 test(typescript-estree): fixes test for windows
higherorderfunctor2859ef9 test(typescript-estree): fixes test name typo
higherorderfunctorf01fe0e chore(typescript-estree): minor cleanup to comments and variables
higherorderfunctor73e2c78 style(typescript-estree): unifies how compiler options are defined in…
higherorderfunctor4b8c010 chore(typescript-estree): updates create-program/getParsedConfigFile.…
higherorderfunctor4f0e2de chore: merge upstream/v8
higherorderfunctorabd0095 test(typescript-estree): adds initial getParsedConfigFile tests
higherorderfunctorc563b8d test(typescript-estree): updates createProjectService tests
higherorderfunctor4d043be chore: merge upstream/v8
higherorderfunctorc7181b9 test(typescript-estree): cleans up PR
higherorderfunctor6d23cb1 Merge remote-tracking branch 'upstream/v8' into fix/9205-project-serv…
higherorderfunctor45551dc test(typescript-estree): fixes snapshot tests
higherorderfunctord5f06c5 style(typescript-estree): applies suggestions from code review
higherorderfunctor1dd15bf style(typescript-estree): applies manual suggestions from code review
higherorderfunctorac8c9bc fixes(typescript-estree): fixes linting errors
higherorderfunctor4478aea Update packages/typescript-estree/src/create-program/createProjectSer…
JoshuaKGoldbergFile filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
35 changes: 12 additions & 23 deletionspackages/typescript-estree/src/create-program/createProjectService.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletionspackages/typescript-estree/src/create-program/getParsedConfigFile.ts
higherorderfunctor marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import * as fs from 'fs'; | ||
| import * as path from 'path'; | ||
| import type * as ts from 'typescript/lib/tsserverlibrary'; | ||
| import { CORE_COMPILER_OPTIONS } from './shared'; | ||
| /** | ||
| * Utility offered by parser to help consumers parse a config file. | ||
| * | ||
| * @param configFile the path to the tsconfig.json file, relative to `projectDirectory` | ||
| * @param projectDirectory the project directory to use as the CWD, defaults to `process.cwd()` | ||
| */ | ||
| function getParsedConfigFile( | ||
| tsserver: typeof ts, | ||
| configFile: string, | ||
| projectDirectory?: string, | ||
| ): ts.ParsedCommandLine { | ||
higherorderfunctor marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (tsserver.sys === undefined) { | ||
| throw new Error( | ||
| '`getParsedConfigFile` is only supported in a Node-like environment.', | ||
| ); | ||
| } | ||
| const parsed = tsserver.getParsedCommandLineOfConfigFile( | ||
| configFile, | ||
| CORE_COMPILER_OPTIONS, | ||
| { | ||
| onUnRecoverableConfigFileDiagnostic: diag => { | ||
| throw new Error(formatDiagnostics([diag])); // ensures that `parsed` is defined. | ||
| }, | ||
| fileExists: fs.existsSync, | ||
| getCurrentDirectory, | ||
| readDirectory: tsserver.sys.readDirectory, | ||
| readFile: file => fs.readFileSync(file, 'utf-8'), | ||
| useCaseSensitiveFileNames: tsserver.sys.useCaseSensitiveFileNames, | ||
| }, | ||
| ); | ||
| if (parsed?.errors.length) { | ||
| throw new Error(formatDiagnostics(parsed.errors)); | ||
| } | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| return parsed!; | ||
| function getCurrentDirectory(): string { | ||
| return projectDirectory ? path.resolve(projectDirectory) : process.cwd(); | ||
| } | ||
| function formatDiagnostics(diagnostics: ts.Diagnostic[]): string | undefined { | ||
| return tsserver.formatDiagnostics(diagnostics, { | ||
| getCanonicalFileName: f => f, | ||
| getCurrentDirectory, | ||
| getNewLine: () => '\n', | ||
| }); | ||
| } | ||
| } | ||
| export { getParsedConfigFile }; | ||
45 changes: 5 additions & 40 deletionspackages/typescript-estree/src/create-program/useProvidedPrograms.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
45 changes: 23 additions & 22 deletionspackages/typescript-estree/tests/lib/createProjectService.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,16 @@ | ||
| import debug from 'debug'; | ||
higherorderfunctor marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| import * as ts from 'typescript'; | ||
| const mockGetParsedConfigFile = jest.fn(); | ||
| const mockSetCompilerOptionsForInferredProjects = jest.fn(); | ||
| const mockSetHostConfiguration = jest.fn(); | ||
| jest.mock('../../src/create-program/getParsedConfigFile', () => ({ | ||
| getParsedConfigFile: mockGetParsedConfigFile, | ||
| })); | ||
| jest.mock('typescript/lib/tsserverlibrary', () => ({ | ||
| ...jest.requireActual('typescript/lib/tsserverlibrary'), | ||
| server: { | ||
| ...jest.requireActual('typescript/lib/tsserverlibrary').server, | ||
| ProjectService: class { | ||
| @@ -33,6 +34,11 @@ jest.mock('typescript/lib/tsserverlibrary', () => ({ | ||
| }, | ||
| })); | ||
| const { | ||
| createProjectService, | ||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | ||
| } = require('../../src/create-program/createProjectService'); | ||
| describe('createProjectService', () => { | ||
| afterEach(() => { | ||
| jest.resetAllMocks(); | ||
| @@ -51,18 +57,9 @@ describe('createProjectService', () => { | ||
| expect(settings.allowDefaultProject).toBeUndefined(); | ||
| }); | ||
| it('throws an error when options.defaultProject is set and getParsedConfigFile throws a diagnostic error', () => { | ||
| mockGetParsedConfigFile.mockImplementation(() => { | ||
| throw new Error('./tsconfig.json(1,1): error TS1234: Oh no!'); | ||
| }); | ||
| expect(() => | ||
| @@ -78,9 +75,11 @@ describe('createProjectService', () => { | ||
| ); | ||
| }); | ||
| it('throws an error when options.defaultProject is set and getParsedConfigFile throws an environment error', () => { | ||
| mockGetParsedConfigFile.mockImplementation(() => { | ||
| throw new Error( | ||
| '`getParsedConfigFile` is only supported in a Node-like environment.', | ||
| ); | ||
| }); | ||
| expect(() => | ||
| @@ -91,12 +90,14 @@ describe('createProjectService', () => { | ||
| }, | ||
| undefined, | ||
| ), | ||
| ).toThrow( | ||
| "Could not read default project './tsconfig.json': `getParsedConfigFile` is only supported in a Node-like environment.", | ||
| ); | ||
| }); | ||
| it('uses the default projects compiler options when options.defaultProject is set andgetParsedConfigFile succeeds', () => { | ||
| const compilerOptions = { strict: true }; | ||
| mockGetParsedConfigFile.mockReturnValue({options:compilerOptions }); | ||
| const { service } = createProjectService( | ||
| { | ||
112 changes: 112 additions & 0 deletionspackages/typescript-estree/tests/lib/getParsedConfigFile.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| import path from 'node:path'; | ||
| import * as ts from 'typescript'; | ||
| import { getParsedConfigFile } from '../../src/create-program/getParsedConfigFile'; | ||
| const mockGetParsedCommandLineOfConfigFile = jest.fn(); | ||
| const mockTsserver: typeof ts = { | ||
| sys: {} as ts.System, | ||
| formatDiagnostics: ts.formatDiagnostics, | ||
| getParsedCommandLineOfConfigFile: mockGetParsedCommandLineOfConfigFile, | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| } as any; | ||
| describe('getParsedConfigFile', () => { | ||
| afterEach(() => { | ||
| jest.resetAllMocks(); | ||
| }); | ||
| it('throws an error when tsserver.sys is undefined', () => { | ||
| expect(() => | ||
| getParsedConfigFile({} as typeof ts, './tsconfig.json'), | ||
| ).toThrow( | ||
| '`getParsedConfigFile` is only supported in a Node-like environment.', | ||
| ); | ||
| }); | ||
| it('uses the cwd as the default project directory', () => { | ||
| getParsedConfigFile(mockTsserver, './tsconfig.json'); | ||
| expect(mockGetParsedCommandLineOfConfigFile).toHaveBeenCalledTimes(1); | ||
| const [_configFileName, _optionsToExtend, host] = | ||
| mockGetParsedCommandLineOfConfigFile.mock.calls[0]; | ||
| expect(host.getCurrentDirectory()).toBe(process.cwd()); | ||
| }); | ||
| it('resolves a relative project directory when passed', () => { | ||
| getParsedConfigFile( | ||
| mockTsserver, | ||
| './tsconfig.json', | ||
| path.relative('./', path.dirname(__filename)), | ||
| ); | ||
| expect(mockGetParsedCommandLineOfConfigFile).toHaveBeenCalledTimes(1); | ||
| const [_configFileName, _optionsToExtend, host] = | ||
| mockGetParsedCommandLineOfConfigFile.mock.calls[0]; | ||
| expect(host.getCurrentDirectory()).toBe(path.dirname(__filename)); | ||
| }); | ||
| it('resolves an absolute project directory when passed', () => { | ||
| getParsedConfigFile(mockTsserver, './tsconfig.json', __dirname); | ||
| expect(mockGetParsedCommandLineOfConfigFile).toHaveBeenCalledTimes(1); | ||
| const [_configFileName, _optionsToExtend, host] = | ||
| mockGetParsedCommandLineOfConfigFile.mock.calls[0]; | ||
| expect(host.getCurrentDirectory()).toBe(__dirname); | ||
| }); | ||
| it('throws a diagnostic error when getParsedCommandLineOfConfigFile returns an error', () => { | ||
| mockGetParsedCommandLineOfConfigFile.mockReturnValue({ | ||
| errors: [ | ||
| { | ||
| category: ts.DiagnosticCategory.Error, | ||
| code: 1234, | ||
| file: ts.createSourceFile('./tsconfig.json', '', { | ||
| languageVersion: ts.ScriptTarget.Latest, | ||
| }), | ||
| start: 0, | ||
| length: 0, | ||
| messageText: 'Oh no!', | ||
| }, | ||
| ] satisfies ts.Diagnostic[], | ||
| }); | ||
| expect(() => getParsedConfigFile(mockTsserver, './tsconfig.json')).toThrow( | ||
| /.+ error TS1234: Oh no!/, | ||
| ); | ||
| }); | ||
| it('throws a diagnostic error when getParsedCommandLineOfConfigFile throws an error', () => { | ||
| mockGetParsedCommandLineOfConfigFile.mockImplementation( | ||
| ( | ||
| ...[_configFileName, _optionsToExtend, host]: Parameters< | ||
| typeof ts.getParsedCommandLineOfConfigFile | ||
| > | ||
| ) => { | ||
| return host.onUnRecoverableConfigFileDiagnostic({ | ||
| category: ts.DiagnosticCategory.Error, | ||
| code: 1234, | ||
| file: ts.createSourceFile('./tsconfig.json', '', { | ||
| languageVersion: ts.ScriptTarget.Latest, | ||
| }), | ||
| start: 0, | ||
| length: 0, | ||
| messageText: 'Oh no!', | ||
| } satisfies ts.Diagnostic); | ||
| }, | ||
| ); | ||
| expect(() => getParsedConfigFile(mockTsserver, './tsconfig.json')).toThrow( | ||
| /.+ error TS1234: Oh no!/, | ||
| ); | ||
| }); | ||
| it('uses compiler options when parsing a config file succeeds', () => { | ||
| const parsedConfigFile = { | ||
| options: { strict: true }, | ||
| raw: { compilerOptions: { strict: true } }, | ||
| errors: [], | ||
| }; | ||
| mockGetParsedCommandLineOfConfigFile.mockReturnValue(parsedConfigFile); | ||
| expect(getParsedConfigFile(mockTsserver, 'tsconfig.json')).toEqual( | ||
| expect.objectContaining(parsedConfigFile), | ||
| ); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.