@depup/nestjs__cli 11.0.24-depup.7 → 12.0.0-depup.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 (220) hide show
  1. package/README.md +7 -15
  2. package/actions/abstract.action.d.ts +1 -2
  3. package/actions/abstract.action.js +1 -5
  4. package/actions/add.action.d.ts +3 -4
  5. package/actions/add.action.js +44 -53
  6. package/actions/build.action.d.ts +18 -11
  7. package/actions/build.action.js +172 -79
  8. package/actions/deploy.action.d.ts +6 -0
  9. package/actions/deploy.action.js +51 -0
  10. package/actions/generate.action.d.ts +3 -3
  11. package/actions/generate.action.js +61 -59
  12. package/actions/index.d.ts +9 -7
  13. package/actions/index.js +9 -23
  14. package/actions/info.action.d.ts +18 -28
  15. package/actions/info.action.js +80 -60
  16. package/actions/new.action.d.ts +3 -3
  17. package/actions/new.action.js +108 -104
  18. package/actions/start.action.d.ts +4 -7
  19. package/actions/start.action.js +40 -47
  20. package/actions/upgrade.action.d.ts +6 -0
  21. package/actions/upgrade.action.js +53 -0
  22. package/bin/nest.js +11 -10
  23. package/changes.json +9 -41
  24. package/commands/abstract.command.d.ts +3 -3
  25. package/commands/abstract.command.js +2 -5
  26. package/commands/add.command.d.ts +3 -3
  27. package/commands/add.command.js +17 -20
  28. package/commands/build.command.d.ts +3 -3
  29. package/commands/build.command.js +52 -56
  30. package/commands/command.input.js +1 -2
  31. package/commands/command.loader.d.ts +2 -2
  32. package/commands/command.loader.js +29 -22
  33. package/commands/context/add.context.d.ts +7 -0
  34. package/commands/context/add.context.js +1 -0
  35. package/commands/context/build.context.d.ts +17 -0
  36. package/commands/context/build.context.js +1 -0
  37. package/commands/context/deploy.context.d.ts +7 -0
  38. package/commands/context/deploy.context.js +1 -0
  39. package/commands/context/generate.context.d.ts +18 -0
  40. package/commands/context/generate.context.js +1 -0
  41. package/commands/context/index.d.ts +7 -0
  42. package/commands/context/index.js +7 -0
  43. package/commands/context/new.context.d.ts +19 -0
  44. package/commands/context/new.context.js +1 -0
  45. package/commands/context/start.context.d.ts +22 -0
  46. package/commands/context/start.context.js +1 -0
  47. package/commands/context/upgrade.context.d.ts +11 -0
  48. package/commands/context/upgrade.context.js +1 -0
  49. package/commands/deploy.command.d.ts +5 -0
  50. package/commands/deploy.command.js +22 -0
  51. package/commands/generate.command.d.ts +3 -3
  52. package/commands/generate.command.js +35 -66
  53. package/commands/index.d.ts +3 -2
  54. package/commands/index.js +3 -18
  55. package/commands/info.command.d.ts +3 -3
  56. package/commands/info.command.js +4 -8
  57. package/commands/new.command.d.ts +3 -3
  58. package/commands/new.command.js +34 -36
  59. package/commands/start.command.d.ts +3 -3
  60. package/commands/start.command.js +48 -73
  61. package/commands/upgrade.command.d.ts +5 -0
  62. package/commands/upgrade.command.js +32 -0
  63. package/lib/compiler/assets-manager.d.ts +20 -8
  64. package/lib/compiler/assets-manager.js +199 -63
  65. package/lib/compiler/base-compiler.d.ts +5 -5
  66. package/lib/compiler/base-compiler.js +10 -13
  67. package/lib/compiler/compiler.d.ts +6 -6
  68. package/lib/compiler/compiler.js +7 -9
  69. package/lib/compiler/defaults/rspack-defaults.d.ts +3 -0
  70. package/lib/compiler/defaults/rspack-defaults.js +179 -0
  71. package/lib/compiler/defaults/swc-defaults.d.ts +5 -2
  72. package/lib/compiler/defaults/swc-defaults.js +22 -6
  73. package/lib/compiler/defaults/webpack-defaults.d.ts +2 -2
  74. package/lib/compiler/defaults/webpack-defaults.js +59 -19
  75. package/lib/compiler/helpers/append-extension.js +3 -6
  76. package/lib/compiler/helpers/copy-path-resolve.js +2 -5
  77. package/lib/compiler/helpers/delete-out-dir.d.ts +1 -1
  78. package/lib/compiler/helpers/delete-out-dir.js +45 -10
  79. package/lib/compiler/helpers/get-builder.d.ts +9 -7
  80. package/lib/compiler/helpers/get-builder.js +3 -6
  81. package/lib/compiler/helpers/get-effective-root-dir.d.ts +16 -0
  82. package/lib/compiler/helpers/get-effective-root-dir.js +56 -0
  83. package/lib/compiler/helpers/get-rspack-config-path.d.ts +10 -0
  84. package/lib/compiler/helpers/get-rspack-config-path.js +19 -0
  85. package/lib/compiler/helpers/get-tsc-config.path.d.ts +2 -3
  86. package/lib/compiler/helpers/get-tsc-config.path.js +6 -9
  87. package/lib/compiler/helpers/get-value-or-default.d.ts +2 -3
  88. package/lib/compiler/helpers/get-value-or-default.js +3 -8
  89. package/lib/compiler/helpers/get-webpack-config-path.d.ts +2 -3
  90. package/lib/compiler/helpers/get-webpack-config-path.js +4 -7
  91. package/lib/compiler/helpers/glob.d.ts +19 -0
  92. package/lib/compiler/helpers/glob.js +185 -0
  93. package/lib/compiler/helpers/manual-restart.js +4 -8
  94. package/lib/compiler/helpers/path-confinement.d.ts +33 -0
  95. package/lib/compiler/helpers/path-confinement.js +63 -0
  96. package/lib/compiler/helpers/tsconfig-provider.d.ts +7 -6
  97. package/lib/compiler/helpers/tsconfig-provider.js +32 -13
  98. package/lib/compiler/hooks/tsconfig-paths.hook.js +13 -13
  99. package/lib/compiler/interfaces/readonly-visitor.interface.js +1 -2
  100. package/lib/compiler/plugins/plugin-metadata-generator.d.ts +37 -1
  101. package/lib/compiler/plugins/plugin-metadata-generator.js +113 -13
  102. package/lib/compiler/plugins/plugin-metadata-printer.d.ts +1 -1
  103. package/lib/compiler/plugins/plugin-metadata-printer.js +5 -9
  104. package/lib/compiler/plugins/plugins-loader.d.ts +2 -2
  105. package/lib/compiler/plugins/plugins-loader.js +12 -12
  106. package/lib/compiler/rspack-compiler.d.ts +21 -0
  107. package/lib/compiler/rspack-compiler.js +111 -0
  108. package/lib/compiler/swc/constants.d.ts +0 -1
  109. package/lib/compiler/swc/constants.js +9 -13
  110. package/lib/compiler/swc/forked-type-checker.js +13 -18
  111. package/lib/compiler/swc/swc-compiler.d.ts +13 -6
  112. package/lib/compiler/swc/swc-compiler.js +116 -70
  113. package/lib/compiler/swc/type-checker-host.js +31 -24
  114. package/lib/compiler/typescript-loader.js +7 -9
  115. package/lib/compiler/watch-compiler.d.ts +5 -5
  116. package/lib/compiler/watch-compiler.js +13 -15
  117. package/lib/compiler/watchers/recursive-directory-watcher.d.ts +37 -0
  118. package/lib/compiler/watchers/recursive-directory-watcher.js +334 -0
  119. package/lib/compiler/webpack-compiler.d.ts +6 -7
  120. package/lib/compiler/webpack-compiler.js +46 -23
  121. package/lib/configuration/configuration.d.ts +16 -3
  122. package/lib/configuration/configuration.js +1 -2
  123. package/lib/configuration/configuration.loader.d.ts +1 -1
  124. package/lib/configuration/configuration.loader.js +1 -2
  125. package/lib/configuration/defaults.d.ts +2 -1
  126. package/lib/configuration/defaults.js +8 -10
  127. package/lib/configuration/index.d.ts +3 -3
  128. package/lib/configuration/index.js +3 -19
  129. package/lib/configuration/nest-configuration.loader.d.ts +3 -3
  130. package/lib/configuration/nest-configuration.loader.js +28 -29
  131. package/lib/package-managers/abstract.package-manager.d.ts +4 -4
  132. package/lib/package-managers/abstract.package-manager.js +21 -32
  133. package/lib/package-managers/bun.package-manager.d.ts +7 -0
  134. package/lib/package-managers/bun.package-manager.js +22 -0
  135. package/lib/package-managers/index.d.ts +9 -8
  136. package/lib/package-managers/index.js +9 -24
  137. package/lib/package-managers/npm.package-manager.d.ts +2 -2
  138. package/lib/package-managers/npm.package-manager.js +6 -10
  139. package/lib/package-managers/package-manager-commands.js +1 -2
  140. package/lib/package-managers/package-manager.d.ts +2 -1
  141. package/lib/package-managers/package-manager.factory.d.ts +2 -2
  142. package/lib/package-managers/package-manager.factory.js +23 -20
  143. package/lib/package-managers/package-manager.js +3 -5
  144. package/lib/package-managers/pnpm.package-manager.d.ts +2 -2
  145. package/lib/package-managers/pnpm.package-manager.js +6 -10
  146. package/lib/package-managers/project.dependency.js +1 -2
  147. package/lib/package-managers/yarn.package-manager.d.ts +2 -2
  148. package/lib/package-managers/yarn.package-manager.js +6 -10
  149. package/lib/questions/questions.d.ts +5 -1
  150. package/lib/questions/questions.js +2 -7
  151. package/lib/readers/file-system.reader.d.ts +1 -1
  152. package/lib/readers/file-system.reader.js +6 -9
  153. package/lib/readers/index.d.ts +2 -2
  154. package/lib/readers/index.js +2 -18
  155. package/lib/readers/reader.js +3 -5
  156. package/lib/runners/abstract.runner.js +40 -21
  157. package/lib/runners/bun.runner.d.ts +4 -0
  158. package/lib/runners/bun.runner.js +6 -0
  159. package/lib/runners/git.runner.d.ts +1 -1
  160. package/lib/runners/git.runner.js +2 -6
  161. package/lib/runners/index.d.ts +3 -3
  162. package/lib/runners/index.js +3 -19
  163. package/lib/runners/mau.runner.d.ts +8 -0
  164. package/lib/runners/mau.runner.js +10 -0
  165. package/lib/runners/npm.runner.d.ts +1 -1
  166. package/lib/runners/npm.runner.js +2 -6
  167. package/lib/runners/pnpm.runner.d.ts +1 -1
  168. package/lib/runners/pnpm.runner.js +2 -6
  169. package/lib/runners/runner.d.ts +2 -1
  170. package/lib/runners/runner.factory.d.ts +7 -6
  171. package/lib/runners/runner.factory.js +18 -20
  172. package/lib/runners/runner.js +3 -5
  173. package/lib/runners/schematic.runner.d.ts +1 -1
  174. package/lib/runners/schematic.runner.js +5 -7
  175. package/lib/runners/yarn.runner.d.ts +1 -1
  176. package/lib/runners/yarn.runner.js +2 -6
  177. package/lib/schematics/abstract.collection.d.ts +3 -3
  178. package/lib/schematics/abstract.collection.js +3 -5
  179. package/lib/schematics/collection.factory.d.ts +2 -2
  180. package/lib/schematics/collection.factory.js +9 -13
  181. package/lib/schematics/collection.js +2 -5
  182. package/lib/schematics/custom.collection.d.ts +2 -2
  183. package/lib/schematics/custom.collection.js +4 -8
  184. package/lib/schematics/index.d.ts +4 -4
  185. package/lib/schematics/index.js +4 -20
  186. package/lib/schematics/nest.collection.d.ts +3 -3
  187. package/lib/schematics/nest.collection.js +113 -109
  188. package/lib/schematics/schematic.option.js +10 -9
  189. package/lib/ui/banner.js +1 -4
  190. package/lib/ui/emojis.d.ts +17 -17
  191. package/lib/ui/emojis.js +19 -22
  192. package/lib/ui/errors.js +1 -4
  193. package/lib/ui/index.d.ts +5 -5
  194. package/lib/ui/index.js +5 -21
  195. package/lib/ui/messages.d.ts +8 -0
  196. package/lib/ui/messages.js +23 -18
  197. package/lib/ui/prefixes.js +3 -6
  198. package/lib/utils/extra-args-warning.js +3 -6
  199. package/lib/utils/formatting.js +1 -4
  200. package/lib/utils/get-default-tsconfig-path.js +4 -7
  201. package/lib/utils/gracefully-exit-on-prompt-error.js +1 -4
  202. package/lib/utils/is-esm-project.d.ts +5 -0
  203. package/lib/utils/is-esm-project.js +16 -0
  204. package/lib/utils/is-interactive.d.ts +8 -0
  205. package/lib/utils/is-interactive.js +10 -0
  206. package/lib/utils/is-module-available.js +3 -4
  207. package/lib/utils/load-configuration.d.ts +1 -1
  208. package/lib/utils/load-configuration.js +4 -7
  209. package/lib/utils/local-binaries.d.ts +2 -2
  210. package/lib/utils/local-binaries.js +8 -11
  211. package/lib/utils/mau.d.ts +14 -0
  212. package/lib/utils/mau.js +46 -0
  213. package/lib/utils/os-info.utils.js +1 -4
  214. package/lib/utils/project-utils.d.ts +1 -3
  215. package/lib/utils/project-utils.js +16 -28
  216. package/lib/utils/remaining-flags.d.ts +2 -2
  217. package/lib/utils/remaining-flags.js +13 -8
  218. package/lib/utils/tree-kill.js +7 -10
  219. package/lib/utils/type-assertions.js +1 -4
  220. package/package.json +70 -83
@@ -0,0 +1,185 @@
1
+ /**
2
+ * A minimal synchronous glob over `fs` + `minimatch`, covering just the
3
+ * surface `assets-manager` needs: one pattern, one optional `ignore`
4
+ * pattern, and `dot`.
5
+ *
6
+ * This replaced the `glob` package (see nestjs/nest-cli#3520), and asset
7
+ * copying is expected to behave exactly as it did before that swap. Several
8
+ * rules below therefore look arbitrary in isolation — zero-segment trailing
9
+ * globstars, the symlink depth budget — but each one preserves a documented
10
+ * behavior that build configurations already depend on. The tests in
11
+ * `test/lib/compiler/helpers/glob.spec.ts` pin them.
12
+ */
13
+ import { readdirSync, statSync } from 'fs';
14
+ import { minimatch } from 'minimatch';
15
+ /**
16
+ * Characters that make a path segment a pattern rather than a literal, under
17
+ * minimatch's default options (no extglob).
18
+ *
19
+ * Matches the literal chars `*`, `?`, `[`, `{` anywhere, plus `!`, `+`, `@`,
20
+ * `?`, or `*` immediately followed by `(` (an extglob-looking prefix, which
21
+ * minimatch still treats specially even without extglob enabled). Chars like
22
+ * `@`, `(`, `)`, `]`, `}` on their own are NOT magic here: treating them as
23
+ * such previously collapsed the literal base far above the real directory
24
+ * (e.g. an `@` in a username segment), causing `readdir` to walk huge trees
25
+ * and silently return no matches on the first unreadable subdirectory.
26
+ */
27
+ const MAGIC_CHARS = /[*?[{]|[!+@?*]\(/;
28
+ const toPosix = (value) => value.replace(/\\/g, '/');
29
+ /**
30
+ * Splits a pattern into the literal prefix we can hand to `readdir` and the
31
+ * trailing segments that still need matching: `/src/a/*.hbs` -> `/src/a`.
32
+ */
33
+ function splitPattern(pattern) {
34
+ const segments = pattern.split('/');
35
+ const magicIndex = segments.findIndex((segment) => MAGIC_CHARS.test(segment));
36
+ if (magicIndex === -1) {
37
+ return { base: pattern, rest: [] };
38
+ }
39
+ return {
40
+ base: segments.slice(0, magicIndex).join('/') || '/',
41
+ rest: segments.slice(magicIndex),
42
+ };
43
+ }
44
+ /**
45
+ * A trailing globstar matches zero segments, so `a/**` must match `a`
46
+ * itself; `minimatch` alone does not. Expanding the suffix restores that for
47
+ * the include pattern, and for `ignore` it prunes the ignored directory
48
+ * itself rather than only its contents.
49
+ */
50
+ function expandGlobstarSuffix(pattern) {
51
+ return pattern.endsWith('/**') ? [pattern, pattern.slice(0, -3)] : [pattern];
52
+ }
53
+ /**
54
+ * How far below a symlinked directory a pattern can still reach.
55
+ *
56
+ * A globstar never recurses *through* a symlinked directory, but the
57
+ * explicit segments that follow the last one keep matching normally once
58
+ * there. So a pattern ending in a bare globstar stops at the symlink itself
59
+ * and gets a budget of 0; adding one more segment after it reaches a single
60
+ * level inside (budget 1), and two segments reach two levels (budget 2). A
61
+ * pattern with no globstar at all does no recursion of this kind, so its own
62
+ * depth is the only bound.
63
+ *
64
+ * Returning a finite number also bounds the walk: symlink cycles terminate
65
+ * because each crossing costs depth we can never regain.
66
+ */
67
+ function symlinkDepthBudget(rest) {
68
+ for (let index = rest.length - 1; index >= 0; index--) {
69
+ if (rest[index].includes('**')) {
70
+ return rest.length - index - 1;
71
+ }
72
+ }
73
+ return rest.length;
74
+ }
75
+ /**
76
+ * Walks the directory tree rooted at `dir`, without using `fs`'s own
77
+ * `recursive` option so we can:
78
+ *
79
+ * - Skip an unreadable directory instead of failing the whole walk: one
80
+ * `EACCES` deep in an asset tree must not silently empty the result.
81
+ * - Stop descending once we are `budget` levels past a symlinked directory
82
+ * (see `symlinkDepthBudget`), which also makes symlink cycles terminate.
83
+ *
84
+ * `depth` is how many levels below the nearest symlinked ancestor an entry
85
+ * sits; 0 means it was reached without crossing one.
86
+ */
87
+ function walk(dir, recursive, budget, depth = 0) {
88
+ let entries;
89
+ try {
90
+ entries = readdirSync(dir, { withFileTypes: true });
91
+ }
92
+ catch {
93
+ // Unreadable or missing directory: contribute nothing here, but let
94
+ // sibling directories elsewhere in the walk still report their matches.
95
+ return [];
96
+ }
97
+ const results = entries.map((entry) => ({
98
+ parent: dir,
99
+ entry,
100
+ }));
101
+ if (!recursive) {
102
+ return results;
103
+ }
104
+ for (const entry of entries) {
105
+ const full = `${dir}/${entry.name}`;
106
+ // Below a symlink every further level costs budget; above one, plain
107
+ // directory recursion is unbounded.
108
+ if (entry.isDirectory()) {
109
+ const childDepth = depth === 0 ? 0 : depth + 1;
110
+ if (childDepth <= budget) {
111
+ results.push(...walk(full, true, budget, childDepth));
112
+ }
113
+ continue;
114
+ }
115
+ // `Dirent.isDirectory()` is false for a symlink (it reports on the link
116
+ // itself), so a symlinked directory only ever reaches this branch.
117
+ if (entry.isSymbolicLink() && depth + 1 <= budget) {
118
+ let stats;
119
+ try {
120
+ stats = statSync(full);
121
+ }
122
+ catch {
123
+ continue;
124
+ }
125
+ if (stats.isDirectory()) {
126
+ results.push(...walk(full, true, budget, depth + 1));
127
+ }
128
+ }
129
+ }
130
+ return results;
131
+ }
132
+ /**
133
+ * Expands `pattern` to every matching path, with the type of each entry.
134
+ * See the file header for the compatibility rules this preserves.
135
+ */
136
+ export function globEntriesSync(pattern, options = {}) {
137
+ const { ignore, dot = false } = options;
138
+ const normalized = toPosix(pattern);
139
+ const { base, rest } = splitPattern(normalized);
140
+ const includeMatchers = expandGlobstarSuffix(normalized);
141
+ const ignoreMatchers = ignore ? expandGlobstarSuffix(toPosix(ignore)) : [];
142
+ const accept = (candidate) => includeMatchers.some((matcher) => minimatch(candidate, matcher, { dot })) &&
143
+ !ignoreMatchers.some((matcher) => minimatch(candidate, matcher, { dot }));
144
+ // No wildcard anywhere: the pattern names one concrete path.
145
+ if (rest.length === 0) {
146
+ const stats = statSync(normalized, { throwIfNoEntry: false });
147
+ if (!stats || !accept(normalized)) {
148
+ return [];
149
+ }
150
+ return [
151
+ {
152
+ path: normalized,
153
+ isFile: stats.isFile(),
154
+ isDirectory: stats.isDirectory(),
155
+ },
156
+ ];
157
+ }
158
+ // Only the immediate children of `base` can match a single trailing
159
+ // wildcard segment, so skip the deep walk in that (common) case.
160
+ const recursive = rest.length > 1 || rest.some((segment) => segment.includes('**'));
161
+ // An asset may be configured for a directory that does not exist, or one
162
+ // that turns out to be unreadable; `walk` reports no matches for it (and
163
+ // any nested directories that fail) rather than failing the whole build.
164
+ const walked = walk(base, recursive, symlinkDepthBudget(rest));
165
+ const matches = [];
166
+ // A trailing `**` matches zero segments, so `base` itself can be a result.
167
+ if (accept(base)) {
168
+ matches.push({ path: base, isFile: false, isDirectory: true });
169
+ }
170
+ for (const { parent, entry } of walked) {
171
+ const full = `${toPosix(parent)}/${entry.name}`;
172
+ if (accept(full)) {
173
+ matches.push({
174
+ path: full,
175
+ isFile: entry.isFile(),
176
+ isDirectory: entry.isDirectory(),
177
+ });
178
+ }
179
+ }
180
+ return matches;
181
+ }
182
+ /** Expands `pattern` to every matching path. */
183
+ export function globSync(pattern, options = {}) {
184
+ return globEntriesSync(pattern, options).map((entry) => entry.path);
185
+ }
@@ -1,9 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listenForManualRestart = listenForManualRestart;
4
- exports.displayManualRestartTip = displayManualRestartTip;
5
- const ansis_1 = require("ansis");
6
- function listenForManualRestart(callback) {
1
+ import { gray } from 'ansis';
2
+ export function listenForManualRestart(callback) {
7
3
  const stdinListener = (data) => {
8
4
  if (data.toString().trim() === 'rs') {
9
5
  process.stdin.removeListener('data', stdinListener);
@@ -12,6 +8,6 @@ function listenForManualRestart(callback) {
12
8
  };
13
9
  process.stdin.on('data', stdinListener);
14
10
  }
15
- function displayManualRestartTip() {
16
- console.log(`To restart at any time, enter ${(0, ansis_1.gray) `rs`}.\n`);
11
+ export function displayManualRestartTip() {
12
+ console.log(`To restart at any time, enter ${gray `rs`}.\n`);
17
13
  }
@@ -0,0 +1,33 @@
1
+ import { Configuration } from '../../configuration/index.js';
2
+ /**
3
+ * Whether paths are allowed to point outside of the project directory.
4
+ *
5
+ * Confinement is on by default: `compilerOptions.allowOutsidePaths` has to be
6
+ * set to `true` explicitly to build to (or delete) locations outside of the
7
+ * project, which some monorepo layouts legitimately rely on. See
8
+ * nestjs/nest-cli#3463.
9
+ */
10
+ export declare function areOutsidePathsAllowed(configuration: Required<Configuration>, appName: string | undefined): boolean;
11
+ export interface PathConfinementOptions {
12
+ /**
13
+ * Resolve symlinks before comparing. Required whenever the path is going to be
14
+ * *written through*: a lexical check alone accepts a path such as "dist" that
15
+ * is itself a symlink pointing outside of the project, and the write would
16
+ * land outside.
17
+ *
18
+ * Deletion does not need it — `fs.rm` unlinks a symlink instead of following
19
+ * it — and turning it on there would reject ordinary setups that legitimately
20
+ * symlink a directory inside the project (a workspace "node_modules", for
21
+ * instance), which is the regression that got the first attempt at this
22
+ * reverted. See nestjs/nest-cli#3460.
23
+ */
24
+ resolveSymlinks?: boolean;
25
+ projectRoot?: string;
26
+ }
27
+ /**
28
+ * Resolves `targetPath` against the project directory and asserts that it stays
29
+ * within it. Returns the resolved absolute path.
30
+ *
31
+ * @throws when the path resolves to the project directory itself or escapes it
32
+ */
33
+ export declare function assertPathInsideProject(targetPath: string, propertyName: string, { resolveSymlinks, projectRoot, }?: PathConfinementOptions): string;
@@ -0,0 +1,63 @@
1
+ import { realpathSync } from 'fs';
2
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from 'path';
3
+ import { getValueOrDefault } from './get-value-or-default.js';
4
+ /**
5
+ * Whether paths are allowed to point outside of the project directory.
6
+ *
7
+ * Confinement is on by default: `compilerOptions.allowOutsidePaths` has to be
8
+ * set to `true` explicitly to build to (or delete) locations outside of the
9
+ * project, which some monorepo layouts legitimately rely on. See
10
+ * nestjs/nest-cli#3463.
11
+ */
12
+ export function areOutsidePathsAllowed(configuration, appName) {
13
+ return (getValueOrDefault(configuration, 'compilerOptions.allowOutsidePaths', appName) ?? false);
14
+ }
15
+ /**
16
+ * Resolves `targetPath` against the project directory and asserts that it stays
17
+ * within it. Returns the resolved absolute path.
18
+ *
19
+ * @throws when the path resolves to the project directory itself or escapes it
20
+ */
21
+ export function assertPathInsideProject(targetPath, propertyName, { resolveSymlinks = false, projectRoot = process.cwd(), } = {}) {
22
+ const resolvedPath = resolve(projectRoot, targetPath);
23
+ const comparableRoot = resolveSymlinks
24
+ ? realpathOrClosestExisting(projectRoot)
25
+ : resolve(projectRoot);
26
+ const comparablePath = resolveSymlinks
27
+ ? realpathOrClosestExisting(resolvedPath)
28
+ : resolvedPath;
29
+ const relativePath = relative(comparableRoot, comparablePath);
30
+ const isProjectRoot = relativePath === '';
31
+ const isOutsideProject = relativePath === '..' ||
32
+ relativePath.startsWith(`..${sep}`) ||
33
+ isAbsolute(relativePath);
34
+ if (isProjectRoot || isOutsideProject) {
35
+ throw new Error(`Refusing to use "${propertyName}" path outside of or equal to the project directory: ${targetPath}. ` +
36
+ `Set "compilerOptions.allowOutsidePaths" to true if this is intentional.`);
37
+ }
38
+ return resolvedPath;
39
+ }
40
+ /**
41
+ * Resolves symlinks in `targetPath`. Output paths routinely do not exist yet, so
42
+ * the closest existing ancestor is resolved instead and the remaining segments
43
+ * are appended back — enough to catch a symlinked ancestor while still working
44
+ * for a directory that is about to be created.
45
+ */
46
+ function realpathOrClosestExisting(targetPath) {
47
+ let current = resolve(targetPath);
48
+ const trailingSegments = [];
49
+ for (;;) {
50
+ try {
51
+ return resolve(realpathSync(current), ...trailingSegments);
52
+ }
53
+ catch {
54
+ const parent = dirname(current);
55
+ if (parent === current) {
56
+ // Reached the root without finding an existing path; nothing to resolve.
57
+ return resolve(targetPath);
58
+ }
59
+ trailingSegments.unshift(basename(current));
60
+ current = parent;
61
+ }
62
+ }
63
+ }
@@ -1,11 +1,12 @@
1
1
  import * as ts from 'typescript';
2
- import { TypeScriptBinaryLoader } from '../typescript-loader';
2
+ import { TypeScriptBinaryLoader } from '../typescript-loader.js';
3
+ export type TsConfigProviderOutput = Pick<ts.ParsedCommandLine, 'options' | 'fileNames' | 'projectReferences'> & {
4
+ exclude: string[];
5
+ };
3
6
  export declare class TsConfigProvider {
4
7
  private readonly typescriptLoader;
5
8
  constructor(typescriptLoader: TypeScriptBinaryLoader);
6
- getByConfigFilename(configFilename: string): {
7
- options: ts.CompilerOptions;
8
- fileNames: string[];
9
- projectReferences: readonly ts.ProjectReference[] | undefined;
10
- };
9
+ getByConfigFilename(configFilename: string): TsConfigProviderOutput;
10
+ private parseExclude;
11
+ private normalizeExclude;
11
12
  }
@@ -1,22 +1,41 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TsConfigProvider = void 0;
4
- const fs_1 = require("fs");
5
- const path_1 = require("path");
6
- const ui_1 = require("../../ui");
7
- class TsConfigProvider {
1
+ import { existsSync } from 'fs';
2
+ import { dirname, isAbsolute, join, relative } from 'path';
3
+ import { CLI_ERRORS } from '../../ui/index.js';
4
+ export class TsConfigProvider {
5
+ typescriptLoader;
8
6
  constructor(typescriptLoader) {
9
7
  this.typescriptLoader = typescriptLoader;
10
8
  }
11
9
  getByConfigFilename(configFilename) {
12
- const configPath = (0, path_1.join)(process.cwd(), configFilename);
13
- if (!(0, fs_1.existsSync)(configPath)) {
14
- throw new Error(ui_1.CLI_ERRORS.MISSING_TYPESCRIPT(configFilename));
10
+ const configPath = join(process.cwd(), configFilename);
11
+ if (!existsSync(configPath)) {
12
+ throw new Error(CLI_ERRORS.MISSING_TYPESCRIPT(configFilename));
15
13
  }
16
14
  const tsBinary = this.typescriptLoader.load();
17
15
  const parsedCmd = tsBinary.getParsedCommandLineOfConfigFile(configPath, undefined, tsBinary.sys);
18
- const { options, fileNames, projectReferences } = parsedCmd;
19
- return { options, fileNames, projectReferences };
16
+ if (!parsedCmd) {
17
+ throw new Error(`Could not parse TypeScript configuration file "${configFilename}". Please, ensure that the file contains valid JSON and compiler options.`);
18
+ }
19
+ const { options, fileNames, projectReferences, raw } = parsedCmd;
20
+ const exclude = this.normalizeExclude(this.parseExclude(raw?.exclude), configPath);
21
+ return { options, fileNames, projectReferences, exclude };
22
+ }
23
+ parseExclude(exclude) {
24
+ const passesTypeValidation = Array.isArray(exclude) &&
25
+ exclude.every((item) => typeof item === 'string');
26
+ if (!passesTypeValidation) {
27
+ return [];
28
+ }
29
+ return exclude;
30
+ }
31
+ normalizeExclude(exclude, configPath) {
32
+ const configDir = dirname(configPath);
33
+ const relativeConfigDir = relative(process.cwd(), configDir);
34
+ return exclude.map((pattern) => {
35
+ const normalized = isAbsolute(pattern)
36
+ ? relative(process.cwd(), pattern)
37
+ : join(relativeConfigDir, pattern);
38
+ return normalized.replace(/\\/g, '/');
39
+ });
20
40
  }
21
41
  }
22
- exports.TsConfigProvider = TsConfigProvider;
@@ -1,12 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tsconfigPathsBeforeHookFactory = tsconfigPathsBeforeHookFactory;
4
- const os = require("os");
5
- const path_1 = require("path");
6
- const typescript_loader_1 = require("../typescript-loader");
7
- const tsPaths = require("tsconfig-paths");
8
- function tsconfigPathsBeforeHookFactory(compilerOptions) {
9
- const tsBinary = new typescript_loader_1.TypeScriptBinaryLoader().load();
1
+ import { createRequire } from 'module';
2
+ import { dirname, posix } from 'path';
3
+ import * as tsPaths from 'tsconfig-paths';
4
+ import { TypeScriptBinaryLoader } from '../typescript-loader.js';
5
+ const require = createRequire(import.meta.url);
6
+ export function tsconfigPathsBeforeHookFactory(compilerOptions) {
7
+ const tsBinary = new TypeScriptBinaryLoader().load();
10
8
  const { paths = {}, baseUrl = './' } = compilerOptions;
11
9
  const matcher = tsPaths.createMatchPath(baseUrl, paths, ['main']);
12
10
  return (ctx) => {
@@ -69,20 +67,22 @@ function getNotAliasedPath(sf, matcher, text) {
69
67
  if (!result) {
70
68
  return;
71
69
  }
72
- if (os.platform() === 'win32') {
70
+ if (process.platform === 'win32') {
73
71
  result = result.replace(/\\/g, '/');
74
72
  }
75
73
  try {
76
74
  // Installed packages (node modules) should take precedence over root files with the same name.
77
75
  // Ref: https://github.com/nestjs/nest-cli/issues/838
78
76
  const packagePath = require.resolve(text, {
79
- paths: [process.cwd(), ...module.paths],
77
+ paths: [process.cwd(), ...(require.resolve.paths(text) ?? [])],
80
78
  });
81
79
  if (packagePath) {
82
80
  return text;
83
81
  }
84
82
  }
85
- catch { }
86
- const resolvedPath = path_1.posix.relative((0, path_1.dirname)(sf.fileName), result) || './';
83
+ catch {
84
+ // package resolution failed, fall through to relative path
85
+ }
86
+ const resolvedPath = posix.relative(dirname(sf.fileName), result) || './';
87
87
  return resolvedPath[0] === '.' ? resolvedPath : './' + resolvedPath;
88
88
  }
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,5 +1,41 @@
1
1
  import * as ts from 'typescript';
2
- import { ReadonlyVisitor } from '../interfaces/readonly-visitor.interface';
2
+ import { DeepPluginMeta, ReadonlyVisitor } from '../interfaces/readonly-visitor.interface.js';
3
+ /**
4
+ * Returns `true` when the consuming project uses an ESM-style module
5
+ * resolution strategy (`node16` / `nodenext`). Under those resolution
6
+ * modes, dynamic `import()` specifiers MUST include the file extension
7
+ * (typically `.js`) for the runtime resolver to find the module. Without
8
+ * the extension, executing the generated metadata file fails with
9
+ * `ERR_MODULE_NOT_FOUND`, and TypeScript reports a diagnostic.
10
+ */
11
+ export declare function requiresExplicitImportExtensions(options: ts.CompilerOptions, tsBinary: typeof ts): boolean;
12
+ /**
13
+ * Returns the same import path with `.js` appended when (and only when)
14
+ * the path is relative and does not already end in a recognized
15
+ * extension. Bare specifiers (e.g. `@nestjs/common`) and absolute paths
16
+ * are returned unchanged because the caller's resolver handles them.
17
+ */
18
+ export declare function appendJsExtensionIfMissing(importPath: string): string;
19
+ /**
20
+ * Rewrites a single `await import("...")`-style string by appending the
21
+ * `.js` extension to the inner specifier when it is a relative path
22
+ * without an extension. Used to patch the visitor-supplied `typeImports`
23
+ * map values.
24
+ */
25
+ export declare function rewriteAsyncImportString(target: string): string;
26
+ /**
27
+ * Walks the given `ts.CallExpression` tree and rewrites every dynamic
28
+ * `import("...")` whose specifier is a relative path missing an
29
+ * extension. Returns a new node when changes are required, or the input
30
+ * node unchanged otherwise.
31
+ */
32
+ export declare function rewriteImportExpressionForNodeNext(expression: ts.CallExpression, tsBinary: typeof ts): ts.CallExpression;
33
+ /**
34
+ * Recursively walks the collected plugin metadata, rewriting every
35
+ * dynamic `import("./relative")` call expression to include the `.js`
36
+ * extension required by node16 / nodenext module resolution.
37
+ */
38
+ export declare function rewriteCollectedMetadataForNodeNext(metadata: Record<string, Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>>, tsBinary: typeof ts): void;
3
39
  export interface PluginMetadataGenerateOptions {
4
40
  /**
5
41
  * The visitors to use to generate the metadata.
@@ -1,10 +1,97 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PluginMetadataGenerator = void 0;
4
- const constants_1 = require("../swc/constants");
5
- const type_checker_host_1 = require("../swc/type-checker-host");
6
- const typescript_loader_1 = require("../typescript-loader");
7
- const plugin_metadata_printer_1 = require("./plugin-metadata-printer");
1
+ import { FOUND_NO_ISSUES_GENERATING_METADATA } from '../swc/constants.js';
2
+ import { TypeCheckerHost } from '../swc/type-checker-host.js';
3
+ import { TypeScriptBinaryLoader } from '../typescript-loader.js';
4
+ import { PluginMetadataPrinter } from './plugin-metadata-printer.js';
5
+ /**
6
+ * Returns `true` when the consuming project uses an ESM-style module
7
+ * resolution strategy (`node16` / `nodenext`). Under those resolution
8
+ * modes, dynamic `import()` specifiers MUST include the file extension
9
+ * (typically `.js`) for the runtime resolver to find the module. Without
10
+ * the extension, executing the generated metadata file fails with
11
+ * `ERR_MODULE_NOT_FOUND`, and TypeScript reports a diagnostic.
12
+ */
13
+ export function requiresExplicitImportExtensions(options, tsBinary) {
14
+ const moduleResolution = options.moduleResolution;
15
+ return (moduleResolution === tsBinary.ModuleResolutionKind.Node16 ||
16
+ moduleResolution === tsBinary.ModuleResolutionKind.NodeNext);
17
+ }
18
+ const RELATIVE_PATH_RE = /^\.\.?\//;
19
+ // Any common JS/TS-style extension that the user could have authored or
20
+ // that our rewrite would have already produced. Prevents double-appending.
21
+ const HAS_KNOWN_EXTENSION_RE = /\.(m?js|c?js|m?ts|c?ts|json|node)$/i;
22
+ /**
23
+ * Returns the same import path with `.js` appended when (and only when)
24
+ * the path is relative and does not already end in a recognized
25
+ * extension. Bare specifiers (e.g. `@nestjs/common`) and absolute paths
26
+ * are returned unchanged because the caller's resolver handles them.
27
+ */
28
+ export function appendJsExtensionIfMissing(importPath) {
29
+ if (!RELATIVE_PATH_RE.test(importPath)) {
30
+ return importPath;
31
+ }
32
+ if (HAS_KNOWN_EXTENSION_RE.test(importPath)) {
33
+ return importPath;
34
+ }
35
+ return `${importPath}.js`;
36
+ }
37
+ /**
38
+ * Rewrites a single `await import("...")`-style string by appending the
39
+ * `.js` extension to the inner specifier when it is a relative path
40
+ * without an extension. Used to patch the visitor-supplied `typeImports`
41
+ * map values.
42
+ */
43
+ export function rewriteAsyncImportString(target) {
44
+ return target.replace(/import\((['"])((?:\\\1|(?!\1).)*)\1\)/g, (match, quote, specifier) => `import(${quote}${appendJsExtensionIfMissing(specifier)}${quote})`);
45
+ }
46
+ /**
47
+ * Walks the given `ts.CallExpression` tree and rewrites every dynamic
48
+ * `import("...")` whose specifier is a relative path missing an
49
+ * extension. Returns a new node when changes are required, or the input
50
+ * node unchanged otherwise.
51
+ */
52
+ export function rewriteImportExpressionForNodeNext(expression, tsBinary) {
53
+ const visit = (node) => {
54
+ if (tsBinary.isCallExpression(node) &&
55
+ node.expression.kind === tsBinary.SyntaxKind.ImportKeyword &&
56
+ node.arguments.length > 0 &&
57
+ tsBinary.isStringLiteralLike(node.arguments[0])) {
58
+ const original = node.arguments[0].text;
59
+ const rewritten = appendJsExtensionIfMissing(original);
60
+ if (rewritten !== original) {
61
+ const updatedArgs = [
62
+ tsBinary.factory.createStringLiteral(rewritten),
63
+ ...node.arguments.slice(1),
64
+ ];
65
+ return tsBinary.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArgs);
66
+ }
67
+ }
68
+ return tsBinary.visitEachChild(node, visit, undefined);
69
+ };
70
+ return visit(expression);
71
+ }
72
+ /**
73
+ * Recursively walks the collected plugin metadata, rewriting every
74
+ * dynamic `import("./relative")` call expression to include the `.js`
75
+ * extension required by node16 / nodenext module resolution.
76
+ */
77
+ export function rewriteCollectedMetadataForNodeNext(metadata, tsBinary) {
78
+ for (const visitorKey of Object.keys(metadata)) {
79
+ const sections = metadata[visitorKey];
80
+ for (const sectionKey of Object.keys(sections)) {
81
+ const tuples = sections[sectionKey];
82
+ if (!Array.isArray(tuples)) {
83
+ continue;
84
+ }
85
+ for (let i = 0; i < tuples.length; i++) {
86
+ const [importExpr, meta] = tuples[i];
87
+ tuples[i] = [
88
+ rewriteImportExpressionForNodeNext(importExpr, tsBinary),
89
+ meta,
90
+ ];
91
+ }
92
+ }
93
+ }
94
+ }
8
95
  /**
9
96
  * Generates plugins metadata by traversing the AST of the project.
10
97
  * @example
@@ -20,11 +107,12 @@ const plugin_metadata_printer_1 = require("./plugin-metadata-printer");
20
107
  * });
21
108
  * ```
22
109
  */
23
- class PluginMetadataGenerator {
110
+ export class PluginMetadataGenerator {
111
+ pluginMetadataPrinter = new PluginMetadataPrinter();
112
+ typeCheckerHost = new TypeCheckerHost();
113
+ typescriptLoader = new TypeScriptBinaryLoader();
114
+ tsBinary;
24
115
  constructor() {
25
- this.pluginMetadataPrinter = new plugin_metadata_printer_1.PluginMetadataPrinter();
26
- this.typeCheckerHost = new type_checker_host_1.TypeCheckerHost();
27
- this.typescriptLoader = new typescript_loader_1.TypeScriptBinaryLoader();
28
116
  this.tsBinary = this.typescriptLoader.load();
29
117
  }
30
118
  generate(options) {
@@ -50,7 +138,7 @@ class PluginMetadataGenerator {
50
138
  console.log(tsBinary.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticsHost));
51
139
  }
52
140
  else {
53
- console.log(constants_1.FOUND_NO_ISSUES_GENERATING_METADATA);
141
+ console.log(FOUND_NO_ISSUES_GENERATING_METADATA);
54
142
  }
55
143
  }
56
144
  };
@@ -75,10 +163,22 @@ class PluginMetadataGenerator {
75
163
  ...visitor.typeImports,
76
164
  };
77
165
  });
166
+ // Under `node16` / `nodenext` module resolution, dynamic `import()`
167
+ // specifiers must include explicit file extensions. The visitors emit
168
+ // bare relative specifiers (e.g. `import("./hello.dto")`), which are
169
+ // valid under classic / node10 resolution but break compilation and
170
+ // runtime under nodenext. When the consuming project uses an ESM-style
171
+ // resolver, rewrite both the metadata import call expressions and the
172
+ // typeImports map values to include the `.js` extension. See #3364.
173
+ if (requiresExplicitImportExtensions(programRef.getCompilerOptions(), this.tsBinary)) {
174
+ rewriteCollectedMetadataForNodeNext(collectedMetadata, this.tsBinary);
175
+ for (const key of Object.keys(typeImports)) {
176
+ typeImports[key] = rewriteAsyncImportString(typeImports[key]);
177
+ }
178
+ }
78
179
  this.pluginMetadataPrinter.print(collectedMetadata, typeImports, {
79
180
  outputDir,
80
181
  filename,
81
182
  }, this.tsBinary);
82
183
  }
83
184
  }
84
- exports.PluginMetadataGenerator = PluginMetadataGenerator;
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript';
2
- import { DeepPluginMeta } from '../interfaces/readonly-visitor.interface';
2
+ import { DeepPluginMeta } from '../interfaces/readonly-visitor.interface.js';
3
3
  export interface PluginMetadataPrintOptions {
4
4
  outputDir: string;
5
5
  filename?: string;