Skip to content

Commit

Permalink
fix: Update source bug when using eslint in watch mode
Browse files Browse the repository at this point in the history
  • Loading branch information
roman.vasilev committed Aug 28, 2018
1 parent e2e7693 commit 481c177
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 68 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"test:r": "npm run mocha -- src/*.spec.ts",
"mocha": "node -r ts-node/register/transpile-only node_modules/mocha/bin/_mocha --timeout 5000",
"test:w": "npm run mocha -- --watch-extensions ts --watch src/**/*.spec.ts",
"test:d": "node --inspect-brk -r ts-node/register/transpile-only node_modules/mocha/bin/_mocha --no-timeouts src/**/*.spec.ts",
"test:d": "node --inspect-brk -r ts-node/register/transpile-only node_modules/mocha/bin/_mocha --no-timeouts --watch-extensions ts --watch src/**/*.spec.ts",
"tscheck": "echo tscheck... && tsc --noEmit",
"tscheck:w": "npm run tscheck -- --watch",
"tsclint": "tsc --noEmit --pretty --strict --forceConsistentCasingInFileNames --noImplicitReturns --noImplicitThis --noUnusedLocals --noUnusedParameters",
Expand Down
52 changes: 0 additions & 52 deletions src/create-program.ts

This file was deleted.

53 changes: 44 additions & 9 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as ts from 'typescript';
import * as assert from 'assert';
import * as lib from './index';
import mockFs = require('mock-fs');

const root = process.cwd();

Expand Down Expand Up @@ -28,13 +30,13 @@ describe('tsconfig-files', () => {

it('get source file which are not in files', () => {
const testFile = `${root}/test-project/file.spec.ts`;
const sourceFile = service.getSourceFile(testFile);
const sourceFile = service.getSourceFile(testFile, undefined);
assert(sourceFile);
});

it('typescript checker (file which is not defined in tsconfig)', () => {
const testFile = `${root}/test-project/file.spec.ts`;
const sourceFile = service.getSourceFile(testFile);
const sourceFile = service.getSourceFile(testFile, undefined);
const checker = service.getProgram().getTypeChecker();
const [itstmt] = sourceFile.statements.filter(x => x.getText() === `it('example test');`);
const itid = (itstmt as any).expression.expression;
Expand All @@ -58,7 +60,7 @@ describe('create service', () => {
const testFile = `${root}/test-project/errors.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 2);
assert.equal(diagnostics[0].messageText, `Type '1' is not assignable to type 'string'.`);
assert.equal(diagnostics[1].messageText, `Type '"foo"' is not assignable to type 'number'.`);
Expand All @@ -68,48 +70,81 @@ describe('create service', () => {
const testFile = `${root}/test-project/number.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

it('built in', () => {
const testFile = `${root}/test-project/builtin.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

it('types', () => {
const testFile = `${root}/test-project/types.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

it('decorator', () => {
const testFile = `${root}/test-project/decorator.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

it('global types', () => {
const testFile = `${root}/test-project/global-types.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

it('date', () => {
const testFile = `${root}/test-project/date.ts`;
const sourceFile = service.getProgram().getSourceFile(testFile);
assert(sourceFile);
const diagnostics = service.getDiagnostics(testFile);
const diagnostics = service.getDiagnostics(testFile, undefined);
assert.equal(diagnostics.length, 0);
});

});

describe('source file of changed file', () => {

const testFile = `${root}/test-project/amok.ts`;
const configFile = `${root}/test-project/tsconfig-empty.json`;
let sourceFile: ts.SourceFile;

before(() => {
mockFs({
[testFile]: 'const x = 1',
[configFile]: '{}',
});
service = lib.createService({ configFile });
sourceFile = service.getProgram().getSourceFile(testFile);
});

after(() => {
mockFs.restore();
});

it('smoke', () => {
assert(service);
assert(sourceFile);
});

it('changes should be reflected', () => {
mockFs({ [testFile]: 'let x = 2' });
sourceFile = service.getSourceFile(testFile, 'let x = 2');
assert(sourceFile);
assert.equal(sourceFile.getText(), 'let x = 2');
});

});
60 changes: 54 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import * as ts from 'typescript';
import { createProgram } from './create-program';
import { getSourceFile } from './get-source-file';
import { dirname, resolve } from 'path';
import { existsSync, readFileSync } from 'fs';

type createServiceOptions = {
configFile: string;
compilerOptions?: ts.CompilerOptions;
projectDirectory?: string;
};

export function createService({ compilerOptions, configFile }: createServiceOptions) {
let { program, host } = createProgram({ configFile, compilerOptions });
let { program, compilerHost } = createTypescriptServices({ configFile, compilerOptions });
const api = {
getProgram: () => program,
getSourceFile: (fileName: string, sourceText?: string) => {
// todo: fix me optimization sourceText is not used
getSourceFile: (fileName: string, sourceText: string) => {
let sourceFile = program.getSourceFile(fileName);
if (sourceFile === undefined) {
fileName = fileName.replace(/\\/g, '/');
const rootFileNames = [...program.getRootFileNames(), fileName];
program = ts.createProgram(rootFileNames, program.getCompilerOptions(), host, program);
program = ts.createProgram(rootFileNames, program.getCompilerOptions(), compilerHost, program);
sourceFile = program.getSourceFile(fileName);
} else if (sourceFile.text !== sourceText) {
program = ts.createProgram(program.getRootFileNames(), program.getCompilerOptions(), compilerHost, program);
sourceFile = program.getSourceFile(fileName);
}
return sourceFile || getSourceFile(program, fileName, sourceText);
},
getDiagnostics: (fileName: string, sourceText?: string) => {
getDiagnostics: (fileName: string, sourceText: string) => {
const sourceFile = api.getSourceFile(fileName, sourceText);
return [
...program.getSyntacticDiagnostics(sourceFile),
Expand All @@ -31,3 +36,46 @@ export function createService({ compilerOptions, configFile }: createServiceOpti
};
return api;
}

function createTypescriptServices({ configFile, projectDirectory = dirname(configFile), compilerOptions = {} }: createServiceOptions) {
const { config, error } = ts.readConfigFile(configFile, ts.sys.readFile);
if (error !== undefined) {
throw new Error(ts.formatDiagnostics([error], {
getCanonicalFileName: f => f,
getCurrentDirectory: process.cwd,
getNewLine: () => '\n',
}));
}
const parseConfigHost: ts.ParseConfigHost = {
fileExists: (path: string) => {
return existsSync(path);
},
readDirectory: ts.sys.readDirectory,
readFile: (file) => {
return readFileSync(file, 'utf8');
},
useCaseSensitiveFileNames: false,
};
config.compilerOptions = { ...(config.compilerOptions || {}), ...compilerOptions };
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, resolve(projectDirectory), {
noEmit: true,
sourceMap: false,
inlineSources: false,
inlineSourceMap: false,
});
if (parsed.errors !== undefined) {
// ignore warnings and 'TS18003: No inputs were found in config file ...'
const errors = parsed.errors.filter(d => d.category === ts.DiagnosticCategory.Error && d.code !== 18003);
if (errors.length !== 0) {
throw new Error(ts.formatDiagnostics(errors, {
getCanonicalFileName: f => f,
getCurrentDirectory: process.cwd,
getNewLine: () => '\n',
}));
}
}
const compilerHost = ts.createCompilerHost(parsed.options, true);
const program = ts.createProgram(parsed.fileNames, parsed.options, compilerHost);

return { program, compilerHost };
}
5 changes: 5 additions & 0 deletions test-project/tsconfig-empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files": [
"file.ts"
]
}

0 comments on commit 481c177

Please sign in to comment.