lazycodex-ai 5.1.44 β 5.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +0 -5
- package/README.es.md +0 -5
- package/README.fr.md +0 -5
- package/README.it.md +0 -5
- package/README.ja.md +0 -5
- package/README.ko.md +0 -5
- package/README.md +4 -5
- package/README.pt-br.md +0 -5
- package/README.ru.md +0 -6
- package/README.zh-cn.md +0 -5
- package/dist/cli/index.js +865 -413
- package/dist/cli/service-auth/keystore.d.ts +4 -2
- package/dist/cli-node/index.js +865 -413
- package/docs/reference/web-terminal-visual-qa.md +8 -0
- package/package.json +1 -1
- package/packages/lsp-daemon/dist/cli.js +7 -7
- package/packages/lsp-daemon/dist/client.js +7 -7
- package/packages/lsp-daemon/dist/index.js +7 -7
- package/packages/lsp-daemon/package.json +3 -3
- package/packages/lsp-tools-mcp/dist/cli.js +7 -7
- package/packages/lsp-tools-mcp/dist/mcp.js +7 -7
- package/packages/lsp-tools-mcp/dist/tools.js +4 -4
- package/packages/lsp-tools-mcp/package.json +3 -3
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +77 -26
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/hook.ts +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/biome.json +1 -21
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/biome.json +1 -21
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/biome.json +1 -21
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +4 -4
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +0 -1
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +1 -1
- package/packages/omo-codex/plugin/components/rules/biome.json +1 -21
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +4 -4
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/biome.json +1 -21
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +90 -1
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +91 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +2 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -9
- package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/browser/SKILL.md +21 -22
- package/packages/omo-codex/plugin/skills/browser/references/commands.md +2 -9
- package/packages/omo-codex/plugin/skills/browser/references/install.md +1 -2
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +79 -118
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +75 -224
- package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/README.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/omo-codex/plugin/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/omo-codex/plugin/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +14 -14
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +21 -3
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +37 -12
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +68 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claims-index.mjs +125 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +89 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +29 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-text.mjs +1 -27
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/lexicon.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +226 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/rational.mjs +139 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sentence-split.mjs +77 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +105 -0
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +71 -64
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +4 -1
- package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +12 -8
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -30
- package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
- package/packages/shared-skills/skills/browser/SKILL.md +21 -22
- package/packages/shared-skills/skills/browser/references/commands.md +2 -9
- package/packages/shared-skills/skills/browser/references/install.md +1 -2
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/shared-skills/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/shared-skills/skills/frontend/SKILL.md +79 -118
- package/packages/shared-skills/skills/frontend/references/design/README.md +75 -224
- package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/shared-skills/skills/frontend/references/perfection/README.md +2 -2
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/shared-skills/skills/programming/SKILL.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/README.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/shared-skills/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/shared-skills/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.test.ts +162 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/shared-skills/skills/refactor/SKILL.md +13 -13
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +21 -3
- package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +68 -3
- package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claims-index.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claims-index.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +89 -3
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +100 -12
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +197 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +254 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-context.test.ts +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +29 -9
- package/packages/shared-skills/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/gates-text.mjs +1 -27
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/lexicon.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/lexicon.test.ts +57 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +226 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +120 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +29 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/shared-skills/skills/ulw-research/scripts/rational.mjs +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/rational.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
- package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.mjs +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/claim-graph.md +13 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-locked.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.test.ts +38 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +105 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +83 -0
- package/packages/shared-skills/skills/visual-qa/SKILL.md +70 -63
- package/packages/shared-skills/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
- package/script/qa/web-terminal-visual-qa.mjs +6 -2
- package/script/qa/xterm-live-terminal.mjs +26 -3
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { readdirSync, readFileSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import type { DraftRuleId } from "./draft-guard/findings"
|
|
6
|
+
import { lintDraft } from "./draft-guard/lint"
|
|
7
|
+
|
|
8
|
+
const FIXTURES = join(import.meta.dir, "fixtures", "draft-guard")
|
|
9
|
+
const ACCENT_FIXTURES = join(import.meta.dir, "fixtures", "accent-stripes")
|
|
10
|
+
const SCRIPT = join(import.meta.dir, "check-draft-guard.ts")
|
|
11
|
+
|
|
12
|
+
const EXPECTED: Readonly<Record<string, ReadonlyArray<[number, DraftRuleId]>>> = {
|
|
13
|
+
"promo-copy.html": [[2, "promo-copy"], [3, "promo-copy"], [5, "promo-copy"]],
|
|
14
|
+
"promo-copy.tsx": [[4, "promo-copy"], [5, "promo-copy"]],
|
|
15
|
+
"emoji-icons.tsx": [[4, "emoji-icon"], [6, "emoji-icon"]],
|
|
16
|
+
"vanity-stats.html": [[2, "vanity-stat"], [3, "vanity-stat"], [4, "vanity-stat"]],
|
|
17
|
+
"decorative-badge.vue": [[3, "decorative-badge"], [4, "decorative-badge"]],
|
|
18
|
+
"components.tsx": [
|
|
19
|
+
[5, "decorative-badge"],
|
|
20
|
+
[6, "decorative-badge"],
|
|
21
|
+
[7, "emoji-icon"],
|
|
22
|
+
[7, "decorative-badge"],
|
|
23
|
+
[8, "emoji-icon"],
|
|
24
|
+
[9, "emoji-icon"],
|
|
25
|
+
[10, "vanity-stat"],
|
|
26
|
+
[11, "vanity-stat"],
|
|
27
|
+
[12, "vanity-stat"],
|
|
28
|
+
[13, "promo-copy"],
|
|
29
|
+
],
|
|
30
|
+
"attribute-edges.html": [[2, "promo-copy"], [3, "promo-copy"], [4, "vanity-stat"], [5, "decorative-badge"], [6, "promo-copy"]],
|
|
31
|
+
"jsx-edges.tsx": [[1, "promo-copy"]],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function lintFixture(dir: string, name: string) {
|
|
35
|
+
const path = join(FIXTURES, dir, name)
|
|
36
|
+
return lintDraft([{ path, text: readFileSync(path, "utf8") }])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("#given a positive fixture with a generated-look tell in its markup", () => {
|
|
40
|
+
test("#then every positive fixture has an expectation", () => {
|
|
41
|
+
expect(readdirSync(join(FIXTURES, "positive")).sort()).toEqual(Object.keys(EXPECTED).sort())
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
for (const [name, expected] of Object.entries(EXPECTED)) {
|
|
45
|
+
test(`#when ${name} is linted #then it reports exactly ${expected.length} finding(s) at the expected lines`, () => {
|
|
46
|
+
const findings = lintFixture("positive", name).map((f): [number, DraftRuleId] => [f.line, f.rule])
|
|
47
|
+
expect(findings).toEqual([...expected])
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe("#given a negative fixture (plain copy, code, scripts, styles, comments, bound values, status badges)", () => {
|
|
53
|
+
for (const name of readdirSync(join(FIXTURES, "negative"))) {
|
|
54
|
+
test(`#when ${name} is linted #then it reports nothing`, () => {
|
|
55
|
+
expect(lintFixture("negative", name)).toEqual([])
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe("#given a generated page written on one line", () => {
|
|
61
|
+
test("#when it holds two promo phrases #then both are reported, each at its own column", () => {
|
|
62
|
+
const text = "<p>Seamless sync and effortless setup</p>\n"
|
|
63
|
+
const findings = lintDraft([{ path: "page.html", text }])
|
|
64
|
+
expect(findings.map((f) => [f.line, f.column, f.rule])).toEqual([
|
|
65
|
+
[1, text.indexOf("Seamless") + 1, "promo-copy"],
|
|
66
|
+
[1, text.indexOf("effortless") + 1, "promo-copy"],
|
|
67
|
+
])
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe("#given a stylesheet", () => {
|
|
72
|
+
test("#when it is linted #then text rules are not applied to CSS", () => {
|
|
73
|
+
expect(lintDraft([{ path: "a.css", text: '.hero::before { content: "Unlock π"; }\n' }])).toEqual([])
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe("#given the CLI", () => {
|
|
78
|
+
const run = (...args: string[]) => spawnSync(process.execPath, [SCRIPT, ...args], { encoding: "utf8", timeout: 30_000 })
|
|
79
|
+
|
|
80
|
+
test("#when it scans the draft-guard positives #then it exits 1 and prints file:line findings", () => {
|
|
81
|
+
const result = run(join(FIXTURES, "positive"))
|
|
82
|
+
expect(result.status).toBe(1)
|
|
83
|
+
expect(result.stdout).toMatch(/vanity-stats\.html:2:\d+ {2}vanity-stat/)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test("#when it scans an accent-stripe positive #then the accent-stripe rules still report through it", () => {
|
|
87
|
+
const result = run("--json", join(ACCENT_FIXTURES, "positive", "border-top-card.css"))
|
|
88
|
+
expect(JSON.parse(result.stdout)).toEqual([expect.objectContaining({ line: 4, rule: "edge-stripe" })])
|
|
89
|
+
expect(result.status).toBe(1)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("#when it scans both negative fixture sets #then it exits 0", () => {
|
|
93
|
+
expect(run(join(FIXTURES, "negative"), join(ACCENT_FIXTURES, "negative")).status).toBe(0)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("#when no path is given #then it exits 2 with usage", () => {
|
|
97
|
+
const result = run()
|
|
98
|
+
expect(result.status).toBe(2)
|
|
99
|
+
expect(result.stderr).toContain("usage:")
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// The draft guard in one command: accent stripes plus generated-look copy and markup (promo copy, emoji icons, hype badges, vanity stats).
|
|
3
|
+
// Usage: bun check-draft-guard.ts [--json] <file-or-dir>... Exit 0 clean, 1 findings, 2 usage or unreadable target.
|
|
4
|
+
import { readFileSync } from "node:fs"
|
|
5
|
+
import { RULE_MESSAGES } from "./accent-stripes/findings"
|
|
6
|
+
import { lintFiles, type SourceFile } from "./accent-stripes/lint"
|
|
7
|
+
import { collectFiles } from "./collect-files"
|
|
8
|
+
import { DRAFT_RULE_MESSAGES } from "./draft-guard/findings"
|
|
9
|
+
import { lintDraft } from "./draft-guard/lint"
|
|
10
|
+
|
|
11
|
+
const MESSAGES: Readonly<Record<string, string>> = { ...RULE_MESSAGES, ...DRAFT_RULE_MESSAGES }
|
|
12
|
+
|
|
13
|
+
function main(argv: readonly string[]): number {
|
|
14
|
+
const json = argv.includes("--json")
|
|
15
|
+
const targets = argv.filter((a) => a !== "--json")
|
|
16
|
+
if (targets.length === 0) {
|
|
17
|
+
process.stderr.write("usage: check-draft-guard.ts [--json] <file-or-dir>...\n")
|
|
18
|
+
return 2
|
|
19
|
+
}
|
|
20
|
+
const { paths, missing } = collectFiles(targets)
|
|
21
|
+
if (missing.length > 0) {
|
|
22
|
+
process.stderr.write(`check-draft-guard: cannot read ${missing.join(", ")}\n`)
|
|
23
|
+
return 2
|
|
24
|
+
}
|
|
25
|
+
const files: SourceFile[] = paths.map((path) => ({ path, text: readFileSync(path, "utf8") }))
|
|
26
|
+
const findings = [...lintFiles(files), ...lintDraft(files)].sort(
|
|
27
|
+
(a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column,
|
|
28
|
+
)
|
|
29
|
+
if (json) process.stdout.write(`${JSON.stringify(findings, null, 2)}\n`)
|
|
30
|
+
else {
|
|
31
|
+
for (const f of findings) {
|
|
32
|
+
process.stdout.write(`${f.file}:${f.line}:${f.column} ${f.rule} ${MESSAGES[f.rule]}\n > ${f.excerpt}\n`)
|
|
33
|
+
}
|
|
34
|
+
const summary = findings.length === 0 ? "clean" : `${findings.length} finding(s); fix each before visual QA`
|
|
35
|
+
process.stdout.write(`check-draft-guard: ${paths.length} file(s), ${summary}\n`)
|
|
36
|
+
}
|
|
37
|
+
return findings.length === 0 ? 0 : 1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
process.exitCode = main(process.argv.slice(2))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { collectFiles } from "./collect-files"
|
|
6
|
+
|
|
7
|
+
// Owner test for the file-walk contract both perfection CLIs share (check-accent-stripes.ts, check-draft-guard.ts).
|
|
8
|
+
function withDir(run: (dir: string) => void): void {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "collect-files-"))
|
|
10
|
+
try {
|
|
11
|
+
run(dir)
|
|
12
|
+
} finally {
|
|
13
|
+
chmodSync(dir, 0o700)
|
|
14
|
+
rmSync(dir, { recursive: true, force: true })
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Permission bits do not stop root, and Windows ignores chmod, so the unreadable-directory case cannot be built there.
|
|
19
|
+
const cannotBuildUnreadableDir = process.platform === "win32" || process.getuid?.() === 0
|
|
20
|
+
|
|
21
|
+
describe("#given a scanned directory", () => {
|
|
22
|
+
test("#when it holds a dangling symlink #then the link is skipped and the rest is collected", () => {
|
|
23
|
+
withDir((dir) => {
|
|
24
|
+
symlinkSync(join(dir, "missing-target"), join(dir, "broken-link.tsx"))
|
|
25
|
+
writeFileSync(join(dir, "page.html"), "<p>ok</p>\n")
|
|
26
|
+
expect(collectFiles([dir])).toEqual({ paths: [join(dir, "page.html")], missing: [] })
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test.skipIf(cannotBuildUnreadableDir)("#when a nested directory cannot be listed #then it is skipped and the rest is collected", () => {
|
|
31
|
+
withDir((dir) => {
|
|
32
|
+
const locked = join(dir, "locked")
|
|
33
|
+
mkdirSync(locked)
|
|
34
|
+
writeFileSync(join(locked, "hidden.css"), ".a {}\n")
|
|
35
|
+
writeFileSync(join(dir, "page.html"), "<p>ok</p>\n")
|
|
36
|
+
chmodSync(locked, 0o000)
|
|
37
|
+
try {
|
|
38
|
+
expect(collectFiles([dir])).toEqual({ paths: [join(dir, "page.html")], missing: [] })
|
|
39
|
+
} finally {
|
|
40
|
+
chmodSync(locked, 0o700)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("#when an explicit target does not exist #then it is reported as missing", () => {
|
|
46
|
+
withDir((dir) => {
|
|
47
|
+
expect(collectFiles([join(dir, "nope")])).toEqual({ paths: [], missing: [join(dir, "nope")] })
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readdirSync, statSync, type Stats } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
|
|
4
|
+
const SCANNED = /\.(?:css|scss|less|pcss|postcss|html?|[cm]?[jt]sx?|vue|svelte|astro)$/i
|
|
5
|
+
const SKIPPED_DIRS = new Set([
|
|
6
|
+
"node_modules", ".git", "dist", "build", "out", ".next", ".nuxt", ".svelte-kit", ".astro",
|
|
7
|
+
".turbo", ".cache", "coverage", "vendor", ".vercel", ".output",
|
|
8
|
+
])
|
|
9
|
+
const MAX_BYTES = 1_000_000
|
|
10
|
+
|
|
11
|
+
function statOrNull(path: string): Stats | null {
|
|
12
|
+
try {
|
|
13
|
+
return statSync(path)
|
|
14
|
+
} catch {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function entriesOrNull(path: string): string[] | null {
|
|
20
|
+
try {
|
|
21
|
+
return readdirSync(path)
|
|
22
|
+
} catch {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Lintable source files under the targets; dangling links and unreadable entries are skipped, an unreadable explicit target is reported. */
|
|
28
|
+
export function collectFiles(targets: readonly string[]): { readonly paths: string[]; readonly missing: string[] } {
|
|
29
|
+
const paths: string[] = []
|
|
30
|
+
const missing: string[] = []
|
|
31
|
+
const walk = (path: string, explicit: boolean): void => {
|
|
32
|
+
const stat = statOrNull(path)
|
|
33
|
+
if (!stat) {
|
|
34
|
+
if (explicit) missing.push(path)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if (stat.isDirectory()) {
|
|
38
|
+
const entries = entriesOrNull(path)
|
|
39
|
+
if (entries === null) {
|
|
40
|
+
if (explicit) missing.push(path)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!SKIPPED_DIRS.has(entry)) walk(join(path, entry), false)
|
|
45
|
+
}
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
const minified = /\.min\.[a-z]+$/i.test(path) || path.endsWith(".d.ts")
|
|
49
|
+
if (SCANNED.test(path) && !minified && stat.size <= MAX_BYTES) paths.push(path)
|
|
50
|
+
}
|
|
51
|
+
for (const target of targets) walk(target, true)
|
|
52
|
+
return { paths, missing }
|
|
53
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type DraftRuleId = "promo-copy" | "emoji-icon" | "decorative-badge" | "vanity-stat"
|
|
2
|
+
|
|
3
|
+
export type DraftFinding = {
|
|
4
|
+
readonly file: string
|
|
5
|
+
readonly line: number
|
|
6
|
+
readonly column: number
|
|
7
|
+
readonly rule: DraftRuleId
|
|
8
|
+
readonly excerpt: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const DRAFT_RULE_MESSAGES: Readonly<Record<DraftRuleId, string>> = {
|
|
12
|
+
"promo-copy": "promotional filler in visible copy; say what the screen does or shows instead",
|
|
13
|
+
"emoji-icon": "emoji used as an icon or bullet; use an SVG icon from the project's icon set, or nothing",
|
|
14
|
+
"decorative-badge": "badge or chip carrying a hype label instead of a state; remove it or show a real status",
|
|
15
|
+
"vanity-stat": "stat tile with a marketing-form number (10x, 500+, 24/7); show the measured figure with its unit and period, or remove the tile",
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { lineLocator } from "../accent-stripes/findings"
|
|
2
|
+
import type { SourceFile } from "../accent-stripes/lint"
|
|
3
|
+
import type { DraftFinding } from "./findings"
|
|
4
|
+
import { checkNode } from "./rules"
|
|
5
|
+
import { type MarkupKind, textNodes } from "./text-nodes"
|
|
6
|
+
|
|
7
|
+
function markupKind(path: string): MarkupKind | null {
|
|
8
|
+
if (/\.html?$/i.test(path)) return "html"
|
|
9
|
+
if (/\.[cm]?[jt]sx$/i.test(path)) return "jsx"
|
|
10
|
+
if (/\.(?:vue|svelte|astro)$/i.test(path)) return "sfc"
|
|
11
|
+
return null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function excerptAt(text: string, offset: number): string {
|
|
15
|
+
const lineStart = text.lastIndexOf("\n", offset - 1) + 1
|
|
16
|
+
const lineEnd = text.indexOf("\n", offset)
|
|
17
|
+
return text.slice(lineStart, lineEnd < 0 ? undefined : lineEnd).trim().slice(0, 140)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function lintDraft(files: readonly SourceFile[]): DraftFinding[] {
|
|
21
|
+
const findings: DraftFinding[] = []
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const kind = markupKind(file.path)
|
|
24
|
+
if (kind === null) continue
|
|
25
|
+
const locate = lineLocator(file.text)
|
|
26
|
+
const seen = new Set<string>()
|
|
27
|
+
for (const node of textNodes(file.text, kind)) {
|
|
28
|
+
for (const hit of checkNode(node)) {
|
|
29
|
+
const at = locate(hit.offset)
|
|
30
|
+
const key = `${hit.offset}:${hit.rule}`
|
|
31
|
+
if (seen.has(key)) continue
|
|
32
|
+
seen.add(key)
|
|
33
|
+
findings.push({ file: file.path, ...at, rule: hit.rule, excerpt: excerptAt(file.text, hit.offset) })
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column)
|
|
38
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { DraftRuleId } from "./findings"
|
|
2
|
+
import { type Element, isStatElement, type TextNode } from "./text-nodes"
|
|
3
|
+
|
|
4
|
+
export type RuleHit = { readonly rule: DraftRuleId; readonly offset: number }
|
|
5
|
+
|
|
6
|
+
const PROMO_PHRASES = new RegExp(
|
|
7
|
+
[
|
|
8
|
+
String.raw`\bseamless(?:ly)?\b`,
|
|
9
|
+
String.raw`\beffortless(?:ly)?\b`,
|
|
10
|
+
String.raw`\bsupercharge[sd]?\b`,
|
|
11
|
+
String.raw`\bunlock (?:the (?:full )?power|the full potential|your (?:full )?potential)\b`,
|
|
12
|
+
String.raw`\bat a glance\b`,
|
|
13
|
+
String.raw`\bcutting[- ]edge\b`,
|
|
14
|
+
String.raw`\bgame[- ]chang(?:er|ing)\b`,
|
|
15
|
+
String.raw`\brevolutioni[sz]e\b`,
|
|
16
|
+
String.raw`\bnext[- ]level\b`,
|
|
17
|
+
String.raw`\bworld[- ]class\b`,
|
|
18
|
+
String.raw`\belevate your\b`,
|
|
19
|
+
"νλμ",
|
|
20
|
+
"μμ½κ²",
|
|
21
|
+
"μ€λ§νΈν",
|
|
22
|
+
"νμ μ μΈ",
|
|
23
|
+
].join("|"),
|
|
24
|
+
"giu",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const EMOJI = /\p{Emoji_Presentation}|\p{Extended_Pictographic}\uFE0F/u
|
|
28
|
+
const EMOJI_ALL = /\p{Emoji_Presentation}|\p{Extended_Pictographic}\uFE0F?/gu
|
|
29
|
+
const NUMERIC_ENTITY = /&#(?:x([0-9a-f]+)|(\d+));/gi
|
|
30
|
+
const BADGE_CLASS = /^(?:badge|chip|pill|tag)(?:$|[-_])/
|
|
31
|
+
const BADGE_COMPONENT = /^(?:Badge|Chip|Pill|Tag)$/
|
|
32
|
+
const HYPE_LABEL = /^(?:new!?|hot|trending|popular|featured|ai|ai[- ]powered|premium|recommended|best|μ κ·|μΈκΈ°|μΆμ²|λ² μ€νΈ)$/i
|
|
33
|
+
const AMOUNT = String.raw`(?:\d{1,3}(?:[.,]\d{3})+|\d+)(?:[.,]\d+)?`
|
|
34
|
+
const MARKETING_NUMBER = new RegExp(String.raw`^(?:[$β¬Β£Β₯β©]?${AMOUNT}\s*(?:[kmb]|%)?\s*\+|${AMOUNT}\s*[xΓ]|24\/7)$`, "i")
|
|
35
|
+
|
|
36
|
+
const isBadge = (element: Element | undefined): boolean =>
|
|
37
|
+
element !== undefined && !element.stateful && (BADGE_COMPONENT.test(element.name) || element.classes.some((c) => BADGE_CLASS.test(c)))
|
|
38
|
+
|
|
39
|
+
function leadingOffset(node: TextNode): number {
|
|
40
|
+
return node.offset + (node.text.length - node.text.trimStart().length)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function emojiOffset(text: string): number | null {
|
|
44
|
+
const direct = EMOJI.exec(text)
|
|
45
|
+
if (direct) return direct.index
|
|
46
|
+
for (const m of text.matchAll(NUMERIC_ENTITY)) {
|
|
47
|
+
const code = m[1] === undefined ? Number(m[2]) : Number.parseInt(m[1], 16)
|
|
48
|
+
if (Number.isInteger(code) && code <= 0x10ffff && EMOJI.test(String.fromCodePoint(code))) return m.index ?? 0
|
|
49
|
+
}
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** A stat is a vanity number only in marketing form (10x, 500+, $2M+, 24/7); a measured figure such as 42% or 1.2k passes. */
|
|
54
|
+
export function checkNode(node: TextNode): RuleHit[] {
|
|
55
|
+
const hits: RuleHit[] = []
|
|
56
|
+
const label = node.text.trim()
|
|
57
|
+
if (node.kind !== "value") {
|
|
58
|
+
for (const m of node.text.matchAll(PROMO_PHRASES)) hits.push({ rule: "promo-copy", offset: node.offset + (m.index ?? 0) })
|
|
59
|
+
const emoji = emojiOffset(node.text)
|
|
60
|
+
if (emoji !== null) hits.push({ rule: "emoji-icon", offset: node.offset + emoji })
|
|
61
|
+
}
|
|
62
|
+
if (node.kind === "text" && isBadge(node.stack.at(-1)) && HYPE_LABEL.test(label.replace(EMOJI_ALL, "").trim())) {
|
|
63
|
+
hits.push({ rule: "decorative-badge", offset: leadingOffset(node) })
|
|
64
|
+
}
|
|
65
|
+
if (node.kind !== "label" && node.stack.some(isStatElement) && MARKETING_NUMBER.test(label)) {
|
|
66
|
+
hits.push({ rule: "vanity-stat", offset: leadingOffset(node) })
|
|
67
|
+
}
|
|
68
|
+
return hits
|
|
69
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export type Element = {
|
|
2
|
+
readonly tag: string
|
|
3
|
+
readonly name: string
|
|
4
|
+
readonly classes: readonly string[]
|
|
5
|
+
readonly stateful: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** `text` is visible copy, `label` an announced attribute (aria-label, title, alt, placeholder), `value` a stat component's value prop. */
|
|
9
|
+
export type NodeKind = "text" | "label" | "value"
|
|
10
|
+
export type TextNode = { readonly kind: NodeKind; readonly text: string; readonly offset: number; readonly stack: readonly Element[] }
|
|
11
|
+
|
|
12
|
+
export type MarkupKind = "html" | "jsx" | "sfc"
|
|
13
|
+
|
|
14
|
+
const NEVER_RENDERED = /<(script|style|pre|code|textarea|noscript)\b[^>]*>[\s\S]*?<\/\1\s*>|<!--[\s\S]*?-->|\{\/\*[\s\S]*?\*\/\}/gi
|
|
15
|
+
const HTML_TEMPLATE = /<template\b(?![^>]*\s(?:x-[\w-]+|v-[\w-]+|[:@#][\w.-]+))[^>]*>[\s\S]*?<\/template\s*>/gi
|
|
16
|
+
const TAG = /<(\/?)([A-Za-z][\w.:-]*)((?:[^>"'{}]|"[^"]*"|'[^']*'|\{(?:[^{}]|\{[^{}]*\})*\})*)>/g
|
|
17
|
+
const CLASS_ATTR = /\b(?:class|className)\s*=\s*(?:"([^"]*)"|'([^']*)'|\{((?:[^{}]|\{[^{}]*\})*)\})/
|
|
18
|
+
const STRING_LITERAL = /["'`]([^"'`]*)["'`]/g
|
|
19
|
+
const ANNOUNCED_NAME = /(?:^|\s)(?:aria-label|title|alt|placeholder)\s*=/g
|
|
20
|
+
const VALUE_NAME = /(?:^|\s)value\s*=/
|
|
21
|
+
const LITERAL_VALUE = /^\s*(?:"([^"]*)"|'([^']*)'|\{\s*["'`]([^"'`]*)["'`]\s*\})/
|
|
22
|
+
const HIDDEN_ATTR = /(?:^|\s)hidden(?=\s|=|\/|$)/
|
|
23
|
+
const STATE_ATTR = /(?:^|\s)(?:data-status|data-state|aria-(?:current|selected|pressed|checked|expanded|busy|invalid|disabled|valuenow))\s*=/
|
|
24
|
+
const ATTR_VALUES = /"[^"]*"|'[^']*'|\{(?:[^{}]|\{[^{}]*\})*\}/g
|
|
25
|
+
const STAT_COMPONENT = /^(?:Stats?|Metrics?|Kpis?|KPIs?)(?:[A-Z]\w*)?$/
|
|
26
|
+
const STAT_CLASS = /^(?:stat|stats|metric|metrics|kpi)(?:$|[-_])/
|
|
27
|
+
const VOID_TAGS = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"])
|
|
28
|
+
const JSX_LEAD = new Set(["(", "[", "{", "}", ",", ":", "?", "=", ">", "&", "|", ";"])
|
|
29
|
+
|
|
30
|
+
function blank(text: string, pattern: RegExp): string {
|
|
31
|
+
return text.replace(pattern, (m) => m.replace(/[^\n]/g, " "))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function classesOf(attrs: string): string[] {
|
|
35
|
+
const m = CLASS_ATTR.exec(attrs)
|
|
36
|
+
if (!m) return []
|
|
37
|
+
const literal = m[1] ?? m[2]
|
|
38
|
+
const raw = literal ?? [...(m[3] ?? "").matchAll(STRING_LITERAL)].map((s) => s[1] ?? "").join(" ")
|
|
39
|
+
return raw.split(/\s+/).filter(Boolean)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isStatElement(element: Element): boolean {
|
|
43
|
+
return STAT_COMPONENT.test(element.name) || element.classes.some((c) => STAT_CLASS.test(c))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function closeTo(stack: Element[], tag: string): void {
|
|
47
|
+
const at = stack.map((e) => e.tag).lastIndexOf(tag)
|
|
48
|
+
if (at >= 0) stack.length = at
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** In a script, `<` opens JSX only where an expression can start; `Array<string>`, `pick<T>(` and `a<b` stay code. */
|
|
52
|
+
function opensJsx(text: string, start: number, name: string, attrs: string): boolean {
|
|
53
|
+
if (/^\s*,|^\s+extends\b/.test(attrs)) return false
|
|
54
|
+
if (/^(?:const|in|out)$/.test(name) && /^\s+[A-Za-z_$]/.test(attrs)) return false
|
|
55
|
+
const before = text.slice(0, start).trimEnd()
|
|
56
|
+
if (before === "" || /\b(?:return|yield|default)$/.test(before)) return true
|
|
57
|
+
return JSX_LEAD.has(before.at(-1) ?? "")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function literalAfter(attrs: string, from: number): { readonly value: string; readonly at: number } | null {
|
|
61
|
+
const m = LITERAL_VALUE.exec(attrs.slice(from))
|
|
62
|
+
if (!m) return null
|
|
63
|
+
const value = m[1] ?? m[2] ?? m[3] ?? ""
|
|
64
|
+
return { value, at: from + m[0].lastIndexOf(value) }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function keepLiterals(segment: string): string {
|
|
68
|
+
return segment.replace(/\{[^{}]*\}/g, (m) => {
|
|
69
|
+
const lit = /^\{\s*(["'`])([^"'`]*)\1\s*\}$/.exec(m)
|
|
70
|
+
if (!lit) return " ".repeat(m.length)
|
|
71
|
+
const inner = lit[2] ?? ""
|
|
72
|
+
const at = m.indexOf(lit[1] ?? "") + 1
|
|
73
|
+
return " ".repeat(at) + inner + " ".repeat(m.length - at - inner.length)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Copy a reader sees or hears, each piece with the elements open around it. Code, hidden elements and never-rendered blocks are skipped. */
|
|
78
|
+
export function textNodes(source: string, kind: MarkupKind): TextNode[] {
|
|
79
|
+
const text = kind === "html" ? blank(blank(source, NEVER_RENDERED), HTML_TEMPLATE) : blank(source, NEVER_RENDERED)
|
|
80
|
+
const nodes: TextNode[] = []
|
|
81
|
+
const stack: Element[] = []
|
|
82
|
+
let hiddenDepth = 0
|
|
83
|
+
let cursor = 0
|
|
84
|
+
const flush = (end: number) => {
|
|
85
|
+
if (stack.length === 0 || hiddenDepth > 0) return
|
|
86
|
+
const segment = keepLiterals(text.slice(cursor, end))
|
|
87
|
+
if (segment.trim()) nodes.push({ kind: "text", text: segment, offset: cursor, stack: [...stack] })
|
|
88
|
+
}
|
|
89
|
+
for (const m of text.matchAll(TAG)) {
|
|
90
|
+
const start = m.index ?? 0
|
|
91
|
+
const [whole, closing, rawName = "", attrs = ""] = m
|
|
92
|
+
const end = start + whole.length
|
|
93
|
+
if (kind === "jsx" && !closing && stack.length === 0 && !opensJsx(text, start, rawName, attrs)) continue
|
|
94
|
+
flush(start)
|
|
95
|
+
cursor = end
|
|
96
|
+
const tag = rawName.toLowerCase()
|
|
97
|
+
if (closing) {
|
|
98
|
+
const at = stack.map((e) => e.tag).lastIndexOf(tag)
|
|
99
|
+
if (at >= 0 && hiddenDepth > at) hiddenDepth = 0
|
|
100
|
+
closeTo(stack, tag)
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
const names = attrs.replace(ATTR_VALUES, (v) => " ".repeat(v.length))
|
|
104
|
+
const element: Element = { tag, name: rawName, classes: classesOf(attrs), stateful: STATE_ATTR.test(names) }
|
|
105
|
+
const attrsAt = start + 1 + rawName.length
|
|
106
|
+
const hidden = hiddenDepth > 0 || HIDDEN_ATTR.test(names)
|
|
107
|
+
if (!hidden) {
|
|
108
|
+
for (const a of names.matchAll(ANNOUNCED_NAME)) {
|
|
109
|
+
const lit = literalAfter(attrs, (a.index ?? 0) + a[0].length)
|
|
110
|
+
if (lit?.value.trim()) nodes.push({ kind: "label", text: lit.value, offset: attrsAt + lit.at, stack: [...stack, element] })
|
|
111
|
+
}
|
|
112
|
+
const v = isStatElement(element) ? VALUE_NAME.exec(names) : null
|
|
113
|
+
const lit = v ? literalAfter(attrs, v.index + v[0].length) : null
|
|
114
|
+
if (lit?.value.trim()) nodes.push({ kind: "value", text: lit.value, offset: attrsAt + lit.at, stack: [...stack, element] })
|
|
115
|
+
}
|
|
116
|
+
if (VOID_TAGS.has(tag) || attrs.trimEnd().endsWith("/")) continue
|
|
117
|
+
stack.push(element)
|
|
118
|
+
if (hiddenDepth === 0 && HIDDEN_ATTR.test(names)) hiddenDepth = stack.length
|
|
119
|
+
}
|
|
120
|
+
return nodes
|
|
121
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function Bar() {
|
|
2
|
+
return (
|
|
3
|
+
<div data-title="Supercharge your team">
|
|
4
|
+
<StatusBar><span>24/7</span></StatusBar>
|
|
5
|
+
<span className="badge" aria-current="page">New</span>
|
|
6
|
+
<div data-x="a title='Supercharge your team'" />
|
|
7
|
+
<Stat data-value="10x" />
|
|
8
|
+
</div>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function Summary({ count, status, uptime }: { count: number; status: string; uptime: string }) {
|
|
2
|
+
return (
|
|
3
|
+
<section>
|
|
4
|
+
<div className="stat"><span className="stat-value">{count}</span><span>open tickets</span></div>
|
|
5
|
+
<div className="metric">{uptime}</div>
|
|
6
|
+
<span className="badge" data-status={status}>{status}</span>
|
|
7
|
+
<span className="badge">3</span>
|
|
8
|
+
<span className="chip">Failed</span>
|
|
9
|
+
<p>Seams in the fabric are reinforced.</p>
|
|
10
|
+
</section>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const labels: Array<string> = ["Unlock the power", "π launch"]
|
|
2
|
+
export const pick = <T,>(xs: T[]): T | undefined => xs[0]
|
|
3
|
+
export const first = <T extends object>(xs: T[]) => xs[0]
|
|
4
|
+
export const flag = (a: number, b: number) => (a<b && b>a ? "seamless sync" : "x")
|
|
5
|
+
export function Saved() {
|
|
6
|
+
return <p>Saved 3 files</p>
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<main>
|
|
2
|
+
<h1>Monthly revenue</h1>
|
|
3
|
+
<p>Revenue rose 4% after the refund fix shipped on 3 June.</p>
|
|
4
|
+
<pre><code>const seamless = "Supercharge"</code></pre>
|
|
5
|
+
<footer>Β© 2026 Example Inc. Next page β</footer>
|
|
6
|
+
<script>const label = "Unlock the power of your data π"</script>
|
|
7
|
+
<style>.stat::after { content: "10x"; }</style>
|
|
8
|
+
<!-- νλμ 보λ λ©λͺ¨ -->
|
|
9
|
+
</main>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<main>
|
|
2
|
+
<p>Unlock your account to continue.</p>
|
|
3
|
+
<p>μ΅μ μ κ²½λ‘λ₯Ό κ³μ°ν©λλ€.</p>
|
|
4
|
+
<p>Docs β</p>
|
|
5
|
+
<p>βΆ Play</p>
|
|
6
|
+
<li>Inbox <span class="badge" data-status="unread">New</span></li>
|
|
7
|
+
<span class="pill">Pro</span>
|
|
8
|
+
<div hidden>Seamless onboarding</div>
|
|
9
|
+
<noscript>Supercharge your browser</noscript>
|
|
10
|
+
<template><p>Unlock the power of templates</p></template>
|
|
11
|
+
</main>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<main>
|
|
2
|
+
<div class="block hidden md:flex"><p>Seamless sync</p></div>
|
|
3
|
+
<button title="Show hidden files"><span>Supercharge your team</span></button>
|
|
4
|
+
<div class="stat"><span>1,000,000+</span></div>
|
|
5
|
+
<span class="badge" aria-hidden="true">New</span>
|
|
6
|
+
<template x-if="open"><p>Effortless setup</p></template>
|
|
7
|
+
</main>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const cn = (...names: string[]) => names.join(" ")
|
|
2
|
+
export function Page() {
|
|
3
|
+
return (
|
|
4
|
+
<main>
|
|
5
|
+
<Badge>New</Badge>
|
|
6
|
+
<span className={cn("badge", "badge--hot")}>Hot</span>
|
|
7
|
+
<span className="badge">β¨ New</span>
|
|
8
|
+
<p>Launch 🚀 today</p>
|
|
9
|
+
<button type="button" aria-label="π Launch">Go</button>
|
|
10
|
+
<Stat value="10x" />
|
|
11
|
+
<StatCard><p>10x</p></StatCard>
|
|
12
|
+
<div className="metric">{"500+"}</div>
|
|
13
|
+
<input placeholder="Supercharge your search" />
|
|
14
|
+
</main>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Beta = () => <p>(beta) Seamless sync</p>
|