@@ -41,6 +41,25 @@ export type TSCompilerOptions = Partial<
4141 >
4242> ;
4343
44+ /**
45+ * The assembly targets have some typing in `spec.PackageJson`, extract it.
46+ *
47+ * The types in the upstream spec library:
48+ *
49+ * - Missing the user-visible `go` target
50+ * - Missing the synthetic `js` target
51+ */
52+ export type AssemblyTargets = spec . PackageJson [ 'jsii' ] [ 'targets' ] & {
53+ js ?: {
54+ npm : string ;
55+ } ;
56+ go ?: {
57+ moduleName : string ;
58+ packageName : string ;
59+ } ;
60+ [ otherLanguage : string ] : unknown ;
61+ } ;
62+
4463export interface ProjectInfo {
4564 readonly projectRoot : string ;
4665 readonly packageJson : PackageJson ;
@@ -65,7 +84,7 @@ export interface ProjectInfo {
6584 readonly peerDependencies : { readonly [ name : string ] : string } ;
6685 readonly dependencyClosure : readonly spec . Assembly [ ] ;
6786 readonly bundleDependencies ?: { readonly [ name : string ] : string } ;
68- readonly targets : spec . AssemblyTargets ;
87+ readonly targets : AssemblyTargets ;
6988 readonly metadata ?: { readonly [ key : string ] : any } ;
7089 readonly jsiiVersionFormat : 'short' | 'full' ;
7190 readonly diagnostics ?: { readonly [ code : string ] : ts . DiagnosticCategory } ;
@@ -85,6 +104,12 @@ export interface ProjectInfo {
85104 readonly validateTsconfig ?: TypeScriptConfigValidationRuleSet ;
86105}
87106
107+ /**
108+ * A type representing the contents of a `package.json` file.
109+ *
110+ * Note that there is also a `PackageJson` type in `@jsii/spec`, and this one is
111+ * not the same. Do not ask me why.
112+ */
88113export interface PackageJson {
89114 readonly description ?: string ;
90115 readonly homepage ?: string ;
@@ -121,7 +146,7 @@ export interface PackageJson {
121146 // main jsii config
122147 readonly diagnostics ?: { readonly [ id : string ] : 'error' | 'warning' | 'suggestion' | 'message' } ;
123148 readonly metadata ?: { readonly [ key : string ] : unknown } ;
124- readonly targets ?: { readonly [ name : string ] : unknown } ;
149+ readonly targets ?: AssemblyTargets ;
125150 readonly versionFormat ?: 'short' | 'full' ;
126151
127152 // Either user-provided config ...
@@ -241,8 +266,10 @@ export function loadProjectInfo(projectRoot: string): ProjectInfoResult {
241266 dependencyClosure : transitiveDependencies ,
242267 bundleDependencies,
243268 targets : {
244- ..._required ( pkg . jsii , 'The "package.json" file must specify the "jsii" attribute' ) . targets ,
245- js : { npm : pkg . name } ,
269+ ...validateTargets (
270+ _required ( pkg . jsii , 'The "package.json" file must specify the "jsii" attribute' ) . targets as AssemblyTargets ,
271+ ) ,
272+ js : { npm : pkg . name ! } ,
246273 } ,
247274 metadata,
248275 jsiiVersionFormat : _validateVersionFormat ( pkg . jsii ?. versionFormat ?? 'full' ) ,
@@ -278,6 +305,53 @@ export function loadProjectInfo(projectRoot: string): ProjectInfoResult {
278305 return { projectInfo, diagnostics } ;
279306}
280307
308+ /**
309+ * Validate the values of the `.jsii.targets` field
310+ */
311+ function validateTargets ( targets : AssemblyTargets | undefined ) : AssemblyTargets | undefined {
312+ if ( ! targets ) {
313+ return undefined ;
314+ }
315+
316+ // Go package names must be valid identifiers
317+ if ( targets . go ) {
318+ if ( ! isIdentifier ( targets . go . packageName ) ) {
319+ throw new JsiiError ( `jsii.targets.go.packageName contains non-identifier characters: ${ targets . go . packageName } ` ) ;
320+ }
321+ }
322+
323+ if ( targets . dotnet ) {
324+ if ( ! targets . dotnet . namespace . split ( '.' ) . every ( isIdentifier ) ) {
325+ throw new JsiiError (
326+ `jsii.targets.dotnet.namespace contains non-identifier characters: ${ targets . dotnet . namespace } ` ,
327+ ) ;
328+ }
329+ }
330+
331+ if ( targets . java ) {
332+ if ( ! targets . java . package . split ( '.' ) . every ( isIdentifier ) ) {
333+ throw new JsiiError ( `jsii.targets.java.package contains non-identifier characters: ${ targets . java . package } ` ) ;
334+ }
335+ }
336+
337+ if ( targets . python ) {
338+ if ( ! targets . python . module . split ( '.' ) . every ( isIdentifier ) ) {
339+ throw new JsiiError ( `jsii.targets.python.module contains non-identifier characters: ${ targets . python . module } ` ) ;
340+ }
341+ }
342+
343+ return targets ;
344+
345+ /**
346+ * Regexp-check for matching an identifier
347+ *
348+ * Conveniently, all identifiers look more or less the same in all languages.
349+ */
350+ function isIdentifier ( x : string ) {
351+ return / ^ [ \w _ ] [ \w \d _ ] * $ / u. test ( x ) ;
352+ }
353+ }
354+
281355function _guessRepositoryType ( url : string ) : string {
282356 if ( url . endsWith ( '.git' ) ) {
283357 return 'git' ;
0 commit comments