diff --git a/e2e/node/src/node.test.ts b/e2e/node/src/node.test.ts index 927efa6e8c349..5ee3186f4d540 100644 --- a/e2e/node/src/node.test.ts +++ b/e2e/node/src/node.test.ts @@ -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), diff --git a/packages/next/src/executors/build/lib/create-package-json.ts b/packages/next/src/executors/build/lib/create-package-json.ts index 65303b9eaea10..c0a784a3b1e3f 100644 --- a/packages/next/src/executors/build/lib/create-package-json.ts +++ b/packages/next/src/executors/build/lib/create-package-json.ts @@ -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); } diff --git a/packages/node/src/utils/generate-package-json.ts b/packages/node/src/utils/generate-package-json.ts index e3d3c72bc3d06..21b8db5c1d7c0 100644 --- a/packages/node/src/utils/generate-package-json.ts +++ b/packages/node/src/utils/generate-package-json.ts @@ -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() -) { - 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; -} diff --git a/packages/workspace/src/utilities/create-package-json.ts b/packages/workspace/src/utilities/create-package-json.ts new file mode 100644 index 0000000000000..2d961487f4619 --- /dev/null +++ b/packages/workspace/src/utilities/create-package-json.ts @@ -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() +) { + 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() +) { + 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; + } +}