@dudousxd/nestjs-codegen 0.27.2 → 0.29.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @dudousxd/nestjs-codegen
2
2
 
3
+ ## 0.29.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#113](https://github.com/DavideCarvalho/nestjs-codegen/pull/113) [`e01376c`](https://github.com/DavideCarvalho/nestjs-codegen/commit/e01376cce17518b32f105526c0b7627f01738921) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `genuiSandboxKit()` takes the same options as the agent's Vite plugin: `components` (files, globs, directories or packages), `styles` (the app's style entries: the model is told the Tailwind tokens or the CSS class vocabulary, and the theme's variables), `wrapper`, `stylesDocs`, `tsconfig` and `output`. The stylesheets and what they `@import` are tracked as inputs.
8
+
9
+ **Breaking:** `include` and `entry` are now `components`, and `css` is now `styles`. Passing the old options throws a `CodegenError` naming the replacement. It needs `@dudousxd/nestjs-agent-core` >= 0.53.0.
10
+
11
+ ## 0.28.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#111](https://github.com/DavideCarvalho/nestjs-codegen/pull/111) [`26529df`](https://github.com/DavideCarvalho/nestjs-codegen/commit/26529df5f6fd1f91148217f9149da41de49fc8ca) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - New built-in `genuiSandboxKit()` extension: writes the generative-UI sandbox-kit docs (`.genui/sandbox-kit.json`, the component names and props of your shadcn `components/ui` read from their TypeScript types) for `@dudousxd/nestjs-agent`, on every generate and in watch mode. It calls `writeSandboxKitDocs` from the optional peer `@dudousxd/nestjs-agent-core/genui/kit` (>=0.52.0), or a `generate` function you pass.
16
+
17
+ Watch mode now also watches the files extensions declare with `ctx.trackInput` (so editing an extension's out-of-glob dependency, such as a filter class or a kit component, regenerates without waiting for an unrelated change), and a tracked directory hashes as its file listing, so adding a file to it invalidates the skip-when-unchanged check.
18
+
3
19
  ## 0.27.2
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -116,6 +116,31 @@ const create = useMutation(api.users.create().mutationOptions());
116
116
  | [`@dudousxd/nestjs-filter-codegen`](https://www.npmjs.com/package/@dudousxd/nestjs-filter-codegen) | Extension — typed `filterQuery` helpers from the [nestjs-filter](https://github.com/DavideCarvalho/nestjs-filter) repo. |
117
117
  | [`@dudousxd/nestjs-inertia-codegen-extension`](https://www.npmjs.com/package/@dudousxd/nestjs-inertia-codegen-extension) | Extension — Inertia `router` / navigate output from the nestjs-inertia repo. |
118
118
 
119
+ ### Generative-UI sandbox kit (built in)
120
+
121
+ `genuiSandboxKit()` writes the sandbox-kit docs that
122
+ [`@dudousxd/nestjs-agent`](https://github.com/DavideCarvalho/nestjs-agent) uses to render
123
+ generative UI in your design system. The names and props of your components (read from their
124
+ TypeScript types), and what your styles offer, go to `.genui/sandbox-kit.json`. That happens on
125
+ every generate and in watch mode. It needs the optional peer `@dudousxd/nestjs-agent-core`
126
+ (>=0.53.0).
127
+
128
+ ```ts
129
+ import { defineConfig, genuiSandboxKit } from '@dudousxd/nestjs-codegen';
130
+
131
+ export default defineConfig({
132
+ contracts: { glob: 'src/**/*.controller.ts' },
133
+ codegen: { outDir: 'src/generated' },
134
+ extensions: [genuiSandboxKit({ components: 'web/components/ui', styles: 'web/styles/globals.css' })],
135
+ });
136
+ ```
137
+
138
+ Options (the same as the agent's Vite plugin):
139
+
140
+ - `components` (default: the first shadcn `components/ui` dir found), `styles`, `wrapper`, `stylesDocs`, `tsconfig`;
141
+ - `output` (default `.genui/sandbox-kit.json`, relative to the app cwd);
142
+ - `generate`, to inject the docs writer.
143
+
119
144
  Write your own against the `@dudousxd/nestjs-codegen/extension` contract:
120
145
 
121
146
  ```ts
package/dist/cli/main.cjs CHANGED
@@ -5663,14 +5663,26 @@ async function computeInputsHash(config, extraInputs = []) {
5663
5663
  hash.update("\n");
5664
5664
  }
5665
5665
  for (const file of extra) {
5666
- const contents = await (0, import_promises11.readFile)((0, import_node_path15.join)(cwd, file), "utf8").catch(() => null);
5667
5666
  hash.update(`extra:${file}
5668
5667
  `);
5669
- hash.update(contents ?? "\0missing");
5668
+ hash.update(await readTrackedInput((0, import_node_path15.join)(cwd, file)));
5670
5669
  hash.update("\n");
5671
5670
  }
5672
5671
  return hash.digest("hex");
5673
5672
  }
5673
+ async function readTrackedInput(path) {
5674
+ try {
5675
+ return await (0, import_promises11.readFile)(path, "utf8");
5676
+ } catch (err) {
5677
+ if (err.code !== "EISDIR") return "\0missing";
5678
+ }
5679
+ try {
5680
+ const entries = await (0, import_promises11.readdir)(path);
5681
+ return `\0dir:${entries.sort().join("\n")}`;
5682
+ } catch {
5683
+ return "\0missing";
5684
+ }
5685
+ }
5674
5686
  async function readManifest(outDir) {
5675
5687
  try {
5676
5688
  const raw = await (0, import_promises11.readFile)((0, import_node_path15.join)(outDir, MANIFEST_FILE), "utf8");
@@ -5908,11 +5920,36 @@ async function watch(config, onChange, options = {}) {
5908
5920
  }
5909
5921
  return discovery;
5910
5922
  }
5923
+ const trackedWatcher = import_chokidar.default.watch([], {
5924
+ ignoreInitial: true,
5925
+ persistent: true,
5926
+ awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5927
+ });
5928
+ const trackedPaths = /* @__PURE__ */ new Set();
5929
+ async function syncTrackedInputs() {
5930
+ const manifest = await readManifest(config.codegen.outDir);
5931
+ const fresh = [];
5932
+ for (const path of manifest?.extraInputs ?? []) {
5933
+ const abs = (0, import_node_path18.resolve)(config.codegen.cwd, path);
5934
+ if (trackedPaths.has(abs)) continue;
5935
+ trackedPaths.add(abs);
5936
+ fresh.push(abs);
5937
+ }
5938
+ if (fresh.length > 0) trackedWatcher.add(fresh);
5939
+ }
5940
+ async function generateAndTrack(routes) {
5941
+ try {
5942
+ await generate(config, routes, entryPoint);
5943
+ } finally {
5944
+ await syncTrackedInputs().catch(() => {
5945
+ });
5946
+ }
5947
+ }
5911
5948
  async function runInitialPass() {
5912
5949
  try {
5913
5950
  const initialRoutes = (await getDiscovery()).discover();
5914
5951
  lastRoutes = initialRoutes;
5915
- await generate(config, initialRoutes, entryPoint);
5952
+ await generateAndTrack(initialRoutes);
5916
5953
  } catch (err) {
5917
5954
  if (err instanceof DriftGuardError) {
5918
5955
  console.error(err.message);
@@ -5922,7 +5959,7 @@ async function watch(config, onChange, options = {}) {
5922
5959
  `[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
5923
5960
  );
5924
5961
  try {
5925
- await generate(config, lastRoutes, entryPoint);
5962
+ await generateAndTrack(lastRoutes);
5926
5963
  } catch {
5927
5964
  }
5928
5965
  }
@@ -5955,7 +5992,7 @@ async function watch(config, onChange, options = {}) {
5955
5992
  pagesDebounceTimer = setTimeout(async () => {
5956
5993
  pagesDebounceTimer = void 0;
5957
5994
  try {
5958
- await generate(config, lastRoutes, entryPoint);
5995
+ await generateAndTrack(lastRoutes);
5959
5996
  } catch (err) {
5960
5997
  console.error(
5961
5998
  "[nestjs-codegen] Pages generation failed:",
@@ -5987,7 +6024,7 @@ async function watch(config, onChange, options = {}) {
5987
6024
  try {
5988
6025
  const routes = await (await getDiscovery()).rediscover(changed);
5989
6026
  lastRoutes = routes;
5990
- await generate(config, routes, entryPoint);
6027
+ await generateAndTrack(routes);
5991
6028
  } catch (err) {
5992
6029
  console.error(
5993
6030
  "[nestjs-codegen] Contracts generation failed:",
@@ -6008,6 +6045,9 @@ async function watch(config, onChange, options = {}) {
6008
6045
  formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6009
6046
  formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
6010
6047
  formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6048
+ trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6049
+ trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
6050
+ trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6011
6051
  return {
6012
6052
  close: async () => {
6013
6053
  if (pagesDebounceTimer !== void 0) {
@@ -6021,13 +6061,14 @@ async function watch(config, onChange, options = {}) {
6021
6061
  await pagesWatcher.close();
6022
6062
  await contractsWatcher.close();
6023
6063
  await formsWatcher.close();
6064
+ await trackedWatcher.close();
6024
6065
  await lock.release();
6025
6066
  }
6026
6067
  };
6027
6068
  }
6028
6069
 
6029
6070
  // src/index.ts
6030
- var VERSION = "0.27.2";
6071
+ var VERSION = "0.29.0";
6031
6072
 
6032
6073
  // src/cli/codegen.ts
6033
6074
  async function runCodegen(opts = {}) {
@@ -6035,9 +6076,9 @@ async function runCodegen(opts = {}) {
6035
6076
  const config = await loadConfig(cwd);
6036
6077
  if (opts.watch) {
6037
6078
  const watcher = await watch(config, void 0, { entryPoint: "cli" });
6038
- await new Promise((resolve5) => {
6079
+ await new Promise((resolve6) => {
6039
6080
  function onSignal() {
6040
- watcher.close().then(resolve5).catch(resolve5);
6081
+ watcher.close().then(resolve6).catch(resolve6);
6041
6082
  }
6042
6083
  process.once("SIGINT", onSignal);
6043
6084
  process.once("SIGTERM", onSignal);
@@ -6167,16 +6208,16 @@ async function detectPackageManager(cwd) {
6167
6208
  }
6168
6209
  async function promptFramework() {
6169
6210
  if (!process.stdin.isTTY) return "react";
6170
- return new Promise((resolve5) => {
6211
+ return new Promise((resolve6) => {
6171
6212
  const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
6172
6213
  rl.question(
6173
6214
  "[nestjs-codegen] Which frontend framework? (react/vue/svelte) [react]: ",
6174
6215
  (answer) => {
6175
6216
  rl.close();
6176
6217
  const trimmed = answer.trim().toLowerCase();
6177
- if (trimmed === "vue") resolve5("vue");
6178
- else if (trimmed === "svelte") resolve5("svelte");
6179
- else resolve5("react");
6218
+ if (trimmed === "vue") resolve6("vue");
6219
+ else if (trimmed === "svelte") resolve6("svelte");
6220
+ else resolve6("react");
6180
6221
  }
6181
6222
  );
6182
6223
  });