diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index d4518a4cbc..8446b19e0f 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -166,17 +166,32 @@ export function readCommandLineAndConfiguration( }; } +function getFormatDiagnosticsHost(options?: api.CompilerOptions): ts.FormatDiagnosticsHost { + const basePath = options ? options.basePath : undefined; + return { + getCurrentDirectory: () => basePath || ts.sys.getCurrentDirectory(), + // We need to normalize the path separators here because by default, TypeScript + // compiler hosts use posix canonical paths. In order to print consistent diagnostics, + // we also normalize the paths. + getCanonicalFileName: fileName => fileName.replace(/\\/g, '/'), + getNewLine: () => { + // Manually determine the proper new line string based on the passed compiler + // options. There is no public TypeScript function that returns the corresponding + // new line string. see: https://github.com/Microsoft/TypeScript/issues/29581 + if (options && options.newLine !== undefined) { + return options.newLine === ts.NewLineKind.LineFeed ? '\n' : '\r\n'; + } + return ts.sys.newLine; + }, + }; +} + function reportErrorsAndExit( allDiagnostics: Diagnostics, options?: api.CompilerOptions, consoleError: (s: string) => void = console.error): number { const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics); if (errorsAndWarnings.length) { - let currentDir = options ? options.basePath : undefined; - const formatHost: ts.FormatDiagnosticsHost = { - getCurrentDirectory: () => currentDir || ts.sys.getCurrentDirectory(), - getCanonicalFileName: fileName => fileName, - getNewLine: () => ts.sys.newLine - }; + const formatHost = getFormatDiagnosticsHost(options); if (options && (options.enableIvy === true || options.enableIvy === 'ngtsc')) { const ngDiagnostics = errorsAndWarnings.filter(api.isNgDiagnostic); const tsDiagnostics = errorsAndWarnings.filter(api.isTsDiagnostic); @@ -193,7 +208,7 @@ function reportErrorsAndExit( export function watchMode( project: string, options: api.CompilerOptions, consoleError: (s: string) => void) { return performWatchCompilation(createPerformWatchHost(project, diagnostics => { - consoleError(formatDiagnostics(diagnostics)); + consoleError(formatDiagnostics(diagnostics, getFormatDiagnosticsHost(options))); }, options, options => createEmitCallback(options))); } diff --git a/packages/compiler-cli/test/ngc_spec.ts b/packages/compiler-cli/test/ngc_spec.ts index 55dbc8ca0e..872b3cf425 100644 --- a/packages/compiler-cli/test/ngc_spec.ts +++ b/packages/compiler-cli/test/ngc_spec.ts @@ -99,6 +99,25 @@ describe('ngc transformer command-line', () => { expect(exitCode).toBe(0); }); + it('should respect the "newLine" compiler option when printing diagnostics', () => { + writeConfig(`{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "newLine": "CRLF", + } + }`); + write('test.ts', 'export NOT_VALID = true;'); + + // Stub the error spy because we don't want to call through and print the + // expected error diagnostic. + errorSpy.and.stub(); + + const exitCode = main(['-p', basePath], errorSpy); + expect(errorSpy).toHaveBeenCalledWith( + `test.ts(1,1): error TS1128: Declaration or statement expected.\r\n`); + expect(exitCode).toBe(1); + }); + describe('errors', () => { beforeEach(() => { errorSpy.and.stub(); });