feat(bundling): add esbuild plugin (#12053)
This commit is contained in:
parent
38e4a8e7d0
commit
ed7db7c114
254
docs/generated/packages/esbuild.json
Normal file
254
docs/generated/packages/esbuild.json
Normal file
@ -0,0 +1,254 @@
|
||||
{
|
||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||
"name": "esbuild",
|
||||
"packageName": "@nrwl/esbuild",
|
||||
"description": "The Nx Plugin for EsBuild contains executors and generators that support building applications using EsBuild",
|
||||
"root": "/packages/esbuild",
|
||||
"source": "/packages/esbuild/src",
|
||||
"documentation": [],
|
||||
"generators": [
|
||||
{
|
||||
"name": "init",
|
||||
"factory": "./src/generators/init/init#esbuildInitGenerator",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxWebpackInit",
|
||||
"cli": "nx",
|
||||
"title": "Init Webpack Plugin",
|
||||
"description": "Init Webpack Plugin.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"enum": ["babel", "swc", "tsc"],
|
||||
"description": "The compiler to initialize for.",
|
||||
"default": "babel"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Initialize the `@nrwl/esbuild` plugin.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": true,
|
||||
"implementation": "/packages/esbuild/src/generators/init/init#esbuildInitGenerator.ts",
|
||||
"path": "/packages/esbuild/src/generators/init/schema.json"
|
||||
},
|
||||
{
|
||||
"name": "esbuild-project",
|
||||
"factory": "./src/generators/esbuild-project/esbuild-project#esbuildProjectGenerator",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxEsBuildProject",
|
||||
"cli": "nx",
|
||||
"title": "Add EsBuild Configuration to a project",
|
||||
"description": "Add EsBuild Configuration to a project.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": { "$source": "argv", "index": 0 },
|
||||
"x-dropdown": "project",
|
||||
"x-prompt": "What is the name of the project to set up a esbuild for?"
|
||||
},
|
||||
"main": {
|
||||
"type": "string",
|
||||
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'.",
|
||||
"alias": "entryFile"
|
||||
},
|
||||
"tsConfig": {
|
||||
"type": "string",
|
||||
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add dependencies to `package.json`."
|
||||
},
|
||||
"importPath": {
|
||||
"type": "string",
|
||||
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Add esbuild configuration to a project.",
|
||||
"hidden": true,
|
||||
"implementation": "/packages/esbuild/src/generators/esbuild-project/esbuild-project#esbuildProjectGenerator.ts",
|
||||
"aliases": [],
|
||||
"path": "/packages/esbuild/src/generators/esbuild-project/schema.json"
|
||||
}
|
||||
],
|
||||
"executors": [
|
||||
{
|
||||
"name": "esbuild",
|
||||
"implementation": "/packages/esbuild/src/executors/esbuild/esbuild.impl.ts",
|
||||
"schema": {
|
||||
"title": "Web Library EsBuild Target (Experimental)",
|
||||
"description": "Packages a library for different web usages (`UMD`, `ESM`, `CJS`).",
|
||||
"cli": "nx",
|
||||
"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"
|
||||
},
|
||||
"outputFileName": {
|
||||
"type": "string",
|
||||
"description": "Name of the main output file. Defaults same basename as 'main' file."
|
||||
},
|
||||
"tsConfig": {
|
||||
"type": "string",
|
||||
"description": "The path to tsconfig file.",
|
||||
"x-completion-type": "file",
|
||||
"x-completion-glob": "tsconfig.*.json"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The path to package.json file."
|
||||
},
|
||||
"format": {
|
||||
"type": "array",
|
||||
"description": "Set the output format(s).",
|
||||
"alias": "f",
|
||||
"items": { "type": "string", "enum": ["esm", "cjs"] },
|
||||
"default": ["esm"]
|
||||
},
|
||||
"watch": {
|
||||
"type": "boolean",
|
||||
"description": "Enable re-building when files change.",
|
||||
"default": false
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"description": "List of static assets.",
|
||||
"default": [],
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"glob": {
|
||||
"type": "string",
|
||||
"description": "The pattern to match."
|
||||
},
|
||||
"input": {
|
||||
"type": "string",
|
||||
"description": "The input directory path in which to apply `glob`. Defaults to the project root."
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"description": "Relative path within the output folder."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"clean": {
|
||||
"type": "boolean",
|
||||
"description": "Remove previous output before build.",
|
||||
"default": true
|
||||
},
|
||||
"external": {
|
||||
"type": "array",
|
||||
"description": "Mark one or more module as external. Can use * wildcards, such as '*.png'.",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"metafile": {
|
||||
"type": "boolean",
|
||||
"description": "Generate a meta.json file in the output folder that includes metadata about the build. This file can be analyzed by other tools.",
|
||||
"default": false
|
||||
},
|
||||
"minify": {
|
||||
"type": "boolean",
|
||||
"description": "Minifies outputs.",
|
||||
"default": false
|
||||
},
|
||||
"platform": {
|
||||
"type": "string",
|
||||
"description": "Platform target for outputs.",
|
||||
"enum": ["browser", "node", "neutral"],
|
||||
"default": "node"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"description": "The environment target for outputs.",
|
||||
"default": "esnext"
|
||||
},
|
||||
"skipTypeCheck": {
|
||||
"type": "boolean",
|
||||
"description": "Skip type-checking via TypeScript. Skipping type-checking speeds up the build but type errors are not caught.",
|
||||
"default": false
|
||||
},
|
||||
"updateBuildableProjectDepsInPackageJson": {
|
||||
"type": "boolean",
|
||||
"description": "Update buildable project dependencies in `package.json`.",
|
||||
"default": true
|
||||
},
|
||||
"buildableProjectDepsInPackageJsonType": {
|
||||
"type": "string",
|
||||
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
||||
"enum": ["dependencies", "peerDependencies"],
|
||||
"default": "peerDependencies"
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main", "outputPath"],
|
||||
"definitions": {
|
||||
"assetPattern": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"glob": {
|
||||
"type": "string",
|
||||
"description": "The pattern to match."
|
||||
},
|
||||
"input": {
|
||||
"type": "string",
|
||||
"description": "The input directory path in which to apply `glob`. Defaults to the project root."
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"description": "Relative path within the output folder."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"presets": []
|
||||
},
|
||||
"description": "Bundle a package using EsBuild.",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/esbuild/src/executors/esbuild/schema.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -126,6 +126,11 @@
|
||||
"default": "tsc",
|
||||
"description": "The compiler used by the build and test targets"
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["none", "esbuild", "rollup", "webpack"],
|
||||
"default": "none"
|
||||
},
|
||||
"skipTypeCheck": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to skip TypeScript type checking for SWC compiler.",
|
||||
|
||||
@ -109,6 +109,16 @@
|
||||
"path": "generated/packages/devkit.json",
|
||||
"schemas": { "executors": [], "generators": [] }
|
||||
},
|
||||
{
|
||||
"name": "esbuild",
|
||||
"packageName": "esbuild",
|
||||
"description": "The Nx Plugin for EsBuild contains executors and generators that support building applications using EsBuild",
|
||||
"path": "generated/packages/esbuild.json",
|
||||
"schemas": {
|
||||
"executors": ["esbuild"],
|
||||
"generators": ["init", "esbuild-project"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "eslint-plugin-nx",
|
||||
"packageName": "eslint-plugin-nx",
|
||||
|
||||
11
e2e/esbuild/jest.config.ts
Normal file
11
e2e/esbuild/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
maxWorkers: 1,
|
||||
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||
displayName: 'e2e-esbuild',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
34
e2e/esbuild/project.json
Normal file
34
e2e/esbuild/project.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "e2e/esbuild",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "yarn e2e-start-local-registry"
|
||||
},
|
||||
{
|
||||
"command": "yarn e2e-build-package-publish"
|
||||
},
|
||||
{
|
||||
"command": "nx run-e2e-tests e2e-esbuild"
|
||||
}
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
},
|
||||
"run-e2e-tests": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "e2e/esbuild/jest.config.ts",
|
||||
"passWithNoTests": true,
|
||||
"runInBand": true
|
||||
},
|
||||
"outputs": ["coverage/e2e/esbuild"]
|
||||
}
|
||||
},
|
||||
"implicitDependencies": ["esbuild"]
|
||||
}
|
||||
63
e2e/esbuild/src/esbuild.test.ts
Normal file
63
e2e/esbuild/src/esbuild.test.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
checkFilesExist,
|
||||
checkFilesDoNotExist,
|
||||
cleanupProject,
|
||||
newProject,
|
||||
readFile,
|
||||
runCLI,
|
||||
runCommand,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
updateProjectConfig,
|
||||
} from '@nrwl/e2e/utils';
|
||||
|
||||
describe('EsBuild Plugin', () => {
|
||||
beforeEach(() => newProject());
|
||||
afterEach(() => cleanupProject());
|
||||
|
||||
it('should setup and build projects using build', async () => {
|
||||
const myPkg = uniq('my-pkg');
|
||||
runCLI(`generate @nrwl/js:lib ${myPkg} --bundler=esbuild`);
|
||||
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`);
|
||||
updateProjectConfig(myPkg, (json) => {
|
||||
json.targets.build.options.assets = [`libs/${myPkg}/assets/*`];
|
||||
return json;
|
||||
});
|
||||
updateFile(`libs/${myPkg}/assets/a.md`, 'file a');
|
||||
updateFile(`libs/${myPkg}/assets/b.md`, 'file b');
|
||||
|
||||
runCLI(`build ${myPkg}`);
|
||||
|
||||
expect(runCommand(`node dist/libs/${myPkg}/main.js`)).toMatch(/Hello/);
|
||||
|
||||
checkFilesExist(`dist/libs/${myPkg}/package.json`);
|
||||
expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/);
|
||||
expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/);
|
||||
|
||||
/* CJS format is not used by default, but passing --format=esm,cjs generates with it.
|
||||
*/
|
||||
checkFilesDoNotExist(`dist/libs/${myPkg}/main.cjs`);
|
||||
runCLI(`build ${myPkg} --format=esm,cjs`);
|
||||
checkFilesExist(`dist/libs/${myPkg}/main.cjs`);
|
||||
|
||||
/* Metafile is not generated by default, but passing --metafile generates it.
|
||||
*/
|
||||
checkFilesDoNotExist(`dist/libs/${myPkg}/meta.json`);
|
||||
runCLI(`build ${myPkg} --metafile`);
|
||||
checkFilesExist(`dist/libs/${myPkg}/meta.json`);
|
||||
|
||||
/* Type errors are turned on by default
|
||||
*/
|
||||
updateFile(
|
||||
`libs/${myPkg}/src/index.ts`,
|
||||
`
|
||||
const x: number = 'a'; // type error
|
||||
console.log('Bye');
|
||||
`
|
||||
);
|
||||
expect(() => runCLI(`build ${myPkg}`)).toThrow();
|
||||
expect(() => runCLI(`build ${myPkg} --skipTypeCheck`)).not.toThrow();
|
||||
expect(runCommand(`node dist/libs/${myPkg}/main.js`)).toMatch(/Bye/);
|
||||
}, 300_000);
|
||||
});
|
||||
13
e2e/esbuild/tsconfig.json
Normal file
13
e2e/esbuild/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "jest"]
|
||||
},
|
||||
"include": [],
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
20
e2e/esbuild/tsconfig.spec.json
Normal file
20
e2e/esbuild/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"**/*.d.ts",
|
||||
"jest.config.ts"
|
||||
]
|
||||
}
|
||||
@ -100,8 +100,8 @@ describe('Workspace Tests', () => {
|
||||
checkFilesExist(`dist/libs/${buildableLib}/README.md`);
|
||||
|
||||
const json = readJson(`dist/libs/${buildableLib}/package.json`);
|
||||
expect(json.main).toEqual('./src/index.js');
|
||||
expect(json.typings).toEqual('./src/index.d.ts');
|
||||
expect(json.main).toEqual('./src/index.cjs');
|
||||
expect(json.types).toEqual('./src/index.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -308,6 +308,7 @@ export function newProject({
|
||||
`@nrwl/angular`,
|
||||
`@nrwl/eslint-plugin-nx`,
|
||||
`@nrwl/express`,
|
||||
`@nrwl/esbuild`,
|
||||
`@nrwl/jest`,
|
||||
`@nrwl/js`,
|
||||
`@nrwl/linter`,
|
||||
|
||||
@ -153,6 +153,11 @@ describe('nx-dev: Packages Section', () => {
|
||||
{ title: '@nrwl/detox:build', path: '/packages/detox/executors/build' },
|
||||
{ title: '@nrwl/detox:test', path: '/packages/detox/executors/test' },
|
||||
{ title: '@nrwl/devkit', path: '/packages/devkit' },
|
||||
{ title: '@nrwl/esbuild', path: '/packages/esbuild' },
|
||||
{
|
||||
title: '@nrwl/esbuild:esbuild',
|
||||
path: '/packages/esbuild/executors/esbuild',
|
||||
},
|
||||
{ title: '@nrwl/eslint-plugin-nx', path: '/packages/eslint-plugin-nx' },
|
||||
{ title: '@nrwl/express', path: '/packages/express' },
|
||||
{ title: '@nrwl/express:init', path: '/packages/express/generators/init' },
|
||||
|
||||
4
nx-dev/nx-dev/public/images/icons/esbuild.svg
Normal file
4
nx-dev/nx-dev/public/images/icons/esbuild.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="100" cy="100" r="100" fill="#000"/>
|
||||
<path d="M47.5 52.5L95 100l-47.5 47.5m60-95L155 100l-47.5 47.5" fill="none" stroke="#FFF" stroke-width="24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@ -4,6 +4,7 @@ export const iconsMap: Record<string, string> = {
|
||||
cypress: '/images/icons/cypress.svg',
|
||||
detox: '/images/icons/react.svg',
|
||||
devkit: '/images/icons/nx.svg',
|
||||
esbuild: '/images/icons/esbuild.svg',
|
||||
'eslint-plugin-nx': '/images/icons/eslint.svg',
|
||||
expo: '/images/icons/expo.svg',
|
||||
express: '/images/icons/express.svg',
|
||||
|
||||
25
packages/esbuild/.eslintrc.json
Normal file
25
packages/esbuild/.eslintrc.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["./package.json", "./generators.json", "./executors.json"],
|
||||
"parser": "jsonc-eslint-parser",
|
||||
"rules": {
|
||||
"@nrwl/nx/nx-plugin-checks": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
packages/esbuild/README.md
Normal file
13
packages/esbuild/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
|
||||
|
||||
{{links}}
|
||||
|
||||
<hr>
|
||||
|
||||
# Nx: Smart, Fast and Extensible Build System
|
||||
|
||||
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||
|
||||
This package is a [EsBuild plugin for Nx](https://nx.dev/packages/esbuild).
|
||||
|
||||
{{content}}
|
||||
16
packages/esbuild/executors.json
Normal file
16
packages/esbuild/executors.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"builders": {
|
||||
"esbuild": {
|
||||
"implementation": "./src/executors/esbuild/compat",
|
||||
"schema": "./src/executors/esbuild/schema.json",
|
||||
"description": "Bundle a package using EsBuild."
|
||||
}
|
||||
},
|
||||
"executors": {
|
||||
"esbuild": {
|
||||
"implementation": "./src/executors/esbuild/esbuild.impl",
|
||||
"schema": "./src/executors/esbuild/schema.json",
|
||||
"description": "Bundle a package using EsBuild."
|
||||
}
|
||||
}
|
||||
}
|
||||
33
packages/esbuild/generators.json
Normal file
33
packages/esbuild/generators.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "Nx esbuild",
|
||||
"version": "0.1",
|
||||
"schematics": {
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#esbuildInitSchematic",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Initialize the `@nrwl/esbuild` plugin.",
|
||||
"hidden": true
|
||||
},
|
||||
"esbuild-project": {
|
||||
"factory": "./src/generators/esbuild-project/esbuild-project#esbuildProjectSchematic",
|
||||
"schema": "./src/generators/esbuild-project/schema.json",
|
||||
"description": "Add esbuild configuration to a project.",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#esbuildInitGenerator",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Initialize the `@nrwl/esbuild` plugin.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": true
|
||||
},
|
||||
"esbuild-project": {
|
||||
"factory": "./src/generators/esbuild-project/esbuild-project#esbuildProjectGenerator",
|
||||
"schema": "./src/generators/esbuild-project/schema.json",
|
||||
"description": "Add esbuild configuration to a project.",
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
2
packages/esbuild/index.ts
Normal file
2
packages/esbuild/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './src/executors/esbuild/schema';
|
||||
export * from './src/executors/esbuild/esbuild.impl';
|
||||
11
packages/esbuild/jest.config.ts
Normal file
11
packages/esbuild/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||
displayName: 'esbuild',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
39
packages/esbuild/package.json
Normal file
39
packages/esbuild/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@nrwl/esbuild",
|
||||
"version": "0.0.1",
|
||||
"description": "The Nx Plugin for EsBuild contains executors and generators that support building applications using EsBuild",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nrwl/nx.git",
|
||||
"directory": "packages/esbuild"
|
||||
},
|
||||
"keywords": [
|
||||
"Monorepo",
|
||||
"EsBuild",
|
||||
"Web",
|
||||
"CLI"
|
||||
],
|
||||
"main": "./index.js",
|
||||
"typings": "./index.d.ts",
|
||||
"author": "Victor Savkin",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nrwl/nx/issues"
|
||||
},
|
||||
"homepage": "https://nx.dev",
|
||||
"schematics": "./generators.json",
|
||||
"builders": "./executors.json",
|
||||
"ng-update": {
|
||||
"requirements": {},
|
||||
"migrations": "./migrations.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nrwl/devkit": "file:../devkit",
|
||||
"@nrwl/js": "file:../js",
|
||||
"@nrwl/workspace": "file:../workspace",
|
||||
"dotenv": "~10.0.0",
|
||||
"esbuild": "^0.15.7",
|
||||
"fs-extra": "^10.1.0",
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
87
packages/esbuild/project.json
Normal file
87
packages/esbuild/project.json
Normal file
@ -0,0 +1,87 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/esbuild",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "packages/esbuild/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
},
|
||||
"outputs": ["coverage/packages/esbuild"]
|
||||
},
|
||||
"build-base": {
|
||||
"executor": "@nrwl/js:tsc",
|
||||
"options": {
|
||||
"outputPath": "build/packages/esbuild",
|
||||
"tsConfig": "packages/esbuild/tsconfig.lib.json",
|
||||
"main": "packages/esbuild/index.ts",
|
||||
"updateBuildableProjectDepsInPackageJson": false,
|
||||
"assets": [
|
||||
{
|
||||
"input": "packages/esbuild",
|
||||
"glob": "**/files/**",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "packages/esbuild",
|
||||
"glob": "**/files/**/.gitkeep",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "packages/esbuild",
|
||||
"glob": "**/*.json",
|
||||
"ignore": ["**/tsconfig*.json", "project.json", ".eslintrc.json"],
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "packages/esbuild",
|
||||
"glob": "**/*.js",
|
||||
"ignore": ["**/jest.config.js"],
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "packages/esbuild",
|
||||
"glob": "**/*.d.ts",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "",
|
||||
"glob": "LICENSE",
|
||||
"output": "/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputs": ["{options.outputPath}"]
|
||||
},
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": ["build/packages/esbuild"],
|
||||
"options": {
|
||||
"command": "node ./scripts/copy-readme.js esbuild"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"packages/esbuild/**/*.ts",
|
||||
"packages/esbuild/**/*.spec.ts",
|
||||
"packages/esbuild/**/*_spec.ts",
|
||||
"packages/esbuild/**/*.spec.tsx",
|
||||
"packages/esbuild/**/*.spec.js",
|
||||
"packages/esbuild/**/*.spec.jsx",
|
||||
"packages/esbuild/**/*.d.ts",
|
||||
"packages/esbuild/**/executors/**/schema.json",
|
||||
"packages/esbuild/**/generators/**/schema.json",
|
||||
"packages/esbuild/generators.json",
|
||||
"packages/esbuild/executors.json",
|
||||
"packages/esbuild/package.json",
|
||||
"packages/esbuild/migrations.json"
|
||||
]
|
||||
},
|
||||
"outputs": ["{options.outputFile}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
5
packages/esbuild/src/executors/esbuild/compat.ts
Normal file
5
packages/esbuild/src/executors/esbuild/compat.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { convertNxExecutor } from '@nrwl/devkit';
|
||||
|
||||
import esbuildExecutor from './esbuild.impl';
|
||||
|
||||
export default convertNxExecutor(esbuildExecutor);
|
||||
192
packages/esbuild/src/executors/esbuild/esbuild.impl.ts
Normal file
192
packages/esbuild/src/executors/esbuild/esbuild.impl.ts
Normal file
@ -0,0 +1,192 @@
|
||||
import 'dotenv/config';
|
||||
import type { ExecutorContext } from '@nrwl/devkit';
|
||||
import { cacheDir, joinPathFragments, logger } from '@nrwl/devkit';
|
||||
import { parse } from 'path';
|
||||
import {
|
||||
copyAssets,
|
||||
copyPackageJson,
|
||||
printDiagnostics,
|
||||
runTypeCheck,
|
||||
TypeCheckOptions,
|
||||
} from '@nrwl/js';
|
||||
import * as esbuild from 'esbuild';
|
||||
import { normalizeOptions } from './lib/normalize';
|
||||
|
||||
import { EsBuildExecutorOptions } from './schema';
|
||||
import { removeSync, writeJsonSync } from 'fs-extra';
|
||||
import { getClientEnvironment } from '../../utils/environment-variables';
|
||||
import { createAsyncIterable } from '@nrwl/js/src/utils/create-async-iterable/create-async-iteratable';
|
||||
|
||||
export async function* esbuildExecutor(
|
||||
_options: EsBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const options = normalizeOptions(_options);
|
||||
if (options.clean) removeSync(options.outputPath);
|
||||
|
||||
const assetsResult = await copyAssets(options, context);
|
||||
|
||||
const packageJsonResult = await copyPackageJson(
|
||||
{
|
||||
...options,
|
||||
skipTypings: options.skipTypeCheck,
|
||||
},
|
||||
context
|
||||
);
|
||||
|
||||
const esbuildOptions: esbuild.BuildOptions = {
|
||||
entryPoints: [options.main],
|
||||
bundle: true,
|
||||
define: getClientEnvironment(),
|
||||
external: options.external,
|
||||
minify: options.minify,
|
||||
platform: options.platform,
|
||||
target: options.target,
|
||||
metafile: options.metafile,
|
||||
};
|
||||
|
||||
if (options.watch) {
|
||||
return yield* createAsyncIterable<{ success: boolean; outfile: string }>(
|
||||
async ({ next, done }) => {
|
||||
const results = await Promise.all(
|
||||
options.format.map((format, idx) => {
|
||||
const outfile = getOutfile(format, options, context);
|
||||
return esbuild.build({
|
||||
...esbuildOptions,
|
||||
metafile: true, // Always include metafile so we can see what files have changed.
|
||||
watch:
|
||||
// Only emit info on one of the watch processes.
|
||||
idx === 0
|
||||
? {
|
||||
onRebuild: (
|
||||
error: esbuild.BuildFailure,
|
||||
result: esbuild.BuildResult
|
||||
) => {
|
||||
if (error) {
|
||||
logger.info(`[watch] build failed`);
|
||||
} else if (result?.metafile) {
|
||||
logger.info(
|
||||
`[watch] build succeeded (change: "${
|
||||
Object.keys(result.metafile?.inputs)[0]
|
||||
}")`
|
||||
);
|
||||
} else {
|
||||
logger.info(`[watch] build succeeded`);
|
||||
}
|
||||
next({ success: !!error, outfile });
|
||||
},
|
||||
}
|
||||
: true,
|
||||
format,
|
||||
outfile,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
logger.info(`[watch] build finished, watching for changes...`);
|
||||
|
||||
const processOnExit = () => {
|
||||
assetsResult?.stop();
|
||||
packageJsonResult?.stop();
|
||||
results.forEach((r) => r?.stop());
|
||||
done();
|
||||
process.off('SIGINT', processOnExit);
|
||||
process.off('SIGTERM', processOnExit);
|
||||
process.off('exit', processOnExit);
|
||||
};
|
||||
|
||||
process.on('SIGINT', processOnExit);
|
||||
process.on('SIGTERM', processOnExit);
|
||||
process.on('exit', processOnExit);
|
||||
|
||||
next({
|
||||
success: results.every((r) => r.errors?.length === 0),
|
||||
outfile: getOutfile(options.format[0], options, context),
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const buildResults = await Promise.all(
|
||||
options.format.map((format) =>
|
||||
esbuild.build({
|
||||
...esbuildOptions,
|
||||
format,
|
||||
outfile: getOutfile(format, options, context),
|
||||
})
|
||||
)
|
||||
);
|
||||
const buildSuccess = buildResults.every((r) => r.errors?.length === 0);
|
||||
|
||||
if (options.skipTypeCheck) {
|
||||
return { success: buildSuccess };
|
||||
}
|
||||
|
||||
const { errors, warnings } = await runTypeCheck(
|
||||
getTypeCheckOptions(options, context)
|
||||
);
|
||||
const hasErrors = errors.length > 0;
|
||||
const hasWarnings = warnings.length > 0;
|
||||
|
||||
if (hasErrors || hasWarnings) {
|
||||
await printDiagnostics(errors, warnings);
|
||||
}
|
||||
|
||||
if (options.metafile) {
|
||||
buildResults.forEach((r, idx) => {
|
||||
const filename =
|
||||
options.format.length === 1
|
||||
? 'meta.json'
|
||||
: `meta.${options.format[idx]}.json`;
|
||||
writeJsonSync(
|
||||
joinPathFragments(options.outputPath, filename),
|
||||
r.metafile
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { success: buildSuccess && !hasErrors };
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeCheckOptions(
|
||||
options: EsBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const root = context.root;
|
||||
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||
const { watch, tsConfig, outputPath } = options;
|
||||
|
||||
const typeCheckOptions: TypeCheckOptions = {
|
||||
mode: 'emitDeclarationOnly',
|
||||
tsConfigPath: tsConfig,
|
||||
outDir: outputPath,
|
||||
workspaceRoot: root,
|
||||
rootDir: projectRoot,
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
typeCheckOptions.incremental = true;
|
||||
typeCheckOptions.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
return typeCheckOptions;
|
||||
}
|
||||
|
||||
function getOutfile(
|
||||
format: 'cjs' | 'esm',
|
||||
options: EsBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const candidate = joinPathFragments(
|
||||
context.target.options.outputPath,
|
||||
options.outputFileName
|
||||
);
|
||||
if (format === 'esm') {
|
||||
return candidate;
|
||||
} else {
|
||||
const { dir, name } = parse(candidate);
|
||||
return `${dir}/${name}.cjs`;
|
||||
}
|
||||
}
|
||||
|
||||
export default esbuildExecutor;
|
||||
9
packages/esbuild/src/executors/esbuild/lib/normalize.ts
Normal file
9
packages/esbuild/src/executors/esbuild/lib/normalize.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { EsBuildExecutorOptions } from '../schema';
|
||||
export function normalizeOptions(
|
||||
options: EsBuildExecutorOptions
|
||||
): EsBuildExecutorOptions {
|
||||
return {
|
||||
...options,
|
||||
outputFileName: options.outputFileName ?? 'main.js',
|
||||
};
|
||||
}
|
||||
22
packages/esbuild/src/executors/esbuild/schema.d.ts
vendored
Normal file
22
packages/esbuild/src/executors/esbuild/schema.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
import { AssetGlob } from '@nrwl/workspace/src/utilities/assets';
|
||||
|
||||
type Compiler = 'babel' | 'swc';
|
||||
export interface EsBuildExecutorOptions {
|
||||
outputPath: string;
|
||||
tsConfig: string;
|
||||
project: string;
|
||||
main: string;
|
||||
outputFileName?: string;
|
||||
assets: AssetGlob[];
|
||||
watch?: boolean;
|
||||
clean?: boolean;
|
||||
external?: string[];
|
||||
format?: Array<'esm' | 'cjs'>;
|
||||
metafile?: boolean;
|
||||
minify?: boolean;
|
||||
platform?: 'node' | 'browser' | 'neutral';
|
||||
target?: string;
|
||||
skipTypeCheck?: boolean;
|
||||
updateBuildableProjectDepsInPackageJson?: boolean;
|
||||
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
|
||||
}
|
||||
144
packages/esbuild/src/executors/esbuild/schema.json
Normal file
144
packages/esbuild/src/executors/esbuild/schema.json
Normal file
@ -0,0 +1,144 @@
|
||||
{
|
||||
"title": "Web Library EsBuild Target (Experimental)",
|
||||
"description": "Packages a library for different web usages (`UMD`, `ESM`, `CJS`).",
|
||||
"cli": "nx",
|
||||
"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"
|
||||
},
|
||||
"outputFileName": {
|
||||
"type": "string",
|
||||
"description": "Name of the main output file. Defaults same basename as 'main' file."
|
||||
},
|
||||
"tsConfig": {
|
||||
"type": "string",
|
||||
"description": "The path to tsconfig file.",
|
||||
"x-completion-type": "file",
|
||||
"x-completion-glob": "tsconfig.*.json"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The path to package.json file."
|
||||
},
|
||||
"format": {
|
||||
"type": "array",
|
||||
"description": "List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise).",
|
||||
"alias": "f",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["esm", "umd", "cjs"]
|
||||
}
|
||||
},
|
||||
"watch": {
|
||||
"type": "boolean",
|
||||
"description": "Enable re-building when files change.",
|
||||
"default": false
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"description": "List of static assets.",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/assetPattern"
|
||||
}
|
||||
},
|
||||
"clean": {
|
||||
"type": "boolean",
|
||||
"description": "Remove previous output before build.",
|
||||
"default": true
|
||||
},
|
||||
"external": {
|
||||
"type": "array",
|
||||
"description": "Mark one or more module as external. Can use * wildcards, such as '*.png'.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"type": "array",
|
||||
"description": "Set the output format(s).",
|
||||
"alias": "f",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["esm", "cjs"]
|
||||
},
|
||||
"default": ["esm"]
|
||||
},
|
||||
"metafile": {
|
||||
"type": "boolean",
|
||||
"description": "Generate a meta.json file in the output folder that includes metadata about the build. This file can be analyzed by other tools.",
|
||||
"default": false
|
||||
},
|
||||
"minify": {
|
||||
"type": "boolean",
|
||||
"description": "Minifies outputs.",
|
||||
"default": false
|
||||
},
|
||||
"platform": {
|
||||
"type": "string",
|
||||
"description": "Platform target for outputs.",
|
||||
"enum": ["browser", "node", "neutral"],
|
||||
"default": "node"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"description": "The environment target for outputs.",
|
||||
"default": "esnext"
|
||||
},
|
||||
"skipTypeCheck": {
|
||||
"type": "boolean",
|
||||
"description": "Skip type-checking via TypeScript. Skipping type-checking speeds up the build but type errors are not caught.",
|
||||
"default": false
|
||||
},
|
||||
"updateBuildableProjectDepsInPackageJson": {
|
||||
"type": "boolean",
|
||||
"description": "Update buildable project dependencies in `package.json`.",
|
||||
"default": true
|
||||
},
|
||||
"buildableProjectDepsInPackageJsonType": {
|
||||
"type": "string",
|
||||
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
||||
"enum": ["dependencies", "peerDependencies"],
|
||||
"default": "peerDependencies"
|
||||
}
|
||||
},
|
||||
"required": ["tsConfig", "main", "outputPath"],
|
||||
"definitions": {
|
||||
"assetPattern": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"glob": {
|
||||
"type": "string",
|
||||
"description": "The pattern to match."
|
||||
},
|
||||
"input": {
|
||||
"type": "string",
|
||||
"description": "The input directory path in which to apply `glob`. Defaults to the project root."
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"description": "Relative path within the output folder."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
getImportPath,
|
||||
getWorkspaceLayout,
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
updateProjectConfiguration,
|
||||
writeJson,
|
||||
} from '@nrwl/devkit';
|
||||
|
||||
import { esbuildInitGenerator } from '../init/init';
|
||||
import { EsBuildExecutorOptions } from '../../executors/esbuild/schema';
|
||||
import { EsBuildProjectSchema } from './schema';
|
||||
|
||||
export async function esbuildProjectGenerator(
|
||||
tree: Tree,
|
||||
options: EsBuildProjectSchema
|
||||
) {
|
||||
const task = await esbuildInitGenerator(tree, options);
|
||||
checkForTargetConflicts(tree, options);
|
||||
addBuildTarget(tree, options);
|
||||
await formatFiles(tree);
|
||||
return task;
|
||||
}
|
||||
|
||||
function checkForTargetConflicts(tree: Tree, options: EsBuildProjectSchema) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
if (project.targets.build) {
|
||||
throw new Error(`Project "${project.name}" already has a build target.`);
|
||||
}
|
||||
|
||||
if (options.devServer && project.targets.serve) {
|
||||
throw new Error(`Project "${project.name}" already has a serve target.`);
|
||||
}
|
||||
}
|
||||
|
||||
function addBuildTarget(tree: Tree, options: EsBuildProjectSchema) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
const packageJsonPath = joinPathFragments(project.root, 'package.json');
|
||||
|
||||
if (!tree.exists(packageJsonPath)) {
|
||||
const { npmScope } = getWorkspaceLayout(tree);
|
||||
const importPath =
|
||||
options.importPath || getImportPath(npmScope, options.project);
|
||||
writeJson(tree, packageJsonPath, {
|
||||
name: importPath,
|
||||
version: '0.0.1',
|
||||
});
|
||||
}
|
||||
const tsConfig =
|
||||
options.tsConfig ?? joinPathFragments(project.root, 'tsconfig.lib.json');
|
||||
|
||||
const buildOptions: EsBuildExecutorOptions = {
|
||||
main: options.main ?? joinPathFragments(project.root, 'src/main.ts'),
|
||||
outputPath: joinPathFragments('dist', project.root),
|
||||
outputFileName: 'main.js',
|
||||
tsConfig,
|
||||
project: `${project.root}/package.json`,
|
||||
assets: [],
|
||||
};
|
||||
|
||||
if (tree.exists(joinPathFragments(project.root, 'README.md'))) {
|
||||
buildOptions.assets = [
|
||||
{
|
||||
glob: `${project.root}/README.md`,
|
||||
input: '.',
|
||||
output: '.',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, options.project, {
|
||||
...project,
|
||||
targets: {
|
||||
...project.targets,
|
||||
build: {
|
||||
executor: '@nrwl/esbuild:esbuild',
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: buildOptions,
|
||||
configurations: {
|
||||
production: {
|
||||
optimization: true,
|
||||
sourceMap: false,
|
||||
namedChunks: false,
|
||||
extractLicenses: true,
|
||||
vendorChunk: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default esbuildProjectGenerator;
|
||||
|
||||
export const esbuildProjectSchematic = convertNxGenerator(
|
||||
esbuildProjectGenerator
|
||||
);
|
||||
10
packages/esbuild/src/generators/esbuild-project/schema.d.ts
vendored
Normal file
10
packages/esbuild/src/generators/esbuild-project/schema.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
export interface EsBuildProjectSchema {
|
||||
project: string;
|
||||
main?: string;
|
||||
tsConfig?: string;
|
||||
devServer?: boolean;
|
||||
skipFormat?: boolean;
|
||||
skipPackageJson?: boolean;
|
||||
importPath?: string;
|
||||
esbuildConfig?: string;
|
||||
}
|
||||
44
packages/esbuild/src/generators/esbuild-project/schema.json
Normal file
44
packages/esbuild/src/generators/esbuild-project/schema.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxEsBuildProject",
|
||||
"cli": "nx",
|
||||
"title": "Add EsBuild Configuration to a project",
|
||||
"description": "Add EsBuild Configuration to a project.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"x-dropdown": "project",
|
||||
"x-prompt": "What is the name of the project to set up a esbuild for?"
|
||||
},
|
||||
"main": {
|
||||
"type": "string",
|
||||
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'.",
|
||||
"alias": "entryFile"
|
||||
},
|
||||
"tsConfig": {
|
||||
"type": "string",
|
||||
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add dependencies to `package.json`."
|
||||
},
|
||||
"importPath": {
|
||||
"type": "string",
|
||||
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
16
packages/esbuild/src/generators/init/init.spec.ts
Normal file
16
packages/esbuild/src/generators/init/init.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Tree } from '@nrwl/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
|
||||
import { esbuildInitGenerator } from './init';
|
||||
|
||||
describe('esbuildInitGenerator', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should run successfully', async () => {
|
||||
await expect(esbuildInitGenerator(tree, {})).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
15
packages/esbuild/src/generators/init/init.ts
Normal file
15
packages/esbuild/src/generators/init/init.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { convertNxGenerator, formatFiles, Tree } from '@nrwl/devkit';
|
||||
import { Schema } from './schema';
|
||||
|
||||
export async function esbuildInitGenerator(tree: Tree, schema: Schema) {
|
||||
/*
|
||||
* Empty for now, might need to add setup files later.
|
||||
*/
|
||||
if (!schema.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
|
||||
export default esbuildInitGenerator;
|
||||
|
||||
export const esbuildInitSchematic = convertNxGenerator(esbuildInitGenerator);
|
||||
4
packages/esbuild/src/generators/init/schema.d.ts
vendored
Normal file
4
packages/esbuild/src/generators/init/schema.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export interface Schema {
|
||||
compiler?: 'babel' | 'swc' | 'tsc';
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
22
packages/esbuild/src/generators/init/schema.json
Normal file
22
packages/esbuild/src/generators/init/schema.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxWebpackInit",
|
||||
"cli": "nx",
|
||||
"title": "Init Webpack Plugin",
|
||||
"description": "Init Webpack Plugin.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"enum": ["babel", "swc", "tsc"],
|
||||
"description": "The compiler to initialize for.",
|
||||
"default": "babel"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
10
packages/esbuild/src/utils/environment-variables.ts
Normal file
10
packages/esbuild/src/utils/environment-variables.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export function getClientEnvironment(): Record<string, string> {
|
||||
const NX_APP = /^NX_/i;
|
||||
|
||||
return Object.keys(process.env)
|
||||
.filter((key) => NX_APP.test(key) || key === 'NODE_ENV')
|
||||
.reduce((env, key) => {
|
||||
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
|
||||
return env;
|
||||
}, {});
|
||||
}
|
||||
14
packages/esbuild/src/utils/fs.ts
Normal file
14
packages/esbuild/src/utils/fs.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import * as path from 'path';
|
||||
import { removeSync } from 'fs-extra';
|
||||
|
||||
/**
|
||||
* Delete an output directory, but error out if it's the root of the project.
|
||||
*/
|
||||
export function deleteOutputDir(root: string, outputPath: string) {
|
||||
const resolvedOutputPath = path.resolve(root, outputPath);
|
||||
if (resolvedOutputPath === root) {
|
||||
throw new Error('Output path MUST not be project root directory!');
|
||||
}
|
||||
|
||||
removeSync(resolvedOutputPath);
|
||||
}
|
||||
5
packages/esbuild/src/utils/versions.ts
Normal file
5
packages/esbuild/src/utils/versions.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const swcLoaderVersion = '0.1.15';
|
||||
export const swcHelpersVersion = '~0.3.3';
|
||||
export const tsLibVersion = '^2.3.0';
|
||||
16
packages/esbuild/tsconfig.json
Normal file
16
packages/esbuild/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/esbuild/tsconfig.lib.json
Normal file
10
packages/esbuild/tsconfig.lib.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": []
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"]
|
||||
}
|
||||
9
packages/esbuild/tsconfig.spec.json
Normal file
9
packages/esbuild/tsconfig.spec.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
import { ExecutorContext, ProjectGraphProjectNode } from '@nrwl/devkit';
|
||||
import { ExecutorContext } from '@nrwl/devkit';
|
||||
import {
|
||||
assetGlobsToFiles,
|
||||
FileInputOutput,
|
||||
} from '@nrwl/workspace/src/utilities/assets';
|
||||
import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||
import { join, relative, resolve } from 'path';
|
||||
|
||||
import { checkDependencies } from '../../utils/check-dependencies';
|
||||
@ -11,15 +10,14 @@ import {
|
||||
getHelperDependency,
|
||||
HelperDependency,
|
||||
} from '../../utils/compiler-helper-dependency';
|
||||
import { CopyAssetsHandler } from '../../utils/copy-assets-handler';
|
||||
import {
|
||||
NormalizedSwcExecutorOptions,
|
||||
SwcExecutorOptions,
|
||||
} from '../../utils/schema';
|
||||
import { compileSwc, compileSwcWatch } from '../../utils/swc/compile-swc';
|
||||
import { getSwcrcPath } from '../../utils/swc/get-swcrc-path';
|
||||
import { updatePackageJson } from '../../utils/update-package-json';
|
||||
import { watchForSingleFileChanges } from '../../utils/watch-for-single-file-changes';
|
||||
import { copyAssets } from '../../utils/assets';
|
||||
import { copyPackageJson } from '../../utils/package-json';
|
||||
|
||||
export function normalizeOptions(
|
||||
options: SwcExecutorOptions,
|
||||
@ -75,32 +73,13 @@ export function normalizeOptions(
|
||||
} as NormalizedSwcExecutorOptions;
|
||||
}
|
||||
|
||||
function processAssetsAndPackageJsonOnce(
|
||||
assetHandler: CopyAssetsHandler,
|
||||
options: NormalizedSwcExecutorOptions,
|
||||
context: ExecutorContext,
|
||||
target: ProjectGraphProjectNode<any>,
|
||||
dependencies: DependentBuildableProjectNode[]
|
||||
) {
|
||||
return async () => {
|
||||
await assetHandler.processAllAssetsOnce();
|
||||
updatePackageJson(
|
||||
options,
|
||||
context,
|
||||
target,
|
||||
dependencies,
|
||||
!options.skipTypeCheck
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export async function* swcExecutor(
|
||||
_options: SwcExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const { sourceRoot, root } = context.workspace.projects[context.projectName];
|
||||
const options = normalizeOptions(_options, context.root, sourceRoot, root);
|
||||
const { tmpTsConfig, projectRoot, target, dependencies } = checkDependencies(
|
||||
const { tmpTsConfig, dependencies } = checkDependencies(
|
||||
context,
|
||||
options.tsConfig
|
||||
);
|
||||
@ -120,58 +99,38 @@ export async function* swcExecutor(
|
||||
dependencies.push(swcHelperDependency);
|
||||
}
|
||||
|
||||
const assetHandler = new CopyAssetsHandler({
|
||||
projectDir: projectRoot,
|
||||
rootDir: context.root,
|
||||
outputDir: options.outputPath,
|
||||
assets: options.assets,
|
||||
});
|
||||
|
||||
if (options.watch) {
|
||||
const disposeWatchAssetChanges =
|
||||
await assetHandler.watchAndProcessOnAssetChange();
|
||||
const disposePackageJsonChanges = await watchForSingleFileChanges(
|
||||
join(context.root, projectRoot),
|
||||
'package.json',
|
||||
() =>
|
||||
updatePackageJson(
|
||||
options,
|
||||
context,
|
||||
target,
|
||||
dependencies,
|
||||
!options.skipTypeCheck
|
||||
)
|
||||
);
|
||||
const handleTermination = async () => {
|
||||
await disposeWatchAssetChanges();
|
||||
await disposePackageJsonChanges();
|
||||
};
|
||||
process.on('SIGINT', () => handleTermination());
|
||||
process.on('SIGTERM', () => handleTermination());
|
||||
let disposeFn: () => void;
|
||||
process.on('SIGINT', () => disposeFn());
|
||||
process.on('SIGTERM', () => disposeFn());
|
||||
|
||||
return yield* compileSwcWatch(
|
||||
context,
|
||||
options,
|
||||
processAssetsAndPackageJsonOnce(
|
||||
assetHandler,
|
||||
options,
|
||||
context,
|
||||
target,
|
||||
dependencies
|
||||
)
|
||||
);
|
||||
return yield* compileSwcWatch(context, options, async () => {
|
||||
const assetResult = await copyAssets(options, context);
|
||||
const packageJsonResult = await copyPackageJson(
|
||||
{
|
||||
...options,
|
||||
skipTypings: !options.skipTypeCheck,
|
||||
},
|
||||
context
|
||||
);
|
||||
disposeFn = () => {
|
||||
assetResult?.stop();
|
||||
packageJsonResult?.stop();
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return yield compileSwc(
|
||||
context,
|
||||
options,
|
||||
processAssetsAndPackageJsonOnce(
|
||||
assetHandler,
|
||||
options,
|
||||
context,
|
||||
target,
|
||||
dependencies
|
||||
)
|
||||
);
|
||||
return yield compileSwc(context, options, async () => {
|
||||
await copyAssets(options, context);
|
||||
await copyPackageJson(
|
||||
{
|
||||
...options,
|
||||
generateExportsField: true,
|
||||
skipTypings: !options.skipTypeCheck,
|
||||
extraDependencies: swcHelperDependency ? [swcHelperDependency] : [],
|
||||
},
|
||||
context
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,11 +16,11 @@ import {
|
||||
getHelperDependency,
|
||||
HelperDependency,
|
||||
} from '../../utils/compiler-helper-dependency';
|
||||
import { CopyAssetsHandler } from '../../utils/copy-assets-handler';
|
||||
import { CopyAssetsHandler } from '../../utils/assets/copy-assets-handler';
|
||||
import { ExecutorOptions, NormalizedExecutorOptions } from '../../utils/schema';
|
||||
import { compileTypeScriptFiles } from '../../utils/typescript/compile-typescript-files';
|
||||
import { loadTsTransformers } from '../../utils/typescript/load-ts-transformers';
|
||||
import { updatePackageJson } from '../../utils/update-package-json';
|
||||
import { updatePackageJson } from '../../utils/package-json/update-package-json';
|
||||
import { watchForSingleFileChanges } from '../../utils/watch-for-single-file-changes';
|
||||
|
||||
export function normalizeOptions(
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
addProjectConfiguration,
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
@ -9,10 +10,10 @@ import {
|
||||
names,
|
||||
offsetFromRoot,
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
toJS,
|
||||
Tree,
|
||||
updateJson,
|
||||
readJson,
|
||||
writeJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { getImportPath } from 'nx/src/utils/path';
|
||||
@ -28,6 +29,7 @@ import { addMinimalPublishScript } from '../../utils/minimal-publish-script';
|
||||
import { LibraryGeneratorSchema } from '../../utils/schema';
|
||||
import { addSwcConfig } from '../../utils/swc/add-swc-config';
|
||||
import { addSwcDependencies } from '../../utils/swc/add-swc-dependencies';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export async function libraryGenerator(
|
||||
tree: Tree,
|
||||
@ -43,18 +45,19 @@ export async function projectGenerator(
|
||||
destinationDir: string,
|
||||
filesDir: string
|
||||
) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
const options = normalizeOptions(tree, schema, destinationDir);
|
||||
|
||||
createFiles(tree, options, `${filesDir}/lib`);
|
||||
|
||||
addProject(tree, options, destinationDir);
|
||||
|
||||
// tasks.push(addProjectDependencies(tree, options));
|
||||
|
||||
if (!schema.skipTsConfig) {
|
||||
updateRootTsConfig(tree, options);
|
||||
}
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
if (options.linter !== 'none') {
|
||||
const lintCallback = await addLint(tree, options);
|
||||
tasks.push(lintCallback);
|
||||
@ -99,7 +102,7 @@ function addProject(
|
||||
if (options.buildable && options.config !== 'npm-scripts') {
|
||||
const outputPath = `dist/${destinationDir}/${options.projectDirectory}`;
|
||||
projectConfiguration.targets.build = {
|
||||
executor: `@nrwl/js:${options.compiler}`,
|
||||
executor: getBuildExecutor(options),
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
outputPath,
|
||||
@ -408,5 +411,50 @@ function updateRootTsConfig(host: Tree, options: NormalizedSchema) {
|
||||
});
|
||||
}
|
||||
|
||||
function addProjectDependencies(
|
||||
tree: Tree,
|
||||
options: NormalizedSchema
|
||||
): GeneratorCallback {
|
||||
if (options.bundler == 'esbuild') {
|
||||
return addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{ '@nrwl/esbuild': nxVersion }
|
||||
);
|
||||
}
|
||||
|
||||
if (options.bundler == 'rollup') {
|
||||
return addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{ '@nrwl/rollup': nxVersion }
|
||||
);
|
||||
}
|
||||
|
||||
if (options.bundler == 'webpack') {
|
||||
return addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{ '@nrwl/webpack': nxVersion }
|
||||
);
|
||||
}
|
||||
|
||||
// noop
|
||||
return () => {};
|
||||
}
|
||||
|
||||
function getBuildExecutor(options: NormalizedSchema) {
|
||||
switch (options.bundler) {
|
||||
case 'esbuild':
|
||||
return `@nrwl/esbuild:esbuild`;
|
||||
case 'rollup':
|
||||
return `@nrwl/rollup:rollup`;
|
||||
case 'webpack':
|
||||
return `@nrwl/webpack:webpack`;
|
||||
default:
|
||||
return `@nrwl/js:${options.compiler}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default libraryGenerator;
|
||||
export const librarySchematic = convertNxGenerator(libraryGenerator);
|
||||
|
||||
@ -109,6 +109,11 @@
|
||||
"default": "tsc",
|
||||
"description": "The compiler used by the build and test targets"
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["none", "esbuild", "rollup", "webpack"],
|
||||
"default": "none"
|
||||
},
|
||||
"skipTypeCheck": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to skip TypeScript type checking for SWC compiler.",
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
export * from './utils/typescript/load-ts-transformers';
|
||||
export * from './utils/typescript/print-diagnostics';
|
||||
export * from './utils/typescript/run-type-check';
|
||||
export * from './utils/package-json';
|
||||
export * from './utils/assets';
|
||||
export * from './utils/package-json/update-package-json';
|
||||
export { libraryGenerator } from './generators/library/library';
|
||||
|
||||
47
packages/js/src/utils/assets/index.ts
Normal file
47
packages/js/src/utils/assets/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { AssetGlob } from '@nrwl/workspace/src/utilities/assets';
|
||||
import { CopyAssetsHandler, FileEvent } from './copy-assets-handler';
|
||||
import { ExecutorContext } from '@nrwl/devkit';
|
||||
|
||||
export interface CopyAssetsOptions {
|
||||
outputPath: string;
|
||||
assets: (string | AssetGlob)[];
|
||||
watch?: boolean | WatchMode;
|
||||
}
|
||||
|
||||
export interface CopyAssetsResult {
|
||||
success?: boolean;
|
||||
// Only when "watch: true"
|
||||
stop?: () => void;
|
||||
}
|
||||
|
||||
export interface WatchMode {
|
||||
onCopy?: (events: FileEvent[]) => void;
|
||||
}
|
||||
|
||||
export async function copyAssets(
|
||||
options: CopyAssetsOptions,
|
||||
context: ExecutorContext
|
||||
): Promise<CopyAssetsResult> {
|
||||
const assetHandler = new CopyAssetsHandler({
|
||||
projectDir: context.workspace.projects[context.projectName].root,
|
||||
rootDir: context.root,
|
||||
outputDir: options.outputPath,
|
||||
assets: options.assets,
|
||||
callback:
|
||||
typeof options?.watch === 'object' ? options.watch.onCopy : undefined,
|
||||
});
|
||||
if (options.watch) {
|
||||
const dispose = await assetHandler.watchAndProcessOnAssetChange();
|
||||
return {
|
||||
success: true,
|
||||
stop: dispose,
|
||||
};
|
||||
} else {
|
||||
try {
|
||||
await assetHandler.processAllAssetsOnce();
|
||||
} catch {
|
||||
return { success: false };
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
52
packages/js/src/utils/package-json/index.ts
Normal file
52
packages/js/src/utils/package-json/index.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { join } from 'path';
|
||||
import { ExecutorContext } from '@nrwl/devkit';
|
||||
import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||
|
||||
import { watchForSingleFileChanges } from '../watch-for-single-file-changes';
|
||||
import type { UpdatePackageJsonOption } from './update-package-json';
|
||||
import { updatePackageJson } from './update-package-json';
|
||||
import { checkDependencies } from '../check-dependencies';
|
||||
|
||||
export interface CopyPackageJsonOptions
|
||||
extends Omit<UpdatePackageJsonOption, 'projectRoot'> {
|
||||
watch?: boolean;
|
||||
extraDependencies?: DependentBuildableProjectNode[];
|
||||
}
|
||||
|
||||
export interface CopyPackageJsonResult {
|
||||
success?: boolean;
|
||||
// Only when "watch: true"
|
||||
stop?: () => void;
|
||||
}
|
||||
|
||||
export async function copyPackageJson(
|
||||
_options: CopyPackageJsonOptions,
|
||||
context: ExecutorContext
|
||||
): Promise<CopyPackageJsonResult> {
|
||||
if (!context.target.options.tsConfig) {
|
||||
throw new Error(
|
||||
`Could not find tsConfig option for "${context.targetName}" target of "${context.projectName}" project. Check that your project configuration is correct.`
|
||||
);
|
||||
}
|
||||
const { target, dependencies, projectRoot } = checkDependencies(
|
||||
context,
|
||||
context.target.options.tsConfig
|
||||
);
|
||||
const options = { ..._options, projectRoot };
|
||||
if (options.extraDependencies?.length) {
|
||||
dependencies.push(...options.extraDependencies);
|
||||
}
|
||||
if (options.watch) {
|
||||
const dispose = await watchForSingleFileChanges(
|
||||
join(context.root, projectRoot),
|
||||
'package.json',
|
||||
() => updatePackageJson(options, context, target, dependencies)
|
||||
);
|
||||
// Copy it once before changes
|
||||
updatePackageJson(options, context, target, dependencies);
|
||||
return { success: true, stop: dispose };
|
||||
} else {
|
||||
updatePackageJson(options, context, target, dependencies);
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
119
packages/js/src/utils/package-json/update-package-json.spec.ts
Normal file
119
packages/js/src/utils/package-json/update-package-json.spec.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { getUpdatedPackageJsonContent } from './update-package-json';
|
||||
|
||||
describe('getUpdatedPackageJsonContent', () => {
|
||||
it('should update fields for commonjs only (default)', () => {
|
||||
const json = getUpdatedPackageJsonContent(
|
||||
{
|
||||
name: 'test',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
main: 'proj/src/index.ts',
|
||||
outputPath: 'dist/proj',
|
||||
projectRoot: 'proj',
|
||||
}
|
||||
);
|
||||
|
||||
expect(json).toEqual({
|
||||
name: 'test',
|
||||
main: './src/index.cjs',
|
||||
types: './src/index.d.ts',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update fields for esm only', () => {
|
||||
const json = getUpdatedPackageJsonContent(
|
||||
{
|
||||
name: 'test',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
main: 'proj/src/index.ts',
|
||||
outputPath: 'dist/proj',
|
||||
projectRoot: 'proj',
|
||||
format: ['esm'],
|
||||
}
|
||||
);
|
||||
|
||||
expect(json).toEqual({
|
||||
name: 'test',
|
||||
type: 'module',
|
||||
module: './src/index.js',
|
||||
main: './src/index.js',
|
||||
types: './src/index.d.ts',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update fields for commonjs + esm', () => {
|
||||
const json = getUpdatedPackageJsonContent(
|
||||
{
|
||||
name: 'test',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
main: 'proj/src/index.ts',
|
||||
outputPath: 'dist/proj',
|
||||
projectRoot: 'proj',
|
||||
format: ['esm', 'cjs'],
|
||||
}
|
||||
);
|
||||
|
||||
expect(json).toEqual({
|
||||
name: 'test',
|
||||
main: './src/index.cjs',
|
||||
module: './src/index.js',
|
||||
types: './src/index.d.ts',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support skipping types', () => {
|
||||
const json = getUpdatedPackageJsonContent(
|
||||
{
|
||||
name: 'test',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
main: 'proj/src/index.ts',
|
||||
outputPath: 'dist/proj',
|
||||
projectRoot: 'proj',
|
||||
skipTypings: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(json).toEqual({
|
||||
name: 'test',
|
||||
main: './src/index.cjs',
|
||||
version: '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support generated exports field', () => {
|
||||
const json = getUpdatedPackageJsonContent(
|
||||
{
|
||||
name: 'test',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
main: 'proj/src/index.ts',
|
||||
outputPath: 'dist/proj',
|
||||
projectRoot: 'proj',
|
||||
format: ['esm', 'cjs'],
|
||||
generateExportsField: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(json).toEqual({
|
||||
name: 'test',
|
||||
module: './src/index.js',
|
||||
main: './src/index.cjs',
|
||||
types: './src/index.d.ts',
|
||||
version: '0.0.1',
|
||||
exports: {
|
||||
'.': { require: './src/index.cjs', import: './src/index.js' },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
124
packages/js/src/utils/package-json/update-package-json.ts
Normal file
124
packages/js/src/utils/package-json/update-package-json.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import {
|
||||
ExecutorContext,
|
||||
normalizePath,
|
||||
ProjectGraphProjectNode,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from '@nrwl/devkit';
|
||||
import {
|
||||
DependentBuildableProjectNode,
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||
import { basename, dirname, join, parse, relative } from 'path';
|
||||
import { fileExists } from 'nx/src/utils/fileutils';
|
||||
import type { PackageJson } from 'nx/src/utils/package-json';
|
||||
|
||||
function getMainFileDirRelativeToProjectRoot(
|
||||
main: string,
|
||||
projectRoot: string
|
||||
): string {
|
||||
const mainFileDir = dirname(main);
|
||||
const relativeDir = normalizePath(relative(projectRoot, mainFileDir));
|
||||
return relativeDir === '' ? `./` : `./${relativeDir}/`;
|
||||
}
|
||||
|
||||
export interface UpdatePackageJsonOption {
|
||||
projectRoot: string;
|
||||
outputPath: string;
|
||||
main: string;
|
||||
format?: string[];
|
||||
skipTypings?: boolean;
|
||||
outputFileName?: string;
|
||||
generateExportsField?: boolean;
|
||||
updateBuildableProjectDepsInPackageJson?: boolean;
|
||||
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
|
||||
}
|
||||
|
||||
export function updatePackageJson(
|
||||
options: UpdatePackageJsonOption,
|
||||
context: ExecutorContext,
|
||||
target: ProjectGraphProjectNode,
|
||||
dependencies: DependentBuildableProjectNode[]
|
||||
): void {
|
||||
const pathToPackageJson = join(
|
||||
context.root,
|
||||
options.projectRoot,
|
||||
'package.json'
|
||||
);
|
||||
|
||||
const packageJson = fileExists(pathToPackageJson)
|
||||
? readJsonFile(pathToPackageJson)
|
||||
: { name: context.projectName };
|
||||
|
||||
writeJsonFile(
|
||||
`${options.outputPath}/package.json`,
|
||||
getUpdatedPackageJsonContent(packageJson, options)
|
||||
);
|
||||
|
||||
if (
|
||||
dependencies.length > 0 &&
|
||||
options.updateBuildableProjectDepsInPackageJson
|
||||
) {
|
||||
updateBuildableProjectPackageJsonDependencies(
|
||||
context.root,
|
||||
context.projectName,
|
||||
context.targetName,
|
||||
context.configurationName,
|
||||
target,
|
||||
dependencies,
|
||||
options.buildableProjectDepsInPackageJsonType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getUpdatedPackageJsonContent(
|
||||
packageJson: PackageJson,
|
||||
options: UpdatePackageJsonOption
|
||||
) {
|
||||
// Default is CJS unless esm is explicitly passed.
|
||||
const hasCjsFormat = !options.format || options.format?.includes('cjs');
|
||||
const hasEsmFormat = options.format?.includes('esm');
|
||||
|
||||
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
|
||||
const relativeMainFileDir = getMainFileDirRelativeToProjectRoot(
|
||||
options.main,
|
||||
options.projectRoot
|
||||
);
|
||||
const typingsFile = `${relativeMainFileDir}${mainFile}.d.ts`;
|
||||
const exports = {
|
||||
'.': {},
|
||||
...packageJson.exports,
|
||||
};
|
||||
|
||||
const mainJsFile =
|
||||
options.outputFileName ?? `${relativeMainFileDir}${mainFile}.js`;
|
||||
|
||||
if (hasEsmFormat) {
|
||||
// Unofficial field for backwards compat.
|
||||
packageJson.module ??= mainJsFile;
|
||||
|
||||
if (!hasCjsFormat) {
|
||||
packageJson.type = 'module';
|
||||
packageJson.main ??= mainJsFile;
|
||||
}
|
||||
|
||||
exports['.']['import'] = mainJsFile;
|
||||
}
|
||||
|
||||
if (hasCjsFormat) {
|
||||
const { dir, name } = parse(mainJsFile);
|
||||
const cjsMain = `${dir}/${name}.cjs`;
|
||||
packageJson.main ??= cjsMain;
|
||||
exports['.']['require'] = cjsMain;
|
||||
}
|
||||
|
||||
if (options.generateExportsField) {
|
||||
packageJson.exports = exports;
|
||||
}
|
||||
|
||||
if (!options.skipTypings) {
|
||||
packageJson.types = packageJson.types ?? typingsFile;
|
||||
}
|
||||
|
||||
return packageJson;
|
||||
}
|
||||
4
packages/js/src/utils/schema.d.ts
vendored
4
packages/js/src/utils/schema.d.ts
vendored
@ -7,6 +7,7 @@ import type {
|
||||
import { TransformerEntry } from './typescript/types';
|
||||
|
||||
export type Compiler = 'tsc' | 'swc';
|
||||
export type Bundler = 'none' | 'rollup' | 'esbuild' | 'webpack';
|
||||
|
||||
export interface LibraryGeneratorSchema {
|
||||
name: string;
|
||||
@ -28,6 +29,7 @@ export interface LibraryGeneratorSchema {
|
||||
setParserOptionsProject?: boolean;
|
||||
config?: 'workspace' | 'project' | 'npm-scripts';
|
||||
compiler?: Compiler;
|
||||
bundler?: Bundler;
|
||||
skipTypeCheck?: boolean;
|
||||
}
|
||||
|
||||
@ -48,7 +50,7 @@ export interface ExecutorOptions {
|
||||
export interface NormalizedExecutorOptions extends ExecutorOptions {
|
||||
root?: string;
|
||||
sourceRoot?: string;
|
||||
projectRoot?: string;
|
||||
projectRoot: string;
|
||||
mainOutputPath: string;
|
||||
files: Array<FileInputOutput>;
|
||||
}
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
import {
|
||||
ExecutorContext,
|
||||
normalizePath,
|
||||
ProjectGraphProjectNode,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from '@nrwl/devkit';
|
||||
import {
|
||||
DependentBuildableProjectNode,
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||
import { basename, dirname, join, relative } from 'path';
|
||||
import { NormalizedExecutorOptions } from './schema';
|
||||
import { fileExists } from 'nx/src/utils/fileutils';
|
||||
|
||||
function getMainFileDirRelativeToProjectRoot(
|
||||
main: string,
|
||||
projectRoot: string
|
||||
): string {
|
||||
const mainFileDir = dirname(main);
|
||||
const relativeDir = normalizePath(relative(projectRoot, mainFileDir));
|
||||
return relativeDir === '' ? `./` : `./${relativeDir}/`;
|
||||
}
|
||||
|
||||
export function updatePackageJson(
|
||||
options: NormalizedExecutorOptions,
|
||||
context: ExecutorContext,
|
||||
target: ProjectGraphProjectNode<any>,
|
||||
dependencies: DependentBuildableProjectNode[],
|
||||
withTypings = true
|
||||
): void {
|
||||
const pathToPackageJson = join(
|
||||
context.root,
|
||||
options.projectRoot,
|
||||
'package.json'
|
||||
);
|
||||
|
||||
const packageJson = fileExists(pathToPackageJson)
|
||||
? readJsonFile(pathToPackageJson)
|
||||
: { name: context.projectName };
|
||||
|
||||
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
|
||||
const relativeMainFileDir = getMainFileDirRelativeToProjectRoot(
|
||||
options.main,
|
||||
options.projectRoot
|
||||
);
|
||||
const mainJsFile = `${relativeMainFileDir}${mainFile}.js`;
|
||||
const typingsFile = `${relativeMainFileDir}${mainFile}.d.ts`;
|
||||
|
||||
packageJson.main = packageJson.main ?? mainJsFile;
|
||||
|
||||
if (withTypings) {
|
||||
packageJson.typings = packageJson.typings ?? typingsFile;
|
||||
}
|
||||
|
||||
writeJsonFile(`${options.outputPath}/package.json`, packageJson);
|
||||
|
||||
if (
|
||||
dependencies.length > 0 &&
|
||||
options.updateBuildableProjectDepsInPackageJson
|
||||
) {
|
||||
updateBuildableProjectPackageJsonDependencies(
|
||||
context.root,
|
||||
context.projectName,
|
||||
context.targetName,
|
||||
context.configurationName,
|
||||
target,
|
||||
dependencies,
|
||||
options.buildableProjectDepsInPackageJsonType
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,7 @@
|
||||
"autoprefixer": "^10.4.9",
|
||||
"babel-plugin-transform-async-to-promises": "^0.8.15",
|
||||
"chalk": "4.1.0",
|
||||
"dotenv": "~10.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"postcss": "^8.4.14",
|
||||
"rollup": "^2.56.2",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dotenv/config';
|
||||
import * as ts from 'typescript';
|
||||
import * as rollup from 'rollup';
|
||||
import * as peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
"chokidar": "^3.5.1",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"dotenv": "~10.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "7.2.13",
|
||||
"fs-extra": "^10.1.0",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dotenv/config';
|
||||
import { ExecutorContext, logger } from '@nrwl/devkit';
|
||||
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
||||
import type { Configuration, Stats } from 'webpack';
|
||||
|
||||
@ -7,7 +7,7 @@ export type FileInputOutput = {
|
||||
};
|
||||
export type AssetGlob = FileInputOutput & {
|
||||
glob: string;
|
||||
ignore: string[];
|
||||
ignore?: string[];
|
||||
dot?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
"e2e-angular-extensions": "e2e/angular-extensions",
|
||||
"e2e-cypress": "e2e/cypress",
|
||||
"e2e-detox": "e2e/detox",
|
||||
"e2e-esbuild": "e2e/esbuild",
|
||||
"e2e-expo": "e2e/expo",
|
||||
"e2e-graph-client": "graph/client-e2e",
|
||||
"e2e-jest": "e2e/jest",
|
||||
@ -37,6 +38,7 @@
|
||||
"e2e-web": "e2e/web",
|
||||
"e2e-webpack": "e2e/webpack",
|
||||
"e2e-workspace-create": "e2e/workspace-create",
|
||||
"esbuild": "packages/esbuild",
|
||||
"eslint-plugin-nx": "packages/eslint-plugin-nx",
|
||||
"eslint-rules": "tools/eslint-rules",
|
||||
"expo": "packages/expo",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user