Skip to content

Commit

Permalink
fix(typescript-estree): correctly account for trailing slash in… (#1205)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Nov 16, 2019
1 parent eb83af1 commit ba89168
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 18 deletions.
@@ -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
? currentLintOperationState.code
: oldReadFile(filePath, encoding);
if (fileContent) {
Expand Down
17 changes: 12 additions & 5 deletions packages/typescript-estree/src/create-program/shared.ts
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
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);
});
});

0 comments on commit ba89168

Please sign in to comment.