Skip to content

Commit

Permalink
feat(core): add lockfile pruning to executors that generate package.j…
Browse files Browse the repository at this point in the history
…son (#13734)
  • Loading branch information
meeroslav committed Dec 9, 2022
1 parent 2941d87 commit 75de165
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/generated/packages/node.json
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@
},
"generatePackageJson": {
"type": "boolean",
"description": "Generates a `package.json` file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"default": false
},
"transformers": {
Expand Down
2 changes: 1 addition & 1 deletion docs/generated/packages/webpack.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@
},
"generatePackageJson": {
"type": "boolean",
"description": "Generates a `package.json` file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"default": false
},
"transformers": {
Expand Down
9 changes: 9 additions & 0 deletions e2e/next/src/next.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
checkFilesExist,
cleanupProject,
detectPackageManager,
isNotWindows,
killPorts,
newProject,
Expand All @@ -11,12 +12,14 @@ import {
runCLIAsync,
runCommandUntil,
runCypressTests,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nrwl/e2e/utils';
import { stringUtils } from '@nrwl/workspace';
import * as http from 'http';
import { getLockFileName } from 'nx/src/lock-file/lock-file';

describe('Next.js Applications', () => {
let proj: string;
Expand Down Expand Up @@ -447,6 +450,12 @@ async function checkApp(
expect(packageJson.dependencies['react-dom']).toBeDefined();
expect(packageJson.dependencies.next).toBeDefined();

checkFilesExist(
`dist/apps/${appName}/${getLockFileName(
detectPackageManager(tmpProjPath())
)}`
);

if (opts.checkLint) {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
Expand Down
7 changes: 7 additions & 0 deletions e2e/node/src/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
checkFilesExist,
cleanupProject,
createFile,
detectPackageManager,
expectJestTestsToPass,
killPorts,
newProject,
Expand All @@ -20,6 +21,7 @@ import {
} from '@nrwl/e2e/utils';
import { exec, execSync } from 'child_process';
import * as http from 'http';
import { getLockFileName } from 'nx/src/lock-file/lock-file';
import { satisfies } from 'semver';

function getData(port, path = '/api'): Promise<any> {
Expand Down Expand Up @@ -237,6 +239,11 @@ describe('Build Node apps', () => {
await runCLIAsync(`build ${nestapp} --generatePackageJson`);

checkFilesExist(`dist/apps/${nestapp}/package.json`);
checkFilesExist(
`dist/apps/${nestapp}/${getLockFileName(
detectPackageManager(tmpProjPath())
)}`
);
const packageJson = JSON.parse(
readFile(`dist/apps/${nestapp}/package.json`)
);
Expand Down
8 changes: 1 addition & 7 deletions e2e/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import {
workspaceRoot,
} from '@nrwl/devkit';
import { angularCliVersion } from '@nrwl/workspace/src/utils/versions';
import {
ChildProcess,
exec,
ExecOptions,
execSync,
ExecSyncOptions,
} from 'child_process';
import { ChildProcess, exec, execSync, ExecSyncOptions } from 'child_process';
import {
copySync,
createFileSync,
Expand Down
15 changes: 15 additions & 0 deletions packages/next/src/executors/build/lib/create-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { ExecutorContext } from '@nrwl/devkit';
import { writeJsonFile } from '@nrwl/devkit';
import { writeFileSync } from 'fs';
import {
getLockFileName,
pruneLockFileFromPackageJson,
} from 'nx/src/lock-file/lock-file';
import { createPackageJson as generatePackageJson } from 'nx/src/utils/create-package-json';
import type { NextBuildBuilderOptions } from '../../../utils/types';

Expand Down Expand Up @@ -32,4 +37,14 @@ export async function createPackageJson(
}

writeJsonFile(`${options.outputPath}/package.json`, packageJson);

// generate lock file
const prunedLockFile = pruneLockFileFromPackageJson(
packageJson,
!options.includeDevDependenciesInPackageJson
);
const lockFileName = getLockFileName();
writeFileSync(`${options.outputPath}/${lockFileName}`, prunedLockFile, {
encoding: 'utf-8',
});
}
2 changes: 1 addition & 1 deletion packages/node/src/executors/webpack/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
},
"generatePackageJson": {
"type": "boolean",
"description": "Generates a `package.json` file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"default": false
},
"transformers": {
Expand Down
49 changes: 42 additions & 7 deletions packages/nx/src/lock-file/lock-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ import { existsSync } from 'fs';
import { createProjectGraphAsync } from '../project-graph/project-graph';
import { createPackageJson } from '../utils/create-package-json';
import { normalizePackageJson } from './utils/pruning';
import { PackageJson } from '../utils/package-json';

const YARN_LOCK_PATH = join(workspaceRoot, 'yarn.lock');
const NPM_LOCK_PATH = join(workspaceRoot, 'package-lock.json');
const PNPM_LOCK_PATH = join(workspaceRoot, 'pnpm-lock.yaml');
const YARN_LOCK_FILE = 'yarn.lock';
const NPM_LOCK_FILE = 'package-lock.json';
const PNPM_LOCK_FILE = 'pnpm-lock.yaml';

const YARN_LOCK_PATH = join(workspaceRoot, YARN_LOCK_FILE);
const NPM_LOCK_PATH = join(workspaceRoot, NPM_LOCK_FILE);
const PNPM_LOCK_PATH = join(workspaceRoot, PNPM_LOCK_FILE);

/**
* Check if lock file exists
Expand Down Expand Up @@ -161,6 +166,21 @@ export function writeLockFile(
throw Error(`Unknown package manager: ${packageManager}`);
}

export function getLockFileName(
packageManager: PackageManager = detectPackageManager(workspaceRoot)
): string {
if (packageManager === 'yarn') {
return YARN_LOCK_FILE;
}
if (packageManager === 'pnpm') {
return PNPM_LOCK_FILE;
}
if (packageManager === 'npm') {
return NPM_LOCK_FILE;
}
throw Error(`Unknown package manager: ${packageManager}`);
}

/**
* Prune lock file based on the given project's dependencies and overrides in local package.json
*
Expand All @@ -174,20 +194,35 @@ export async function pruneLockFile(
isProduction = true,
packageManager: PackageManager = detectPackageManager(workspaceRoot)
): Promise<string> {
const lockFileData = parseLockFile(packageManager);
const projectGraph = await createProjectGraphAsync();

if (!projectGraph.nodes[projectName]) {
throw Error(`Project "${projectName}" was not found.`);
}

const packageJson = createPackageJson(projectName, projectGraph, {});
// cleanup irrelevant fields from the generated package.json
const normalizedPackageJson = normalizePackageJson(
return pruneLockFileFromPackageJson(
packageJson,
isProduction,
projectName
packageManager
);
}

/**
* Prune lock file based on the package.json
*
* @param packageJson
* @param isProduction
* @param packageManager
* @returns
*/
export function pruneLockFileFromPackageJson(
packageJson: PackageJson,
isProduction = true,
packageManager: PackageManager = detectPackageManager(workspaceRoot)
): string {
const lockFileData = parseLockFile(packageManager);
const normalizedPackageJson = normalizePackageJson(packageJson, isProduction);

if (packageManager === 'yarn') {
const prunedData = pruneYarnLockFile(lockFileData, normalizedPackageJson);
Expand Down
5 changes: 2 additions & 3 deletions packages/nx/src/lock-file/utils/pruning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ export type PackageJsonDeps = Pick<
*/
export function normalizePackageJson(
packageJson: PackageJson,
isProduction: boolean,
projectName: string
isProduction: boolean
): PackageJsonDeps {
const normalized: PackageJsonDeps = {
name: packageJson.name || projectName,
name: packageJson.name,
version: packageJson.version || '0.0.0',
...(packageJson.license && { license: packageJson.license }),
};
Expand Down
2 changes: 1 addition & 1 deletion packages/webpack/src/executors/webpack/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
},
"generatePackageJson": {
"type": "boolean",
"description": "Generates a `package.json` file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
"default": false
},
"transformers": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type Compiler, sources, type WebpackPluginInstance } from 'webpack';
import {
ExecutorContext,
ProjectConfiguration,
type ProjectGraph,
serializeJson,
} from '@nrwl/devkit';
Expand All @@ -13,6 +12,10 @@ import {
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';

import { NormalizedWebpackExecutorOptions } from '../executors/webpack/schema';
import {
getLockFileName,
pruneLockFileFromPackageJson,
} from 'nx/src/lock-file/lock-file';

export class GeneratePackageJsonWebpackPlugin implements WebpackPluginInstance {
private readonly projectGraph: ProjectGraph;
Expand Down Expand Up @@ -76,6 +79,10 @@ export class GeneratePackageJsonWebpackPlugin implements WebpackPluginInstance {
'package.json',
new sources.RawSource(serializeJson(packageJson))
);
compilation.emitAsset(
getLockFileName(),
new sources.RawSource(pruneLockFileFromPackageJson(packageJson))
);
}
);
});
Expand Down

1 comment on commit 75de165

@vercel
Copy link

@vercel vercel bot commented on 75de165 Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.