From ccf871dec934fd947e8ffe298e797c6ee37b9410 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Sun, 26 Mar 2023 23:58:13 +0200 Subject: [PATCH 1/3] Throw diagnostics as error on empty emit --- .../src/TSTypesTransformer.js | 118 +++++++++--------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/packages/transformers/typescript-types/src/TSTypesTransformer.js b/packages/transformers/typescript-types/src/TSTypesTransformer.js index 96396af96f7..b6e1f4b784f 100644 --- a/packages/transformers/typescript-types/src/TSTypesTransformer.js +++ b/packages/transformers/typescript-types/src/TSTypesTransformer.js @@ -9,7 +9,7 @@ import type {CompilerOptions} from 'typescript'; import ts from 'typescript'; import {CompilerHost, loadTSConfig} from '@parcel/ts-utils'; import {normalizeSeparators} from '@parcel/utils'; -import {escapeMarkdown} from '@parcel/diagnostic'; +import ThrowableDiagnostic, {escapeMarkdown} from '@parcel/diagnostic'; import {TSModuleGraph} from './TSModuleGraph'; import nullthrows from 'nullthrows'; import {collect} from './collect'; @@ -76,67 +76,73 @@ export default (new Transformer({ .getPreEmitDiagnostics(program) .concat(emitResult.diagnostics); - if (diagnostics.length > 0) { - for (let diagnostic of diagnostics) { - let filename = asset.filePath; - let {file} = diagnostic; - - let diagnosticMessage = - typeof diagnostic.messageText === 'string' - ? diagnostic.messageText - : diagnostic.messageText.messageText; - - let codeframe: ?DiagnosticCodeFrame; - if (file != null && diagnostic.start != null) { - let source = file.text || diagnostic.source; - if (file.fileName) { - filename = file.fileName; - } + let parcelDiagnostics = diagnostics.map(diagnostic => { + let filename = asset.filePath; + let {file} = diagnostic; - // $FlowFixMe - if (source) { - let lineChar = file.getLineAndCharacterOfPosition(diagnostic.start); - let start = { - line: lineChar.line + 1, - column: lineChar.character + 1, - }; - let end = { - line: start.line, - column: start.column + 1, - }; + let diagnosticMessage = + typeof diagnostic.messageText === 'string' + ? diagnostic.messageText + : diagnostic.messageText.messageText; + + let codeframe: ?DiagnosticCodeFrame; + if (file != null && diagnostic.start != null) { + let source = file.text || diagnostic.source; + if (file.fileName) { + filename = file.fileName; + } - if ( - typeof diagnostic.start === 'number' && - typeof diagnostic.length === 'number' - ) { - let endCharPosition = file.getLineAndCharacterOfPosition( - diagnostic.start + diagnostic.length, - ); - - end = { - line: endCharPosition.line + 1, - column: endCharPosition.character + 1, - }; - } - - codeframe = { - filePath: filename, - code: source, - codeHighlights: [ - { - start, - end, - message: escapeMarkdown(diagnosticMessage), - }, - ], + // $FlowFixMe + if (source) { + let lineChar = file.getLineAndCharacterOfPosition(diagnostic.start); + let start = { + line: lineChar.line + 1, + column: lineChar.character + 1, + }; + let end = { + line: start.line, + column: start.column + 1, + }; + + if ( + typeof diagnostic.start === 'number' && + typeof diagnostic.length === 'number' + ) { + let endCharPosition = file.getLineAndCharacterOfPosition( + diagnostic.start + diagnostic.length, + ); + + end = { + line: endCharPosition.line + 1, + column: endCharPosition.character + 1, }; } + + codeframe = { + filePath: filename, + code: source, + codeHighlights: [ + { + start, + end, + message: escapeMarkdown(diagnosticMessage), + }, + ], + }; } + } + + return { + message: escapeMarkdown(diagnosticMessage), + codeFrames: codeframe ? [codeframe] : undefined, + }; + }); - logger.warn({ - message: escapeMarkdown(diagnosticMessage), - codeFrames: codeframe ? [codeframe] : undefined, - }); + if (host.outputCode == null) { + throw new ThrowableDiagnostic({diagnostic: parcelDiagnostics}); + } else { + for (let d of parcelDiagnostics) { + logger.warn(d); } } From 9d5e4e04dca2a0e13c374273c8310c577991c5a9 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Mon, 27 Mar 2023 18:08:39 +0200 Subject: [PATCH 2/3] Deduplicate diagnostics --- .../src/TSTypesTransformer.js | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/transformers/typescript-types/src/TSTypesTransformer.js b/packages/transformers/typescript-types/src/TSTypesTransformer.js index b6e1f4b784f..2559abc21c9 100644 --- a/packages/transformers/typescript-types/src/TSTypesTransformer.js +++ b/packages/transformers/typescript-types/src/TSTypesTransformer.js @@ -76,14 +76,31 @@ export default (new Transformer({ .getPreEmitDiagnostics(program) .concat(emitResult.diagnostics); - let parcelDiagnostics = diagnostics.map(diagnostic => { + let diagnosticIds = new Set(); + let deduplicatedDiagnostics = []; + for (let d of diagnostics) { + if (d.start != null && d.length != null && d.messageText != null) { + let id = `${d.start}:${d.length}:${ts.flattenDiagnosticMessageText( + d.messageText, + '\n', + )}`; + if (!diagnosticIds.has(id)) { + deduplicatedDiagnostics.push(d); + } + diagnosticIds.add(id); + } else { + deduplicatedDiagnostics.push(d); + } + } + + let parcelDiagnostics = deduplicatedDiagnostics.map(diagnostic => { let filename = asset.filePath; let {file} = diagnostic; - let diagnosticMessage = - typeof diagnostic.messageText === 'string' - ? diagnostic.messageText - : diagnostic.messageText.messageText; + let diagnosticMessage = ts.flattenDiagnosticMessageText( + diagnostic.messageText, + '\n', + ); let codeframe: ?DiagnosticCodeFrame; if (file != null && diagnostic.start != null) { From 0eaa6a856ee490aca8e8a9686c80f208b864ebf0 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Mon, 27 Mar 2023 18:09:13 +0200 Subject: [PATCH 3/3] Add test --- .../test/integration/ts-types/error/file2.ts | 7 ++++ .../test/integration/ts-types/error/index.ts | 15 +++++++ .../integration/ts-types/error/package.json | 9 ++++ .../test/integration/ts-types/error/yarn.lock | 0 .../core/integration-tests/test/ts-types.js | 41 +++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 packages/core/integration-tests/test/integration/ts-types/error/file2.ts create mode 100644 packages/core/integration-tests/test/integration/ts-types/error/index.ts create mode 100644 packages/core/integration-tests/test/integration/ts-types/error/package.json create mode 100644 packages/core/integration-tests/test/integration/ts-types/error/yarn.lock diff --git a/packages/core/integration-tests/test/integration/ts-types/error/file2.ts b/packages/core/integration-tests/test/integration/ts-types/error/file2.ts new file mode 100644 index 00000000000..98d1f78003d --- /dev/null +++ b/packages/core/integration-tests/test/integration/ts-types/error/file2.ts @@ -0,0 +1,7 @@ +type Snapshot = { + readonly [K in keyof T]: Snapshot; +}; + +export function snapShot(): Snapshot { + return 1 as any; +} diff --git a/packages/core/integration-tests/test/integration/ts-types/error/index.ts b/packages/core/integration-tests/test/integration/ts-types/error/index.ts new file mode 100644 index 00000000000..7c901358ecf --- /dev/null +++ b/packages/core/integration-tests/test/integration/ts-types/error/index.ts @@ -0,0 +1,15 @@ +import { snapShot } from "./file2"; + +export interface State { + value: any; +} + +type ContextType = ReturnType>; +function id(v: T) { + return v; +} +const Context = id(null); + +export function useStateContext() { + return Context; +} diff --git a/packages/core/integration-tests/test/integration/ts-types/error/package.json b/packages/core/integration-tests/test/integration/ts-types/error/package.json new file mode 100644 index 00000000000..531726dd00f --- /dev/null +++ b/packages/core/integration-tests/test/integration/ts-types/error/package.json @@ -0,0 +1,9 @@ +{ + "name": "ts-types-error", + "private": true, + "main": "dist/main.js", + "types": "dist/types.d.ts", + "dependencies": { + "react": "*" + } +} diff --git a/packages/core/integration-tests/test/integration/ts-types/error/yarn.lock b/packages/core/integration-tests/test/integration/ts-types/error/yarn.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/integration-tests/test/ts-types.js b/packages/core/integration-tests/test/ts-types.js index 957aafae66b..7974aa43b9b 100644 --- a/packages/core/integration-tests/test/ts-types.js +++ b/packages/core/integration-tests/test/ts-types.js @@ -8,6 +8,7 @@ import { outputFS, ncp, } from '@parcel/test-utils'; +import {md} from '@parcel/diagnostic'; describe('typescript types', function () { it('should generate a typescript declaration file', async function () { @@ -324,6 +325,46 @@ describe('typescript types', function () { assert.equal(dist, expected); }); + it('should throw a diagnostic on fatal errors', async function () { + let message = md`Return type of exported function has or is using name 'Snapshot' from external module "${path.join( + __dirname, + '/integration/ts-types/error/file2', + )}" but cannot be named.`; + await assert.rejects( + () => + bundle(path.join(__dirname, '/integration/ts-types/error/index.ts')), + { + name: 'BuildError', + message, + diagnostics: [ + { + message, + codeFrames: [ + { + filePath: path.join( + __dirname, + '/integration/ts-types/error/index.ts', + ), + code: await inputFS.readFile( + path.join(__dirname, '/integration/ts-types/error/index.ts'), + 'utf8', + ), + codeHighlights: [ + { + start: {line: 13, column: 17}, + end: {line: 13, column: 32}, + message, + }, + ], + }, + ], + origin: '@parcel/transformer-typescript-types', + }, + ], + }, + ); + }); + it('should work with module augmentation', async function () { let fixtureDir = path.join(__dirname, 'integration/ts-types/augmentation'); await outputFS.mkdirp(path.join(fixtureDir, 'node_modules'));