@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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- 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())
|