@impactor/nx-manager 2.0.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/executors.json +9 -0
  2. package/index.d.ts +11 -0
  3. package/index.js +16 -0
  4. package/package.json +30 -20
  5. package/src/executors/universal-builder/builders/angular.d.ts +16 -0
  6. package/src/executors/universal-builder/builders/angular.js +112 -0
  7. package/src/executors/universal-builder/builders/esbuild.d.ts +44 -0
  8. package/src/executors/universal-builder/builders/esbuild.js +63 -0
  9. package/src/executors/universal-builder/builders/nest.d.ts +6 -0
  10. package/src/executors/universal-builder/builders/nest.js +54 -0
  11. package/src/executors/universal-builder/post-build.d.ts +2 -0
  12. package/src/executors/universal-builder/post-build.js +82 -0
  13. package/src/executors/universal-builder/schema.d.js +0 -0
  14. package/src/executors/universal-builder/types/builder-options.d.ts +44 -0
  15. package/src/executors/universal-builder/types/builder-options.js +0 -0
  16. package/src/executors/universal-builder/types/builder.d.ts +1 -0
  17. package/src/executors/universal-builder/types/builder.js +0 -0
  18. package/src/executors/universal-builder/types/package.d.ts +64 -0
  19. package/src/executors/universal-builder/types/package.js +0 -0
  20. package/src/executors/universal-builder/universal-builder.d.ts +6 -0
  21. package/src/executors/universal-builder/universal-builder.js +123 -0
  22. package/src/executors/universal-builder/universal-builder.spec.js +23 -0
  23. package/src/executors/universal-builder/utils/detect-project-type.d.ts +7 -0
  24. package/src/executors/universal-builder/utils/detect-project-type.js +21 -0
  25. package/src/executors/universal-builder/utils/exec.d.ts +1 -0
  26. package/src/executors/universal-builder/utils/exec.js +22 -0
  27. package/src/executors/universal-builder/utils/fs.d.ts +5 -0
  28. package/src/executors/universal-builder/utils/fs.js +92 -0
  29. package/src/executors/universal-builder/utils/get-tsconfig-file.d.ts +2 -0
  30. package/src/executors/universal-builder/utils/get-tsconfig-file.js +12 -0
package/executors.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "executors": {
3
+ "universal-builder": {
4
+ "implementation": "./src/executors/universal-builder/universal-builder",
5
+ "schema": "./src/executors/universal-builder/schema.json",
6
+ "description": "universal-builder executor"
7
+ }
8
+ }
9
+ }
package/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { default as ngBuilder } from './src/executors/universal-builder/builders/angular.js';
2
+ export { default as esBuilder } from './src/executors/universal-builder/builders/esbuild.js';
3
+ export { default as nestBuilder } from './src/executors/universal-builder/builders/nest.js';
4
+ export * from './src/executors/universal-builder/post-build.js';
5
+ export * from './src/executors/universal-builder/types/builder.js';
6
+ export * from './src/executors/universal-builder/types/builder-options.js';
7
+ export * from './src/executors/universal-builder/types/package.js';
8
+ export * from './src/executors/universal-builder/universal-builder.js';
9
+ export * from './src/executors/universal-builder/utils/detect-project-type.js';
10
+ export * from './src/executors/universal-builder/utils/exec.js';
11
+ export * from './src/executors/universal-builder/utils/get-tsconfig-file.js';
package/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { default as default2 } from "./src/executors/universal-builder/builders/angular.js";
2
+ import { default as default3 } from "./src/executors/universal-builder/builders/esbuild.js";
3
+ import { default as default4 } from "./src/executors/universal-builder/builders/nest.js";
4
+ export * from "./src/executors/universal-builder/post-build.js";
5
+ export * from "./src/executors/universal-builder/types/builder.js";
6
+ export * from "./src/executors/universal-builder/types/builder-options.js";
7
+ export * from "./src/executors/universal-builder/types/package.js";
8
+ export * from "./src/executors/universal-builder/universal-builder.js";
9
+ export * from "./src/executors/universal-builder/utils/detect-project-type.js";
10
+ export * from "./src/executors/universal-builder/utils/exec.js";
11
+ export * from "./src/executors/universal-builder/utils/get-tsconfig-file.js";
12
+ export {
13
+ default3 as esBuilder,
14
+ default4 as nestBuilder,
15
+ default2 as ngBuilder
16
+ };
package/package.json CHANGED
@@ -1,46 +1,54 @@
1
1
  {
2
2
  "name": "@impactor/nx-manager",
3
- "version": "2.0.2",
3
+ "version": "3.0.0",
4
4
  "description": "NX generators and executors",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "publishConfig": {
8
- "access": "public"
8
+ "access": "public",
9
+ "linkDirectory": false
9
10
  },
10
11
  "nx": {
11
12
  "projectType": "library",
12
13
  "targets": {
13
- "build": {},
14
- "semantic-release": {}
14
+ "build": {
15
+ "options": {
16
+ "postBuild": [
17
+ "default",
18
+ "{workspaceRoot}/tasks/post-build.ts",
19
+ "{projectRoot}/post-build.ts"
20
+ ]
21
+ }
22
+ }
15
23
  }
16
24
  },
25
+ "imports": {
26
+ "#*": "./*"
27
+ },
17
28
  "exports": {
18
29
  ".": {
19
30
  "types": "./index.d.js",
20
31
  "default": "./index.js"
21
32
  },
33
+ "./*": {
34
+ "types": "./src/*",
35
+ "default": "./src/*"
36
+ },
22
37
  "./package.json": {
23
38
  "default": "./package.json"
24
39
  },
25
- "./*": "./src/*"
40
+ "./executors.json": "./executors.json"
26
41
  },
27
- "files": [
28
- "dist",
29
- "!**/*.tsbuildinfo",
30
- "executors.json"
31
- ],
42
+ "types": "./index.d.ts",
43
+ "main": "./index.js",
32
44
  "dependencies": {
33
- "@nx/angular": "22.3.3",
34
- "@nx/devkit": "22.3.3",
35
- "@nx/esbuild": "22.3.3",
36
- "tslib": "^2.8.1",
37
- "@impactor/nodejs": "3.0.2"
45
+ "@nx/angular": "22.5.0",
46
+ "@nx/devkit": "22.5.0",
47
+ "@nx/esbuild": "22.5.0",
48
+ "tslib": "^2.8.1"
38
49
  },
39
50
  "executors": "./executors.json",
40
- "devDependencies": {
41
- "@nestjs/cli": "^11.0.16",
42
- "esbuild": "^0.27.2"
43
- },
51
+ "devDependencies": {},
44
52
  "engines": {
45
53
  "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
46
54
  },
@@ -66,5 +74,7 @@
66
74
  "url": "https://www.patreon.com/GoogleDev"
67
75
  }
68
76
  ],
69
- "scripts": {}
77
+ "scripts": {
78
+ "start": "NODE_ENV=production pnpm tsx --env-file-if-exists=.env --env-file-if-exists=.env.local --env-file-if-exists=.env.production --env-file-if-exists=.env.production.local main.js"
79
+ }
70
80
  }
@@ -0,0 +1,16 @@
1
+ import { type ExecutorContext } from '@nx/devkit';
2
+ import type { IBuildAngularAppOptions, IBuildAngularLibraryOptions, IBuildAngularNormalizedOptions, IBuildAngularOptions } from '../types/builder-options.js';
3
+ export default function build(options: IBuildAngularOptions, ctx: ExecutorContext): Promise<{
4
+ success: boolean;
5
+ }>;
6
+ export declare function buildAngularLibrary(options: IBuildAngularNormalizedOptions, ctx: ExecutorContext): Promise<{
7
+ success: boolean;
8
+ }>;
9
+ export declare function buildAngularApp(options: IBuildAngularNormalizedOptions, ctx: ExecutorContext): Promise<{
10
+ success: boolean;
11
+ }>;
12
+ export declare function getOptions(options: IBuildAngularOptions, _ctx: ExecutorContext): IBuildAngularNormalizedOptions;
13
+ export declare function getLibraryOptions(options: IBuildAngularNormalizedOptions, _ctx: ExecutorContext): IBuildAngularLibraryOptions;
14
+ export declare function getAppOptions(options: IBuildAngularNormalizedOptions, _ctx: ExecutorContext): IBuildAngularAppOptions & {
15
+ tsConfig: string;
16
+ };
@@ -0,0 +1,112 @@
1
+ import { packageExecutor } from "@nx/angular/src/executors/package/package.impl";
2
+ import applicationExecutor from "@nx/angular/src/executors/application/application.impl";
3
+ import { getTsConfigFile } from "../utils/get-tsconfig-file";
4
+ import { relative } from "node:path";
5
+ import { readJSON } from "../utils/fs";
6
+ async function build(options, ctx) {
7
+ let opts = getOptions(options, ctx);
8
+ if (opts.projectType === "library") return buildAngularLibrary(opts, ctx);
9
+ if (opts.projectType === "application") return buildAngularApp(opts, ctx);
10
+ return { success: false };
11
+ }
12
+ async function buildAngularLibrary(options, ctx) {
13
+ let opts = getLibraryOptions(options, ctx);
14
+ let results = await packageExecutor(opts, ctx);
15
+ for await (let result of results) {
16
+ if (!result.success) return { success: false };
17
+ }
18
+ return { success: true };
19
+ }
20
+ async function buildAngularApp(options, ctx) {
21
+ let opts = getAppOptions(options, ctx);
22
+ let results = await applicationExecutor(opts, ctx);
23
+ for await (let result of results) {
24
+ if (!result.success) return { success: false };
25
+ }
26
+ return { success: true };
27
+ }
28
+ function getOptions(options, _ctx) {
29
+ let opts = { ...options };
30
+ if (!opts.angularConfigs) {
31
+ opts.angularConfigs = readJSON(
32
+ `${opts.projectPath}/angular.json`
33
+ );
34
+ }
35
+ opts.projectType = opts.projectType || opts.projectConfig.projectType || // todo: remove the scope part from the projectName i.e. @scope/name -> name
36
+ // @ts-ignore
37
+ opts.angularConfigs?.projects?.[opts.projectName]?.projectType;
38
+ if (!opts.projectType) {
39
+ throw new Error(`projectType is not provided`);
40
+ }
41
+ if (!["library", "application"].includes(opts.projectType)) {
42
+ throw new Error(
43
+ `projectType ${opts.projectType} is not supported. supported values are "library","application"`
44
+ );
45
+ }
46
+ let tsConfig = (
47
+ // todo: remove `./`
48
+ opts.tsConfig || // @ts-ignore
49
+ opts.angularConfigs?.projects?.[opts.projectName]?.tsConfig || getTsConfigFile(opts)
50
+ );
51
+ let relativeProjectPath = relative(opts.root, opts.projectPath);
52
+ opts.tsConfig = `${relativeProjectPath}/${tsConfig}`;
53
+ let buildOptions = (
54
+ // @ts-ignore
55
+ opts.angularConfigs?.projects?.[opts.projectName]?.architect?.build?.options
56
+ );
57
+ if (buildOptions) {
58
+ let properties = [
59
+ "root",
60
+ "sourceRoot",
61
+ "index",
62
+ "browser",
63
+ "assets",
64
+ "styles",
65
+ "scripts",
66
+ "server",
67
+ "serviceWorker",
68
+ "outputPath",
69
+ "tsConfig"
70
+ // 'serviceWorker',
71
+ ];
72
+ for (let prop of properties) {
73
+ if (typeof buildOptions[prop] === "string") {
74
+ buildOptions[prop] = `${relativeProjectPath}/${buildOptions[prop]}`;
75
+ } else if (Array.isArray(buildOptions[prop])) {
76
+ buildOptions[prop] = buildOptions[prop]?.map(
77
+ (el) => {
78
+ return typeof el === "string" ? `${relativeProjectPath}/${el}` : { ...el, input: `${relativeProjectPath}/${el.input}` };
79
+ }
80
+ );
81
+ }
82
+ }
83
+ if (buildOptions.ssr?.entry) {
84
+ buildOptions.ssr.entry = `${relativeProjectPath}/${buildOptions.ssr.entry}`;
85
+ }
86
+ }
87
+ opts = {
88
+ ...buildOptions,
89
+ ...opts
90
+ };
91
+ return opts;
92
+ }
93
+ function getLibraryOptions(options, _ctx) {
94
+ let opts = {
95
+ // todo: only if exists
96
+ project: `${options.projectPath}/ng-package.json`,
97
+ ...options
98
+ };
99
+ return opts;
100
+ }
101
+ function getAppOptions(options, _ctx) {
102
+ let opts = { ...options };
103
+ return opts;
104
+ }
105
+ export {
106
+ buildAngularApp,
107
+ buildAngularLibrary,
108
+ build as default,
109
+ getAppOptions,
110
+ getLibraryOptions,
111
+ getOptions
112
+ };
@@ -0,0 +1,44 @@
1
+ import { type EsBuildExecutorOptions } from '@nx/esbuild/src/executors/esbuild/schema.js';
2
+ import { type IBuildEsbuildOptions } from '../types/builder-options.js';
3
+ import { type ExecutorContext } from '@nx/devkit';
4
+ export default function build(options: IBuildEsbuildOptions, ctx: ExecutorContext): Promise<{
5
+ success: boolean;
6
+ }>;
7
+ export declare function getTargetOptions(opts: IBuildEsbuildOptions, ctx: ExecutorContext): Promise<{
8
+ tsConfig?: EsBuildExecutorOptions["tsConfig"];
9
+ esbuildConfigFile?: string;
10
+ esbuildOptions?: import("~~node_modules/esbuild/lib/main.js").BuildOptions;
11
+ builder: import("../types/builder.js").Builder;
12
+ projectName: string;
13
+ projectPath: string;
14
+ projectConfig: import("@nx/devkit").ProjectConfiguration;
15
+ flags: string[];
16
+ root: string;
17
+ outDir?: string;
18
+ postBuild?: string[] | null;
19
+ mode?: "development" | "production";
20
+ cwd?: string;
21
+ additionalEntryPoints?: string[] | undefined;
22
+ assets: (import("~~node_modules/@nx/js/src/utils/assets/assets.js").AssetGlob | string)[];
23
+ bundle?: boolean | undefined;
24
+ declaration?: boolean | undefined;
25
+ declarationRootDir?: string | undefined;
26
+ deleteOutputPath?: boolean | undefined;
27
+ esbuildConfig?: string | undefined;
28
+ external?: string[] | undefined;
29
+ excludeFromExternal?: string[] | undefined;
30
+ format?: Array<"esm" | "cjs"> | undefined;
31
+ generatePackageJson?: boolean | undefined;
32
+ main?: string | undefined;
33
+ metafile?: boolean | undefined;
34
+ minify?: boolean | undefined;
35
+ outputFileName?: string | undefined;
36
+ outputHashing?: "none" | "all" | undefined;
37
+ outputPath?: string | undefined;
38
+ platform?: "node" | "browser" | "neutral" | undefined;
39
+ sourcemap?: boolean | "linked" | "inline" | "external" | "both" | undefined;
40
+ skipTypeCheck?: boolean | undefined;
41
+ target?: string | undefined;
42
+ thirdParty?: boolean | undefined;
43
+ watch?: boolean | undefined;
44
+ }>;
@@ -0,0 +1,63 @@
1
+ import { esbuildExecutor } from "@nx/esbuild/src/executors/esbuild/esbuild.impl.js";
2
+ import { existsSync } from "node:fs";
3
+ import { getTsConfigFile } from "../utils/get-tsconfig-file";
4
+ async function build(options, ctx) {
5
+ let opts = await getTargetOptions(options, ctx);
6
+ ctx.target = ctx.target || {};
7
+ ctx.target.options.outputPath = opts.outputPath;
8
+ let results = esbuildExecutor(opts, ctx);
9
+ for await (let result of results) {
10
+ if (!result.success) return { success: false };
11
+ }
12
+ return { success: true };
13
+ }
14
+ async function getTargetOptions(opts, ctx) {
15
+ if (!opts.esbuildOptions && !opts.esbuildConfig) {
16
+ if (!opts.esbuildConfigFile) {
17
+ if (existsSync(`${opts.projectPath}/esbuild.config.ts`)) {
18
+ opts.esbuildConfigFile = `${opts.projectPath}/esbuild.config.ts`;
19
+ } else if (existsSync(`${ctx.root}/esbuild.config.ts`)) {
20
+ opts.esbuildConfigFile = `${ctx.root}/esbuild.config.ts`;
21
+ }
22
+ }
23
+ if (!opts.tsConfig) {
24
+ opts.tsConfig = `${getTsConfigFile(opts)}`;
25
+ }
26
+ if (opts.esbuildConfigFile) {
27
+ opts.esbuildOptions = await import(opts.esbuildConfigFile).then(
28
+ (m) => m.default({
29
+ projectRoot: opts.projectPath,
30
+ tsconfig: `${opts.projectPath}/${opts.tsConfig}`
31
+ })
32
+ );
33
+ }
34
+ }
35
+ if (!opts.outputPath) {
36
+ opts.outputPath = opts.esbuildOptions?.outdir || "dist";
37
+ }
38
+ if (!opts.main) {
39
+ if (Array.isArray(opts.esbuildOptions?.entryPoints) && opts.esbuildOptions.entryPoints.length > 0 && opts.esbuildOptions.entryPoints.every((el) => typeof el === "string")) {
40
+ [opts.main, ...opts.additionalEntryPoints] = opts.esbuildOptions.entryPoints;
41
+ } else if (existsSync(`${opts.projectPath}/index.ts`)) {
42
+ opts.main = `${opts.projectPath}/index.ts`;
43
+ } else if (existsSync(`${opts.projectPath}/src/index.ts`)) {
44
+ opts.main = `${opts.projectPath}/src/index.ts`;
45
+ } else {
46
+ throw new Error(
47
+ `[universal-builder] No entry point found for esbuild. specify options.esbuildOptions.entrypoint or create index.ts or src/index.ts in the project's root dir`
48
+ );
49
+ }
50
+ }
51
+ if (!opts.format) {
52
+ if (opts.esbuildOptions?.format && opts.esbuildOptions.format !== "iife") {
53
+ opts.format = [opts.esbuildOptions.format];
54
+ } else {
55
+ opts.format = ["esm"];
56
+ }
57
+ }
58
+ return { assets: [], ...opts };
59
+ }
60
+ export {
61
+ build as default,
62
+ getTargetOptions
63
+ };
@@ -0,0 +1,6 @@
1
+ import { type ExecutorContext } from '@nx/devkit';
2
+ import type { IBuilderNestOptions } from '../types/builder-options.js';
3
+ export default function build(options: IBuilderNestOptions, ctx: ExecutorContext): Promise<{
4
+ success: boolean;
5
+ }>;
6
+ export declare function getOptions(options: IBuilderNestOptions, _ctx: ExecutorContext): IBuilderNestOptions;
@@ -0,0 +1,54 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { exec } from "../utils/exec";
4
+ async function build(options, ctx) {
5
+ let opts = getOptions(options, ctx);
6
+ let flags = ["@nestjs/cli", "build", ...opts.flags];
7
+ if (!flags.includes("--path") && !flags.includes("-p") && opts.tsConfig) {
8
+ flags.push("-p", opts.tsConfig);
9
+ }
10
+ if (opts.bundler) {
11
+ if (["tsc", "webpack"].includes(opts.bundler) && !flags.includes(`--${opts.bundler}`)) {
12
+ flags.push(`--${opts.bundler}`);
13
+ } else if (!flags.includes("--builder") && !flags.includes("-b")) {
14
+ flags.push("-b", opts.bundler);
15
+ }
16
+ }
17
+ if (!flags.includes("--config") && !flags.includes("-c") && opts.configFile) {
18
+ flags.push("-c", opts.configFile);
19
+ }
20
+ if (opts.typeCheck) {
21
+ flags.push("--type-check");
22
+ }
23
+ if (opts.watch && !flags.includes("--watch") && !flags.includes("-w")) {
24
+ flags.push("-w");
25
+ }
26
+ if (opts.watch && !flags.includes("--watchAssets")) {
27
+ flags.push("--watchAssets");
28
+ }
29
+ if (opts.webpackPath && !flags.includes("--webpackPath")) {
30
+ flags.push("--webpackPath", opts.webpackPath);
31
+ }
32
+ if (opts.webpackPath && !flags.includes("--preserveWatchOutput")) {
33
+ flags.push("--preserveWatchOutput", opts.webpackPath);
34
+ }
35
+ await exec("npx", flags, opts.projectPath);
36
+ return { success: true };
37
+ }
38
+ function getOptions(options, _ctx) {
39
+ let opts = { ...options };
40
+ if (!opts.bundler && !opts.flags.includes("--builder") && !opts.flags.includes("-b") && existsSync(resolve(`${opts.projectPath}/.swcrc`))) {
41
+ console.info(
42
+ `[universal-builder] SWC config file is detected, building using SWC`
43
+ );
44
+ opts.bundler = "swc";
45
+ if (!opts.configFile && !opts.flags.includes("--config") && !opts.flags.includes("-c")) {
46
+ opts.configFile = ".swcrc";
47
+ }
48
+ }
49
+ return opts;
50
+ }
51
+ export {
52
+ build as default,
53
+ getOptions
54
+ };
@@ -0,0 +1,2 @@
1
+ import { IBuilderBaseOptions } from './types/builder-options.js';
2
+ export default function postBuild(options: IBuilderBaseOptions): void;
@@ -0,0 +1,82 @@
1
+ import { copyFileSync, existsSync, readdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { readJSON, writeJSON } from "./utils/fs";
4
+ function postBuild(options) {
5
+ console.info(
6
+ `[universal-builder] running the default post-build script. to skip use options.postBuild=false`
7
+ );
8
+ let opts = {
9
+ ...options
10
+ };
11
+ let pkg = readJSON(`${opts.projectPath}/package.json`) || {}, distPkg = existsSync(`${opts.projectPath}/dist/package.json`) ? readJSON(`${opts.projectPath}/dist/package.json`) : pkg, rootPkg = readJSON(`${opts.root}/package.json`) || {}, dist = `${opts.projectPath}/${opts.outDir || "dist"}`;
12
+ if (!existsSync(dist)) {
13
+ throw new Error(
14
+ `[universal-builder] "${dist}" doesn't exist, be sure the app is built. if you're using a custom output dir, provide options.outDir`
15
+ );
16
+ }
17
+ for (let property of [
18
+ "engines",
19
+ "packageManager",
20
+ "repository",
21
+ "author",
22
+ "homepage",
23
+ "bugs",
24
+ "license",
25
+ "contributors",
26
+ "funding",
27
+ // `add pnpm.overrides` to override deps when installing in the server
28
+ "pnpm"
29
+ ]) {
30
+ if (!distPkg[property]) {
31
+ if (pkg[property]) distPkg[property] = pkg[property];
32
+ else if (rootPkg[property]) distPkg[property] = rootPkg[property];
33
+ }
34
+ }
35
+ if (!distPkg.exports) {
36
+ distPkg.exports = {};
37
+ }
38
+ if (!distPkg.exports["./package.json"]) {
39
+ distPkg.exports["./package.json"] = "./package.json";
40
+ }
41
+ if (distPkg.exports["."]) {
42
+ if (typeof distPkg.exports["."] === "string") {
43
+ distPkg.exports["."] = distPkg.exports["."].replace(/\.ts$/, ".js");
44
+ } else {
45
+ for (let key in distPkg.exports["."]) {
46
+ distPkg.exports["."][key] = distPkg.exports["."][key].replace(
47
+ /\.ts$/,
48
+ ".js"
49
+ );
50
+ }
51
+ }
52
+ } else {
53
+ distPkg.exports["."] = {
54
+ types: "./index.d.ts",
55
+ default: "./index.js"
56
+ };
57
+ }
58
+ if (!distPkg.exports["./*"]) {
59
+ distPkg.exports["./*"] = existsSync(`${dist}/src`) ? "./src/*" : "./*";
60
+ }
61
+ writeJSON(`${dist}/package.json`, distPkg);
62
+ ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"].map((el) => {
63
+ let path = resolve(`${opts.root}/${el}`);
64
+ if (existsSync(path)) {
65
+ copyFileSync(path, resolve(`${dist}/${el}`));
66
+ }
67
+ });
68
+ readdirSync(opts.projectPath).filter(
69
+ (el) => (
70
+ // matches `.env` and `.env.*`
71
+ /^\.env($|\..+)/.test(el)
72
+ )
73
+ ).map((el) => copyFileSync(`${opts.projectPath}/${el}`, `${dist}/${el}`));
74
+ ["README.md"].map((file) => {
75
+ if (existsSync(`${opts.projectPath}/${file}`)) {
76
+ copyFileSync(`${opts.projectPath}/${file}`, `${dist}/${file}`);
77
+ }
78
+ });
79
+ }
80
+ export {
81
+ postBuild as default
82
+ };
File without changes
@@ -0,0 +1,44 @@
1
+ import { ProjectConfiguration } from 'nx/src/devkit-exports.js';
2
+ import { UniversalBuilderExecutorSchema } from '../schema.js';
3
+ import { type Builder } from './builder.js';
4
+ import { type BuildOptions } from 'esbuild';
5
+ import type { BuildAngularLibraryExecutorOptions } from '@nx/angular/src/executors/package/schema';
6
+ import type { ApplicationExecutorOptions } from '@nx/angular/src/executors/application/schema';
7
+ import type { EsBuildExecutorOptions } from '@nx/esbuild/src/executors/esbuild/schema.js';
8
+ export type IBuilderOptions = IBuildEsbuildOptions | IBuildAngularOptions | IBuilderNestOptions;
9
+ export interface IBuilderBaseOptions extends UniversalBuilderExecutorSchema {
10
+ builder: Builder;
11
+ projectName: string;
12
+ projectPath: string;
13
+ projectConfig: ProjectConfiguration;
14
+ flags: string[];
15
+ root: string;
16
+ }
17
+ export interface IBuildEsbuildOptions extends IBuilderBaseOptions, Partial<EsBuildExecutorOptions> {
18
+ tsConfig?: EsBuildExecutorOptions['tsConfig'];
19
+ esbuildConfigFile?: string;
20
+ esbuildOptions?: BuildOptions;
21
+ }
22
+ export interface IBuildAngularBaseOptions extends IBuilderBaseOptions {
23
+ projectType?: 'application' | 'library';
24
+ tsConfig?: string;
25
+ angularConfigs?: Record<string, unknown>;
26
+ }
27
+ export interface IBuildAngularLibraryOptions extends IBuildAngularBaseOptions, BuildAngularLibraryExecutorOptions {
28
+ }
29
+ export interface IBuildAngularAppOptions extends IBuildAngularBaseOptions, Omit<ApplicationExecutorOptions, 'tsConfig'> {
30
+ }
31
+ export type IBuildAngularOptions = IBuildAngularLibraryOptions | IBuildAngularAppOptions;
32
+ export type IBuildAngularNormalizedOptions = IBuildAngularOptions & {
33
+ tsConfig: string;
34
+ };
35
+ export interface IBuilderNestOptions extends IBuilderBaseOptions {
36
+ bundler?: 'swc' | 'webpack' | 'tsc';
37
+ configFile?: string;
38
+ typeCheck?: boolean;
39
+ tsConfig?: string;
40
+ watch?: boolean;
41
+ watchAssets?: boolean;
42
+ webpackPath?: string;
43
+ preserveWatchOutput?: boolean;
44
+ }
@@ -0,0 +1 @@
1
+ export type Builder = 'tsc' | 'esbuild' | 'webpack' | 'angular' | 'nest';
@@ -0,0 +1,64 @@
1
+ export interface IPackage {
2
+ name?: string;
3
+ description?: string;
4
+ version?: string;
5
+ type?: 'module' | 'commonjs';
6
+ workspaces?: string[];
7
+ private?: boolean;
8
+ engines: {
9
+ node?: string;
10
+ [key: string]: unknown;
11
+ };
12
+ packageManager?: string;
13
+ scripts?: {
14
+ [key: string]: string;
15
+ };
16
+ imports?: {
17
+ [key: string]: string | {
18
+ [key: string]: string;
19
+ };
20
+ };
21
+ exports?: {
22
+ [key: string]: string | {
23
+ [key: string]: string;
24
+ };
25
+ };
26
+ dependencies?: {
27
+ [key: string]: string;
28
+ };
29
+ devDependencies?: {
30
+ [key: string]: string;
31
+ };
32
+ peerDependencies?: {
33
+ [key: string]: string;
34
+ };
35
+ optionalDependencies?: {
36
+ [key: string]: string;
37
+ };
38
+ keywords?: string[];
39
+ repository: string | {
40
+ type?: string;
41
+ url?: string;
42
+ [key: string]: unknown;
43
+ };
44
+ homepage?: string;
45
+ bugs: {
46
+ url?: string;
47
+ email?: string;
48
+ };
49
+ license?: string;
50
+ author?: string;
51
+ contributors?: string[];
52
+ funding?: [
53
+ {
54
+ type?: string;
55
+ url?: string;
56
+ }
57
+ ];
58
+ pnpm?: {
59
+ overrides?: {
60
+ [key: string]: string;
61
+ };
62
+ };
63
+ [key: string]: unknown;
64
+ }
@@ -0,0 +1,6 @@
1
+ import { type ExecutorContext, type PromiseExecutor } from '@nx/devkit';
2
+ import type { UniversalBuilderExecutorSchema } from './schema.js';
3
+ import type { IBuilderOptions } from './types/builder-options.js';
4
+ declare const run: PromiseExecutor<UniversalBuilderExecutorSchema>;
5
+ export default run;
6
+ export declare function getOptions(options: UniversalBuilderExecutorSchema, ctx: ExecutorContext): IBuilderOptions;
@@ -0,0 +1,123 @@
1
+ import {
2
+ joinPathFragments
3
+ } from "@nx/devkit";
4
+ import {
5
+ detectProjectType
6
+ } from "./utils/detect-project-type";
7
+ import buildESBuild from "./builders/esbuild";
8
+ import buildAngular from "./builders/angular";
9
+ import buildNest from "./builders/nest";
10
+ import { exec } from "./utils/exec";
11
+ import { readFileSync } from "node:fs";
12
+ const run = async (options, ctx) => {
13
+ let opts = getOptions(options, ctx);
14
+ console.info(`[universal-builder] building using builder: ${opts.builder}`);
15
+ let result;
16
+ let originalCwd = process.cwd();
17
+ if (opts.cwd && opts.cwd !== originalCwd) {
18
+ console.info(
19
+ `[universal-builder] changing CWD from ${originalCwd} to ${opts.cwd}`
20
+ );
21
+ process.chdir(opts.cwd);
22
+ }
23
+ if (opts.builder === "angular") {
24
+ result = await buildAngular(opts, ctx);
25
+ } else if (opts.builder === "nest") {
26
+ result = await buildNest(opts, ctx);
27
+ } else if (opts.builder === "esbuild") {
28
+ result = await buildESBuild(opts, ctx);
29
+ } else if (opts.builder === "webpack") {
30
+ throw new Error(
31
+ `[universal-builder] webpack builder has not implemented yet!`
32
+ );
33
+ } else {
34
+ throw new Error(
35
+ `[universal-builder] invalid or not supported builder ${opts.builder}`
36
+ );
37
+ }
38
+ if (!result.success) return result;
39
+ if (opts.postBuild !== null) {
40
+ console.info(`[universal-builder] executing postbuild commands`);
41
+ if (!opts.postBuild) {
42
+ opts.postBuild = ["default", "script:postbuild"];
43
+ }
44
+ for (let item of opts.postBuild) {
45
+ if (item.toLowerCase() === "default") {
46
+ await import("./post-build").then((m) => m.default(opts));
47
+ } else if (item.toLowerCase().startsWith("script:")) {
48
+ let script = item.split(":").slice().join("");
49
+ console.info(
50
+ `[universal-builder] executing the script ${script} (if exists)`
51
+ );
52
+ let pkg = JSON.parse(
53
+ readFileSync(`${opts.projectPath}/package.json`, "utf8")
54
+ );
55
+ if (pkg.scripts?.[script]) {
56
+ await exec(
57
+ // todo: detect the package manager (may be NX already provide it with opts)
58
+ "pnpm",
59
+ ["run", script],
60
+ // the script should be executed from the project root even if opts.cwd is not specified
61
+ opts.projectPath
62
+ );
63
+ }
64
+ } else if (item.endsWith(".js") || item.endsWith(".ts")) {
65
+ await import(`${opts.root}/${item}`).then((m) => m.default(opts));
66
+ } else {
67
+ await exec(item, void 0, opts.projectPath);
68
+ }
69
+ }
70
+ }
71
+ if (opts.cwd && opts.cwd !== originalCwd) {
72
+ console.info(
73
+ `[universal-builder] resetting CWD from ${opts.cwd} to ${originalCwd}`
74
+ );
75
+ process.chdir(originalCwd);
76
+ }
77
+ console.info(
78
+ `[universal-builder] building ${opts.projectName} finished successfully`
79
+ );
80
+ return {
81
+ success: true
82
+ };
83
+ };
84
+ var universal_builder_default = run;
85
+ function getOptions(options, ctx) {
86
+ let opts = {
87
+ projectName: ctx.projectName,
88
+ flags: [],
89
+ ...options
90
+ };
91
+ if (!opts.projectName) {
92
+ throw new Error("[universal-builder] projectName is not provided");
93
+ }
94
+ let projectConfig = ctx.projectsConfigurations?.projects[opts.projectName];
95
+ if (!projectConfig) {
96
+ throw new Error(
97
+ `[universal-builder] The project ${opts.projectName} is not existing`
98
+ );
99
+ }
100
+ if (!opts.projectPath) {
101
+ opts.projectPath = joinPathFragments(ctx.root, projectConfig.root);
102
+ }
103
+ if (!opts.projectPath) {
104
+ throw new Error("[universal-builder] projectPath is not provided");
105
+ }
106
+ if (!opts.builder) {
107
+ opts.builder = detectProjectType({
108
+ ...opts,
109
+ root: ctx.root
110
+ });
111
+ }
112
+ if (!opts.builder) {
113
+ throw new Error("[universal-builder] builder is not provided");
114
+ }
115
+ if (!opts.mode) {
116
+ opts.mode = process.env.NODE_MODE || ctx.configurationName || "production";
117
+ }
118
+ return { ...opts, root: ctx.root, projectConfig };
119
+ }
120
+ export {
121
+ universal_builder_default as default,
122
+ getOptions
123
+ };
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import executor from "./universal-builder";
3
+ const options = {};
4
+ const context = {
5
+ root: "",
6
+ cwd: process.cwd(),
7
+ isVerbose: false,
8
+ projectGraph: {
9
+ nodes: {},
10
+ dependencies: {}
11
+ },
12
+ projectsConfigurations: {
13
+ projects: {},
14
+ version: 2
15
+ },
16
+ nxJsonConfiguration: {}
17
+ };
18
+ describe("UniversalBuilder Executor", () => {
19
+ it("can run", async () => {
20
+ const output = await executor(options, context);
21
+ expect(output.success).toBe(true);
22
+ });
23
+ });
@@ -0,0 +1,7 @@
1
+ import { Builder } from '../types/builder.js';
2
+ export interface IDetectProjectTypeOptions {
3
+ projectPath: string;
4
+ projectName?: string;
5
+ root?: string;
6
+ }
7
+ export declare function detectProjectType(options: IDetectProjectTypeOptions): Builder;
@@ -0,0 +1,21 @@
1
+ import { existsSync } from "node:fs";
2
+ function detectProjectType(options) {
3
+ if (existsSync(`${options.projectPath}/nest-cli.json`)) {
4
+ return "nest";
5
+ } else if (existsSync(`${options.projectPath}/angular.json`)) {
6
+ return "angular";
7
+ } else if (["ng-", "ngx-"].some((prefix) => {
8
+ let pkgName = options.projectName?.match(/(?:@.+\/)?(.+)/)?.[1];
9
+ pkgName?.startsWith(prefix);
10
+ })) {
11
+ return "angular";
12
+ } else if (existsSync(`${options.projectPath}/esbuild.config.ts`) || existsSync(`${options.root}/esbuild.config.ts`)) {
13
+ return "esbuild";
14
+ } else if (existsSync(`${options.projectPath}/webpack.config.js`)) {
15
+ return "webpack";
16
+ } else if (existsSync(`${options.projectPath}/tsconfig.json`)) return "tsc";
17
+ else throw new Error(`cannot detect the project type`);
18
+ }
19
+ export {
20
+ detectProjectType
21
+ };
@@ -0,0 +1 @@
1
+ export declare function exec(cmd: string, args?: string[], cwd?: string): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { spawn } from "node:child_process";
2
+ function exec(cmd, args = [], cwd) {
3
+ console.info(`[universal-builder] cwd: ${cwd || process.cwd()}`);
4
+ console.info(`[universal-builder] > ${cmd} ${args?.join(" ")}`);
5
+ return new Promise((res, rej) => {
6
+ let childProcess = spawn(cmd, args, {
7
+ shell: true,
8
+ cwd,
9
+ stdio: "inherit"
10
+ });
11
+ childProcess.on("error", (error) => {
12
+ rej(error);
13
+ });
14
+ childProcess.on("exit", (exitCode) => {
15
+ if (exitCode === 0) res();
16
+ else rej(`Process exited with code ${exitCode}`);
17
+ });
18
+ });
19
+ }
20
+ export {
21
+ exec
22
+ };
@@ -0,0 +1,5 @@
1
+ import { PathLike } from 'node:fs';
2
+ export declare function readJSON<R extends Record<string, unknown>>(path: PathLike): R;
3
+ export declare function writeJSON(path: PathLike, data: any): void;
4
+ export declare function cleanJson(content: string): string;
5
+ export declare function getEntriesSync(dir?: string | string[], filter?: ((entry: string) => boolean) | RegExp | 'files' | 'dirs', depth?: number, skip?: ((entry: string) => boolean) | RegExp): Array<string>;
@@ -0,0 +1,92 @@
1
+ import {
2
+ lstatSync,
3
+ mkdirSync,
4
+ readdirSync,
5
+ readFileSync,
6
+ writeFileSync
7
+ } from "node:fs";
8
+ import { dirname, join, resolve } from "node:path";
9
+ function readJSON(path) {
10
+ path = resolve(path.toString());
11
+ let data = readFileSync(path, "utf8");
12
+ return JSON.parse(cleanJson(data));
13
+ }
14
+ function writeJSON(path, data) {
15
+ path = resolve(path.toString());
16
+ mkdirSync(dirname(path), { recursive: true });
17
+ let dataString = JSON.stringify(data, null, 2);
18
+ return writeFileSync(path, dataString);
19
+ }
20
+ function cleanJson(content) {
21
+ if (typeof content !== "string") {
22
+ throw new TypeError(`the content must be a string`);
23
+ }
24
+ let isInsideComment = false;
25
+ let isInsideString = false;
26
+ let offset = 0;
27
+ let result = "";
28
+ for (let index = 0; index < content.length; index++) {
29
+ let currentCharacter = content[index], chars = currentCharacter + content[index + 1];
30
+ if (isInsideComment) {
31
+ if (isInsideComment === "single" && chars === "\r\n" || isInsideComment === "multi" && chars === "*/") {
32
+ isInsideComment = false;
33
+ index++;
34
+ offset = ++index;
35
+ } else if (isInsideComment === "single" && ["\r", "\n"].includes(currentCharacter)) {
36
+ isInsideComment = false;
37
+ offset = ++index;
38
+ }
39
+ } else {
40
+ if (currentCharacter === '"' && !isEscaped(content, index)) {
41
+ isInsideString = !isInsideString;
42
+ }
43
+ if (isInsideString) continue;
44
+ else if (["//", "/*"].includes(chars)) {
45
+ isInsideComment = chars === "//" ? "single" : "multi";
46
+ result += content.slice(offset, index);
47
+ index++;
48
+ }
49
+ }
50
+ }
51
+ return result + content.slice(offset);
52
+ }
53
+ function isEscaped(content, charPosition) {
54
+ let index = charPosition - 1;
55
+ let backslashCount = 0;
56
+ while (content[index] === "\\") {
57
+ index--;
58
+ backslashCount++;
59
+ }
60
+ return Boolean(backslashCount % 2);
61
+ }
62
+ function getEntriesSync(dir = ".", filter, depth, skip) {
63
+ if (Array.isArray(dir)) {
64
+ return dir.flatMap((el) => getEntriesSync(el, filter, depth));
65
+ }
66
+ dir = resolve(dir);
67
+ let filterFunction = filter === "files" ? (entry) => lstatSync(entry).isFile() : filter === "dirs" ? (entry) => lstatSync(entry).isDirectory() : filter instanceof RegExp ? (entry) => filter.test(entry) : typeof filter === "function" ? filter : void 0;
68
+ let skipFunction = skip instanceof RegExp ? (entry) => skip.test(entry) : typeof skip === "function" ? skip : (entry) => ["node_modules", "dist"].includes(entry);
69
+ let entries = readdirSync(dir);
70
+ let result = [];
71
+ for (let entry of entries) {
72
+ let fullPath = join(dir, entry);
73
+ if (!filterFunction || filterFunction?.(fullPath)) {
74
+ result.push(fullPath);
75
+ }
76
+ if ((depth === void 0 || depth > 0) && lstatSync(fullPath).isDirectory() && !skipFunction?.(entry)) {
77
+ let subEntries = getEntriesSync(
78
+ fullPath,
79
+ filterFunction,
80
+ depth === void 0 ? void 0 : depth - 1
81
+ );
82
+ result = [...result, ...subEntries];
83
+ }
84
+ }
85
+ return result;
86
+ }
87
+ export {
88
+ cleanJson,
89
+ getEntriesSync,
90
+ readJSON,
91
+ writeJSON
92
+ };
@@ -0,0 +1,2 @@
1
+ import { IBuilderOptions } from '../types/builder-options.js';
2
+ export declare function getTsConfigFile(options: IBuilderOptions): string;
@@ -0,0 +1,12 @@
1
+ import { existsSync } from "node:fs";
2
+ function getTsConfigFile(options) {
3
+ if (options.tsConfig) return options.tsConfig;
4
+ let files = ["tsconfig.lib.json", "tsconfig.app.json", "tsconfig.json"];
5
+ for (let file of files) {
6
+ if (existsSync(`${options.projectPath}/${file}`)) return file;
7
+ }
8
+ throw new Error(`No tsconfig file is found in the project`);
9
+ }
10
+ export {
11
+ getTsConfigFile
12
+ };