devendra-skill-package 1.0.2
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/.github/skills/boiler-project-generator/SKILL.md +103 -0
- package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
- package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
- package/.github/skills/boiler-project-generator/references/android.md +19 -0
- package/.github/skills/boiler-project-generator/references/angular.md +14 -0
- package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
- package/.github/skills/boiler-project-generator/references/ios.md +17 -0
- package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
- package/.github/skills/boiler-project-generator/references/react.md +17 -0
- package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
- package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
- package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
- package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
- package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
- package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
- package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
- package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
- package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
- package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
- package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
- package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
- package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
- package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
- package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
- package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
- package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
- package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
- package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
- package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
- package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
- package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
- package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
- package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
- package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
- package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
- package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
- package/.github/skills/custom-font-integrator/SKILL.md +373 -0
- package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
- package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
- package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
- package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
- package/.github/skills/encryption-decryption-utility/README.md +222 -0
- package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
- package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
- package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
- package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
- package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
- package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
- package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
- package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
- package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
- package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
- package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
- package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
- package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
- package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
- package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
- package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
- package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
- package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
- package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
- package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
- package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
- package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
- package/.github/skills/encryption-decryption-utility/template.md +241 -0
- package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
- package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
- package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
- package/.github/skills/feature-generator/SKILL.MD +2741 -0
- package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
- package/.github/skills/feature-generator/assets/spec-template.md +172 -0
- package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
- package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
- package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
- package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
- package/.github/skills/figma-ui-mapper/README.md +34 -0
- package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
- package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
- package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
- package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
- package/.github/skills/figma-ui-mapper/references/android.md +11 -0
- package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
- package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
- package/.github/skills/figma-ui-mapper/references/common.md +23 -0
- package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
- package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
- package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
- package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
- package/.github/skills/figma-ui-mapper/references/next.md +18 -0
- package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
- package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react.md +18 -0
- package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
- package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
- package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
- package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
- package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
- package/.github/skills/md-file-converter/SKILL.md +291 -0
- package/.github/skills/md-file-converter/config/formats.json +114 -0
- package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
- package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
- package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
- package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
- package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
- package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
- package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
- package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
- package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
- package/.github/skills/md-file-converter/templates/output.md +73 -0
- package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
- package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
- package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
- package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
- package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
- package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
- package/.github/skills/test-case-validator/README.md +111 -0
- package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
- package/.github/skills/test-case-validator/SKILL.md +209 -0
- package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
- package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
- package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
- package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
- package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
- package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
- package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
- package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
- package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
- package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
- package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
- package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
- package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
- package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
- package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
- package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
- package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
- package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
- package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
- package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
- package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
- package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
- package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
- package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
- package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
- package/.github/skills/validation-engine-builder/README.md +50 -0
- package/.github/skills/validation-engine-builder/SKILL.md +241 -0
- package/.github/skills/validation-engine-builder/references/core.md +158 -0
- package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
- package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
- package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
- package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
- package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
- package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
- package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
- package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
- package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
- package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
- package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
- package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
- package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
- package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
- package/.github/skills/validation-engine-builder/template.md +73 -0
- package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
- package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
- package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
- package/bin/index.js +145 -0
- package/package.json +22 -0
- package/readme.md +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Angular has no react-hook-form/Yup equivalent to reuse — react-hook-form is
|
|
6
|
+
* a React library and cannot run in Angular at all. This generator targets
|
|
7
|
+
* Angular's own framework-native Reactive Forms (Validators + FormBuilder)
|
|
8
|
+
* instead of porting the Yup/react-hook-form pattern.
|
|
9
|
+
*
|
|
10
|
+
* Output (5 files, same count/role as the RN/React generators):
|
|
11
|
+
* validation-messages.ts — via shared/ts-messages-regex.js (identical shape)
|
|
12
|
+
* validation-regex.ts — via shared/ts-messages-regex.js (identical shape)
|
|
13
|
+
* pattern-with-message.validator.ts — custom ValidatorFn carrying a message key
|
|
14
|
+
* validators.ts — per-schema Validators[] arrays, replaces schemas.ts
|
|
15
|
+
* validation-error.component.ts — replaces ValidationError.tsx; also replaces the
|
|
16
|
+
* RN/React "hook" role since Angular's own
|
|
17
|
+
* FormBuilder already serves that purpose
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const { readTemplate, ensureDir, relativeImportPath } = require('./shared/util.js');
|
|
24
|
+
const { genMessages, genRegex } = require('./shared/ts-messages-regex.js');
|
|
25
|
+
|
|
26
|
+
// ── pattern-with-message.validator.ts ───────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function genPatternValidator(outDir) {
|
|
29
|
+
const content = [
|
|
30
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
31
|
+
``,
|
|
32
|
+
`import type { AbstractControl, ValidatorFn } from '@angular/forms';`,
|
|
33
|
+
``,
|
|
34
|
+
`/**`,
|
|
35
|
+
` * Regex validator that reports a specific message key on failure (instead of`,
|
|
36
|
+
` * Angular's built-in 'pattern' key), so ValidationErrorComponent can look up`,
|
|
37
|
+
` * the exact VALIDATION_MESSAGES entry configured in schemas.json.`,
|
|
38
|
+
` */`,
|
|
39
|
+
`export function patternWithMessage(regex: RegExp, messageKey: string): ValidatorFn {`,
|
|
40
|
+
` return (control: AbstractControl) => {`,
|
|
41
|
+
` if (control.value === null || control.value === undefined || control.value === '') return null;`,
|
|
42
|
+
` return regex.test(control.value) ? null : { [messageKey]: true };`,
|
|
43
|
+
` };`,
|
|
44
|
+
`}`,
|
|
45
|
+
].join('\n');
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(path.join(outDir, 'pattern-with-message.validator.ts'), content);
|
|
48
|
+
console.log(' ✅ pattern-with-message.validator.ts');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── validators.ts (replaces schemas.ts — Angular Validators, not Yup) ───────────
|
|
52
|
+
|
|
53
|
+
function buildFieldValidators(fieldName, rules) {
|
|
54
|
+
const parts = [];
|
|
55
|
+
|
|
56
|
+
if (rules.required) parts.push('Validators.required');
|
|
57
|
+
|
|
58
|
+
if (rules.min !== undefined) {
|
|
59
|
+
parts.push(rules.type === 'number' ? `Validators.min(${rules.min})` : `Validators.minLength(${rules.min})`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (rules.max !== undefined) {
|
|
63
|
+
parts.push(rules.type === 'number' ? `Validators.max(${rules.max})` : `Validators.maxLength(${rules.max})`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (rules.regex) {
|
|
67
|
+
const msgKey = rules.regexMessage || rules.regex;
|
|
68
|
+
parts.push(`patternWithMessage(VALIDATION_REGEX.${rules.regex}, '${msgKey}')`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// `nullable` in schemas.json means "no required validator" — Angular has no
|
|
72
|
+
// separate nullability validator; the absence of Validators.required already
|
|
73
|
+
// permits an empty value, so nothing is emitted for this flag.
|
|
74
|
+
|
|
75
|
+
return `[${parts.join(', ')}]`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function genValidators(schemasRaw, outDir) {
|
|
79
|
+
const schemaBlocks = Object.entries(schemasRaw).map(([schemaName, fields]) => {
|
|
80
|
+
const fieldLines = Object.entries(fields).map(([fieldName, rules]) =>
|
|
81
|
+
` ${fieldName}: ${buildFieldValidators(fieldName, rules)},`
|
|
82
|
+
);
|
|
83
|
+
return [
|
|
84
|
+
` ${schemaName}: {`,
|
|
85
|
+
...fieldLines,
|
|
86
|
+
` },`,
|
|
87
|
+
].join('\n');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const content = [
|
|
91
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
92
|
+
`// Edit templates/schemas.json and re-run apply-to-project.js`,
|
|
93
|
+
``,
|
|
94
|
+
`import { Validators, type ValidatorFn } from '@angular/forms';`,
|
|
95
|
+
`import { VALIDATION_REGEX } from './validation-regex';`,
|
|
96
|
+
`import { patternWithMessage } from './pattern-with-message.validator';`,
|
|
97
|
+
``,
|
|
98
|
+
`export const validators: Record<string, Record<string, ValidatorFn[]>> = {`,
|
|
99
|
+
...schemaBlocks,
|
|
100
|
+
`};`,
|
|
101
|
+
``,
|
|
102
|
+
`export type SchemaKey = keyof typeof validators;`,
|
|
103
|
+
].join('\n');
|
|
104
|
+
|
|
105
|
+
fs.writeFileSync(path.join(outDir, 'validators.ts'), content);
|
|
106
|
+
console.log(' ✅ validators.ts');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── validation-error.component.ts ───────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
function genValidationErrorComponent(outDir) {
|
|
112
|
+
const content = [
|
|
113
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
114
|
+
``,
|
|
115
|
+
`import { Component, Input } from '@angular/core';`,
|
|
116
|
+
`import type { AbstractControl } from '@angular/forms';`,
|
|
117
|
+
`import { VALIDATION_MESSAGES, msg } from './validation-messages';`,
|
|
118
|
+
``,
|
|
119
|
+
`/**`,
|
|
120
|
+
` * Displays the first active validation error for a control, mapped to the`,
|
|
121
|
+
` * centralized VALIDATION_MESSAGES. Covers Angular's built-in error keys`,
|
|
122
|
+
` * (required, minlength, maxlength, min, max) plus any custom message-key`,
|
|
123
|
+
` * error set by patternWithMessage().`,
|
|
124
|
+
` *`,
|
|
125
|
+
` * Usage (template):`,
|
|
126
|
+
` * <app-validation-error [control]="form.get('email')" label="Email" />`,
|
|
127
|
+
` */`,
|
|
128
|
+
`@Component({`,
|
|
129
|
+
` selector: 'app-validation-error',`,
|
|
130
|
+
` standalone: true,`,
|
|
131
|
+
` template: \`<span class="validation-error" *ngIf="message">{{ message }}</span>\`,`,
|
|
132
|
+
`})`,
|
|
133
|
+
`export class ValidationErrorComponent {`,
|
|
134
|
+
` @Input() control: AbstractControl | null = null;`,
|
|
135
|
+
` @Input() label = '';`,
|
|
136
|
+
``,
|
|
137
|
+
` get message(): string | null {`,
|
|
138
|
+
` const control = this.control;`,
|
|
139
|
+
` if (!control || !control.errors || !(control.dirty || control.touched)) return null;`,
|
|
140
|
+
` const errors = control.errors;`,
|
|
141
|
+
``,
|
|
142
|
+
` if (errors['required']) return msg.required(this.label);`,
|
|
143
|
+
` if (errors['minlength']) return msg.minLength(errors['minlength'].requiredLength);`,
|
|
144
|
+
` if (errors['maxlength']) return msg.maxLength(errors['maxlength'].requiredLength);`,
|
|
145
|
+
` if (errors['min']) return msg.minNumber(errors['min'].min);`,
|
|
146
|
+
` if (errors['max']) return msg.maxNumber(errors['max'].max);`,
|
|
147
|
+
``,
|
|
148
|
+
` const customKey = Object.keys(errors).find((key) => key in VALIDATION_MESSAGES);`,
|
|
149
|
+
` if (customKey) return (VALIDATION_MESSAGES as Record<string, string>)[customKey];`,
|
|
150
|
+
``,
|
|
151
|
+
` return VALIDATION_MESSAGES.invalidFormat;`,
|
|
152
|
+
` }`,
|
|
153
|
+
`}`,
|
|
154
|
+
].join('\n');
|
|
155
|
+
|
|
156
|
+
fs.writeFileSync(path.join(outDir, 'validation-error.component.ts'), content);
|
|
157
|
+
console.log(' ✅ validation-error.component.ts');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Page scaffold ─────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
function toPascalCase(kebab) {
|
|
163
|
+
return kebab.split('-').filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join('');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function generateScreen(projectRoot, pageName, schemaKey, pagePath, schemasRaw) {
|
|
167
|
+
const fields = schemasRaw[schemaKey];
|
|
168
|
+
if (!fields) {
|
|
169
|
+
console.log(`\n ⚠️ Schema "${schemaKey}" not found in schemas.json.`);
|
|
170
|
+
console.log(` Available schemas: ${Object.keys(schemasRaw).join(', ')}`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const className = `${toPascalCase(pageName)}Component`;
|
|
175
|
+
const pageDir = path.join(projectRoot, pagePath, pageName);
|
|
176
|
+
ensureDir(pageDir);
|
|
177
|
+
|
|
178
|
+
const validationOutDir = path.join(projectRoot, 'src', 'app', 'validation');
|
|
179
|
+
const validationImportPath = relativeImportPath(pageDir, validationOutDir);
|
|
180
|
+
|
|
181
|
+
const formGroupLines = Object.keys(fields).map(
|
|
182
|
+
(fieldName) => ` ${fieldName}: ['', validators.${schemaKey}.${fieldName}],`
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const templateFields = Object.entries(fields).map(([fieldName, rules]) => {
|
|
186
|
+
const label = rules.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
187
|
+
const inputType = ['password', 'confirmPassword', 'newPassword', 'currentPassword'].includes(fieldName)
|
|
188
|
+
? 'password'
|
|
189
|
+
: (rules.type === 'number' ? 'number' : 'text');
|
|
190
|
+
return [
|
|
191
|
+
` <div class="field">`,
|
|
192
|
+
` <label for="${fieldName}">${label}</label>`,
|
|
193
|
+
` <input id="${fieldName}" type="${inputType}" formControlName="${fieldName}" />`,
|
|
194
|
+
` <app-validation-error [control]="form.get('${fieldName}')" label="${label}"></app-validation-error>`,
|
|
195
|
+
` </div>`,
|
|
196
|
+
].join('\n');
|
|
197
|
+
}).join('\n');
|
|
198
|
+
|
|
199
|
+
const componentContent = [
|
|
200
|
+
`// Auto-generated scaffold — wire up submit logic before shipping.`,
|
|
201
|
+
``,
|
|
202
|
+
`import { Component } from '@angular/core';`,
|
|
203
|
+
`import { FormBuilder, ReactiveFormsModule, type FormGroup } from '@angular/forms';`,
|
|
204
|
+
`import { validators } from '${validationImportPath}/validators';`,
|
|
205
|
+
`import { ValidationErrorComponent } from '${validationImportPath}/validation-error.component';`,
|
|
206
|
+
``,
|
|
207
|
+
`@Component({`,
|
|
208
|
+
` selector: 'app-${pageName}',`,
|
|
209
|
+
` standalone: true,`,
|
|
210
|
+
` imports: [ReactiveFormsModule, ValidationErrorComponent],`,
|
|
211
|
+
` templateUrl: './${pageName}.component.html',`,
|
|
212
|
+
` styleUrl: './${pageName}.component.scss',`,
|
|
213
|
+
`})`,
|
|
214
|
+
`export class ${className} {`,
|
|
215
|
+
` // Built in the constructor body, not as a field initializer — a field`,
|
|
216
|
+
` // initializer runs before constructor parameter properties are assigned,`,
|
|
217
|
+
` // so 'this.fb' would still be undefined at that point.`,
|
|
218
|
+
` readonly form: FormGroup;`,
|
|
219
|
+
``,
|
|
220
|
+
` constructor(private readonly fb: FormBuilder) {`,
|
|
221
|
+
` this.form = this.fb.group({`,
|
|
222
|
+
...formGroupLines.map((l) => ` ${l}`),
|
|
223
|
+
` });`,
|
|
224
|
+
` }`,
|
|
225
|
+
``,
|
|
226
|
+
` onSubmit(): void {`,
|
|
227
|
+
` if (this.form.invalid) {`,
|
|
228
|
+
` this.form.markAllAsTouched();`,
|
|
229
|
+
` return;`,
|
|
230
|
+
` }`,
|
|
231
|
+
` console.log('${className} submit:', this.form.value);`,
|
|
232
|
+
` // TODO: Add your submit logic here`,
|
|
233
|
+
` }`,
|
|
234
|
+
`}`,
|
|
235
|
+
].join('\n');
|
|
236
|
+
|
|
237
|
+
const templateContent = [
|
|
238
|
+
`<form [formGroup]="form" (ngSubmit)="onSubmit()">`,
|
|
239
|
+
templateFields,
|
|
240
|
+
` <button type="submit">Submit</button>`,
|
|
241
|
+
`</form>`,
|
|
242
|
+
].join('\n');
|
|
243
|
+
|
|
244
|
+
fs.writeFileSync(path.join(pageDir, `${pageName}.component.ts`), componentContent);
|
|
245
|
+
fs.writeFileSync(path.join(pageDir, `${pageName}.component.html`), templateContent);
|
|
246
|
+
fs.writeFileSync(path.join(pageDir, `${pageName}.component.scss`), `.field {\n margin-bottom: 16px;\n}\n\n.validation-error {\n color: #DC2626;\n font-size: 12px;\n display: block;\n margin-top: 4px;\n}\n`);
|
|
247
|
+
console.log(` ✅ ${pageName}.component.ts/.html/.scss → ${pagePath}/${pageName}/`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Main export ───────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
function generate(projectRoot) {
|
|
253
|
+
const outDir = path.join(projectRoot, 'src', 'app', 'validation');
|
|
254
|
+
ensureDir(outDir);
|
|
255
|
+
|
|
256
|
+
console.log('\n📁 Generating src/app/validation/ (Angular) ...');
|
|
257
|
+
|
|
258
|
+
const messages = readTemplate('messages.json');
|
|
259
|
+
const regexMap = readTemplate('regex.json');
|
|
260
|
+
const schemasRaw = readTemplate('schemas.json');
|
|
261
|
+
|
|
262
|
+
genMessages(messages, outDir, 'validation-messages.ts');
|
|
263
|
+
genRegex(regexMap, outDir, 'validation-regex.ts');
|
|
264
|
+
genPatternValidator(outDir);
|
|
265
|
+
genValidators(schemasRaw, outDir);
|
|
266
|
+
genValidationErrorComponent(outDir);
|
|
267
|
+
|
|
268
|
+
return schemasRaw;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = { generate, generateScreen };
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Flutter cannot run any JS library (no Yup, no react-hook-form) — this
|
|
6
|
+
* generator emits pure Dart. Flutter's own Form + TextFormField.validator
|
|
7
|
+
* mechanism already displays the returned string under the field, so unlike
|
|
8
|
+
* the other 3 platforms there is no separate "ValidationError" file to
|
|
9
|
+
* generate — see references/platform-flutter.md for why.
|
|
10
|
+
*
|
|
11
|
+
* Output (3 files — fewer than the other platforms because Flutter needs no
|
|
12
|
+
* separate hook file and no separate error-display file):
|
|
13
|
+
* validation_messages.dart
|
|
14
|
+
* validation_regex.dart
|
|
15
|
+
* validators.dart — one class per schema, static `String? Function(String?)` per field
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
const { readTemplate, ensureDir, relativeDartImportPath } = require('./shared/util.js');
|
|
22
|
+
|
|
23
|
+
// ── Dart literal helpers ─────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
function dartStringLiteral(str) {
|
|
26
|
+
const escaped = String(str).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\$/g, '\\$');
|
|
27
|
+
return `'${escaped}'`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function dartRawRegexLiteral(pattern) {
|
|
31
|
+
if (!pattern.includes("'")) return `r'${pattern}'`;
|
|
32
|
+
if (!pattern.includes('"')) return `r"${pattern}"`;
|
|
33
|
+
// Pattern contains both quote types — fall back to a non-raw, fully escaped string.
|
|
34
|
+
const escaped = pattern.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\$/g, '\\$');
|
|
35
|
+
return `'${escaped}'`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toPascalCase(camelOrSnake) {
|
|
39
|
+
return camelOrSnake
|
|
40
|
+
.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
|
|
41
|
+
.replace(/^[a-z]/, (c) => c.toUpperCase());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── validation_messages.dart ─────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const INTERPOLATED_HELPERS = {
|
|
47
|
+
required: { param: 'String field', argName: 'field', argType: 'String' },
|
|
48
|
+
minLength: { param: 'int min', argName: 'min', argType: 'int' },
|
|
49
|
+
maxLength: { param: 'int max', argName: 'max', argType: 'int' },
|
|
50
|
+
minNumber: { param: 'num min', argName: 'min', argType: 'num' },
|
|
51
|
+
maxNumber: { param: 'num max', argName: 'max', argType: 'num' },
|
|
52
|
+
minAge: { param: 'int min', argName: 'min', argType: 'int' },
|
|
53
|
+
fileSize: { param: 'num max', argName: 'max', argType: 'num' },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function genDartMessages(messages, outDir) {
|
|
57
|
+
const constLines = Object.entries(messages).map(
|
|
58
|
+
([key, value]) => ` static const String ${key} = ${dartStringLiteral(value)};`
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const placeholder = { required: '{{field}}', minLength: '{{min}}', maxLength: '{{max}}', minNumber: '{{min}}', maxNumber: '{{max}}', minAge: '{{min}}', fileSize: '{{max}}' };
|
|
62
|
+
|
|
63
|
+
const helperLines = Object.entries(INTERPOLATED_HELPERS)
|
|
64
|
+
.filter(([key]) => messages[key] !== undefined)
|
|
65
|
+
.map(([key, { param, argName, argType }]) => {
|
|
66
|
+
const interpolated = argType === 'String' ? argName : `${argName}.toString()`;
|
|
67
|
+
return ` static String ${key}Msg(${param}) => ${key}.replaceAll('${placeholder[key]}', ${interpolated});`;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const content = [
|
|
71
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
72
|
+
`// Edit templates/messages.json and re-run apply-to-project.js`,
|
|
73
|
+
``,
|
|
74
|
+
`class ValidationMessages {`,
|
|
75
|
+
...constLines,
|
|
76
|
+
``,
|
|
77
|
+
...helperLines,
|
|
78
|
+
`}`,
|
|
79
|
+
].join('\n');
|
|
80
|
+
|
|
81
|
+
fs.writeFileSync(path.join(outDir, 'validation_messages.dart'), content);
|
|
82
|
+
console.log(' ✅ validation_messages.dart');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── validation_regex.dart ────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
function genDartRegex(regexMap, outDir) {
|
|
88
|
+
const entries = Object.entries(regexMap).map(
|
|
89
|
+
([key, pattern]) => ` static final RegExp ${key} = RegExp(${dartRawRegexLiteral(pattern)});`
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const content = [
|
|
93
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
94
|
+
`// Edit templates/regex.json and re-run apply-to-project.js`,
|
|
95
|
+
``,
|
|
96
|
+
`class ValidationRegex {`,
|
|
97
|
+
...entries,
|
|
98
|
+
`}`,
|
|
99
|
+
].join('\n');
|
|
100
|
+
|
|
101
|
+
fs.writeFileSync(path.join(outDir, 'validation_regex.dart'), content);
|
|
102
|
+
console.log(' ✅ validation_regex.dart');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── validators.dart ───────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
function buildFieldValidatorBody(fieldName, rules) {
|
|
108
|
+
const type = rules.type || 'string';
|
|
109
|
+
const label = rules.label || fieldName;
|
|
110
|
+
const lines = [];
|
|
111
|
+
|
|
112
|
+
if (rules.required) {
|
|
113
|
+
lines.push(` if (value == null || value.isEmpty) return ValidationMessages.requiredMsg(${dartStringLiteral(label)});`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const guardedLines = [];
|
|
117
|
+
|
|
118
|
+
if (rules.min !== undefined) {
|
|
119
|
+
if (type === 'number') {
|
|
120
|
+
guardedLines.push(`final parsed = num.tryParse(value);`);
|
|
121
|
+
guardedLines.push(`if (parsed == null || parsed < ${rules.min}) return ValidationMessages.minNumberMsg(${rules.min});`);
|
|
122
|
+
} else {
|
|
123
|
+
guardedLines.push(`if (value.length < ${rules.min}) return ValidationMessages.minLengthMsg(${rules.min});`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (rules.max !== undefined) {
|
|
128
|
+
if (type === 'number') {
|
|
129
|
+
guardedLines.push(`final parsedMax = num.tryParse(value);`);
|
|
130
|
+
guardedLines.push(`if (parsedMax == null || parsedMax > ${rules.max}) return ValidationMessages.maxNumberMsg(${rules.max});`);
|
|
131
|
+
} else {
|
|
132
|
+
guardedLines.push(`if (value.length > ${rules.max}) return ValidationMessages.maxLengthMsg(${rules.max});`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (rules.regex) {
|
|
137
|
+
const msgKey = rules.regexMessage || rules.regex;
|
|
138
|
+
guardedLines.push(`if (!ValidationRegex.${rules.regex}.hasMatch(value)) return ValidationMessages.${msgKey};`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (guardedLines.length > 0) {
|
|
142
|
+
if (rules.required) {
|
|
143
|
+
lines.push(...guardedLines.map((l) => ` ${l}`));
|
|
144
|
+
} else {
|
|
145
|
+
// Not required — only run min/max/regex checks when a value was actually entered.
|
|
146
|
+
lines.push(` if (value != null && value.isNotEmpty) {`);
|
|
147
|
+
lines.push(...guardedLines.map((l) => ` ${l}`));
|
|
148
|
+
lines.push(` }`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
lines.push(` return null;`);
|
|
153
|
+
return lines.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function genValidators(schemasRaw, outDir) {
|
|
157
|
+
const classBlocks = Object.entries(schemasRaw).map(([schemaName, fields]) => {
|
|
158
|
+
const className = `${toPascalCase(schemaName)}Validators`;
|
|
159
|
+
const methods = Object.entries(fields).map(([fieldName, rules]) => [
|
|
160
|
+
` static String? ${fieldName}(String? value) {`,
|
|
161
|
+
buildFieldValidatorBody(fieldName, rules),
|
|
162
|
+
` }`,
|
|
163
|
+
].join('\n'));
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
`class ${className} {`,
|
|
167
|
+
methods.join('\n\n'),
|
|
168
|
+
`}`,
|
|
169
|
+
].join('\n');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const content = [
|
|
173
|
+
`// Auto-generated by validation-engine-builder skill. DO NOT EDIT MANUALLY.`,
|
|
174
|
+
`// Edit templates/schemas.json and re-run apply-to-project.js`,
|
|
175
|
+
``,
|
|
176
|
+
`import 'validation_messages.dart';`,
|
|
177
|
+
`import 'validation_regex.dart';`,
|
|
178
|
+
``,
|
|
179
|
+
classBlocks.join('\n\n'),
|
|
180
|
+
].join('\n');
|
|
181
|
+
|
|
182
|
+
fs.writeFileSync(path.join(outDir, 'validators.dart'), content);
|
|
183
|
+
console.log(' ✅ validators.dart');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Page scaffold ─────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
function generateScreen(projectRoot, pageName, schemaKey, pagePath, schemasRaw) {
|
|
189
|
+
const fields = schemasRaw[schemaKey];
|
|
190
|
+
if (!fields) {
|
|
191
|
+
console.log(`\n ⚠️ Schema "${schemaKey}" not found in schemas.json.`);
|
|
192
|
+
console.log(` Available schemas: ${Object.keys(schemasRaw).join(', ')}`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// No forced "Page" suffix here — pageName is expected to already follow the
|
|
197
|
+
// Flutter naming convention (e.g. "login_page"), matching how the other 3
|
|
198
|
+
// platform generators trust the caller's name as-is.
|
|
199
|
+
const className = toPascalCase(pageName);
|
|
200
|
+
const validatorsClassName = `${toPascalCase(schemaKey)}Validators`;
|
|
201
|
+
const pageDir = path.join(projectRoot, pagePath);
|
|
202
|
+
ensureDir(pageDir);
|
|
203
|
+
|
|
204
|
+
const validationOutDir = path.join(projectRoot, 'lib', 'core', 'validation');
|
|
205
|
+
const validatorsImportPath = relativeDartImportPath(pageDir, validationOutDir);
|
|
206
|
+
|
|
207
|
+
const fieldNames = Object.keys(fields);
|
|
208
|
+
const controllerLines = fieldNames.map((f) => ` final _${f}Controller = TextEditingController();`);
|
|
209
|
+
const disposeLines = fieldNames.map((f) => ` _${f}Controller.dispose();`);
|
|
210
|
+
|
|
211
|
+
const formFieldBlocks = fieldNames.map((fieldName) => {
|
|
212
|
+
const rules = fields[fieldName];
|
|
213
|
+
const label = rules.label || fieldName;
|
|
214
|
+
const isPassword = ['password', 'confirmPassword', 'newPassword', 'currentPassword'].includes(fieldName);
|
|
215
|
+
const keyboardType = rules.type === 'number' ? 'keyboardType: TextInputType.number,\n ' : '';
|
|
216
|
+
return [
|
|
217
|
+
` TextFormField(`,
|
|
218
|
+
` controller: _${fieldName}Controller,`,
|
|
219
|
+
` decoration: const InputDecoration(labelText: ${dartStringLiteral(label)}),`,
|
|
220
|
+
` ${keyboardType}obscureText: ${isPassword},`,
|
|
221
|
+
` validator: ${validatorsClassName}.${fieldName},`,
|
|
222
|
+
` ),`,
|
|
223
|
+
].join('\n');
|
|
224
|
+
}).join('\n');
|
|
225
|
+
|
|
226
|
+
const content = [
|
|
227
|
+
`// Auto-generated scaffold — wire up submit logic before shipping.`,
|
|
228
|
+
``,
|
|
229
|
+
`import 'package:flutter/material.dart';`,
|
|
230
|
+
`import '${validatorsImportPath}/validators.dart';`,
|
|
231
|
+
``,
|
|
232
|
+
`class ${className} extends StatefulWidget {`,
|
|
233
|
+
` const ${className}({super.key});`,
|
|
234
|
+
``,
|
|
235
|
+
` @override`,
|
|
236
|
+
` State<${className}> createState() => _${className}State();`,
|
|
237
|
+
`}`,
|
|
238
|
+
``,
|
|
239
|
+
`class _${className}State extends State<${className}> {`,
|
|
240
|
+
` final _formKey = GlobalKey<FormState>();`,
|
|
241
|
+
...controllerLines,
|
|
242
|
+
``,
|
|
243
|
+
` @override`,
|
|
244
|
+
` void dispose() {`,
|
|
245
|
+
...disposeLines,
|
|
246
|
+
` super.dispose();`,
|
|
247
|
+
` }`,
|
|
248
|
+
``,
|
|
249
|
+
` void _submit() {`,
|
|
250
|
+
` if (_formKey.currentState!.validate()) {`,
|
|
251
|
+
` // TODO: Add your submit logic here`,
|
|
252
|
+
` }`,
|
|
253
|
+
` }`,
|
|
254
|
+
``,
|
|
255
|
+
` @override`,
|
|
256
|
+
` Widget build(BuildContext context) {`,
|
|
257
|
+
` return Scaffold(`,
|
|
258
|
+
` body: Form(`,
|
|
259
|
+
` key: _formKey,`,
|
|
260
|
+
` child: ListView(`,
|
|
261
|
+
` padding: const EdgeInsets.all(24),`,
|
|
262
|
+
` children: [`,
|
|
263
|
+
formFieldBlocks,
|
|
264
|
+
` const SizedBox(height: 16),`,
|
|
265
|
+
` ElevatedButton(onPressed: _submit, child: const Text('Submit')),`,
|
|
266
|
+
` ],`,
|
|
267
|
+
` ),`,
|
|
268
|
+
` ),`,
|
|
269
|
+
` );`,
|
|
270
|
+
` }`,
|
|
271
|
+
`}`,
|
|
272
|
+
].join('\n');
|
|
273
|
+
|
|
274
|
+
const outFile = path.join(pageDir, `${pageName}.dart`);
|
|
275
|
+
fs.writeFileSync(outFile, content);
|
|
276
|
+
console.log(` ✅ ${pageName}.dart → ${pagePath}/`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── Main export ───────────────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
function generate(projectRoot) {
|
|
282
|
+
const outDir = path.join(projectRoot, 'lib', 'core', 'validation');
|
|
283
|
+
ensureDir(outDir);
|
|
284
|
+
|
|
285
|
+
console.log('\n📁 Generating lib/core/validation/ (Flutter) ...');
|
|
286
|
+
|
|
287
|
+
const messages = readTemplate('messages.json');
|
|
288
|
+
const regexMap = readTemplate('regex.json');
|
|
289
|
+
const schemasRaw = readTemplate('schemas.json');
|
|
290
|
+
|
|
291
|
+
genDartMessages(messages, outDir);
|
|
292
|
+
genDartRegex(regexMap, outDir);
|
|
293
|
+
genValidators(schemasRaw, outDir);
|
|
294
|
+
|
|
295
|
+
return schemasRaw;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = { generate, generateScreen };
|