feat(core): use inputs to determine package dependencies (#13966)
This commit is contained in:
parent
9acd7757ae
commit
ebdb193f0e
@ -1070,13 +1070,15 @@ Convert an Nx Generator into an Angular Devkit Schematic.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :---------------------- | :---------------------------------------------------------------- |
|
||||
| `projectName` | `string` |
|
||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||
| `options` | `Object` |
|
||||
| `options.isProduction?` | `boolean` |
|
||||
| `options.root?` | `string` |
|
||||
| Name | Type |
|
||||
| :---------------------------- | :---------------------------------------------------------------- |
|
||||
| `projectName` | `string` |
|
||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||
| `options` | `Object` |
|
||||
| `options.helperDependencies?` | `string`[] |
|
||||
| `options.isProduction?` | `boolean` |
|
||||
| `options.root?` | `string` |
|
||||
| `options.target?` | `string` |
|
||||
|
||||
#### Returns
|
||||
|
||||
|
||||
@ -1070,13 +1070,15 @@ Convert an Nx Generator into an Angular Devkit Schematic.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :---------------------- | :---------------------------------------------------------------- |
|
||||
| `projectName` | `string` |
|
||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||
| `options` | `Object` |
|
||||
| `options.isProduction?` | `boolean` |
|
||||
| `options.root?` | `string` |
|
||||
| Name | Type |
|
||||
| :---------------------------- | :---------------------------------------------------------------- |
|
||||
| `projectName` | `string` |
|
||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||
| `options` | `Object` |
|
||||
| `options.helperDependencies?` | `string`[] |
|
||||
| `options.isProduction?` | `boolean` |
|
||||
| `options.root?` | `string` |
|
||||
| `options.target?` | `string` |
|
||||
|
||||
#### Returns
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
UpdatePackageJsonOption,
|
||||
} from './update-package-json';
|
||||
import { vol } from 'memfs';
|
||||
import { ExecutorContext, ProjectGraph } from '@nrwl/devkit';
|
||||
import { DependencyType, ExecutorContext, ProjectGraph } from '@nrwl/devkit';
|
||||
import { DependentBuildableProjectNode } from '../buildable-libs-utils';
|
||||
|
||||
jest.mock('nx/src/utils/workspace-root', () => ({
|
||||
@ -298,7 +298,24 @@ describe('updatePackageJson', () => {
|
||||
outputs: ['{workspaceRoot}/dist/libs/lib1'],
|
||||
},
|
||||
},
|
||||
files: [],
|
||||
files: [
|
||||
{
|
||||
file: 'test.ts',
|
||||
hash: '',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:external1',
|
||||
source: '@org/lib1',
|
||||
},
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:external2',
|
||||
source: '@org/lib1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -330,8 +347,16 @@ describe('updatePackageJson', () => {
|
||||
},
|
||||
dependencies: {
|
||||
'@org/lib1': [
|
||||
{ source: '@org/lib1', target: 'npm:external1', type: 'static' },
|
||||
{ source: '@org/lib1', target: 'npm:external2', type: 'static' },
|
||||
{
|
||||
source: '@org/lib1',
|
||||
target: 'npm:external1',
|
||||
type: DependencyType.static,
|
||||
},
|
||||
{
|
||||
source: '@org/lib1',
|
||||
target: 'npm:external2',
|
||||
type: DependencyType.static,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@ -54,6 +54,7 @@ export function updatePackageJson(
|
||||
|
||||
if (options.updateBuildableProjectDepsInPackageJson) {
|
||||
packageJson = createPackageJson(context.projectName, context.projectGraph, {
|
||||
target: context.targetName,
|
||||
root: context.root,
|
||||
// By default we remove devDependencies since this is a production build.
|
||||
isProduction: true,
|
||||
|
||||
@ -84,6 +84,7 @@ export default async function buildExecutor(
|
||||
context.projectName,
|
||||
context.projectGraph,
|
||||
{
|
||||
target: context.targetName,
|
||||
root: context.root,
|
||||
isProduction: !options.includeDevDependenciesInPackageJson, // By default we remove devDependencies since this is a production build.
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ export class Hasher {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_INPUTS = [
|
||||
const DEFAULT_INPUTS: ReadonlyArray<InputDefinition> = [
|
||||
{
|
||||
projects: 'self',
|
||||
fileset: '{projectRoot}/**/*',
|
||||
@ -221,11 +221,7 @@ class TaskHasher {
|
||||
if (!projectNode) {
|
||||
return this.hashExternalDependency(task.target.project);
|
||||
}
|
||||
const namedInputs = {
|
||||
default: [{ fileset: '{projectRoot}/**/*' }],
|
||||
...this.nxJson.namedInputs,
|
||||
...projectNode.data.namedInputs,
|
||||
};
|
||||
const namedInputs = getNamedInputs(this.nxJson, projectNode);
|
||||
const targetData = projectNode.data.targets[task.target.target];
|
||||
const targetDefaults = (this.nxJson.targetDefaults || {})[
|
||||
task.target.target
|
||||
@ -399,9 +395,7 @@ class TaskHasher {
|
||||
projectName: string,
|
||||
inputs: ExpandedSelfInput[]
|
||||
): Promise<PartialHash[]> {
|
||||
const filesets = inputs
|
||||
.filter((r) => !!r['fileset'])
|
||||
.map((r) => r['fileset']);
|
||||
const filesets = extractPatternsFromFileSets(inputs);
|
||||
|
||||
const projectFilesets = [];
|
||||
const workspaceFilesets = [];
|
||||
@ -563,9 +557,52 @@ class TaskHasher {
|
||||
}
|
||||
}
|
||||
|
||||
export function getNamedInputs(
|
||||
nxJson: NxJsonConfiguration,
|
||||
project: ProjectGraphProjectNode
|
||||
) {
|
||||
return {
|
||||
default: [{ fileset: '{projectRoot}/**/*' }],
|
||||
...nxJson.namedInputs,
|
||||
...project.data.namedInputs,
|
||||
};
|
||||
}
|
||||
|
||||
export function getTargetInputs(
|
||||
nxJson: NxJsonConfiguration,
|
||||
projectNode: ProjectGraphProjectNode,
|
||||
target: string
|
||||
) {
|
||||
const namedInputs = getNamedInputs(nxJson, projectNode);
|
||||
|
||||
const targetData = projectNode.data.targets[target];
|
||||
const targetDefaults = (nxJson.targetDefaults || {})[target];
|
||||
|
||||
const inputs = splitInputsIntoSelfAndDependencies(
|
||||
targetData.inputs || targetDefaults?.inputs || DEFAULT_INPUTS,
|
||||
namedInputs
|
||||
);
|
||||
|
||||
const selfInputs = extractPatternsFromFileSets(inputs.selfInputs);
|
||||
|
||||
const dependencyInputs = extractPatternsFromFileSets(
|
||||
inputs.depsInputs.map((s) => expandNamedInput(s.input, namedInputs)).flat()
|
||||
);
|
||||
|
||||
return { selfInputs, dependencyInputs };
|
||||
}
|
||||
|
||||
export function extractPatternsFromFileSets(
|
||||
inputs: readonly ExpandedSelfInput[]
|
||||
): string[] {
|
||||
return inputs
|
||||
.filter((c): c is { fileset: string } => !!c['fileset'])
|
||||
.map((c) => c['fileset']);
|
||||
}
|
||||
|
||||
export function splitInputsIntoSelfAndDependencies(
|
||||
inputs: (InputDefinition | string)[],
|
||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
||||
inputs: ReadonlyArray<InputDefinition | string>,
|
||||
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||
): {
|
||||
depsInputs: { input: string }[];
|
||||
selfInputs: ExpandedSelfInput[];
|
||||
@ -591,8 +628,8 @@ export function splitInputsIntoSelfAndDependencies(
|
||||
}
|
||||
|
||||
function expandSelfInputs(
|
||||
inputs: (InputDefinition | string)[],
|
||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
||||
inputs: ReadonlyArray<InputDefinition | string>,
|
||||
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||
): ExpandedSelfInput[] {
|
||||
const expanded = [];
|
||||
for (const d of inputs) {
|
||||
@ -623,7 +660,7 @@ function expandSelfInputs(
|
||||
|
||||
export function expandNamedInput(
|
||||
input: string,
|
||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
||||
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||
): ExpandedSelfInput[] {
|
||||
namedInputs ||= {};
|
||||
if (!namedInputs[input]) throw new Error(`Input '${input}' is not defined`);
|
||||
|
||||
502
packages/nx/src/utils/create-package-json.spec.ts
Normal file
502
packages/nx/src/utils/create-package-json.spec.ts
Normal file
@ -0,0 +1,502 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as configModule from '../config/configuration';
|
||||
import { DependencyType } from '../config/project-graph';
|
||||
import * as hashModule from '../hasher/hasher';
|
||||
import { createPackageJson } from './create-package-json';
|
||||
import * as fileutilsModule from './fileutils';
|
||||
|
||||
describe('createPackageJson', () => {
|
||||
it('should add additional dependencies', () => {
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
jest.spyOn(fileutilsModule, 'readJsonFile').mockReturnValue({
|
||||
dependencies: {
|
||||
typescript: '4.8.4',
|
||||
tslib: '2.4.0',
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
createPackageJson(
|
||||
'lib1',
|
||||
{
|
||||
nodes: {
|
||||
lib1: {
|
||||
type: 'lib',
|
||||
name: 'lib1',
|
||||
data: { files: [], targets: {}, root: '' },
|
||||
},
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:tslib': {
|
||||
type: 'npm',
|
||||
name: 'npm:tslib',
|
||||
data: { version: '2.4.0', hash: '', packageName: 'tslib' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
},
|
||||
{ helperDependencies: ['npm:tslib'] }
|
||||
)
|
||||
).toEqual({
|
||||
dependencies: {
|
||||
tslib: '2.4.0',
|
||||
},
|
||||
name: 'lib1',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should only add file dependencies if target is specified', () => {
|
||||
jest.spyOn(configModule, 'readNxJson').mockReturnValueOnce({
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*'],
|
||||
production: ['!{projectRoot}/**/*.spec.ts'],
|
||||
},
|
||||
targetDefaults: {
|
||||
build: {
|
||||
inputs: ['default', 'production', '^production'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
jest.spyOn(fileutilsModule, 'readJsonFile').mockReturnValue({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
tslib: '2.4.0',
|
||||
jest: '29.0.0',
|
||||
typescript: '4.8.4',
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
createPackageJson(
|
||||
'lib1',
|
||||
{
|
||||
nodes: {
|
||||
lib1: {
|
||||
type: 'lib',
|
||||
name: 'lib1',
|
||||
data: {
|
||||
root: 'libs/lib1',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib1/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:typescript',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib1/src/main2.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib2',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib1/src/main.spec.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:jest',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lib2: {
|
||||
type: 'lib',
|
||||
name: 'lib2',
|
||||
data: {
|
||||
root: 'libs/lib2',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib2/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:axios',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib2/src/main.spec.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:jest',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:tslib': {
|
||||
type: 'npm',
|
||||
name: 'npm:tslib',
|
||||
data: { version: '2.4.0', hash: '', packageName: 'tslib' },
|
||||
},
|
||||
'npm:typescript': {
|
||||
type: 'npm',
|
||||
name: 'npm:typescript',
|
||||
data: { version: '4.8.4', hash: '', packageName: 'typescript' },
|
||||
},
|
||||
'npm:jest': {
|
||||
type: 'npm',
|
||||
name: 'npm:jest',
|
||||
data: { version: '29.0.0', hash: '', packageName: 'jest' },
|
||||
},
|
||||
'npm:axios': {
|
||||
type: 'npm',
|
||||
name: 'npm:jest',
|
||||
data: { version: '1.0.0', hash: '', packageName: 'axios' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
},
|
||||
{
|
||||
target: 'build',
|
||||
isProduction: true,
|
||||
helperDependencies: ['npm:tslib'],
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
tslib: '2.4.0',
|
||||
typescript: '4.8.4',
|
||||
},
|
||||
name: 'lib1',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should only add all dependencies if target is not specified', () => {
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
jest.spyOn(fileutilsModule, 'readJsonFile').mockReturnValue({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
tslib: '2.4.0',
|
||||
jest: '29.0.0',
|
||||
typescript: '4.8.4',
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
createPackageJson(
|
||||
'lib1',
|
||||
{
|
||||
nodes: {
|
||||
lib1: {
|
||||
type: 'lib',
|
||||
name: 'lib1',
|
||||
data: {
|
||||
root: 'libs/lib1',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib1/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:typescript',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib1/src/main2.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib2',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib1/src/main.spec.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:jest',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lib2: {
|
||||
type: 'lib',
|
||||
name: 'lib2',
|
||||
data: {
|
||||
root: 'libs/lib2',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib2/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:axios',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib2/src/main.spec.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:jest',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:tslib': {
|
||||
type: 'npm',
|
||||
name: 'npm:tslib',
|
||||
data: { version: '2.4.0', hash: '', packageName: 'tslib' },
|
||||
},
|
||||
'npm:typescript': {
|
||||
type: 'npm',
|
||||
name: 'npm:typescript',
|
||||
data: { version: '4.8.4', hash: '', packageName: 'typescript' },
|
||||
},
|
||||
'npm:jest': {
|
||||
type: 'npm',
|
||||
name: 'npm:jest',
|
||||
data: { version: '29.0.0', hash: '', packageName: 'jest' },
|
||||
},
|
||||
'npm:axios': {
|
||||
type: 'npm',
|
||||
name: 'npm:axios',
|
||||
data: { version: '1.0.0', hash: '', packageName: 'axios' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
},
|
||||
{ isProduction: true, helperDependencies: ['npm:tslib'] }
|
||||
)
|
||||
).toEqual({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
jest: '29.0.0',
|
||||
tslib: '2.4.0',
|
||||
typescript: '4.8.4',
|
||||
},
|
||||
name: 'lib1',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should cache filterUsingGlobPatterns', () => {
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
jest.spyOn(fileutilsModule, 'readJsonFile').mockReturnValue({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
tslib: '2.4.0',
|
||||
jest: '29.0.0',
|
||||
typescript: '4.8.4',
|
||||
},
|
||||
});
|
||||
const filterUsingGlobPatternsSpy = jest.spyOn(
|
||||
hashModule,
|
||||
'filterUsingGlobPatterns'
|
||||
);
|
||||
|
||||
expect(
|
||||
createPackageJson(
|
||||
'lib1',
|
||||
{
|
||||
nodes: {
|
||||
lib1: {
|
||||
type: 'lib',
|
||||
name: 'lib1',
|
||||
data: {
|
||||
root: 'libs/lib1',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib1/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib3',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
{
|
||||
file: 'libs/lib1/src/main2.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib2',
|
||||
source: 'lib1',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lib2: {
|
||||
type: 'lib',
|
||||
name: 'lib2',
|
||||
data: {
|
||||
root: 'libs/lib2',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib2/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib4',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lib3: {
|
||||
type: 'lib',
|
||||
name: 'lib3',
|
||||
data: {
|
||||
root: 'libs/lib3',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib3/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'lib4',
|
||||
source: 'lib3',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
lib4: {
|
||||
type: 'lib',
|
||||
name: 'lib4',
|
||||
data: {
|
||||
root: 'libs/lib4',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{
|
||||
file: 'libs/lib2/src/main.ts',
|
||||
dependencies: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
target: 'npm:axios',
|
||||
source: 'lib2',
|
||||
},
|
||||
],
|
||||
hash: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:axios': {
|
||||
type: 'npm',
|
||||
name: 'npm:axios',
|
||||
data: { version: '1.0.0', hash: '', packageName: 'axios' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
},
|
||||
{ isProduction: true }
|
||||
)
|
||||
).toEqual({
|
||||
dependencies: {
|
||||
axios: '1.0.0',
|
||||
},
|
||||
name: 'lib1',
|
||||
version: '0.0.1',
|
||||
});
|
||||
|
||||
expect(filterUsingGlobPatternsSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'libs/lib1',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(filterUsingGlobPatternsSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'libs/lib3',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(filterUsingGlobPatternsSpy).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'libs/lib4',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(filterUsingGlobPatternsSpy).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
'libs/lib2',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(filterUsingGlobPatternsSpy).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
@ -1,9 +1,17 @@
|
||||
import { readJsonFile } from './fileutils';
|
||||
import { sortObjectByKeys } from './object-sort';
|
||||
import { ProjectGraph } from '../config/project-graph';
|
||||
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
|
||||
import { PackageJson } from './package-json';
|
||||
import { existsSync } from 'fs';
|
||||
import { workspaceRoot } from './workspace-root';
|
||||
import { filterUsingGlobPatterns, getTargetInputs } from '../hasher/hasher';
|
||||
import { readNxJson } from '../config/configuration';
|
||||
|
||||
interface NpmDeps {
|
||||
readonly dependencies: Record<string, string>;
|
||||
readonly peerDependencies: Record<string, string>;
|
||||
readonly peerDependenciesMeta: Record<string, { optional: boolean }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a package.json in the output directory for support to install dependencies within containers.
|
||||
@ -15,11 +23,42 @@ export function createPackageJson(
|
||||
projectName: string,
|
||||
graph: ProjectGraph,
|
||||
options: {
|
||||
target?: string;
|
||||
root?: string;
|
||||
isProduction?: boolean;
|
||||
helperDependencies?: string[];
|
||||
} = {}
|
||||
): PackageJson {
|
||||
const npmDeps = findAllNpmDeps(projectName, graph);
|
||||
const projectNode = graph.nodes[projectName];
|
||||
|
||||
const { selfInputs, dependencyInputs } = options.target
|
||||
? getTargetInputs(readNxJson(), projectNode, options.target)
|
||||
: { selfInputs: [], dependencyInputs: [] };
|
||||
|
||||
const npmDeps: NpmDeps = {
|
||||
dependencies: {},
|
||||
peerDependencies: {},
|
||||
peerDependenciesMeta: {},
|
||||
};
|
||||
|
||||
const seen = new Set<string>();
|
||||
|
||||
options.helperDependencies?.forEach((dep) => {
|
||||
seen.add(dep);
|
||||
npmDeps.dependencies[graph.externalNodes[dep].data.packageName] =
|
||||
graph.externalNodes[dep].data.version;
|
||||
recursivelyCollectPeerDependencies(dep, graph, npmDeps, seen);
|
||||
});
|
||||
|
||||
findAllNpmDeps(
|
||||
projectNode,
|
||||
graph,
|
||||
npmDeps,
|
||||
seen,
|
||||
dependencyInputs,
|
||||
selfInputs
|
||||
);
|
||||
|
||||
// default package.json if one does not exist
|
||||
let packageJson: PackageJson = {
|
||||
name: projectName,
|
||||
@ -104,64 +143,73 @@ export function createPackageJson(
|
||||
}
|
||||
|
||||
function findAllNpmDeps(
|
||||
projectName: string,
|
||||
projectNode: ProjectGraphProjectNode,
|
||||
graph: ProjectGraph,
|
||||
list: {
|
||||
dependencies: Record<string, string>;
|
||||
peerDependencies: Record<string, string>;
|
||||
peerDependenciesMeta: Record<string, { optional: boolean }>;
|
||||
} = { dependencies: {}, peerDependencies: {}, peerDependenciesMeta: {} },
|
||||
seen = new Set<string>()
|
||||
) {
|
||||
const node = graph.externalNodes[projectName];
|
||||
npmDeps: NpmDeps,
|
||||
seen: Set<string>,
|
||||
dependencyPatterns: string[],
|
||||
rootPatterns?: string[]
|
||||
): void {
|
||||
if (seen.has(projectNode.name)) return;
|
||||
|
||||
if (seen.has(projectName)) {
|
||||
// if it's in peerDependencies, move it to regular dependencies
|
||||
// since this is a direct dependency of the project
|
||||
if (node && list.peerDependencies[node.data.packageName]) {
|
||||
list.dependencies[node.data.packageName] = node.data.version;
|
||||
delete list.peerDependencies[node.data.packageName];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
seen.add(projectNode.name);
|
||||
|
||||
seen.add(projectName);
|
||||
const projectFiles = filterUsingGlobPatterns(
|
||||
projectNode.data.root,
|
||||
projectNode.data.files,
|
||||
rootPatterns ?? dependencyPatterns
|
||||
);
|
||||
|
||||
if (node) {
|
||||
list.dependencies[node.data.packageName] = node.data.version;
|
||||
recursivelyCollectPeerDependencies(node.name, graph, list, seen);
|
||||
} else {
|
||||
// we are not interested in the dependencies of external projects
|
||||
graph.dependencies[projectName]?.forEach((dep) => {
|
||||
if (dep.type === 'static' || dep.type === 'dynamic') {
|
||||
findAllNpmDeps(dep.target, graph, list, seen);
|
||||
const projectDependencies = new Set<string>();
|
||||
|
||||
projectFiles.forEach((fileData) =>
|
||||
fileData.dependencies?.forEach((dep) => projectDependencies.add(dep.target))
|
||||
);
|
||||
|
||||
for (const dep of projectDependencies) {
|
||||
const node = graph.externalNodes[dep];
|
||||
|
||||
if (seen.has(dep)) {
|
||||
// if it's in peerDependencies, move it to regular dependencies
|
||||
// since this is a direct dependency of the project
|
||||
if (node && npmDeps.peerDependencies[node.data.packageName]) {
|
||||
npmDeps.dependencies[node.data.packageName] = node.data.version;
|
||||
delete npmDeps.peerDependencies[node.data.packageName];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (node) {
|
||||
seen.add(dep);
|
||||
npmDeps.dependencies[node.data.packageName] = node.data.version;
|
||||
recursivelyCollectPeerDependencies(node.name, graph, npmDeps, seen);
|
||||
} else {
|
||||
findAllNpmDeps(
|
||||
graph.nodes[dep],
|
||||
graph,
|
||||
npmDeps,
|
||||
seen,
|
||||
dependencyPatterns
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
function recursivelyCollectPeerDependencies(
|
||||
projectName: string,
|
||||
graph: ProjectGraph,
|
||||
list: {
|
||||
dependencies: Record<string, string>;
|
||||
peerDependencies: Record<string, string>;
|
||||
peerDependenciesMeta: Record<string, { optional: boolean }>;
|
||||
},
|
||||
seen = new Set<string>()
|
||||
npmDeps: NpmDeps,
|
||||
seen: Set<string>
|
||||
) {
|
||||
const npmPackage = graph.externalNodes[projectName];
|
||||
if (!npmPackage) {
|
||||
return list;
|
||||
return npmDeps;
|
||||
}
|
||||
|
||||
const packageName = npmPackage.data.packageName;
|
||||
try {
|
||||
const packageJson = require(`${packageName}/package.json`);
|
||||
if (!packageJson.peerDependencies) {
|
||||
return list;
|
||||
return npmDeps;
|
||||
}
|
||||
|
||||
Object.keys(packageJson.peerDependencies)
|
||||
@ -171,21 +219,21 @@ function recursivelyCollectPeerDependencies(
|
||||
.forEach((node) => {
|
||||
if (!seen.has(node.name)) {
|
||||
seen.add(node.name);
|
||||
list.peerDependencies[node.data.packageName] = node.data.version;
|
||||
npmDeps.peerDependencies[node.data.packageName] = node.data.version;
|
||||
if (
|
||||
packageJson.peerDependenciesMeta &&
|
||||
packageJson.peerDependenciesMeta[node.data.packageName] &&
|
||||
packageJson.peerDependenciesMeta[node.data.packageName].optional
|
||||
) {
|
||||
list.peerDependenciesMeta[node.data.packageName] = {
|
||||
npmDeps.peerDependenciesMeta[node.data.packageName] = {
|
||||
optional: true,
|
||||
};
|
||||
}
|
||||
recursivelyCollectPeerDependencies(node.name, graph, list, seen);
|
||||
recursivelyCollectPeerDependencies(node.name, graph, npmDeps, seen);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
return npmDeps;
|
||||
} catch (e) {
|
||||
return list;
|
||||
return npmDeps;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,17 +54,15 @@ export class GeneratePackageJsonPlugin implements WebpackPluginInstance {
|
||||
});
|
||||
}
|
||||
|
||||
if (helperDependencies.length > 0) {
|
||||
this.projectGraph.dependencies[this.context.projectName] =
|
||||
this.projectGraph.dependencies[this.context.projectName].concat(
|
||||
helperDependencies
|
||||
);
|
||||
}
|
||||
|
||||
const packageJson = createPackageJson(
|
||||
this.context.projectName,
|
||||
this.projectGraph,
|
||||
{ root: this.context.root, isProduction: true }
|
||||
{
|
||||
target: this.context.targetName,
|
||||
root: this.context.root,
|
||||
isProduction: true,
|
||||
helperDependencies: helperDependencies.map((dep) => dep.target),
|
||||
}
|
||||
);
|
||||
packageJson.main = packageJson.main ?? this.options.outputFileName;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user