constructor()

in projenrc/jsii.ts [141:370]


  constructor(project: yarn.TypeScriptWorkspace, options: JsiiBuildOptions) {
    super(project);

    this.monoProject = project;

    if (!(project instanceof pj.typescript.TypeScriptProject)) {
      throw new Error('JsiiBuild() must be passed a TypeScript project');
    }
    if (!project.parent || !yarn.Monorepo.isMonorepo(project.parent)) {
      throw new Error('Project root must be Monorepo component');
    }
    if (!project.parent.monorepoRelease) {
      throw new Error('Monorepo does not have a release component');
    }

    this.monorepoRelease = project.parent.monorepoRelease;

    const tsProject = project;
    this.tsProject = tsProject;

    if (tsProject.tsconfig) {
      throw new Error('The TypeScript project for JsiiBuild() must be configured with { disableTsconfig: true }');
    }
    if ((tsProject.release?.publisher as any)?.publishJobs?.npm) {
      throw new Error('The TypeScript project for JsiiBuild() must be configured without an NPM publishing job');
    }

    const srcdir = tsProject.srcdir;
    const libdir = tsProject.libdir;

    tsProject.addFields({ types: `${libdir}/index.d.ts` });

    const compressAssembly = options.compressAssembly ?? false;

    // this is an unhelpful warning
    const jsiiFlags = ['--silence-warnings=reserved-word'];
    if (compressAssembly) {
      jsiiFlags.push('--compress-assembly');
    }

    const compatIgnore = options.compatIgnore ?? '.compatignore';

    tsProject.addFields({ stability: options.stability ?? Stability.STABLE });

    if (options.stability === Stability.DEPRECATED) {
      tsProject.addFields({ deprecated: true });
    }

    const compatTask = tsProject.addTask('compat', {
      description: 'Perform API compatibility check against latest version',
      exec: `jsii-diff npm:$(node -p "require(\'./package.json\').name") -k --ignore-file ${compatIgnore} || (echo "\nUNEXPECTED BREAKING CHANGES: add keys such as \'removed:constructs.Node.of\' to ${compatIgnore} to skip.\n" && exit 1)`,
    });

    const compat = options.compat ?? false;
    if (compat) {
      tsProject.compileTask.spawn(compatTask);
    }

    tsProject.compileTask.reset(['jsii', ...jsiiFlags].join(' '));
    tsProject.watchTask.reset(['jsii', '-w', ...jsiiFlags].join(' '));

    // Create a new package:all task, it will be filled with language targets later
    this.packageAllTask = tsProject.addTask('package-all', {
      description: 'Packages artifacts for all target languages',
    });

    // in jsii we consider the entire repo (post build) as the build artifact
    // which is then used to create the language bindings in separate jobs.
    // we achieve this by doing a checkout and overwrite with the files from the js package.
    this.packageJsTask = this.addPackagingTask('js');

    // When running inside CI we initially only package js. Other targets are packaged in separate jobs.
    // Outside of CI (i.e locally) we simply package all targets.
    tsProject.packageTask.reset();
    tsProject.packageTask.spawn(this.packageJsTask, {
      // Only run in CI
      condition: 'node -e "if (!process.env.CI) process.exit(1)"',
    });

    // Do not spawn 'package-all' automatically as part of 'package', the jsii packaging will
    // be done as part of the release task.
    /*
    tsProject.packageTask.spawn(this.packageAllTask, {
      // Don't run in CI
      condition: 'node -e "if (process.env.CI) process.exit(1)"',
    });
    */

    const targets: Record<string, any> = {};

    const jsii: any = {
      outdir: tsProject.artifactsDirectory,
      targets,
      tsc: {
        outDir: libdir,
        rootDir: srcdir,
      },
    };

    if (options.excludeTypescript) {
      jsii.excludeTypescript = options.excludeTypescript;
    }

    if (options.composite) {
      jsii.projectReferences = true;
    }

    tsProject.addFields({ jsii });

    // FIXME: Not support "runsOn" and the workflow container image for now
    const extraJobOptions: Partial<Job> = {
      /*
        ...this.getJobRunsOnConfig(options),
        ...(options.workflowContainerImage
          ? { container: { image: options.workflowContainerImage } }
          : {}),
      */
    };

    const npmjs: NpmPublishOptions = {
      registry: tsProject.package.npmRegistry,
      npmTokenSecret: tsProject.package.npmTokenSecret,
      npmProvenance: tsProject.package.npmProvenance,
      // No support for CodeArtifact here
      // codeArtifactOptions: tsProject.codeArtifactOptions,
    };
    this.addTargetToBuild('js', this.packageJsTask, extraJobOptions);
    this.addTargetToRelease('js', this.packageJsTask, npmjs);

    const maven = options.publishToMaven;
    if (maven) {
      targets.java = {
        package: maven.javaPackage,
        maven: {
          groupId: maven.mavenGroupId,
          artifactId: maven.mavenArtifactId,
        },
      };

      const task = this.addPackagingTask('java');
      this.addTargetToBuild('java', task, extraJobOptions);
      this.addTargetToRelease('java', task, maven);
    }

    const pypi = options.publishToPypi;
    if (pypi) {
      targets.python = {
        distName: pypi.distName,
        module: pypi.module,
      };

      const task = this.addPackagingTask('python');
      this.addTargetToBuild('python', task, extraJobOptions);
      this.addTargetToRelease('python', task, pypi);
    }

    const nuget = options.publishToNuget;
    if (nuget) {
      targets.dotnet = {
        namespace: nuget.dotNetNamespace,
        packageId: nuget.packageId,
        iconUrl: nuget.iconUrl,
      };

      const task = this.addPackagingTask('dotnet');
      this.addTargetToBuild('dotnet', task, extraJobOptions);
      this.addTargetToRelease('dotnet', task, nuget);
    }

    const golang = options.publishToGo;
    if (golang) {
      targets.go = {
        moduleName: golang.moduleName,
        packageName: golang.packageName,
        versionSuffix: golang.versionSuffix,
      };

      const task = this.addPackagingTask('go');
      this.addTargetToBuild('go', task, extraJobOptions);
      this.addTargetToRelease('go', task, golang);
    }

    const jsiiSuffix =
      options.jsiiVersion === '*'
        ? // If jsiiVersion is "*", don't specify anything so the user can manage.
        ''
        : // Otherwise, use `jsiiVersion` or fall back to `5.7`
        `@${options.jsiiVersion ?? '5.7'}`;
    tsProject.addDevDeps(
      `jsii${jsiiSuffix}`,
      `jsii-rosetta${jsiiSuffix}`,
      'jsii-diff',
      'jsii-pacmak',
    );

    tsProject.gitignore.exclude('.jsii', 'tsconfig.json');
    tsProject.npmignore?.include('.jsii');

    if (options.docgen ?? true) {
      // If jsiiVersion is "*", don't specify anything so the user can manage.
      // Otherwise use a version that is compatible with all supported jsii releases.
      const docgenVersion = options.jsiiVersion === '*' ? '*' : '^10.5.0';
      new pj.cdk.JsiiDocgen(tsProject, {
        version: docgenVersion,
        filePath: options.docgenFilePath,
      });
    }

    // jsii updates .npmignore, so we make it writable
    if (tsProject.npmignore) {
      tsProject.npmignore.readonly = false;
    }

    const packageJson = tsProject.package.file;

    if ((options.pypiClassifiers ?? []).length > 0) {
      packageJson.patch(
        pj.JsonPatch.add('/jsii/targets/python/classifiers', options.pypiClassifiers),
      );
    }

    if (options.rosettaStrict) {
      packageJson.patch(
        pj.JsonPatch.add('/jsii/metadata', {}),
        pj.JsonPatch.add('/jsii/metadata/jsii', {}),
        pj.JsonPatch.add('/jsii/metadata/jsii/rosetta', {}),
        pj.JsonPatch.add('/jsii/metadata/jsii/rosetta/strict', true),
      );
    }
  }