@researchdatabox/redbox-dev-tools 0.0.1-beta.10173
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -0
- package/bin/cli.js +3 -0
- package/bin/run-hook-mocha.js +3 -0
- package/bin/run-hook-tsc.js +3 -0
- package/bin/wrapper-webpack.js +168 -0
- package/config/tsconfig.base.json +16 -0
- package/dist/bin/run-hook-mocha.d.ts +1 -0
- package/dist/bin/run-hook-mocha.js +49 -0
- package/dist/bin/run-hook-tsc.d.ts +1 -0
- package/dist/bin/run-hook-tsc.js +19 -0
- package/dist/cli-parsers.d.ts +9 -0
- package/dist/cli-parsers.js +131 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +491 -0
- package/dist/commands/form-config.d.ts +6 -0
- package/dist/commands/form-config.js +404 -0
- package/dist/completion/index.d.ts +1 -0
- package/dist/completion/index.js +218 -0
- package/dist/generators/add-method.d.ts +27 -0
- package/dist/generators/add-method.js +235 -0
- package/dist/generators/angular-app.d.ts +24 -0
- package/dist/generators/angular-app.js +342 -0
- package/dist/generators/angular-service.d.ts +21 -0
- package/dist/generators/angular-service.js +154 -0
- package/dist/generators/controller.d.ts +40 -0
- package/dist/generators/controller.js +195 -0
- package/dist/generators/form-component.d.ts +46 -0
- package/dist/generators/form-component.js +906 -0
- package/dist/generators/form-field.d.ts +15 -0
- package/dist/generators/form-field.js +160 -0
- package/dist/generators/migration.d.ts +15 -0
- package/dist/generators/migration.js +100 -0
- package/dist/generators/model.d.ts +42 -0
- package/dist/generators/model.js +206 -0
- package/dist/generators/service.d.ts +16 -0
- package/dist/generators/service.js +111 -0
- package/dist/runtime-resolver.d.ts +3 -0
- package/dist/runtime-resolver.js +27 -0
- package/dist/templates/hook-archetype.d.ts +12 -0
- package/dist/templates/hook-archetype.js +186 -0
- package/dist/testing.d.ts +7 -0
- package/dist/testing.js +53 -0
- package/dist/utils/config-helper.d.ts +71 -0
- package/dist/utils/config-helper.js +574 -0
- package/dist/utils/generator.d.ts +10 -0
- package/dist/utils/generator.js +68 -0
- package/dist/utils/hook-tsconfig.d.ts +9 -0
- package/dist/utils/hook-tsconfig.js +104 -0
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.js +60 -0
- package/index.js +4 -0
- package/package.json +85 -0
- package/runtime-resolver.d.ts +1 -0
- package/runtime-resolver.js +3 -0
- package/skills/redbox-angular-apps/SKILL.md +42 -0
- package/skills/redbox-angular-services/SKILL.md +29 -0
- package/skills/redbox-controllers/SKILL.md +65 -0
- package/skills/redbox-feature-design-planner/SKILL.md +169 -0
- package/skills/redbox-feature-implementation-review/SKILL.md +117 -0
- package/skills/redbox-form-components/SKILL.md +114 -0
- package/skills/redbox-form-config/SKILL.md +34 -0
- package/skills/redbox-hook-development/SKILL.md +66 -0
- package/skills/redbox-services/SKILL.md +54 -0
- package/skills/redbox-test-verification/SKILL.md +38 -0
- package/skills/redbox-testing/SKILL.md +64 -0
- package/skills/redbox-web-verification/SKILL.md +71 -0
- package/skills/redbox-web-verification/redbox-angular-apps/SKILL.md +48 -0
- package/skills/redbox-web-verification/redbox-angular-services/SKILL.md +30 -0
- package/skills/redbox-web-verification/redbox-controllers/SKILL.md +56 -0
- package/skills/redbox-web-verification/redbox-form-config/SKILL.md +29 -0
- package/skills/redbox-web-verification/redbox-hook-development/SKILL.md +61 -0
- package/skills/redbox-web-verification/redbox-services/SKILL.md +45 -0
- package/skills/redbox-web-verification/redbox-testing/SKILL.md +53 -0
- package/skills/redbox-web-verification/redbox-web-verification/SKILL.md +47 -0
- package/templates/hook-archetype/standard/.gitignore.hbs +4 -0
- package/templates/hook-archetype/standard/.npmignore.hbs +3 -0
- package/templates/hook-archetype/standard/.npmrc.hbs +6 -0
- package/templates/hook-archetype/standard/.nvmrc.hbs +1 -0
- package/templates/hook-archetype/standard/AGENTS.md.hbs +34 -0
- package/templates/hook-archetype/standard/Dockerfile.hbs +28 -0
- package/templates/hook-archetype/standard/README.md.hbs +39 -0
- package/templates/hook-archetype/standard/assets/fonts/.gitkeep +1 -0
- package/templates/hook-archetype/standard/language-defaults/.gitkeep +1 -0
- package/templates/hook-archetype/standard/package.json.hbs +40 -0
- package/templates/hook-archetype/standard/src/api/controllers/ExampleController.ts.hbs +21 -0
- package/templates/hook-archetype/standard/src/api/controllers/index.ts.hbs +23 -0
- package/templates/hook-archetype/standard/src/api/services/ExampleService.ts.hbs +22 -0
- package/templates/hook-archetype/standard/src/api/services/index.ts.hbs +23 -0
- package/templates/hook-archetype/standard/src/config/brandingConfigurationDefaults.ts.hbs +9 -0
- package/templates/hook-archetype/standard/src/config/reusableFormDefinitions.ts.hbs +6 -0
- package/templates/hook-archetype/standard/src/form-config/default-1.0-draft.ts.hbs +20 -0
- package/templates/hook-archetype/standard/src/form-config/index.ts.hbs +8 -0
- package/templates/hook-archetype/standard/src/index.ts.hbs +55 -0
- package/templates/hook-archetype/standard/support/bootstrap-data/.gitkeep +1 -0
- package/templates/hook-archetype/standard/support/development/docker-compose.yml.hbs +75 -0
- package/templates/hook-archetype/standard/support/integration-testing/docker-compose.mocha.yml.hbs +84 -0
- package/templates/hook-archetype/standard/support/integration-testing/run-mocha-hook.sh.hbs +9 -0
- package/templates/hook-archetype/standard/test/integration/hook-registration.test.ts.hbs +13 -0
- package/templates/hook-archetype/standard/test/support/globals.ts.hbs +40 -0
- package/templates/hook-archetype/standard/test/tsconfig.json.hbs +8 -0
- package/templates/hook-archetype/standard/test/unit/.mocharc.cjs.hbs +9 -0
- package/templates/hook-archetype/standard/test/unit/index.test.ts.hbs +40 -0
- package/templates/hook-archetype/standard/tsconfig.json.hbs +15 -0
- package/templates/hook-archetype/standard/views/.gitkeep +1 -0
- package/testing.d.ts +1 -0
- package/testing.js +3 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
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
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const paths_1 = require("./utils/paths");
|
|
41
|
+
const hook_archetype_1 = require("./templates/hook-archetype");
|
|
42
|
+
const form_config_1 = require("./commands/form-config");
|
|
43
|
+
const completion_1 = require("./completion");
|
|
44
|
+
const cli_parsers_1 = require("./cli-parsers");
|
|
45
|
+
const program = new commander_1.Command();
|
|
46
|
+
// List of dependencies that are common in our legacy hooks but should not be in modern hook packages as they are supplied by redbox-core
|
|
47
|
+
const forbiddenHookDeps = ['axios', 'rxjs', 'lodash', 'mocha', 'chai', 'ts-node', 'typescript'];
|
|
48
|
+
const allowedSharedHookDeps = {
|
|
49
|
+
dependencies: new Set(),
|
|
50
|
+
devDependencies: new Set(['@researchdatabox/redbox-dev-tools']),
|
|
51
|
+
peerDependencies: new Set(['@researchdatabox/redbox-core']),
|
|
52
|
+
};
|
|
53
|
+
function readPackageJson(pkgPath) {
|
|
54
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
55
|
+
}
|
|
56
|
+
function writePackageJson(pkgPath, pkg) {
|
|
57
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
58
|
+
}
|
|
59
|
+
function isForbiddenSharedHookDep(section, depName) {
|
|
60
|
+
if (!depName.startsWith('@researchdatabox/')) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return !allowedSharedHookDeps[section].has(depName);
|
|
64
|
+
}
|
|
65
|
+
function migrateHookDependencyContract(pkg) {
|
|
66
|
+
const nextPkg = { ...pkg };
|
|
67
|
+
nextPkg.dependencies = { ...(pkg.dependencies ?? {}) };
|
|
68
|
+
nextPkg.devDependencies = { ...(pkg.devDependencies ?? {}) };
|
|
69
|
+
nextPkg.peerDependencies = { ...(pkg.peerDependencies ?? {}) };
|
|
70
|
+
for (const section of Object.keys(allowedSharedHookDeps)) {
|
|
71
|
+
for (const depName of Object.keys(nextPkg[section] ?? {})) {
|
|
72
|
+
if (isForbiddenSharedHookDep(section, depName)) {
|
|
73
|
+
delete nextPkg[section][depName];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const depName of forbiddenHookDeps) {
|
|
78
|
+
delete nextPkg.dependencies[depName];
|
|
79
|
+
delete nextPkg.devDependencies[depName];
|
|
80
|
+
delete nextPkg.peerDependencies[depName];
|
|
81
|
+
}
|
|
82
|
+
nextPkg.peerDependencies['@researchdatabox/redbox-core'] = nextPkg.peerDependencies['@researchdatabox/redbox-core'] ?? '*';
|
|
83
|
+
nextPkg.devDependencies['@researchdatabox/redbox-dev-tools'] = nextPkg.devDependencies['@researchdatabox/redbox-dev-tools'] ?? '*';
|
|
84
|
+
return nextPkg;
|
|
85
|
+
}
|
|
86
|
+
function findForbiddenHookDeps(pkg) {
|
|
87
|
+
const sections = ['dependencies', 'devDependencies', 'peerDependencies'];
|
|
88
|
+
const findings = [];
|
|
89
|
+
for (const section of sections) {
|
|
90
|
+
const deps = pkg[section] ?? {};
|
|
91
|
+
for (const depName of Object.keys(deps)) {
|
|
92
|
+
if (isForbiddenSharedHookDep(section, depName)) {
|
|
93
|
+
findings.push(`${section}.${depName}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const depName of forbiddenHookDeps) {
|
|
97
|
+
if (deps[depName] != null) {
|
|
98
|
+
findings.push(`${section}.${depName}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return findings;
|
|
103
|
+
}
|
|
104
|
+
program
|
|
105
|
+
.name('redbox-dev-tools')
|
|
106
|
+
.description('CLI for ReDBox hook development and code generation')
|
|
107
|
+
.version('1.0.0')
|
|
108
|
+
.option('--root <path>', 'ReDBox repo root directory (required for generators)')
|
|
109
|
+
.option('--core-types-root <path>', 'Override core-types package root')
|
|
110
|
+
.option('--angular-root <path>', 'Override angular projects root')
|
|
111
|
+
.option('--dry-run', 'Print intended changes without writing files', false);
|
|
112
|
+
function resolveSkillsDir() {
|
|
113
|
+
return path.resolve(__dirname, '..', 'skills');
|
|
114
|
+
}
|
|
115
|
+
program
|
|
116
|
+
.command('completion <shell>')
|
|
117
|
+
.description('Output shell completion script for bash, zsh, fish, or powershell')
|
|
118
|
+
.action((shell) => {
|
|
119
|
+
const normalized = shell.toLowerCase();
|
|
120
|
+
try {
|
|
121
|
+
const script = (0, completion_1.generateCompletionScript)(normalized);
|
|
122
|
+
process.stdout.write(script);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
(0, form_config_1.registerMigrateFormConfigCommand)(program);
|
|
130
|
+
(0, form_config_1.registerMigrateDataClassificationCommand)(program);
|
|
131
|
+
(0, form_config_1.registerMigrateFigshareConfigCommand)(program);
|
|
132
|
+
(0, form_config_1.registerClientFormConfigCommand)(program);
|
|
133
|
+
(0, form_config_1.registerQuestionTreeDiagramCommand)(program);
|
|
134
|
+
program
|
|
135
|
+
.command('init [name]')
|
|
136
|
+
.description('Initialize a new ReDBox hook project from the standard archetype')
|
|
137
|
+
.option('--template <template>', 'Archetype template name', 'standard')
|
|
138
|
+
.option('--description <description>', 'Project description')
|
|
139
|
+
.option('--force', 'Overwrite existing archetype files', false)
|
|
140
|
+
.action((name, options) => {
|
|
141
|
+
if (options.template !== 'standard') {
|
|
142
|
+
console.error(`❌ Unsupported template: ${options.template}. Supported templates: standard`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const cwd = process.cwd();
|
|
146
|
+
const packageName = name || path.basename(cwd);
|
|
147
|
+
console.log(`🚀 Initializing ReDBox hook archetype for ${packageName}...\n`);
|
|
148
|
+
const result = (0, hook_archetype_1.generateHookArchetype)({
|
|
149
|
+
cwd,
|
|
150
|
+
packageName,
|
|
151
|
+
description: options.description,
|
|
152
|
+
force: options.force,
|
|
153
|
+
templateName: options.template,
|
|
154
|
+
});
|
|
155
|
+
for (const createdPath of result.created) {
|
|
156
|
+
console.log(`✅ Created ${createdPath}`);
|
|
157
|
+
}
|
|
158
|
+
for (const skippedPath of result.skipped) {
|
|
159
|
+
console.log(`ℹ️ Skipped existing ${skippedPath}`);
|
|
160
|
+
}
|
|
161
|
+
console.log('\n✨ Archetype ready!\n');
|
|
162
|
+
console.log('Next steps:');
|
|
163
|
+
console.log(' 1. Install dependencies: npm install');
|
|
164
|
+
console.log(' 2. Compile TypeScript: npm run compile');
|
|
165
|
+
console.log(' 3. Run unit tests: npm run test:unit');
|
|
166
|
+
console.log(' 4. Start tailoring the hook in src/, test/, and support/\n');
|
|
167
|
+
});
|
|
168
|
+
program
|
|
169
|
+
.command('check')
|
|
170
|
+
.description('Validate that a hook uses the shared Redbox dependency contract')
|
|
171
|
+
.action(() => {
|
|
172
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
173
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
174
|
+
console.error('❌ package.json not found in the current directory.');
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
const pkg = readPackageJson(packageJsonPath);
|
|
178
|
+
const findings = findForbiddenHookDeps(pkg);
|
|
179
|
+
if (findings.length === 0) {
|
|
180
|
+
console.log('✅ Hook dependency contract looks good.');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
console.error('❌ Hook dependency contract violations found:');
|
|
184
|
+
for (const finding of findings) {
|
|
185
|
+
console.error(` - ${finding}`);
|
|
186
|
+
}
|
|
187
|
+
console.error('Shared runtime dependencies should come from @researchdatabox/redbox-core, and shared toolchain dependencies should come from @researchdatabox/redbox-dev-tools.');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|
|
190
|
+
program
|
|
191
|
+
.command('migrate-hook-dependencies')
|
|
192
|
+
.description('Rewrite the current hook package.json to the minimal shared dependency contract')
|
|
193
|
+
.action(() => {
|
|
194
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
195
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
196
|
+
console.error('❌ package.json not found in the current directory.');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
const pkg = readPackageJson(packageJsonPath);
|
|
200
|
+
const migratedPkg = migrateHookDependencyContract(pkg);
|
|
201
|
+
writePackageJson(packageJsonPath, migratedPkg);
|
|
202
|
+
console.log('✅ Updated package.json to use the minimal shared hook dependency contract.');
|
|
203
|
+
});
|
|
204
|
+
const generate = program.command('generate').alias('g').description('Generate code components');
|
|
205
|
+
generate
|
|
206
|
+
.command('controller <name>')
|
|
207
|
+
.description('Generate a new ReDBox controller')
|
|
208
|
+
.option('--actions <actions>', 'Comma-separated list of actions', val => val.split(','))
|
|
209
|
+
.option('--webservice', 'Generate as a webservice controller', false)
|
|
210
|
+
.option('--class-name <className>', 'Override controller class name (defaults to name without Controller suffix)')
|
|
211
|
+
.option('--route <route>', 'Single route (deprecated, use --routes)')
|
|
212
|
+
.option('--routes <routes>', 'Action-to-route mappings: action:verb:path[:role1:role2],... (e.g., list:get:/api/items:Admin:Researcher,delete:delete:/api/items/:id:Admin)', cli_parsers_1.parseRoutes)
|
|
213
|
+
.option('--nav <nav>', 'Navigation mappings: action:type[:containerId]:labelKey[:itemId],... (e.g., list:menu:plan:create-rdmp:plan-create)', cli_parsers_1.parseNavMappings)
|
|
214
|
+
.option('--lang <lang>', 'Language defaults: key=value[:lang],... (e.g., menu-custom=Custom menu:en)', cli_parsers_1.parseLanguageDefaults)
|
|
215
|
+
.option('--auth <roles>', 'Default roles for auth rules (can be overridden per-route)', val => val.split(','))
|
|
216
|
+
.action(async (name, options) => {
|
|
217
|
+
try {
|
|
218
|
+
const globalOptions = program.opts();
|
|
219
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
220
|
+
const { ControllerGenerator } = require('./generators/controller');
|
|
221
|
+
const generator = new ControllerGenerator({
|
|
222
|
+
name,
|
|
223
|
+
actions: options.actions,
|
|
224
|
+
webservice: options.webservice,
|
|
225
|
+
route: options.route,
|
|
226
|
+
routes: options.routes,
|
|
227
|
+
auth: options.auth,
|
|
228
|
+
navigation: options.nav,
|
|
229
|
+
languageDefaults: options.lang,
|
|
230
|
+
className: options.className,
|
|
231
|
+
dryRun: globalOptions.dryRun,
|
|
232
|
+
root: paths.root,
|
|
233
|
+
paths,
|
|
234
|
+
});
|
|
235
|
+
console.log(`\n🛠️ Generating controller: ${name}...\n`);
|
|
236
|
+
await generator.generate();
|
|
237
|
+
console.log('\n✅ Done!\n');
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
generate
|
|
245
|
+
.command('service <name>')
|
|
246
|
+
.description('Generate a new ReDBox service')
|
|
247
|
+
.option('--methods <methods>', 'Comma-separated list of methods', val => val.split(','))
|
|
248
|
+
.action(async (name, options) => {
|
|
249
|
+
try {
|
|
250
|
+
const globalOptions = program.opts();
|
|
251
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
252
|
+
const { ServiceGenerator } = require('./generators/service');
|
|
253
|
+
const generator = new ServiceGenerator({
|
|
254
|
+
name,
|
|
255
|
+
methods: options.methods,
|
|
256
|
+
dryRun: globalOptions.dryRun,
|
|
257
|
+
root: paths.root,
|
|
258
|
+
paths,
|
|
259
|
+
});
|
|
260
|
+
console.log(`\n🛠️ Generating service: ${name}...\n`);
|
|
261
|
+
await generator.generate();
|
|
262
|
+
console.log('\n✅ Done!\n');
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
generate
|
|
270
|
+
.command('method')
|
|
271
|
+
.description('Add a method to an existing controller or service')
|
|
272
|
+
.requiredOption('--file <path>', 'Path to existing controller or service file')
|
|
273
|
+
.requiredOption('--method <name>', 'Method name to add')
|
|
274
|
+
.option('--route <route>', 'Route path (for controllers)')
|
|
275
|
+
.option('--http <verb>', 'HTTP verb (GET, POST, etc.)')
|
|
276
|
+
.option('--auth <roles>', 'Comma-separated list of roles', val => val.split(','))
|
|
277
|
+
.option('--nav <nav>', 'Navigation mappings: type[:containerId]:labelKey[:itemId],... (e.g., menu:plan:create-rdmp:plan-create)', cli_parsers_1.parseNavMappings)
|
|
278
|
+
.option('--lang <lang>', 'Language defaults: key=value[:lang],... (e.g., menu-custom=Custom menu:en)', cli_parsers_1.parseLanguageDefaults)
|
|
279
|
+
.action(async (options) => {
|
|
280
|
+
try {
|
|
281
|
+
const globalOptions = program.opts();
|
|
282
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
283
|
+
const { AddMethodGenerator } = require('./generators/add-method');
|
|
284
|
+
const generator = new AddMethodGenerator({
|
|
285
|
+
file: options.file,
|
|
286
|
+
method: options.method,
|
|
287
|
+
route: options.route,
|
|
288
|
+
http: options.http,
|
|
289
|
+
auth: options.auth,
|
|
290
|
+
navigation: options.nav,
|
|
291
|
+
languageDefaults: options.lang,
|
|
292
|
+
dryRun: globalOptions.dryRun,
|
|
293
|
+
root: paths.root,
|
|
294
|
+
paths,
|
|
295
|
+
});
|
|
296
|
+
console.log(`\n🛠️ Adding method ${options.method} to ${options.file}...\n`);
|
|
297
|
+
await generator.generate();
|
|
298
|
+
console.log('\n✅ Done!\n');
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
generate
|
|
306
|
+
.command('angular-app <name>')
|
|
307
|
+
.description('Scaffold a new Angular app for embedded EJS usage')
|
|
308
|
+
.requiredOption('--ejs-view <view>', 'EJS view name')
|
|
309
|
+
.option('--auth <roles>', 'Comma-separated list of roles', val => val.split(','))
|
|
310
|
+
.action(async (name, options) => {
|
|
311
|
+
try {
|
|
312
|
+
const globalOptions = program.opts();
|
|
313
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
314
|
+
const { AngularAppGenerator } = require('./generators/angular-app');
|
|
315
|
+
const generator = new AngularAppGenerator({
|
|
316
|
+
name,
|
|
317
|
+
ejsView: options.ejsView,
|
|
318
|
+
auth: options.auth,
|
|
319
|
+
dryRun: globalOptions.dryRun,
|
|
320
|
+
root: paths.root,
|
|
321
|
+
paths,
|
|
322
|
+
});
|
|
323
|
+
console.log(`\n🛠️ Generating Angular app: ${name}...\n`);
|
|
324
|
+
await generator.generate();
|
|
325
|
+
console.log('\n✅ Done!\n');
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
generate
|
|
333
|
+
.command('angular-service <name>')
|
|
334
|
+
.description('Generate a new Angular service extending HttpClientService')
|
|
335
|
+
.requiredOption('--app <app>', 'Target Angular app name')
|
|
336
|
+
.option('--methods <methods>', 'Comma-separated list of methods', val => val.split(','))
|
|
337
|
+
.action(async (name, options) => {
|
|
338
|
+
try {
|
|
339
|
+
const globalOptions = program.opts();
|
|
340
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
341
|
+
const { AngularServiceGenerator } = require('./generators/angular-service');
|
|
342
|
+
const generator = new AngularServiceGenerator({
|
|
343
|
+
name,
|
|
344
|
+
app: options.app,
|
|
345
|
+
methods: options.methods,
|
|
346
|
+
dryRun: globalOptions.dryRun,
|
|
347
|
+
root: paths.root,
|
|
348
|
+
paths,
|
|
349
|
+
});
|
|
350
|
+
console.log(`\n🛠️ Generating Angular service: ${name} for app ${options.app}...\n`);
|
|
351
|
+
await generator.generate();
|
|
352
|
+
console.log('\n✅ Done!\n');
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
generate
|
|
360
|
+
.command('form-component <name>')
|
|
361
|
+
.description('Generate scaffold for a new form component across Angular and sails-ng-common')
|
|
362
|
+
.option('--app <app>', 'Target Angular app name', 'form')
|
|
363
|
+
.option('--with-service', 'Also generate a companion Angular HttpClient service', false)
|
|
364
|
+
.action(async (name, options) => {
|
|
365
|
+
try {
|
|
366
|
+
const globalOptions = program.opts();
|
|
367
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
368
|
+
const { FormComponentGenerator } = require('./generators/form-component');
|
|
369
|
+
const generator = new FormComponentGenerator({
|
|
370
|
+
name,
|
|
371
|
+
app: options.app,
|
|
372
|
+
withService: options.withService,
|
|
373
|
+
dryRun: globalOptions.dryRun,
|
|
374
|
+
root: paths.root,
|
|
375
|
+
paths,
|
|
376
|
+
});
|
|
377
|
+
console.log(`\n🛠️ Generating form component scaffold: ${name}...\n`);
|
|
378
|
+
await generator.generate();
|
|
379
|
+
console.log('\n✅ Done!\n');
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
generate
|
|
387
|
+
.command('migration <description>')
|
|
388
|
+
.description('Generate an app-local data migration in api/migrations/ (runs at lift, before coreBootstrap)')
|
|
389
|
+
.action(async (description) => {
|
|
390
|
+
try {
|
|
391
|
+
const globalOptions = program.opts();
|
|
392
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
393
|
+
const { MigrationGenerator } = require('./generators/migration');
|
|
394
|
+
const generator = new MigrationGenerator({
|
|
395
|
+
description,
|
|
396
|
+
dryRun: globalOptions.dryRun,
|
|
397
|
+
root: paths.root,
|
|
398
|
+
});
|
|
399
|
+
console.log(`\n🛠️ Generating data migration: ${description}...\n`);
|
|
400
|
+
await generator.generate();
|
|
401
|
+
console.log(`\n✅ Done! Created migration '${generator.migrationName}'.\n`);
|
|
402
|
+
console.log('Next steps:');
|
|
403
|
+
console.log(` 1. Implement up() in ${path.relative(paths.root, generator.migrationPath)} (remove the placeholder throw)`);
|
|
404
|
+
console.log(' 2. Make sure up() is idempotent - it can run more than once');
|
|
405
|
+
console.log(' 3. Test by lifting the app, then verify the Migration table contains the new name\n');
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
generate
|
|
413
|
+
.command('form-field <name>')
|
|
414
|
+
.description('Generate a new form field DSL configuration')
|
|
415
|
+
.requiredOption('--type <type>', 'Record type')
|
|
416
|
+
.action(async (name, options) => {
|
|
417
|
+
try {
|
|
418
|
+
const globalOptions = program.opts();
|
|
419
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
420
|
+
const { FormFieldGenerator } = require('./generators/form-field');
|
|
421
|
+
const generator = new FormFieldGenerator({
|
|
422
|
+
name,
|
|
423
|
+
type: options.type,
|
|
424
|
+
dryRun: globalOptions.dryRun,
|
|
425
|
+
root: paths.root,
|
|
426
|
+
});
|
|
427
|
+
console.log(`\n🛠️ Generating form field config: ${name}...\n`);
|
|
428
|
+
await generator.generate();
|
|
429
|
+
console.log('\n✅ Done!\n');
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
program
|
|
437
|
+
.command('install-skills')
|
|
438
|
+
.alias('skills')
|
|
439
|
+
.description('Install bundled agent skills using the vercel-labs skills CLI')
|
|
440
|
+
.allowUnknownOption(true)
|
|
441
|
+
.allowExcessArguments(true)
|
|
442
|
+
.action(() => {
|
|
443
|
+
const skillsDir = resolveSkillsDir();
|
|
444
|
+
if (!fs.existsSync(skillsDir)) {
|
|
445
|
+
console.error(`Skills directory not found: ${skillsDir}`);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
const rawArgs = process.argv.slice(2);
|
|
449
|
+
const cmdIndex = rawArgs.findIndex(arg => arg === 'install-skills' || arg === 'skills');
|
|
450
|
+
const forwardedArgs = cmdIndex === -1 ? [] : rawArgs.slice(cmdIndex + 1);
|
|
451
|
+
const result = (0, child_process_1.spawnSync)('npx', ['skills', 'add', skillsDir, ...forwardedArgs], {
|
|
452
|
+
stdio: 'inherit',
|
|
453
|
+
shell: process.platform === 'win32',
|
|
454
|
+
});
|
|
455
|
+
if (result.error) {
|
|
456
|
+
console.error('Failed to run npx skills:', result.error.message);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
460
|
+
});
|
|
461
|
+
generate
|
|
462
|
+
.command('model <name>')
|
|
463
|
+
.description('Generate a new Waterline model with decorator-based definition')
|
|
464
|
+
.option('--identity <identity>', 'Waterline identity (table name), defaults to lowercase name')
|
|
465
|
+
.option('--attrs <attrs>', 'Comma-separated list of attributes in format name:type[:required][:unique]', cli_parsers_1.parseAttributes)
|
|
466
|
+
.option('--belongs-to <associations>', 'Comma-separated belongsTo associations in format name:model', cli_parsers_1.parseBelongsTo)
|
|
467
|
+
.option('--has-many <associations>', 'Comma-separated hasMany associations in format name:model:via[:dominant]', cli_parsers_1.parseHasMany)
|
|
468
|
+
.action(async (name, options) => {
|
|
469
|
+
try {
|
|
470
|
+
const globalOptions = program.opts();
|
|
471
|
+
const paths = (0, paths_1.resolvePaths)(globalOptions);
|
|
472
|
+
const { ModelGenerator } = require('./generators/model');
|
|
473
|
+
const generator = new ModelGenerator({
|
|
474
|
+
name,
|
|
475
|
+
identity: options.identity,
|
|
476
|
+
attributes: options.attrs,
|
|
477
|
+
associations: [...(options.belongsTo || []), ...(options.hasMany || [])],
|
|
478
|
+
dryRun: globalOptions.dryRun,
|
|
479
|
+
root: paths.root,
|
|
480
|
+
paths,
|
|
481
|
+
});
|
|
482
|
+
console.log(`\n🛠️ Generating Waterline model: ${name}...\n`);
|
|
483
|
+
await generator.generate();
|
|
484
|
+
console.log('\n✅ Done!\n');
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
program.parse();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function registerMigrateFormConfigCommand(program: Command): void;
|
|
3
|
+
export declare function registerMigrateDataClassificationCommand(program: Command): void;
|
|
4
|
+
export declare function registerClientFormConfigCommand(program: Command): void;
|
|
5
|
+
export declare function registerQuestionTreeDiagramCommand(program: Command): void;
|
|
6
|
+
export declare function registerMigrateFigshareConfigCommand(program: Command): void;
|