Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(typescript-estree): correctly account for trailing slash in includes #1205

Merged
merged 7 commits into from
Nov 16, 2019
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import debug from 'debug';
import fs from 'fs';
import path from 'path';
import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports
import { Extra } from '../parser-options';
import { WatchCompilerHostOfConfigFile } from './WatchCompilerHostOfConfigFile';
Expand Down Expand Up @@ -67,7 +66,7 @@ function saveWatchCallback(
fileName: string,
callback: ts.FileWatcherCallback,
): ts.FileWatcher => {
const normalizedFileName = getCanonicalFileName(path.normalize(fileName));
const normalizedFileName = getCanonicalFileName(fileName);
const watchers = ((): Set<ts.FileWatcherCallback> => {
let watchers = trackingMap.get(normalizedFileName);
if (!watchers) {
Expand Down Expand Up @@ -246,8 +245,7 @@ function createWatchProgram(
watchCompilerHost.readFile = (filePathIn, encoding): string | undefined => {
const filePath = getCanonicalFileName(filePathIn);
const fileContent =
path.normalize(filePath) ===
path.normalize(currentLintOperationState.filePath)
filePath === currentLintOperationState.filePath
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
? currentLintOperationState.code
: oldReadFile(filePath, encoding);
if (fileContent) {
Expand Down
17 changes: 12 additions & 5 deletions packages/typescript-estree/src/create-program/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@ const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions = {

// This narrows the type so we can be sure we're passing canonical names in the correct places
type CanonicalPath = string & { __brand: unknown };

// typescript doesn't provide a ts.sys implementation for browser environments
const useCaseSensitiveFileNames =
ts.sys !== undefined ? ts.sys.useCaseSensitiveFileNames : true;
const getCanonicalFileName = useCaseSensitiveFileNames
? (filePath: string): CanonicalPath =>
path.normalize(filePath) as CanonicalPath
: (filePath: string): CanonicalPath =>
path.normalize(filePath).toLowerCase() as CanonicalPath;
const correctPathCasing = useCaseSensitiveFileNames
? (filePath: string): string => filePath
: (filePath: string): string => filePath.toLowerCase();

function getCanonicalFileName(filePath: string): CanonicalPath {
let normalized = path.normalize(filePath);
if (normalized.endsWith('/')) {
normalized = normalized.substr(0, normalized.length - 1);
}
return correctPathCasing(normalized) as CanonicalPath;
}

function getTsconfigPath(tsconfigPath: string, extra: Extra): CanonicalPath {
return getCanonicalFileName(
Expand Down
57 changes: 48 additions & 9 deletions packages/typescript-estree/tests/lib/persistentParse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@ import path from 'path';
import tmp from 'tmp';
import { clearCaches, parseAndGenerateServices } from '../../src/parser';

const tsConfigExcludeBar = {
include: ['src'],
exclude: ['./src/bar.ts'],
};
const tsConfigIncludeAll = {
include: ['src'],
exclude: [],
};
const CONTENTS = {
foo: 'console.log("foo")',
bar: 'console.log("bar")',
Expand Down Expand Up @@ -69,7 +61,10 @@ function parseFile(filename: 'foo' | 'bar' | 'baz/bar', tmpDir: string): void {
});
}

describe('persistent lint session', () => {
function baseTests(
tsConfigExcludeBar: Record<string, unknown>,
tsConfigIncludeAll: Record<string, unknown>,
): void {
it('parses both files successfully when included', () => {
const PROJECT_DIR = setup(tsConfigIncludeAll);

Expand Down Expand Up @@ -175,4 +170,48 @@ describe('persistent lint session', () => {
});

// TODO - support the complex monorepo case with a tsconfig with no include/exclude
}

describe('persistent parse', () => {
describe('includes not ending in a slash', () => {
const tsConfigExcludeBar = {
include: ['src'],
exclude: ['./src/bar.ts'],
};
const tsConfigIncludeAll = {
include: ['src'],
exclude: [],
};

baseTests(tsConfigExcludeBar, tsConfigIncludeAll);
});

/*
If the includes ends in a slash, typescript will ask for watchers ending in a slash.
These tests ensure the normalisation code works as expected in this case.
*/
describe('includes ending in a slash', () => {
const tsConfigExcludeBar = {
include: ['src/'],
exclude: ['./src/bar.ts'],
};
const tsConfigIncludeAll = {
include: ['src/'],
exclude: [],
};

baseTests(tsConfigExcludeBar, tsConfigIncludeAll);
});

/*
If there is no includes, then typescript will ask for a slightly different set of watchers.
*/
describe('tsconfig with no includes / files', () => {
const tsConfigExcludeBar = {
exclude: ['./src/bar.ts'],
};
const tsConfigIncludeAll = {};

baseTests(tsConfigExcludeBar, tsConfigIncludeAll);
});
});