<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior If `project.json` exists without a name, we infer one based on the root path. This can be confusing when there exists a `package.json` alongside it that contains a name which doesn't match our inferred name. ## Expected Behavior If `project.json` and `package.json` both exist, the name from `package.json` will be used if `project.json` contains no name. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #26347
268 lines
7.4 KiB
TypeScript
268 lines
7.4 KiB
TypeScript
import { existsSync } from 'fs';
|
|
import { dirname, join } from 'path';
|
|
import {
|
|
InputDefinition,
|
|
ProjectMetadata,
|
|
TargetConfiguration,
|
|
} from '../config/workspace-json-project-json';
|
|
import { mergeTargetConfigurations } from '../project-graph/utils/project-configuration-utils';
|
|
import { readJsonFile } from './fileutils';
|
|
import { getNxRequirePaths } from './installation-directory';
|
|
import {
|
|
PackageManagerCommands,
|
|
getPackageManagerCommand,
|
|
} from './package-manager';
|
|
|
|
export interface NxProjectPackageJsonConfiguration {
|
|
name?: string;
|
|
implicitDependencies?: string[];
|
|
tags?: string[];
|
|
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };
|
|
targets?: Record<string, TargetConfiguration>;
|
|
includedScripts?: string[];
|
|
}
|
|
|
|
export type ArrayPackageGroup = { package: string; version: string }[];
|
|
export type MixedPackageGroup =
|
|
| (string | { package: string; version: string })[]
|
|
| Record<string, string>;
|
|
export type PackageGroup = MixedPackageGroup | ArrayPackageGroup;
|
|
|
|
export interface NxMigrationsConfiguration {
|
|
migrations?: string;
|
|
packageGroup?: PackageGroup;
|
|
}
|
|
|
|
type PackageOverride = { [key: string]: string | PackageOverride };
|
|
|
|
export interface PackageJson {
|
|
// Generic Package.Json Configuration
|
|
name: string;
|
|
version: string;
|
|
license?: string;
|
|
private?: boolean;
|
|
scripts?: Record<string, string>;
|
|
type?: 'module' | 'commonjs';
|
|
main?: string;
|
|
types?: string;
|
|
module?: string;
|
|
exports?:
|
|
| string
|
|
| Record<
|
|
string,
|
|
string | { types?: string; require?: string; import?: string }
|
|
>;
|
|
dependencies?: Record<string, string>;
|
|
devDependencies?: Record<string, string>;
|
|
optionalDependencies?: Record<string, string>;
|
|
peerDependencies?: Record<string, string>;
|
|
peerDependenciesMeta?: Record<string, { optional: boolean }>;
|
|
resolutions?: Record<string, string>;
|
|
overrides?: PackageOverride;
|
|
bin?: Record<string, string> | string;
|
|
workspaces?:
|
|
| string[]
|
|
| {
|
|
packages: string[];
|
|
};
|
|
publishConfig?: Record<string, string>;
|
|
|
|
// Nx Project Configuration
|
|
nx?: NxProjectPackageJsonConfiguration;
|
|
|
|
// Nx Plugin Configuration
|
|
generators?: string;
|
|
schematics?: string;
|
|
builders?: string;
|
|
executors?: string;
|
|
'nx-migrations'?: string | NxMigrationsConfiguration;
|
|
'ng-update'?: string | NxMigrationsConfiguration;
|
|
packageManager?: string;
|
|
}
|
|
|
|
export function normalizePackageGroup(
|
|
packageGroup: PackageGroup
|
|
): ArrayPackageGroup {
|
|
return Array.isArray(packageGroup)
|
|
? packageGroup.map((x) =>
|
|
typeof x === 'string' ? { package: x, version: '*' } : x
|
|
)
|
|
: Object.entries(packageGroup).map(([pkg, version]) => ({
|
|
package: pkg,
|
|
version,
|
|
}));
|
|
}
|
|
|
|
export function readNxMigrateConfig(
|
|
json: Partial<PackageJson>
|
|
): NxMigrationsConfiguration & { packageGroup?: ArrayPackageGroup } {
|
|
const parseNxMigrationsConfig = (
|
|
fromJson?: string | NxMigrationsConfiguration
|
|
): NxMigrationsConfiguration & { packageGroup?: ArrayPackageGroup } => {
|
|
if (!fromJson) {
|
|
return {};
|
|
}
|
|
if (typeof fromJson === 'string') {
|
|
return { migrations: fromJson, packageGroup: [] };
|
|
}
|
|
|
|
return {
|
|
...(fromJson.migrations ? { migrations: fromJson.migrations } : {}),
|
|
...(fromJson.packageGroup
|
|
? { packageGroup: normalizePackageGroup(fromJson.packageGroup) }
|
|
: {}),
|
|
};
|
|
};
|
|
|
|
return {
|
|
...parseNxMigrationsConfig(json['ng-update']),
|
|
...parseNxMigrationsConfig(json['nx-migrations']),
|
|
// In case there's a `migrations` field in `package.json`
|
|
...parseNxMigrationsConfig(json as any),
|
|
};
|
|
}
|
|
|
|
export function buildTargetFromScript(
|
|
script: string,
|
|
scripts: Record<string, string> = {},
|
|
packageManagerCommand: PackageManagerCommands
|
|
): TargetConfiguration {
|
|
return {
|
|
executor: 'nx:run-script',
|
|
options: {
|
|
script,
|
|
},
|
|
metadata: {
|
|
scriptContent: scripts[script],
|
|
runCommand: packageManagerCommand.run(script),
|
|
},
|
|
};
|
|
}
|
|
|
|
let packageManagerCommand: PackageManagerCommands | undefined;
|
|
|
|
export function getMetadataFromPackageJson(
|
|
packageJson: PackageJson
|
|
): ProjectMetadata {
|
|
const { scripts, nx } = packageJson ?? {};
|
|
const includedScripts = nx?.includedScripts || Object.keys(scripts ?? {});
|
|
return {
|
|
targetGroups: {
|
|
'NPM Scripts': includedScripts,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function readTargetsFromPackageJson(packageJson: PackageJson) {
|
|
const { scripts, nx, private: isPrivate } = packageJson ?? {};
|
|
const res: Record<string, TargetConfiguration> = {};
|
|
const includedScripts = nx?.includedScripts || Object.keys(scripts ?? {});
|
|
packageManagerCommand ??= getPackageManagerCommand();
|
|
for (const script of includedScripts) {
|
|
res[script] = buildTargetFromScript(script, scripts, packageManagerCommand);
|
|
}
|
|
for (const targetName in nx?.targets) {
|
|
res[targetName] = mergeTargetConfigurations(
|
|
nx?.targets[targetName],
|
|
res[targetName]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add implicit nx-release-publish target for all package.json files that are
|
|
* not marked as `"private": true` to allow for lightweight configuration for
|
|
* package based repos.
|
|
*/
|
|
if (!isPrivate && !res['nx-release-publish']) {
|
|
res['nx-release-publish'] = {
|
|
dependsOn: ['^nx-release-publish'],
|
|
executor: '@nx/js:release-publish',
|
|
options: {},
|
|
};
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Uses `require.resolve` to read the package.json for a module.
|
|
*
|
|
* This will fail if the module doesn't export package.json
|
|
*
|
|
* @returns package json contents and path
|
|
*/
|
|
export function readModulePackageJsonWithoutFallbacks(
|
|
moduleSpecifier: string,
|
|
requirePaths = getNxRequirePaths()
|
|
): {
|
|
packageJson: PackageJson;
|
|
path: string;
|
|
} {
|
|
const packageJsonPath: string = require.resolve(
|
|
`${moduleSpecifier}/package.json`,
|
|
{
|
|
paths: requirePaths,
|
|
}
|
|
);
|
|
const packageJson: PackageJson = readJsonFile(packageJsonPath);
|
|
|
|
return {
|
|
path: packageJsonPath,
|
|
packageJson,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Reads the package.json file for a specified module.
|
|
*
|
|
* Includes a fallback that accounts for modules that don't export package.json
|
|
*
|
|
* @param {string} moduleSpecifier The module to look up
|
|
* @param {string[]} requirePaths List of paths look in. Pass `module.paths` to ensure non-hoisted dependencies are found.
|
|
*
|
|
* @example
|
|
* // Use the caller's lookup paths for non-hoisted dependencies
|
|
* readModulePackageJson('http-server', module.paths);
|
|
*
|
|
* @returns package json contents and path
|
|
*/
|
|
export function readModulePackageJson(
|
|
moduleSpecifier: string,
|
|
requirePaths = getNxRequirePaths()
|
|
): {
|
|
packageJson: PackageJson;
|
|
path: string;
|
|
} {
|
|
let packageJsonPath: string;
|
|
let packageJson: PackageJson;
|
|
|
|
try {
|
|
({ path: packageJsonPath, packageJson } =
|
|
readModulePackageJsonWithoutFallbacks(moduleSpecifier, requirePaths));
|
|
} catch {
|
|
const entryPoint = require.resolve(moduleSpecifier, {
|
|
paths: requirePaths,
|
|
});
|
|
|
|
let moduleRootPath = dirname(entryPoint);
|
|
packageJsonPath = join(moduleRootPath, 'package.json');
|
|
|
|
while (!existsSync(packageJsonPath)) {
|
|
moduleRootPath = dirname(moduleRootPath);
|
|
packageJsonPath = join(moduleRootPath, 'package.json');
|
|
}
|
|
|
|
packageJson = readJsonFile(packageJsonPath);
|
|
if (packageJson.name && packageJson.name !== moduleSpecifier) {
|
|
throw new Error(
|
|
`Found module ${packageJson.name} while trying to locate ${moduleSpecifier}/package.json`
|
|
);
|
|
}
|
|
}
|
|
|
|
return {
|
|
packageJson,
|
|
path: packageJsonPath,
|
|
};
|
|
}
|