@nestjs-dash/schematics 0.0.1

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 (92) hide show
  1. package/README.md +19 -0
  2. package/dist/collection.json +58 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +4 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/action/action.factory.d.ts +4 -0
  8. package/dist/lib/action/action.factory.d.ts.map +1 -0
  9. package/dist/lib/action/action.factory.js +41 -0
  10. package/dist/lib/action/action.factory.js.map +1 -0
  11. package/dist/lib/action/schema.json +42 -0
  12. package/dist/lib/entity/entity.factory.d.ts +4 -0
  13. package/dist/lib/entity/entity.factory.d.ts.map +1 -0
  14. package/dist/lib/entity/entity.factory.js +20 -0
  15. package/dist/lib/entity/entity.factory.js.map +1 -0
  16. package/dist/lib/entity/files/ts/__name@dasherize__.entity.ts +32 -0
  17. package/dist/lib/entity/schema.json +38 -0
  18. package/dist/lib/filter/filter.factory.d.ts +4 -0
  19. package/dist/lib/filter/filter.factory.d.ts.map +1 -0
  20. package/dist/lib/filter/filter.factory.js +66 -0
  21. package/dist/lib/filter/filter.factory.js.map +1 -0
  22. package/dist/lib/filter/schema.json +46 -0
  23. package/dist/lib/module/module.factory.d.ts +4 -0
  24. package/dist/lib/module/module.factory.d.ts.map +1 -0
  25. package/dist/lib/module/module.factory.js +75 -0
  26. package/dist/lib/module/module.factory.js.map +1 -0
  27. package/dist/lib/module/module.render.d.ts +13 -0
  28. package/dist/lib/module/module.render.d.ts.map +1 -0
  29. package/dist/lib/module/module.render.js +42 -0
  30. package/dist/lib/module/module.render.js.map +1 -0
  31. package/dist/lib/module/schema.json +57 -0
  32. package/dist/lib/page/files/ts/pages/__name@dasherize__-overview.page.ts +15 -0
  33. package/dist/lib/page/page.factory.d.ts +4 -0
  34. package/dist/lib/page/page.factory.d.ts.map +1 -0
  35. package/dist/lib/page/page.factory.js +18 -0
  36. package/dist/lib/page/page.factory.js.map +1 -0
  37. package/dist/lib/page/schema.json +28 -0
  38. package/dist/lib/panel/files/ts/__name@dasherize__.panel.ts +17 -0
  39. package/dist/lib/panel/panel.factory.d.ts +4 -0
  40. package/dist/lib/panel/panel.factory.d.ts.map +1 -0
  41. package/dist/lib/panel/panel.factory.js +20 -0
  42. package/dist/lib/panel/panel.factory.js.map +1 -0
  43. package/dist/lib/panel/schema.json +28 -0
  44. package/dist/lib/relation-manager/files/ts/relation-managers/__name@dasherize__.relation-manager.ts +13 -0
  45. package/dist/lib/relation-manager/relation-manager.factory.d.ts +4 -0
  46. package/dist/lib/relation-manager/relation-manager.factory.d.ts.map +1 -0
  47. package/dist/lib/relation-manager/relation-manager.factory.js +59 -0
  48. package/dist/lib/relation-manager/relation-manager.factory.js.map +1 -0
  49. package/dist/lib/relation-manager/schema.json +56 -0
  50. package/dist/lib/resource/resource.factory.d.ts +4 -0
  51. package/dist/lib/resource/resource.factory.d.ts.map +1 -0
  52. package/dist/lib/resource/resource.factory.js +112 -0
  53. package/dist/lib/resource/resource.factory.js.map +1 -0
  54. package/dist/lib/resource/resource.render.d.ts +10 -0
  55. package/dist/lib/resource/resource.render.d.ts.map +1 -0
  56. package/dist/lib/resource/resource.render.js +125 -0
  57. package/dist/lib/resource/resource.render.js.map +1 -0
  58. package/dist/lib/resource/schema.json +62 -0
  59. package/dist/lib/widget/schema.json +39 -0
  60. package/dist/lib/widget/widget.factory.d.ts +4 -0
  61. package/dist/lib/widget/widget.factory.d.ts.map +1 -0
  62. package/dist/lib/widget/widget.factory.js +40 -0
  63. package/dist/lib/widget/widget.factory.js.map +1 -0
  64. package/dist/utils/class-codemod.d.ts +33 -0
  65. package/dist/utils/class-codemod.d.ts.map +1 -0
  66. package/dist/utils/class-codemod.js +146 -0
  67. package/dist/utils/class-codemod.js.map +1 -0
  68. package/dist/utils/detect.d.ts +27 -0
  69. package/dist/utils/detect.d.ts.map +1 -0
  70. package/dist/utils/detect.js +135 -0
  71. package/dist/utils/detect.js.map +1 -0
  72. package/dist/utils/entity-introspect.d.ts +17 -0
  73. package/dist/utils/entity-introspect.d.ts.map +1 -0
  74. package/dist/utils/entity-introspect.js +164 -0
  75. package/dist/utils/entity-introspect.js.map +1 -0
  76. package/dist/utils/field-mapping.d.ts +14 -0
  77. package/dist/utils/field-mapping.d.ts.map +1 -0
  78. package/dist/utils/field-mapping.js +100 -0
  79. package/dist/utils/field-mapping.js.map +1 -0
  80. package/dist/utils/module-codemod.d.ts +26 -0
  81. package/dist/utils/module-codemod.d.ts.map +1 -0
  82. package/dist/utils/module-codemod.js +134 -0
  83. package/dist/utils/module-codemod.js.map +1 -0
  84. package/dist/utils/naming.d.ts +5 -0
  85. package/dist/utils/naming.d.ts.map +1 -0
  86. package/dist/utils/naming.js +21 -0
  87. package/dist/utils/naming.js.map +1 -0
  88. package/dist/utils/ts-morph-utils.d.ts +10 -0
  89. package/dist/utils/ts-morph-utils.d.ts.map +1 -0
  90. package/dist/utils/ts-morph-utils.js +34 -0
  91. package/dist/utils/ts-morph-utils.js.map +1 -0
  92. package/package.json +69 -0
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsNestjs-DashModule",
4
+ "title": "Nestjs-Dash Module Options Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the module's folder, e.g. widgets.",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What name would you like to use for the module?"
15
+ },
16
+ "path": {
17
+ "type": "string",
18
+ "format": "path",
19
+ "description": "Parent directory the module's folder is created under."
20
+ },
21
+ "resourceClassName": {
22
+ "type": "string",
23
+ "description": "Resource class to register via AdminModule.forFeature([...]). Normally supplied by the resource schematic, not a human."
24
+ },
25
+ "resourceImportPath": {
26
+ "type": "string",
27
+ "description": "Import specifier for resourceClassName."
28
+ },
29
+ "entityClassName": {
30
+ "type": "string",
31
+ "description": "Entity class to register via TypeOrmModule.forFeature([...]). Normally supplied by the resource schematic, not a human."
32
+ },
33
+ "entityImportPath": {
34
+ "type": "string",
35
+ "description": "Import specifier for entityClassName."
36
+ },
37
+ "pageClassName": {
38
+ "type": "string",
39
+ "description": "Page class to register as a provider. Normally supplied by the resource/page schematics, not a human."
40
+ },
41
+ "pageImportPath": {
42
+ "type": "string",
43
+ "description": "Import specifier for pageClassName."
44
+ },
45
+ "skipRootImport": {
46
+ "type": "boolean",
47
+ "default": false,
48
+ "description": "Skip wiring a newly-created module into the root app module."
49
+ },
50
+ "verbose": {
51
+ "type": "boolean",
52
+ "default": false,
53
+ "description": "Print detailed diagnostics about codemod/root-wiring decisions."
54
+ }
55
+ },
56
+ "required": ["name"]
57
+ }
@@ -0,0 +1,15 @@
1
+ import { Page, type PageRenderContext } from '@nestjs-dash/core';
2
+ import { AdminPage } from '@nestjs-dash/nestjs';
3
+ import { Injectable } from '@nestjs/common';
4
+
5
+ @Injectable()
6
+ @AdminPage()
7
+ export class <%= classify(name) %>Page extends Page {
8
+ static override slug = '<%= dasherize(name) %>-overview';
9
+ static override title = '<%= classify(name) %> Overview';
10
+
11
+ // TODO: query real data, e.g. by injecting NESTJS_DASH_DATA_SOURCE from '@nestjs-dash/nestjs'
12
+ override async content(_ctx: PageRenderContext): Promise<string> {
13
+ return '<div class="p-6">TODO: implement <%= classify(name) %> overview content.</div>';
14
+ }
15
+ }
@@ -0,0 +1,4 @@
1
+ import { type Rule } from '@angular-devkit/schematics';
2
+ import type { PageOptions } from './page.schema.js';
3
+ export declare function main(options: PageOptions): Rule;
4
+ //# sourceMappingURL=page.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.factory.d.ts","sourceRoot":"","sources":["../../../src/lib/page/page.factory.ts"],"names":[],"mappings":"AACA,OAAO,EAOL,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAiB/C"}
@@ -0,0 +1,18 @@
1
+ import { strings } from '@angular-devkit/core';
2
+ import { apply, MergeStrategy, mergeWith, move, template, url, } from '@angular-devkit/schematics';
3
+ import { join, normalize } from 'node:path/posix';
4
+ import { toKebabCase } from '../../utils/naming.js';
5
+ export function main(options) {
6
+ return (tree, context) => {
7
+ const parentDir = options.dir ? normalize(`/${options.dir}`) : normalize('/');
8
+ const kebab = toKebabCase(options.name);
9
+ const targetPath = join(parentDir, 'pages', `${kebab}-overview.page.ts`);
10
+ if (tree.exists(targetPath) && !options.force) {
11
+ context.logger.warn(`Skipped (already exists): ${targetPath}`);
12
+ return tree;
13
+ }
14
+ const strategy = tree.exists(targetPath) ? MergeStrategy.Overwrite : MergeStrategy.Default;
15
+ return mergeWith(apply(url('./files/ts'), [template({ ...strings, ...options }), move(parentDir)]), strategy)(tree, context);
16
+ };
17
+ }
18
+ //# sourceMappingURL=page.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.factory.js","sourceRoot":"","sources":["../../../src/lib/page/page.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACL,KAAK,EACL,aAAa,EACb,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,GAAG,GAIJ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,UAAU,IAAI,CAAC,OAAoB;IACvC,OAAO,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;QAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,mBAAmB,CAAC,CAAC;QAEzE,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC;QAC3F,OAAO,SAAS,CACd,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EACjF,QAAQ,CACT,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsNestjs-DashPage",
4
+ "title": "Nestjs-Dash Page Options Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the page, e.g. Widget or widget.",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What name would you like to use for this page?"
15
+ },
16
+ "dir": {
17
+ "type": "string",
18
+ "format": "path",
19
+ "description": "Target directory (a pages/ subfolder is added)."
20
+ },
21
+ "force": {
22
+ "type": "boolean",
23
+ "default": false,
24
+ "description": "Overwrite an existing <name>-overview.page.ts without prompting."
25
+ }
26
+ },
27
+ "required": ["name"]
28
+ }
@@ -0,0 +1,17 @@
1
+ import { Panel } from '@nestjs-dash/core';
2
+ import { AdminModule } from '@nestjs-dash/nestjs';
3
+
4
+ export function create<%= classify(name) %>Panel() {
5
+ return Panel.make()
6
+ .id('<%= dasherize(name) %>')
7
+ .path('/<%= dasherize(name) %>')
8
+ .brandName('<%= classify(name) %>')
9
+ .resources([]);
10
+ }
11
+
12
+ export const <%= classify(name) %>AdminModule = AdminModule.forRoot({
13
+ panel: create<%= classify(name) %>Panel(),<% if (audit) { %>
14
+ // audit.enabled requires a TypeORM DataSource — pass `dataSource` here,
15
+ // or switch to AdminModule.forRootAsync({ inject: [DataSource], useFactory: (dataSource) => ({ ... }) }).
16
+ audit: { enabled: true },<% } %>
17
+ });
@@ -0,0 +1,4 @@
1
+ import { type Rule } from '@angular-devkit/schematics';
2
+ import type { PanelOptions } from './panel.schema.js';
3
+ export declare function main(options: PanelOptions): Rule;
4
+ //# sourceMappingURL=panel.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.factory.d.ts","sourceRoot":"","sources":["../../../src/lib/panel/panel.factory.ts"],"names":[],"mappings":"AACA,OAAO,EAOL,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,wBAAgB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAsBhD"}
@@ -0,0 +1,20 @@
1
+ import { strings } from '@angular-devkit/core';
2
+ import { apply, MergeStrategy, mergeWith, move, template, url, } from '@angular-devkit/schematics';
3
+ import { join } from 'node:path/posix';
4
+ import { toKebabCase } from '../../utils/naming.js';
5
+ const PANEL_DIR = '/src/admin';
6
+ export function main(options) {
7
+ return (tree, context) => {
8
+ const targetPath = join(PANEL_DIR, `${toKebabCase(options.name)}.panel.ts`);
9
+ if (tree.exists(targetPath) && !options.force) {
10
+ context.logger.warn(`Skipped (already exists): ${targetPath}`);
11
+ return tree;
12
+ }
13
+ const strategy = tree.exists(targetPath) ? MergeStrategy.Overwrite : MergeStrategy.Default;
14
+ return mergeWith(apply(url('./files/ts'), [
15
+ template({ audit: false, ...strings, ...options }),
16
+ move(PANEL_DIR),
17
+ ]), strategy)(tree, context);
18
+ };
19
+ }
20
+ //# sourceMappingURL=panel.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.factory.js","sourceRoot":"","sources":["../../../src/lib/panel/panel.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACL,KAAK,EACL,aAAa,EACb,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,GAAG,GAIJ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,SAAS,GAAG,YAAY,CAAC;AAE/B,MAAM,UAAU,IAAI,CAAC,OAAqB;IACxC,OAAO,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE5E,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC;QAC3F,OAAO,SAAS,CACd,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;YAKvB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;YAClD,IAAI,CAAC,SAAS,CAAC;SAChB,CAAC,EACF,QAAQ,CACT,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsNestjs-DashPanel",
4
+ "title": "Nestjs-Dash Panel Options Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the panel, e.g. Admin.",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What name would you like to use for this panel?"
15
+ },
16
+ "force": {
17
+ "type": "boolean",
18
+ "default": false,
19
+ "description": "Overwrite an existing <name>.panel.ts without prompting."
20
+ },
21
+ "audit": {
22
+ "type": "boolean",
23
+ "default": false,
24
+ "description": "Enable the built-in audit log (audit.enabled) and read-only Activity resource. Requires a TypeORM dataSource — see the generated TODO comment."
25
+ }
26
+ },
27
+ "required": ["name"]
28
+ }
@@ -0,0 +1,13 @@
1
+ import { RelationManager, TextColumn, type Table } from '@nestjs-dash/core';
2
+
3
+ export class <%= classify(name) %>RelationManager extends RelationManager {
4
+ static override relationship = '<%= dasherize(name) %>';
5
+ static override type = '<%= type %>' as const;
6
+ static override relatedResource = '<%= relatedResource %>';
7
+ <% if (foreignKey) { %> static override foreignKey = '<%= foreignKey %>';
8
+ <% } %> static override title = '<%= title %>';
9
+
10
+ static override table(table: Table) {
11
+ return table.columns([TextColumn.make('id').label('ID')]);
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ import { type Rule } from '@angular-devkit/schematics';
2
+ import type { RelationManagerOptions } from './relation-manager.schema.js';
3
+ export declare function main(options: RelationManagerOptions): Rule;
4
+ //# sourceMappingURL=relation-manager.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-manager.factory.d.ts","sourceRoot":"","sources":["../../../src/lib/relation-manager/relation-manager.factory.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAKpC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAE3E,wBAAgB,IAAI,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAgE1D"}
@@ -0,0 +1,59 @@
1
+ import { strings } from '@angular-devkit/core';
2
+ import { apply, chain, MergeStrategy, mergeWith, move, template, url, } from '@angular-devkit/schematics';
3
+ import { join, normalize } from 'node:path/posix';
4
+ import { codemodStaticArrayProperty } from '../../utils/class-codemod.js';
5
+ import { normalizePath, relativeImportPath } from '../../utils/detect.js';
6
+ import { humanize, toKebabCase, toPascalCase } from '../../utils/naming.js';
7
+ export function main(options) {
8
+ return (tree, context) => {
9
+ const parentDir = normalize(`/${options.dir}`);
10
+ const kebab = toKebabCase(options.name);
11
+ const relatedResource = options.relatedResource ? toKebabCase(options.relatedResource) : kebab;
12
+ const type = options.type ?? 'has-many';
13
+ const title = options.title ?? humanize(toPascalCase(options.name));
14
+ const className = `${toPascalCase(options.name)}RelationManager`;
15
+ const targetPath = join(parentDir, 'relation-managers', `${kebab}.relation-manager.ts`);
16
+ if (tree.exists(targetPath) && !options.force) {
17
+ context.logger.warn(`Skipped (already exists): ${targetPath}`);
18
+ return tree;
19
+ }
20
+ const strategy = tree.exists(targetPath) ? MergeStrategy.Overwrite : MergeStrategy.Default;
21
+ const rules = [
22
+ mergeWith(apply(url('./files/ts'), [
23
+ template({
24
+ foreignKey: null,
25
+ ...strings,
26
+ ...options,
27
+ name: options.name,
28
+ relatedResource,
29
+ type,
30
+ title,
31
+ }),
32
+ move(parentDir),
33
+ ]), strategy),
34
+ ];
35
+ if (options.resource) {
36
+ rules.push((wiringTree, wiringContext) => {
37
+ const resourcePath = normalizePath(options.resource);
38
+ if (!wiringTree.exists(resourcePath)) {
39
+ throw new Error(`--resource points at ${resourcePath}, which doesn't exist`);
40
+ }
41
+ const result = codemodStaticArrayProperty({
42
+ tree: wiringTree,
43
+ filePath: resourcePath,
44
+ propertyName: 'relationManagers',
45
+ elementText: className,
46
+ namedImports: [
47
+ { moduleSpecifier: relativeImportPath(resourcePath, targetPath), name: className },
48
+ ],
49
+ });
50
+ if (options.verbose) {
51
+ wiringContext.logger.info(`${result.changed ? 'Modified' : 'No changes needed'}: ${resourcePath}`);
52
+ }
53
+ return wiringTree;
54
+ });
55
+ }
56
+ return chain(rules)(tree, context);
57
+ };
58
+ }
59
+ //# sourceMappingURL=relation-manager.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-manager.factory.js","sourceRoot":"","sources":["../../../src/lib/relation-manager/relation-manager.factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "SchematicsNestjs-DashRelationManager",
4
+ "title": "Nestjs-Dash RelationManager Options Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The relationship name, e.g. books.",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What is the name of this relationship?"
15
+ },
16
+ "dir": {
17
+ "type": "string",
18
+ "format": "path",
19
+ "description": "Target directory (a relation-managers/ subfolder is added)."
20
+ },
21
+ "relatedResource": {
22
+ "type": "string",
23
+ "description": "Slug of the related resource. Defaults to the dasherized name."
24
+ },
25
+ "type": {
26
+ "type": "string",
27
+ "enum": ["has-many", "belongs-to-many"],
28
+ "default": "has-many",
29
+ "description": "Relation type."
30
+ },
31
+ "foreignKey": {
32
+ "type": "string",
33
+ "description": "Foreign key column on the related entity (has-many only)."
34
+ },
35
+ "title": {
36
+ "type": "string",
37
+ "description": "Tab/section title. Defaults to the humanized name."
38
+ },
39
+ "resource": {
40
+ "type": "string",
41
+ "format": "path",
42
+ "description": "Path to an existing Resource file — when given, wires the generated class into its static override relationManagers = [...] array."
43
+ },
44
+ "force": {
45
+ "type": "boolean",
46
+ "default": false,
47
+ "description": "Overwrite an existing <name>.relation-manager.ts without prompting."
48
+ },
49
+ "verbose": {
50
+ "type": "boolean",
51
+ "default": false,
52
+ "description": "Print diagnostics about the resource wiring codemod."
53
+ }
54
+ },
55
+ "required": ["name", "dir"]
56
+ }
@@ -0,0 +1,4 @@
1
+ import { type Rule } from '@angular-devkit/schematics';
2
+ import type { ResourceOptions } from './resource.schema.js';
3
+ export declare function main(options: ResourceOptions): Rule;
4
+ //# sourceMappingURL=resource.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.factory.d.ts","sourceRoot":"","sources":["../../../src/lib/resource/resource.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAapC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAqC5D,wBAAgB,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CA0GnD"}
@@ -0,0 +1,112 @@
1
+ import { chain, schematic, SchematicsException, } from '@angular-devkit/schematics';
2
+ import { dirname, join } from 'node:path/posix';
3
+ import { findEntityFile, hasNestCoreDependency, normalizePath, relativeImportPath, resolveSourceRoot, resolveTargetDir, } from '../../utils/detect.js';
4
+ import { introspectEntitySource } from '../../utils/entity-introspect.js';
5
+ import { toKebabCase, toPascalCase } from '../../utils/naming.js';
6
+ import { renderResourceTemplate } from './resource.render.js';
7
+ function writeResourceFile(options) {
8
+ return (tree, context) => {
9
+ if (tree.exists(options.resourceFilePath) && !options.force) {
10
+ context.logger.warn(`Skipped (already exists): ${options.resourceFilePath}`);
11
+ return tree;
12
+ }
13
+ const source = renderResourceTemplate(options.name, {
14
+ entityImportPath: options.entityImportPath,
15
+ entityClassName: options.entityClassName,
16
+ fields: options.fields,
17
+ softDelete: options.softDelete,
18
+ withPermissions: options.withPermissions,
19
+ });
20
+ if (tree.exists(options.resourceFilePath)) {
21
+ tree.overwrite(options.resourceFilePath, source);
22
+ }
23
+ else {
24
+ tree.create(options.resourceFilePath, source);
25
+ }
26
+ return tree;
27
+ };
28
+ }
29
+ export function main(options) {
30
+ return (tree, context) => {
31
+ if (!hasNestCoreDependency(tree)) {
32
+ throw new SchematicsException('Could not find a NestJS project (no package.json with a "@nestjs/core" dependency at the tree root). Run this from inside a NestJS project.');
33
+ }
34
+ const sourceRoot = resolveSourceRoot(tree);
35
+ const { dir, folderName, matched, matchedModulePath } = resolveTargetDir(tree, {
36
+ sourceRoot,
37
+ name: options.name,
38
+ explicitDir: options.dir,
39
+ });
40
+ if (options.verbose && matched) {
41
+ context.logger.info(`Matched existing module folder ${dir} via *.module.ts fuzzy-match (found ${matchedModulePath}).`);
42
+ }
43
+ const pascal = toPascalCase(options.name);
44
+ const kebab = toKebabCase(options.name);
45
+ let entityClassName = `${pascal}Entity`;
46
+ const resourceClassName = `${pascal}Resource`;
47
+ const pageClassName = options.page ? `${pascal}Page` : undefined;
48
+ const entityFilePath = options.entityFilePath
49
+ ? normalizePath(options.entityFilePath)
50
+ : findEntityFile(tree, dir, options.name);
51
+ let entityFields;
52
+ const notes = [];
53
+ const rules = [];
54
+ if (!entityFilePath) {
55
+ if (options.entity === false) {
56
+ throw new SchematicsException(`--no-entity was passed but no existing entity file was found under ${dir}. Create the entity first, or drop --no-entity.`);
57
+ }
58
+ rules.push(schematic('entity', {
59
+ name: options.name,
60
+ path: dir,
61
+ flat: false,
62
+ force: options.force ?? false,
63
+ softDelete: options.softDelete ?? false,
64
+ }));
65
+ }
66
+ else {
67
+ const content = tree.read(entityFilePath).toString('utf-8');
68
+ try {
69
+ const introspected = introspectEntitySource(entityFilePath, content);
70
+ entityClassName = introspected.className;
71
+ entityFields = introspected.fields;
72
+ notes.push(`Reusing existing entity at ${entityFilePath}`);
73
+ }
74
+ catch (error) {
75
+ notes.push(`Could not read field declarations from ${entityFilePath} (${error instanceof Error ? error.message : String(error)}) — using default table/form fields.`);
76
+ }
77
+ }
78
+ const resolvedEntityFilePath = entityFilePath ?? join(dir, 'entities', `${kebab}.entity.ts`);
79
+ const resourceFilePath = join(dir, 'resources', `${kebab}.resource.ts`);
80
+ rules.push(writeResourceFile({
81
+ resourceFilePath,
82
+ entityImportPath: relativeImportPath(resourceFilePath, resolvedEntityFilePath),
83
+ entityClassName,
84
+ fields: entityFields,
85
+ name: options.name,
86
+ force: options.force ?? false,
87
+ softDelete: Boolean(options.softDelete) && !entityFilePath,
88
+ withPermissions: options.withPermissions,
89
+ }));
90
+ let pageFilePath;
91
+ if (options.page) {
92
+ pageFilePath = join(dir, 'pages', `${kebab}-overview.page.ts`);
93
+ rules.push(schematic('page', { name: options.name, dir, force: options.force ?? false }));
94
+ }
95
+ const modulePath = join(dir, `${folderName}.module.ts`);
96
+ rules.push(schematic('module', {
97
+ name: folderName,
98
+ path: dirname(dir),
99
+ resourceClassName,
100
+ resourceImportPath: relativeImportPath(modulePath, resourceFilePath),
101
+ entityClassName,
102
+ entityImportPath: relativeImportPath(modulePath, resolvedEntityFilePath),
103
+ pageClassName,
104
+ pageImportPath: pageFilePath ? relativeImportPath(modulePath, pageFilePath) : undefined,
105
+ verbose: options.verbose ?? false,
106
+ }));
107
+ for (const note of notes)
108
+ context.logger.info(note);
109
+ return chain(rules)(tree, context);
110
+ };
111
+ }
112
+ //# sourceMappingURL=resource.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.factory.js","sourceRoot":"","sources":["../../../src/lib/resource/resource.factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { EntityField } from '../../utils/entity-introspect.js';
2
+ export interface RenderResourceOptions {
3
+ entityImportPath: string;
4
+ entityClassName?: string;
5
+ fields?: EntityField[];
6
+ softDelete?: boolean;
7
+ withPermissions?: boolean;
8
+ }
9
+ export declare function renderResourceTemplate(name: string, options: RenderResourceOptions): string;
10
+ //# sourceMappingURL=resource.render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.render.d.ts","sourceRoot":"","sources":["../../../src/lib/resource/resource.render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAIpE,MAAM,WAAW,qBAAqB;IAEpC,gBAAgB,EAAE,MAAM,CAAC;IAEzB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IAEvB,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA4BD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAgH3F"}
@@ -0,0 +1,125 @@
1
+ import { buildResourceDeclarations } from '../../utils/field-mapping.js';
2
+ import { humanize, toKebabCase, toPascalCase } from '../../utils/naming.js';
3
+ function splitNotes(notes) {
4
+ const fieldNotes = new Map();
5
+ const classNotes = [];
6
+ for (const note of notes) {
7
+ const match = /^Field '([^']+)' is an enum/.exec(note);
8
+ if (match)
9
+ fieldNotes.set(match[1], note);
10
+ else
11
+ classNotes.push(note);
12
+ }
13
+ return { fieldNotes, classNotes };
14
+ }
15
+ function annotateFormLines(formLines, fieldNotes) {
16
+ return formLines.flatMap((line) => {
17
+ const match = /\.make\('([^']+)'\)/.exec(line);
18
+ const note = match ? fieldNotes.get(match[1]) : undefined;
19
+ return note ? [`// TODO: ${note}`, line] : [line];
20
+ });
21
+ }
22
+ export function renderResourceTemplate(name, options) {
23
+ const pascal = toPascalCase(name);
24
+ const slug = `${toKebabCase(name)}s`;
25
+ const label = `${humanize(pascal)}s`;
26
+ const entityClassName = options.entityClassName ?? `${pascal}Entity`;
27
+ const decl = options.fields ? buildResourceDeclarations(options.fields) : undefined;
28
+ const { fieldNotes, classNotes } = decl
29
+ ? splitNotes(decl.notes)
30
+ : { fieldNotes: new Map(), classNotes: [] };
31
+ const columnImports = decl ? decl.columnImports : ['TextColumn'];
32
+ const formImports = decl ? decl.formImports : ['TextInput'];
33
+ const namedImports = [
34
+ ...new Set([
35
+ 'BulkDeleteAction',
36
+ 'CreateAction',
37
+ 'DeleteAction',
38
+ 'EditAction',
39
+ 'Resource',
40
+ 'ViewAction',
41
+ ...(options.softDelete ? ['RestoreAction'] : []),
42
+ ...columnImports,
43
+ ...formImports,
44
+ ]),
45
+ ].sort();
46
+ const rowActions = [
47
+ 'ViewAction.make()',
48
+ 'EditAction.make()',
49
+ 'DeleteAction.make()',
50
+ ...(options.softDelete ? ['RestoreAction.make()'] : []),
51
+ ].join(', ');
52
+ const columnsBlock = decl
53
+ ? decl.columnLines.map((line) => ` ${line}`).join('\n')
54
+ : [
55
+ ` TextColumn.make('id').label('ID').sortable(),`,
56
+ ` TextColumn.make('name').label('Name').searchable().sortable(),`,
57
+ ` TextColumn.make('createdAt').label('Created').sortable(),`,
58
+ ].join('\n');
59
+ const formBlock = decl
60
+ ? `\n${annotateFormLines(decl.formLines, fieldNotes)
61
+ .map((line) => ` ${line}`)
62
+ .join('\n')}\n `
63
+ : `TextInput.make('name').required().label('Name')`;
64
+ const sortBlock = decl
65
+ ? decl.defaultSortField
66
+ ? `\n .defaultSortBy([['${decl.defaultSortField.name}', '${decl.defaultSortField.direction}']])`
67
+ : ''
68
+ : `\n .defaultSortBy([['createdAt', 'DESC']])`;
69
+ const relationManagerComment = classNotes.length > 0
70
+ ? classNotes.map((note) => ` // TODO: ${note}`).join('\n')
71
+ : ' // Add RelationManager subclasses here, e.g. static override relationManagers = [...]';
72
+ const permissionsBlock = options.withPermissions
73
+ ? `
74
+ // TODO: replace with real role/permission checks — \`ctx.user\` is whatever
75
+ // your auth resolver returned. Each case defaults to \`true\` (unrestricted);
76
+ // tighten as needed. See AdminPolicyContext for the full ctx shape.
77
+ static override can(action: string, ctx: AdminPolicyContext) {
78
+ switch (action) {
79
+ case 'view':
80
+ return true;
81
+ case 'create':
82
+ return true;
83
+ case 'update':
84
+ return true;
85
+ case 'delete':
86
+ return true;
87
+ default:
88
+ return true;
89
+ }
90
+ }
91
+
92
+ `
93
+ : '';
94
+ return `import {
95
+ ${namedImports.join(',\n ')},${options.withPermissions ? '\n type AdminPolicyContext,' : ''}
96
+ type Schema,
97
+ type Table,
98
+ } from '@nestjs-dash/core';
99
+ import { ${entityClassName} } from '${options.entityImportPath}';
100
+
101
+ export class ${pascal}Resource extends Resource {
102
+ static override slug = '${slug}';
103
+ static override label = '${label}';
104
+ static override model = ${entityClassName};
105
+ ${permissionsBlock}
106
+ ${relationManagerComment}
107
+
108
+ static override table(table: Table) {
109
+ return table
110
+ .columns([
111
+ ${columnsBlock}
112
+ ])
113
+ .actions([${rowActions}])
114
+ .toolbarActions([CreateAction.make().icon('plus')])
115
+ .bulkActions([BulkDeleteAction.make()])${sortBlock}
116
+ .defaultLimit(20);
117
+ }
118
+
119
+ static override form(form: Schema) {
120
+ return form.components([${formBlock}]);
121
+ }
122
+ }
123
+ `;
124
+ }
125
+ //# sourceMappingURL=resource.render.js.map