Repository: alpinejs/alpine Branch: main Commit: 1735d0bf0c62 Files: 294 Total size: 3.8 MB Directory structure: gitextract_pwh8a3u7/ ├── .claude/ │ └── skills/ │ ├── review-pr/ │ │ └── SKILL.md │ └── summarize-activity/ │ └── SKILL.md ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ └── config.yml │ ├── SECURITY.md │ └── workflows/ │ └── run-tests.yml ├── .gitignore ├── CLAUDE.md ├── LICENSE.md ├── README.md ├── benchmarks/ │ ├── giant.html │ ├── init.html │ ├── loop.html │ ├── memory.html │ └── mutation_observer.html ├── cypress.json ├── index.html ├── index2.html ├── jest.config.js ├── morph.html ├── package.json ├── packages/ │ ├── alpinejs/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ ├── alpine.js │ │ ├── binds.js │ │ ├── clone.js │ │ ├── datas.js │ │ ├── directives/ │ │ │ ├── index.js │ │ │ ├── x-bind.js │ │ │ ├── x-cloak.js │ │ │ ├── x-data.js │ │ │ ├── x-effect.js │ │ │ ├── x-for.js │ │ │ ├── x-html.js │ │ │ ├── x-id.js │ │ │ ├── x-if.js │ │ │ ├── x-ignore.js │ │ │ ├── x-init.js │ │ │ ├── x-model.js │ │ │ ├── x-modelable.js │ │ │ ├── x-on.js │ │ │ ├── x-ref.js │ │ │ ├── x-show.js │ │ │ ├── x-teleport.js │ │ │ ├── x-text.js │ │ │ └── x-transition.js │ │ ├── directives.js │ │ ├── entangle.js │ │ ├── evaluator.js │ │ ├── ids.js │ │ ├── index.js │ │ ├── interceptor.js │ │ ├── lifecycle.js │ │ ├── magics/ │ │ │ ├── $data.js │ │ │ ├── $dispatch.js │ │ │ ├── $el.js │ │ │ ├── $id.js │ │ │ ├── $nextTick.js │ │ │ ├── $refs.js │ │ │ ├── $root.js │ │ │ ├── $store.js │ │ │ ├── $watch.js │ │ │ └── index.js │ │ ├── magics.js │ │ ├── mutation.js │ │ ├── nextTick.js │ │ ├── plugin.js │ │ ├── reactivity.js │ │ ├── scheduler.js │ │ ├── scope.js │ │ ├── store.js │ │ └── utils/ │ │ ├── bind.js │ │ ├── classes.js │ │ ├── debounce.js │ │ ├── dispatch.js │ │ ├── error.js │ │ ├── on.js │ │ ├── once.js │ │ ├── styles.js │ │ ├── throttle.js │ │ ├── walk.js │ │ └── warn.js │ ├── anchor/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── collapse/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── csp/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ ├── directives/ │ │ │ └── x-html.js │ │ ├── evaluator.js │ │ ├── index.js │ │ └── parser.js │ ├── docs/ │ │ ├── package.json │ │ └── src/ │ │ └── en/ │ │ ├── advanced/ │ │ │ ├── async.md │ │ │ ├── csp.md │ │ │ ├── extending.md │ │ │ └── reactivity.md │ │ ├── advanced.md │ │ ├── directives/ │ │ │ ├── bind.md │ │ │ ├── cloak.md │ │ │ ├── data.md │ │ │ ├── effect.md │ │ │ ├── for.md │ │ │ ├── html.md │ │ │ ├── id.md │ │ │ ├── if.md │ │ │ ├── ignore.md │ │ │ ├── init.md │ │ │ ├── model.md │ │ │ ├── modelable.md │ │ │ ├── on.md │ │ │ ├── ref.md │ │ │ ├── show.md │ │ │ ├── teleport.md │ │ │ ├── text.md │ │ │ └── transition.md │ │ ├── directives.md │ │ ├── essentials/ │ │ │ ├── events.md │ │ │ ├── installation.md │ │ │ ├── lifecycle.md │ │ │ ├── state.md │ │ │ └── templating.md │ │ ├── essentials.md │ │ ├── globals/ │ │ │ ├── alpine-bind.md │ │ │ ├── alpine-data.md │ │ │ └── alpine-store.md │ │ ├── globals.md │ │ ├── magics/ │ │ │ ├── data.md │ │ │ ├── dispatch.md │ │ │ ├── el.md │ │ │ ├── id.md │ │ │ ├── nextTick.md │ │ │ ├── refs.md │ │ │ ├── root.md │ │ │ ├── store.md │ │ │ └── watch.md │ │ ├── magics.md │ │ ├── plugins/ │ │ │ ├── anchor.md │ │ │ ├── collapse.md │ │ │ ├── focus.md │ │ │ ├── intersect.md │ │ │ ├── mask.md │ │ │ ├── morph.md │ │ │ ├── persist.md │ │ │ ├── resize.md │ │ │ └── sort.md │ │ ├── plugins.md │ │ ├── start-here.md │ │ └── upgrade-guide.md │ ├── focus/ │ │ ├── .gitignore │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── history/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── intersect/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── mask/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── morph/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ ├── index.js │ │ ├── morph.js │ │ └── old_morph.js │ ├── navigate/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── persist/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── resize/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ ├── sort/ │ │ ├── builds/ │ │ │ ├── cdn.js │ │ │ └── module.js │ │ ├── package.json │ │ └── src/ │ │ └── index.js │ └── ui/ │ ├── builds/ │ │ ├── cdn.js │ │ └── module.js │ ├── demo/ │ │ ├── index.html │ │ └── listbox/ │ │ ├── data-driven.html │ │ ├── index.html │ │ └── multiple.html │ ├── package.json │ └── src/ │ ├── combobox.js │ ├── dialog.js │ ├── disclosure.js │ ├── index.js │ ├── list-context.js │ ├── listbox.js │ ├── menu.js │ ├── popover.js │ ├── radio.js │ ├── switch.js │ └── tabs.js ├── scripts/ │ ├── build.js │ ├── cleanup-worktree.sh │ ├── release.js │ ├── setup.sh │ ├── update-docs.js │ ├── utils.js │ └── worktree.sh ├── tests/ │ ├── cypress/ │ │ ├── .gitignore │ │ ├── fixtures/ │ │ │ └── example.json │ │ ├── integration/ │ │ │ ├── clone.spec.js │ │ │ ├── custom-bind.spec.js │ │ │ ├── custom-data.spec.js │ │ │ ├── custom-directives.spec.js │ │ │ ├── custom-interceptors.spec.js │ │ │ ├── custom-magics.spec.js │ │ │ ├── custom-prefix.spec.js │ │ │ ├── directives/ │ │ │ │ ├── x-bind-class.spec.js │ │ │ │ ├── x-bind-style.spec.js │ │ │ │ ├── x-bind.spec.js │ │ │ │ ├── x-cloak.spec.js │ │ │ │ ├── x-data.spec.js │ │ │ │ ├── x-for.spec.js │ │ │ │ ├── x-html.spec.js │ │ │ │ ├── x-if.spec.js │ │ │ │ ├── x-ignore.spec.js │ │ │ │ ├── x-init.spec.js │ │ │ │ ├── x-model.spec.js │ │ │ │ ├── x-modelable.spec.js │ │ │ │ ├── x-on.spec.js │ │ │ │ ├── x-show.spec.js │ │ │ │ ├── x-teleport.spec.js │ │ │ │ ├── x-text.spec.js │ │ │ │ └── x-transition.spec.js │ │ │ ├── entangle.spec.js │ │ │ ├── error-handling.spec.js │ │ │ ├── magics/ │ │ │ │ ├── $data.spec.js │ │ │ │ ├── $dispatch.spec.js │ │ │ │ ├── $el.spec.js │ │ │ │ ├── $id.spec.js │ │ │ │ ├── $nextTick.spec.js │ │ │ │ ├── $refs.spec.js │ │ │ │ ├── $root.spec.js │ │ │ │ └── $watch.spec.js │ │ │ ├── mutation.spec.js │ │ │ ├── plugins/ │ │ │ │ ├── anchor.spec.js │ │ │ │ ├── collapse.spec.js │ │ │ │ ├── csp-compatibility.spec.js │ │ │ │ ├── focus.spec.js │ │ │ │ ├── history.spec.js │ │ │ │ ├── intersect.spec.js │ │ │ │ ├── mask.spec.js │ │ │ │ ├── morph.spec.js │ │ │ │ ├── navigate.spec.js │ │ │ │ ├── persist.spec.js │ │ │ │ ├── resize.spec.js │ │ │ │ ├── sort.spec.js │ │ │ │ └── ui/ │ │ │ │ ├── combobox.spec.js │ │ │ │ ├── dialog.spec.js │ │ │ │ ├── disclosure.spec.js │ │ │ │ ├── listbox.spec.js │ │ │ │ ├── menu.spec.js │ │ │ │ ├── popover.spec.js │ │ │ │ ├── radio.spec.js │ │ │ │ ├── switch.spec.js │ │ │ │ └── tabs.spec.js │ │ │ ├── scope.spec.js │ │ │ └── store.spec.js │ │ ├── manual-memory.html │ │ ├── manual-transition-test.html │ │ ├── plugins/ │ │ │ └── index.js │ │ ├── spec-csp.html │ │ ├── spec.html │ │ ├── support/ │ │ │ ├── commands.js │ │ │ ├── drag.js │ │ │ └── index.js │ │ └── utils.js │ └── vitest/ │ ├── csp-evaluator.spec.js │ ├── csp-parser.spec.js │ ├── evaluator.spec.js │ └── mask.spec.js └── vitest.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .claude/skills/review-pr/SKILL.md ================================================ --- name: review-pr description: Review an open PR like a maintainer — checkout, fix issues, push changes, post a structured verdict comment. You just merge or close. disable-model-invocation: true allowed-tools: Bash, Read, Glob, Grep, Edit, Write, Task argument-hint: "[PR number (optional - picks latest if omitted)]" --- # /review-pr - Maintainer-style PR review bot You are a strict, opinionated maintainer of the Alpine.js project. Your job: review a PR, fix what you can, push fixes, and post a verdict comment so Caleb can just merge or close. **IMPORTANT: Every numbered step below is mandatory. Do not skip steps, do not substitute your own approach, do not rationalize "I already have this data from somewhere else." Run the exact commands listed. If a command fails, retry it — do not silently move on. Complete each step fully before starting the next.** ## Step 1: Pick a PR If `$ARGUMENTS` is provided, use that as the PR number. Otherwise, pick the latest open PR: ```bash gh pr list --state open --limit 1 --json number -q '.[0].number' ``` ## Step 2: Check if already reviewed Look for the `` marker in PR comments: ```bash gh pr view {number} --json comments -q '.comments[].body' | grep -q '' ``` If found, tell the user this PR was already reviewed and stop. Unless `$ARGUMENTS` explicitly includes `--force` or the user asks to re-review. ## Step 3: Fetch PR data Run ALL FOUR of these commands in parallel. If any fail, retry them. Do not proceed to Step 4 until you have output from all four: ```bash gh pr view {number} --json title,body,author,state,labels,comments,reviews,files,additions,deletions,baseRefName,headRefName,createdAt,updatedAt,reviewDecision,statusCheckRollup,url gh pr diff {number} gh pr checks {number} gh api repos/{owner}/{repo}/issues/{number}/reactions ``` ## Step 4: Checkout locally and merge main ```bash gh pr checkout {number} git merge main ``` Always merge main into the PR branch before reviewing. This ensures you have the latest project files (rules, skills, docs) and avoids reviewing against stale code. If the merge has conflicts, resolve them or flag for the contributor. ## Step 5: Read and classify Read through the diff and PR body. Classify the PR: - **Bug fix** - Fixes broken behavior - **Feature** - Adds new functionality - **Refactor** - Restructures without changing behavior - **Docs** - Documentation only - **Mixed** - Multiple categories (flag this as a concern) ## Step 6: Challenge the contributor's framing Don't accept the PR description's framing of the bug or problem at face value. Verify independently: 1. **Identify the root cause yourself.** Read the code the PR modifies. Understand *why* the bug exists before looking at how the PR fixes it. 2. **Does the test actually isolate that root cause?** Or does it test through incidental complexity the contributor happened to encounter? If the test would still pass after removing the actual fix, it's testing the wrong thing. 3. **If the test encodes a wrong mental model, rewrite it.** Strip it to the minimum reproduction that targets the real bug. Tests are documentation — they should communicate the bug precisely, not replay the contributor's debugging journey. 4. **Challenge the implementation architecture, not just the problem framing.** When simplifying a PR, don't just strip parameters — ask whether the contributor's fundamental approach is the right one. A simpler version of a bad approach is still a bad approach. Ask: "What's the laziest correct solution? Does the language/framework already handle this if I just let it?" ## Step 7: Evaluate ### For bug fixes 1. **Has a test?** If not, write one. The test should fail on `main` and pass on the PR branch. 2. **Test covers the actual fix?** Including edge cases? 3. **Actually verify regression.** Don't just reason about whether the test fails without the fix — prove it. Stash the fix (`git stash -- `), rebuild (`npm run build`), run the test. If it passes without the fix, the test is not testing the fix. Unstash and rewrite the test. This is non-negotiable for bug fix PRs. 4. **Test isolates root cause?** Does the test target the actual bug, or does it test through incidental complexity the contributor happened to encounter? Strip tests to the minimum reproduction. Tests are documentation — they should communicate the bug precisely. 5. **Naming quality?** Review all test names, component names, variable names. Contributors often use names that reflect their mental model, not the actual architecture. Fix these before merging — they become permanent. 6. **Unnecessary fixtures/setup?** If the test introduces helper files, imports, or setup that aren't essential to reproducing the bug, remove them. 7. **For visual/browser bugs, test observable behavior, not DOM state.** Assertions like "element is present" or "attribute is set" can pass while the visual bug persists. For animation bugs: assert on `document.getAnimations()` state. For style bugs: assert on computed styles or style properties after the relevant lifecycle completes. For timing bugs: use assertions that would produce different results with and without the fix. 8. **Fix is surgical/minimal?** No unrelated changes? 9. **Regression risk?** Could this break something else? ### For features Address EVERY item below. Do not skip any — even to say "N/A": 1. **Already possible without new API?** Default stance: reject new public API surface. Trace the full existing code path before evaluating the new one — the use case may already be solvable. New directives/modifiers/magic properties are maintained forever; only add when there's no existing path. 2. **Community demand?** Check reactions on the PR and linked issues. Low engagement = higher bar. 3. **Intuitive API?** Single-word modifiers preferred (`x-transition.opacity` not `x-transition.opacity-only`). Alpine favors short, expressive directive syntax. 4. **Precedent?** Does it build on existing patterns or introduce new ones? New patterns need strong justification. 5. **Scope?** Should this be a core Alpine feature or a separate plugin package? Alpine core should stay minimal. 6. **Docs included?** Features need documentation. 7. **Registration complete?** Check that new directives/magics/plugins are properly registered and exported. ### For all PRs Address EVERY item below: 1. **Project style?** - JS: no semicolons, `let` not `const` - Follows Alpine's existing patterns and conventions 2. **Single responsibility?** Flag PRs doing too many things. 3. **Security?** Extra scrutiny for: `x-html`, expression evaluation, `Alpine.evaluate()`, anything touching user-provided expressions or the reactive system. 4. **Built JS assets in diff?** Check the file list from `gh pr diff --name-only` for `dist/` files. These should NOT be committed. Remove them. 5. **"No for now" bias.** When in doubt, lean toward not merging. It's easier to add later than remove. 6. **Async timing fixes are treacherous.** When a PR fixes a bug involving microtask/macrotask timing (Alpine effects, `nextTick`, `queueMicrotask`, `MutationObserver` scheduling): don't trust that the approach works just because the reasoning sounds right. Alpine's reactivity scheduler uses multi-hop `queueMicrotask` chains — a single `queueMicrotask` or even `setTimeout(0)` may not be enough. If you can't verify the timing empirically, flag it for discussion. 7. **"What's the laziest correct solution?"** Before evaluating the PR's implementation details, independently brainstorm the simplest possible fix. The contributor's approach is often shaped by their discovery path, not by what's optimal. ## Step 8: Run relevant tests only **NEVER run the full test suite.** Only run tests the PR adds or touches: ```bash # Find test files in the diff gh pr diff {number} --name-only | grep -E '\.spec\.js$' ``` Run those specific tests: ```bash # For Cypress browser tests npx cypress run --spec ./tests/cypress/integration/{test-file}.spec.js # For Vitest unit tests npx vitest run tests/vitest/{test-file}.spec.js ``` If the PR doesn't touch test files but you wrote tests in step 6, run those. Also check CI status: ```bash gh pr checks {number} ``` ## Step 8b: If the PR has no fix, write one If the PR only adds a failing test (or describes a bug without a fix), don't just review the test and stop. **Explore solution paths and try to fix the bug yourself.** This is the most valuable thing you can do. 1. Identify 2-3 possible fix approaches 2. Evaluate trade-offs of each (surgical vs broad, risk of regressions, etc.) 3. Present the options to Caleb with a brief explanation of each 4. Once Caleb picks a direction, implement and test it ## Step 9: Make fixes directly Fix issues you find. Common fixes: - **Style violations**: Remove semicolons from JS, change `const` to `let` - **Built assets in diff**: `git checkout main -- dist/` (or whatever the build output path is) - **Missing tests**: Write them - **Small refactors**: Simplify overly complex code - **Missing registration**: Add to package index files, etc. - **Before committing a simplified version of the contributor's code, do a smell test:** Could this be done in fewer lines with a completely different approach? The best code is the code you delete. Stage and commit fixes: ```bash git add -A git commit -m "Review fixes: [brief description] Co-Authored-By: Claude Opus 4.6 " ``` ## Step 10: Push to PR branch Try to push to the contributor's branch: ```bash git push ``` ### If push fails (fork doesn't allow maintainer edits) 1. Create a new branch from `main` 2. Cherry-pick the contributor's commits 3. Apply your fixes on top 4. Push the new branch 5. Create a new PR: ```bash gh pr create --title "{original title}" --body "$(cat <<'EOF' Closes #{original_number} Cherry-picked from #{original_number} by @{author} with review fixes applied. ## Original description {original_body} ## Review fixes applied {list of fixes} EOF )" ``` 6. Comment on the original PR explaining the new PR. ## Step 11: Post verdict comment Post a structured comment on the PR: ```bash gh pr comment {number} --body "$(cat <<'EOF' ## PR Review: #{number} — {title} **Type**: {Bug fix | Feature | Refactor | Docs | Mixed} **Verdict**: {Merge | Request changes | Needs discussion | Close} ### What's happening (plain English) {Explain the PR like Caleb is a 3-year-old who happens to be an expert in Alpine internals but has zero context on this specific PR. Use a numbered step-by-step walkthrough of the exact sequence that triggers the bug/feature. No jargon beyond what Alpine devs already know. Be crystal clear and concise — this is the most important section.} ### Other approaches considered {Briefly list 2-3 alternative ways this could have been solved, with one sentence each on why the PR's approach is better (or worse). If there's only one reasonable approach, say so and explain why. This helps Caleb quickly evaluate whether the chosen path is the right one.} ### Changes Made {List of fixups you pushed, or "No changes made" if none} ### Test Results {Which tests ran, pass/fail status, CI status} ### Code Review {Specific feedback with file:line references. What's good, what's concerning.} ### Security {Any security considerations, or "No security concerns identified."} ### Verdict {Your reasoning for the verdict. Be direct. If it should be merged, say why. If closed, say why kindly but clearly.} --- *Reviewed by Claude* EOF )" ``` ## Verdict guidelines - **Merge**: Code is correct, tests pass, style is clean, feature is wanted. You've fixed any minor issues. - **Request changes**: Significant issues you can't fix yourself (architectural problems, missing context, needs author input). - **Needs discussion**: Feature scope questions, API design debates, core vs plugin questions. Tag these for Caleb. - **Close**: PR is stale with no response, duplicates existing functionality, or solves a problem that shouldn't be solved. Be kind. ## Important rules - NEVER run the full test suite. Only run tests the PR touches or that you wrote. - Always use the `` marker so you can detect previous reviews. - Be opinionated. This project has strong conventions — enforce them. - Fix what you can. Don't just point out problems if you can solve them. - Security is non-negotiable. If you see a security issue, verdict is always "Request changes" regardless of everything else. - Match the project voice: practical, direct, minimal. - Don't accept the contributor's framing of the problem at face value. Verify the root cause independently, then ensure the test targets that root cause — not the contributor's incidental path to discovering it. - "Should this exist?" before "Is this correct?" — Don't get pulled into reviewing implementation details (code quality, edge cases, naming) until you've decided the feature itself is justified. Implementation nits imply acceptance. - Tests are documentation. A sloppy test that passes is not good enough — it should precisely communicate what broke and why. - Review contributor naming as critically as contributor code. Bad names get merged and become permanent. ================================================ FILE: .claude/skills/summarize-activity/SKILL.md ================================================ --- name: summarize-activity description: Summarize recent GitHub activity — discussions, PRs, issues, events, traffic — into an actionable report so you can stay on top of the project without reading everything. disable-model-invocation: true allowed-tools: Bash, Read, Task argument-hint: "[timeframe: 24h (default), 48h, 7d, 1w]" --- # /summarize-activity - GitHub activity digest You generate a concise, actionable summary of recent Alpine.js GitHub activity for Caleb. **IMPORTANT: Every numbered step below is mandatory. Do not skip steps, do not substitute your own approach. Run the exact commands listed. If a command fails, retry it — do not silently move on. Complete each step fully before starting the next.** ## Step 1: Parse timeframe Parse `$ARGUMENTS` into a cutoff timestamp. Supported formats: - `24h`, `48h`, `72h` — hours (default: `24h` if no argument) - `7d`, `14d`, `30d` — days - `1w`, `2w` — weeks (1w = 7d) Compute the ISO 8601 cutoff timestamp: ```bash # Example for 24h: date -u -v-24H '+%Y-%m-%dT%H:%M:%SZ' # Example for 7d: date -u -v-7d '+%Y-%m-%dT%H:%M:%SZ' ``` Store the cutoff timestamp and the human-readable timeframe label (e.g., "last 24 hours", "last 7 days") for use in later steps. ## Step 2: Fetch activity in parallel Run ALL FIVE of these commands in parallel using the Bash tool. If any fail, retry them. Do not proceed to Step 3 until you have output from all five. **Owner/repo:** `alpinejs/alpine` ### 2a. Discussions (GraphQL) ```bash gh api graphql -f query=' { repository(owner: "alpinejs", name: "alpine") { discussions(first: 50, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { title url author { login } category { name } comments { totalCount } body answer { author { login } body createdAt } createdAt updatedAt } } } }' ``` ### 2b. Pull Requests ```bash gh pr list --repo alpinejs/alpine --state all --limit 50 --json number,title,url,author,state,labels,createdAt,updatedAt,additions,deletions,headRefName,baseRefName,reviewDecision,comments ``` ### 2c. Issues ```bash gh issue list --repo alpinejs/alpine --state all --limit 50 --json number,title,url,author,state,labels,createdAt,updatedAt,comments ``` ### 2d. Events ```bash gh api repos/alpinejs/alpine/events --paginate --jq '.[] | {type, actor: .actor.login, created_at: .created_at, payload_action: .payload.action, ref: .payload.ref, ref_type: .payload.ref_type}' | head -100 ``` ### 2e. Traffic & stars ```bash gh api repos/alpinejs/alpine/traffic/views 2>/dev/null; echo "---SEPARATOR---"; gh api repos/alpinejs/alpine/traffic/clones 2>/dev/null; echo "---SEPARATOR---"; gh api repos/alpinejs/alpine --jq '{stargazers_count, forks_count, open_issues_count, watchers_count}' ``` Note: Traffic endpoints require push access. If they return 403, skip traffic data and note it in the report. ## Step 3: Fetch comment threads for active items For discussions that have comments updated within the timeframe, fetch full comment bodies via GraphQL. Batch up to 10 discussions per query: ```bash gh api graphql -f query=' { repository(owner: "alpinejs", name: "alpine") { discussion(number: {NUMBER}) { comments(last: 10) { nodes { author { login } body createdAt updatedAt } } } } }' ``` For the most active PRs and issues (those with comments updated in timeframe), fetch recent comments: ```bash gh api repos/alpinejs/alpine/issues/{number}/comments --jq '.[] | select(.updated_at > "{CUTOFF}") | {user: .user.login, body: .body, created_at: .created_at}' ``` Only fetch threads that are clearly within the timeframe. Don't fetch everything — be selective. ## Step 4: Filter by timeframe Discard anything with `updatedAt` / `updated_at` before the cutoff timestamp. Keep items where: - The item was created within the timeframe - The item received new comments within the timeframe - The item changed state (opened, closed, merged) within the timeframe ## Step 5: Analyze and write report Output a markdown report with these sections. Be concise — this is a digest, not a novel. --- ### Report format: ```markdown # Alpine.js Activity — {timeframe label} _{start date} to {end date}_ ## TL;DR {2-3 sentences. What's the pulse? Any fires? Anything exciting? Give Caleb the vibe in 10 seconds.} ## Needs Your Attention {Actionable items only. Each with a recommended next step: reply, merge, close, investigate, etc.} - **[Title](url)** by @author — {why it needs attention}. **Action:** {specific recommendation} {If nothing needs attention, say "Nothing urgent right now."} ## Hot Discussions {Discussions with the most activity or notable sentiment. Include key quotes if illuminating.} - **[Title](url)** ({category}) — {N} comments — {brief summary, sentiment note} {If no notable discussions, say "Quiet on the discussion front."} ## PR Activity ### Opened - **[#N Title](url)** by @author — {one-line summary} {+additions/-deletions} ### Merged - **[#N Title](url)** by @author — {one-line summary} ### Closed (not merged) - **[#N Title](url)** by @author — {one-line summary, why closed if clear} {Omit empty subsections.} ## Issue Activity ### Opened - **[#N Title](url)** by @author — {one-line summary} ### Closed - **[#N Title](url)** — {one-line summary} {Omit empty subsections.} ## Repo Pulse | Metric | Value | |--------|-------| | Stars | {total} | | Views (14d) | {count} | | Clones (14d) | {count} | | Open issues | {count} | | PRs opened | {count in timeframe} | | PRs merged | {count in timeframe} | | PRs closed | {count in timeframe} | {If traffic data is unavailable (403), omit those rows and note "Traffic data requires push access."} ``` --- ## Important rules - Every item must include a `[title](url)` link so Caleb can click through. - Include @author for attribution. - Keep summaries to ONE line per item. This is a digest. - The "Needs Your Attention" section is the most important. Be opinionated about what deserves Caleb's time. - If a discussion or issue has heated sentiment, note it (e.g., "heated", "confused users", "strong demand"). - Omit empty sections entirely — don't show "No activity" headers. - For PRs, mention if CI is failing when relevant. - Don't editorialize beyond what's helpful for triage. Be practical, not chatty. ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = space indent_size = 4 trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [calebporzio] ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Get help or ask a Question url: https://github.com/alpinejs/alpine/discussions/new?category=1-help about: Ask questions or get help from other community members - name: Feature Request url: https://github.com/alpinejs/alpine/discussions/new?category=3-feature-ideas about: For ideas or feature requests - name: Bug Report url: https://github.com/alpinejs/alpine/discussions/new?category=5-bugs about: Submit a bug report - name: Documentation issue url: https://github.com/alpinejs/alpine/pulls about: For documentation issues, please open a pull request ================================================ FILE: .github/SECURITY.md ================================================ # Security Policy **PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY.** ## Reporting a Vulnerability If you discover a security vulnerability within Alpine.js, please send an email to Caleb Porzio at support@alpinejs.dev. All security vulnerabilities will be promptly addressed. ================================================ FILE: .github/workflows/run-tests.yml ================================================ name: Run Tests on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - run: npm install - run: npm run build - run: npm run test - run: npm run vitest ================================================ FILE: .gitignore ================================================ node_modules scratch.md dist/ prompts/ .env.json .idea ================================================ FILE: CLAUDE.md ================================================ # Alpine.js Development Guidelines ## Pull Request Evaluation Criteria When evaluating pull requests for Alpine.js, assess the following: ### 1. Tests - Are tests provided for the change? - Do existing tests still pass? - For configuration changes (package.json, build scripts), tests may not be required ### 2. Code Style - Does the code match Alpine's existing patterns? - Check indentation, naming conventions, and structure - For package.json changes, ensure consistency with other packages in the monorepo ### 3. Code Quality - Is the code clean and maintainable? - Is the change focused and minimal? - Are there any unnecessary changes or complexity? - Does this PR contain changes that should apply elsewhere? ### 4. Simplicity - Is this a simple, focused change? - Does it follow Alpine's philosophy of simplicity? - Could it be implemented more simply? - Are the proposed additions intuitive for users? or do they require extra knowledge that they have to dig for. ### 5. Precedent - Does this PR (both public facing additions and internal implementation) follow established precedents in the project - Does it use terms that are unfamiliar to the project as of yet? ### 6. Description Quality - Is there a clear explanation of what/why/how? - Are breaking changes documented? - Is backward compatibility addressed? ### 7. Community Engagement - Are there comments, reviews, or discussions? - Has it been approved by maintainers? - Are there any conflicting opinions or unresolved concerns? ### Mergeability Rating Based on the above, rate as: - **HIGH**: Ready to merge (all criteria met, approved) - **MEDIUM**: Needs attention (technically sound but missing reviews/tests) - **LOW**: Requires work (has issues or conflicts to resolve) ## Project Structure Alpine.js is a monorepo with packages in `/packages/`: - Each package has its own package.json - Build outputs go to `dist/` with `.cjs.js`, `.esm.js`, and `.min.js` versions - Browser tests use Cypress, unit tests use Vitest - CI runs on GitHub Actions ## Common Commands ```bash # Build npm run build # Build all packages # Browser tests (Cypress) npm test # Run all tests npx cypress run --spec ./tests/cypress/integration/[filename].spec.js # Run single spec # Unit tests (Vitest) npx vitest run tests/vitest/[filename].spec.js # Run single spec # Review PRs gh pr list # List open PRs gh pr view [number] # View PR details gh pr diff [number] # View code changes gh pr checks [number] # Check CI status ``` ## Manual Testing 1. Edit `./index.html` at project root 2. Open in browser at `http://alpine.test/` (assumes local dev server mapped to directory name) ## Summary After assessing the pull request on the above qualities, provide a summary explaining the problem this PR addresses and the fix, and why it's a good or bad fix. Do it in plain language as if you are personally advising me on what the PR is and weather or not I should merge it. And if not, what might need to be addressed first. If things need to be addressed, offer to address them yourself. Please use code snippets to establish a starting point and and ending point if helpful. For example, when explaining the problem, it is often easier to provide a brief explanation alongside a code snippet of what is currently problematic, then when explaining the solution, showing what new code will allow a fix if applicable. ================================================ FILE: LICENSE.md ================================================ # MIT License Copyright © 2019-2025 Caleb Porzio and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Alpine.js Go to the Alpine docs for most things: [Alpine Docs](https://alpinejs.dev) You are welcome to submit updates to the docs by submitting a PR to this repo. Docs are located in the [`/packages/docs`](/packages/docs) directory. Stay here for contribution-related information. > Looking for V2 docs? [here they are](https://github.com/alpinejs/alpine/tree/v2.8.2)

Alpine Component Patterns

## Contribution Guide: ### Quickstart * clone this repo locally * run `npm install` & `npm run build` * Include the `/packages/alpinejs/dist/cdn.js` file from a ` Comparing v2.4.0...master · livewire/livewire
  • Unwatch

    Notifications

  • Fork

    Fork livewire

    If this dialog fails to load, you can visit the fork page directly.

Permalink

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
base repository: livewire/livewire
base: v2.4.0
head repository: livewire/livewire
compare: master
Commits on Mar 01, 2021
This PR updates `WithPagination` to account for  `queryString()` method introduced in 2.4.0
* fixed TestableLivewire to always return an instance of itself when calling unknown method

* added test
* allow App namespace in config - closes #2107

* added test

* fixed namespacing to also work with app default - #2107
Commits on Mar 24, 2021
…#2517)

* Add failing test for dynamic nested entangled components

* Bump Alpine to 2.8.2 for browser tests
Commits on Apr 04, 2021
* Add helpful entangle error

* Build assets
* Add isPreviewable Method to TemporaryUpload Class

* Add isPreviewable Tests
* fix $wire.emit, $wire.emitUp and $wire.emitTo

* add test for $wire.emit, $wire.emitSelf, $wire.emitTo, and $wire.emitUp

* commit build artifacts

* Build assets

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Commits on Apr 12, 2021
* rollback & simplify

* add tests
* Add actingAs method to docblock

* Add mixin for forwarded calls to the response
* Add tests for optional route parameters

* Add support for optional route parameters

* wip

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Commits on Apr 16, 2021
* Allow polling to be disabled outside viewport

* feat: block 95% poll requests out of viewport

This will stop polling if the element is not in the
viewport by default. If it needs to continue even
when it is not in the viewport, add `keep-alive`:

`wire:poll.keep-alive`

* chore: rewrite comment

* feat: don't poll element outside viewport

The .visible modifier ensures that polling only happens
when the element is within the viewport.

* tests: add test for visibile polling

* feat: never poll instead of 5% of the time

* fix: first check for the modifier

* chore: add livewire js builds
* Avoid exception for unsupported HTTP methods

In case a route is defined, but the utilized HTTP method (which is normally POST) is not supported, an exception other than `NotFoundHttpException` is thrown. By catching this `MethodNotAllowedHttpException`, we avoid exceptions to bubble up to the user.

* Add browser test for method not allowed
…#2725)

* Fixes to ensure Livewire can run in a sessionless Laravel application

* wip

* Merge master and re-build

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Commits on Apr 21, 2021
* Add dynamic Livewire component support

* Add tag compiler tests

* Add support for "is" syntax

* Add check for existing dynamic component
* Add params method to macros

* Add params tests
Commits on Apr 25, 2021
* Fix layoutData bug

* Update tests
Showing with 1,300 additions and 79 deletions.
  1. +2 −1 README.md
  2. +1 −1 config/livewire.php
  3. +2 −2 dist/livewire.js
  4. +1 −1 dist/livewire.js.map
  5. +1 −1 dist/manifest.json
  6. +18 −1 js/component/Polling.js
  7. +10 −1 js/component/SupportAlpine.js
  8. +4 −1 js/component/UploadManager.js
  9. +3 −2 js/component/index.js
  10. +11 −5 js/connection/index.js
  11. +7 −7 js/dom/morphdom/morphdom.js
  12. +3 −10 js/util/getCsrfToken.js
  13. +1 −2 src/Commands/ComponentParser.php
  14. +0 −2 src/Commands/FileManipulationCommand.php
  15. +80 −2 src/Commands/MakeCommand.php
  16. +5 −0 src/Commands/StubsCommand.php
  17. +2 −2 src/Commands/the-tao.php
  18. +1 −1 src/ComponentConcerns/HandlesActions.php
  19. +13 −0 src/Exceptions/ComponentAttributeMissingOnDynamicComponentException.php
  20. +15 −0 src/Exceptions/DirectlyCallingLifecycleHooksNotAllowedException.php
  21. +16 −0 src/HydrationMiddleware/PerformActionCalls.php
  22. +1 −0 src/Livewire.php
  23. +1 −1 src/LivewireBladeDirectives.php
  24. +7 −2 src/LivewireManager.php
  25. +37 −7 src/LivewireServiceProvider.php
  26. +33 −7 src/LivewireTagCompiler.php
  27. +8 −0 src/Macros/DuskBrowserMacros.php
  28. +15 −0 src/Macros/ViewMacros.php
  29. +4 −3 src/RenameMe/SupportBrowserHistory.php
  30. +2 −2 src/RenameMe/SupportRedirects.php
  31. +12 −7 src/TemporaryUploadedFile.php
  32. +19 −0 src/Testing/Concerns/MakesAssertions.php
  33. +4 −1 src/Testing/TestableLivewire.php
  34. +7 −3 src/WithPagination.php
  35. +1 −1 src/views/pagination/tailwind.blade.php
  36. +15 −0 tests/AppLayout.php
  37. +54 −0 tests/Browser/Alpine/Emit/EmitComponent.php
  38. +24 −0 tests/Browser/Alpine/Emit/EmitNestedComponent.php
  39. +36 −0 tests/Browser/Alpine/Emit/Test.php
  40. +23 −0 tests/Browser/Alpine/Entangle/EntangleNestedChildComponent.php
  41. +41 −0 tests/Browser/Alpine/Entangle/EntangleNestedParentComponent.php
  42. +13 −0 tests/Browser/Alpine/Entangle/Test.php
  43. +24 −0 tests/Browser/DynamicComponentLoading/ClickableComponent.php
  44. +24 −0 tests/Browser/DynamicComponentLoading/Test.php
  45. +7 −0 tests/Browser/DynamicComponentLoading/view-clickable-component.blade.php
  46. +7 −0 tests/Browser/DynamicComponentLoading/view-dynamic-component.blade.php
  47. +25 −0 tests/Browser/DynamicComponentLoading/view-load-dynamic-component.blade.php
  48. +1 −0 tests/Browser/Morphdom/Component.php
  49. +10 −0 tests/Browser/Morphdom/Test.php
  50. +11 −0 tests/Browser/Morphdom/view.blade.php
  51. +18 −0 tests/Browser/PollingViewport/Component.php
  52. +25 −0 tests/Browser/PollingViewport/Test.php
  53. +11 −0 tests/Browser/PollingViewport/view.blade.php
  54. +26 −0 tests/Browser/SyncHistory/ComponentWithOptionalParameter.php
  55. +13 −0 tests/Browser/SyncHistory/Test.php
  56. +16 −0 tests/Browser/TestCase.php
  57. +2 −0 tests/Browser/console/.gitignore
  58. +2 −0 tests/Browser/screenshots/.gitignore
  59. +2 −0 tests/Browser/source/.gitignore
  60. +12 −0 tests/Browser/views/layouts/app-for-normal-views.blade.php
  61. +1 −1 tests/Browser/views/layouts/app.blade.php
  62. +2 −2 tests/Unit/BladeComponentAttributeMacrosTest.php
  63. +94 −0 tests/Unit/CantCallLifecycleHooksDirectlyFromJSTest.php
  64. +64 −0 tests/Unit/ComponentLayoutTest.php
  65. +41 −0 tests/Unit/ComponentNameAndNamespaceTest.php
  66. +4 −0 tests/Unit/FileUploadsTest.php
  67. +117 −0 tests/Unit/LivewireDirectivesTest.php
  68. +5 −0 tests/Unit/MakeCommandTest.php
  69. +39 −0 tests/Unit/NestingComponentsTest.php
  70. +1 −0 tests/Unit/StubCommandTest.php
  71. +118 −0 tests/Unit/TagCompilerTest.php
  72. +17 −0 tests/Unit/TestableLivewireCanAssertStatusCodesTest.php
  73. +7 −0 tests/Unit/views/layouts/app-from-class-component.blade.php
  74. +1 −0 tests/Unit/views/layouts/data-test.blade.php
@@ -10,7 +10,7 @@ Awesome Livewire stuff here: https://github.com/imliam/awesome-livewire


All contributions are welcomed! (but please submit an issue to make sure the PR is warranted first) All contributions are welcomed! (but please submit an issue to make sure the PR is warranted first)


Open GitHub issues for all bugs. Ideas and questions belong on the [forum](https://forum.laravel-livewire.com) or [Discord server](https://discord.gg/livewire). Open GitHub issues for all bugs. Ideas and questions belong in [Discussions](https://github.com/livewire/livewire/discussions) or [Discord server](https://discord.gg/livewire).


Contribute to the docs here: https://github.com/livewire/docs Contribute to the docs here: https://github.com/livewire/docs


@@ -28,6 +28,7 @@ Livewire uses semantic versioning and will use the following release schedule st
* Refine the "asset_url" config. Potentially change to "app_url" (https://github.com/livewire/livewire/pull/1693) * Refine the "asset_url" config. Potentially change to "app_url" (https://github.com/livewire/livewire/pull/1693)
* Support multiple pagination (https://github.com/livewire/livewire/pull/1997) * Support multiple pagination (https://github.com/livewire/livewire/pull/1997)
* A CSP-safe mode for Livewire (https://github.com/livewire/livewire/pull/2485#issuecomment-784355989) * A CSP-safe mode for Livewire (https://github.com/livewire/livewire/pull/2485#issuecomment-784355989)
* Add `$wire.errors()` type deal


## Contributors ✨ ## Contributors ✨


@@ -38,7 +38,7 @@
| the view returned by SomeComponent will be wrapped in "layouts.app" | the view returned by SomeComponent will be wrapped in "layouts.app"
| |
*/ */

'layout' => 'layouts.app', 'layout' => 'layouts.app',


/* /*

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -1 +1 @@
{"/livewire.js":"/livewire.js?id=25f025805c3c370f7e87"} {"/livewire.js":"/livewire.js?id=54d078b2ce39327a1702"}
@@ -1,5 +1,5 @@
import MethodAction from '@/action/method' import MethodAction from '@/action/method'
import { wireDirectives} from '@/util' import { wireDirectives } from '@/util'
import store from '@/Store' import store from '@/Store'


export default function () { export default function () {
@@ -56,9 +56,26 @@ function fireActionOnInterval(node, component) {
if (Math.random() < .95) return if (Math.random() < .95) return
} }


// Only poll visible elements. Visible elements are elements that
// are visible in the current viewport.
if (directive.modifiers.includes('visible') && ! inViewport(directive.el)) {
return
}

// Don't poll if livewire is offline as well. // Don't poll if livewire is offline as well.
if (store.livewireIsOffline) return if (store.livewireIsOffline) return


component.addAction(new MethodAction(method, directive.params, node)) component.addAction(new MethodAction(method, directive.params, node))
}, interval); }, interval);
} }

function inViewport(el) {
var bounding = el.getBoundingClientRect();

return (
bounding.top < (window.innerHeight || document.documentElement.clientHeight) &&
bounding.left < (window.innerWidth || document.documentElement.clientWidth) &&
bounding.bottom > 0 &&
bounding.right > 0
);
}
@@ -64,10 +64,19 @@ function supportEntangle() {
let isDeferred = value.isDeferred let isDeferred = value.isDeferred
let livewireComponent = livewireEl.__livewire let livewireComponent = livewireEl.__livewire


let livewirePropertyValue = livewireEl.__livewire.get(livewireProperty)

// Check to see if the Livewire property exists and if not log a console error
// and return so everything else keeps running.
if (typeof livewirePropertyValue === 'undefined') {
console.error(`Livewire Entangle Error: Livewire property '${livewireProperty}' cannot be found`)
return
}

// Let's set the initial value of the Alpine prop to the Livewire prop's value. // Let's set the initial value of the Alpine prop to the Livewire prop's value.
component.unobservedData[key] component.unobservedData[key]
// We need to stringify and parse it though to get a deep clone. // We need to stringify and parse it though to get a deep clone.
= JSON.parse(JSON.stringify(livewireEl.__livewire.get(livewireProperty))) = JSON.parse(JSON.stringify(livewirePropertyValue))


let blockAlpineWatcher = false let blockAlpineWatcher = false


@@ -71,10 +71,13 @@ class UploadManager {
Array.from(this.uploadBag.first(name).files).forEach(file => formData.append('files[]', file)) Array.from(this.uploadBag.first(name).files).forEach(file => formData.append('files[]', file))


let headers = { let headers = {
'X-CSRF-TOKEN': getCsrfToken(),
'Accept': 'application/json', 'Accept': 'application/json',
} }


let csrfToken = getCsrfToken()

if (csrfToken) headers['X-CSRF-TOKEN'] = csrfToken

this.makeRequest(name, formData, 'post', url, headers, response => { this.makeRequest(name, formData, 'post', url, headers, response => {
return response.paths return response.paths
}) })
@@ -641,8 +641,9 @@ export default class Component {
// Forward "emits" to base Livewire object. // Forward "emits" to base Livewire object.
if (typeof property === 'string' && property.match(/^emit.*/)) return function (...args) { if (typeof property === 'string' && property.match(/^emit.*/)) return function (...args) {
if (property === 'emitSelf') return store.emitSelf(component.id, ...args) if (property === 'emitSelf') return store.emitSelf(component.id, ...args)

if (property === 'emitUp') return store.emitUp(component.el, ...args)
return store[property].apply(component, args)
return store[property](...args)
} }


if ( if (
@@ -13,8 +13,16 @@ export default class Connection {
return componentStore.onErrorCallback(status) return componentStore.onErrorCallback(status)
} }


showExpiredMessage() {
confirm(
'This page has expired due to inactivity.\nWould you like to refresh the page?'
) && window.location.reload()
}

sendMessage(message) { sendMessage(message) {
let payload = message.payload() let payload = message.payload()
let csrfToken = getCsrfToken()
let socketId = this.getSocketId()


if (window.__testing_request_interceptor) { if (window.__testing_request_interceptor) {
return window.__testing_request_interceptor(payload, this) return window.__testing_request_interceptor(payload, this)
@@ -31,12 +39,12 @@ export default class Connection {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'text/html, application/xhtml+xml', 'Accept': 'text/html, application/xhtml+xml',
'X-CSRF-TOKEN': getCsrfToken(),
'X-Socket-ID': this.getSocketId(),
'X-Livewire': true, 'X-Livewire': true,


// We'll set this explicitly to mitigate potential interference from ad-blockers/etc. // We'll set this explicitly to mitigate potential interference from ad-blockers/etc.
'Referer': window.location.href, 'Referer': window.location.href,
...(csrfToken && { 'X-CSRF-TOKEN': csrfToken }),
...(socketId && { 'X-Socket-ID': socketId })
}, },
} }
) )
@@ -58,9 +66,7 @@ export default class Connection {


store.sessionHasExpired = true store.sessionHasExpired = true


confirm( this.showExpiredMessage()
'This page has expired due to inactivity.\nWould you like to refresh the page?'
) && window.location.reload()
} else { } else {
response.text().then(response => { response.text().then(response => {
this.showHtmlModal(response) this.showHtmlModal(response)
@@ -10,8 +10,8 @@


'use strict'; 'use strict';


import { compareNodeNames, toElement, moveChildren, createElementNS, doc } from './util';
import specialElHandlers from './specialElHandlers'; import specialElHandlers from './specialElHandlers';
import { compareNodeNames, createElementNS, doc, moveChildren, toElement } from './util';


var ELEMENT_NODE = 1; var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11; var DOCUMENT_FRAGMENT_NODE = 11;
@@ -153,15 +153,15 @@ export default function morphdomFactory(morphAttrs) {
if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) { if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {
curChild.parentNode.replaceChild(unmatchedFromEl, curChild); curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild); morphEl(unmatchedFromEl, curChild);

// @livewireModification
// Otherwise, "curChild" will be unnatached when it is passed to "handleNodeAdde"
// things like .parent and .closest will break.
curChild = unmatchedFromEl
} }
else {
handleNodeAdded(curChild);
}
}
else {
handleNodeAdded(curChild);
} }


handleNodeAdded(curChild);
curChild = nextSibling; curChild = nextSibling;
} }
} }
@@ -1,16 +1,9 @@
export function getCsrfToken() { export function getCsrfToken() {
const tokenTag = document.head.querySelector('meta[name="csrf-token"]') const tokenTag = document.head.querySelector('meta[name="csrf-token"]')
let token


if (!tokenTag) { if (tokenTag) {
if (!window.livewire_token) { return tokenTag.content
throw new Error('Whoops, looks like you haven\'t added a "csrf-token" meta tag')
}

token = window.livewire_token
} else {
token = tokenTag.content
} }


return token return window.livewire_token ?? undefined
} }
@@ -200,8 +200,7 @@ public function wisdomOfTheTao()


public static function generatePathFromNamespace($namespace) public static function generatePathFromNamespace($namespace)
{ {
$name = str($namespace)->replaceFirst(app()->getNamespace(), ''); $name = str($namespace)->finish('\\')->replaceFirst(app()->getNamespace(), '');

return app('path').'/'.str_replace('\\', '/', $name); return app('path').'/'.str_replace('\\', '/', $name);
} }


@@ -52,8 +52,6 @@ public function writeWelcomeMessage()
if(PHP_OS_FAMILY == 'Linux') exec('xdg-open https://github.com/livewire/livewire'); if(PHP_OS_FAMILY == 'Linux') exec('xdg-open https://github.com/livewire/livewire');


$this->line("Thanks! Means the world to me!"); $this->line("Thanks! Means the world to me!");
} else {
$this->line("I understand, but am not going to pretend I'm not sad about it...");
} }
} }
} }
@@ -48,7 +48,7 @@ public function handle()
} }


if ($test) { if ($test) {
$test && $this->line("<options=bold;fg=green>Test:</> {$this->parser->relativeTestPath()}"); $test && $this->line("<options=bold;fg=green>TEST:</> {$this->parser->relativeTestPath()}");
} }


if ($showWelcomeMessage && ! app()->environment('testing')) { if ($showWelcomeMessage && ! app()->environment('testing')) {
@@ -115,6 +115,84 @@ protected function createTest($force = false)


public function isReservedClassName($name) public function isReservedClassName($name)
{ {
return array_search($name, ['Parent', 'Component', 'Interface']) !== false; return array_search(strtolower($name), $this->getReservedName()) !== false;
} }

private function getReservedName()
{
return [
'parent',
'component',
'interface',
'__halt_compiler',
'abstract',
'and',
'array',
'as',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'eval',
'exit',
'extends',
'final',
'finally',
'fn',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'isset',
'list',
'namespace',
'new',
'or',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'return',
'static',
'switch',
'throw',
'trait',
'try',
'unset',
'use',
'var',
'while',
'xor',
'yield',
];
}

} }
@@ -34,6 +34,11 @@ public function handle()
$stubsPath.'/livewire.view.stub', $stubsPath.'/livewire.view.stub',
file_get_contents(__DIR__.'/livewire.view.stub') file_get_contents(__DIR__.'/livewire.view.stub')
); );

file_put_contents(
$stubsPath.'/livewire.test.stub',
file_get_contents(__DIR__.'/livewire.test.stub')
);


$this->info('Stubs published successfully.'); $this->info('Stubs published successfully.');
} }
@@ -14,8 +14,8 @@
'Knowing others is intelligence; knowing yourself is true wisdom.', 'Knowing others is intelligence; knowing yourself is true wisdom.',
'If your happiness depends on money, you will never be happy with yourself.', 'If your happiness depends on money, you will never be happy with yourself.',
'If you look to others for fulfillment, you will never truly be fulfilled.', 'If you look to others for fulfillment, you will never truly be fulfilled.',
'To attain knowledge, add things every day; To attain wisdom, subtract things every day', 'To attain knowledge, add things every day; To attain wisdom, subtract things every day.',
'Close your eyes. Count to one. That is how long forever feels.', 'Close your eyes. Count to one. That is how long forever feels.',
'The whole world belongs to you', 'The whole world belongs to you.',
'Stop trying to control.', 'Stop trying to control.',
]; ];
@@ -129,7 +129,7 @@ public function callMethod($method, $params = [])
} else { } else {
$currentValue = $this->{$prop}; $currentValue = $this->{$prop};
} }

$this->syncInput($prop, ! $currentValue, $rehash = false); $this->syncInput($prop, ! $currentValue, $rehash = false);


return; return;
@@ -0,0 +1,13 @@
<?php

namespace Livewire\Exceptions;

class ComponentAttributeMissingOnDynamicComponentException extends \Exception
{
use BypassViewHandler;

public function __construct()
{
parent::__construct('Dynamic component tag is missing component attribute.');
}
}
@@ -0,0 +1,15 @@
<?php

namespace Livewire\Exceptions;

class DirectlyCallingLifecycleHooksNotAllowedException extends \Exception
{
use BypassViewHandler;

public function __construct($method, $component)
{
parent::__construct(
"Unable to call lifecycle method [{$method}] directly on component: [{$component}]"
);
}
}

No commit comments for this range

================================================ FILE: benchmarks/init.html ================================================ Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

Alpine-3-keyed

5 tall yellow pony x
6 small purple bbq x
7 handsome pink burger x
8 cheap orange keyboard x
9 pretty white desk x
10 odd blue pizza x
11 expensive brown sandwich x
12 large white bbq x
13 short orange desk x
14 easy white mouse x
15 expensive pink pizza x
16 plain brown table x
17 important yellow table x
18 inexpensive orange burger x
19 unsightly purple sandwich x
20 elegant purple pizza x
21 plain pink cookie x
22 handsome brown desk x
23 short green mouse x
24 helpful yellow table x
25 inexpensive brown sandwich x
26 large white chair x
27 crazy red bbq x
28 fancy brown car x
29 tall brown pony x
30 helpful orange chair x
31 handsome white chair x
32 elegant blue chair x
33 quaint yellow cookie x
34 elegant green desk x
35 pretty purple keyboard x
36 odd purple chair x
37 small yellow sandwich x
38 fancy black mouse x
39 odd red house x
40 crazy white keyboard x
41 angry white car x
42 plain blue car x
43 fancy pink chair x
44 angry purple chair x
45 cheap orange pony x
46 short purple desk x
47 big black car x
48 unsightly orange desk x
49 short orange car x
50 handsome blue chair x
51 adorable pink desk x
52 small brown sandwich x
53 angry purple chair x
54 crazy brown burger x
55 unsightly brown keyboard x
56 plain red pony x
57 angry yellow car x
58 large black cookie x
59 crazy black pizza x
60 clean black table x
61 plain brown bbq x
62 big orange cookie x
63 pretty black bbq x
64 quaint red mouse x
65 clean black desk x
66 handsome red pony x
67 odd purple keyboard x
68 mushy yellow chair x
69 important blue table x
70 angry orange keyboard x
71 crazy brown chair x
72 unsightly brown car x
73 unsightly yellow pizza x
74 pretty purple keyboard x
75 inexpensive white keyboard x
76 short brown chair x
77 important orange pizza x
78 important purple chair x
79 crazy yellow car x
80 angry orange keyboard x
81 pretty yellow table x
82 odd green house x
83 big purple desk x
84 tall brown car x
85 small green table x
86 odd purple chair x
87 cheap yellow burger x
88 large purple car x
89 unsightly brown chair x
90 clean green desk x
91 easy yellow house x
92 small brown pizza x
93 inexpensive white pizza x
94 plain orange pizza x
95 cheap brown burger x
96 long blue cookie x
97 inexpensive pink pony x
98 important red cookie x
99 angry yellow mouse x
100 mushy purple cookie x
101 helpful orange cookie x
102 easy brown house x
103 helpful white mouse x
104 adorable pink burger x
105 inexpensive brown desk x
106 angry red chair x
107 inexpensive blue house x
108 helpful purple chair x
109 short black cookie x
110 unsightly yellow house x
111 crazy yellow keyboard x
112 inexpensive orange pony x
113 mushy brown chair x
114 long pink pizza x
115 quaint yellow cookie x
116 helpful green car x
117 important yellow pony x
118 tall orange pizza x
119 elegant blue car x
120 long orange pony x
121 unsightly purple sandwich x
122 handsome brown bbq x
123 fancy red mouse x
124 adorable red mouse x
125 crazy blue keyboard x
126 handsome yellow sandwich x
127 large red table x
128 easy black cookie x
129 unsightly brown pizza x
130 helpful orange bbq x
131 crazy white desk x
132 fancy blue keyboard x
133 adorable green table x
134 short red house x
135 angry pink car x
136 odd brown bbq x
137 important purple keyboard x
138 big yellow cookie x
139 crazy pink keyboard x
140 short brown desk x
141 clean yellow house x
142 helpful green chair x
143 plain pink pizza x
144 plain white table x
145 short white pony x
146 easy orange keyboard x
147 angry brown burger x
148 adorable blue cookie x
149 adorable brown car x
150 plain green house x
151 crazy blue bbq x
152 crazy white bbq x
153 big brown cookie x
154 plain pink keyboard x
155 easy green mouse x
156 large blue mouse x
157 adorable yellow house x
158 clean brown pony x
159 large red keyboard x
160 angry pink pony x
161 quaint red mouse x
162 crazy pink house x
163 short brown burger x
164 plain black car x
165 handsome black car x
166 plain red mouse x
167 handsome purple sandwich x
168 angry blue bbq x
169 adorable black car x
170 handsome green car x
171 big red keyboard x
172 pretty pink keyboard x
173 easy red car x
174 quaint black cookie x
175 pretty blue chair x
176 adorable red mouse x
177 quaint brown keyboard x
178 handsome white mouse x
179 adorable blue sandwich x
180 important brown table x
181 tall blue mouse x
182 plain red bbq x
183 mushy purple pizza x
184 crazy orange keyboard x
185 mushy green desk x
186 cheap yellow house x
187 expensive purple cookie x
188 tall white car x
189 crazy white mouse x
190 easy purple cookie x
191 pretty red mouse x
192 adorable orange chair x
193 crazy brown car x
194 plain white chair x
195 small pink table x
196 easy blue keyboard x
197 unsightly brown pony x
198 fancy white sandwich x
199 big pink pony x
200 cheap green burger x
201 quaint red mouse x
202 tall black car x
203 cheap pink table x
204 mushy red keyboard x
205 elegant orange burger x
206 short purple sandwich x
207 long yellow table x
208 helpful red bbq x
209 quaint purple chair x
210 easy orange pony x
211 tall brown keyboard x
212 tall white sandwich x
213 large brown chair x
214 adorable green keyboard x
215 important brown cookie x
216 odd red car x
217 mushy red keyboard x
218 mushy brown mouse x
219 easy pink pony x
220 small yellow car x
221 tall black table x
222 short brown desk x
223 large purple chair x
224 expensive white cookie x
225 big purple bbq x
226 crazy white desk x
227 long blue car x
228 unsightly orange table x
229 tall yellow bbq x
230 big purple bbq x
231 angry brown desk x
232 big green house x
233 angry red desk x
234 mushy yellow house x
235 helpful black pizza x
236 cheap pink pizza x
237 short green house x
238 tall blue bbq x
239 pretty black mouse x
240 long yellow burger x
241 big blue bbq x
242 odd orange chair x
243 handsome black car x
244 helpful brown desk x
245 unsightly purple car x
246 tall green house x
247 crazy brown sandwich x
248 short black pony x
249 unsightly brown pizza x
250 handsome black bbq x
251 adorable purple pizza x
252 pretty orange car x
253 long brown mouse x
254 handsome green burger x
255 important purple table x
256 cheap green pony x
257 expensive brown burger x
258 cheap yellow pizza x
259 short blue mouse x
260 angry brown burger x
261 unsightly purple desk x
262 adorable white mouse x
263 crazy purple sandwich x
264 expensive brown car x
265 unsightly orange sandwich x
266 short orange chair x
267 odd purple cookie x
268 elegant white pizza x
269 handsome orange sandwich x
270 large yellow pony x
271 small brown keyboard x
272 crazy green chair x
273 short white sandwich x
274 large brown bbq x
275 angry red house x
276 small yellow car x
277 elegant blue chair x
278 quaint purple table x
279 mushy purple car x
280 crazy white house x
281 odd blue pizza x
282 odd yellow sandwich x
283 tall brown burger x
284 tall white mouse x
285 inexpensive white desk x
286 handsome pink mouse x
287 elegant white keyboard x
288 long pink pizza x
289 crazy purple sandwich x
290 expensive yellow desk x
291 easy brown house x
292 big brown table x
293 helpful pink pizza x
294 fancy black car x
295 short orange mouse x
296 easy purple burger x
297 adorable white car x
298 easy brown cookie x
299 inexpensive black cookie x
300 inexpensive purple pizza x
301 adorable brown mouse x
302 pretty black keyboard x
303 adorable brown burger x
304 handsome white mouse x
305 quaint white bbq x
306 plain green car x
307 odd black bbq x
308 odd black chair x
309 clean black keyboard x
310 expensive pink house x
311 important red bbq x
312 handsome blue pony x
313 inexpensive black burger x
314 plain black house x
315 expensive orange chair x
316 crazy purple pizza x
317 angry purple keyboard x
318 big purple burger x
319 tall orange keyboard x
320 inexpensive black pony x
321 mushy pink burger x
322 elegant brown keyboard x
323 adorable brown pizza x
324 easy yellow bbq x
325 unsightly brown bbq x
326 small yellow car x
327 cheap red cookie x
328 long orange burger x
329 mushy white table x
330 fancy red burger x
331 quaint black car x
332 plain yellow table x
333 tall red house x
334 odd green car x
335 crazy yellow burger x
336 fancy green car x
337 clean red bbq x
338 crazy blue pizza x
339 helpful brown pizza x
340 fancy brown cookie x
341 pretty red burger x
342 helpful brown pizza x
343 clean blue desk x
344 mushy brown pony x
345 adorable white bbq x
346 tall red burger x
347 helpful blue chair x
348 handsome green pony x
349 helpful red pony x
350 easy red chair x
351 easy pink table x
352 pretty purple desk x
353 mushy orange car x
354 important red mouse x
355 handsome pink pizza x
356 helpful yellow pony x
357 handsome brown bbq x
358 expensive green burger x
359 odd pink table x
360 unsightly white pony x
361 big yellow mouse x
362 small black bbq x
363 inexpensive brown car x
364 easy brown burger x
365 handsome orange pony x
366 quaint white table x
367 fancy brown mouse x
368 cheap green burger x
369 unsightly brown sandwich x
370 handsome red house x
371 expensive brown car x
372 adorable green mouse x
373 fancy yellow pizza x
374 adorable pink cookie x
375 clean brown table x
376 expensive purple chair x
377 angry green chair x
378 odd blue car x
379 handsome blue desk x
380 short blue sandwich x
381 elegant brown desk x
382 unsightly orange keyboard x
383 mushy purple table x
384 small pink chair x
385 quaint pink pony x
386 expensive red burger x
387 angry pink keyboard x
388 plain black chair x
389 quaint black desk x
390 cheap red chair x
391 angry red burger x
392 adorable orange cookie x
393 large green cookie x
394 unsightly blue desk x
395 large purple pizza x
396 cheap green pony x
397 long black chair x
398 unsightly white table x
399 short green desk x
400 handsome red keyboard x
401 mushy brown car x
402 handsome black mouse x
403 small white house x
404 long brown house x
405 cheap pink car x
406 clean black cookie x
407 short green pizza x
408 mushy pink chair x
409 big brown table x
410 expensive orange sandwich x
411 short orange pony x
412 tall red chair x
413 cheap purple cookie x
414 pretty yellow keyboard x
415 easy green table x
416 crazy yellow pizza x
417 important yellow table x
418 cheap brown bbq x
419 pretty pink pony x
420 important red table x
421 plain yellow chair x
422 handsome brown pizza x
423 handsome pink pony x
424 clean pink mouse x
425 quaint white pony x
426 expensive green mouse x
427 easy pink sandwich x
428 unsightly pink chair x
429 pretty purple car x
430 quaint red pony x
431 cheap brown mouse x
432 long pink pizza x
433 large blue burger x
434 expensive black house x
435 mushy yellow house x
436 easy pink car x
437 handsome pink keyboard x
438 small brown house x
439 quaint brown cookie x
440 elegant black mouse x
441 quaint white house x
442 fancy purple pony x
443 handsome red car x
444 important blue house x
445 short red cookie x
446 plain brown sandwich x
447 adorable green chair x
448 pretty yellow burger x
449 elegant blue sandwich x
450 inexpensive blue pizza x
451 crazy brown pony x
452 important yellow table x
453 fancy brown mouse x
454 crazy red sandwich x
455 tall blue mouse x
456 handsome green chair x
457 long pink desk x
458 elegant orange keyboard x
459 clean black keyboard x
460 clean purple table x
461 small orange sandwich x
462 clean brown keyboard x
463 inexpensive purple pizza x
464 crazy pink pizza x
465 angry blue chair x
466 helpful black pizza x
467 pretty pink table x
468 easy white keyboard x
469 clean blue bbq x
470 crazy purple table x
471 expensive orange pony x
472 large black keyboard x
473 cheap green sandwich x
474 long black sandwich x
475 long black chair x
476 pretty purple cookie x
477 short brown pony x
478 plain blue bbq x
479 handsome orange chair x
480 elegant white house x
481 mushy yellow table x
482 fancy red bbq x
483 adorable purple sandwich x
484 adorable black pony x
485 fancy brown pizza x
486 angry red desk x
487 pretty pink cookie x
488 fancy green table x
489 mushy black house x
490 short brown chair x
491 tall pink cookie x
492 tall red cookie x
493 unsightly red car x
494 handsome green house x
495 easy brown car x
496 helpful pink keyboard x
497 small pink car x
498 easy white burger x
499 large green house x
500 handsome blue desk x
501 long red mouse x
502 unsightly pink cookie x
503 elegant brown sandwich x
504 fancy red bbq x
505 plain green chair x
506 plain orange pony x
507 big blue keyboard x
508 fancy purple bbq x
509 adorable yellow house x
510 big yellow bbq x
511 large orange desk x
512 long orange chair x
513 crazy orange burger x
514 important pink mouse x
515 pretty blue sandwich x
516 pretty brown table x
517 odd white pizza x
518 tall red desk x
519 quaint blue sandwich x
520 long green keyboard x
521 handsome red bbq x
522 helpful pink pizza x
523 short purple bbq x
524 crazy brown cookie x
525 handsome green sandwich x
526 tall white car x
527 fancy red house x
528 odd brown mouse x
529 adorable green sandwich x
530 tall green car x
531 quaint brown pizza x
532 cheap green keyboard x
533 large black chair x
534 cheap white pizza x
535 inexpensive purple burger x
536 expensive green mouse x
537 fancy brown sandwich x
538 odd pink burger x
539 important purple chair x
540 unsightly pink table x
541 helpful black burger x
542 large brown mouse x
543 plain green burger x
544 important brown pony x
545 small white keyboard x
546 crazy green pony x
547 quaint purple pizza x
548 long green mouse x
549 long orange cookie x
550 helpful red house x
551 quaint black mouse x
552 tall yellow mouse x
553 important pink keyboard x
554 small green pony x
555 fancy purple mouse x
556 plain yellow bbq x
557 tall purple house x
558 inexpensive red pizza x
559 quaint purple bbq x
560 helpful brown bbq x
561 adorable yellow chair x
562 adorable pink burger x
563 elegant yellow cookie x
564 important green mouse x
565 expensive red cookie x
566 inexpensive pink burger x
567 long white sandwich x
568 expensive brown cookie x
569 easy orange table x
570 angry black house x
571 tall blue keyboard x
572 big orange pony x
573 unsightly brown sandwich x
574 angry purple bbq x
575 big purple pizza x
576 adorable blue pizza x
577 tall yellow cookie x
578 adorable blue burger x
579 fancy pink burger x
580 short brown keyboard x
581 crazy red cookie x
582 angry brown chair x
583 easy red chair x
584 quaint pink keyboard x
585 easy red pizza x
586 helpful red burger x
587 angry green car x
588 big black cookie x
589 handsome brown sandwich x
590 clean pink cookie x
591 short white desk x
592 clean black sandwich x
593 quaint blue cookie x
594 quaint brown car x
595 clean pink desk x
596 helpful green car x
597 cheap orange pizza x
598 plain blue burger x
599 large brown cookie x
600 mushy brown mouse x
601 unsightly brown pony x
602 inexpensive brown pizza x
603 easy orange pizza x
604 plain pink house x
605 quaint black bbq x
606 elegant white cookie x
607 mushy red burger x
608 crazy blue sandwich x
609 important red bbq x
610 unsightly blue car x
611 small green cookie x
612 unsightly black keyboard x
613 small green pony x
614 adorable black table x
615 mushy orange table x
616 fancy black table x
617 mushy yellow car x
618 short yellow desk x
619 adorable red mouse x
620 small orange chair x
621 important orange pizza x
622 pretty brown sandwich x
623 easy brown cookie x
624 quaint blue keyboard x
625 easy black sandwich x
626 angry red desk x
627 adorable red sandwich x
628 unsightly yellow pony x
629 crazy white cookie x
630 big blue house x
631 inexpensive red chair x
632 handsome brown pony x
633 big brown bbq x
634 odd purple sandwich x
635 expensive pink sandwich x
636 crazy red chair x
637 mushy brown mouse x
638 odd black pizza x
639 tall orange chair x
640 clean orange house x
641 plain brown pizza x
642 inexpensive black table x
643 tall orange cookie x
644 elegant orange burger x
645 quaint white keyboard x
646 fancy brown sandwich x
647 odd black chair x
648 long white cookie x
649 plain red desk x
650 large brown burger x
651 tall yellow keyboard x
652 easy brown chair x
653 large black table x
654 tall brown sandwich x
655 easy orange table x
656 tall purple house x
657 unsightly black sandwich x
658 quaint yellow car x
659 inexpensive brown chair x
660 short yellow mouse x
661 pretty green bbq x
662 important blue pony x
663 fancy pink sandwich x
664 short orange burger x
665 long orange sandwich x
666 clean blue mouse x
667 helpful brown chair x
668 tall yellow house x
669 inexpensive blue chair x
670 quaint black pony x
671 plain blue desk x
672 important green bbq x
673 clean red keyboard x
674 long white burger x
675 angry yellow pony x
676 cheap white cookie x
677 helpful blue chair x
678 angry brown bbq x
679 pretty pink keyboard x
680 odd black bbq x
681 elegant brown pony x
682 adorable purple bbq x
683 adorable purple table x
684 angry purple bbq x
685 crazy blue burger x
686 expensive pink car x
687 handsome orange bbq x
688 small black table x
689 elegant pink chair x
690 important brown keyboard x
691 mushy blue keyboard x
692 unsightly brown car x
693 unsightly white bbq x
694 inexpensive brown chair x
695 cheap brown sandwich x
696 important white mouse x
697 crazy white chair x
698 small blue bbq x
699 angry blue pony x
700 inexpensive white sandwich x
701 small green bbq x
702 important red pony x
703 large pink burger x
704 quaint orange pizza x
705 unsightly pink cookie x
706 cheap pink chair x
707 cheap orange chair x
708 mushy brown pony x
709 mushy pink sandwich x
710 angry pink burger x
711 mushy red cookie x
712 handsome blue house x
713 odd black desk x
714 pretty white burger x
715 adorable green burger x
716 small yellow chair x
717 large brown mouse x
718 fancy white desk x
719 crazy green chair x
720 small yellow cookie x
721 expensive purple burger x
722 adorable green chair x
723 small brown chair x
724 pretty purple house x
725 helpful orange pizza x
726 pretty brown mouse x
727 odd green cookie x
728 clean green desk x
729 pretty pink chair x
730 tall brown chair x
731 adorable yellow house x
732 inexpensive black table x
733 mushy purple car x
734 large pink keyboard x
735 tall blue keyboard x
736 pretty brown cookie x
737 quaint orange cookie x
738 adorable black sandwich x
739 adorable yellow keyboard x
740 helpful yellow pizza x
741 expensive brown car x
742 handsome brown burger x
743 helpful blue bbq x
744 plain pink mouse x
745 inexpensive purple sandwich x
746 long blue house x
747 inexpensive green burger x
748 short orange bbq x
749 handsome pink chair x
750 elegant pink keyboard x
751 angry blue car x
752 angry pink mouse x
753 elegant red table x
754 big white cookie x
755 angry yellow car x
756 large brown desk x
757 inexpensive blue sandwich x
758 easy yellow cookie x
759 long green car x
760 mushy black cookie x
761 crazy brown pizza x
762 odd purple pizza x
763 short blue pizza x
764 easy orange sandwich x
765 handsome green house x
766 small red mouse x
767 tall white desk x
768 large purple pizza x
769 handsome green car x
770 easy green table x
771 fancy brown pizza x
772 fancy yellow mouse x
773 helpful black mouse x
774 tall orange chair x
775 quaint brown desk x
776 elegant white cookie x
777 large red keyboard x
778 long brown sandwich x
779 crazy green sandwich x
780 elegant red bbq x
781 quaint yellow cookie x
782 fancy red mouse x
783 crazy green bbq x
784 expensive blue mouse x
785 plain brown sandwich x
786 expensive orange sandwich x
787 important white pizza x
788 short orange desk x
789 clean black pizza x
790 easy brown cookie x
791 crazy orange sandwich x
792 unsightly red table x
793 easy orange car x
794 odd orange chair x
795 long yellow chair x
796 elegant green sandwich x
797 cheap brown pony x
798 cheap red keyboard x
799 pretty brown burger x
800 mushy blue pizza x
801 easy red house x
802 easy pink house x
803 easy green house x
804 handsome yellow table x
805 adorable blue cookie x
806 small white keyboard x
807 cheap brown desk x
808 large blue cookie x
809 inexpensive purple table x
810 plain brown chair x
811 tall black bbq x
812 large blue house x
813 quaint orange sandwich x
814 long red desk x
815 small red chair x
816 pretty blue keyboard x
817 clean orange house x
818 clean orange burger x
819 mushy yellow cookie x
820 quaint brown desk x
821 odd pink bbq x
822 expensive pink car x
823 tall brown sandwich x
824 cheap pink keyboard x
825 quaint purple pony x
826 helpful brown pizza x
827 easy white house x
828 fancy pink house x
829 quaint blue pony x
830 angry green burger x
831 mushy brown burger x
832 inexpensive black pizza x
833 clean black cookie x
834 important orange car x
835 inexpensive blue desk x
836 quaint black chair x
837 long red keyboard x
838 large brown house x
839 pretty white car x
840 crazy brown house x
841 crazy green mouse x
842 unsightly black pizza x
843 expensive blue sandwich x
844 handsome brown sandwich x
845 plain pink pizza x
846 adorable brown mouse x
847 inexpensive purple pizza x
848 long white mouse x
849 helpful purple burger x
850 fancy green cookie x
851 small brown desk x
852 short green pizza x
853 big red keyboard x
854 fancy orange car x
855 unsightly orange burger x
856 crazy brown cookie x
857 tall orange car x
858 clean white keyboard x
859 quaint brown cookie x
860 mushy yellow bbq x
861 crazy blue car x
862 fancy brown cookie x
863 fancy blue mouse x
864 inexpensive yellow house x
865 mushy brown desk x
866 cheap black desk x
867 clean blue mouse x
868 small pink car x
869 fancy red table x
870 big purple desk x
871 unsightly red burger x
872 elegant orange pizza x
873 crazy white sandwich x
874 big blue chair x
875 easy black mouse x
876 odd white house x
877 plain green house x
878 plain black mouse x
879 adorable brown pony x
880 plain orange bbq x
881 easy blue desk x
882 mushy white bbq x
883 unsightly brown pizza x
884 crazy white cookie x
885 small white house x
886 clean red chair x
887 clean purple chair x
888 crazy brown sandwich x
889 important white burger x
890 pretty yellow keyboard x
891 big brown cookie x
892 large black cookie x
893 fancy brown house x
894 helpful red burger x
895 big brown burger x
896 handsome red house x
897 cheap green bbq x
898 clean orange mouse x
899 unsightly orange house x
900 mushy white chair x
901 long red mouse x
902 short orange bbq x
903 quaint orange pony x
904 cheap yellow keyboard x
905 angry black cookie x
906 odd orange mouse x
907 pretty red desk x
908 small red cookie x
909 inexpensive red table x
910 small purple burger x
911 angry brown desk x
912 big white keyboard x
913 adorable pink sandwich x
914 big green house x
915 helpful brown cookie x
916 easy pink bbq x
917 quaint orange chair x
918 small yellow keyboard x
919 angry yellow table x
920 adorable yellow keyboard x
921 important white mouse x
922 important brown chair x
923 small orange keyboard x
924 adorable green pony x
925 small black desk x
926 unsightly yellow chair x
927 easy red table x
928 inexpensive white table x
929 plain blue cookie x
930 long white sandwich x
931 elegant white bbq x
932 long yellow car x
933 small blue burger x
934 odd red cookie x
935 helpful white table x
936 long red cookie x
937 important yellow cookie x
938 odd purple pony x
939 tall purple desk x
940 odd brown cookie x
941 tall orange sandwich x
942 big blue burger x
943 helpful blue pony x
944 important brown sandwich x
945 expensive brown bbq x
946 small purple keyboard x
947 handsome purple table x
948 mushy green burger x
949 angry brown desk x
950 fancy brown desk x
951 adorable black pizza x
952 important green pony x
953 small blue cookie x
954 large pink keyboard x
955 mushy brown cookie x
956 handsome pink bbq x
957 short purple mouse x
958 crazy yellow car x
959 angry black burger x
960 quaint black pizza x
961 plain purple table x
962 large black desk x
963 short pink pony x
964 helpful orange desk x
965 large red table x
966 important pink house x
967 tall green mouse x
968 mushy yellow mouse x
969 helpful brown chair x
970 helpful purple bbq x
971 easy yellow desk x
972 small green pizza x
973 adorable brown bbq x
974 tall red bbq x
975 crazy red chair x
976 cheap purple sandwich x
977 clean green pony x
978 elegant pink house x
979 short purple car x
980 adorable white chair x
981 cheap blue mouse x
982 long white car x
983 crazy red burger x
984 odd black cookie x
985 handsome black mouse x
986 pretty yellow cookie x
987 fancy orange bbq x
988 unsightly yellow pony x
989 expensive black pizza x
990 long green table x
991 cheap orange mouse x
992 pretty orange bbq x
993 fancy purple house x
994 unsightly blue chair x
995 cheap brown desk x
996 helpful pink pony x
997 plain blue sandwich x
998 helpful purple table x
999 unsightly blue keyboard x
1000 big green house x
================================================ FILE: benchmarks/loop.html ================================================ Alpine-3-keyed

Alpine-3-keyed

================================================ FILE: benchmarks/memory.html ================================================
================================================ FILE: benchmarks/mutation_observer.html ================================================ Document

yo

there

================================================ FILE: cypress.json ================================================ { "ignoreTestFiles": "*.html", "screenshotOnRunFailure": false, "video": false, "fixturesFolder": "tests/cypress/fixtures", "integrationFolder": "tests/cypress/integration", "pluginsFile": "tests/cypress/plugins/index.js", "screenshotsFolder": "tests/cypress/screenshots", "videosFolder": "tests/cypress/videos", "supportFile": "tests/cypress/support/index.js" } ================================================ FILE: index.html ================================================
foo
foo
foo
================================================ FILE: index2.html ================================================
Previous
Second page:
================================================ FILE: jest.config.js ================================================ let config = { rootDir: './tests/jest', } module.exports = config ================================================ FILE: morph.html ================================================
  • foo

        

        
    ================================================ FILE: package.json ================================================ { "private": true, "workspaces": [ "packages/*" ], "devDependencies": { "axios": "^0.21.1", "chalk": "^4.1.1", "cypress": "^7.0.0", "cypress-plugin-tab": "^1.0.5", "dot-json": "^1.2.2", "esbuild": "~0.16.17", "jest": "^26.6.3", "vitest": "^3.2.4" }, "scripts": { "build": "node ./scripts/build.js", "watch": "node ./scripts/build.js --watch", "test": "cypress run --quiet", "cypress": "cypress open", "vitest": "vitest run", "update-docs": "node ./scripts/update-docs.js", "release": "node ./scripts/release.js" } } ================================================ FILE: packages/alpinejs/builds/cdn.js ================================================ import Alpine from './../src/index' window.Alpine = Alpine queueMicrotask(() => { Alpine.start() }) ================================================ FILE: packages/alpinejs/builds/module.js ================================================ import Alpine from './../src/index' export default Alpine export { Alpine } ================================================ FILE: packages/alpinejs/package.json ================================================ { "name": "alpinejs", "version": "3.15.8", "description": "The rugged, minimal JavaScript framework", "homepage": "https://alpinejs.dev", "repository": { "type": "git", "url": "https://github.com/alpinejs/alpine.git", "directory": "packages/alpinejs" }, "author": "Caleb Porzio", "license": "MIT", "main": "dist/module.cjs.js", "module": "dist/module.esm.js", "unpkg": "dist/cdn.min.js", "dependencies": { "@vue/reactivity": "~3.1.1" } } ================================================ FILE: packages/alpinejs/src/alpine.js ================================================ import { setReactivityEngine, disableEffectScheduling, reactive, effect, release, raw, watch, transaction } from './reactivity' import { mapAttributes, directive, setPrefix as prefix, prefix as prefixed } from './directives' import { start, addRootSelector, addInitSelector, closestRoot, findClosest, initTree, destroyTree, interceptInit } from './lifecycle' import { onElRemoved, onAttributeRemoved, onAttributesAdded, mutateDom, deferMutations, flushAndStopDeferringMutations, startObservingMutations, stopObservingMutations } from './mutation' import { mergeProxies, closestDataStack, addScopeToNode, scope as $data } from './scope' import { setEvaluator, setRawEvaluator, evaluate, evaluateLater, dontAutoEvaluateFunctions, evaluateRaw } from './evaluator' import { transition } from './directives/x-transition' import { clone, cloneNode, skipDuringClone, onlyDuringClone, interceptClone } from './clone' import { interceptor, initInterceptors } from './interceptor' import { getBinding as bound, extractProp } from './utils/bind' import { setErrorHandler } from './utils/error' import { debounce } from './utils/debounce' import { throttle } from './utils/throttle' import { setStyles } from './utils/styles' import { entangle } from './entangle' import { nextTick } from './nextTick' import { walk } from './utils/walk' import { plugin } from './plugin' import { magic, injectMagics } from './magics' import { store } from './store' import { bind } from './binds' import { data } from './datas' let Alpine = { get reactive() { return reactive }, get release() { return release }, get effect() { return effect }, get raw() { return raw }, get transaction() { return transaction }, version: ALPINE_VERSION, flushAndStopDeferringMutations, dontAutoEvaluateFunctions, disableEffectScheduling, startObservingMutations, stopObservingMutations, setReactivityEngine, onAttributeRemoved, onAttributesAdded, closestDataStack, skipDuringClone, onlyDuringClone, addRootSelector, addInitSelector, setErrorHandler, interceptClone, addScopeToNode, deferMutations, mapAttributes, evaluateLater, interceptInit, initInterceptors, injectMagics, setEvaluator, setRawEvaluator, mergeProxies, extractProp, findClosest, onElRemoved, closestRoot, destroyTree, interceptor, // INTERNAL: not public API and is subject to change without major release. transition, // INTERNAL setStyles, // INTERNAL mutateDom, directive, entangle, throttle, debounce, evaluate, evaluateRaw, initTree, nextTick, prefixed, prefix, plugin, magic, store, start, clone, // INTERNAL cloneNode, // INTERNAL bound, $data, watch, walk, data, bind, } export default Alpine ================================================ FILE: packages/alpinejs/src/binds.js ================================================ import { attributesOnly, directives } from "./directives" let binds = {} export function bind(name, bindings) { let getBindings = typeof bindings !== 'function' ? () => bindings : bindings if (name instanceof Element) { return applyBindingsObject(name, getBindings()) } else { binds[name] = getBindings } return () => {} // Null cleanup... } export function injectBindingProviders(obj) { Object.entries(binds).forEach(([name, callback]) => { Object.defineProperty(obj, name, { get() { return (...args) => { return callback(...args) } } }) }) return obj } export function addVirtualBindings(el, bindings) { let getBindings = typeof bindings !== 'function' ? () => bindings : bindings el._x_virtualDirectives = getBindings() } export function applyBindingsObject(el, obj, original) { let cleanupRunners = [] while (cleanupRunners.length) cleanupRunners.pop()() let attributes = Object.entries(obj).map(([name, value]) => ({ name, value })) let staticAttributes = attributesOnly(attributes) // Handle binding normal HTML attributes (non-Alpine directives). attributes = attributes.map(attribute => { if (staticAttributes.find(attr => attr.name === attribute.name)) { return { name: `x-bind:${attribute.name}`, value: `"${attribute.value}"`, } } return attribute }) directives(el, attributes, original).map(handle => { cleanupRunners.push(handle.runCleanups) handle() }) return () => { while (cleanupRunners.length) cleanupRunners.pop()() } } ================================================ FILE: packages/alpinejs/src/clone.js ================================================ import { effect, release, overrideEffect } from "./reactivity" import { initTree, isRoot } from "./lifecycle" import { walk } from "./utils/walk" export let isCloning = false export function skipDuringClone(callback, fallback = () => {}) { return (...args) => isCloning ? fallback(...args) : callback(...args) } export function onlyDuringClone(callback) { return (...args) => isCloning && callback(...args) } let interceptors = [] export function interceptClone(callback) { interceptors.push(callback) } export function cloneNode(from, to) { interceptors.forEach(i => i(from, to)) isCloning = true // We don't need reactive effects in the new tree. // Cloning is just used to seed new server HTML with // Alpine before "morphing" it onto live Alpine... dontRegisterReactiveSideEffects(() => { initTree(to, (el, callback) => { // We're hijacking the "walker" so that we // only initialize the element we're cloning... callback(el, () => {}) }) }) isCloning = false } export let isCloningLegacy = false /** deprecated */ export function clone(oldEl, newEl) { if (! newEl._x_dataStack) newEl._x_dataStack = oldEl._x_dataStack isCloning = true isCloningLegacy = true dontRegisterReactiveSideEffects(() => { cloneTree(newEl) }) isCloning = false isCloningLegacy = false } /** deprecated */ export function cloneTree(el) { let hasRunThroughFirstEl = false let shallowWalker = (el, callback) => { walk(el, (el, skip) => { if (hasRunThroughFirstEl && isRoot(el)) return skip() hasRunThroughFirstEl = true callback(el, skip) }) } initTree(el, shallowWalker) } function dontRegisterReactiveSideEffects(callback) { let cache = effect overrideEffect((callback, el) => { let storedEffect = cache(callback) release(storedEffect) return () => {} }) callback() overrideEffect(cache) } ================================================ FILE: packages/alpinejs/src/datas.js ================================================ let datas = {} export function data(name, callback) { datas[name] = callback } export function injectDataProviders(obj, context) { Object.entries(datas).forEach(([name, callback]) => { Object.defineProperty(obj, name, { get() { return (...args) => { return callback.bind(context)(...args) } }, enumerable: false, }) }) return obj } ================================================ FILE: packages/alpinejs/src/directives/index.js ================================================ import { directive } from '../directives' import { warn } from '../utils/warn' import './x-transition' import './x-modelable' import './x-teleport' import './x-ignore' import './x-effect' import './x-model' import './x-cloak' import './x-init' import './x-text' import './x-html' import './x-bind' import './x-data' import './x-show' import './x-for' import './x-ref' import './x-if' import './x-id' import './x-on' // Register warnings for people using plugin syntaxes and not loading the plugin itself: warnMissingPluginDirective('Collapse', 'collapse', 'collapse') warnMissingPluginDirective('Intersect', 'intersect', 'intersect') warnMissingPluginDirective('Focus', 'trap', 'focus') warnMissingPluginDirective('Mask', 'mask', 'mask') function warnMissingPluginDirective(name, directiveName, slug) { directive(directiveName, (el) => warn(`You can't use [x-${directiveName}] without first installing the "${name}" plugin here: https://alpinejs.dev/plugins/${slug}`, el)) } ================================================ FILE: packages/alpinejs/src/directives/x-bind.js ================================================ import { directive, into, mapAttributes, prefix, startingWith } from '../directives' import { evaluateLater } from '../evaluator' import { mutateDom } from '../mutation' import bind from '../utils/bind' import { applyBindingsObject, injectBindingProviders } from '../binds' mapAttributes(startingWith(':', into(prefix('bind:')))) let handler = (el, { value, modifiers, expression, original }, { effect, cleanup }) => { if (! value) { let bindingProviders = {} injectBindingProviders(bindingProviders) let getBindings = evaluateLater(el, expression) getBindings(bindings => { applyBindingsObject(el, bindings, original) }, { scope: bindingProviders } ) return } if (value === 'key') return storeKeyForXFor(el, expression) if (el._x_inlineBindings && el._x_inlineBindings[value] && el._x_inlineBindings[value].extract) { return } let evaluate = evaluateLater(el, expression) effect(() => evaluate(result => { // If nested object key is undefined, set the default value to empty string. if (result === undefined && typeof expression === 'string' && expression.match(/\./)) { result = '' } mutateDom(() => bind(el, value, result, modifiers)) })) cleanup(() => { el._x_undoAddedClasses && el._x_undoAddedClasses() el._x_undoAddedStyles && el._x_undoAddedStyles() }) } // @todo: see if I can take advantage of the object created here inside the // non-inline handler above so we're not duplicating work twice... handler.inline = (el, { value, modifiers, expression }) => { if (! value) return; if (! el._x_inlineBindings) el._x_inlineBindings = {} el._x_inlineBindings[value] = { expression, extract: false } } directive('bind', handler) function storeKeyForXFor(el, expression) { el._x_keyExpression = expression } ================================================ FILE: packages/alpinejs/src/directives/x-cloak.js ================================================ import { directive, prefix } from '../directives' import { mutateDom } from '../mutation' directive('cloak', el => queueMicrotask(() => mutateDom(() => el.removeAttribute(prefix('cloak'))))) ================================================ FILE: packages/alpinejs/src/directives/x-data.js ================================================ import { directive, prefix } from '../directives' import { initInterceptors } from '../interceptor' import { injectDataProviders } from '../datas' import { addRootSelector } from '../lifecycle' import { interceptClone, isCloning, isCloningLegacy } from '../clone' import { addScopeToNode } from '../scope' import { injectMagics, magic } from '../magics' import { reactive } from '../reactivity' import { evaluate } from '../evaluator' addRootSelector(() => `[${prefix('data')}]`) directive('data', ((el, { expression }, { cleanup }) => { if (shouldSkipRegisteringDataDuringClone(el)) return expression = expression === '' ? '{}' : expression let magicContext = {} injectMagics(magicContext, el) let dataProviderContext = {} injectDataProviders(dataProviderContext, magicContext) let data = evaluate(el, expression, { scope: dataProviderContext }) if (data === undefined || data === true) data = {} injectMagics(data, el) let reactiveData = reactive(data) initInterceptors(reactiveData) let undo = addScopeToNode(el, reactiveData) reactiveData['init'] && evaluate(el, reactiveData['init']) cleanup(() => { reactiveData['destroy'] && evaluate(el, reactiveData['destroy']) undo() }) })) interceptClone((from, to) => { // Transfer over existing runtime Alpine state from // the existing dom tree over to the new one... if (from._x_dataStack) { to._x_dataStack = from._x_dataStack // Set a flag to signify the new tree is using // pre-seeded state (used so x-data knows when // and when not to initialize state)... to.setAttribute('data-has-alpine-state', true) } }) // If we are cloning a tree, we only want to evaluate x-data if another // x-data context DOESN'T exist on the component. // The reason a data context WOULD exist is that we graft root x-data state over // from the live tree before hydrating the clone tree. function shouldSkipRegisteringDataDuringClone(el) { if (! isCloning) return false if (isCloningLegacy) return true return el.hasAttribute('data-has-alpine-state') } ================================================ FILE: packages/alpinejs/src/directives/x-effect.js ================================================ import { skipDuringClone } from '../clone' import { directive } from '../directives' import { evaluate, evaluateLater } from '../evaluator' directive('effect', skipDuringClone((el, { expression }, { effect }) => { effect(evaluateLater(el, expression)) })) ================================================ FILE: packages/alpinejs/src/directives/x-for.js ================================================ import { addScopeToNode } from '../scope' import { evaluateLater } from '../evaluator' import { directive } from '../directives' import { reactive } from '../reactivity' import { initTree, destroyTree } from '../lifecycle' import { mutateDom } from '../mutation' import { warn } from '../utils/warn' import { skipDuringClone } from '../clone' directive('for', (el, { expression }, { effect, cleanup }) => { let iteratorNames = parseForExpression(expression) let evaluateItems = evaluateLater(el, iteratorNames.items) let evaluateKey = evaluateLater(el, // the x-bind:key expression is stored for our use instead of evaluated. el._x_keyExpression || 'index' ) el._x_lookup = new Map() effect(() => loop(el, iteratorNames, evaluateItems, evaluateKey)) cleanup(() => { el._x_lookup.forEach(el => mutateDom(() => { destroyTree(el) el.remove() }) ) delete el._x_lookup }) }) function refreshScope(scope) { return (newScope) => { Object.entries(newScope).forEach(([key, value]) => { scope[key] = value }) } } function loop(templateEl, iteratorNames, evaluateItems, evaluateKey) { evaluateItems(items => { // Prepare yourself. There's a lot going on here. Take heart, // every bit of complexity in this function was added for // the purpose of making Alpine fast with large datas. // Support number literals. Ex: x-for="i in 100" if (isNumeric(items)) items = Array.from({ length: items }, (_, i) => i + 1) if (items === undefined) items = [] // Support Set and Map objects by converting to arrays. if (items instanceof Set) items = Array.from(items) if (items instanceof Map) items = Array.from(items) // In order to remove elements early we need to generate the key/scope // pairs up front, moving existing elements from the old lookup to the // new. This leaves only the elements to be removed in the old lookup. let oldLookup = templateEl._x_lookup let lookup = new Map() templateEl._x_lookup = lookup let hasStringKeys = isObject(items) let scopeEntries = Object.entries(items).map(([index, item]) => { if (! hasStringKeys) index = parseInt(index) let scope = getIterationScopeVariables(iteratorNames, item, index, items) let key evaluateKey(innerKey => { if (typeof innerKey === 'object') warn('x-for key cannot be an object, it must be a string or an integer', templateEl) if (oldLookup.has(innerKey)) { lookup.set(innerKey, oldLookup.get(innerKey)) oldLookup.delete(innerKey) } key = innerKey }, { scope: { index, ...scope } }) return [key, scope] }) mutateDom(() => { oldLookup.forEach((el) => { destroyTree(el) el.remove() }) let added = new Set() let prev = templateEl scopeEntries.forEach(([key, scope]) => { if (lookup.has(key)) { let el = lookup.get(key) el._x_refreshXForScope(scope) if (prev.nextElementSibling !== el) { if (prev.nextElementSibling) el.replaceWith(prev.nextElementSibling) prev.after(el) } prev = el if (el._x_currentIfEl) { if (el.nextElementSibling !== el._x_currentIfEl) prev.after(el._x_currentIfEl) prev = el._x_currentIfEl } return } let clone = document.importNode(templateEl.content, true).firstElementChild let reactiveScope = reactive(scope) addScopeToNode(clone, reactiveScope, templateEl) clone._x_refreshXForScope = refreshScope(reactiveScope) lookup.set(key, clone) added.add(clone) prev.after(clone) prev = clone }) skipDuringClone(() => added.forEach(clone => initTree(clone)))() }) }) } // This was taken from VueJS 2.* core. Thanks Vue! function parseForExpression(expression) { let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/ let stripParensRE = /^\s*\(|\)\s*$/g let forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/ let inMatch = expression.match(forAliasRE) if (! inMatch) return let res = {} res.items = inMatch[2].trim() let item = inMatch[1].replace(stripParensRE, '').trim() let iteratorMatch = item.match(forIteratorRE) if (iteratorMatch) { res.item = item.replace(forIteratorRE, '').trim() res.index = iteratorMatch[1].trim() if (iteratorMatch[2]) { res.collection = iteratorMatch[2].trim() } } else { res.item = item } return res } function getIterationScopeVariables(iteratorNames, item, index, items) { // We must create a new object, so each iteration has a new scope let scopeVariables = {} // Support array destructuring ([foo, bar]). if (/^\[.*\]$/.test(iteratorNames.item) && Array.isArray(item)) { let names = iteratorNames.item.replace('[', '').replace(']', '').split(',').map(i => i.trim()) names.forEach((name, i) => { scopeVariables[name] = item[i] }) // Support object destructuring ({ foo: 'oof', bar: 'rab' }). } else if (/^\{.*\}$/.test(iteratorNames.item) && ! Array.isArray(item) && typeof item === 'object') { let names = iteratorNames.item.replace('{', '').replace('}', '').split(',').map(i => i.trim()) names.forEach(name => { scopeVariables[name] = item[name] }) } else { scopeVariables[iteratorNames.item] = item } if (iteratorNames.index) scopeVariables[iteratorNames.index] = index if (iteratorNames.collection) scopeVariables[iteratorNames.collection] = items return scopeVariables } function isNumeric(subject){ return ! Array.isArray(subject) && ! isNaN(subject) } function isObject(subject) { return typeof subject === 'object' && ! Array.isArray(subject) } ================================================ FILE: packages/alpinejs/src/directives/x-html.js ================================================ import { directive } from '../directives' import { initTree } from '../lifecycle' import { mutateDom } from '../mutation' directive('html', (el, { expression }, { effect, evaluateLater }) => { let evaluate = evaluateLater(expression) effect(() => { evaluate(value => { mutateDom(() => { el.innerHTML = value ?? '' el._x_ignoreSelf = true initTree(el) delete el._x_ignoreSelf }) }) }) }) ================================================ FILE: packages/alpinejs/src/directives/x-id.js ================================================ import { interceptClone } from "../clone" import { directive } from "../directives" import { setIdRoot } from '../ids' directive('id', (el, { expression }, { evaluate }) => { let names = evaluate(expression) names.forEach(name => setIdRoot(el, name)) }) interceptClone((from, to) => { // Transfer over existing ID registrations from // the existing dom tree over to the new one // so that there aren't ID mismatches... if (from._x_ids) { to._x_ids = from._x_ids } }) ================================================ FILE: packages/alpinejs/src/directives/x-if.js ================================================ import { evaluateLater } from '../evaluator' import { addScopeToNode } from '../scope' import { directive } from '../directives' import { initTree, destroyTree } from '../lifecycle' import { mutateDom } from '../mutation' import { warn } from "../utils/warn" import { skipDuringClone } from '../clone' directive('if', (el, { expression }, { effect, cleanup }) => { if (el.tagName.toLowerCase() !== 'template') warn('x-if can only be used on a