Skip to content

Commit

Permalink
feat(js): standalone executor for generating package.json
Browse files Browse the repository at this point in the history
* generates a package.json based on the project's dependencies in node_modules
* moves getExtraDependencies helper from esbuild package to js package
  • Loading branch information
jensbodal committed Apr 16, 2023
1 parent fab2325 commit 896c1ca
Show file tree
Hide file tree
Showing 15 changed files with 512 additions and 1 deletion.
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -4787,6 +4787,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "generate-package-json",
"path": "/packages/js/executors/generate-package-json",
"name": "generate-package-json",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,15 @@
"originalFilePath": "/packages/js/src/executors/node/schema.json",
"path": "/packages/js/executors/node",
"type": "executor"
},
"/packages/js/executors/generate-package-json": {
"description": "Generates a `package.json` and pruned lock file (if generateLockfile not disabled) 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.",
"file": "generated/packages/js/executors/generate-package-json.json",
"hidden": false,
"name": "generate-package-json",
"originalFilePath": "/packages/js/src/executors/generate-package-json/schema.json",
"path": "/packages/js/executors/generate-package-json",
"type": "executor"
}
},
"generators": {
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,15 @@
"originalFilePath": "/packages/js/src/executors/node/schema.json",
"path": "js/executors/node",
"type": "executor"
},
{
"description": "Generates a `package.json` and pruned lock file (if generateLockfile not disabled) 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.",
"file": "generated/packages/js/executors/generate-package-json.json",
"hidden": false,
"name": "generate-package-json",
"originalFilePath": "/packages/js/src/executors/generate-package-json/schema.json",
"path": "js/executors/generate-package-json",
"type": "executor"
}
],
"generators": [
Expand Down
66 changes: 66 additions & 0 deletions docs/generated/packages/js/executors/generate-package-json.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "generate-package-json",
"implementation": "/packages/js/src/executors/generate-package-json/generate-package-json.impl.ts",
"schema": {
"$schema": "http://json-schema.org/schema",
"outputCapture": "direct-nodejs",
"version": 2,
"cli": "nx",
"title": "GeneratePackageJson builder",
"description": "Generates a package.json file",
"type": "object",
"properties": {
"main": {
"type": "string",
"description": "The path to the entry file, relative to project.",
"alias": "entryFile",
"x-completion-type": "file",
"x-completion-glob": "**/*@(.js|.ts)"
},
"outputPath": {
"type": "string",
"description": "The output path of the generated files.",
"x-completion-type": "directory",
"x-priority": "important"
},
"tsConfig": {
"type": "string",
"description": "The path to tsconfig file.",
"x-completion-type": "file",
"x-completion-glob": "tsconfig.*.json"
},
"buildableProjectDepsInPackageJsonType": {
"type": "string",
"description": "The type of dependency to use for buildable project dependencies in the generated `package.json`.",
"enum": ["dependencies", "devDependencies"],
"default": "dependencies"
},
"excludeLibsInPackageJson": {
"type": "boolean",
"description": "Exclude libraries in the `package.json` file. This is useful if you are using a `package.json` file in the project's directory.",
"default": true
},
"generateLockfile": {
"type": "boolean",
"description": "Generate a lock file for the generated `package.json`.",
"default": true
},
"format": {
"type": "string",
"description": "List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise).",
"alias": "f",
"enum": ["esm", "cjs"],
"default": "esm"
}
},
"definitions": {},
"required": ["main", "outputPath", "tsConfig"],
"examplesFile": "---\ntitle: Examples for the generate package json executor\ndescription: This page contains examples for the js @nrwl/js:generate-package-json executor.\n---\n\n`project.json`:\n\n```json\n{\n \"name\": \"api\",\n //...\n \"targets\": {\n \"build\": {\n \"dependsOn\": [\n { \"projects\": \"self\", \"target\": \"build\", \"params\": \"forward\" }\n ],\n \"executor\": \"@nrwl/js:generate-package-json\",\n \"options\": {\n \"outputPath\": \"dist/apps/api\",\n \"main\": \"apps/api/src/main.ts\",\n \"tsConfig\": \"apps/api/tsconfig.app.json\"\n }\n },\n \"build-api\": {\n \"executor\": \"@nrwl/vite:build\",\n //...\n \"configurations\": {\n \"production\": {\n \"mode\": \"production\"\n }\n }\n },\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n //...\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"api:build:production\"\n }\n }\n }\n }\n}\n```\n\n```bash\nnx build api --prod\nnx build api --dev\n```\n",
"presets": []
},
"description": "Generates a `package.json` and pruned lock file (if generateLockfile not disabled) 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.",
"aliases": [],
"hidden": false,
"path": "/packages/js/src/executors/generate-package-json/schema.json",
"type": "executor"
}
2 changes: 1 addition & 1 deletion packages/esbuild/src/executors/esbuild/esbuild.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
copyAssets,
copyPackageJson,
CopyPackageJsonOptions,
getExtraDependencies,
printDiagnostics,
runTypeCheck as _runTypeCheck,
TypeCheckOptions,
Expand All @@ -21,7 +22,6 @@ import {
getOutExtension,
getOutfile,
} from './lib/build-esbuild-options';
import { getExtraDependencies } from './lib/get-extra-dependencies';
import { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils';
import { join } from 'path';

Expand Down
49 changes: 49 additions & 0 deletions packages/js/docs/generate-package-json.examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: Examples for the generate package json executor
description: This page contains examples for the js @nrwl/js:generate-package-json executor.
---

`project.json`:

```json
{
"name": "api",
//...
"targets": {
"build": {
"dependsOn": [
{ "projects": "self", "target": "build", "params": "forward" }
],
"executor": "@nrwl/js:generate-package-json",
"options": {
"outputPath": "dist/apps/api",
"main": "apps/api/src/main.ts",
"tsConfig": "apps/api/tsconfig.app.json"
}
},
"build-api": {
"executor": "@nrwl/vite:build",
//...
"configurations": {
"production": {
"mode": "production"
}
}
},
"serve": {
"executor": "@nrwl/vite:dev-server",
//...
"configurations": {
"production": {
"buildTarget": "api:build:production"
}
}
}
}
}
```

```bash
nx build api --prod
nx build api --dev
```
5 changes: 5 additions & 0 deletions packages/js/executors.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
"implementation": "./src/executors/node/node.impl",
"schema": "./src/executors/node/schema.json",
"description": "Execute a Node application."
},
"generate-package-json": {
"implementation": "./src/executors/generate-package-json/generate-package-json.impl",
"schema": "./src/executors/generate-package-json/schema.json",
"description": "Generates a `package.json` and pruned lock file (if generateLockfile not disabled) 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."
}
},
"builders": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import 'nx/src/utils/testing/mock-fs';

import { GeneratePackageJsonExecutorOptions } from './schema';
import executor from './generate-package-json.impl';
import { ExecutorContext } from '@nrwl/devkit';
import { vol } from 'memfs';
import { readFileSync, statSync } from 'fs';
import { readdirSync } from 'fs-extra';
import { join } from 'path';

const getAllFiles = (dirPath: string, arrayOfFiles = []) => {
const files = readdirSync(dirPath);

files.forEach((file) => {
const filepath = join(dirPath, file);
if (statSync(filepath).isDirectory()) {
arrayOfFiles = getAllFiles(filepath, arrayOfFiles);
} else {
arrayOfFiles.push(filepath);
}
});

return arrayOfFiles;
};

describe('GeneratePackageJson Executor', () => {
const projectName = 'parent';
const projectPath = `apps/${projectName}`;
const outputPath = `dist/apps/${projectName}`;

const mockWorkspaceRoot = process.cwd();

const mockBuildOptions: GeneratePackageJsonExecutorOptions = {
main: `${projectPath}/index.js`,
outputPath,
tsConfig: `${projectPath}/tsconfig.app.json`,
};

const mockContext: ExecutorContext = {
cwd: '',
isVerbose: false,
root: mockWorkspaceRoot,
projectName,
target: {
options: mockBuildOptions,
},
targetName: 'build',
projectGraph: {
nodes: {
parent: {
type: 'app',
name: 'parent',
data: {
files: [],
root: projectPath,
targets: {
build: {
inputs: [],
options: mockBuildOptions,
},
},
},
},
child1: {
type: 'lib',
name: 'child1',
data: {},
},
child2: {
type: 'lib',
name: 'child2',
data: {},
},
} as any,
externalNodes: {
'npm:react': {
type: 'npm',
name: 'npm:react',
data: { packageName: 'react', version: '18.0.0' },
},
'npm:axios': {
type: 'npm',
name: 'npm:axios',
data: { packageName: 'axios', version: '1.0.0' },
},
'npm:dayjs': {
type: 'npm',
name: 'npm:dayjs',
data: { packageName: 'dayjs', version: '1.11.0' },
},
},
dependencies: {
parent: [
{ source: 'parent', target: 'child1', type: 'static' },
{ source: 'parent', target: 'npm:react', type: 'static' },
],
child1: [
{ source: 'child1', target: 'child2', type: 'static' },
{ source: 'child1', target: 'npm:axios', type: 'static' },
],
child2: [{ source: 'child2', target: 'npm:dayjs', type: 'static' }],
},
},
};

beforeEach(async () => {
vol.reset();
const fileSys = {
[`${projectPath}/package.json`]: JSON.stringify({
name: '@company/parent',
author: 'John Doe',
something: 'else',
}),
'package.json': JSON.stringify({
name: 'package.name.does.not.matter',
}),
'package-lock.json': JSON.stringify({
name: 'package-lock.name.does.not.matter',
version: 'package-lock.version.does.not.matter',
lockfileVersion: 3,
packages: {
'': {},
'node_modules/axios': {
version: '1.0.0',
},
'node_modules/dayjs': {
version: '1.11.0',
},
'node_modules/react': {
version: '18.0.0',
},
},
}),
};
vol.fromJSON(fileSys, mockWorkspaceRoot);
});

it('generates a package.json and package-lock.json with child dependencies', async () => {
const output = await executor(mockBuildOptions, mockContext);

const expectedPackageLockJson = {
name: '@company/parent',
lockfileVersion: 3,
version: '0.0.1',
packages: {
'': {
name: '@company/parent',
dependencies: {
axios: '1.0.0',
dayjs: '1.11.0',
react: '18.0.0',
},
},
'node_modules/axios': {
version: '1.0.0',
},
'node_modules/dayjs': {
version: '1.11.0',
},
'node_modules/react': {
version: '18.0.0',
},
},
};

const expectedPackageJson = {
name: '@company/parent',
author: 'John Doe',
something: 'else',
dependencies: {
axios: '1.0.0',
dayjs: '1.11.0',
react: '18.0.0',
},
module: './index.js',
type: 'module',
main: './index.js',
};

expect(
JSON.parse(readFileSync(`${outputPath}/package-lock.json`, 'utf-8'))
).toEqual(expectedPackageLockJson);

expect(
JSON.parse(readFileSync(`${outputPath}/package.json`, 'utf-8'))
).toEqual(expectedPackageJson);

expect(output.success).toBe(true);

expect(getAllFiles('')).toEqual([
`${projectPath}/package.json`,
`${outputPath}/package-lock.json`,
`${outputPath}/package.json`,
'package-lock.json',
'package.json',
'tmp/apps/parent/tsconfig.generated.json',
]);
});
});
Loading

0 comments on commit 896c1ca

Please sign in to comment.