@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate Block Selectors Script
4
+
5
+ Generates BLOCK_SELECTORS entry for a new page builder block.
6
+
7
+ Usage:
8
+ python generate-block-selectors.py --block BLOCK_NAME [--theme THEME]
9
+
10
+ Options:
11
+ --block BLOCK_NAME Name of the block (e.g., hero, faq-accordion)
12
+ --theme THEME Theme name (default: from NEXT_PUBLIC_ACTIVE_THEME or 'default')
13
+ --analyze Analyze existing block component for elements
14
+ --full Generate full example with component and test usage
15
+ --dry-run Print output without instructions
16
+ """
17
+
18
+ import os
19
+ import re
20
+ import sys
21
+ import argparse
22
+ from pathlib import Path
23
+ from typing import List, Dict
24
+
25
+
26
+ def to_camel_case(name: str) -> str:
27
+ """Convert kebab-case to camelCase."""
28
+ components = name.split('-')
29
+ return components[0] + ''.join(x.title() for x in components[1:])
30
+
31
+
32
+ def to_pascal_case(name: str) -> str:
33
+ """Convert kebab-case to PascalCase."""
34
+ return ''.join(x.title() for x in name.split('-'))
35
+
36
+
37
+ def get_active_theme() -> str:
38
+ """Get active theme from environment or default."""
39
+ return os.environ.get('NEXT_PUBLIC_ACTIVE_THEME', 'default')
40
+
41
+
42
+ def analyze_block_component(block_path: Path) -> List[Dict]:
43
+ """Analyze a block component for interactive elements."""
44
+ elements = []
45
+
46
+ component_file = block_path / 'component.tsx'
47
+ if not component_file.exists():
48
+ return elements
49
+
50
+ with open(component_file, 'r') as f:
51
+ content = f.read()
52
+
53
+ # Find interactive elements
54
+ patterns = [
55
+ (r'<button[^>]*>', 'button'),
56
+ (r'<Button[^>]*>', 'button'),
57
+ (r'<input[^>]*>', 'input'),
58
+ (r'<Input[^>]*>', 'input'),
59
+ (r'<a\s[^>]*>', 'link'),
60
+ (r'<Link[^>]*>', 'link'),
61
+ (r'<form[^>]*>', 'form'),
62
+ (r'<Form[^>]*>', 'form'),
63
+ (r'<select[^>]*>', 'select'),
64
+ (r'<textarea[^>]*>', 'textarea'),
65
+ ]
66
+
67
+ for pattern, element_type in patterns:
68
+ matches = re.findall(pattern, content, re.IGNORECASE)
69
+ for match in matches:
70
+ elements.append({
71
+ 'type': element_type,
72
+ 'context': match[:80]
73
+ })
74
+
75
+ # Check for map/array iterations (suggests indexed selectors)
76
+ if '.map(' in content or '.forEach(' in content:
77
+ elements.append({
78
+ 'type': 'indexed',
79
+ 'context': 'Array iteration found - consider indexed selectors'
80
+ })
81
+
82
+ return elements
83
+
84
+
85
+ def generate_selectors_entry(block_name: str, elements: List[Dict] = None) -> str:
86
+ """Generate BLOCK_SELECTORS entry matching project conventions."""
87
+ camel_name = to_camel_case(block_name)
88
+ kebab_name = block_name # Keep kebab for selector values
89
+
90
+ lines = [
91
+ f" {camel_name}: {{",
92
+ f" container: 'block-{kebab_name}',",
93
+ ]
94
+
95
+ has_indexed = False
96
+ if elements:
97
+ # Add selectors based on analyzed elements
98
+ element_counts = {}
99
+ for elem in elements:
100
+ elem_type = elem['type']
101
+ if elem_type == 'indexed':
102
+ has_indexed = True
103
+ else:
104
+ count = element_counts.get(elem_type, 0)
105
+ if count == 0:
106
+ lines.append(f" {elem_type}: '{kebab_name}-{elem_type}',")
107
+ else:
108
+ lines.append(f" {elem_type}{count + 1}: '{kebab_name}-{elem_type}-{count + 1}',")
109
+ element_counts[elem_type] = count + 1
110
+
111
+ if has_indexed:
112
+ lines.append(f" item: '{kebab_name}-item-{{index}}',")
113
+ else:
114
+ # Default structure for new blocks
115
+ lines.append(f" // Add more selectors as needed:")
116
+ lines.append(f" // title: '{kebab_name}-title',")
117
+ lines.append(f" // item: '{kebab_name}-item-{{index}}',")
118
+
119
+ lines.append(" },")
120
+
121
+ return '\n'.join(lines)
122
+
123
+
124
+ def generate_full_example(block_name: str, theme: str, elements: List[Dict] = None) -> str:
125
+ """Generate full usage example matching project conventions."""
126
+ camel_name = to_camel_case(block_name)
127
+ pascal_name = to_pascal_case(block_name)
128
+ kebab_name = block_name
129
+
130
+ has_indexed = elements and any(e['type'] == 'indexed' for e in elements) if elements else False
131
+
132
+ return f'''
133
+ ================================================================================
134
+ BLOCK SELECTORS: {block_name}
135
+ ================================================================================
136
+
137
+ 1. ADD TO BLOCK_SELECTORS
138
+ File: contents/themes/{theme}/lib/selectors.ts
139
+
140
+ Find the BLOCK_SELECTORS constant and add:
141
+
142
+ {generate_selectors_entry(block_name, elements)}
143
+
144
+ --------------------------------------------------------------------------------
145
+
146
+ 2. COMPONENT USAGE
147
+ File: contents/themes/{theme}/blocks/{block_name}/component.tsx
148
+
149
+ Import:
150
+ ```typescript
151
+ import {{ sel }} from '../../lib/selectors'
152
+ ```
153
+
154
+ Usage in JSX:
155
+ ```tsx
156
+ <section data-cy={{sel('blocks.{camel_name}.container')}}>
157
+ <h2 data-cy={{sel('blocks.{camel_name}.title')}}>
158
+ {{title}}
159
+ </h2>
160
+ {has_indexed and f"""
161
+ {{items.map((item, index) => (
162
+ <div
163
+ key={{index}}
164
+ data-cy={{sel('blocks.{camel_name}.item', {{ index: String(index) }})}}
165
+ >
166
+ {{item.content}}
167
+ </div>
168
+ ))}}""" or ""}
169
+ </section>
170
+ ```
171
+
172
+ --------------------------------------------------------------------------------
173
+
174
+ 3. CYPRESS TEST USAGE
175
+ File: contents/themes/{theme}/tests/cypress/e2e/blocks/{block_name}.cy.ts
176
+
177
+ ```typescript
178
+ import {{ cySelector }} from '../../src/selectors'
179
+
180
+ describe('{pascal_name} Block', () => {{
181
+ beforeEach(() => {{
182
+ cy.visit('/page-with-{block_name}')
183
+ }})
184
+
185
+ it('should display the block', () => {{
186
+ cy.get(cySelector('blocks.{camel_name}.container')).should('be.visible')
187
+ }})
188
+ {has_indexed and f"""
189
+ it('should display items', () => {{
190
+ cy.get(cySelector('blocks.{camel_name}.item', {{ index: '0' }}))
191
+ .should('be.visible')
192
+ }})""" or ""}
193
+ }})
194
+ ```
195
+
196
+ --------------------------------------------------------------------------------
197
+
198
+ 4. POM USAGE (Optional - for complex blocks)
199
+ File: contents/themes/{theme}/tests/cypress/src/blocks/{pascal_name}POM.ts
200
+
201
+ ```typescript
202
+ import {{ BasePOM }} from '../core/BasePOM'
203
+ import {{ cySelector }} from '../selectors'
204
+
205
+ export class {pascal_name}POM extends BasePOM {{
206
+ get selectors() {{
207
+ return {{
208
+ container: cySelector('blocks.{camel_name}.container'),
209
+ {has_indexed and f"item: (index: number) => cySelector('blocks.{camel_name}.item', {{ index: String(index) }})," or ""}
210
+ }}
211
+ }}
212
+
213
+ // Factory method
214
+ static create(): {pascal_name}POM {{
215
+ return new {pascal_name}POM()
216
+ }}
217
+
218
+ // Helper methods
219
+ shouldBeVisible(): {pascal_name}POM {{
220
+ cy.get(this.selectors.container).should('be.visible')
221
+ return this
222
+ }}
223
+ }}
224
+ ```
225
+
226
+ ================================================================================
227
+ '''
228
+
229
+
230
+ def main():
231
+ parser = argparse.ArgumentParser(description='Generate block selectors')
232
+ parser.add_argument('--block', required=True, help='Block name (kebab-case)')
233
+ parser.add_argument('--theme', default=None, help='Theme name')
234
+ parser.add_argument('--analyze', action='store_true', help='Analyze existing block')
235
+ parser.add_argument('--full', action='store_true', help='Generate full example')
236
+ parser.add_argument('--dry-run', action='store_true', help='Print only selector entry')
237
+
238
+ args = parser.parse_args()
239
+
240
+ theme = args.theme or get_active_theme()
241
+ block_name = args.block.lower()
242
+
243
+ print(f"\n{'='*60}")
244
+ print(f"Generating selectors for block: {block_name}")
245
+ print(f"Theme: {theme}")
246
+ print(f"{'='*60}")
247
+
248
+ elements = None
249
+ if args.analyze:
250
+ block_path = Path(f'contents/themes/{theme}/blocks/{block_name}')
251
+ if block_path.exists():
252
+ elements = analyze_block_component(block_path)
253
+ if elements:
254
+ print(f"\nAnalyzed component - found {len(elements)} elements:")
255
+ for elem in elements:
256
+ print(f" - {elem['type']}")
257
+ else:
258
+ print(f"\nWarning: Block not found at {block_path}")
259
+ print("Run without --analyze to generate default selectors.")
260
+
261
+ if args.dry_run:
262
+ print("\nBLOCK_SELECTORS entry:")
263
+ print("-" * 40)
264
+ print(generate_selectors_entry(block_name, elements))
265
+ print("-" * 40)
266
+ elif args.full:
267
+ print(generate_full_example(block_name, theme, elements))
268
+ else:
269
+ # Default: show selector entry with brief instructions
270
+ print("\nAdd to BLOCK_SELECTORS in lib/selectors.ts:")
271
+ print("-" * 40)
272
+ print(generate_selectors_entry(block_name, elements))
273
+ print("-" * 40)
274
+ print(f"\nUsage in component:")
275
+ print(f" import {{ sel }} from '../../lib/selectors'")
276
+ print(f" <div data-cy={{sel('blocks.{to_camel_case(block_name)}.container')}}>")
277
+ print(f"\nFor full example run with --full flag")
278
+
279
+ return 0
280
+
281
+
282
+ if __name__ == '__main__':
283
+ sys.exit(main())
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate Selectors Script
4
+
5
+ Validates that all data-cy attributes in components use the sel() function
6
+ instead of hardcoded strings.
7
+
8
+ Usage:
9
+ python validate-selectors.py [--path PATH] [--fix]
10
+
11
+ Options:
12
+ --path PATH Directory to scan (default: contents/themes/)
13
+ --fix Attempt to auto-fix simple violations
14
+ """
15
+
16
+ import os
17
+ import re
18
+ import sys
19
+ import argparse
20
+ from pathlib import Path
21
+ from typing import List, Tuple, Dict
22
+
23
+ # Patterns
24
+ HARDCODED_DATA_CY = re.compile(r'data-cy=["\']([^"\']+)["\']')
25
+ VALID_SEL_PATTERN = re.compile(r'data-cy=\{sel\(["\']([^"\']+)["\']')
26
+ VALID_S_PATTERN = re.compile(r'data-cy=\{s\(["\']([^"\']+)["\']')
27
+
28
+
29
+ def find_tsx_files(path: str) -> List[Path]:
30
+ """Find all TSX files in the given path."""
31
+ root = Path(path)
32
+ return list(root.rglob("*.tsx"))
33
+
34
+
35
+ def analyze_file(file_path: Path) -> Dict:
36
+ """Analyze a single file for selector issues."""
37
+ issues = []
38
+ valid_count = 0
39
+
40
+ with open(file_path, 'r', encoding='utf-8') as f:
41
+ content = f.read()
42
+ lines = content.split('\n')
43
+
44
+ for line_num, line in enumerate(lines, 1):
45
+ # Check for hardcoded data-cy
46
+ hardcoded = HARDCODED_DATA_CY.findall(line)
47
+ for selector in hardcoded:
48
+ issues.append({
49
+ 'line': line_num,
50
+ 'type': 'hardcoded',
51
+ 'selector': selector,
52
+ 'context': line.strip()
53
+ })
54
+
55
+ # Count valid usages
56
+ valid_sel = VALID_SEL_PATTERN.findall(line)
57
+ valid_s = VALID_S_PATTERN.findall(line)
58
+ valid_count += len(valid_sel) + len(valid_s)
59
+
60
+ return {
61
+ 'file': str(file_path),
62
+ 'issues': issues,
63
+ 'valid_count': valid_count
64
+ }
65
+
66
+
67
+ def print_report(results: List[Dict], verbose: bool = False) -> Tuple[int, int]:
68
+ """Print validation report."""
69
+ total_issues = 0
70
+ total_valid = 0
71
+ files_with_issues = []
72
+
73
+ for result in results:
74
+ total_valid += result['valid_count']
75
+ if result['issues']:
76
+ files_with_issues.append(result)
77
+ total_issues += len(result['issues'])
78
+
79
+ print("\n" + "=" * 60)
80
+ print("CYPRESS SELECTORS VALIDATION REPORT")
81
+ print("=" * 60)
82
+
83
+ if files_with_issues:
84
+ print(f"\n{'VIOLATIONS FOUND':^60}")
85
+ print("-" * 60)
86
+
87
+ for result in files_with_issues:
88
+ print(f"\n{result['file']}")
89
+ for issue in result['issues']:
90
+ print(f" Line {issue['line']}: {issue['type'].upper()}")
91
+ print(f" Selector: {issue['selector']}")
92
+ if verbose:
93
+ print(f" Context: {issue['context'][:80]}...")
94
+ print(f" Fix: Use sel('{convert_to_path(issue['selector'])}')")
95
+ else:
96
+ print(f"\n{'NO VIOLATIONS FOUND':^60}")
97
+
98
+ print("\n" + "-" * 60)
99
+ print(f"Total files scanned: {len(results)}")
100
+ print(f"Valid sel() usages: {total_valid}")
101
+ print(f"Violations found: {total_issues}")
102
+ print("=" * 60 + "\n")
103
+
104
+ return total_issues, total_valid
105
+
106
+
107
+ def convert_to_path(selector: str) -> str:
108
+ """Convert a hardcoded selector to a sel() path suggestion."""
109
+ # Split by common separators
110
+ parts = re.split(r'[-_]', selector)
111
+
112
+ # Try to create a reasonable path
113
+ if len(parts) >= 2:
114
+ return f"{parts[0]}.{'.'.join(parts[1:])}"
115
+ return selector
116
+
117
+
118
+ def main():
119
+ parser = argparse.ArgumentParser(description='Validate Cypress selectors')
120
+ parser.add_argument('--path', default='contents/themes/', help='Directory to scan')
121
+ parser.add_argument('--verbose', '-v', action='store_true', help='Show context')
122
+ parser.add_argument('--exit-code', action='store_true', help='Exit with error if violations found')
123
+
124
+ args = parser.parse_args()
125
+
126
+ # Find files
127
+ files = find_tsx_files(args.path)
128
+
129
+ if not files:
130
+ print(f"No .tsx files found in {args.path}")
131
+ return 0
132
+
133
+ # Analyze files
134
+ results = [analyze_file(f) for f in files]
135
+
136
+ # Print report
137
+ violations, valid = print_report(results, args.verbose)
138
+
139
+ if args.exit_code and violations > 0:
140
+ return 1
141
+ return 0
142
+
143
+
144
+ if __name__ == '__main__':
145
+ sys.exit(main())