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

TS types transformer: Throw diagnostics as error on empty emit #8914

Merged
merged 6 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type Snapshot<T> = {
readonly [K in keyof T]: Snapshot<T[K]>;
};

export function snapShot<V>(): Snapshot<V> {
return 1 as any;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { snapShot } from "./file2";

export interface State {
value: any;
}

type ContextType = ReturnType<typeof snapShot<State>>;
function id<T>(v: T) {
return v;
}
const Context = id<ContextType | null>(null);

export function useStateContext() {
return Context;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "ts-types-error",
"private": true,
"main": "dist/main.js",
"types": "dist/types.d.ts",
"dependencies": {
"react": "*"
}
}
41 changes: 41 additions & 0 deletions packages/core/integration-tests/test/ts-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down Expand Up @@ -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'));
Expand Down
135 changes: 79 additions & 56 deletions packages/transformers/typescript-types/src/TSTypesTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -76,67 +76,90 @@ 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 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);
}
}

// $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 parcelDiagnostics = deduplicatedDiagnostics.map(diagnostic => {
let filename = asset.filePath;
let {file} = diagnostic;

let diagnosticMessage = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
'\n',
);

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),
},
],
let codeframe: ?DiagnosticCodeFrame;
if (file != null && diagnostic.start != null) {
let source = file.text || diagnostic.source;
if (file.fileName) {
filename = file.fileName;
}

// $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);
}
}

Expand Down