From d42ba4ba5fcb57b43f87b03a3edd56c2143b91c2 Mon Sep 17 00:00:00 2001 From: Dom Talbot <3301714+domjtalbot@users.noreply.github.com> Date: Tue, 31 Jan 2023 19:57:15 +0000 Subject: [PATCH] feat(nx-mesh): add build watcher (#124) --- .changeset/smooth-grapes-lie.md | 5 + README.md | 2 + .../sdk/graphql/star-wars-api/project.json | 7 + .../build-gateway/build-gateway.impl.ts | 2 + .../src/executors/build-swc/build-swc.impl.ts | 124 ++++++++++-------- .../src/executors/build-swc/schema.json | 2 +- .../nx-mesh/src/executors/build/build.impl.ts | 120 +++++++++-------- libs/nx-mesh/src/executors/build/schema.json | 2 +- libs/nx-mesh/src/utils/index.ts | 1 + libs/nx-mesh/src/utils/watcher/index.ts | 1 + libs/nx-mesh/src/utils/watcher/watcher.ts | 47 +++++++ package.json | 4 +- pnpm-lock.yaml | 50 ++++--- 13 files changed, 235 insertions(+), 132 deletions(-) create mode 100644 .changeset/smooth-grapes-lie.md create mode 100644 libs/nx-mesh/src/utils/watcher/index.ts create mode 100644 libs/nx-mesh/src/utils/watcher/watcher.ts diff --git a/.changeset/smooth-grapes-lie.md b/.changeset/smooth-grapes-lie.md new file mode 100644 index 00000000..f068a55c --- /dev/null +++ b/.changeset/smooth-grapes-lie.md @@ -0,0 +1,5 @@ +--- +'nx-mesh': minor +--- + +Add build watcher diff --git a/README.md b/README.md index 031d0d94..27fa0a72 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ This is the equivalent of using `graphql-mesh dev`, but with extra steps for pac | `transformers` | `string[]` | - | - | List of TypeScript Transformer Plugins. | | `tsConfig` | `string` | ✅ | - | The path to the Typescript configuration file. | | `updateBuildableProjectDepsInPackageJson` | `boolean` | - | `true` | Whether to update the buildable project dependencies in `package.json`. | +| `watch` | `boolean` | - | `false` | Rebuild upon file changes. | @@ -391,6 +392,7 @@ This is the equivalent of using `graphql-mesh build`, but with extra steps for p | `transformers` | `string[]` | - | - | List of TypeScript Transformer Plugins. | | `tsConfig` | `string` | ✅ | - | The path to the Typescript configuration file. | | `updateBuildableProjectDepsInPackageJson` | `boolean` | - | `true` | Whether to update the buildable project dependencies in `package.json`. | +| `watch` | `boolean` | - | `false` | Rebuild upon file changes. | diff --git a/libs/example/sdk/graphql/star-wars-api/project.json b/libs/example/sdk/graphql/star-wars-api/project.json index 98d26c26..e8ff9d2b 100644 --- a/libs/example/sdk/graphql/star-wars-api/project.json +++ b/libs/example/sdk/graphql/star-wars-api/project.json @@ -37,6 +37,13 @@ } } }, + "dev": { + "executor": "nx-mesh:serve", + "options": { + "dev": true, + "dir": "libs/example/sdk/graphql/star-wars-api" + } + }, "serve": { "executor": "nx-mesh:serve", "options": { diff --git a/libs/nx-mesh/src/executors/build-gateway/build-gateway.impl.ts b/libs/nx-mesh/src/executors/build-gateway/build-gateway.impl.ts index bff4bd02..ba1b90f9 100644 --- a/libs/nx-mesh/src/executors/build-gateway/build-gateway.impl.ts +++ b/libs/nx-mesh/src/executors/build-gateway/build-gateway.impl.ts @@ -52,6 +52,8 @@ export default async function* buildExecutor( context ); + logger.info('Done.'); + yield { success: true, }; diff --git a/libs/nx-mesh/src/executors/build-swc/build-swc.impl.ts b/libs/nx-mesh/src/executors/build-swc/build-swc.impl.ts index 3a351d18..1b1a9c05 100644 --- a/libs/nx-mesh/src/executors/build-swc/build-swc.impl.ts +++ b/libs/nx-mesh/src/executors/build-swc/build-swc.impl.ts @@ -3,7 +3,7 @@ import type { ExecutorContext } from '@nrwl/devkit'; import { logger } from '@nrwl/devkit'; import { resolve } from 'path'; -import { createPackageJson } from '../../utils'; +import { createPackageJson, watcher } from '../../utils'; import { runCodegenCli } from '../../utils/graphql-codegen-cli'; import { runMeshCli } from '../../utils/mesh-cli'; import { swcExecutor } from './swc-executor/swc.impl'; @@ -20,69 +20,79 @@ export default async function* buildExecutor( const dir = resolve(context.root, options.dir); let success = false; - await runMeshCli( - 'build', - { - args: { - dir, - require: options.require, - }, - env: { - debug: options.debug, - }, - }, - context - ); + await watcher( + async () => { + await runMeshCli( + 'build', + { + args: { + dir, + require: options.require, + }, + env: { + debug: options.debug, + }, + }, + context + ); - if (options.codegen?.config) { - logger.info(''); - logger.info('Running GraphQL Codegen...'); + if (options.codegen?.config) { + logger.info(''); + logger.info('Running GraphQL Codegen...'); - await runCodegenCli( - { - ...options.codegen, - debug: options.debug, - verbose: true, - watch: options.watch, - }, - context - ); - } + await runCodegenCli( + { + ...options.codegen, + debug: options.debug, + verbose: true, + watch: false, + }, + context + ); + } - logger.info(''); - logger.info('Running SWC compiler...'); + logger.info(''); + logger.info('Running SWC compiler...'); - const tsc = swcExecutor( - { - assets: [...options.assets], - main: options.main, - outputPath: options.outputPath, - skipTypeCheck: options.skipTypeCheck, - swcrc: options.swcrc, - transformers: [], - tsConfig: options.tsConfig, - watch: false, - }, - context - ); + const tsc = swcExecutor( + { + assets: [...options.assets], + main: options.main, + outputPath: options.outputPath, + skipTypeCheck: options.skipTypeCheck, + swcrc: options.swcrc, + transformers: [], + tsConfig: options.tsConfig, + watch: false, + }, + context + ); - for await (const result of tsc) { - success = result.success; - } + for await (const result of tsc) { + success = result.success; + } - if (success) { - logger.info(''); - logger.info('Creating package.json...'); + if (success) { + logger.info(''); + logger.info('Creating package.json...'); - await createPackageJson( - { - dir: options.dir, - outputPath: options.outputPath, - projectRoot: options.outputPath, - }, - context - ); - } + await createPackageJson( + { + dir, + outputPath: options.outputPath, + projectRoot: options.outputPath, + }, + context + ); + + logger.info('Done.'); + } + }, + { + dir, + watch: options.watch, + } + ); yield { success, diff --git a/libs/nx-mesh/src/executors/build-swc/schema.json b/libs/nx-mesh/src/executors/build-swc/schema.json index ea49c6b0..8a0389ba 100644 --- a/libs/nx-mesh/src/executors/build-swc/schema.json +++ b/libs/nx-mesh/src/executors/build-swc/schema.json @@ -34,7 +34,7 @@ }, "watch": { "type": "boolean", - "description": "Enable re-building when files change.", + "description": "Rebuild upon file changes.", "default": false }, "transformers": { diff --git a/libs/nx-mesh/src/executors/build/build.impl.ts b/libs/nx-mesh/src/executors/build/build.impl.ts index 2645677f..061a239b 100644 --- a/libs/nx-mesh/src/executors/build/build.impl.ts +++ b/libs/nx-mesh/src/executors/build/build.impl.ts @@ -4,7 +4,7 @@ import { logger } from '@nrwl/devkit'; import { tscExecutor } from '@nrwl/js/src/executors/tsc/tsc.impl'; import { resolve } from 'path'; -import { createPackageJson } from '../../utils'; +import { createPackageJson, watcher } from '../../utils'; import { runCodegenCli } from '../../utils/graphql-codegen-cli'; import { runMeshCli } from '../../utils/mesh-cli'; @@ -21,67 +21,77 @@ export default async function* buildExecutor( const dir = resolve(context.root, options.dir); let success = false; - await runMeshCli( - 'build', - { - args: { - dir, - require: options.require, - }, - env: { - debug: options.debug, - }, - }, - context - ); + await watcher( + async () => { + await runMeshCli( + 'build', + { + args: { + dir, + require: options.require, + }, + env: { + debug: options.debug, + }, + }, + context + ); - if (options.codegen?.config) { - logger.info(''); - logger.info('Running GraphQL Codegen...'); + if (options.codegen?.config) { + logger.info(''); + logger.info('Running GraphQL Codegen...'); - await runCodegenCli( - { - ...options.codegen, - debug: options.debug, - verbose: true, - watch: options.watch, - }, - context - ); - } + await runCodegenCli( + { + ...options.codegen, + debug: options.debug, + verbose: true, + watch: options.watch, + }, + context + ); + } - logger.info(''); - logger.info('Running Typescript compiler...'); + logger.info(''); + logger.info('Running Typescript compiler...'); - const tsc = tscExecutor( - { - assets: [...options.assets], - main: options.main, - outputPath: options.outputPath, - transformers: [], - tsConfig: options.tsConfig, - watch: false, - }, - context - ); + const tsc = tscExecutor( + { + assets: [...options.assets], + main: options.main, + outputPath: options.outputPath, + transformers: [], + tsConfig: options.tsConfig, + watch: false, + }, + context + ); - for await (const result of tsc) { - success = result.success; - } + for await (const result of tsc) { + success = result.success; + } - if (success) { - logger.info(''); - logger.info('Creating package.json...'); + if (success) { + logger.info(''); + logger.info('Creating package.json...'); - await createPackageJson( - { - dir: options.dir, - outputPath: options.outputPath, - projectRoot: options.outputPath, - }, - context - ); - } + await createPackageJson( + { + dir, + outputPath: options.outputPath, + projectRoot: options.outputPath, + }, + context + ); + + logger.info('Done.'); + } + }, + { + dir, + watch: options.watch, + } + ); yield { success, diff --git a/libs/nx-mesh/src/executors/build/schema.json b/libs/nx-mesh/src/executors/build/schema.json index 48c94e39..2b417230 100644 --- a/libs/nx-mesh/src/executors/build/schema.json +++ b/libs/nx-mesh/src/executors/build/schema.json @@ -25,7 +25,7 @@ }, "watch": { "type": "boolean", - "description": "Enable re-building when files change.", + "description": "Rebuild upon file changes.", "default": false }, "transformers": { diff --git a/libs/nx-mesh/src/utils/index.ts b/libs/nx-mesh/src/utils/index.ts index be1067fa..2ed656da 100644 --- a/libs/nx-mesh/src/utils/index.ts +++ b/libs/nx-mesh/src/utils/index.ts @@ -4,3 +4,4 @@ export * from './get-package-versions'; export * from './get-source-file'; export * from './get-wildcard-packages'; export * from './mesh-packages'; +export * from './watcher'; diff --git a/libs/nx-mesh/src/utils/watcher/index.ts b/libs/nx-mesh/src/utils/watcher/index.ts new file mode 100644 index 00000000..ed6bf4cd --- /dev/null +++ b/libs/nx-mesh/src/utils/watcher/index.ts @@ -0,0 +1 @@ +export * from './watcher'; diff --git a/libs/nx-mesh/src/utils/watcher/watcher.ts b/libs/nx-mesh/src/utils/watcher/watcher.ts new file mode 100644 index 00000000..b808dc5e --- /dev/null +++ b/libs/nx-mesh/src/utils/watcher/watcher.ts @@ -0,0 +1,47 @@ +import type { WatcherOptions } from 'watchpack'; + +import { logger } from '@nrwl/devkit'; +import Watchpack = require('watchpack'); + +export type WatchFunc = () => Promise; + +export type Options = Omit & { + dir: string; + watch?: boolean; +}; + +export async function watcher(func: WatchFunc, options: Options) { + const { dir, watch, ...watcherOptions } = options; + + if (watch === true) { + const wp = new Watchpack({ + ...watcherOptions, + ignored: [ + '.eslintrc.json', + '.lib.swcrc', + '**/.codegen', + '**/.git', + '**/.mesh', + '**/node_modules', + 'jest.config.ts', + 'project.json', + ], + }); + + wp.watch({ directories: [dir], startTime: 0 }); + + wp.on('aggregated', async () => { + await func(); + + logger.info(''); + logger.info(''); + logger.info('Watching for changes...'); + }); + + await new Promise<{ success: boolean }>(() => { + // This Promise intentionally never resolves, leaving the process running. + }); + } else { + await func(); + } +} diff --git a/package.json b/package.json index 87814449..0aded86d 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@nrwl/nx-plugin": "15.4.1", "@swc-node/register": "1.5.1", "@swc/helpers": "0.4.11", + "@types/watchpack": "^2.4.0", "core-js": "^3.6.5", "encoding": "0.1.13", "get-port": "5.1.1", @@ -64,7 +65,8 @@ "regenerator-runtime": "0.13.7", "styled-components": "5.3.6", "tslib": "2.4.0", - "type-fest": "2.18.0" + "type-fest": "2.18.0", + "watchpack": "^2.4.0" }, "devDependencies": { "@nrwl/cli": "15.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62bdbfc0..efbba110 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,7 @@ importers: '@types/react-dom': 18.0.9 '@types/react-is': 17.0.3 '@types/styled-components': 5.1.26 + '@types/watchpack': ^2.4.0 '@typescript-eslint/eslint-plugin': 5.42.1 '@typescript-eslint/parser': 5.42.1 babel-jest: 28.1.3 @@ -93,6 +94,7 @@ importers: tslib: 2.4.0 type-fest: 2.18.0 typescript: 4.8.4 + watchpack: ^2.4.0 dependencies: '@changesets/changelog-github': 0.4.8_encoding@0.1.13 '@changesets/cli': 2.26.0 @@ -126,6 +128,7 @@ importers: '@nrwl/nx-plugin': 15.4.1_scu4s6lne6ql7roxoe5yixihr4 '@swc-node/register': 1.5.1_typescript@4.8.4 '@swc/helpers': 0.4.11 + '@types/watchpack': 2.4.0 core-js: 3.22.8 encoding: 0.1.13 get-port: 5.1.1 @@ -139,6 +142,7 @@ importers: styled-components: 5.3.6_7i5myeigehqah43i5u7wbekgba tslib: 2.4.0 type-fest: 2.18.0 + watchpack: 2.4.0 devDependencies: '@nrwl/cli': 15.4.1_zhrgkbtg447ekvrhgcqluapfue '@nrwl/cypress': 15.4.1_byanviwxe3ttxbz3vvqjrqhmpi @@ -8399,6 +8403,16 @@ packages: } dev: false + /@types/watchpack/2.4.0: + resolution: + { + integrity: sha512-PSAD+o9hezvfUFFzrYB/PO6Je7kwiZ2BSnB3/EZ9le+jTDKB6x5NJ96WWzQz1h/AyGJ/de3/1KpuBTkUFZm77A==, + } + dependencies: + '@types/graceful-fs': 4.1.5 + '@types/node': 18.11.17 + dev: false + /@types/ws/8.5.3: resolution: { @@ -9548,7 +9562,7 @@ packages: loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /babel-plugin-const-enum/1.2.0_@babel+core@7.20.7: resolution: @@ -10755,7 +10769,7 @@ packages: normalize-path: 3.0.0 schema-utils: 4.0.0 serialize-javascript: 6.0.0 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /core-js-compat/3.26.1: resolution: @@ -10963,7 +10977,7 @@ packages: postcss-modules-values: 4.0.0_postcss@8.4.20 postcss-value-parser: 4.2.0 semver: 7.3.8 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /css-minimizer-webpack-plugin/3.4.1_webpack@5.75.0: resolution: @@ -10993,7 +11007,7 @@ packages: schema-utils: 4.0.0 serialize-javascript: 6.0.0 source-map: 0.6.1 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /css-select/4.3.0: resolution: @@ -12915,7 +12929,7 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 3.1.1 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /file-uri-to-path/2.0.0: resolution: @@ -16135,7 +16149,7 @@ packages: dependencies: klona: 2.0.5 less: 3.12.2 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /less/3.12.2: resolution: @@ -16196,7 +16210,7 @@ packages: webpack-sources: optional: true dependencies: - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 webpack-sources: 3.2.3 /lie/3.1.1: @@ -16867,7 +16881,7 @@ packages: webpack: ^5.0.0 dependencies: schema-utils: 4.0.0 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /mini-svg-data-uri/1.4.4: resolution: @@ -18308,7 +18322,7 @@ packages: klona: 2.0.5 postcss: 8.4.20 semver: 7.3.8 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /postcss-merge-longhand/5.1.7_postcss@8.4.20: resolution: @@ -18951,7 +18965,7 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 3.1.1 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /react-dom/18.2.0_react@18.2.0: resolution: @@ -19579,7 +19593,7 @@ packages: klona: 2.0.5 neo-async: 2.6.2 sass: 1.57.1 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /sass/1.57.1: resolution: @@ -20011,7 +20025,7 @@ packages: abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.0.2 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /source-map-resolve/0.6.0: resolution: @@ -20450,7 +20464,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /styled-components/5.3.6_7i5myeigehqah43i5u7wbekgba: resolution: @@ -20525,7 +20539,7 @@ packages: klona: 2.0.5 normalize-path: 3.0.0 stylus: 0.55.0 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /stylus/0.55.0: resolution: @@ -20733,6 +20747,7 @@ packages: serialize-javascript: 6.0.0 terser: 5.16.1 webpack: 5.75.0 + dev: false /terser/5.16.1: resolution: @@ -21729,7 +21744,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /webpack-dev-server/4.11.1_webpack@5.75.0: resolution: @@ -21772,7 +21787,7 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 webpack-dev-middleware: 5.3.3_webpack@5.75.0 ws: 8.11.0 transitivePeerDependencies: @@ -21819,7 +21834,7 @@ packages: optional: true dependencies: typed-assert: 1.0.9 - webpack: 5.75.0 + webpack: 5.75.0_@swc+core@1.2.173 /webpack/5.75.0: resolution: @@ -21862,6 +21877,7 @@ packages: - '@swc/core' - esbuild - uglify-js + dev: false /webpack/5.75.0_@swc+core@1.2.173: resolution: