Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(js): standalone executor for generating package.json #16330

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -5053,6 +5053,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 @@ -1020,6 +1020,15 @@
"originalFilePath": "/packages/js/src/executors/verdaccio/schema.json",
"path": "/packages/js/executors/verdaccio",
"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 @@ -1004,6 +1004,15 @@
"originalFilePath": "/packages/js/src/executors/verdaccio/schema.json",
"path": "js/executors/verdaccio",
"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 @nx/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-api\", \"params\": \"forward\" }\n ],\n \"executor\": \"@nx/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\": \"@nx/vite:build\",\n //...\n \"configurations\": {\n \"production\": {\n \"mode\": \"production\"\n }\n }\n },\n \"serve\": {\n \"executor\": \"@nx/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 @nx/js:generate-package-json executor.
---

`project.json`:

```json
{
"name": "api",
//...
"targets": {
"build": {
"dependsOn": [
{ "projects": "self", "target": "build-api", "params": "forward" }
],
"executor": "@nx/js:generate-package-json",
"options": {
"outputPath": "dist/apps/api",
"main": "apps/api/src/main.ts",
"tsConfig": "apps/api/tsconfig.app.json"
}
},
"build-api": {
"executor": "@nx/vite:build",
//...
"configurations": {
"production": {
"mode": "production"
}
}
},
"serve": {
"executor": "@nx/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 @@ -21,6 +21,11 @@
"implementation": "./src/executors/verdaccio/verdaccio.impl",
"schema": "./src/executors/verdaccio/schema.json",
"description": "Start local registry with verdaccio"
},
"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 '@nx/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