@codependix/nestjs-modules 0.0.3

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.
@@ -0,0 +1,438 @@
1
+ import { ConsoleLogger as e, Global as t, Injectable as n, Module as r, Scope as i } from "@nestjs/common";
2
+ import a from "node:path";
3
+ import { existsSync as o, mkdirSync as s, readdirSync as c } from "node:fs";
4
+ import l from "pino";
5
+ import { pathToFileURL as u } from "node:url";
6
+ import { ModulesContainer as d, NestFactory as f } from "@nestjs/core";
7
+ import { ConfigModule as p } from "@nestjs/config";
8
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/module-graph/module-graph.constants.ts
9
+ var m = "_This project defines no NestJS modules to graph._", h = "flowchart LR", g = "_Rounded modules are global: every module can inject them, so their edges are left out._", _ = 4;
10
+ //#endregion
11
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateMetadata.js
12
+ function v(e, t) {
13
+ if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
14
+ }
15
+ //#endregion
16
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
17
+ function y(e, t, n, r) {
18
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
19
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
20
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
21
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
22
+ }
23
+ //#endregion
24
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/module-graph/module-graph.service.ts
25
+ var b = class {
26
+ constructor() {}
27
+ collectEdgesAndNodes(e, t) {
28
+ let n = [], r = /* @__PURE__ */ new Set(), i = [], a = (e, t) => {
29
+ n.some((n) => n.name === e && n.declaringFile === t) || n.push({
30
+ declaringFile: t,
31
+ name: e
32
+ });
33
+ };
34
+ for (let t of e) a(t.name, t.declaringFile ?? "");
35
+ for (let n of e) for (let o of n.imports) t.has(o) || (i.push({
36
+ source: n.name,
37
+ target: o
38
+ }), r.add(n.name), r.add(o), a(o, e.find((e) => e.name === o)?.declaringFile ?? ""));
39
+ return {
40
+ connectedModuleNames: r,
41
+ edges: i,
42
+ nodes: n
43
+ };
44
+ }
45
+ compareEdges(e, t) {
46
+ return e.source.localeCompare(t.source) || e.target.localeCompare(t.target);
47
+ }
48
+ compareNodes(e, t) {
49
+ return e.name.localeCompare(t.name) || e.declaringFile.localeCompare(t.declaringFile);
50
+ }
51
+ countInboundEdges(e) {
52
+ let t = /* @__PURE__ */ new Map();
53
+ for (let n of e) for (let e of n.imports) t.set(e, (t.get(e) ?? 0) + 1);
54
+ return t;
55
+ }
56
+ findAmbientModuleNames(e) {
57
+ let t = /* @__PURE__ */ new Set();
58
+ if (e.length < 4) return t;
59
+ for (let [n, r] of this.countInboundEdges(e)) r >= e.length - 1 && t.add(n);
60
+ return t;
61
+ }
62
+ renderNode(e, t) {
63
+ return t.ambientModuleNames.includes(e) ? `${e}([${e}])` : e;
64
+ }
65
+ sortNames(e) {
66
+ return [...e].toSorted((e, t) => e.localeCompare(t));
67
+ }
68
+ buildGraph(e, t) {
69
+ let n = this.findAmbientModuleNames(e), { connectedModuleNames: r, edges: i, nodes: a } = this.collectEdgesAndNodes(e, n), o = a.toSorted((e, t) => this.compareNodes(e, t)), s = this.sortNames(new Set(o.map((e) => e.name)));
70
+ return {
71
+ ambientModuleNames: this.sortNames(n),
72
+ edges: i.toSorted((e, t) => this.compareEdges(e, t)),
73
+ isolatedModuleNames: s.filter((e) => !r.has(e)),
74
+ nodes: o,
75
+ projectName: t
76
+ };
77
+ }
78
+ deriveModuleFolder(e) {
79
+ return a.posix.dirname(e.declaringFile.replaceAll("\\", "/"));
80
+ }
81
+ renderMermaid(e) {
82
+ if (e.nodes.length === 0) return m;
83
+ let t = [
84
+ "```mermaid",
85
+ h,
86
+ ...e.nodes.map((t) => ` ${this.renderNode(t.name, e)}`),
87
+ ...e.edges.map((e) => ` ${e.source} --> ${e.target}`),
88
+ "```"
89
+ ];
90
+ return e.ambientModuleNames.length > 0 && t.push("", g), t.join("\n");
91
+ }
92
+ };
93
+ b = y([n(), v("design:paramtypes", [])], b);
94
+ //#endregion
95
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/module-graph/module-graph.module.ts
96
+ var x = class {};
97
+ x = y([r({
98
+ controllers: [],
99
+ exports: [b],
100
+ imports: [],
101
+ providers: [b]
102
+ })], x);
103
+ //#endregion
104
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-modules-workspace-graph/nestjs-modules-workspace-graph.constants.ts
105
+ var S = "_This workspace has no NestJS modules, in any project._", C = "graph LR", w = class {
106
+ constructor() {}
107
+ compareEdges(e, t) {
108
+ return e.source.localeCompare(t.source) || e.target.localeCompare(t.target);
109
+ }
110
+ qualifyModuleName(e, t) {
111
+ return `${e}/${t}`;
112
+ }
113
+ renderNode(e) {
114
+ return ` ${this.toNodeIdentifier(e)}["${e}"]`;
115
+ }
116
+ sortNames(e) {
117
+ return [...e].toSorted((e, t) => e.localeCompare(t));
118
+ }
119
+ toNodeIdentifier(e) {
120
+ return `module_${e.replaceAll(/[^\dA-Za-z]/gu, "_")}`;
121
+ }
122
+ buildWorkspaceGraph(e) {
123
+ let t = this.sortNames(e.flatMap((e) => e.nodes.map((t) => this.qualifyModuleName(e.projectName, t.name))));
124
+ return {
125
+ edges: e.flatMap((e) => e.edges.map((t) => ({
126
+ source: this.qualifyModuleName(e.projectName, t.source),
127
+ target: this.qualifyModuleName(e.projectName, t.target)
128
+ }))).toSorted((e, t) => this.compareEdges(e, t)),
129
+ moduleNames: t
130
+ };
131
+ }
132
+ renderMermaid(e) {
133
+ return e.edges.length === 0 ? S : [
134
+ "```mermaid",
135
+ C,
136
+ ...e.moduleNames.map((e) => this.renderNode(e)),
137
+ ...e.edges.map((e) => ` ${this.toNodeIdentifier(e.source)} --> ${this.toNodeIdentifier(e.target)}`),
138
+ "```"
139
+ ].join("\n");
140
+ }
141
+ };
142
+ w = y([n(), v("design:paramtypes", [])], w);
143
+ //#endregion
144
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-modules-workspace-graph/nestjs-modules-workspace-graph.module.ts
145
+ var T = class {};
146
+ T = y([r({
147
+ controllers: [],
148
+ exports: [w],
149
+ imports: [],
150
+ providers: [w]
151
+ })], T);
152
+ //#endregion
153
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-project/nestjs-project.constants.ts
154
+ var E = "framework:nestjs", D = ".module.ts", O = "src/main.module.ts", k = "MainModule", A = [
155
+ /^ConfigHostModule$/,
156
+ /^InternalCoreModule$/,
157
+ /^SyntheticRootModule$/,
158
+ /^TypeOrmCoreModule$/
159
+ ], j = [/^ConfigModule$/], M = /^(\p{Extended_Pictographic}️?(?:‍\p{Extended_Pictographic}️?)*)\s+/u, N = /^([A-Za-z]+)/, P = /* @__PURE__ */ new Set([
160
+ "built",
161
+ "cut",
162
+ "found",
163
+ "got",
164
+ "kept",
165
+ "left",
166
+ "lost",
167
+ "made",
168
+ "put",
169
+ "ran",
170
+ "read",
171
+ "rebuilt",
172
+ "sent",
173
+ "set",
174
+ "split",
175
+ "took",
176
+ "wrote"
177
+ ]), F = /* @__PURE__ */ new Set([
178
+ "CommandFactory",
179
+ "ExceptionHandler",
180
+ "GraphQLModule",
181
+ "InstanceLoader",
182
+ "NestApplication",
183
+ "NestFactory",
184
+ "RouterExplorer",
185
+ "RoutesResolver",
186
+ "TypeOrmModule"
187
+ ]), I, L = class extends e {
188
+ static {
189
+ I = this;
190
+ }
191
+ constructor() {
192
+ super();
193
+ }
194
+ static isProduction = process.env.NODE_ENV === "production";
195
+ static rootLogger;
196
+ static writesToStandardError = !1;
197
+ child = I.root;
198
+ static createRootLogger() {
199
+ let e = process.env.LOG_LEVEL ?? "info";
200
+ return I.isProduction ? I.writesToStandardError ? l({ level: e }, l.destination(2)) : l({ level: e }) : l({
201
+ level: e,
202
+ transport: {
203
+ options: {
204
+ colorize: !0,
205
+ destination: I.writesToStandardError ? 2 : 1,
206
+ ignore: "pid,hostname,emoji",
207
+ messageFormat: "{emoji} {msg}",
208
+ singleLine: !0
209
+ },
210
+ target: "pino-pretty"
211
+ }
212
+ });
213
+ }
214
+ static logToStandardError() {
215
+ if (I.rootLogger !== void 0) {
216
+ process.emitWarning("LoggerService.logToStandardError() was called after the first log line, so log lines still go to standard output and anything piping that stream will read them as data. Call it as the first statement of the application's bootstrap.");
217
+ return;
218
+ }
219
+ I.writesToStandardError = !0;
220
+ }
221
+ assertConventionalMessage(e) {
222
+ if (I.isProduction || this.shouldSkipConventionalMessageValidation(e.context)) return;
223
+ let t = this.getConventionalMessageViolation(e.parsed);
224
+ if (t !== void 0) throw Error(t);
225
+ }
226
+ buildBindings(e) {
227
+ return this.assertConventionalMessage({
228
+ context: e.context,
229
+ parsed: e.parsed
230
+ }), {
231
+ ...e.data,
232
+ context: e.context,
233
+ ...I.isProduction ? {} : { emoji: e.parsed.emoji }
234
+ };
235
+ }
236
+ getConventionalMessageViolation(e) {
237
+ let t = e.emoji, n = e.text;
238
+ if (t === void 0) return `Log message must start with an emoji naming its subject, then a verb: "${n}"`;
239
+ let r = N.exec(n)?.[1];
240
+ if (r === void 0 || !this.isConventionalVerb(r)) return `Log message must begin with a verb in present progressive or past tense, got "${r ?? ""}": "${t} ${n}"`;
241
+ }
242
+ isConventionalVerb(e) {
243
+ let t = e.toLowerCase();
244
+ return t.endsWith("ing") || t.endsWith("ed") || P.has(t);
245
+ }
246
+ parseMessage(e) {
247
+ let t = String(e), n = M.exec(t), r = n?.[1];
248
+ return r === void 0 ? {
249
+ emoji: void 0,
250
+ text: t
251
+ } : {
252
+ emoji: r,
253
+ text: t.slice(n?.[0].length)
254
+ };
255
+ }
256
+ shouldSkipConventionalMessageValidation(e) {
257
+ return e !== void 0 && F.has(e);
258
+ }
259
+ static get root() {
260
+ return I.rootLogger ??= I.createRootLogger(), I.rootLogger;
261
+ }
262
+ buildErrorLogEntry(e, t) {
263
+ let n = t instanceof Error ? t.stack || t.message : String(t);
264
+ return {
265
+ errorMessage: n,
266
+ logLine: `[${(/* @__PURE__ */ new Date()).toISOString()}] ${e}: ${n}\n`
267
+ };
268
+ }
269
+ createTimestampedOutputLogFilePath(e) {
270
+ let t = a.join(process.cwd(), "output");
271
+ return o(t) || s(t, { recursive: !0 }), a.join(t, `${e}-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-")}.log`);
272
+ }
273
+ debug(e, t, n) {
274
+ let r = this.parseMessage(e);
275
+ this.child.debug(this.buildBindings({
276
+ context: t ?? this.context,
277
+ data: n,
278
+ parsed: r
279
+ }), r.text);
280
+ }
281
+ error(e, t, n) {
282
+ let r = this.parseMessage(e), i = typeof n == "object" ? n : void 0, a = typeof n == "string" ? n : this.context;
283
+ this.child.error({
284
+ ...this.buildBindings({
285
+ context: a,
286
+ data: i,
287
+ parsed: r
288
+ }),
289
+ stack: t
290
+ }, r.text);
291
+ }
292
+ info(e, t, n) {
293
+ let r = this.parseMessage(e);
294
+ this.child.info(this.buildBindings({
295
+ context: t ?? this.context,
296
+ data: n,
297
+ parsed: r
298
+ }), r.text);
299
+ }
300
+ log(e, t, n) {
301
+ this.info(e, t, n);
302
+ }
303
+ setContext(e) {
304
+ super.setContext(e), this.child = I.root.child({ context: e });
305
+ }
306
+ verbose(e, t, n) {
307
+ let r = this.parseMessage(e);
308
+ this.child.trace(this.buildBindings({
309
+ context: t ?? this.context,
310
+ data: n,
311
+ parsed: r
312
+ }), r.text);
313
+ }
314
+ warn(e, t, n) {
315
+ let r = this.parseMessage(e);
316
+ this.child.warn(this.buildBindings({
317
+ context: t ?? this.context,
318
+ data: n,
319
+ parsed: r
320
+ }), r.text);
321
+ }
322
+ };
323
+ L = I = y([n({ scope: i.TRANSIENT }), v("design:paramtypes", [])], L);
324
+ //#endregion
325
+ //#region packages/logger/src/modules/logger/logger.module.ts
326
+ var R = class {};
327
+ R = y([t(), r({
328
+ controllers: [],
329
+ exports: [L],
330
+ imports: [],
331
+ providers: [L]
332
+ })], R);
333
+ //#endregion
334
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-project/nestjs-project-synthetic.module.ts
335
+ var z, B = z = class {
336
+ static forModules(e) {
337
+ return {
338
+ imports: [p.forRoot({
339
+ ignoreEnvFile: !0,
340
+ isGlobal: !0
341
+ }), ...e],
342
+ module: z
343
+ };
344
+ }
345
+ };
346
+ B = z = y([r({})], B);
347
+ //#endregion
348
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-project/nestjs-project.service.ts
349
+ var V, H = V = class {
350
+ logger;
351
+ constructor(e) {
352
+ this.logger = e, this.logger.setContext(V.name);
353
+ }
354
+ async buildSyntheticRootModule(e) {
355
+ let t = this.findModuleFiles(a.join(e.absoluteRoot, "src")), n = await Promise.all(t.map(async (e) => this.loadModuleClasses(e)));
356
+ return B.forModules(n.flat());
357
+ }
358
+ async discoverModuleDeclaringFiles(e) {
359
+ let t = this.findModuleFiles(a.join(e.absoluteRoot, "src"));
360
+ e.rootModuleFile !== void 0 && !t.includes(e.rootModuleFile) && t.push(e.rootModuleFile);
361
+ let n = /* @__PURE__ */ new Map();
362
+ for (let r of t) {
363
+ let t = a.relative(e.absoluteRoot, r).replaceAll("\\", "/"), i = await this.loadModuleClasses(r);
364
+ for (let e of i) n.set(e.name, t);
365
+ }
366
+ return n;
367
+ }
368
+ exploreContainer(e, t) {
369
+ let n = [...e.get(d).values()], r = /* @__PURE__ */ new Set();
370
+ for (let { name: e } of n) t.some((t) => t.test(e)) || r.add(e);
371
+ let i = [];
372
+ for (let { imports: e, name: t } of n) r.has(t) && i.push({
373
+ imports: [...e].map((e) => e.name).filter((e) => r.has(e)),
374
+ name: t
375
+ });
376
+ return i;
377
+ }
378
+ findModuleFiles(e) {
379
+ if (!o(e)) return [];
380
+ let t = [];
381
+ for (let n of c(e, { withFileTypes: !0 })) {
382
+ let r = a.join(e, n.name);
383
+ n.isDirectory() ? t.push(...this.findModuleFiles(r)) : n.name.endsWith(".module.ts") && t.push(r);
384
+ }
385
+ return t.toSorted((e, t) => e.localeCompare(t));
386
+ }
387
+ async loadModuleClasses(e) {
388
+ let t = await import(u(e).href);
389
+ return Object.entries(t).filter((e) => e[0].endsWith("Module") && typeof e[1] == "function").map(([, e]) => e);
390
+ }
391
+ async loadRootModule(e) {
392
+ let t = (await import(u(e).href))[k];
393
+ if (typeof t != "function") throw TypeError(`Expected ${e} to export a ${k} class`);
394
+ return t;
395
+ }
396
+ describeProject(e, t) {
397
+ let n = a.join(e, O);
398
+ return {
399
+ absoluteRoot: e,
400
+ name: t,
401
+ rootModuleFile: o(n) ? n : void 0
402
+ };
403
+ }
404
+ discoverProjects(e) {
405
+ return e.filter((e) => this.isNestjsProject(e)).map((e) => this.describeProject(e.absoluteRoot, e.name));
406
+ }
407
+ async exploreProject(e) {
408
+ let { rootModuleFile: t } = e, n = t === void 0 ? await this.buildSyntheticRootModule(e) : await this.loadRootModule(t), r = await this.discoverModuleDeclaringFiles(e), i = await f.createApplicationContext(n, {
409
+ abortOnError: !1,
410
+ logger: !1,
411
+ preview: !0
412
+ }), a;
413
+ try {
414
+ a = this.exploreContainer(i, [...A, ...t === void 0 ? j : []]);
415
+ } finally {
416
+ this.logger.debug("🚀 Booted a project's container", void 0, { project: e.name }), await i.close();
417
+ }
418
+ return a.map((e) => ({
419
+ ...e,
420
+ declaringFile: r.get(e.name)
421
+ }));
422
+ }
423
+ isNestjsProject(e) {
424
+ return e.tags.includes(E);
425
+ }
426
+ };
427
+ H = V = y([n(), v("design:paramtypes", [L === void 0 ? Object : L])], H);
428
+ //#endregion
429
+ //#region packages/ic-suite/codependix/codependix-nestjs-modules/src/modules/nestjs-project/nestjs-project.module.ts
430
+ var U = class {};
431
+ U = y([r({
432
+ controllers: [],
433
+ exports: [H],
434
+ imports: [R],
435
+ providers: [H]
436
+ })], U);
437
+ //#endregion
438
+ export { g as MODULE_GRAPH_AMBIENT_LEGEND, _ as MODULE_GRAPH_AMBIENT_MINIMUM_MODULES, h as MODULE_GRAPH_MERMAID_HEADER, m as MODULE_GRAPH_UNCONNECTED, x as ModuleGraphModule, b as ModuleGraphService, C as NESTJS_MODULES_WORKSPACE_GRAPH_MERMAID_HEADER, S as NESTJS_MODULES_WORKSPACE_GRAPH_UNCONNECTED, A as NESTJS_PROJECT_IGNORED_MODULES, D as NESTJS_PROJECT_MODULE_FILE_SUFFIX, k as NESTJS_PROJECT_ROOT_MODULE_EXPORT, O as NESTJS_PROJECT_ROOT_MODULE_FILE, j as NESTJS_PROJECT_SYNTHETIC_IGNORED_MODULES, E as NESTJS_PROJECT_TAG, T as NestjsModulesWorkspaceGraphModule, w as NestjsModulesWorkspaceGraphService, U as NestjsProjectModule, H as NestjsProjectService };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "author": "Jimmy Paolini",
3
+ "bugs": {
4
+ "url": "https://github.com/organizzolini/codebase/issues"
5
+ },
6
+ "dependencies": {
7
+ "@nestjs/common": "^11.1.21 || ^12.0.1",
8
+ "@nestjs/config": "^12.0.0",
9
+ "@nestjs/core": "^11.1.21 || ^12.0.1",
10
+ "pino": "^10.3.1",
11
+ "pino-pretty": "^13.1.3",
12
+ "reflect-metadata": "^0.2.2"
13
+ },
14
+ "description": "NestJS module container inspection and module-level dependency graph extraction for Codependix",
15
+ "devDependencies": {
16
+ "@codebase/logger": "0.0.1",
17
+ "@golevelup/ts-vitest": "^4.0.0",
18
+ "@nestjs/testing": "^12.0.1",
19
+ "@nx/eslint-plugin": "^23.0.0",
20
+ "@swc/helpers": "^0.5.21",
21
+ "@types/node": "^26.5.1",
22
+ "@vitest/coverage-v8": "^5.0.0",
23
+ "vitest": "^5.0.0"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/src/index.d.ts",
28
+ "default": "./dist/src/index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "LICENSE",
35
+ "README.md"
36
+ ],
37
+ "homepage": "https://github.com/organizzolini/codebase/tree/main/packages/ic-suite/codependix/codependix-nestjs-modules#readme",
38
+ "keywords": [
39
+ "architecture",
40
+ "codependix",
41
+ "dependency-injection",
42
+ "module-graph",
43
+ "nestjs"
44
+ ],
45
+ "license": "MIT",
46
+ "main": "./dist/src/index.js",
47
+ "name": "@codependix/nestjs-modules",
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "registry": "https://registry.npmjs.org"
51
+ },
52
+ "repository": {
53
+ "directory": "packages/ic-suite/codependix/codependix-nestjs-modules",
54
+ "type": "git",
55
+ "url": "git+https://github.com/organizzolini/codebase.git"
56
+ },
57
+ "type": "module",
58
+ "typeCoverage": {
59
+ "atLeast": 100,
60
+ "strict": true
61
+ },
62
+ "types": "./dist/src/index.d.ts",
63
+ "version": "0.0.3"
64
+ }