@@ -11,7 +11,7 @@ import * as os from 'node:os';
1111import * as path from 'node:path' ;
1212import { PackageJson , loadAssemblyFromPath , writeAssembly } from '@jsii/spec' ;
1313import * as spec from '@jsii/spec' ;
14- import { DiagnosticCategory } from 'typescript' ;
14+ import { Diagnostic , DiagnosticCategory } from 'typescript' ;
1515
1616import { Compiler , CompilerOptions } from './compiler' ;
1717import { loadProjectInfo , ProjectInfo } from './project-info' ;
@@ -25,6 +25,11 @@ export type MultipleSourceFiles = {
2525 [ name : string ] : string ;
2626} ;
2727
28+ /**
29+ * Assembly features supported by this compiler
30+ */
31+ export const ASSEMBLY_FEATURES_SUPPORTED : spec . JsiiFeature [ ] = [ 'intersection-types' ] ;
32+
2833/**
2934 * Compile a piece of source and return the JSII assembly for it
3035 *
@@ -39,10 +44,24 @@ export function sourceToAssemblyHelper(
3944 source : string | MultipleSourceFiles ,
4045 options ?: TestCompilationOptions | ( ( obj : PackageJson ) => void ) ,
4146) : spec . Assembly {
42- return compileJsiiForTest ( source , options ) . assembly ;
47+ const result = compileJsiiForTest ( source , options ) ;
48+ if ( result . type !== 'success' ) {
49+ throw new Error ( 'Compilation failed' ) ;
50+ }
51+ return result . assembly ;
4352}
4453
54+ export type HelperCompilationOut = HelperCompilationResult | HelperCompilationFailure ;
55+
56+ /**
57+ * Successful output of a compilation command (for testing)
58+ *
59+ * A better name would have been `HelperCompilationSuccess`, but the name is part of
60+ * the public API surface, so we keep it like this.
61+ */
4562export interface HelperCompilationResult {
63+ readonly type : 'success' ;
64+
4665 /**
4766 * The generated assembly
4867 */
@@ -62,6 +81,22 @@ export interface HelperCompilationResult {
6281 * Whether to compress the assembly file
6382 */
6483 readonly compressAssembly : boolean ;
84+
85+ /**
86+ * Diagnostics that occurred during compilation
87+ */
88+ readonly diagnostics : readonly Diagnostic [ ] ;
89+ }
90+
91+ export interface HelperCompilationFailure {
92+ readonly type : 'failure' ;
93+
94+ /**
95+ * Diagnostics that occurred during compilation
96+ *
97+ * Contains at least one error.
98+ */
99+ readonly diagnostics : readonly Diagnostic [ ] ;
65100}
66101
67102/**
@@ -74,11 +109,11 @@ export interface HelperCompilationResult {
74109 * @param options accepts a callback for historical reasons but really expects to
75110 * take an options object.
76111 */
77- export function compileJsiiForTest (
112+ export function compileJsiiForTest < O extends TestCompilationOptions > (
78113 source : string | { 'index.ts' : string ; [ name : string ] : string } ,
79- options ?: TestCompilationOptions | ( ( obj : PackageJson ) => void ) ,
114+ options ?: O | ( ( obj : PackageJson ) => void ) ,
80115 compilerOptions ?: Omit < CompilerOptions , 'projectInfo' | 'watch' > ,
81- ) : HelperCompilationResult {
116+ ) : ResultOrSuccess < O > {
82117 if ( typeof source === 'string' ) {
83118 source = { 'index.ts' : source } ;
84119 }
@@ -108,14 +143,25 @@ export function compileJsiiForTest(
108143 const emitResult = compiler . emit ( ) ;
109144
110145 const errors = emitResult . diagnostics . filter ( ( d ) => d . category === DiagnosticCategory . Error ) ;
111- for ( const error of errors ) {
112- console . error ( formatDiagnostic ( error , projectInfo . projectRoot ) ) ;
113- // logDiagnostic() doesn't work out of the box, so console.error() it is.
146+
147+ if ( typeof options !== 'object' || ! options ?. captureDiagnostics ) {
148+ for ( const error of errors ) {
149+ console . error ( formatDiagnostic ( error , projectInfo . projectRoot ) ) ;
150+ // logDiagnostic() doesn't work out of the box, so console.error() it is.
151+ }
114152 }
153+
115154 if ( errors . length > 0 || emitResult . emitSkipped ) {
155+ if ( typeof options === 'object' && options ?. captureDiagnostics ) {
156+ return {
157+ type : 'failure' ,
158+ diagnostics : emitResult . diagnostics ,
159+ } satisfies HelperCompilationFailure ;
160+ }
116161 throw new JsiiError ( 'There were compiler errors' ) ;
117162 }
118- const assembly = loadAssemblyFromPath ( process . cwd ( ) , false ) ;
163+
164+ const assembly = loadAssemblyFromPath ( process . cwd ( ) , false , ASSEMBLY_FEATURES_SUPPORTED ) ;
119165 const files : Record < string , string > = { } ;
120166
121167 for ( const filename of Object . keys ( source ) ) {
@@ -141,14 +187,20 @@ export function compileJsiiForTest(
141187 }
142188
143189 return {
190+ type : 'success' ,
144191 assembly,
145192 files,
146193 packageJson,
147194 compressAssembly : isOptionsObject ( options ) && options . compressAssembly ? true : false ,
148- } as HelperCompilationResult ;
149- } ) ;
195+ diagnostics : emitResult . diagnostics ,
196+ } satisfies HelperCompilationResult ;
197+ } ) as ResultOrSuccess < O > ;
150198}
151199
200+ type ResultOrSuccess < O extends TestCompilationOptions > = O [ 'captureDiagnostics' ] extends true
201+ ? HelperCompilationOut
202+ : HelperCompilationResult ;
203+
152204function inTempDir < T > ( block : ( ) => T ) : T {
153205 const origDir = process . cwd ( ) ;
154206 const tmpDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'jsii' ) ) ;
@@ -236,6 +288,13 @@ export interface TestCompilationOptions {
236288 * @default false
237289 */
238290 readonly compressAssembly ?: boolean ;
291+
292+ /**
293+ * Whether or not to print the diagnostics
294+ *
295+ * @default false
296+ */
297+ readonly captureDiagnostics ?: boolean ;
239298}
240299
241300function isOptionsObject (
@@ -278,7 +337,11 @@ export class TestWorkspace {
278337 /**
279338 * Add a test-compiled jsii assembly as a dependency
280339 */
281- public addDependency ( dependencyAssembly : HelperCompilationResult ) {
340+ public addDependency ( dependencyAssembly : HelperCompilationOut ) {
341+ if ( dependencyAssembly . type !== 'success' ) {
342+ throw new JsiiError ( 'Cannot add dependency: assembly compilation failed' ) ;
343+ }
344+
282345 if ( this . installed . has ( dependencyAssembly . assembly . name ) ) {
283346 throw new JsiiError (
284347 `A dependency with name '${ dependencyAssembly . assembly . name } ' was already installed. Give one a different name.` ,
0 commit comments