export function loadProjectInfo()

in src/project-info.ts [147:278]


export function loadProjectInfo(projectRoot: string): ProjectInfoResult {
  const packageJsonPath = path.join(projectRoot, 'package.json');
  const pkg: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));

  const diagnostics: ts.Diagnostic[] = [];

  let bundleDependencies: { [name: string]: string } | undefined;
  for (const name of pkg.bundleDependencies ?? pkg.bundledDependencies ?? []) {
    const version = pkg.dependencies?.[name];
    if (!version) {
      throw new JsiiError(
        `The "package.json" file has "${name}" in "bundleDependencies", but it is not declared in "dependencies"`,
      );
    }

    if (pkg.peerDependencies && name in pkg.peerDependencies) {
      throw new JsiiError(
        `The "package.json" file has "${name}" in "bundleDependencies", and also in "peerDependencies"`,
      );
    }

    bundleDependencies = bundleDependencies ?? {};
    bundleDependencies[name] = _resolveVersion(version, projectRoot).version;
  }

  // Check peerDependencies are also in devDependencies
  // You need this to write tests properly. There are probably cases where
  // it makes sense to have this different, so most of what this checking
  // produces is warnings, not errors.
  const devDependencies = pkg.devDependencies ?? {};
  for (const [name, rng] of Object.entries(pkg.peerDependencies ?? {})) {
    const range = new semver.Range(_resolveVersion(rng as string, projectRoot).version);
    const minVersion = semver.minVersion(range)?.raw;

    if (!(name in devDependencies) || devDependencies[name] !== `${minVersion}`) {
      diagnostics.push(
        JsiiDiagnostic.JSII_0006_MISSING_DEV_DEPENDENCY.createDetached(
          name,
          `${rng as any}`,
          `${minVersion}`,
          `${devDependencies[name]}`,
        ),
      );
      continue;
    }
  }

  const bundled = new Set(Object.keys(bundleDependencies ?? {}));
  const dependencies: Record<string, string> = filterDictByKey(
    pkg.dependencies ?? {},
    (depName) => !bundled.has(depName),
  );
  const peerDependencies: Record<string, string> = pkg.peerDependencies ?? {};

  const resolver = new DependencyResolver();
  const resolved = resolver.discoverDependencyTree(projectRoot, {
    ...dependencies,
    ...peerDependencies,
  });
  const transitiveDependencies = resolver.assemblyClosure(resolved);

  const metadata = mergeMetadata(
    {
      jsii: {
        pacmak: {
          // When `true`, `jsii-pacmak` will use the `Jsii$Default` implementation in code generation even for dependencies.
          hasDefaultInterfaces: true,
        },
      },
    },
    pkg.jsii?.metadata,
  );

  const projectInfo: ProjectInfo = {
    projectRoot,
    packageJson: pkg,

    name: _required(pkg.name, 'The "package.json" file must specify the "name" attribute'),
    version: _required(pkg.version, 'The "package.json" file must specify the "version" attribute'),
    deprecated: pkg.deprecated,
    stability: _validateStability(pkg.stability, pkg.deprecated),
    author: _toPerson(_required(pkg.author, 'The "package.json" file must specify the "author" attribute'), 'author'),
    repository: _toRepository(
      _required(pkg.repository, 'The "package.json" file must specify the "repository" attribute'),
    ),
    license: _validateLicense(pkg.license),
    keywords: pkg.keywords,

    main: _required(pkg.main, 'The "package.json" file must specify the "main" attribute'),
    types: _required(pkg.types, 'The "package.json" file must specify the "types" attribute'),

    dependencies,
    peerDependencies,
    dependencyClosure: transitiveDependencies,
    bundleDependencies,
    targets: {
      ..._required(pkg.jsii, 'The "package.json" file must specify the "jsii" attribute').targets,
      js: { npm: pkg.name },
    },
    metadata,
    jsiiVersionFormat: _validateVersionFormat(pkg.jsii?.versionFormat ?? 'full'),

    description: pkg.description,
    homepage: pkg.homepage,
    contributors: (pkg.contributors as any[])?.map((contrib, index) =>
      _toPerson(contrib, `contributors[${index}]`, 'contributor'),
    ),

    excludeTypescript: pkg.jsii?.excludeTypescript ?? [],
    projectReferences: pkg.jsii?.projectReferences,
    tsc: {
      outDir: pkg.jsii?.tsc?.outDir,
      rootDir: pkg.jsii?.tsc?.rootDir,
      baseUrl: pkg.jsii?.tsc?.baseUrl,
      paths: pkg.jsii?.tsc?.paths,
      forceConsistentCasingInFileNames: pkg.jsii?.tsc?.forceConsistentCasingInFileNames,
      noImplicitOverride: pkg.jsii?.tsc?.noImplicitOverride,
      noPropertyAccessFromIndexSignature: pkg.jsii?.tsc?.noPropertyAccessFromIndexSignature,
      noUncheckedIndexedAccess: pkg.jsii?.tsc?.noUncheckedIndexedAccess,
      ..._sourceMapPreferences(pkg.jsii?.tsc),
      types: pkg.jsii?.tsc?.types,
    },
    bin: pkg.bin,
    exports: pkg.exports,
    diagnostics: _loadDiagnostics(pkg.jsii?.diagnostics),

    // user-provided tsconfig
    tsconfig: pkg.jsii?.tsconfig,
    validateTsconfig: _validateTsconfigRuleSet(pkg.jsii?.validateTsconfig ?? 'strict'),
  };
  return { projectInfo, diagnostics };
}