@researchdatabox/redbox-dev-tools 0.0.1 → 5.0.0-RC1

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 (129) hide show
  1. package/README.md +259 -14
  2. package/bin/cli.js +3 -0
  3. package/bin/run-hook-mocha.js +3 -0
  4. package/bin/run-hook-tsc.js +3 -0
  5. package/bin/wrapper-webpack.js +168 -0
  6. package/config/tsconfig.base.json +16 -0
  7. package/dist/bin/run-hook-mocha.d.ts +1 -0
  8. package/dist/bin/run-hook-mocha.js +49 -0
  9. package/dist/bin/run-hook-tsc.d.ts +1 -0
  10. package/dist/bin/run-hook-tsc.js +19 -0
  11. package/dist/cli-parsers.d.ts +9 -0
  12. package/dist/cli-parsers.js +131 -0
  13. package/dist/cli.d.ts +1 -0
  14. package/dist/cli.js +495 -0
  15. package/dist/commands/form-config.d.ts +6 -0
  16. package/dist/commands/form-config.js +401 -0
  17. package/dist/commands/sync-translation.d.ts +23 -0
  18. package/dist/commands/sync-translation.js +348 -0
  19. package/dist/completion/index.d.ts +1 -0
  20. package/dist/completion/index.js +218 -0
  21. package/dist/generators/add-method.d.ts +27 -0
  22. package/dist/generators/add-method.js +235 -0
  23. package/dist/generators/angular-app.d.ts +24 -0
  24. package/dist/generators/angular-app.js +342 -0
  25. package/dist/generators/angular-service.d.ts +21 -0
  26. package/dist/generators/angular-service.js +154 -0
  27. package/dist/generators/controller.d.ts +40 -0
  28. package/dist/generators/controller.js +195 -0
  29. package/dist/generators/form-component.d.ts +46 -0
  30. package/dist/generators/form-component.js +906 -0
  31. package/dist/generators/form-field.d.ts +15 -0
  32. package/dist/generators/form-field.js +160 -0
  33. package/dist/generators/migration.d.ts +15 -0
  34. package/dist/generators/migration.js +100 -0
  35. package/dist/generators/model.d.ts +42 -0
  36. package/dist/generators/model.js +206 -0
  37. package/dist/generators/service.d.ts +16 -0
  38. package/dist/generators/service.js +111 -0
  39. package/dist/runtime-resolver.d.ts +3 -0
  40. package/dist/runtime-resolver.js +27 -0
  41. package/dist/templates/hook-archetype.d.ts +12 -0
  42. package/dist/templates/hook-archetype.js +187 -0
  43. package/dist/testing.d.ts +7 -0
  44. package/dist/testing.js +53 -0
  45. package/dist/utils/config-helper.d.ts +71 -0
  46. package/dist/utils/config-helper.js +574 -0
  47. package/dist/utils/generator.d.ts +10 -0
  48. package/dist/utils/generator.js +68 -0
  49. package/dist/utils/hook-tsconfig.d.ts +9 -0
  50. package/dist/utils/hook-tsconfig.js +104 -0
  51. package/dist/utils/paths.d.ts +10 -0
  52. package/dist/utils/paths.js +60 -0
  53. package/index.js +4 -0
  54. package/package.json +76 -19
  55. package/runtime-resolver.d.ts +1 -0
  56. package/runtime-resolver.js +3 -0
  57. package/skills/redbox-angular-apps/SKILL.md +42 -0
  58. package/skills/redbox-angular-services/SKILL.md +29 -0
  59. package/skills/redbox-controllers/SKILL.md +65 -0
  60. package/skills/redbox-feature-design-planner/SKILL.md +169 -0
  61. package/skills/redbox-feature-implementation-review/SKILL.md +117 -0
  62. package/skills/redbox-form-components/SKILL.md +114 -0
  63. package/skills/redbox-form-config/SKILL.md +34 -0
  64. package/skills/redbox-hook-development/SKILL.md +66 -0
  65. package/skills/redbox-services/SKILL.md +54 -0
  66. package/skills/redbox-test-verification/SKILL.md +38 -0
  67. package/skills/redbox-testing/SKILL.md +64 -0
  68. package/skills/redbox-web-verification/SKILL.md +71 -0
  69. package/skills/redbox-web-verification/redbox-angular-apps/SKILL.md +48 -0
  70. package/skills/redbox-web-verification/redbox-angular-services/SKILL.md +30 -0
  71. package/skills/redbox-web-verification/redbox-controllers/SKILL.md +56 -0
  72. package/skills/redbox-web-verification/redbox-form-config/SKILL.md +29 -0
  73. package/skills/redbox-web-verification/redbox-hook-development/SKILL.md +61 -0
  74. package/skills/redbox-web-verification/redbox-services/SKILL.md +45 -0
  75. package/skills/redbox-web-verification/redbox-testing/SKILL.md +53 -0
  76. package/skills/redbox-web-verification/redbox-web-verification/SKILL.md +47 -0
  77. package/templates/hook-archetype/standard/.gitignore.hbs +5 -0
  78. package/templates/hook-archetype/standard/.npmignore.hbs +4 -0
  79. package/templates/hook-archetype/standard/.npmrc.hbs +6 -0
  80. package/templates/hook-archetype/standard/.nvmrc.hbs +1 -0
  81. package/templates/hook-archetype/standard/AGENTS.md.hbs +51 -0
  82. package/templates/hook-archetype/standard/Dockerfile.hbs +29 -0
  83. package/templates/hook-archetype/standard/README.md.hbs +57 -0
  84. package/templates/hook-archetype/standard/assets/default/.gitkeep +1 -0
  85. package/templates/hook-archetype/standard/assets/fonts/.gitkeep +1 -0
  86. package/templates/hook-archetype/standard/assets/images/.gitkeep +1 -0
  87. package/templates/hook-archetype/standard/assets/styles/.gitkeep +1 -0
  88. package/templates/hook-archetype/standard/language-defaults/.gitkeep +1 -0
  89. package/templates/hook-archetype/standard/language-defaults/README.md.hbs +10 -0
  90. package/templates/hook-archetype/standard/language-defaults/en/translation.json.hbs +3 -0
  91. package/templates/hook-archetype/standard/language-defaults/meta.json.hbs +6 -0
  92. package/templates/hook-archetype/standard/package.json.hbs +44 -0
  93. package/templates/hook-archetype/standard/src/api/controllers/ExampleController.ts.hbs +21 -0
  94. package/templates/hook-archetype/standard/src/api/controllers/index.ts.hbs +23 -0
  95. package/templates/hook-archetype/standard/src/api/services/ExampleService.ts.hbs +22 -0
  96. package/templates/hook-archetype/standard/src/api/services/index.ts.hbs +23 -0
  97. package/templates/hook-archetype/standard/src/config/brandingConfigurationDefaults.ts.hbs +9 -0
  98. package/templates/hook-archetype/standard/src/config/reusableFormDefinitions.ts.hbs +6 -0
  99. package/templates/hook-archetype/standard/src/form-config/default-1.0-draft.ts.hbs +20 -0
  100. package/templates/hook-archetype/standard/src/form-config/index.ts.hbs +8 -0
  101. package/templates/hook-archetype/standard/src/index.ts.hbs +55 -0
  102. package/templates/hook-archetype/standard/src/migrations/index.ts.hbs +19 -0
  103. package/templates/hook-archetype/standard/support/bootstrap-data/.gitkeep +1 -0
  104. package/templates/hook-archetype/standard/support/bootstrap-data/namedqueries/.gitkeep +1 -0
  105. package/templates/hook-archetype/standard/support/bootstrap-data/records/.gitkeep +1 -0
  106. package/templates/hook-archetype/standard/support/bootstrap-data/reports/.gitkeep +1 -0
  107. package/templates/hook-archetype/standard/support/bootstrap-data/vocabularies/.gitkeep +1 -0
  108. package/templates/hook-archetype/standard/support/development/core-mount.env.hbs +3 -0
  109. package/templates/hook-archetype/standard/support/development/docker-compose.core-mount.yml.hbs +37 -0
  110. package/templates/hook-archetype/standard/support/development/docker-compose.yml.hbs +108 -0
  111. package/templates/hook-archetype/standard/support/development/prepare-core-mount.sh.hbs +61 -0
  112. package/templates/hook-archetype/standard/support/integration-testing/docker-compose.mocha.yml.hbs +118 -0
  113. package/templates/hook-archetype/standard/support/integration-testing/run-mocha-hook.sh.hbs +14 -0
  114. package/templates/hook-archetype/standard/support/integration-testing/run-mocha-redbox.sh.hbs +100 -0
  115. package/templates/hook-archetype/standard/support/prepare-minio.sh.hbs +74 -0
  116. package/templates/hook-archetype/standard/test/integration/hook-registration.test.ts.hbs +15 -0
  117. package/templates/hook-archetype/standard/test/support/globals.ts.hbs +40 -0
  118. package/templates/hook-archetype/standard/test/tsconfig.json.hbs +8 -0
  119. package/templates/hook-archetype/standard/test/unit/.mocharc.cjs.hbs +9 -0
  120. package/templates/hook-archetype/standard/test/unit/index.test.ts.hbs +46 -0
  121. package/templates/hook-archetype/standard/tsconfig.json.hbs +15 -0
  122. package/templates/hook-archetype/standard/views/.gitkeep +1 -0
  123. package/templates/hook-archetype/standard/views/default/.gitkeep +1 -0
  124. package/testing.d.ts +1 -0
  125. package/testing.js +3 -0
  126. package/.travis.yml +0 -23
  127. package/commands/initProject.js +0 -129
  128. package/redbox-dev-tools +0 -70
  129. package/yarn.lock +0 -1061
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AngularServiceGenerator = void 0;
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ const generator_1 = require("../utils/generator");
40
+ class AngularServiceGenerator extends generator_1.Generator {
41
+ app;
42
+ name;
43
+ methods;
44
+ paths;
45
+ constructor(options) {
46
+ super(options);
47
+ this.app = options.app.toLowerCase();
48
+ this.name = options.name.toLowerCase();
49
+ this.methods = options.methods || [];
50
+ this.paths = options.paths;
51
+ }
52
+ async generate() {
53
+ const projectRoot = path.join(this.paths.angular, 'projects', 'researchdatabox', this.app);
54
+ if (!fs.existsSync(projectRoot)) {
55
+ throw new Error(`Angular app project not found at: ${projectRoot}`);
56
+ }
57
+ const serviceClassName = this.toClassName(this.name);
58
+ const fileName = `${this.name}.service.ts`;
59
+ const servicePath = path.join(projectRoot, 'src', 'app', fileName);
60
+ const content = this.generateServiceContent(serviceClassName);
61
+ this.writeFile(servicePath, content);
62
+ }
63
+ generateServiceContent(serviceClassName) {
64
+ const className = `${serviceClassName}Service`;
65
+ let methodsContent = '';
66
+ if (this.methods.length > 0) {
67
+ const methodInfos = this.methods.map(method => ({
68
+ raw: method,
69
+ identifier: this.toSafeMethodName(method)
70
+ }));
71
+ methodsContent = methodInfos.map(method => `
72
+ /**
73
+ * ${this.capitalize(method.identifier)}
74
+ */
75
+ public async ${method.identifier}(): Promise<any> {
76
+ const url = \`\${this.brandingAndPortalUrl}/app/${this.app}/${method.raw}\`;
77
+ const result$ = this.http.get(url, { ...this.reqOptsJsonBodyOnly, context: this.httpContext });
78
+ return await firstValueFrom(result$);
79
+ }
80
+ `).join('\n');
81
+ }
82
+ return `import { Injectable, Inject } from '@angular/core';
83
+ import { HttpClient } from '@angular/common/http';
84
+ import { APP_BASE_HREF } from '@angular/common';
85
+ import { HttpClientService, ConfigService, UtilityService } from '@researchdatabox/portal-ng-common';
86
+ import { firstValueFrom } from 'rxjs';
87
+
88
+ /**
89
+ * ${serviceClassName} Service
90
+ *
91
+ * Generated by ReDBox Hook Kit
92
+ */
93
+ @Injectable({
94
+ providedIn: 'root'
95
+ })
96
+ export class ${className} extends HttpClientService {
97
+
98
+ constructor(
99
+ @Inject(HttpClient) http: HttpClient,
100
+ @Inject(APP_BASE_HREF) rootContext: string,
101
+ @Inject(UtilityService) utilService: UtilityService,
102
+ @Inject(ConfigService) configService: ConfigService
103
+ ) {
104
+ super(http, rootContext, utilService, configService);
105
+ }
106
+
107
+ public override async waitForInit(): Promise<any> {
108
+ await super.waitForInit();
109
+ this.enableCsrfHeader();
110
+ return this;
111
+ }
112
+ ${methodsContent}
113
+ }
114
+ `;
115
+ }
116
+ toClassName(name) {
117
+ return name.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
118
+ }
119
+ capitalize(s) {
120
+ return s.charAt(0).toUpperCase() + s.slice(1);
121
+ }
122
+ isValidTsIdentifier(name) {
123
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
124
+ }
125
+ toSafeMethodName(method) {
126
+ const trimmed = method.trim();
127
+ if (!trimmed) {
128
+ throw new Error('Method name cannot be empty.');
129
+ }
130
+ if (this.isValidTsIdentifier(trimmed)) {
131
+ return trimmed;
132
+ }
133
+ const parts = trimmed.split(/[^A-Za-z0-9_$]+/).filter(Boolean);
134
+ const camel = parts.map((part, index) => {
135
+ const lower = part.toLowerCase();
136
+ if (index === 0)
137
+ return lower;
138
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
139
+ }).join('');
140
+ let candidate = camel || trimmed.replace(/[^A-Za-z0-9_$]/g, '');
141
+ if (!candidate) {
142
+ throw new Error(`Method name '${method}' cannot be converted to a valid identifier.`);
143
+ }
144
+ if (!/^[A-Za-z_$]/.test(candidate)) {
145
+ candidate = `_${candidate}`;
146
+ }
147
+ if (!this.isValidTsIdentifier(candidate)) {
148
+ throw new Error(`Method name '${method}' cannot be converted to a valid identifier.`);
149
+ }
150
+ console.warn(` [WARN] Sanitized method name '${method}' to '${candidate}'.`);
151
+ return candidate;
152
+ }
153
+ }
154
+ exports.AngularServiceGenerator = AngularServiceGenerator;
@@ -0,0 +1,40 @@
1
+ import { Generator, GeneratorOptions } from '../utils/generator';
2
+ import { RedboxPaths } from '../utils/paths';
3
+ import { NavigationMapping, LanguageDefaultEntry } from '../utils/config-helper';
4
+ export interface RouteMapping {
5
+ action: string;
6
+ verb?: string;
7
+ path: string;
8
+ auth?: string[];
9
+ csrf?: boolean;
10
+ }
11
+ export interface ControllerGeneratorOptions extends GeneratorOptions {
12
+ name: string;
13
+ actions?: string[];
14
+ webservice?: boolean;
15
+ /** @deprecated Use routes instead */
16
+ route?: string;
17
+ routes?: RouteMapping[];
18
+ auth?: string[];
19
+ navigation?: NavigationMapping[];
20
+ languageDefaults?: LanguageDefaultEntry[];
21
+ className?: string;
22
+ paths: RedboxPaths;
23
+ }
24
+ export declare class ControllerGenerator extends Generator {
25
+ private name;
26
+ private className;
27
+ private actions;
28
+ private webservice;
29
+ private routes;
30
+ private auth?;
31
+ private navigation;
32
+ private languageDefaults;
33
+ private paths;
34
+ private project;
35
+ constructor(options: ControllerGeneratorOptions);
36
+ generate(): Promise<void>;
37
+ private generateControllerContent;
38
+ private validateActions;
39
+ private isValidIdentifier;
40
+ }
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ControllerGenerator = void 0;
37
+ const path = __importStar(require("path"));
38
+ const ts_morph_1 = require("ts-morph");
39
+ const generator_1 = require("../utils/generator");
40
+ const config_helper_1 = require("../utils/config-helper");
41
+ class ControllerGenerator extends generator_1.Generator {
42
+ name;
43
+ className;
44
+ actions;
45
+ webservice;
46
+ routes;
47
+ auth;
48
+ navigation;
49
+ languageDefaults;
50
+ paths;
51
+ project;
52
+ constructor(options) {
53
+ super(options);
54
+ this.name = options.name;
55
+ this.actions = options.actions || [];
56
+ this.webservice = !!options.webservice;
57
+ this.auth = options.auth;
58
+ this.navigation = options.navigation || [];
59
+ this.languageDefaults = options.languageDefaults || [];
60
+ this.paths = options.paths;
61
+ const inferredClassName = this.name.endsWith('Controller') ? this.name.replace('Controller', '') : this.name;
62
+ this.className = options.className || inferredClassName;
63
+ this.project = new ts_morph_1.Project({
64
+ manipulationSettings: {
65
+ quoteKind: ts_morph_1.QuoteKind.Single,
66
+ },
67
+ });
68
+ this.validateActions();
69
+ // Handle backwards compatibility: convert single route to routes array
70
+ if (options.routes && options.routes.length > 0) {
71
+ this.routes = options.routes;
72
+ }
73
+ else if (options.route) {
74
+ // Legacy: single route maps to first action or 'index'
75
+ const action = (this.actions && this.actions.length > 0) ? this.actions[0] : 'index';
76
+ this.routes = [{ action, path: options.route, verb: 'get' }];
77
+ }
78
+ else {
79
+ this.routes = [];
80
+ }
81
+ }
82
+ async generate() {
83
+ const controllerName = this.name.endsWith('Controller') ? this.name : `${this.name}Controller`;
84
+ const className = this.className;
85
+ const controllerDir = path.join(this.paths.coreTypes, 'src', 'controllers', this.webservice ? 'webservice' : '');
86
+ const controllerPath = path.join(controllerDir, `${controllerName}.ts`);
87
+ const content = this.generateControllerContent(className);
88
+ this.writeFile(controllerPath, content);
89
+ if (!this.dryRun) {
90
+ await (0, config_helper_1.updateControllerIndex)({
91
+ project: this.project,
92
+ paths: this.paths,
93
+ root: this.root,
94
+ controllerName: controllerName,
95
+ className: className,
96
+ webservice: this.webservice,
97
+ dryRun: this.dryRun
98
+ });
99
+ // Process each route mapping
100
+ for (const routeMapping of this.routes) {
101
+ const routeKey = routeMapping.verb
102
+ ? `${routeMapping.verb.toLowerCase()} ${routeMapping.path}`
103
+ : routeMapping.path;
104
+ await (0, config_helper_1.updateRoutes)({
105
+ project: this.project,
106
+ paths: this.paths,
107
+ root: this.root,
108
+ route: routeKey,
109
+ controllerName: controllerName,
110
+ action: routeMapping.action,
111
+ webservice: this.webservice,
112
+ csrf: routeMapping.csrf,
113
+ dryRun: this.dryRun
114
+ });
115
+ // Apply auth rules: use route-specific auth if provided, otherwise controller-level auth
116
+ const routeAuth = routeMapping.auth || this.auth;
117
+ if (routeAuth && routeAuth.length > 0) {
118
+ await (0, config_helper_1.updateAuth)({
119
+ project: this.project,
120
+ paths: this.paths,
121
+ root: this.root,
122
+ route: routeKey,
123
+ auth: routeAuth,
124
+ dryRun: this.dryRun
125
+ });
126
+ }
127
+ }
128
+ if (this.navigation.length > 0) {
129
+ for (const nav of this.navigation) {
130
+ const actionName = nav.action || (this.routes.length === 1 ? this.routes[0].action : (this.actions.length === 1 ? this.actions[0] : undefined));
131
+ if (!actionName) {
132
+ console.warn(' [NAV] Navigation mapping missing action and cannot be inferred. Skipping.');
133
+ continue;
134
+ }
135
+ const routeMapping = this.routes.find(r => r.action === actionName);
136
+ if (!routeMapping) {
137
+ console.warn(` [NAV] No route mapping found for action '${actionName}'. Skipping.`);
138
+ continue;
139
+ }
140
+ await (0, config_helper_1.updateNavigationConfig)({
141
+ project: this.project,
142
+ paths: this.paths,
143
+ root: this.root,
144
+ mapping: {
145
+ ...nav,
146
+ href: routeMapping.path
147
+ },
148
+ dryRun: this.dryRun
149
+ });
150
+ }
151
+ }
152
+ if (this.languageDefaults.length > 0) {
153
+ await (0, config_helper_1.updateLanguageDefaults)({
154
+ root: this.root,
155
+ entries: this.languageDefaults,
156
+ dryRun: this.dryRun
157
+ });
158
+ }
159
+ }
160
+ }
161
+ generateControllerContent(className) {
162
+ const importPath = this.webservice ? '../../index' : '../index';
163
+ const actionsCode = this.actions.map(action => `
164
+ public async ${action}(req: Sails.Req, res: Sails.Res): Promise<unknown> {
165
+ return this.sendResp(req, res, { data: { message: 'Action ${action} in ${className}' } });
166
+ }`).join('\n');
167
+ const exportedMethods = ['init', ...this.actions].map(a => `'${a}'`).join(',\n ');
168
+ return `import { Controllers as controllers } from '${importPath}';
169
+
170
+ export namespace Controllers {
171
+ export class ${className} extends controllers.Core.Controller {
172
+
173
+ protected override _exportedMethods: string[] = [
174
+ ${exportedMethods}
175
+ ];
176
+
177
+ public init(): void {
178
+ // Initialize services here
179
+ }
180
+ ${actionsCode}
181
+ }
182
+ }
183
+ `;
184
+ }
185
+ validateActions() {
186
+ const invalidActions = this.actions.filter(action => !this.isValidIdentifier(action));
187
+ if (invalidActions.length > 0) {
188
+ throw new Error(`Invalid action name(s): ${invalidActions.join(', ')}`);
189
+ }
190
+ }
191
+ isValidIdentifier(name) {
192
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
193
+ }
194
+ }
195
+ exports.ControllerGenerator = ControllerGenerator;
@@ -0,0 +1,46 @@
1
+ import { Generator, GeneratorOptions } from '../utils/generator';
2
+ import { RedboxPaths } from '../utils/paths';
3
+ export interface FormComponentGeneratorOptions extends GeneratorOptions {
4
+ name: string;
5
+ app?: string;
6
+ withService?: boolean;
7
+ paths: RedboxPaths;
8
+ }
9
+ export declare class FormComponentGenerator extends Generator {
10
+ private readonly rawName;
11
+ private readonly app;
12
+ private readonly withService;
13
+ private readonly paths;
14
+ private readonly kebabName;
15
+ private readonly pascalName;
16
+ private readonly componentClassName;
17
+ private readonly modelClassName;
18
+ private readonly componentNameConstant;
19
+ private readonly modelNameConstant;
20
+ constructor(options: FormComponentGeneratorOptions);
21
+ generate(): Promise<void>;
22
+ private toKebabCase;
23
+ private toPascalCase;
24
+ private readFile;
25
+ private updateTextFile;
26
+ private insertBeforeMarker;
27
+ private ensureImport;
28
+ private appendToObjectLiteral;
29
+ private appendToArrayLiteral;
30
+ private findMatchingDelimiter;
31
+ private generateSailsNgCommonComponentFiles;
32
+ private generateAngularComponentFiles;
33
+ private generateAngularServiceFiles;
34
+ private updateSailsNgCommonDictionaries;
35
+ private updateSailsNgCommonIndex;
36
+ private updateBaseVisitors;
37
+ private updateConcreteVisitors;
38
+ private updateClientVisitor;
39
+ private updateDataValueVisitor;
40
+ private updateJsonTypeDefVisitor;
41
+ private updateValidatorVisitor;
42
+ private updateTemplateVisitor;
43
+ private updateConstructVisitor;
44
+ private updateMigrateVisitor;
45
+ private updateAngularWiring;
46
+ }