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
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type |
|
| Name | Type |
|
||||||
| :---------------------- | :---------------------------------------------------------------- |
|
| :---------------------------- | :---------------------------------------------------------------- |
|
||||||
| `projectName` | `string` |
|
| `projectName` | `string` |
|
||||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||||
| `options` | `Object` |
|
| `options` | `Object` |
|
||||||
| `options.isProduction?` | `boolean` |
|
| `options.helperDependencies?` | `string`[] |
|
||||||
| `options.root?` | `string` |
|
| `options.isProduction?` | `boolean` |
|
||||||
|
| `options.root?` | `string` |
|
||||||
|
| `options.target?` | `string` |
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
|
|||||||
@ -1070,13 +1070,15 @@ Convert an Nx Generator into an Angular Devkit Schematic.
|
|||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type |
|
| Name | Type |
|
||||||
| :---------------------- | :---------------------------------------------------------------- |
|
| :---------------------------- | :---------------------------------------------------------------- |
|
||||||
| `projectName` | `string` |
|
| `projectName` | `string` |
|
||||||
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
| `graph` | [`ProjectGraph`](../../devkit/documents/nrwl_devkit#projectgraph) |
|
||||||
| `options` | `Object` |
|
| `options` | `Object` |
|
||||||
| `options.isProduction?` | `boolean` |
|
| `options.helperDependencies?` | `string`[] |
|
||||||
| `options.root?` | `string` |
|
| `options.isProduction?` | `boolean` |
|
||||||
|
| `options.root?` | `string` |
|
||||||
|
| `options.target?` | `string` |
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
UpdatePackageJsonOption,
|
UpdatePackageJsonOption,
|
||||||
} from './update-package-json';
|
} from './update-package-json';
|
||||||
import { vol } from 'memfs';
|
import { vol } from 'memfs';
|
||||||
import { ExecutorContext, ProjectGraph } from '@nrwl/devkit';
|
import { DependencyType, ExecutorContext, ProjectGraph } from '@nrwl/devkit';
|
||||||
import { DependentBuildableProjectNode } from '../buildable-libs-utils';
|
import { DependentBuildableProjectNode } from '../buildable-libs-utils';
|
||||||
|
|
||||||
jest.mock('nx/src/utils/workspace-root', () => ({
|
jest.mock('nx/src/utils/workspace-root', () => ({
|
||||||
@ -298,7 +298,24 @@ describe('updatePackageJson', () => {
|
|||||||
outputs: ['{workspaceRoot}/dist/libs/lib1'],
|
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: {
|
dependencies: {
|
||||||
'@org/lib1': [
|
'@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) {
|
if (options.updateBuildableProjectDepsInPackageJson) {
|
||||||
packageJson = createPackageJson(context.projectName, context.projectGraph, {
|
packageJson = createPackageJson(context.projectName, context.projectGraph, {
|
||||||
|
target: context.targetName,
|
||||||
root: context.root,
|
root: context.root,
|
||||||
// By default we remove devDependencies since this is a production build.
|
// By default we remove devDependencies since this is a production build.
|
||||||
isProduction: true,
|
isProduction: true,
|
||||||
|
|||||||
@ -84,6 +84,7 @@ export default async function buildExecutor(
|
|||||||
context.projectName,
|
context.projectName,
|
||||||
context.projectGraph,
|
context.projectGraph,
|
||||||
{
|
{
|
||||||
|
target: context.targetName,
|
||||||
root: context.root,
|
root: context.root,
|
||||||
isProduction: !options.includeDevDependenciesInPackageJson, // By default we remove devDependencies since this is a production build.
|
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',
|
projects: 'self',
|
||||||
fileset: '{projectRoot}/**/*',
|
fileset: '{projectRoot}/**/*',
|
||||||
@ -221,11 +221,7 @@ class TaskHasher {
|
|||||||
if (!projectNode) {
|
if (!projectNode) {
|
||||||
return this.hashExternalDependency(task.target.project);
|
return this.hashExternalDependency(task.target.project);
|
||||||
}
|
}
|
||||||
const namedInputs = {
|
const namedInputs = getNamedInputs(this.nxJson, projectNode);
|
||||||
default: [{ fileset: '{projectRoot}/**/*' }],
|
|
||||||
...this.nxJson.namedInputs,
|
|
||||||
...projectNode.data.namedInputs,
|
|
||||||
};
|
|
||||||
const targetData = projectNode.data.targets[task.target.target];
|
const targetData = projectNode.data.targets[task.target.target];
|
||||||
const targetDefaults = (this.nxJson.targetDefaults || {})[
|
const targetDefaults = (this.nxJson.targetDefaults || {})[
|
||||||
task.target.target
|
task.target.target
|
||||||
@ -399,9 +395,7 @@ class TaskHasher {
|
|||||||
projectName: string,
|
projectName: string,
|
||||||
inputs: ExpandedSelfInput[]
|
inputs: ExpandedSelfInput[]
|
||||||
): Promise<PartialHash[]> {
|
): Promise<PartialHash[]> {
|
||||||
const filesets = inputs
|
const filesets = extractPatternsFromFileSets(inputs);
|
||||||
.filter((r) => !!r['fileset'])
|
|
||||||
.map((r) => r['fileset']);
|
|
||||||
|
|
||||||
const projectFilesets = [];
|
const projectFilesets = [];
|
||||||
const workspaceFilesets = [];
|
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(
|
export function splitInputsIntoSelfAndDependencies(
|
||||||
inputs: (InputDefinition | string)[],
|
inputs: ReadonlyArray<InputDefinition | string>,
|
||||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||||
): {
|
): {
|
||||||
depsInputs: { input: string }[];
|
depsInputs: { input: string }[];
|
||||||
selfInputs: ExpandedSelfInput[];
|
selfInputs: ExpandedSelfInput[];
|
||||||
@ -591,8 +628,8 @@ export function splitInputsIntoSelfAndDependencies(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function expandSelfInputs(
|
function expandSelfInputs(
|
||||||
inputs: (InputDefinition | string)[],
|
inputs: ReadonlyArray<InputDefinition | string>,
|
||||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||||
): ExpandedSelfInput[] {
|
): ExpandedSelfInput[] {
|
||||||
const expanded = [];
|
const expanded = [];
|
||||||
for (const d of inputs) {
|
for (const d of inputs) {
|
||||||
@ -623,7 +660,7 @@ function expandSelfInputs(
|
|||||||
|
|
||||||
export function expandNamedInput(
|
export function expandNamedInput(
|
||||||
input: string,
|
input: string,
|
||||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
namedInputs: { [inputName: string]: ReadonlyArray<InputDefinition | string> }
|
||||||
): ExpandedSelfInput[] {
|
): ExpandedSelfInput[] {
|
||||||
namedInputs ||= {};
|
namedInputs ||= {};
|
||||||
if (!namedInputs[input]) throw new Error(`Input '${input}' is not defined`);
|
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 { readJsonFile } from './fileutils';
|
||||||
import { sortObjectByKeys } from './object-sort';
|
import { sortObjectByKeys } from './object-sort';
|
||||||
import { ProjectGraph } from '../config/project-graph';
|
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
|
||||||
import { PackageJson } from './package-json';
|
import { PackageJson } from './package-json';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { workspaceRoot } from './workspace-root';
|
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.
|
* Creates a package.json in the output directory for support to install dependencies within containers.
|
||||||
@ -15,11 +23,42 @@ export function createPackageJson(
|
|||||||
projectName: string,
|
projectName: string,
|
||||||
graph: ProjectGraph,
|
graph: ProjectGraph,
|
||||||
options: {
|
options: {
|
||||||
|
target?: string;
|
||||||
root?: string;
|
root?: string;
|
||||||
isProduction?: boolean;
|
isProduction?: boolean;
|
||||||
|
helperDependencies?: string[];
|
||||||
} = {}
|
} = {}
|
||||||
): PackageJson {
|
): 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
|
// default package.json if one does not exist
|
||||||
let packageJson: PackageJson = {
|
let packageJson: PackageJson = {
|
||||||
name: projectName,
|
name: projectName,
|
||||||
@ -104,64 +143,73 @@ export function createPackageJson(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findAllNpmDeps(
|
function findAllNpmDeps(
|
||||||
projectName: string,
|
projectNode: ProjectGraphProjectNode,
|
||||||
graph: ProjectGraph,
|
graph: ProjectGraph,
|
||||||
list: {
|
npmDeps: NpmDeps,
|
||||||
dependencies: Record<string, string>;
|
seen: Set<string>,
|
||||||
peerDependencies: Record<string, string>;
|
dependencyPatterns: string[],
|
||||||
peerDependenciesMeta: Record<string, { optional: boolean }>;
|
rootPatterns?: string[]
|
||||||
} = { dependencies: {}, peerDependencies: {}, peerDependenciesMeta: {} },
|
): void {
|
||||||
seen = new Set<string>()
|
if (seen.has(projectNode.name)) return;
|
||||||
) {
|
|
||||||
const node = graph.externalNodes[projectName];
|
|
||||||
|
|
||||||
if (seen.has(projectName)) {
|
seen.add(projectNode.name);
|
||||||
// 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(projectName);
|
const projectFiles = filterUsingGlobPatterns(
|
||||||
|
projectNode.data.root,
|
||||||
|
projectNode.data.files,
|
||||||
|
rootPatterns ?? dependencyPatterns
|
||||||
|
);
|
||||||
|
|
||||||
if (node) {
|
const projectDependencies = new Set<string>();
|
||||||
list.dependencies[node.data.packageName] = node.data.version;
|
|
||||||
recursivelyCollectPeerDependencies(node.name, graph, list, seen);
|
projectFiles.forEach((fileData) =>
|
||||||
} else {
|
fileData.dependencies?.forEach((dep) => projectDependencies.add(dep.target))
|
||||||
// we are not interested in the dependencies of external projects
|
);
|
||||||
graph.dependencies[projectName]?.forEach((dep) => {
|
|
||||||
if (dep.type === 'static' || dep.type === 'dynamic') {
|
for (const dep of projectDependencies) {
|
||||||
findAllNpmDeps(dep.target, graph, list, seen);
|
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(
|
function recursivelyCollectPeerDependencies(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
graph: ProjectGraph,
|
graph: ProjectGraph,
|
||||||
list: {
|
npmDeps: NpmDeps,
|
||||||
dependencies: Record<string, string>;
|
seen: Set<string>
|
||||||
peerDependencies: Record<string, string>;
|
|
||||||
peerDependenciesMeta: Record<string, { optional: boolean }>;
|
|
||||||
},
|
|
||||||
seen = new Set<string>()
|
|
||||||
) {
|
) {
|
||||||
const npmPackage = graph.externalNodes[projectName];
|
const npmPackage = graph.externalNodes[projectName];
|
||||||
if (!npmPackage) {
|
if (!npmPackage) {
|
||||||
return list;
|
return npmDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageName = npmPackage.data.packageName;
|
const packageName = npmPackage.data.packageName;
|
||||||
try {
|
try {
|
||||||
const packageJson = require(`${packageName}/package.json`);
|
const packageJson = require(`${packageName}/package.json`);
|
||||||
if (!packageJson.peerDependencies) {
|
if (!packageJson.peerDependencies) {
|
||||||
return list;
|
return npmDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(packageJson.peerDependencies)
|
Object.keys(packageJson.peerDependencies)
|
||||||
@ -171,21 +219,21 @@ function recursivelyCollectPeerDependencies(
|
|||||||
.forEach((node) => {
|
.forEach((node) => {
|
||||||
if (!seen.has(node.name)) {
|
if (!seen.has(node.name)) {
|
||||||
seen.add(node.name);
|
seen.add(node.name);
|
||||||
list.peerDependencies[node.data.packageName] = node.data.version;
|
npmDeps.peerDependencies[node.data.packageName] = node.data.version;
|
||||||
if (
|
if (
|
||||||
packageJson.peerDependenciesMeta &&
|
packageJson.peerDependenciesMeta &&
|
||||||
packageJson.peerDependenciesMeta[node.data.packageName] &&
|
packageJson.peerDependenciesMeta[node.data.packageName] &&
|
||||||
packageJson.peerDependenciesMeta[node.data.packageName].optional
|
packageJson.peerDependenciesMeta[node.data.packageName].optional
|
||||||
) {
|
) {
|
||||||
list.peerDependenciesMeta[node.data.packageName] = {
|
npmDeps.peerDependenciesMeta[node.data.packageName] = {
|
||||||
optional: true,
|
optional: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
recursivelyCollectPeerDependencies(node.name, graph, list, seen);
|
recursivelyCollectPeerDependencies(node.name, graph, npmDeps, seen);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return list;
|
return npmDeps;
|
||||||
} catch (e) {
|
} 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(
|
const packageJson = createPackageJson(
|
||||||
this.context.projectName,
|
this.context.projectName,
|
||||||
this.projectGraph,
|
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;
|
packageJson.main = packageJson.main ?? this.options.outputFileName;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user