diff --git a/packages/jsii/lib/assembler.ts b/packages/jsii/lib/assembler.ts index fa58017c8e..77d00fc718 100644 --- a/packages/jsii/lib/assembler.ts +++ b/packages/jsii/lib/assembler.ts @@ -1240,10 +1240,18 @@ export class Assembler implements Emitter { } for (const memberDecl of classDecl.members) { - // The "??" is to get to the __constructor symbol (getSymbolAtLocation wouldn't work there..) - const member = - this._typeChecker.getSymbolAtLocation(memberDecl.name!) ?? - ((memberDecl as any).symbol as ts.Symbol); + if (ts.isSemicolonClassElement(memberDecl)) { + this._diagnostics.push( + JsiiDiagnostic.JSII_9996_UNNECESSARY_TOKEN.create(memberDecl), + ); + continue; + } + + const member: ts.Symbol = ts.isConstructorDeclaration(memberDecl) + ? (memberDecl as any).symbol + : this._typeChecker.getSymbolAtLocation( + ts.getNameOfDeclaration(memberDecl)!, + )!; if ( !(declaringType.symbol.getDeclarations() ?? []).find( diff --git a/packages/jsii/lib/compiler.ts b/packages/jsii/lib/compiler.ts index ea92a6d1c9..208ffe1a3c 100644 --- a/packages/jsii/lib/compiler.ts +++ b/packages/jsii/lib/compiler.ts @@ -7,6 +7,7 @@ import * as ts from 'typescript'; import { Assembler } from './assembler'; import { Emitter } from './emitter'; +import { JsiiDiagnostic } from './jsii-diagnostic'; import { ProjectInfo } from './project-info'; import * as utils from './utils'; @@ -246,7 +247,9 @@ export class Compiler implements Emitter { diagnostics.push(...assmEmit.diagnostics); } catch (e) { - LOG.error(`Error during type model analysis: ${e}\n${e.stack}`); + diagnostics.push( + JsiiDiagnostic.JSII_9997_UNKNOWN_ERROR.createDetached(e), + ); hasErrors = true; } diff --git a/packages/jsii/lib/helpers.ts b/packages/jsii/lib/helpers.ts index 0007c4ae08..960e418663 100644 --- a/packages/jsii/lib/helpers.ts +++ b/packages/jsii/lib/helpers.ts @@ -77,7 +77,7 @@ export async function compileJsiiForTest( console.error(error.messageText); // logDiagnostic() doesn't work out of the box, so console.error() it is. } - if (errors.length > 0) { + if (errors.length > 0 || emitResult.emitSkipped) { throw new Error('There were compiler errors'); } const assembly = await fs.readJSON('.jsii', { encoding: 'utf-8' }); diff --git a/packages/jsii/lib/jsii-diagnostic.ts b/packages/jsii/lib/jsii-diagnostic.ts index da2382988c..8088e96220 100644 --- a/packages/jsii/lib/jsii-diagnostic.ts +++ b/packages/jsii/lib/jsii-diagnostic.ts @@ -708,7 +708,7 @@ export class JsiiDiagnostic implements ts.Diagnostic { public static readonly JSII_9002_UNRESOLVEABLE_TYPE = Code.error({ code: 9002, formatter: (reference: string) => - `Unable to resolve type "${reference}". It may be @iternal or not exported from the module's entry point (as configured in "package.json" as "main").`, + `Unable to resolve type "${reference}". It may be @internal or not exported from the module's entry point (as configured in "package.json" as "main").`, name: 'miscellaneous/unresolveable-type', }); @@ -726,6 +726,19 @@ export class JsiiDiagnostic implements ts.Diagnostic { name: 'miscellaneous/unable-to-compute-signature', }); + public static readonly JSII_9996_UNNECESSARY_TOKEN = Code.message({ + code: 9996, + formatter: () => 'Unnecessary token, consider removing it', + name: 'miscellaneous/unnecessary-token', + }); + + public static readonly JSII_9997_UNKNOWN_ERROR = Code.error({ + code: 9997, + formatter: (error: Error) => + `Unknown error: ${error.message} -- ${error.stack}`, + name: 'miscellaneous/unknown-error', + }); + public static readonly JSII_9998_UNSUPORTED_NODE = Code.message({ code: 9998, formatter: (kindOrMessage: ts.SyntaxKind | string) => diff --git a/packages/jsii/test/quirks.test.ts b/packages/jsii/test/quirks.test.ts new file mode 100644 index 0000000000..ecb1fb230b --- /dev/null +++ b/packages/jsii/test/quirks.test.ts @@ -0,0 +1,32 @@ +import { sourceToAssemblyHelper } from '../lib'; + +// ---------------------------------------------------------------------- +test('trailing semicolon after method is correctly ignored', async () => { + const assembly = await sourceToAssemblyHelper(` + export class Foo { + private readonly initialized: boolean; + + public constructor() { + this.initialized = true; + }; + + public method() { return this.initialized; }; + } + `); + + expect(assembly.types!['testpkg.Foo']).toEqual({ + assembly: 'testpkg', + fqn: 'testpkg.Foo', + kind: 'class', + methods: [ + { + locationInModule: { filename: 'index.ts', line: 9 }, + name: 'method', + returns: { type: { primitive: 'boolean' } }, + }, + ], + initializer: { locationInModule: { filename: 'index.ts', line: 5 } }, + locationInModule: { filename: 'index.ts', line: 2 }, + name: 'Foo', + }); +});