Skip to content

Commit

Permalink
fix(nextjs): include peer dependencies in built package.json
Browse files Browse the repository at this point in the history
  • Loading branch information
kirjai authored and vsavkin committed Mar 15, 2021
1 parent caf5531 commit 9d863a7
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 129 deletions.
3 changes: 3 additions & 0 deletions e2e/node/src/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ describe('Build Node apps', () => {
dependencies: {
'@nestjs/common': '^7.0.0',
'@nestjs/core': '^7.0.0',
'@nestjs/platform-express': '^7.0.0',
'reflect-metadata': '^0.1.13',
rxjs: '~6.6.3',
},
main: 'main.js',
name: expect.any(String),
Expand Down
84 changes: 20 additions & 64 deletions packages/next/src/executors/build/lib/create-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,32 @@
import { ExecutorContext } from '@nrwl/devkit';

import { writeFileSync } from 'fs';
import { join } from 'path';

import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
import { readJsonFile } from '@nrwl/workspace/src/utilities/fileutils';
import { calculateProjectDependencies } from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import { writeJsonFile } from '@nrwl/workspace/src/utilities/fileutils';
import { createPackageJson as generatePackageJson } from '@nrwl/workspace/src/utilities/create-package-json';

import { NextBuildBuilderOptions } from '../../../utils/types';

function getProjectDeps(context: ExecutorContext, rootPackageJson: any) {
const projGraph = createProjectGraph();
const { dependencies: deps } = calculateProjectDependencies(
projGraph,
context.root,
context.projectName,
context.targetName,
context.configurationName
);
const depNames = deps
.map((d) => d.node)
.filter((node) => node.type === 'npm')
.map((node) => node.data.packageName)
// Need to make sure @nrwl/workspace is installed
// It is only a peer dependency of @nrwl/next so does not get installed automatically
// See: https://github.com/nrwl/nx/issues/4336
.concat('@nrwl/workspace');
const dependencies: string[] = depNames
.filter((packageName) => packageName in rootPackageJson.dependencies)
.reduce((deps, pkgName) => {
return { ...deps, [pkgName]: rootPackageJson.dependencies[pkgName] };
}, {});
const devDependencies = depNames
.filter((packageName) => packageName in rootPackageJson.devDependencies)
.reduce((deps, pkgName) => {
return { ...deps, [pkgName]: rootPackageJson.devDependencies[pkgName] };
}, {});
return {
dependencies,
devDependencies,
};
}

export function createPackageJson(
options: NextBuildBuilderOptions,
context: ExecutorContext
) {
const rootPackageJson = readJsonFile(join(context.root, 'package.json'));
const { dependencies, devDependencies } = getProjectDeps(
context,
rootPackageJson
);

const outPackageJson = {
name: context.projectName,
version: '0.0.1',
scripts: {
start: 'next start',
},
dependencies: {
...dependencies,
// peer deps of next, so we need to add them here
react: rootPackageJson.dependencies['react'],
'react-dom': rootPackageJson.dependencies['react-dom'],
next: rootPackageJson.dependencies['next'],
},
// needed for the next.config.js file
devDependencies,
};
const depGraph = createProjectGraph();
const packageJson = generatePackageJson(context.projectName, depGraph, {
root: context.root,
projectRoot: context.workspace.projects[context.projectName].sourceRoot,
});
if (!packageJson.scripts) {
packageJson.scripts = {};
}
packageJson.scripts.start = 'next start';
if (!packageJson.devDependencies) {
packageJson.devDependencies = {};
}
const nrwlWorkspaceNode = depGraph.nodes['npm:@nrwl/workspace'];

writeFileSync(
join(options.outputPath, 'package.json'),
JSON.stringify(outPackageJson, null, 2)
);
if (nrwlWorkspaceNode) {
packageJson.devDependencies['@nrwl/workspace'] =
nrwlWorkspaceNode.data.version;
}
writeJsonFile(`${options.outputPath}/package.json`, packageJson);
}
70 changes: 5 additions & 65 deletions packages/node/src/utils/generate-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,17 @@
import { ProjectGraph } from '@nrwl/workspace/src/core/project-graph';
import {
readJsonFile,
writeJsonFile,
} from '@nrwl/workspace/src/utilities/fileutils';
import { writeJsonFile } from '@nrwl/workspace/src/utilities/fileutils';

import { BuildNodeBuilderOptions } from './types';
import { createPackageJson } from '@nrwl/workspace/src/utilities/create-package-json';
import { OUT_FILENAME } from './config';

/**
* Creates a package.json in the output directory for support to install dependencies within containers.
*
* If a package.json exists in the project, it will reuse that.
*
* @param projectName
* @param graph
* @param options
* @constructor
*/
export function generatePackageJson(
projectName: string,
graph: ProjectGraph,
options: BuildNodeBuilderOptions
) {
const npmDeps = findAllNpmDeps(projectName, graph);
// default package.json if one does not exist
let packageJson = {
name: projectName,
version: '0.0.1',
main: OUT_FILENAME,
dependencies: {},
};

try {
packageJson = readJsonFile(`${options.projectRoot}/package.json`);
if (!packageJson.dependencies) {
packageJson.dependencies = {};
}
} catch (e) {}

const rootPackageJson = readJsonFile(`${options.root}/package.json`);

Object.entries(npmDeps).forEach(([packageName, version]) => {
// don't include devDeps
if (rootPackageJson.devDependencies?.[packageName]) {
return;
}

packageJson.dependencies[packageName] = version;
});

const packageJson = createPackageJson(projectName, graph, options);
packageJson.main = OUT_FILENAME;
delete packageJson.devDependencies;
writeJsonFile(`${options.outputPath}/package.json`, packageJson);
}

function findAllNpmDeps(
projectName: string,
graph: ProjectGraph,
list: { [packageName: string]: string } = {},
seen = new Set<string>()
) {
if (seen.has(projectName)) {
return list;
}

seen.add(projectName);

const node = graph.nodes[projectName];

if (node.type === 'npm') {
list[node.data.packageName] = node.data.version;
}
graph.dependencies[projectName]?.forEach((dep) => {
findAllNpmDeps(dep.target, graph, list, seen);
});

return list;
}
106 changes: 106 additions & 0 deletions packages/workspace/src/utilities/create-package-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { ProjectGraph } from '../core/project-graph';
import { readJsonFile } from './fileutils';

/**
* Creates a package.json in the output directory for support to install dependencies within containers.
*
* If a package.json exists in the project, it will reuse that.
*/
export function createPackageJson(
projectName: string,
graph: ProjectGraph,
options: {
projectRoot?: string;
root?: string;
}
): any {
const npmDeps = findAllNpmDeps(projectName, graph);
// default package.json if one does not exist
let packageJson = {
name: projectName,
version: '0.0.1',
dependencies: {},
devDependencies: {},
};
try {
packageJson = readJsonFile(`${options.projectRoot}/package.json`);
if (!packageJson.dependencies) {
packageJson.dependencies = {};
}
if (!packageJson.devDependencies) {
packageJson.devDependencies = {};
}
} catch (e) {}

const rootPackageJson = readJsonFile(`${options.root}/package.json`);
Object.entries(npmDeps).forEach(([packageName, version]) => {
if (rootPackageJson.devDependencies?.[packageName]) {
packageJson.devDependencies[packageName] = version;
} else {
packageJson.dependencies[packageName] = version;
}
});

return packageJson;
}

function findAllNpmDeps(
projectName: string,
graph: ProjectGraph,
list: { [packageName: string]: string } = {},
seen = new Set<string>()
) {
if (seen.has(projectName)) {
return list;
}

seen.add(projectName);

const node = graph.nodes[projectName];

if (node.type === 'npm') {
list[node.data.packageName] = node.data.version;
recursivelyCollectPeerDependencies(node.name, graph, list);
}
graph.dependencies[projectName]?.forEach((dep) => {
findAllNpmDeps(dep.target, graph, list, seen);
});

return list;
}

function recursivelyCollectPeerDependencies(
projectName: string,
graph: ProjectGraph,
list: { [packageName: string]: string } = {},
seen = new Set<string>()
) {
if (
!graph.nodes[projectName] ||
graph.nodes[projectName].type !== 'npm' ||
seen.has(projectName)
) {
return list;
}

seen.add(projectName);
const packageName = graph.nodes[projectName].data.packageName;
try {
const packageJson = require(`${packageName}/package.json`);
if (!packageJson.peerDependencies) {
return list;
}

Object.keys(packageJson.peerDependencies)
.map((dependencyName) => `npm:${dependencyName}`)
.map((dependency) => graph.nodes[dependency])
.filter(Boolean)
.forEach((node) => {
list[node.data.packageName] = node.data.version;
recursivelyCollectPeerDependencies(node.name, graph, list, seen);
});
return list;
} catch (e) {
return list;
}
}

0 comments on commit 9d863a7

Please sign in to comment.