From dc1424298493f1ee2e5a357e103c504655546d61 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 21 Feb 2023 16:54:58 -0500 Subject: [PATCH] fix(typescript-estree): check for relative/root paths in printing file path errors (#6491) * fix(typescript-estree): check for relative/root paths in printing file path errors * Missing slash in a test case * Update packages/typescript-estree/src/create-program/createProjectProgram.ts Co-authored-by: Armano * Post-suggestion format --------- Co-authored-by: Armano --- .../create-program/createProjectProgram.ts | 20 +-- .../src/create-program/describeFilePath.ts | 31 ++++ .../describeFilePath.test.ts.snap | 169 ++++++++++++++++++ .../tests/lib/describeFilePath.test.ts | 41 +++++ 4 files changed, 249 insertions(+), 12 deletions(-) create mode 100644 packages/typescript-estree/src/create-program/describeFilePath.ts create mode 100644 packages/typescript-estree/tests/lib/__snapshots__/describeFilePath.test.ts.snap create mode 100644 packages/typescript-estree/tests/lib/describeFilePath.test.ts diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index 77a3bec2141..f60ae9cbfd5 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -4,6 +4,7 @@ import * as ts from 'typescript'; import { firstDefined } from '../node-utils'; import type { ParseSettings } from '../parseSettings'; +import { describeFilePath } from './describeFilePath'; import { getWatchProgramsForProjects } from './getWatchProgramsForProjects'; import type { ASTAndProgram } from './shared'; import { getAstFromProgram } from './shared'; @@ -40,19 +41,14 @@ function createProjectProgram( return astAndProgram; } - const describeFilePath = (filePath: string): string => { - const relative = path.relative( - parseSettings.tsconfigRootDir || process.cwd(), - filePath, - ); - if (parseSettings.tsconfigRootDir) { - return `/${relative}`; - } - return `/${relative}`; - }; + const describeProjectFilePath = (projectFile: string): string => + describeFilePath(projectFile, parseSettings.tsconfigRootDir); - const describedFilePath = describeFilePath(parseSettings.filePath); - const relativeProjects = parseSettings.projects.map(describeFilePath); + const describedFilePath = describeFilePath( + parseSettings.filePath, + parseSettings.tsconfigRootDir, + ); + const relativeProjects = parseSettings.projects.map(describeProjectFilePath); const describedPrograms = relativeProjects.length === 1 ? relativeProjects[0] diff --git a/packages/typescript-estree/src/create-program/describeFilePath.ts b/packages/typescript-estree/src/create-program/describeFilePath.ts new file mode 100644 index 00000000000..83e1338d91f --- /dev/null +++ b/packages/typescript-estree/src/create-program/describeFilePath.ts @@ -0,0 +1,31 @@ +import path from 'path'; + +export function describeFilePath( + filePath: string, + tsconfigRootDir: string, +): string { + // If the TSConfig root dir is a parent of the filePath, use + // `` as a prefix for the path. + const relative = path.relative(tsconfigRootDir, filePath); + if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) { + return `/${relative}`; + } + + // Root-like Mac/Linux (~/*, ~*) or Windows (C:/*, /) paths that aren't + // relative to the TSConfig root dir should be fully described. + // This avoids strings like /../../../../repo/file.ts. + // https://github.com/typescript-eslint/typescript-eslint/issues/6289 + if (/^[(\w+:)\\/~]/.test(filePath)) { + return filePath; + } + + // Similarly, if the relative path would contain a lot of ../.., then + // ignore it and print the file path directly. + if (/\.\.[/\\]\.\./.test(relative)) { + return filePath; + } + + // Lastly, since we've eliminated all special cases, we know the cleanest + // path to print is probably the prefixed relative one. + return `/${relative}`; +} diff --git a/packages/typescript-estree/tests/lib/__snapshots__/describeFilePath.test.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/describeFilePath.test.ts.snap new file mode 100644 index 00000000000..702bde4b5e7 --- /dev/null +++ b/packages/typescript-estree/tests/lib/__snapshots__/describeFilePath.test.ts.snap @@ -0,0 +1,169 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./elsewhere/repo/file.ts 1`] = `"./elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./elsewhere/repo/nested/file.ts 1`] = `"./elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./repos/file.ts 1`] = `"/../file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./repos/other/file.ts 1`] = `"/../other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./repos/repo/file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ./repos/repo/nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /elsewhere/repo/file.ts 1`] = `"/elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /elsewhere/repo/nested/file.ts 1`] = `"/elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /repos/repo/file.ts 1`] = `"/repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath /repos/repo/nested/file.ts 1`] = `"/repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/file.ts 1`] = `"~/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/nested/file.ts 1`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/nested/file.ts 2`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/other/nested/path/to/file.ts 1`] = `"~/other/nested/path/to/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/other/repo/file.ts 1`] = `"~/other/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/repos/file.ts 1`] = `"~/repos/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/repos/other/file.ts 1`] = `"~/repos/other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/repos/other/nested/file.ts 1`] = `"~/repos/other/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/repos/repo/file.ts 1`] = `"~/repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ~/repos/repo/nested/file.ts 1`] = `"~/repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath A:/file.ts 1`] = `"A:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath A:/nested/file.ts 1`] = `"A:/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath ABC:/file.ts 1`] = `"ABC:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath C:/file.ts 1`] = `"C:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath file.ts 1`] = `"file.ts"`; + +exports[`describeFilePath tsconfigRootDir ./repos/repo filePath nested/file.ts 1`] = `"nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./elsewhere/repo/file.ts 1`] = `"./elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./elsewhere/repo/nested/file.ts 1`] = `"./elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./repos/file.ts 1`] = `"./repos/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./repos/other/file.ts 1`] = `"./repos/other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./repos/repo/file.ts 1`] = `"./repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ./repos/repo/nested/file.ts 1`] = `"./repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /elsewhere/repo/file.ts 1`] = `"/elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /elsewhere/repo/nested/file.ts 1`] = `"/elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /repos/repo/file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath /repos/repo/nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/file.ts 1`] = `"~/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/nested/file.ts 1`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/nested/file.ts 2`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/other/nested/path/to/file.ts 1`] = `"~/other/nested/path/to/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/other/repo/file.ts 1`] = `"~/other/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/repos/file.ts 1`] = `"~/repos/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/repos/other/file.ts 1`] = `"~/repos/other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/repos/other/nested/file.ts 1`] = `"~/repos/other/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/repos/repo/file.ts 1`] = `"~/repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ~/repos/repo/nested/file.ts 1`] = `"~/repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath A:/file.ts 1`] = `"A:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath A:/nested/file.ts 1`] = `"A:/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath ABC:/file.ts 1`] = `"ABC:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath C:/file.ts 1`] = `"C:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath file.ts 1`] = `"file.ts"`; + +exports[`describeFilePath tsconfigRootDir /repos/repo filePath nested/file.ts 1`] = `"nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./elsewhere/repo/file.ts 1`] = `"./elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./elsewhere/repo/nested/file.ts 1`] = `"./elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./repos/file.ts 1`] = `"./repos/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./repos/other/file.ts 1`] = `"./repos/other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./repos/repo/file.ts 1`] = `"./repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ./repos/repo/nested/file.ts 1`] = `"./repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /elsewhere/repo/file.ts 1`] = `"/elsewhere/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /elsewhere/repo/nested/file.ts 1`] = `"/elsewhere/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /repos/repo/file.ts 1`] = `"/repos/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath /repos/repo/nested/file.ts 1`] = `"/repos/repo/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/file.ts 1`] = `"~/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/nested/file.ts 1`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/nested/file.ts 2`] = `"~/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/other/nested/path/to/file.ts 1`] = `"~/other/nested/path/to/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/other/repo/file.ts 1`] = `"~/other/repo/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/repos/file.ts 1`] = `"~/repos/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/repos/other/file.ts 1`] = `"~/repos/other/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/repos/other/nested/file.ts 1`] = `"~/repos/other/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/repos/repo/file.ts 1`] = `"/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ~/repos/repo/nested/file.ts 1`] = `"/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath A:/file.ts 1`] = `"A:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath A:/nested/file.ts 1`] = `"A:/nested/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath ABC:/file.ts 1`] = `"ABC:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath C:/file.ts 1`] = `"C:/file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath file.ts 1`] = `"file.ts"`; + +exports[`describeFilePath tsconfigRootDir ~/repos/repo filePath nested/file.ts 1`] = `"nested/file.ts"`; diff --git a/packages/typescript-estree/tests/lib/describeFilePath.test.ts b/packages/typescript-estree/tests/lib/describeFilePath.test.ts new file mode 100644 index 00000000000..0a23ddf8af3 --- /dev/null +++ b/packages/typescript-estree/tests/lib/describeFilePath.test.ts @@ -0,0 +1,41 @@ +import { describeFilePath } from '../../src/create-program/describeFilePath'; + +describe('describeFilePath', () => { + describe.each(['./repos/repo', '/repos/repo', '~/repos/repo'])( + 'tsconfigRootDir %s', + tsconfigRootDir => { + test.each([ + './elsewhere/repo/file.ts', + './elsewhere/repo/nested/file.ts', + './repos/file.ts', + './repos/other/file.ts', + './repos/repo/file.ts', + './repos/repo/nested/file.ts', + '/elsewhere/repo/file.ts', + '/elsewhere/repo/nested/file.ts', + '/file.ts', + '/nested/file.ts', + '/repos/repo/file.ts', + '/repos/repo/nested/file.ts', + '~/file.ts', + '~/nested/file.ts', + '~/nested/file.ts', + '~/other/nested/path/to/file.ts', + '~/other/repo/file.ts', + '~/repos/file.ts', + '~/repos/other/file.ts', + '~/repos/other/nested/file.ts', + '~/repos/repo/file.ts', + '~/repos/repo/nested/file.ts', + 'A:/file.ts', + 'A:/nested/file.ts', + 'ABC:/file.ts', + 'C:/file.ts', + 'file.ts', + 'nested/file.ts', + ])('filePath %s', filePath => { + expect(describeFilePath(filePath, tsconfigRootDir)).toMatchSnapshot(); + }); + }, + ); +});