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.
Files changed (223) hide show
  1. package/.github/skills/boiler-project-generator/SKILL.md +103 -0
  2. package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
  3. package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
  4. package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
  5. package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
  6. package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
  7. package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
  8. package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
  9. package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
  10. package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
  11. package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
  12. package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
  13. package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
  14. package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
  15. package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
  16. package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
  17. package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
  18. package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
  19. package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
  20. package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
  21. package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
  22. package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
  23. package/.github/skills/boiler-project-generator/references/android.md +19 -0
  24. package/.github/skills/boiler-project-generator/references/angular.md +14 -0
  25. package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
  26. package/.github/skills/boiler-project-generator/references/ios.md +17 -0
  27. package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
  28. package/.github/skills/boiler-project-generator/references/react.md +17 -0
  29. package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
  30. package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
  31. package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
  32. package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
  33. package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
  34. package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
  35. package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
  36. package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
  37. package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
  38. package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
  39. package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
  40. package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
  41. package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
  42. package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
  43. package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
  44. package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
  45. package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
  46. package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
  47. package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
  48. package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
  49. package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
  50. package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
  51. package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
  52. package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
  53. package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
  54. package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
  55. package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
  56. package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
  57. package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
  58. package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
  59. package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
  60. package/.github/skills/custom-font-integrator/SKILL.md +373 -0
  61. package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
  62. package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
  63. package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
  64. package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
  65. package/.github/skills/encryption-decryption-utility/README.md +222 -0
  66. package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
  67. package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
  68. package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
  69. package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
  70. package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
  71. package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
  72. package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
  73. package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
  74. package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
  75. package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
  76. package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
  77. package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
  78. package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
  79. package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
  80. package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
  81. package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
  82. package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
  83. package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
  84. package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
  85. package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
  86. package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
  87. package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
  88. package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
  89. package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
  90. package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
  91. package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
  92. package/.github/skills/encryption-decryption-utility/template.md +241 -0
  93. package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
  94. package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
  95. package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
  96. package/.github/skills/feature-generator/SKILL.MD +2741 -0
  97. package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
  98. package/.github/skills/feature-generator/assets/spec-template.md +172 -0
  99. package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
  100. package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
  101. package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
  102. package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
  103. package/.github/skills/figma-ui-mapper/README.md +34 -0
  104. package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
  105. package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
  106. package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
  107. package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
  108. package/.github/skills/figma-ui-mapper/references/android.md +11 -0
  109. package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
  110. package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
  111. package/.github/skills/figma-ui-mapper/references/common.md +23 -0
  112. package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
  113. package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
  114. package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
  115. package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
  116. package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
  117. package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
  118. package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
  119. package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
  120. package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
  121. package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
  122. package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
  123. package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
  124. package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
  125. package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
  126. package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
  127. package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
  128. package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
  129. package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
  130. package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
  131. package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
  132. package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
  133. package/.github/skills/figma-ui-mapper/references/next.md +18 -0
  134. package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
  135. package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
  136. package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
  137. package/.github/skills/figma-ui-mapper/references/react.md +18 -0
  138. package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
  139. package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
  140. package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
  141. package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
  142. package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
  143. package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
  144. package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
  145. package/.github/skills/md-file-converter/SKILL.md +291 -0
  146. package/.github/skills/md-file-converter/config/formats.json +114 -0
  147. package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
  148. package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
  149. package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
  150. package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
  151. package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
  152. package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
  153. package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
  154. package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
  155. package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
  156. package/.github/skills/md-file-converter/templates/output.md +73 -0
  157. package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
  158. package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
  159. package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
  160. package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
  161. package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
  162. package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
  163. package/.github/skills/test-case-validator/README.md +111 -0
  164. package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
  165. package/.github/skills/test-case-validator/SKILL.md +209 -0
  166. package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
  167. package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
  168. package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
  169. package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
  170. package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
  171. package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
  172. package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
  173. package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
  174. package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
  175. package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
  176. package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
  177. package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
  178. package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
  179. package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
  180. package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
  181. package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
  182. package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
  183. package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
  184. package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
  185. package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
  186. package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
  187. package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
  188. package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
  189. package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
  190. package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
  191. package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
  192. package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
  193. package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
  194. package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
  195. package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
  196. package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
  197. package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
  198. package/.github/skills/validation-engine-builder/README.md +50 -0
  199. package/.github/skills/validation-engine-builder/SKILL.md +241 -0
  200. package/.github/skills/validation-engine-builder/references/core.md +158 -0
  201. package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
  202. package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
  203. package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
  204. package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
  205. package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
  206. package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
  207. package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
  208. package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
  209. package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
  210. package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
  211. package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
  212. package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
  213. package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
  214. package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
  215. package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
  216. package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
  217. package/.github/skills/validation-engine-builder/template.md +73 -0
  218. package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
  219. package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
  220. package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
  221. package/bin/index.js +145 -0
  222. package/package.json +22 -0
  223. package/readme.md +0 -0
@@ -0,0 +1,635 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const readline = require('node:readline/promises');
6
+
7
+ const DEFAULT_ASSET_PATH = 'src/assets';
8
+ const MAX_SCAN_DEPTH = 5;
9
+ const IGNORED_DIRS = new Set([
10
+ '.git', '.github', 'node_modules', '.next', '.nuxt', 'dist', 'build', 'coverage',
11
+ '.turbo', '.nx', '.dart_tool', 'Pods',
12
+ ]);
13
+
14
+ const FRAMEWORK_ORDER = [
15
+ 'expo', 'next', 'nuxt', 'angular', 'ionic', 'react-native', 'flutter', 'vue',
16
+ 'svelte', 'capacitor', 'kotlin-multiplatform', 'native-android', 'native-ios', 'react',
17
+ ];
18
+
19
+ const FRAMEWORK_RULE_PATH = framework => `.github/skills/figma-ui-mapper/references/frameworks/${framework}/rules.md`;
20
+ const PLATFORM_REFERENCE_PATH = framework => `.github/skills/figma-ui-mapper/references/${framework}.md`;
21
+ const FIGMA_URL_PATTERN = /https?:\/\/(?:www\.)?figma\.com\/(?:design|file)\/[A-Za-z0-9]+\/[^\s"'<>\]]+/;
22
+
23
+ const ADAPTERS = {
24
+ react: {
25
+ displayName: 'React',
26
+ pageFile: pageName => `${pageName}.tsx`,
27
+ styleFile: pageName => `${pageName}Style.ts`,
28
+ tokenFile: pageName => `${pageName}Tokens.ts`,
29
+ pageTemplate: pageName => `import React from 'react';\n\nimport { styles } from './${pageName}Style';\n\nconst ${pageName} = () => <div style={styles.container} />;\n\nexport default ${pageName};\n`,
30
+ styleTemplate: () => `import React from 'react';\n\nexport const styles: Record<string, React.CSSProperties> = {\n container: {\n display: 'flex',\n flexDirection: 'column',\n flex: 1,\n },\n};\n`,
31
+ tokenTemplate: () => `export const tokens = {\n colors: {},\n spacing: {},\n typography: {},\n radii: {},\n shadows: {},\n};\n`,
32
+ },
33
+ 'react-native': {
34
+ displayName: 'React Native',
35
+ pageFile: pageName => `${pageName}.tsx`,
36
+ styleFile: pageName => `${pageName}Style.ts`,
37
+ tokenFile: pageName => `${pageName}Tokens.ts`,
38
+ pageTemplate: pageName => `import React from 'react';\nimport { View } from 'react-native';\n\nimport { styles } from './${pageName}Style';\n\nconst ${pageName} = () => <View style={styles.container} />;\n\nexport default ${pageName};\n`,
39
+ styleTemplate: () => `import { StyleSheet } from 'react-native';\n\nexport const styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n});\n`,
40
+ tokenTemplate: () => `export const tokens = {\n colors: {},\n spacing: {},\n typography: {},\n radii: {},\n shadows: {},\n};\n`,
41
+ },
42
+ flutter: {
43
+ displayName: 'Flutter',
44
+ pageFile: pageName => `${toKebab(pageName)}.dart`,
45
+ styleFile: pageName => `${toKebab(pageName)}_style.dart`,
46
+ tokenFile: pageName => `${toKebab(pageName)}_tokens.dart`,
47
+ pageTemplate: pageName => `import 'package:flutter/widgets.dart';\n\nclass ${pageName} extends StatelessWidget {\n const ${pageName}({super.key});\n\n @override\n Widget build(BuildContext context) {\n return const SizedBox.expand();\n }\n}\n`,
48
+ styleTemplate: pageName => `class ${pageName}Style {}\n`,
49
+ tokenTemplate: () => `const tokens = <String, Object>{};\n`,
50
+ },
51
+ angular: {
52
+ displayName: 'Angular',
53
+ pageFile: pageName => `${toKebab(pageName)}.component.ts`,
54
+ styleFile: pageName => `${toKebab(pageName)}.component.scss`,
55
+ tokenFile: pageName => `${toKebab(pageName)}.tokens.ts`,
56
+ pageTemplate: pageName => `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-${toKebab(pageName)}',\n standalone: true,\n templateUrl: './${toKebab(pageName)}.component.html',\n styleUrl: './${toKebab(pageName)}.component.scss',\n})\nexport class ${pageName}Component {}\n`,
57
+ styleTemplate: () => `:host {\n display: block;\n}\n`,
58
+ tokenTemplate: () => `export const tokens = {};\n`,
59
+ extras: pageName => [{name: `${toKebab(pageName)}.component.html`, content: '<section></section>\n'}],
60
+ },
61
+ vue: {
62
+ displayName: 'Vue',
63
+ pageFile: pageName => `${pageName}.vue`,
64
+ styleFile: pageName => `${pageName}.css`,
65
+ tokenFile: pageName => `${pageName}.tokens.ts`,
66
+ pageTemplate: pageName => `<template>\n <section class=\"container\"></section>\n</template>\n\n<script setup lang=\"ts\">\n</script>\n\n<style scoped src=\"./${pageName}.css\"></style>\n`,
67
+ styleTemplate: () => `.container {\n display: flex;\n flex-direction: column;\n}\n`,
68
+ tokenTemplate: () => `export const tokens = {};\n`,
69
+ },
70
+ svelte: {
71
+ displayName: 'Svelte',
72
+ pageFile: pageName => `${pageName}.svelte`,
73
+ styleFile: pageName => `${pageName}.css`,
74
+ tokenFile: pageName => `${pageName}.tokens.ts`,
75
+ pageTemplate: pageName => `<section class=\"container\"></section>\n\n<style src=\"./${pageName}.css\"></style>\n`,
76
+ styleTemplate: () => `.container {\n display: flex;\n}\n`,
77
+ tokenTemplate: () => `export const tokens = {};\n`,
78
+ },
79
+ 'native-android': {
80
+ displayName: 'Native Android',
81
+ pageFile: pageName => `${pageName}Screen.kt`,
82
+ styleFile: pageName => `${toKebab(pageName)}_screen.xml`,
83
+ tokenFile: pageName => `${toKebab(pageName)}_tokens.json`,
84
+ pageTemplate: pageName => `package com.example.generated\n\nclass ${pageName}Screen\n`,
85
+ styleTemplate: () => `<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\" />\n`,
86
+ tokenTemplate: () => `{\n \"tokens\": {}\n}\n`,
87
+ },
88
+ 'native-ios': {
89
+ displayName: 'Native iOS',
90
+ pageFile: pageName => `${pageName}View.swift`,
91
+ styleFile: pageName => `${pageName}Theme.swift`,
92
+ tokenFile: pageName => `${pageName}Tokens.json`,
93
+ pageTemplate: pageName => `import SwiftUI\n\nstruct ${pageName}View: View {\n var body: some View {\n VStack { }\n }\n}\n`,
94
+ styleTemplate: pageName => `import SwiftUI\n\nenum ${pageName}Theme {}\n`,
95
+ tokenTemplate: () => `{\n \"tokens\": {}\n}\n`,
96
+ },
97
+ 'kotlin-multiplatform': {
98
+ displayName: 'Kotlin Multiplatform',
99
+ pageFile: pageName => `${pageName}Screen.kt`,
100
+ styleFile: pageName => `${pageName}Theme.kt`,
101
+ tokenFile: pageName => `${pageName}Tokens.json`,
102
+ pageTemplate: pageName => `package com.example.generated\n\nclass ${pageName}Screen\n`,
103
+ styleTemplate: pageName => `object ${pageName}Theme\n`,
104
+ tokenTemplate: () => `{\n \"tokens\": {}\n}\n`,
105
+ },
106
+ };
107
+ ADAPTERS.next = ADAPTERS.react;
108
+ ADAPTERS.nuxt = ADAPTERS.vue;
109
+ ADAPTERS.expo = ADAPTERS['react-native'];
110
+ ADAPTERS.ionic = ADAPTERS.react;
111
+ ADAPTERS.capacitor = ADAPTERS.react;
112
+
113
+ function parseArgs(argv) {
114
+ const out = {};
115
+ for (let i = 0; i < argv.length; i += 1) {
116
+ const token = argv[i];
117
+ if (!token.startsWith('--')) continue;
118
+ const key = token.slice(2);
119
+ const next = argv[i + 1];
120
+ if (!next || next.startsWith('--')) out[key] = 'true';
121
+ else {
122
+ out[key] = next;
123
+ i += 1;
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+
129
+ function printHelp() {
130
+ console.log(`\nFigma UI Mapper\n\nUsage:\n node ./scripts/generate-page-through-figma.js [options]\n\nOptions:\n --figmaUrl <url>\n --pageName <PascalCase>\n --pages <json-array>\n --linksFile <path>\n --pagePath <path>\n --componentPath <path>\n --assetPath <path>\n --reusableComponentsPath <path>\n --projectPath <path>\n --nonInteractive\n --dryRun\n --allowOutsideProject\n --help\n`);
131
+ }
132
+
133
+ function parseFigma(url) {
134
+ try {
135
+ const parsed = new URL(url);
136
+ const segments = parsed.pathname.split('/').filter(Boolean);
137
+ const idx = segments.findIndex(v => v === 'design' || v === 'file');
138
+ const fileKey = idx >= 0 ? segments[idx + 1] : '';
139
+ const title = idx >= 0 ? segments[idx + 2] : 'FigmaPage';
140
+ const nodeId = String(parsed.searchParams.get('node-id') || '').trim().replace(/-/g, ':');
141
+ return {fileKey, nodeId, title};
142
+ } catch {
143
+ return {fileKey: '', nodeId: '', title: ''};
144
+ }
145
+ }
146
+
147
+ function isUrl(v) {
148
+ try {
149
+ const u = new URL(v);
150
+ return u.protocol === 'http:' || u.protocol === 'https:';
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+
156
+ function ensureDir(dir) {
157
+ fs.mkdirSync(dir, {recursive: true});
158
+ }
159
+
160
+ function writeFileSafe(file, content, dryRun) {
161
+ if (dryRun) return {created: !fs.existsSync(file), file};
162
+ ensureDir(path.dirname(file));
163
+ if (fs.existsSync(file)) return {created: false, file};
164
+ fs.writeFileSync(file, content, 'utf8');
165
+ return {created: true, file};
166
+ }
167
+
168
+ function toPascal(input) {
169
+ return String(input)
170
+ .replace(/[^A-Za-z0-9]+/g, ' ')
171
+ .split(' ')
172
+ .filter(Boolean)
173
+ .map(token => token.charAt(0).toUpperCase() + token.slice(1))
174
+ .join('') || 'FigmaPage';
175
+ }
176
+
177
+ function toKebab(input) {
178
+ return String(input)
179
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
180
+ .replace(/[^A-Za-z0-9]+/g, '-')
181
+ .replace(/^-+|-+$/g, '')
182
+ .toLowerCase();
183
+ }
184
+
185
+ function readJson(file) {
186
+ try { return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : null; }
187
+ catch { return null; }
188
+ }
189
+
190
+ function readText(file) {
191
+ try { return fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : ''; }
192
+ catch { return ''; }
193
+ }
194
+
195
+ function dep(pkg, name) {
196
+ if (!pkg) return '';
197
+ return pkg.dependencies?.[name] || pkg.devDependencies?.[name] || pkg.peerDependencies?.[name] || '';
198
+ }
199
+
200
+ function detectArchitecture(projectRoot) {
201
+ const has = rel => fs.existsSync(path.join(projectRoot, rel));
202
+ if (has('src/domain') && has('src/data') && has('src/presentation')) return 'clean-architecture';
203
+ if (has('src/features')) return 'feature-first';
204
+ if (has('src/pages') || has('pages')) return 'page-first';
205
+ return 'standard';
206
+ }
207
+
208
+ function isLikelyProjectRoot(dir) {
209
+ const checks = [
210
+ 'package.json', 'pubspec.yaml', 'angular.json', 'next.config.js', 'next.config.ts',
211
+ 'nuxt.config.js', 'nuxt.config.ts', 'android', 'ios', 'build.gradle', 'build.gradle.kts', 'Podfile',
212
+ ];
213
+ return checks.some(item => fs.existsSync(path.join(dir, item)));
214
+ }
215
+
216
+ function scanProjects(rootDir, depth = 0, found = []) {
217
+ if (depth > MAX_SCAN_DEPTH) return found;
218
+ if (isLikelyProjectRoot(rootDir)) found.push(rootDir);
219
+ let entries = [];
220
+ try { entries = fs.readdirSync(rootDir, {withFileTypes: true}); }
221
+ catch { return found; }
222
+ for (const entry of entries) {
223
+ if (!entry.isDirectory()) continue;
224
+ if (IGNORED_DIRS.has(entry.name)) continue;
225
+ scanProjects(path.join(rootDir, entry.name), depth + 1, found);
226
+ }
227
+ return found;
228
+ }
229
+
230
+ function uniquePaths(paths) {
231
+ return [...new Set(paths.map(p => path.resolve(p)))];
232
+ }
233
+
234
+ function detectFramework(projectRoot) {
235
+ const pkg = readJson(path.join(projectRoot, 'package.json'));
236
+ const gradle = [
237
+ readText(path.join(projectRoot, 'build.gradle')),
238
+ readText(path.join(projectRoot, 'build.gradle.kts')),
239
+ readText(path.join(projectRoot, 'settings.gradle')),
240
+ readText(path.join(projectRoot, 'settings.gradle.kts')),
241
+ ].join('\n');
242
+ const has = rel => fs.existsSync(path.join(projectRoot, rel));
243
+
244
+ const checks = {
245
+ expo: () => dep(pkg, 'expo') || has('app.json') || has('app.config.ts') || has('app.config.js'),
246
+ next: () => dep(pkg, 'next') || has('next.config.js') || has('next.config.ts') || has('next.config.mjs') || has('next.config.cjs'),
247
+ nuxt: () => dep(pkg, 'nuxt') || has('nuxt.config.js') || has('nuxt.config.ts') || has('nuxt.config.mjs') || has('nuxt.config.cjs'),
248
+ angular: () => dep(pkg, '@angular/core') || has('angular.json'),
249
+ ionic: () => dep(pkg, '@ionic/angular') || dep(pkg, '@ionic/react') || dep(pkg, '@ionic/vue') || has('ionic.config.json'),
250
+ 'react-native': () => dep(pkg, 'react-native') || (has('android') && has('ios') && (has('App.tsx') || has('index.js'))),
251
+ flutter: () => has('pubspec.yaml') || has('main.dart') || has('lib/main.dart'),
252
+ vue: () => dep(pkg, 'vue'),
253
+ svelte: () => dep(pkg, 'svelte') || has('svelte.config.js') || has('svelte.config.ts'),
254
+ capacitor: () => dep(pkg, '@capacitor/core') || has('capacitor.config.ts') || has('capacitor.config.js') || has('capacitor.config.json'),
255
+ 'kotlin-multiplatform': () => has('shared/src/commonMain') || /kotlin\("multiplatform"\)|org\.jetbrains\.kotlin\.multiplatform/.test(gradle),
256
+ 'native-android': () => has('android') || has('build.gradle') || has('build.gradle.kts'),
257
+ 'native-ios': () => has('ios') || has('Podfile') || has('ios/Podfile'),
258
+ react: () => dep(pkg, 'react') || dep(pkg, 'react-dom') || has('src/App.tsx') || has('src/App.jsx'),
259
+ };
260
+
261
+ for (const name of FRAMEWORK_ORDER) {
262
+ if (checks[name]()) {
263
+ return {
264
+ framework: name,
265
+ reason: `Detected ${name} by repository signals in ${path.relative(process.cwd(), projectRoot) || '.'}.`,
266
+ version: dep(pkg, name === 'react' ? 'react' : name) || 'unknown',
267
+ architecture: detectArchitecture(projectRoot),
268
+ };
269
+ }
270
+ }
271
+ return {
272
+ framework: 'react',
273
+ reason: `No explicit framework markers found in ${path.relative(process.cwd(), projectRoot) || '.'}. Defaulted to react adapter.`,
274
+ version: dep(pkg, 'react') || 'unknown',
275
+ architecture: detectArchitecture(projectRoot),
276
+ };
277
+ }
278
+
279
+ function parseLinksText(raw) {
280
+ const entries = [];
281
+ for (const lineRaw of raw.split(/\r?\n/)) {
282
+ const line = lineRaw.trim();
283
+ if (!line || line.startsWith('#')) continue;
284
+ const m = line.match(FIGMA_URL_PATTERN);
285
+ if (!m) continue;
286
+ const figmaUrl = m[0];
287
+ const before = line.slice(0, m.index).trim();
288
+ const pageName = before ? toPascal(before) : undefined;
289
+ entries.push(pageName ? {pageName, figmaUrl} : {figmaUrl});
290
+ }
291
+ return entries;
292
+ }
293
+
294
+ function parsePagesFromArgs(args, projectRoot) {
295
+ if (args.pages) {
296
+ const parsed = JSON.parse(args.pages);
297
+ if (!Array.isArray(parsed)) throw new Error('--pages must be a JSON array');
298
+ return parsed;
299
+ }
300
+ if (args.linksFile) {
301
+ const file = path.resolve(projectRoot, args.linksFile);
302
+ if (!fs.existsSync(file)) throw new Error(`linksFile not found: ${file}`);
303
+ const raw = fs.readFileSync(file, 'utf8').trim();
304
+ if (file.toLowerCase().endsWith('.json')) {
305
+ const parsed = JSON.parse(raw);
306
+ if (!Array.isArray(parsed)) throw new Error('linksFile JSON must be an array');
307
+ return parsed;
308
+ }
309
+ return parseLinksText(raw);
310
+ }
311
+ if (args.figmaUrl) return [{pageName: args.pageName, figmaUrl: args.figmaUrl}];
312
+ return null;
313
+ }
314
+
315
+ function resolveWithinProject(projectRoot, candidate, allowOutside) {
316
+ const resolved = path.resolve(projectRoot, candidate || '.');
317
+ if (allowOutside) return resolved;
318
+ const root = path.resolve(projectRoot);
319
+ if (resolved === root || resolved.startsWith(root + path.sep)) return resolved;
320
+ throw new Error(`Path is outside project root and blocked: ${candidate}`);
321
+ }
322
+
323
+ async function pickProjectRoot(args, reader) {
324
+ if (args.projectPath) {
325
+ const selected = path.resolve(process.cwd(), args.projectPath);
326
+ if (!fs.existsSync(selected)) throw new Error(`projectPath does not exist: ${selected}`);
327
+ return selected;
328
+ }
329
+ const projects = uniquePaths(scanProjects(process.cwd()));
330
+ if (projects.length <= 1) return projects[0] || process.cwd();
331
+ if (args.nonInteractive === 'true') throw new Error(`Multiple projects detected (${projects.length}). Use --projectPath.`);
332
+ console.log('\nMultiple candidate projects detected:');
333
+ projects.forEach((p, i) => console.log(` ${i + 1}. ${path.relative(process.cwd(), p) || '.'}`));
334
+ while (true) {
335
+ const input = (await reader.question('Select project number: ')).trim();
336
+ const idx = Number.parseInt(input, 10) - 1;
337
+ if (!Number.isNaN(idx) && projects[idx]) return projects[idx];
338
+ }
339
+ }
340
+
341
+ async function promptPages(reader) {
342
+ const pages = [];
343
+ let i = 1;
344
+ while (true) {
345
+ const value = (await reader.question(`Page ${i} Figma URL (or done): `)).trim();
346
+ if (value.toLowerCase() === 'done') {
347
+ if (pages.length === 0) {
348
+ console.log('At least one page is required.');
349
+ continue;
350
+ }
351
+ return pages;
352
+ }
353
+ if (!isUrl(value)) {
354
+ console.log('Invalid URL. Provide a valid https URL.');
355
+ continue;
356
+ }
357
+ const info = parseFigma(value);
358
+ if (!info.fileKey || !info.nodeId) {
359
+ console.log('Figma URL must include file key and node-id query parameter.');
360
+ continue;
361
+ }
362
+ const inferred = toPascal(info.title);
363
+ const name = (await reader.question(`Page name [${inferred}]: `)).trim() || inferred;
364
+ pages.push({pageName: toPascal(name), figmaUrl: value});
365
+ i += 1;
366
+ }
367
+ }
368
+
369
+ function renderTemplate(templateText, values) {
370
+ let out = templateText;
371
+ for (const [k, v] of Object.entries(values)) out = out.split(`{{${k}}}`).join(String(v));
372
+ return out;
373
+ }
374
+
375
+ function buildReportValues(config, detection, rows, skipped, projectRoot) {
376
+ return {
377
+ generated_at: new Date().toISOString(),
378
+ project_root: projectRoot,
379
+ figma_urls: rows.map(r => r.figmaUrl).join(', '),
380
+ page_path: config.pagePath,
381
+ component_path: config.componentPath,
382
+ asset_path: config.assetPath,
383
+ reusable_components_path: config.reusableComponentsPath,
384
+ detected_framework: detection.framework,
385
+ framework_version: detection.version,
386
+ architecture_detected: detection.architecture,
387
+ detection_reason: detection.reason,
388
+ adapter_used: detection.framework,
389
+ platform_reference: PLATFORM_REFERENCE_PATH(detection.framework),
390
+ framework_rules: FRAMEWORK_RULE_PATH(detection.framework),
391
+ pages_requested: config.pages.length,
392
+ pages_generated: rows.length,
393
+ pages_skipped: skipped.length,
394
+ coverage_map_match: 'PENDING_IMPLEMENTATION_PHASE',
395
+ components_reused: 0,
396
+ components_generated: rows.length,
397
+ token_extraction: 'PENDING_MCP_PHASE',
398
+ assets_resolved_local: 0,
399
+ assets_downloaded_figma: 0,
400
+ asset_failures: 0,
401
+ component_first_generation: 'PENDING_MCP_PHASE',
402
+ accessibility_check: 'PENDING_IMPLEMENTATION_PHASE',
403
+ screenshot_comparison: 'PENDING_IMPLEMENTATION_PHASE',
404
+ iteration_count: 0,
405
+ qa_status: 'PENDING_IMPLEMENTATION_PHASE',
406
+ errors: 'none',
407
+ warnings: skipped.length ? skipped.map(s => `${s.pageName}: ${s.reason}`).join('; ') : 'none',
408
+ summary: 'Scaffold created. Continue with MCP-backed implementation workflow.',
409
+ };
410
+ }
411
+
412
+ function createScaffold(config, detection, options) {
413
+ const adapter = ADAPTERS[detection.framework] || ADAPTERS.react;
414
+ const dryRun = options.dryRun;
415
+ const projectRoot = options.projectRoot;
416
+
417
+ const reusableDir = resolveWithinProject(projectRoot, config.reusableComponentsPath, options.allowOutside);
418
+ const assetRoot = resolveWithinProject(projectRoot, config.assetPath, options.allowOutside);
419
+ const reportsRoot = path.join(projectRoot, '.figma-ui-mapper', 'reports');
420
+ const artifactsRoot = path.join(projectRoot, '.figma-ui-mapper', 'artifacts');
421
+
422
+ if (!dryRun) {
423
+ ensureDir(reusableDir);
424
+ ensureDir(path.join(assetRoot, 'images'));
425
+ ensureDir(path.join(assetRoot, 'svg'));
426
+ ensureDir(reportsRoot);
427
+ ensureDir(artifactsRoot);
428
+ }
429
+
430
+ const rows = [];
431
+ const skipped = [];
432
+ const componentMapTemplate = readText(path.join(__dirname, '..', 'assets', 'templates', 'component-map-template.md'));
433
+
434
+ for (const page of config.pages) {
435
+ try {
436
+ const pageDir = resolveWithinProject(projectRoot, path.join(config.pagePath, page.pageName), options.allowOutside);
437
+ const componentDir = resolveWithinProject(projectRoot, path.join(config.componentPath, page.pageName), options.allowOutside);
438
+ if (!dryRun) { ensureDir(pageDir); ensureDir(componentDir); }
439
+
440
+ const pageFile = path.join(pageDir, adapter.pageFile(page.pageName));
441
+ const styleFile = path.join(pageDir, adapter.styleFile(page.pageName));
442
+ const tokenFile = path.join(pageDir, adapter.tokenFile(page.pageName));
443
+
444
+ const createdPage = writeFileSafe(pageFile, adapter.pageTemplate(page.pageName), dryRun);
445
+ const createdStyle = writeFileSafe(styleFile, adapter.styleTemplate(page.pageName), dryRun);
446
+ const createdToken = writeFileSafe(tokenFile, adapter.tokenTemplate(page.pageName), dryRun);
447
+ for (const extra of (adapter.extras ? adapter.extras(page.pageName) : [])) writeFileSafe(path.join(pageDir, extra.name), extra.content, dryRun);
448
+
449
+ const row = {
450
+ pageName: page.pageName,
451
+ figmaUrl: page.figmaUrl,
452
+ fileKey: page.fileKey,
453
+ nodeId: page.nodeId,
454
+ pageFile,
455
+ styleFile,
456
+ tokenFile,
457
+ componentDir,
458
+ createdPage: createdPage.created,
459
+ createdStyle: createdStyle.created,
460
+ createdToken: createdToken.created,
461
+ };
462
+ rows.push(row);
463
+
464
+ const componentMap = renderTemplate(componentMapTemplate, {
465
+ page_name: page.pageName,
466
+ figma_url: page.figmaUrl,
467
+ file_key: page.fileKey,
468
+ node_id: page.nodeId,
469
+ frame_name: page.pageName,
470
+ size: 'PENDING_MCP',
471
+ sections_table: 'PENDING_MCP',
472
+ reusable_components_table: 'PENDING_MCP',
473
+ assets_table: 'PENDING_MCP',
474
+ inventory_reference: 'PENDING_MCP',
475
+ coverage_map_reference: 'PENDING_MCP',
476
+ generation_order: 'PENDING_MCP',
477
+ status_table: 'PENDING_MCP',
478
+ });
479
+ writeFileSafe(path.join(reportsRoot, `${page.pageName}-component-map.md`), componentMap, dryRun);
480
+ } catch (error) {
481
+ skipped.push({pageName: page.pageName, reason: error.message});
482
+ }
483
+ }
484
+
485
+ const reportTemplate = readText(path.join(__dirname, '..', 'assets', 'templates', 'generation-report-template.md'));
486
+ const reportValues = buildReportValues(config, detection, rows, skipped, projectRoot);
487
+ const reportMarkdown = renderTemplate(reportTemplate, reportValues);
488
+ writeFileSafe(path.join(reportsRoot, 'generation-report.md'), reportMarkdown, dryRun);
489
+
490
+ const artifact = {
491
+ generatedAt: reportValues.generated_at,
492
+ projectRoot,
493
+ framework: detection.framework,
494
+ version: detection.version,
495
+ architecture: detection.architecture,
496
+ detectionReason: detection.reason,
497
+ pagesRequested: config.pages.length,
498
+ pagesGenerated: rows.map(r => ({
499
+ pageName: r.pageName,
500
+ figmaUrl: r.figmaUrl,
501
+ fileKey: r.fileKey,
502
+ nodeId: r.nodeId,
503
+ pageFile: path.relative(projectRoot, r.pageFile).replace(/\\/g, '/'),
504
+ styleFile: path.relative(projectRoot, r.styleFile).replace(/\\/g, '/'),
505
+ tokenFile: path.relative(projectRoot, r.tokenFile).replace(/\\/g, '/'),
506
+ componentDir: path.relative(projectRoot, r.componentDir).replace(/\\/g, '/'),
507
+ })),
508
+ pagesSkipped: skipped,
509
+ workflowState: {
510
+ detection: 'DONE',
511
+ scaffolding: 'DONE',
512
+ mcpExtraction: 'PENDING',
513
+ componentFirstGeneration: 'PENDING',
514
+ validation: 'PENDING',
515
+ finalQa: 'PENDING',
516
+ },
517
+ references: {
518
+ workflow: '.github/skills/figma-ui-mapper/references/workflow.md',
519
+ frameworkRules: FRAMEWORK_RULE_PATH(detection.framework),
520
+ platformReference: PLATFORM_REFERENCE_PATH(detection.framework),
521
+ },
522
+ report: {
523
+ path: path.join('.figma-ui-mapper', 'reports', 'generation-report.md').replace(/\\/g, '/'),
524
+ warnings: reportValues.warnings,
525
+ errors: reportValues.errors,
526
+ },
527
+ };
528
+
529
+ const artifactPath = path.join(artifactsRoot, 'figma-generated-pages.json');
530
+ writeFileSafe(artifactPath, JSON.stringify(artifact, null, 2) + '\n', dryRun);
531
+ return {rows, skipped, artifact, artifactPath, reportsRoot, artifactsRoot};
532
+ }
533
+
534
+ function printSummary(detection, output, projectRoot, dryRun) {
535
+ const adapter = ADAPTERS[detection.framework] || ADAPTERS.react;
536
+ console.log('\nFigma UI Mapper scaffold prepared\n');
537
+ console.log(`Project Root: ${projectRoot}`);
538
+ console.log(`Detected Framework: ${adapter.displayName}`);
539
+ console.log(`Detection Reason: ${detection.reason}`);
540
+ console.log(`Framework Adapter Used: ${detection.framework}`);
541
+ console.log(`Framework Version: ${detection.version}`);
542
+ console.log(`Architecture Detected: ${detection.architecture}`);
543
+ console.log(`Rule File: ${FRAMEWORK_RULE_PATH(detection.framework)}`);
544
+ console.log(`Platform Reference: ${PLATFORM_REFERENCE_PATH(detection.framework)}`);
545
+ console.log(`Mode: ${dryRun ? 'DRY RUN' : 'WRITE'}`);
546
+ console.log('');
547
+
548
+ for (const row of output.rows) {
549
+ console.log(`Page: ${row.pageName}`);
550
+ console.log(` Figma URL: ${row.figmaUrl}`);
551
+ console.log(` fileKey: ${row.fileKey}`);
552
+ console.log(` nodeId: ${row.nodeId}`);
553
+ console.log(` Page File: ${row.pageFile}${row.createdPage ? ' (created)' : ' (existing)'}`);
554
+ console.log(` Style File: ${row.styleFile}${row.createdStyle ? ' (created)' : ' (existing)'}`);
555
+ console.log(` Token File: ${row.tokenFile}${row.createdToken ? ' (created)' : ' (existing)'}`);
556
+ console.log(` Component Dir: ${row.componentDir}`);
557
+ console.log('');
558
+ }
559
+
560
+ if (output.skipped.length) {
561
+ console.log('Skipped pages:');
562
+ for (const item of output.skipped) console.log(` - ${item.pageName}: ${item.reason}`);
563
+ console.log('');
564
+ }
565
+
566
+ console.log(`Reports Root: ${output.reportsRoot}`);
567
+ console.log(`Artifact Path: ${output.artifactPath}`);
568
+ console.log('');
569
+ console.log(`FIGMA_ARTIFACT:${JSON.stringify(output.artifact)}`);
570
+ }
571
+
572
+ async function collectConfig(args, reader, projectRoot) {
573
+ const incoming = parsePagesFromArgs(args, projectRoot);
574
+ const pagesRaw = incoming || (args.nonInteractive === 'true' ? null : await promptPages(reader));
575
+ if (!pagesRaw) throw new Error('No input pages. Provide --figmaUrl, --pages, or --linksFile in non-interactive mode.');
576
+
577
+ const pages = [];
578
+ const rejected = [];
579
+ for (const entry of pagesRaw) {
580
+ const figmaUrl = String(entry.figmaUrl || '').trim();
581
+ if (!isUrl(figmaUrl)) { rejected.push({entry, reason: 'Invalid URL'}); continue; }
582
+ const info = parseFigma(figmaUrl);
583
+ if (!info.fileKey || !info.nodeId) { rejected.push({entry, reason: 'Missing fileKey or node-id'}); continue; }
584
+ pages.push({ pageName: toPascal(entry.pageName || info.title), figmaUrl, fileKey: info.fileKey, nodeId: info.nodeId });
585
+ }
586
+ if (!pages.length) throw new Error('No valid Figma pages provided.');
587
+
588
+ const ask = async (label, fallback) => {
589
+ const v = (await reader.question(`${label}${fallback ? ` [${fallback}]` : ''}: `)).trim();
590
+ return v || fallback;
591
+ };
592
+
593
+ const pagePath = args.pagePath || (args.nonInteractive === 'true' ? null : await ask('Page path'));
594
+ const componentPath = args.componentPath || (args.nonInteractive === 'true' ? null : await ask('Component path'));
595
+ const assetPath = args.assetPath || (args.nonInteractive === 'true' ? DEFAULT_ASSET_PATH : await ask('Asset path', DEFAULT_ASSET_PATH));
596
+ const reusableComponentsPath = args.reusableComponentsPath || (args.nonInteractive === 'true' ? null : await ask('Reusable components path'));
597
+
598
+ if (!pagePath || !componentPath || !reusableComponentsPath) {
599
+ throw new Error('Missing required paths. In non-interactive mode provide --pagePath --componentPath --reusableComponentsPath.');
600
+ }
601
+
602
+ return {pages, rejected, pagePath, componentPath, assetPath, reusableComponentsPath};
603
+ }
604
+
605
+ async function main() {
606
+ const args = parseArgs(process.argv.slice(2));
607
+ if (args.help === 'true') { printHelp(); return; }
608
+
609
+ const reader = readline.createInterface({input: process.stdin, output: process.stdout});
610
+ try {
611
+ const projectRoot = await pickProjectRoot(args, reader);
612
+ const detection = detectFramework(projectRoot);
613
+ const config = await collectConfig(args, reader, projectRoot);
614
+
615
+ const output = createScaffold(config, detection, {
616
+ dryRun: args.dryRun === 'true',
617
+ allowOutside: args.allowOutsideProject === 'true',
618
+ projectRoot,
619
+ });
620
+
621
+ printSummary(detection, output, projectRoot, args.dryRun === 'true');
622
+
623
+ if (config.rejected.length) {
624
+ console.log('\nRejected inputs:');
625
+ for (const item of config.rejected) console.log(` - ${JSON.stringify(item.entry)} :: ${item.reason}`);
626
+ }
627
+ } catch (error) {
628
+ console.error(`Error: ${error.message}`);
629
+ process.exit(1);
630
+ } finally {
631
+ reader.close();
632
+ }
633
+ }
634
+
635
+ main();
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ console.warn('[DEPRECATED] Use generate-page-through-figma.js. Redirecting to generic entrypoint.');
4
+ require('./generate-page-through-figma.js');