nx/packages/js/src/utils/package-json/update-package-json.ts
2022-09-20 12:05:58 -04:00

125 lines
3.2 KiB
TypeScript

import {
ExecutorContext,
normalizePath,
ProjectGraphProjectNode,
readJsonFile,
writeJsonFile,
} from '@nrwl/devkit';
import {
DependentBuildableProjectNode,
updateBuildableProjectPackageJsonDependencies,
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import { basename, dirname, join, parse, relative } from 'path';
import { fileExists } from 'nx/src/utils/fileutils';
import type { PackageJson } from 'nx/src/utils/package-json';
function getMainFileDirRelativeToProjectRoot(
main: string,
projectRoot: string
): string {
const mainFileDir = dirname(main);
const relativeDir = normalizePath(relative(projectRoot, mainFileDir));
return relativeDir === '' ? `./` : `./${relativeDir}/`;
}
export interface UpdatePackageJsonOption {
projectRoot: string;
outputPath: string;
main: string;
format?: string[];
skipTypings?: boolean;
outputFileName?: string;
generateExportsField?: boolean;
updateBuildableProjectDepsInPackageJson?: boolean;
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
}
export function updatePackageJson(
options: UpdatePackageJsonOption,
context: ExecutorContext,
target: ProjectGraphProjectNode,
dependencies: DependentBuildableProjectNode[]
): void {
const pathToPackageJson = join(
context.root,
options.projectRoot,
'package.json'
);
const packageJson = fileExists(pathToPackageJson)
? readJsonFile(pathToPackageJson)
: { name: context.projectName };
writeJsonFile(
`${options.outputPath}/package.json`,
getUpdatedPackageJsonContent(packageJson, options)
);
if (
dependencies.length > 0 &&
options.updateBuildableProjectDepsInPackageJson
) {
updateBuildableProjectPackageJsonDependencies(
context.root,
context.projectName,
context.targetName,
context.configurationName,
target,
dependencies,
options.buildableProjectDepsInPackageJsonType
);
}
}
export function getUpdatedPackageJsonContent(
packageJson: PackageJson,
options: UpdatePackageJsonOption
) {
// Default is CJS unless esm is explicitly passed.
const hasCjsFormat = !options.format || options.format?.includes('cjs');
const hasEsmFormat = options.format?.includes('esm');
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
const relativeMainFileDir = getMainFileDirRelativeToProjectRoot(
options.main,
options.projectRoot
);
const typingsFile = `${relativeMainFileDir}${mainFile}.d.ts`;
const exports = {
'.': {},
...packageJson.exports,
};
const mainJsFile =
options.outputFileName ?? `${relativeMainFileDir}${mainFile}.js`;
if (hasEsmFormat) {
// Unofficial field for backwards compat.
packageJson.module ??= mainJsFile;
if (!hasCjsFormat) {
packageJson.type = 'module';
packageJson.main ??= mainJsFile;
}
exports['.']['import'] = mainJsFile;
}
if (hasCjsFormat) {
const { dir, name } = parse(mainJsFile);
const cjsMain = `${dir}/${name}.cjs`;
packageJson.main ??= cjsMain;
exports['.']['require'] = cjsMain;
}
if (options.generateExportsField) {
packageJson.exports = exports;
}
if (!options.skipTypings) {
packageJson.types = packageJson.types ?? typingsFile;
}
return packageJson;
}