pax_global_header00006660000000000000000000000064152333400300014503gustar00rootroot0000000000000052 comment=389c7bcc5140566f3fbae73cf17edf4ab44f4d96 preactjs-preact-389c7bc/000077500000000000000000000000001523334003000152165ustar00rootroot00000000000000preactjs-preact-389c7bc/.editorconfig000066400000000000000000000003461523334003000176760ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [{*.json,.*rc,*.yml}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false preactjs-preact-389c7bc/.eslintignore000066400000000000000000000000201523334003000177110ustar00rootroot00000000000000dist benchmarks preactjs-preact-389c7bc/.github/000077500000000000000000000000001523334003000165565ustar00rootroot00000000000000preactjs-preact-389c7bc/.github/CODEOWNERS000066400000000000000000000001121523334003000201430ustar00rootroot00000000000000# Request review for GitHub configuration changes. .github/ @JoviDeCroock preactjs-preact-389c7bc/.github/CODE_OF_CONDUCT.md000066400000000000000000000062171523334003000213630ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@preactjs.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ preactjs-preact-389c7bc/.github/FUNDING.yml000066400000000000000000000000531523334003000203710ustar00rootroot00000000000000github: [preactjs] open_collective: preact preactjs-preact-389c7bc/.github/ISSUE_TEMPLATE/000077500000000000000000000000001523334003000207415ustar00rootroot00000000000000preactjs-preact-389c7bc/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000012451523334003000234350ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- - [ ] Check if updating to the latest Preact version resolves the issue **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** If possible, please provide a link to a StackBlitz/CodeSandbox/Codepen project or a GitHub repository that demonstrates the issue. You can use the following template on StackBlitz to get started: https://stackblitz.com/edit/create-preact-starter Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. See error **Expected behavior** What should have happened when following the steps above? preactjs-preact-389c7bc/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000005251523334003000244700ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: feature request assignees: '' --- **Describe the feature you'd love to see** A clear and concise description of what you'd love to see added to Preact. **Additional context (optional)** Add any other context or screenshots about the feature request here. preactjs-preact-389c7bc/.github/workflows/000077500000000000000000000000001523334003000206135ustar00rootroot00000000000000preactjs-preact-389c7bc/.github/workflows/benchmarks.yml000066400000000000000000000041421523334003000234540ustar00rootroot00000000000000name: Benchmarks on: workflow_dispatch: workflow_call: jobs: prepare: name: Prepare environment runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Download locally built preact package uses: actions/download-artifact@v4 with: name: npm-package - run: mv preact.tgz preact-local.tgz - name: Download base package uses: andrewiggins/download-base-artifact@v3 with: artifact: npm-package workflow: ci.yml required: false - run: mv preact.tgz preact-main.tgz - name: Upload locally build & base preact package uses: actions/upload-artifact@v4 with: name: bench-environment path: | preact-local.tgz preact-main.tgz bench_todo: name: Bench todo uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: todo/todo timeout: 10 bench_text_update: name: Bench text-update uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: text-update/text-update timeout: 10 bench_many_updates: name: Bench many-updates uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: many-updates/many-updates timeout: 10 bench_replace1k: name: Bench replace1k uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: table-app/replace1k bench_update10th1k: name: Bench 03_update10th1k_x16 uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: table-app/update10th1k bench_create10k: name: Bench create10k uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: table-app/create10k bench_hydrate1k: name: Bench hydrate1k uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: table-app/hydrate1k bench_filter_list: name: Bench filter-list uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: filter-list/filter-list timeout: 10 preactjs-preact-389c7bc/.github/workflows/build-test.yml000066400000000000000000000031711523334003000234140ustar00rootroot00000000000000name: Build & Test on: workflow_dispatch: workflow_call: inputs: ref: description: 'Branch or tag ref to check out' type: string required: false default: '' artifact_name: description: 'Name of the artifact to upload' type: string required: false default: 'npm-package' permissions: contents: read jobs: build_test: name: Build & Test runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ inputs.ref || '' }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: 'package.json' - run: npm ci - name: test env: CI: true COVERAGE: true FLAKEY: false # Not using `npm test` since it rebuilds source which npm ci has already done run: npm run lint && npm run test:unit - name: Coveralls GitHub Action uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0 timeout-minutes: 2 with: github-token: ${{ secrets.GITHUB_TOKEN }} fail-on-error: false - name: Package # Use --ignore-scripts here to avoid re-building again before pack run: | npm pack --ignore-scripts mv preact-*.tgz preact.tgz - name: Upload npm package uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ${{ inputs.artifact_name || 'npm-package' }} path: preact.tgz preactjs-preact-389c7bc/.github/workflows/ci.yml000066400000000000000000000020431523334003000217300ustar00rootroot00000000000000name: CI on: workflow_dispatch: pull_request: branches: - '**' push: branches: - main - v10.x jobs: filter_jobs: name: Filter jobs runs-on: ubuntu-latest outputs: jsChanged: ${{ steps.filter.outputs.jsChanged }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: # Should be kept in sync with the filter in the PR Reporter workflow predicate-quantifier: 'every' filters: | jsChanged: - '**/src/**/*.js' - '!devtools/src/devtools.js' compressed_size: name: Compressed Size needs: filter_jobs if: ${{ needs.filter_jobs.outputs.jsChanged == 'true' }} uses: ./.github/workflows/size.yml build_test: name: Build & Test needs: filter_jobs uses: ./.github/workflows/build-test.yml benchmarks: name: Benchmarks needs: build_test if: ${{ needs.filter_jobs.outputs.jsChanged == 'true' }} uses: ./.github/workflows/benchmarks.yml preactjs-preact-389c7bc/.github/workflows/pr-reporter.yml000066400000000000000000000055061523334003000236250ustar00rootroot00000000000000name: Report Results to PR on: # The pull_request event can't write comments for PRs from forks so using this # workflow_run workflow as a workaround workflow_run: workflows: ['CI'] types: - completed - requested jobs: filter_jobs: name: Filter jobs runs-on: ubuntu-latest if: | github.event.workflow_run.event == 'pull_request' outputs: jsChanged: ${{ steps.filter.outputs.jsChanged }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: # As this Workflow is triggered by a `workflow_run` event, the filter action # can't automatically assume we're working with PR data. As such, we need to # wire it up manually with a base (merge target) and ref (source branch). base: ${{ github.event.workflow_run.pull_requests[0].base.sha }} ref: ${{ github.event.workflow_run.pull_requests[0].head.sha }} # Should be kept in sync with the filter in the CI workflow predicate-quantifier: 'every' filters: | jsChanged: - '**/src/**/*.js' - '!devtools/src/devtools.js' report_running: name: Report benchmarks are in-progress needs: filter_jobs runs-on: ubuntu-latest # Only add the "benchmarks are running" text when a workflow_run is # requested (a.k.a starting) if: | needs.filter_jobs.outputs.jsChanged == 'true' && github.event.action == 'requested' steps: - name: Report Tachometer Running uses: andrewiggins/tachometer-reporter-action@v2 with: # Set initialize to true so this action just creates the comment or # adds the "benchmarks are running" text initialize: true report_results: name: Report benchmark results needs: filter_jobs runs-on: ubuntu-latest # Only run this job if the event action was "completed" and the triggering # workflow_run was successful if: | needs.filter_jobs.outputs.jsChanged == 'true' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success' steps: # Download the artifact from the triggering workflow that contains the # Tachometer results to report - uses: dawidd6/action-download-artifact@v2 with: workflow: ${{ github.event.workflow.id }} run_id: ${{ github.event.workflow_run.id }} name_is_regexp: true name: results-* path: results # Create/update the comment with the latest results - name: Report Tachometer Results uses: andrewiggins/tachometer-reporter-action@v2 with: path: results/**/*.json base-bench-name: preact-main pr-bench-name: preact-local summarize: 'duration, usedJSHeapSize' preactjs-preact-389c7bc/.github/workflows/release.yml000066400000000000000000000074461523334003000227710ustar00rootroot00000000000000name: Release on: push: tags: - '10.*' permissions: contents: read jobs: build: if: github.event.deleted == false permissions: contents: read uses: ./.github/workflows/build-test.yml release: runs-on: ubuntu-latest needs: build permissions: contents: write steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: npm-package - name: Create draft release id: create-release uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 with: script: | const script = require('./scripts/release/create-gh-release.js') return script({ github, context }) - name: Upload release artifact uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 env: RELEASE_DATA: ${{ steps.create-release.outputs.result }} with: script: | const script = require('./scripts/release/upload-gh-asset.js') const release = JSON.parse(process.env.RELEASE_DATA) return script({ require, github, context, glob, release }) publish: needs: [build, release] runs-on: ubuntu-latest environment: name: npm url: https://www.npmjs.com/package/preact permissions: contents: read id-token: write steps: - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: npm-package - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '24' registry-url: 'https://registry.npmjs.org' - name: Update npm run: npm install -g npm@11.15.0 - name: Validate package run: | set -euo pipefail TAG="${GITHUB_REF_NAME}" mkdir -p package-metadata tar -xOf preact.tgz package/package.json > package-metadata/package.json PACKAGE_NAME="$(node -p "require('./package-metadata/package.json').name")" PACKAGE_VERSION="$(node -p "require('./package-metadata/package.json').version")" if [[ "$PACKAGE_NAME" != "preact" ]]; then echo "::error::Expected package name 'preact', got '$PACKAGE_NAME'" exit 1 fi if [[ "$PACKAGE_VERSION" != "$TAG" ]]; then echo "::error::Tag '$TAG' does not match package version '$PACKAGE_VERSION'" exit 1 fi # Determine the npm dist-tag from the git tag: # - Stable versions publish with --tag latest # - Prerelease versions must use an approved prerelease identifier as the # dist-tag (10.29.0-rc.1 -> rc, 10.29.0-beta.1 -> beta) - name: Determine dist-tag id: dist-tag run: | set -euo pipefail TAG="${GITHUB_REF_NAME}" if [[ "$TAG" =~ ^10\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then echo "tag=latest" >> "$GITHUB_OUTPUT" elif [[ "$TAG" =~ ^10\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-([0-9A-Za-z-]+)(\.[0-9A-Za-z-]+)*$ ]]; then DIST_TAG="${BASH_REMATCH[3]}" case "$DIST_TAG" in alpha|beta|rc|next) echo "tag=$DIST_TAG" >> "$GITHUB_OUTPUT" ;; *) echo "::error::Refusing to publish prerelease '$TAG' with unapproved npm dist-tag '$DIST_TAG'" exit 1 ;; esac else echo "::error::Unexpected release tag '$TAG'" exit 1 fi - name: Publish to npm run: npm stage publish preact.tgz --provenance --access public --tag "${{ steps.dist-tag.outputs.tag }}" preactjs-preact-389c7bc/.github/workflows/run-bench.yml000066400000000000000000000106711523334003000232240ustar00rootroot00000000000000name: Benchmark Worker # Expectations: # # This workflow expects calling workflows to have uploaded an artifact named # "bench-environment" that contains any built artifacts required to run the # benchmark. This typically is the dist/ folder that running `npm run build` # produces and/or a tarball of a previous build to bench the local build against on: workflow_call: inputs: benchmark: description: 'The name of the benchmark to run. Should be name of an HTML file without the .html extension' type: string required: true trace: description: 'Whether to capture browser traces for this benchmark run' type: boolean required: false default: false timeout: description: 'How many minutes to give the benchmark to run before timing out and failing' type: number required: false default: 20 jobs: run_bench: name: Bench ${{ inputs.benchmark }} runs-on: ubuntu-latest timeout-minutes: ${{ inputs.timeout }} steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - uses: actions/setup-node@v4 with: node-version-file: 'package.json' cache: 'npm' cache-dependency-path: '**/package-lock.json' # Setup pnpm - name: Install pnpm uses: pnpm/action-setup@v3 with: version: 8 run_install: false - name: Get pnpm store directory id: pnpm-cache run: | echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- # Install benchmark dependencies - uses: actions/download-artifact@v4 with: name: bench-environment - name: Move tarballs from env to correct location run: | ls -al mv preact-local.tgz benchmarks/dependencies/preact/local-pinned/preact-local-pinned.tgz ls -al benchmarks/dependencies/preact/local-pinned mv preact-main.tgz benchmarks/dependencies/preact/main/preact-main.tgz ls -al benchmarks/dependencies/preact/main - name: Install deps working-directory: benchmarks # Set the CHROMEDRIVER_FILEPATH so the chromedriver npm package uses the # correct binary when its installed run: | export CHROMEDRIVER_FILEPATH=$(which chromedriver) pnpm install # Install local dependencies with --no-frozen-lockfile to ensure local tarballs # are installed regardless of if they match the integrity hash stored in the lockfile pnpm install --no-frozen-lockfile --filter ./dependencies # Run benchmark - name: Run benchmark working-directory: benchmarks run: | export CHROMEDRIVER_FILEPATH=$(which chromedriver) pnpm run bench apps/${{ inputs.benchmark }}.html -d preact@local-pinned -d preact@main --trace=${{ inputs.trace }} # Prepare output - name: Anaylze logs if present working-directory: benchmarks run: '[ -d out/logs ] && pnpm run analyze ${{ inputs.benchmark }} || echo "No logs to analyze"' - name: Tar logs if present working-directory: benchmarks run: | if [ -d out/logs ]; then LOGS_FILE=out/${{ inputs.benchmark }}_logs.tgz mkdir -p $(dirname $LOGS_FILE) tar -zcvf $LOGS_FILE out/logs else echo "No logs found" fi # Upload results and logs - name: Calculate log artifact name id: log-artifact-name run: | NAME=$(echo "${{ inputs.benchmark }}" | sed -r 's/[\/]+/_/g') echo "clean_name=$NAME" >> $GITHUB_OUTPUT echo "artifact_name=logs_$NAME" >> $GITHUB_OUTPUT - name: Upload results uses: actions/upload-artifact@v4 with: name: results-${{ steps.log-artifact-name.outputs.clean_name }} path: benchmarks/out/results/${{ inputs.benchmark }}.json - name: Upload logs uses: actions/upload-artifact@v4 with: name: ${{ steps.log-artifact-name.outputs.artifact_name }} path: benchmarks/out/${{ inputs.benchmark }}_logs.tgz if-no-files-found: ignore preactjs-preact-389c7bc/.github/workflows/single-bench.yml000066400000000000000000000050061523334003000236750ustar00rootroot00000000000000name: Benchmark Debug on: workflow_dispatch: inputs: benchmark: description: 'Which benchmark to run' type: choice options: - table-app/replace1k - table-app/update10th1k - table-app/create10k - table-app/hydrate1k - filter_list/filter-list - many-updates/many-updates - text-update/text-update - todo/todo required: true base: description: 'The branch name, tag, or commit sha of the version of preact to benchmark against.' type: string default: main required: false trace: description: 'Whether to capture browser traces for this benchmark run' type: boolean default: true required: false # A bug in GitHub actions prevents us from passing numbers (as either # number or string type) to called workflows. So disabling this for now. # See: https://github.com/orgs/community/discussions/67182 # # timeout: # description: 'How many minutes to give the benchmark to run before timing out and failing' # type: number # default: 20 # required: false jobs: build_local: name: Build local package uses: ./.github/workflows/ci.yml build_base: name: Build base package uses: ./.github/workflows/ci.yml with: ref: ${{ inputs.base }} artifact_name: base-npm-package prepare: name: Prepare environment runs-on: ubuntu-latest needs: - build_local - build_base timeout-minutes: 5 steps: - name: Download locally built preact package uses: actions/download-artifact@v4 with: name: npm-package - run: mv preact.tgz preact-local.tgz - name: Clear working directory run: | ls -al rm -rf * echo "====================" ls -al - name: Download base package uses: actions/download-artifact@v4 with: name: base-npm-package - run: mv preact.tgz preact-main.tgz - name: Upload locally built & base preact package uses: actions/upload-artifact@v4 with: name: bench-environment path: | preact-local.tgz preact-main.tgz benchmark: name: Bench ${{ inputs.benchmark }} uses: ./.github/workflows/run-bench.yml needs: prepare with: benchmark: ${{ inputs.benchmark }} trace: ${{ inputs.trace }} # timeout: ${{ inputs.timeout }} preactjs-preact-389c7bc/.github/workflows/size.yml000066400000000000000000000011171523334003000223100ustar00rootroot00000000000000name: Compressed Size on: workflow_call: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: 'package.json' cache: 'npm' cache-dependency-path: '**/package-lock.json' - uses: preactjs/compressed-size-action@v2 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' # Our `prepare` script already builds the app post-install, # building it again would be redundant build-script: 'npm run --if-present noop' preactjs-preact-389c7bc/.gitignore000066400000000000000000000002371523334003000172100ustar00rootroot00000000000000.DS_Store node_modules npm-debug.log dist */package-lock.json yarn.lock .vscode .idea test/ts/**/*.js coverage *.sw[op] *.log package/ preact-*.tgz preact.tgz preactjs-preact-389c7bc/.gitmodules000066400000000000000000000001321523334003000173670ustar00rootroot00000000000000[submodule "benchmarks"] path = benchmarks url = https://github.com/preactjs/benchmarks preactjs-preact-389c7bc/.husky/000077500000000000000000000000001523334003000164375ustar00rootroot00000000000000preactjs-preact-389c7bc/.husky/pre-commit000066400000000000000000000000201523334003000204260ustar00rootroot00000000000000npx nano-staged preactjs-preact-389c7bc/.oxfmtrc.json000066400000000000000000000017251523334003000176560ustar00rootroot00000000000000{ "$schema": "./node_modules/oxfmt/configuration_schema.json", "endOfLine": "lf", "insertFinalNewline": true, "useTabs": true, "tabWidth": 2, "printWidth": 80, "singleQuote": true, "jsxSingleQuote": false, "quoteProps": "as-needed", "trailingComma": "none", "semi": true, "arrowParens": "avoid", "bracketSameLine": false, "bracketSpacing": true, "singleAttributePerLine": false, "experimentalSortPackageJson": false, "ignorePatterns": [ "benchmarks/**", "**/.DS_Store", "**/node_modules", "**/npm-debug.log", "**/dist", "*/package-lock.json", "**/yarn.lock", "**/.vscode", "**/.idea", "test/ts/**/*.js", "**/coverage", "**/*.sw[op]", "**/*.log", "**/package/", "**/preact-*.tgz", "**/preact.tgz", "**/package-lock.json" ], "overrides": [ { "files": ["*.json", ".*rc", "*.yml"], "options": { "useTabs": false, "tabWidth": 2 } } ] } preactjs-preact-389c7bc/.prettierignore000066400000000000000000000002621523334003000202610ustar00rootroot00000000000000.DS_Store node_modules npm-debug.log dist */package-lock.json yarn.lock .vscode .idea test/ts/**/*.js coverage *.sw[op] *.log package/ preact-*.tgz preact.tgz package-lock.json preactjs-preact-389c7bc/CODE_OF_CONDUCT.md000066400000000000000000000062171523334003000200230ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@preactjs.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ preactjs-preact-389c7bc/CONTRIBUTING.md000066400000000000000000000365451523334003000174640ustar00rootroot00000000000000# Contributing This document is intended for developers interest in making contributions to Preact and document our internal processes like releasing a new version. ## Getting Started This steps will help you to set up your development environment. That includes all dependencies we use to build Preact and developer tooling like git commit hooks. 1. Clone the git repository: `git clone git@github.com:preactjs/preact.git` 2. Go into the cloned folder: `cd preact/` 3. Install all dependencies: `npm install` ## The Repo Structure This repository contains Preact itself, as well as several addons like the debugging package for example. This is reflected in the directory structure of this repository. Each package has a `src/` folder where the source code can be found, a `test` folder for all sorts of tests that check if the code in `src/` is correct, and a `dist/` folder where you can find the bundled artifacts. Note that the `dist/` folder may not be present initially. It will be created as soon as you run any of the build scripts inside `package.json`. More on that later ;) A quick overview of our repository: ```bash # The repo root (folder where you cloned the repo into) / src/ # Source code of our core test/ # Unit tests for core dist/ # Build artifacts for publishing on npm (may not be present) # Sub-package, can be imported via `preact/compat` by users. # Compat stands for react-compatibility layer which tries to mirror the # react API as close as possible (mostly legacy APIs) compat/ src/ # Source code of the compat addon test/ # Tests related to the compat addon dist/ # Build artifacts for publishing on npm (may not be present) # Sub-package, can be imported via `preact/hooks` by users. # The hooks API is an effect based API to deal with component lifcycles. # It's similar to hooks in React hooks/ src/ # Source code of the hooks addon test/ # Tests related to the hooks addon dist/ # Build artifacts for publishing on npm (may not be present) # Sub-package, can be imported via `preact/debug` by users. # Includes debugging warnings and error messages for common mistakes found # in Preact application. Also hosts the devtools bridge debug/ src/ # Source code of the debug addon test/ # Tests related to the debug addon dist/ # Build artifacts for publishing on npm (may not be present) # Sub-package, can be imported via `preact/test-utils` by users. # Provides helpers to make testing Preact applications easier test-utils/ src/ # Source code of the test-utils addon test/ # Tests related to the test-utils addon dist/ # Build artifacts for publishing on npm (may not be present) # A demo application that we use to debug tricky errors and play with new # features. demo/ # Contains build scripts and dependencies for development package.json ``` _Note: The code for rendering Preact on the server lives in another repo and is a completely separate npm package. It can be found here: [https://github.com/preactjs/preact-render-to-string](https://github.com/preactjs/preact-render-to-string)_ ### What does `mangle.json` do? It's a special file that can be used to specify how `terser` (previously known as `uglify`) will minify variable names. Because each sub-package has it's own distribution files we need to ensure that the variable names stay consistent across bundles. ## What does `options.js` do? Unique to Preact we do support several ways to hook into our renderer. All our addons use that to inject code at different stages of a render process. They are documented in our typings in `internal.d.ts`. The core itself doesn't make use of them, which is why the file only contains an empty `object`. ## Important Branches We merge every PR into the `main` branch which is the one that we'll use to publish code to npm. For the previous Preact release line we have a branch called `8` which is in maintenance mode. As a new contributor you won't have to deal with that ;) ## Creating your first Pull-Request We try to make it as easy as possible to contribute to Preact and make heavy use of GitHub's "Draft PR" feature which tags Pull-Requests (short = PR) as work in progress. PRs tend to be published as soon as there is an idea that the developer deems worthwhile to include into Preact and has written some rough code. The PR doesn't have to be perfect or anything really ;) Once a PR or a Draft PR has been created our community typically joins the discussion about the proposed change. Sometimes that includes ideas for test cases or even different ways to go about implementing a feature. Often this also includes ideas on how to make the code smaller. We usually refer to the latter as "code-golfing" or just "golfing". When everything is good to go someone will approve the PR and the changes will be merged into the `main` branch and we usually cut a release a few days/ a week later. _The big takeaway for you here is, that we will guide you along the way. We're here to help to make a PR ready for approval!_ The short summary is: 1. Make changes and submit a PR 2. Modify change according to feedback (if there is any) 3. PR will be merged into `main` 4. A new release will be cut (every 2-3 weeks). ## Commonly used scripts for contributions Scripts can be executed via `npm run [script]` or `yarn [script]` respectively. - `build` - compiles all packages ready for publishing to npm - `build:core` - builds just Preact itself - `build:debug` - builds the debug addon only - `build:hooks` - builds the hook addon only - `build:test-utils` - builds the test-utils addon only - `test:ts` - Run all tests for TypeScript definitions - `test:karma` - Run all unit/integration tests. - `test:karma:watch` - Same as above, but it will automatically re-run the test suite if a code change was detected. But to be fair, the only ones we use ourselves are `build` and `test:karma:watch`. The other ones are mainly used on our CI pipeline and we rarely use them. _Note: Both `test:karma` and `test:karma:watch` listen to the environment variable `COVERAGE=true`. Disabling code coverage can significantly speed up the time it takes to complete the test suite._ _Note2: The test suite is based on `karma` and `mocha`. Individual tests can be executed by appending `.only`:_ ```jsx it.only('should test something', () => { expect(1).to.equal(1); }); ``` ## Common terminology and variable names - `vnode` -> shorthand for `virtual-node` which is an object that specifies how a Component or DOM-node looks like - `commit` -> A commit is the moment in time when you flush all changes to the DOM - `c` -> The variable `c` always refers to a `component` instance throughout our code base. - `diff/diffing` -> Diffing describes the process of comparing two "things". In our case we compare the previous `vnode` tree with the new one and apply the delta to the DOM. - `root` -> The topmost node of a `vnode` tree ## Tips for getting to know the code base - Check the JSDoc block right above the function definition to understand what it does. It contains a short description of each function argument and what it does. - Check the callsites of a function to understand how it's used. Modern editors/IDEs allow you to quickly find those, or use the plain old search feature instead. ## Benchmarks We have a benchmark suite that we use to measure the performance of Preact. Our benchmark suite lives in our [preactjs/benchmarks repository](https://github.com/preactjs/benchmarks), but is included here as Git submodule. To run the benchmarks, first ensure [PNPM](https://pnpm.io/installation) is installed on your system and initialize and setup the submodule (it uses `pnpm` as a package manager): ```bash pnpm -v # Make sure pnpm is installed git submodule update --init --recursive cd benchmarks pnpm i ``` Then you can run the benchmarks: ```bash # In the benchmarks folder pnpm run bench ``` Checkout the README in the benchmarks folder for more information on running benchmarks. > **Note:** When switching branches, git submodules are not automatically updated to the commit of the new branch - it stays at the commit of the previous branch. This can be a feature! It allows you to work in different branches with the latest versions of the benchmarks - especially if you have made changes to the benchmarks. > > However if you want to switch branches and also update the benchmarks to the latest commit of the new branch, you can run `git submodule update --recursive` after switching branches, or run `git checkout --recurse-submodules` when checking out a new branch. ## FAQ ### Why does the JSDoc use TypeScript syntax to specify types? Several members of the team are very fond of TypeScript and we wanted to leverage as many of its advantages, like improved autocompletion, for Preact. We even attempted to port Preact to TypeScript a few times, but we ran into many issues with the DOM typings. Those would force us to fill our codebase with many `any` castings, making our code very noisy. Luckily TypeScript has a mode where it can somewhat reliably typecheck JavaScript code by reusing the types defined in JSDoc blocks. It's not perfect and it often has trouble inferring the correct types the further one strays away from the function arguments, but it's good enough that it helps us a lot with autocompletion. Another plus is that we can make sure that our TypeScript definitons are correct at the same time. Check out the [official TypeScript documentation](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) for more information. _Note that we have separate tests for our TypeScript definition files. We only use `ts-check` for local development and don't check it anywhere else like on the CI._ ### Why does the code base often use `let` instead of `const`? There is no real reason for that other a historical one. Back before auto-formatting via prettier was a thing and minifiers weren't as advanced as they are today we used a pretty terse code-style. The code-style deliberately was aimed at making code look as concise and short as possible. The `let` keyword is a bit shorter than `const` to write, so we only used that. This was done only for stylistic reasons. This helped our minds to not lose sight of focusing on size, but made it difficult for newcomers to start contributing to Preact. For that reason alone we switched to `prettier` and loosened our rule regarding usage of `let` or `const`. Today we use both, but you can still find many existing places where `let` is still in use. In the end there is no effect on size regardless if you use `const`, `let` or use both. Our code is downtranspiled to `ES5` for npm so both will be replaced with `var` anyways. Therefore it doesn't really matter at all which one is used in our codebase. This will only become important once shipping modern JavaScript code on npm becomes a thing and bundlers follow suit. ## How to create a good bug report To be able to fix issues we need to see them on our machine. This is only possible when we can reproduce the error. The easiest way to do that is narrow down the problem to specific components or combination of them. This can be done by removing as much unrelated code as possible. The perfect way to do that is to make a [codesandbox](https://codesandbox.io/). That way you can easily share the problematic code and ensure that others can see the same issue you are seeing. For us a [codesandbox](https://codesandbox.io/) says more than a 1000 words :tada: ## I have more questions on how to contribute to Preact. How can I reach you? We closely watch our issues and have a pretty active [Slack workspace](https://chat.preactjs.com/). Nearly all our communication happens via these two forms of communication. ## Releasing Preact (Maintainers only) This guide is intended for core team members that have the necessary rights to publish new releases on npm. Before using the automated npm publishing flow, make sure npm trusted publishing is configured for the `preactjs/preact` repository, the `release.yml` workflow, and the `npm` environment. The GitHub `npm` environment should require reviewer approval, and repository rules should protect `10.*` tags. 1. Make a PR where **only** the version number is incremented in `package.json` and everywhere else. A simple search and replace works. (note: We follow `SemVer` conventions) 2. Wait until the PR is approved and merged. 3. Switch back to the `v10.x` branch and pull the merged PR 4. Create and push a tag for the new version you want to publish: 1. `git tag 10.0.0` 2. `git push origin 10.0.0` 5. Wait for the Release workflow to reach the `npm` environment approval gate, approve it, and let it complete - It'll validate that the tag matches the package version, create a draft release, upload the built npm package as a release asset, and publish it to npm. - Stable releases publish to the `latest` npm dist-tag; prereleases publish to the approved prerelease dist-tag (`alpha`, `beta`, `rc`, or `next`). 6. [Fill in the release notes](#writing-release-notes) in GitHub and publish them 7. Tweet it out ## Legacy Releases (8.x) > **ATTENTION:** Make sure that you've cleared the project correctly > when switching from a 10.x branch. 0. Run `rm -rf dist node_modules && npm i` to make sure to have the correct dependencies. 1. [Write the release notes](#writing-release-notes) and keep them as a draft in GitHub 1. I'd recommend writing them in an offline editor because each edit to a draft will change the URL in GitHub. 2. Make a PR where **only** the version number is incremented in `package.json` (note: We follow `SemVer` conventions) 3. Wait until the PR is approved and merged. 4. Switch back to the `main` branch and pull the merged PR 5. Run `npm run build && npm publish` 1. Make sure you have 2FA enabled in npm, otherwise the above command will fail. 2. If you're doing a pre-release add `--tag next` to the `npm publish` command to publish it under a different tag (default is `latest`) 6. Publish the release notes and create the correct git tag. 7. Tweet it out ## Writing release notes The release notes have become a sort of tiny blog post about what's happening in preact-land. The title usually has this format: ```txt Version Name ``` Example: ```txt 10.0.0-beta.1 Los Compresseros ``` The name is optional, we just have fun finding creative names :wink: To keep them interesting we try to be as concise as possible and to just reflect where we are. There are some rules we follow while writing them: - Be nice, use a positive tone. Avoid negative words - Show, don't just tell. - Be honest. - Don't write too much, keep it simple and short. - Avoid making promises and don't overpromise. That leads to unhappy users - Avoid framework comparisons if possible - Highlight awesome community contributions (or great issue reports) - If in doubt, praise the users. After this section we typically follow with a changelog part that's divided into 4 groups in order of importance for the user: - Features - Bug Fixes - Typings - Maintenance We generate it via this handy cli program: [changelogged](https://github.com/marvinhagemeister/changelogged). It will collect and format the descriptions of all PRs that have been merged between two tags. The usual command is `changelogged 10.0.0-rc.2..HEAD` similar to how you'd diff two points in time with git. This will get you 90% there, but you still need to divide it into groups. It's also a good idea to unify the formatting of the descriptions, so that they're easier to read and don't look like a mess. preactjs-preact-389c7bc/LICENSE000066400000000000000000000020771523334003000162310ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-present Jason Miller 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. preactjs-preact-389c7bc/README.md000066400000000000000000000350631523334003000165040ustar00rootroot00000000000000

![Preact](https://raw.githubusercontent.com/preactjs/preact/8b0bcc927995c188eca83cba30fbc83491cc0b2f/logo.svg?sanitize=true 'Preact')

Fast 3kB alternative to React with the same modern API.

**All the power of Virtual DOM components, without the overhead:** - Familiar React API & patterns: ES6 Class, hooks, and Functional Components - Extensive React compatibility via a simple [preact/compat] alias - Everything you need: JSX, VDOM, [DevTools], HMR, SSR. - Highly optimized diff algorithm and seamless hydration from Server Side Rendering - Supports all modern browsers and IE11 - Transparent asynchronous rendering with a pluggable scheduler ### 💁 More information at the [Preact Website ➞](https://preactjs.com)
[![npm](https://img.shields.io/npm/v/preact.svg)](https://www.npmjs.com/package/preact) [![Preact Slack Community](https://img.shields.io/badge/Slack%20Community-preact.slack.com-blue)](https://chat.preactjs.com) [![OpenCollective Backers](https://opencollective.com/preact/backers/badge.svg)](#backers) [![OpenCollective Sponsors](https://opencollective.com/preact/sponsors/badge.svg)](#sponsors) [![coveralls](https://img.shields.io/coveralls/preactjs/preact/main.svg)](https://coveralls.io/github/preactjs/preact) [![gzip size](https://img.badgesize.io/https://unpkg.com/preact/dist/preact.min.js?compression=gzip&label=gzip)](https://unpkg.com/preact/dist/preact.min.js) [![brotli size](https://img.badgesize.io/https://unpkg.com/preact/dist/preact.min.js?compression=brotli&label=brotli)](https://unpkg.com/preact/dist/preact.min.js)
You can find some awesome libraries in the [awesome-preact list](https://github.com/preactjs/awesome-preact) :sunglasses: --- ## Getting Started > 💁 _**Note:** You [don't need ES2015 to use Preact](https://github.com/developit/preact-in-es3)... but give it a try!_ #### Tutorial: Building UI with Preact With Preact, you create user interfaces by assembling trees of components and elements. Components are functions or classes that return a description of what their tree should output. These descriptions are typically written in [JSX](https://facebook.github.io/jsx/) (shown underneath), or [HTM](https://github.com/developit/htm) which leverages standard JavaScript Tagged Templates. Both syntaxes can express trees of elements with "props" (similar to HTML attributes) and children. To get started using Preact, first look at the render() function. This function accepts a tree description and creates the structure described. Next, it appends this structure to a parent DOM element provided as the second argument. Future calls to render() will reuse the existing tree and update it in-place in the DOM. Internally, render() will calculate the difference from previous outputted structures in an attempt to perform as few DOM operations as possible. ```js import { h, render } from 'preact'; // Tells babel to use h for JSX. It's better to configure this globally. // See https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#usage // In tsconfig you can specify this with the jsxFactory /** @jsx h */ // create our tree and append it to document.body: render(

Hello

, document.body ); // update the tree in-place: render(

Hello World!

, document.body ); // ^ this second invocation of render(...) will use a single DOM call to update the text of the

``` Hooray! render() has taken our structure and output a User Interface! This approach demonstrates a simple case, but would be difficult to use as an application grows in complexity. Each change would be forced to calculate the difference between the current and updated structure for the entire application. Components can help here – by dividing the User Interface into nested Components each can calculate their difference from their mounted point. Here's an example: ```js import { render, h } from 'preact'; import { useState } from 'preact/hooks'; /** @jsx h */ const App = () => { const [input, setInput] = useState(''); return (

Do you agree to the statement: "Preact is awesome"?

setInput(e.target.value)} />
); }; render(, document.body); ``` --- ## Sponsors Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/preact#sponsor)]             SentDM Songsterr Deno ## Backers Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/preact#backer)] --- ## License MIT [![Preact](https://i.imgur.com/YqCHvEW.gif)](https://preactjs.com) [preact/compat]: https://github.com/preactjs/preact/tree/main/compat [hyperscript]: https://github.com/dominictarr/hyperscript [DevTools]: https://github.com/preactjs/preact-devtools preactjs-preact-389c7bc/babel.config.js000066400000000000000000000017361523334003000200740ustar00rootroot00000000000000module.exports = function (api) { api.cache(true); const noModules = String(process.env.BABEL_NO_MODULES) === 'true'; const rename = {}; const mangle = require('./mangle.json'); for (let prop in mangle.props.props) { let name = prop; if (name[0] === '$') { name = name.slice(1); } rename[name] = mangle.props.props[prop]; } return { presets: [ [ '@babel/preset-env', { loose: true, // Don't transform modules when using esbuild modules: noModules ? false : 'auto', exclude: ['@babel/plugin-transform-typeof-symbol'], targets: { browsers: ['last 2 versions', 'IE >= 9'] } } ] ], plugins: [ '@babel/plugin-transform-react-jsx', ['babel-plugin-transform-rename-properties', { rename }] ], include: ['**/src/**/*.js', '**/test/**/*.js', '**/test/**/*.jsx'], overrides: [ { test: /(component-stack|debug)\.test\.jsx?$/, plugins: ['@babel/plugin-transform-react-jsx-source'] } ] }; }; preactjs-preact-389c7bc/benchmarks/000077500000000000000000000000001523334003000173335ustar00rootroot00000000000000preactjs-preact-389c7bc/compat/000077500000000000000000000000001523334003000165015ustar00rootroot00000000000000preactjs-preact-389c7bc/compat/LICENSE000066400000000000000000000020771523334003000175140ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-present Jason Miller 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. preactjs-preact-389c7bc/compat/client.d.ts000066400000000000000000000006211523334003000205500ustar00rootroot00000000000000// Intentionally not using a relative path to take advantage of // the TS version resolution mechanism import * as preact from 'preact'; export function createRoot(container: preact.ContainerNode): { render(children: preact.ComponentChild): void; unmount(): void; }; export function hydrateRoot( container: preact.ContainerNode, children: preact.ComponentChild ): ReturnType; preactjs-preact-389c7bc/compat/client.js000066400000000000000000000007371523334003000203240ustar00rootroot00000000000000const { render, hydrate, unmountComponentAtNode } = require('preact/compat'); function createRoot(container) { return { // eslint-disable-next-line render: function (children) { render(children, container); }, // eslint-disable-next-line unmount: function () { unmountComponentAtNode(container); } }; } exports.createRoot = createRoot; exports.hydrateRoot = function (container, children) { hydrate(children, container); return createRoot(container); }; preactjs-preact-389c7bc/compat/client.mjs000066400000000000000000000007511523334003000204750ustar00rootroot00000000000000import { render, hydrate, unmountComponentAtNode } from 'preact/compat'; export function createRoot(container) { return { // eslint-disable-next-line render: function (children) { render(children, container); }, // eslint-disable-next-line unmount: function () { unmountComponentAtNode(container); } }; } export function hydrateRoot(container, children) { hydrate(children, container); return createRoot(container); } export default { createRoot, hydrateRoot }; preactjs-preact-389c7bc/compat/jsx-dev-runtime.js000066400000000000000000000001131523334003000220730ustar00rootroot00000000000000require('preact/compat'); module.exports = require('preact/jsx-runtime'); preactjs-preact-389c7bc/compat/jsx-dev-runtime.mjs000066400000000000000000000000751523334003000222570ustar00rootroot00000000000000import 'preact/compat'; export * from 'preact/jsx-runtime'; preactjs-preact-389c7bc/compat/jsx-runtime.js000066400000000000000000000001131523334003000213170ustar00rootroot00000000000000require('preact/compat'); module.exports = require('preact/jsx-runtime'); preactjs-preact-389c7bc/compat/jsx-runtime.mjs000066400000000000000000000000751523334003000215030ustar00rootroot00000000000000import 'preact/compat'; export * from 'preact/jsx-runtime'; preactjs-preact-389c7bc/compat/mangle.json000066400000000000000000000012541523334003000206410ustar00rootroot00000000000000{ "help": { "what is this file?": "It controls protected/private property mangling so that minified builds have consistent property names.", "why are there duplicate minified properties?": "Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size." }, "minify": { "mangle": { "properties": { "regex": "^_[^_]", "reserved": [ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", "__REACT_DEVTOOLS_GLOBAL_HOOK__", "__PREACT_DEVTOOLS__", "_renderers", "__source", "__self" ] } } } } preactjs-preact-389c7bc/compat/package.json000066400000000000000000000025141523334003000207710ustar00rootroot00000000000000{ "name": "preact-compat", "amdName": "preactCompat", "version": "4.0.0", "private": true, "description": "A React compatibility layer for Preact", "main": "dist/compat.js", "module": "dist/compat.module.js", "umd:main": "dist/compat.umd.js", "source": "src/index.js", "types": "src/index.d.ts", "license": "MIT", "mangle": { "regex": "^_" }, "peerDependencies": { "preact": "^10.0.0" }, "exports": { ".": { "types": "./src/index.d.ts", "browser": "./dist/compat.module.js", "umd": "./dist/compat.umd.js", "import": "./dist/compat.mjs", "require": "./dist/compat.js" }, "./client": { "types": "./client.d.ts", "import": "./client.mjs", "require": "./client.js" }, "./server": { "browser": "./server.browser.js", "import": "./server.mjs", "require": "./server.js" }, "./jsx-runtime": { "import": "./jsx-runtime.mjs", "require": "./jsx-runtime.js" }, "./jsx-dev-runtime": { "import": "./jsx-dev-runtime.mjs", "require": "./jsx-dev-runtime.js" }, "./scheduler": { "import": "./scheduler.mjs", "require": "./scheduler.js" }, "./test-utils": { "import": "./test-utils.mjs", "require": "./test-utils.js" }, "./package.json": "./package.json" } } preactjs-preact-389c7bc/compat/scheduler.js000066400000000000000000000005341523334003000210170ustar00rootroot00000000000000// see scheduler.mjs function unstable_runWithPriority(priority, callback) { return callback(); } module.exports = { unstable_ImmediatePriority: 1, unstable_UserBlockingPriority: 2, unstable_NormalPriority: 3, unstable_LowPriority: 4, unstable_IdlePriority: 5, unstable_runWithPriority, unstable_now: performance.now.bind(performance) }; preactjs-preact-389c7bc/compat/scheduler.mjs000066400000000000000000000014001523334003000211650ustar00rootroot00000000000000/* eslint-disable */ // This file includes experimental React APIs exported from the "scheduler" // npm package. Despite being explicitely marked as unstable some libraries // already make use of them. This file is not a full replacement for the // scheduler package, but includes the necessary shims to make those libraries // work with Preact. export var unstable_ImmediatePriority = 1; export var unstable_UserBlockingPriority = 2; export var unstable_NormalPriority = 3; export var unstable_LowPriority = 4; export var unstable_IdlePriority = 5; /** * @param {number} priority * @param {() => void} callback */ export function unstable_runWithPriority(priority, callback) { return callback(); } export var unstable_now = performance.now.bind(performance); preactjs-preact-389c7bc/compat/server.browser.js000066400000000000000000000003531523334003000220300ustar00rootroot00000000000000import { renderToString } from 'preact-render-to-string'; export { renderToString, renderToString as renderToStaticMarkup } from 'preact-render-to-string'; export default { renderToString, renderToStaticMarkup: renderToString }; preactjs-preact-389c7bc/compat/server.js000066400000000000000000000020441523334003000203450ustar00rootroot00000000000000/* eslint-disable */ var renderToString; try { const mod = require('preact-render-to-string'); renderToString = mod.default || mod.renderToString || mod; } catch (e) { throw Error( 'renderToString() error: missing "preact-render-to-string" dependency.' ); } var renderToReadableStream; try { const mod = require('preact-render-to-string/stream'); renderToReadableStream = mod.default || mod.renderToReadableStream || mod; } catch (e) { throw Error( 'renderToReadableStream() error: update "preact-render-to-string" dependency to at least 6.5.0.' ); } var renderToPipeableStream; try { const mod = require('preact-render-to-string/stream-node'); renderToPipeableStream = mod.default || mod.renderToPipeableStream || mod; } catch (e) { throw Error( 'renderToPipeableStream() error: update "preact-render-to-string" dependency to at least 6.5.0.' ); } module.exports = { renderToString: renderToString, renderToStaticMarkup: renderToString, renderToPipeableStream: renderToPipeableStream, renderToReadableStream: renderToReadableStream }; preactjs-preact-389c7bc/compat/server.mjs000066400000000000000000000011131523334003000205160ustar00rootroot00000000000000import { renderToString } from 'preact-render-to-string'; import { renderToPipeableStream } from 'preact-render-to-string/stream-node'; import { renderToReadableStream } from 'preact-render-to-string/stream'; export { renderToString, renderToString as renderToStaticMarkup } from 'preact-render-to-string'; export { renderToPipeableStream } from 'preact-render-to-string/stream-node'; export { renderToReadableStream } from 'preact-render-to-string/stream'; export default { renderToString, renderToStaticMarkup: renderToString, renderToPipeableStream, renderToReadableStream }; preactjs-preact-389c7bc/compat/src/000077500000000000000000000000001523334003000172705ustar00rootroot00000000000000preactjs-preact-389c7bc/compat/src/Children.js000066400000000000000000000010521523334003000213540ustar00rootroot00000000000000import { toChildArray } from 'preact'; const mapFn = (children, fn) => { if (children == null) return null; return toChildArray(toChildArray(children).map(fn)); }; // This API is completely unnecessary for Preact, so it's basically passthrough. export const Children = { map: mapFn, forEach: mapFn, count(children) { return children ? toChildArray(children).length : 0; }, only(children) { const normalized = toChildArray(children); if (normalized.length !== 1) throw 'Children.only'; return normalized[0]; }, toArray: toChildArray }; preactjs-preact-389c7bc/compat/src/PureComponent.js000066400000000000000000000010531523334003000224230ustar00rootroot00000000000000import { Component } from 'preact'; import { shallowDiffers } from './util'; /** * Component class with a predefined `shouldComponentUpdate` implementation */ export function PureComponent(p, c) { this.props = p; this.context = c; } PureComponent.prototype = new Component(); // Some third-party libraries check if this property is present PureComponent.prototype.isPureReactComponent = true; PureComponent.prototype.shouldComponentUpdate = function (props, state) { return shallowDiffers(this.props, props) || shallowDiffers(this.state, state); }; preactjs-preact-389c7bc/compat/src/forwardRef.js000066400000000000000000000025231523334003000217310ustar00rootroot00000000000000import { options } from 'preact'; import { assign } from './util'; let oldDiffHook = options._diff; options._diff = vnode => { if (vnode.type && vnode.type._forwarded && vnode.ref) { vnode.props.ref = vnode.ref; vnode.ref = null; } if (oldDiffHook) oldDiffHook(vnode); }; export const REACT_FORWARD_SYMBOL = (typeof Symbol != 'undefined' && Symbol.for && Symbol.for('react.forward_ref')) || 0xf47; /** * Pass ref down to a child. This is mainly used in libraries with HOCs that * wrap components. Using `forwardRef` there is an easy way to get a reference * of the wrapped component instead of one of the wrapper itself. * @param {import('./index').ForwardFn} fn * @returns {import('./internal').FunctionComponent} */ export function forwardRef(fn) { function Forwarded(props) { let clone = assign({}, props); delete clone.ref; return fn(clone, props.ref || null); } // mobx-react checks for this being present Forwarded.$$typeof = REACT_FORWARD_SYMBOL; // mobx-react heavily relies on implementation details. // It expects an object here with a `render` property, // and prototype.render will fail. Without this // mobx-react throws. Forwarded.render = fn; Forwarded.prototype.isReactComponent = Forwarded._forwarded = true; Forwarded.displayName = 'ForwardRef(' + (fn.displayName || fn.name) + ')'; return Forwarded; } preactjs-preact-389c7bc/compat/src/hooks.js000066400000000000000000000032041523334003000207500ustar00rootroot00000000000000import { useState, useLayoutEffect, useEffect } from 'preact/hooks'; import { is } from './util'; /** * This is taken from https://github.com/facebook/react/blob/main/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L84 * on a high level this cuts out the warnings, ... and attempts a smaller implementation * @typedef {{ _value: any; _getSnapshot: () => any }} Store */ export function useSyncExternalStore(subscribe, getSnapshot) { const value = getSnapshot(); /** * @typedef {{ _instance: Store }} StoreRef * @type {[StoreRef, (store: StoreRef) => void]} */ const [{ _instance }, forceUpdate] = useState({ _instance: { _value: value, _getSnapshot: getSnapshot } }); useLayoutEffect(() => { _instance._value = value; _instance._getSnapshot = getSnapshot; if (didSnapshotChange(_instance)) { forceUpdate({ _instance }); } }, [subscribe, value, getSnapshot]); useEffect(() => { if (didSnapshotChange(_instance)) { forceUpdate({ _instance }); } return subscribe(() => { if (didSnapshotChange(_instance)) { forceUpdate({ _instance }); } }); }, [subscribe]); return value; } /** @type {(inst: Store) => boolean} */ function didSnapshotChange(inst) { try { return !is(inst._value, inst._getSnapshot()); } catch (error) { return true; } } export function startTransition(cb) { cb(); } export function useDeferredValue(val) { return val; } export function useTransition() { return [false, startTransition]; } // TODO: in theory this should be done after a VNode is diffed as we want to insert // styles/... before it attaches export const useInsertionEffect = useLayoutEffect; preactjs-preact-389c7bc/compat/src/index.d.ts000066400000000000000000000332741523334003000212020ustar00rootroot00000000000000import * as _hooks from '../../hooks'; // Intentionally not using a relative path to take advantage of // the TS version resolution mechanism import * as preact from 'preact'; import { JSXInternal } from '../../src/jsx'; import * as _Suspense from './suspense'; import * as _SuspenseList from './suspense-list'; // export default React; export = React; export as namespace React; declare namespace React { // Export JSX export import JSX = JSXInternal; // Hooks export import CreateHandle = _hooks.CreateHandle; export import EffectCallback = _hooks.EffectCallback; export import Inputs = _hooks.Inputs; export import PropRef = _hooks.PropRef; export import Reducer = _hooks.Reducer; export import Dispatch = _hooks.Dispatch; export import SetStateAction = _hooks.StateUpdater; export import useCallback = _hooks.useCallback; export import useContext = _hooks.useContext; export import useDebugValue = _hooks.useDebugValue; export import useEffect = _hooks.useEffect; export import useImperativeHandle = _hooks.useImperativeHandle; export import useId = _hooks.useId; export import useLayoutEffect = _hooks.useLayoutEffect; export import useMemo = _hooks.useMemo; export import useReducer = _hooks.useReducer; export import useRef = _hooks.useRef; export import useState = _hooks.useState; // React 18 hooks export import useInsertionEffect = _hooks.useLayoutEffect; export function useTransition(): [false, typeof startTransition]; export function useDeferredValue(val: T): T; export function useSyncExternalStore( subscribe: (flush: () => void) => () => void, getSnapshot: () => T ): T; // Preact Defaults export import Context = preact.Context; export import ContextType = preact.ContextType; export import RefObject = preact.RefObject; export import Component = preact.Component; export import FunctionComponent = preact.FunctionComponent; export import ComponentType = preact.ComponentType; export import ComponentClass = preact.ComponentClass; export import FC = preact.FunctionComponent; export import createContext = preact.createContext; export import Ref = preact.Ref; export import createRef = preact.createRef; export import Fragment = preact.Fragment; export import createElement = preact.createElement; export import cloneElement = preact.cloneElement; export import ComponentProps = preact.ComponentProps; export import ReactNode = preact.ComponentChild; export import ReactElement = preact.VNode; export import Consumer = preact.Consumer; export import ErrorInfo = preact.ErrorInfo; export import Key = preact.Key; // Suspense export import Suspense = _Suspense.Suspense; export import lazy = _Suspense.lazy; export import SuspenseList = _SuspenseList.SuspenseList; // Compat export import StrictMode = preact.Fragment; export const version: string; export function startTransition(cb: () => void): void; // HTML export interface HTMLAttributes< T extends EventTarget > extends JSXInternal.HTMLAttributes {} export interface HTMLProps extends JSXInternal.AllHTMLAttributes, preact.ClassAttributes {} export interface AllHTMLAttributes< T extends EventTarget > extends JSXInternal.AllHTMLAttributes {} export import DetailedHTMLProps = JSXInternal.DetailedHTMLProps; export import CSSProperties = JSXInternal.CSSProperties; export interface SVGProps extends JSXInternal.SVGAttributes, preact.ClassAttributes {} interface SVGAttributes< T extends EventTarget = SVGElement > extends JSXInternal.SVGAttributes {} interface ReactSVG extends JSXInternal.IntrinsicSVGElements {} export import AriaAttributes = JSXInternal.AriaAttributes; export import HTMLAttributeReferrerPolicy = JSXInternal.HTMLAttributeReferrerPolicy; export import HTMLAttributeAnchorTarget = JSXInternal.HTMLAttributeAnchorTarget; export import HTMLInputTypeAttribute = JSXInternal.HTMLInputTypeAttribute; export import HTMLAttributeCrossOrigin = JSXInternal.HTMLAttributeCrossOrigin; export import AnchorHTMLAttributes = JSXInternal.AnchorHTMLAttributes; export import AudioHTMLAttributes = JSXInternal.AudioHTMLAttributes; export import AreaHTMLAttributes = JSXInternal.AreaHTMLAttributes; export import BaseHTMLAttributes = JSXInternal.BaseHTMLAttributes; export import BlockquoteHTMLAttributes = JSXInternal.BlockquoteHTMLAttributes; export import ButtonHTMLAttributes = JSXInternal.ButtonHTMLAttributes; export import CanvasHTMLAttributes = JSXInternal.CanvasHTMLAttributes; export import ColHTMLAttributes = JSXInternal.ColHTMLAttributes; export import ColgroupHTMLAttributes = JSXInternal.ColgroupHTMLAttributes; export import DataHTMLAttributes = JSXInternal.DataHTMLAttributes; export import DetailsHTMLAttributes = JSXInternal.DetailsHTMLAttributes; export import DelHTMLAttributes = JSXInternal.DelHTMLAttributes; export import DialogHTMLAttributes = JSXInternal.DialogHTMLAttributes; export import EmbedHTMLAttributes = JSXInternal.EmbedHTMLAttributes; export import FieldsetHTMLAttributes = JSXInternal.FieldsetHTMLAttributes; export import FormHTMLAttributes = JSXInternal.FormHTMLAttributes; export import IframeHTMLAttributes = JSXInternal.IframeHTMLAttributes; export import ImgHTMLAttributes = JSXInternal.ImgHTMLAttributes; export import InsHTMLAttributes = JSXInternal.InsHTMLAttributes; export import InputHTMLAttributes = JSXInternal.InputHTMLAttributes; export import KeygenHTMLAttributes = JSXInternal.KeygenHTMLAttributes; export import LabelHTMLAttributes = JSXInternal.LabelHTMLAttributes; export import LiHTMLAttributes = JSXInternal.LiHTMLAttributes; export import LinkHTMLAttributes = JSXInternal.LinkHTMLAttributes; export import MapHTMLAttributes = JSXInternal.MapHTMLAttributes; export import MenuHTMLAttributes = JSXInternal.MenuHTMLAttributes; export import MediaHTMLAttributes = JSXInternal.MediaHTMLAttributes; export import MetaHTMLAttributes = JSXInternal.MetaHTMLAttributes; export import MeterHTMLAttributes = JSXInternal.MeterHTMLAttributes; export import QuoteHTMLAttributes = JSXInternal.QuoteHTMLAttributes; export import ObjectHTMLAttributes = JSXInternal.ObjectHTMLAttributes; export import OlHTMLAttributes = JSXInternal.OlHTMLAttributes; export import OptgroupHTMLAttributes = JSXInternal.OptgroupHTMLAttributes; export import OptionHTMLAttributes = JSXInternal.OptionHTMLAttributes; export import OutputHTMLAttributes = JSXInternal.OutputHTMLAttributes; export import ParamHTMLAttributes = JSXInternal.ParamHTMLAttributes; export import ProgressHTMLAttributes = JSXInternal.ProgressHTMLAttributes; export import SlotHTMLAttributes = JSXInternal.SlotHTMLAttributes; export import ScriptHTMLAttributes = JSXInternal.ScriptHTMLAttributes; export import SelectHTMLAttributes = JSXInternal.SelectHTMLAttributes; export import SourceHTMLAttributes = JSXInternal.SourceHTMLAttributes; export import StyleHTMLAttributes = JSXInternal.StyleHTMLAttributes; export import TableHTMLAttributes = JSXInternal.TableHTMLAttributes; export import TextareaHTMLAttributes = JSXInternal.TextareaHTMLAttributes; export import TdHTMLAttributes = JSXInternal.TdHTMLAttributes; export import ThHTMLAttributes = JSXInternal.ThHTMLAttributes; export import TimeHTMLAttributes = JSXInternal.TimeHTMLAttributes; export import TrackHTMLAttributes = JSXInternal.TrackHTMLAttributes; export import VideoHTMLAttributes = JSXInternal.VideoHTMLAttributes; // Events export import TargetedEvent = JSXInternal.TargetedEvent; export import ChangeEvent = JSXInternal.TargetedEvent; export import ClipboardEvent = JSXInternal.TargetedClipboardEvent; export import CompositionEvent = JSXInternal.TargetedCompositionEvent; export import DragEvent = JSXInternal.TargetedDragEvent; export import PointerEvent = JSXInternal.TargetedPointerEvent; export import FocusEvent = JSXInternal.TargetedFocusEvent; export import FormEvent = JSXInternal.TargetedEvent; export import InvalidEvent = JSXInternal.TargetedEvent; export import KeyboardEvent = JSXInternal.TargetedKeyboardEvent; export import MouseEvent = JSXInternal.TargetedMouseEvent; export import TouchEvent = JSXInternal.TargetedTouchEvent; export import UIEvent = JSXInternal.TargetedUIEvent; export import AnimationEvent = JSXInternal.TargetedAnimationEvent; export import TransitionEvent = JSXInternal.TargetedTransitionEvent; // Event Handler Types export import EventHandler = JSXInternal.EventHandler; export import ChangeEventHandler = JSXInternal.GenericEventHandler; export import ClipboardEventHandler = JSXInternal.ClipboardEventHandler; export import CompositionEventHandler = JSXInternal.CompositionEventHandler; export import DragEventHandler = JSXInternal.DragEventHandler; export import PointerEventHandler = JSXInternal.PointerEventHandler; export import FocusEventHandler = JSXInternal.FocusEventHandler; export import FormEventHandler = JSXInternal.GenericEventHandler; export import InvalidEventHandler = JSXInternal.GenericEventHandler; export import KeyboardEventHandler = JSXInternal.KeyboardEventHandler; export import MouseEventHandler = JSXInternal.MouseEventHandler; export import TouchEventHandler = JSXInternal.TouchEventHandler; export import UIEventHandler = JSXInternal.UIEventHandler; export import AnimationEventHandler = JSXInternal.AnimationEventHandler; export import TransitionEventHandler = JSXInternal.TransitionEventHandler; export function createPortal( vnode: preact.ComponentChildren, container: preact.ContainerNode ): preact.VNode; export function render( vnode: preact.ComponentChild, parent: preact.ContainerNode, callback?: () => void ): Component | null; export function hydrate( vnode: preact.ComponentChild, parent: preact.ContainerNode, callback?: () => void ): Component | null; export function unmountComponentAtNode( container: preact.ContainerNode ): boolean; export function createFactory( type: preact.VNode['type'] ): ( props?: any, ...children: preact.ComponentChildren[] ) => preact.VNode; export function isValidElement(element: any): boolean; export function isFragment(element: any): boolean; export function isMemo(element: any): boolean; export function findDOMNode( component: preact.Component | Element ): Element | null; export abstract class PureComponent< P = {}, S = {}, SS = any > extends preact.Component { isPureReactComponent: boolean; } export type MemoExoticComponent> = preact.FunctionComponent> & { readonly type: C; }; export function memo

( component: preact.FunctionalComponent

, comparer?: (prev: P, next: P) => boolean ): preact.FunctionComponent

; export function memo>( component: C, comparer?: ( prev: preact.ComponentProps, next: preact.ComponentProps ) => boolean ): C; export interface RefAttributes extends preact.Attributes { ref?: preact.Ref | undefined; } /** * @deprecated Please use `ForwardRefRenderFunction` instead. */ export interface ForwardFn

{ (props: P, ref: ForwardedRef): preact.ComponentChild; displayName?: string; } export interface ForwardRefRenderFunction { (props: P, ref: ForwardedRef): preact.ComponentChild; displayName?: string; } export interface ForwardRefExoticComponent< P > extends preact.FunctionComponent

{ defaultProps?: Partial

| undefined; } export function forwardRef( fn: ForwardRefRenderFunction ): preact.FunctionalComponent & { ref?: preact.Ref }>; export type PropsWithoutRef

= Omit; interface MutableRefObject { current: T; } export type ForwardedRef = | ((instance: T | null) => void) | MutableRefObject | null; export type ElementType< P = any, Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements > = | { [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never }[Tag] | ComponentType

; export type ComponentPropsWithoutRef = PropsWithoutRef< ComponentProps >; export type ComponentPropsWithRef = C extends new ( props: infer P ) => Component ? PropsWithoutRef

& RefAttributes> : ComponentProps; export type ElementRef< C extends | ForwardRefExoticComponent | { new (props: any): Component } | ((props: any) => ReactNode) | keyof JSXInternal.IntrinsicElements > = 'ref' extends keyof ComponentPropsWithRef ? NonNullable['ref']> extends RefAttributes< infer Instance >['ref'] ? Instance : never : never; export function flushSync(fn: () => R): R; export function flushSync(fn: (a: A) => R, a: A): R; export function unstable_batchedUpdates( callback: (arg?: any) => void, arg?: any ): void; export type PropsWithChildren

= P & { children?: preact.ComponentChildren | undefined; }; export const Children: { map( children: T | T[], fn: (child: T, i: number) => R ): R[]; forEach( children: T | T[], fn: (child: T, i: number) => void ): void; count: (children: preact.ComponentChildren) => number; only: (children: preact.ComponentChildren) => preact.ComponentChild; toArray: (children: preact.ComponentChildren) => preact.VNode<{}>[]; }; // scheduler export const unstable_ImmediatePriority: number; export const unstable_UserBlockingPriority: number; export const unstable_NormalPriority: number; export const unstable_LowPriority: number; export const unstable_IdlePriority: number; export function unstable_runWithPriority( priority: number, callback: () => void ): void; export const unstable_now: () => number; } preactjs-preact-389c7bc/compat/src/index.js000066400000000000000000000123321523334003000207360ustar00rootroot00000000000000import { createElement, render as preactRender, cloneElement as preactCloneElement, createRef, Component, createContext, Fragment, options } from 'preact'; import { useState, useId, useReducer, useEffect, useLayoutEffect, useRef, useImperativeHandle, useMemo, useCallback, useContext, useDebugValue } from 'preact/hooks'; import { useInsertionEffect, startTransition, useDeferredValue, useSyncExternalStore, useTransition } from './hooks'; import { PureComponent } from './PureComponent'; import { memo } from './memo'; import { forwardRef } from './forwardRef'; import { Children } from './Children'; import { Suspense, lazy } from './suspense'; import { SuspenseList } from './suspense-list'; import { createPortal } from './portals'; import { hydrate, render, REACT_ELEMENT_TYPE, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } from './render'; const version = '18.3.1'; // trick libraries to think we are react /** * Legacy version of createElement. * @param {import('./internal').VNode["type"]} type The node name or Component constructor */ function createFactory(type) { return createElement.bind(null, type); } /** * Check if the passed element is a valid (p)react node. * @param {*} element The element to check * @returns {boolean} */ function isValidElement(element) { return !!element && element.$$typeof === REACT_ELEMENT_TYPE; } /** * Check if the passed element is a Fragment node. * @param {*} element The element to check * @returns {boolean} */ function isFragment(element) { return isValidElement(element) && element.type === Fragment; } /** * Check if the passed element is a Memo node. * @param {*} element The element to check * @returns {boolean} */ function isMemo(element) { return ( !!element && typeof element.displayName == 'string' && element.displayName.indexOf('Memo(') == 0 ); } /** * Wrap `cloneElement` to abort if the passed element is not a valid element and apply * all vnode normalizations. * @param {import('./internal').VNode} element The vnode to clone * @param {object} props Props to add when cloning * @param {Array} rest Optional component children */ function cloneElement(element) { if (!isValidElement(element)) return element; return preactCloneElement.apply(null, arguments); } /** * Remove a component tree from the DOM, including state and event handlers. * @param {import('./internal').PreactElement} container * @returns {boolean} */ function unmountComponentAtNode(container) { if (container._children) { preactRender(null, container); return true; } return false; } /** * Get the matching DOM node for a component * @param {import('./internal').Component} component * @returns {import('./internal').PreactElement | null} */ function findDOMNode(component) { return ( (component && (component.base || (component.nodeType === 1 && component))) || null ); } /** * Deprecated way to control batched rendering inside the reconciler, but we * already schedule in batches inside our rendering code * @template Arg * @param {(arg: Arg) => void} callback function that triggers the updated * @param {Arg} [arg] Optional argument that can be passed to the callback */ // eslint-disable-next-line camelcase const unstable_batchedUpdates = (callback, arg) => callback(arg); /** * In React, `flushSync` flushes the entire tree and forces a rerender. * @template Arg * @template Result * @param {(arg: Arg) => Result} callback function that runs before the flush * @param {Arg} [arg] Optional argument that can be passed to the callback * @returns */ const flushSync = (callback, arg) => { const prevDebounce = options.debounceRendering; let flush; options.debounceRendering = cb => { flush = cb; }; try { const res = callback(arg); if (flush) flush(); return res; } finally { options.debounceRendering = prevDebounce; } }; // compat to react-is export const isElement = isValidElement; export * from 'preact/hooks'; export { version, Children, render, hydrate, unmountComponentAtNode, createPortal, createElement, createContext, createFactory, cloneElement, createRef, Fragment, isValidElement, isFragment, isMemo, findDOMNode, Component, PureComponent, memo, forwardRef, flushSync, useInsertionEffect, startTransition, useDeferredValue, useSyncExternalStore, useTransition, // eslint-disable-next-line camelcase unstable_batchedUpdates, Fragment as StrictMode, Suspense, SuspenseList, lazy, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED }; // React copies the named exports to the default one. export default { useState, useId, useReducer, useEffect, useLayoutEffect, useInsertionEffect, useTransition, useDeferredValue, useSyncExternalStore, startTransition, useRef, useImperativeHandle, useMemo, useCallback, useContext, useDebugValue, version, Children, render, hydrate, unmountComponentAtNode, createPortal, createElement, createContext, createFactory, cloneElement, createRef, Fragment, isValidElement, isElement, isFragment, isMemo, findDOMNode, Component, PureComponent, memo, forwardRef, flushSync, unstable_batchedUpdates, StrictMode: Fragment, Suspense, SuspenseList, lazy, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED }; preactjs-preact-389c7bc/compat/src/internal.d.ts000066400000000000000000000024361523334003000217030ustar00rootroot00000000000000import { Component as PreactComponent, VNode as PreactVNode, FunctionComponent as PreactFunctionComponent, PreactElement } from '../../src/internal'; import { SuspenseProps } from './suspense'; export { ComponentChildren } from '../..'; export { PreactElement }; export interface Component

extends PreactComponent { isReactComponent?: object; isPureReactComponent?: true; _patchedLifecycles?: true; // Suspense internal properties _childDidSuspend?(error: Promise, suspendingVNode: VNode): void; _suspended: (vnode: VNode) => (unsuspend: () => void) => void; _onResolve?(): void; // Portal internal properties _temp: any; _container: PreactElement; } export interface FunctionComponent

extends PreactFunctionComponent

{ shouldComponentUpdate?(nextProps: Readonly

): boolean; _forwarded?: boolean; _patchedLifecycles?: true; } export interface VNode extends PreactVNode { $$typeof?: symbol | string; preactCompatNormalized?: boolean; } export interface SuspenseState { _suspended?: null | VNode; } export interface SuspenseComponent extends PreactComponent< SuspenseProps, SuspenseState > { _pendingSuspensionCount: number; _suspenders: Component[]; _detachOnNextRender: null | VNode; _mask?: [number, number]; } preactjs-preact-389c7bc/compat/src/memo.js000066400000000000000000000020231523334003000205600ustar00rootroot00000000000000import { createElement } from 'preact'; import { shallowDiffers } from './util'; /** * Memoize a component, so that it only updates when the props actually have * changed. This was previously known as `React.pure`. * @param {import('./internal').FunctionComponent} c functional component * @param {(prev: object, next: object) => boolean} [comparer] Custom equality function * @returns {import('./internal').FunctionComponent} */ export function memo(c, comparer) { function shouldUpdate(nextProps) { let ref = this.props.ref; if (ref != nextProps.ref && ref) { typeof ref == 'function' ? ref(null) : (ref.current = null); } return comparer ? !comparer(this.props, nextProps) || ref != nextProps.ref : shallowDiffers(this.props, nextProps); } function Memoed(props) { this.shouldComponentUpdate = shouldUpdate; return createElement(c, props); } Memoed.displayName = 'Memo(' + (c.displayName || c.name) + ')'; Memoed._forwarded = Memoed.prototype.isReactComponent = true; Memoed.type = c; return Memoed; } preactjs-preact-389c7bc/compat/src/portals.js000066400000000000000000000041411523334003000213120ustar00rootroot00000000000000import { createElement, render } from 'preact'; /** * @param {import('../../src/index').RenderableProps<{ context: any }>} props */ function ContextProvider(props) { this.getChildContext = () => props.context; return props.children; } /** * Portal component * @this {import('./internal').Component} * @param {object | null | undefined} props * * TODO: use createRoot() instead of fake root */ function Portal(props) { const _this = this; let container = props._container; _this.componentWillUnmount = function () { render(null, _this._temp); _this._temp = null; _this._container = null; }; // When we change container we should clear our old container and // indicate a new mount. if (_this._container && _this._container !== container) { _this.componentWillUnmount(); } if (!_this._temp) { // Ensure the element has a mask for useId invocations let root = _this._vnode; while (root !== null && !root._mask && root._parent !== null) { root = root._parent; } _this._container = container; // Create a fake DOM parent node that manages a subset of `container`'s children: _this._temp = { nodeType: 1, parentNode: container, childNodes: [], _children: { _mask: root._mask }, contains: () => true, namespaceURI: container.namespaceURI, insertBefore(child, before) { this.childNodes.push(child); _this._container.insertBefore(child, before); }, removeChild(child) { this.childNodes.splice(this.childNodes.indexOf(child) >>> 1, 1); _this._container.removeChild(child); } }; } // Render our wrapping element into temp. render( createElement(ContextProvider, { context: _this.context }, props._vnode), _this._temp ); } /** * Create a `Portal` to continue rendering the vnode tree at a different DOM node * @param {import('./internal').VNode} vnode The vnode to render * @param {import('./internal').PreactElement} container The DOM node to continue rendering in to. */ export function createPortal(vnode, container) { const el = createElement(Portal, { _vnode: vnode, _container: container }); el.containerInfo = container; return el; } preactjs-preact-389c7bc/compat/src/render.js000066400000000000000000000210531523334003000211060ustar00rootroot00000000000000import { render as preactRender, hydrate as preactHydrate, options, toChildArray, Component } from 'preact'; import { useCallback, useContext, useDebugValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from 'preact/hooks'; import { useDeferredValue, useInsertionEffect, useSyncExternalStore, useTransition } from './index'; export const REACT_ELEMENT_TYPE = (typeof Symbol != 'undefined' && Symbol.for && Symbol.for('react.element')) || 0xeac7; const CAMEL_PROPS = /^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/; const ON_ANI = /^on(Ani|Tra|Tou|BeforeInp|Compo)/; const CAMEL_REPLACE = /[A-Z0-9]/g; const IS_DOM = typeof document !== 'undefined'; // Input types for which onchange should not be converted to oninput. // type="file|checkbox|radio", plus "range" in IE11. // (IE11 doesn't support Symbol, which we use here to turn `rad` into `ra` which matches "range") const onChangeInputType = type => (typeof Symbol != 'undefined' && typeof Symbol() == 'symbol' ? /fil|che|rad/ : /fil|che|ra/ ).test(type); // Some libraries like `react-virtualized` explicitly check for this. Component.prototype.isReactComponent = true; // `UNSAFE_*` lifecycle hooks // Preact only ever invokes the unprefixed methods. // Here we provide a base "fallback" implementation that calls any defined UNSAFE_ prefixed method. // - If a component defines its own `componentDidMount()` (including via defineProperty), use that. // - If a component defines `UNSAFE_componentDidMount()`, `componentDidMount` is the alias getter/setter. // - If anything assigns to an `UNSAFE_*` property, the assignment is forwarded to the unprefixed property. // See https://github.com/preactjs/preact/issues/1941 [ 'componentWillMount', 'componentWillReceiveProps', 'componentWillUpdate' ].forEach(key => { Object.defineProperty(Component.prototype, key, { configurable: true, get() { return this['UNSAFE_' + key]; }, set(v) { Object.defineProperty(this, key, { configurable: true, writable: true, value: v }); } }); }); /** * Proxy render() since React returns a Component reference. * @param {import('./internal').VNode} vnode VNode tree to render * @param {import('./internal').PreactElement} parent DOM node to render vnode tree into * @param {() => void} [callback] Optional callback that will be called after rendering * @returns {import('./internal').Component | null} The root component reference or null */ export function render(vnode, parent, callback) { // React destroys any existing DOM nodes, see #1727 // ...but only on the first render, see #1828 if (parent._children == null) { parent.textContent = ''; } preactRender(vnode, parent); if (typeof callback == 'function') callback(); return vnode ? vnode._component : null; } export function hydrate(vnode, parent, callback) { preactHydrate(vnode, parent); if (typeof callback == 'function') callback(); return vnode ? vnode._component : null; } let oldEventHook = options.event; options.event = e => { if (oldEventHook) e = oldEventHook(e); e.persist = () => {}; e.isPropagationStopped = function isPropagationStopped() { return this.cancelBubble; }; e.isDefaultPrevented = function isDefaultPrevented() { return this.defaultPrevented; }; return (e.nativeEvent = e); }; const classNameDescriptorNonEnumberable = { configurable: true, get() { return this.class; } }; function handleDomVNode(vnode) { let props = vnode.props, type = vnode.type, normalizedProps = {}, isNonDashedType = type.indexOf('-') == -1; for (let i in props) { let value = props[i]; if ( (i === 'value' && 'defaultValue' in props && value == null) || // Emulate React's behavior of not rendering the contents of noscript tags on the client. (IS_DOM && i === 'children' && type === 'noscript') || i === 'class' || i === 'className' ) { // Skip applying value if it is null/undefined and we already set // a default value continue; } let lowerCased = i.toLowerCase(); if (i === 'defaultValue' && 'value' in props && props.value == null) { // `defaultValue` is treated as a fallback `value` when a value prop is present but null/undefined. // `defaultValue` for Elements with no value prop is the same as the DOM defaultValue property. i = 'value'; } else if (i === 'download' && value === true) { // Calling `setAttribute` with a truthy value will lead to it being // passed as a stringified value, e.g. `download="true"`. React // converts it to an empty string instead, otherwise the attribute // value will be used as the file name and the file will be called // "true" upon downloading it. value = ''; } else if (lowerCased === 'translate' && value === 'no') { value = false; } else if (lowerCased[0] === 'o' && lowerCased[1] === 'n') { if (lowerCased === 'ondoubleclick') { i = 'ondblclick'; } else if ( lowerCased === 'onchange' && (type === 'input' || type === 'textarea') && !onChangeInputType(props.type) ) { lowerCased = i = 'oninput'; } else if (lowerCased === 'onfocus') { i = 'onfocusin'; } else if (lowerCased === 'onblur') { i = 'onfocusout'; } else if (ON_ANI.test(i)) { i = lowerCased; } } else if (isNonDashedType && CAMEL_PROPS.test(i)) { i = i.replace(CAMEL_REPLACE, '-$&').toLowerCase(); } else if (value === null) { value = undefined; } // Add support for onInput and onChange, see #3561 // if we have an oninput prop already change it to oninputCapture if (lowerCased === 'oninput') { i = lowerCased; if (normalizedProps[i]) { i = 'oninputCapture'; } } normalizedProps[i] = value; } if (type == 'select') { // Add support for array select values: '); hydrate(, scratch); expect(scratch.firstElementChild.value).to.equal('foo'); // IE11 always displays the value as node.innerHTML if (!/Trident/.test(window.navigator.userAgent)) { expect(scratch.innerHTML).to.be.equal(''); } }); it('should alias defaultValue to children', () => { // TODO: IE11 doesn't update `node.value` when // `node.defaultValue` is set. if (/Trident/.test(navigator.userAgent)) return; render('); act(() => { set('hello'); }); // Note: This looks counterintuitive, but it's working correctly - the value // missing from HTML because innerHTML doesn't serialize form field values. // See demo: https://jsfiddle.net/4had2Lu8 // Related renderToString PR: preactjs/preact-render-to-string#161 // // This is not true for IE11. It displays the value in // node.innerHTML regardless. if (!/Trident/.test(window.navigator.userAgent)) { expect(scratch.innerHTML).to.equal(''); } expect(scratch.firstElementChild.value).to.equal('hello'); act(() => { set(''); }); // Same as earlier: IE11 always displays the value as node.innerHTML if (!/Trident/.test(window.navigator.userAgent)) { expect(scratch.innerHTML).to.equal(''); } expect(scratch.firstElementChild.value).to.equal(''); }); }); preactjs-preact-389c7bc/compat/test/browser/unmountComponentAtNode.test.jsx000066400000000000000000000013231523334003000273310ustar00rootroot00000000000000import React, { createElement, unmountComponentAtNode } from 'preact/compat'; import { setupScratch, teardown } from '../../../test/_util/helpers'; describe('unmountComponentAtNode', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should unmount a root node', () => { const App = () =>

foo
; React.render(, scratch); expect(unmountComponentAtNode(scratch)).to.equal(true); expect(scratch.innerHTML).to.equal(''); }); it('should do nothing if root is not mounted', () => { expect(unmountComponentAtNode(scratch)).to.equal(false); expect(scratch.innerHTML).to.equal(''); }); }); preactjs-preact-389c7bc/compat/test/browser/unstable_batchedUpdates.test.jsx000066400000000000000000000033531523334003000274700ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { useState } from 'preact/hooks'; import { unstable_batchedUpdates, flushSync } from 'preact/compat'; import { setupScratch, teardown } from '../../../test/_util/helpers'; describe('unstable_batchedUpdates', () => { it('should call the callback', () => { const spy = sinon.spy(); unstable_batchedUpdates(spy); expect(spy).to.be.calledOnce; }); it('should call callback with only one arg', () => { const spy = sinon.spy(); // @ts-expect-error unstable_batchedUpdates(spy, 'foo', 'bar'); expect(spy).to.be.calledWithExactly('foo'); }); }); describe('flushSync', () => { let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should invoke the given callback', () => { const returnValue = {}; const spy = sinon.spy(() => returnValue); const result = flushSync(spy); expect(spy).to.have.been.calledOnce; expect(result).to.equal(returnValue); }); it('should invoke the given callback with the given argument', () => { const returnValue = {}; const spy = sinon.spy(() => returnValue); const result = flushSync(spy, 'foo'); expect(spy).to.be.calledWithExactly('foo'); expect(result).to.equal(returnValue); }); it('should batch updates and flush them synchronously', () => { let setA; let setB; const renders = sinon.spy(); function App() { const [a, updateA] = useState(0); const [b, updateB] = useState(0); setA = updateA; setB = updateB; renders(); return createElement('p', null, a + b); } render(createElement(App), scratch); flushSync(() => { setA(1); setB(1); }); expect(scratch.textContent).to.equal('2'); expect(renders).to.have.been.calledTwice; }); }); preactjs-preact-389c7bc/compat/test/browser/useSyncExternalStore.test.jsx000066400000000000000000000512331523334003000270240ustar00rootroot00000000000000import React, { createElement, Fragment, useSyncExternalStore, render, useState, useCallback, useEffect, useLayoutEffect } from 'preact/compat'; import { setupRerender, act } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; const ReactDOM = React; describe('useSyncExternalStore', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; /** @type {{ logs: string[], log(arg: string): void; }} */ const Scheduler = { logs: [], log(arg) { this.logs.push(arg); } }; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); Scheduler.logs = []; }); function defer(cb) { return Promise.resolve().then(cb); } function assertLog(expected) { expect(Scheduler.logs).to.deep.equal(expected); Scheduler.logs = []; } function Text({ text }) { Scheduler.log(text); return text; } /** @type {(container: Element) => { render(children: React.JSX.Element): void}} */ function createRoot(container) { return { render(children) { render(children, container); } }; } function createExternalStore(initialState) { const listeners = new Set(); let currentState = initialState; return { listeners, set(text) { currentState = text; listeners.forEach(listener => listener()); }, subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }, getState() { return currentState; }, getSubscriberCount() { return listeners.size; } }; } it('subscribes and follows effects', () => { const subscribe = sinon.spy(() => () => {}); const getSnapshot = sinon.spy(() => 'hello world'); const App = () => { const value = useSyncExternalStore(subscribe, getSnapshot); return

{value}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

hello world

'); expect(subscribe).to.be.calledOnce; expect(getSnapshot).to.be.calledThrice; }); it('subscribes and rerenders when called', () => { /** @type {() => void} */ let flush; const subscribe = sinon.spy(cb => { flush = cb; return () => {}; }); let called = false; const getSnapshot = sinon.spy(() => { if (called) { return 'hello new world'; } return 'hello world'; }); const App = () => { const value = useSyncExternalStore(subscribe, getSnapshot); return

{value}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

hello world

'); expect(subscribe).to.be.calledOnce; expect(getSnapshot).to.be.calledThrice; called = true; flush(); rerender(); expect(scratch.innerHTML).to.equal('

hello new world

'); }); it('getSnapshot can return NaN without causing infinite loop', () => { /** @type {() => void} */ let flush; const subscribe = sinon.spy(cb => { flush = cb; return () => {}; }); let called = false; const getSnapshot = sinon.spy(() => { if (called) { return NaN; } return 1; }); const App = () => { const value = useSyncExternalStore(subscribe, getSnapshot); return

{value}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

1

'); expect(subscribe).to.be.calledOnce; expect(getSnapshot).to.be.calledThrice; called = true; flush(); rerender(); expect(scratch.innerHTML).to.equal('

NaN

'); }); it('should not call function values on subscription', () => { /** @type {() => void} */ let flush; const subscribe = sinon.spy(cb => { flush = cb; return () => {}; }); const func = () => 'value: ' + i++; let i = 0; const getSnapshot = sinon.spy(() => { return func; }); const App = () => { const value = useSyncExternalStore(subscribe, getSnapshot); return

{value()}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

value: 0

'); expect(subscribe).to.be.calledOnce; expect(getSnapshot).to.be.calledThrice; flush(); rerender(); expect(scratch.innerHTML).to.equal('

value: 0

'); }); it('should work with changing getSnapshot', () => { /** @type {() => void} */ let flush; const subscribe = sinon.spy(cb => { flush = cb; return () => {}; }); let i = 0; const App = () => { const value = useSyncExternalStore(subscribe, () => { return i; }); return

value: {value}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

value: 0

'); expect(subscribe).to.be.calledOnce; i++; flush(); rerender(); expect(scratch.innerHTML).to.equal('

value: 1

'); }); it('works with useCallback', () => { /** @type {() => void} */ let toggle; const App = () => { const [state, setState] = useState(true); toggle = setState.bind(this, () => false); const value = useSyncExternalStore( useCallback(() => { return () => {}; }, [state]), () => (state ? 'yep' : 'nope') ); return

{value}

; }; act(() => { render(, scratch); }); expect(scratch.innerHTML).to.equal('

yep

'); toggle(); rerender(); expect(scratch.innerHTML).to.equal('

nope

'); }); it('handles store updates before subscribing', async () => { // This test is testing scheduling mechanics, so teardown the manual // rerender test setup to rely on Preact's built-in scheduling and verify // this behavior works. We still need a DOM container to render into so set // that back up. teardown(scratch); scratch = setupScratch(); const store = createExternalStore(0); function App() { const value = useSyncExternalStore(store.subscribe, store.getState); useEffect(() => { Scheduler.log('Passive effect: ' + value); }, [value]); return ; } const container = document.createElement('div'); const root = createRoot(container); // Schedule a mutation in the next microtask after the initial render but // before subscribing to the store const mutation = defer(() => { // Assert we are running this mutation before subscribing to the store expect(store.listeners.size).to.equal(0); store.set(1); }); root.render(); expect(container.textContent).to.equal('0'); assertLog([0]); // Wait for the mutation to occur. Then wait for the passive effects that // subscribe to the store and log the new value. await mutation; await new Promise(r => setTimeout(r, 32)); expect(container.textContent).to.equal('1'); expect(store.listeners.size).to.equal(1); assertLog(['Passive effect: 0', 1, 'Passive effect: 1']); }); // The following tests are taken from the React test suite: // https://github.com/facebook/react/blob/3e09c27b880e1fecdb1eca5db510ecce37ea6be2/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js describe('React useSyncExternalStore test suite', () => { it('basic usage', async () => { const store = createExternalStore('Initial'); function App() { const text = useSyncExternalStore(store.subscribe, store.getState); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['Initial']); expect(container.textContent).to.equal('Initial'); await act(() => { store.set('Updated'); }); assertLog(['Updated']); expect(container.textContent).to.equal('Updated'); }); it('skips re-rendering if nothing changes', async () => { const store = createExternalStore('Initial'); function App() { const text = useSyncExternalStore(store.subscribe, store.getState); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['Initial']); expect(container.textContent).to.equal('Initial'); // Update to the same value await act(() => { store.set('Initial'); }); // Should not re-render assertLog([]); expect(container.textContent).to.equal('Initial'); }); it('switch to a different store', async () => { const storeA = createExternalStore(0); const storeB = createExternalStore(0); let setStore; function App() { const [store, _setStore] = useState(storeA); setStore = _setStore; const value = useSyncExternalStore(store.subscribe, store.getState); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog([0]); expect(container.textContent).to.equal('0'); await act(() => { storeA.set(1); }); assertLog([1]); expect(container.textContent).to.equal('1'); // Switch stores and update in the same batch await act(() => { // This update will be disregarded storeA.set(2); setStore(storeB); }); // Now reading from B instead of A assertLog([0]); expect(container.textContent).to.equal('0'); // Update A await act(() => { storeA.set(3); }); // Nothing happened, because we're no longer subscribed to A assertLog([]); expect(container.textContent).to.equal('0'); // Update B await act(() => { storeB.set(1); }); assertLog([1]); expect(container.textContent).to.equal('1'); }); it('selecting a specific value inside getSnapshot', async () => { const store = createExternalStore({ a: 0, b: 0 }); function A() { const a = useSyncExternalStore( store.subscribe, () => store.getState().a ); return ; } function B() { const b = useSyncExternalStore( store.subscribe, () => store.getState().b ); return ; } function App() { return ( ); } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['A0', 'B0']); expect(container.textContent).to.equal('A0B0'); // Update b but not a await act(() => { store.set({ a: 0, b: 1 }); }); // Only b re-renders assertLog(['B1']); expect(container.textContent).to.equal('A0B1'); // Update a but not b await act(() => { store.set({ a: 1, b: 1 }); }); // Only a re-renders assertLog(['A1']); expect(container.textContent).to.equal('A1B1'); }); // In React 18, you can't observe in between a sync render and its // passive effects, so this is only relevant to legacy roots // @gate enableUseSyncExternalStoreShim it("compares to current state before bailing out, even when there's a mutation in between the sync and passive effects", async () => { const store = createExternalStore(0); function App() { const value = useSyncExternalStore(store.subscribe, store.getState); useEffect(() => { Scheduler.log('Passive effect: ' + value); }, [value]); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog([0, 'Passive effect: 0']); // Schedule an update. We'll intentionally not use `act` so that we can // insert a mutation before React subscribes to the store in a // passive effect. store.set(1); rerender(); assertLog([ 1 // Passive effect hasn't fired yet ]); expect(container.textContent).to.equal('1'); // Flip the store state back to the previous value. store.set(0); rerender(); assertLog([ 'Passive effect: 1', // Re-render. If the current state were tracked by updating a ref in a // passive effect, then this would break because the previous render's // passive effect hasn't fired yet, so we'd incorrectly think that // the state hasn't changed. 0 ]); // Should flip back to 0 expect(container.textContent).to.equal('0'); // Preact: Wait for 'Passive effect: 0' to flush from the rAF so it doesn't impact other tests await new Promise(r => setTimeout(r, 32)); }); it('mutating the store in between render and commit when getSnapshot has changed', async () => { const store = createExternalStore({ a: 1, b: 1 }); const getSnapshotA = () => store.getState().a; const getSnapshotB = () => store.getState().b; function Child1({ step }) { const value = useSyncExternalStore(store.subscribe, store.getState); useLayoutEffect(() => { if (step === 1) { // Update B in a layout effect. This happens in the same commit // that changed the getSnapshot in Child2. Child2's effects haven't // fired yet, so it doesn't have access to the latest getSnapshot. So // it can't use the getSnapshot to bail out. Scheduler.log('Update B in commit phase'); store.set({ a: value.a, b: 2 }); } }, [step]); return null; } function Child2({ step }) { const label = step === 0 ? 'A' : 'B'; const getSnapshot = step === 0 ? getSnapshotA : getSnapshotB; const value = useSyncExternalStore(store.subscribe, getSnapshot); return ; } let setStep; function App() { const [step, _setStep] = useState(0); setStep = _setStep; return ( <> ); } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['A1']); expect(container.textContent).to.equal('A1'); await act(() => { // Change getSnapshot and update the store in the same batch setStep(1); }); assertLog([ 'B1', 'Update B in commit phase', // If Child2 had used the old getSnapshot to bail out, then it would have // incorrectly bailed out here instead of re-rendering. 'B2' ]); expect(container.textContent).to.equal('B2'); }); it('mutating the store in between render and commit when getSnapshot has _not_ changed', async () => { // Same as previous test, but `getSnapshot` does not change const store = createExternalStore({ a: 1, b: 1 }); const getSnapshotA = () => store.getState().a; function Child1({ step }) { const value = useSyncExternalStore(store.subscribe, store.getState); useLayoutEffect(() => { if (step === 1) { // Update B in a layout effect. This happens in the same commit // that changed the getSnapshot in Child2. Child2's effects haven't // fired yet, so it doesn't have access to the latest getSnapshot. So // it can't use the getSnapshot to bail out. Scheduler.log('Update B in commit phase'); store.set({ a: value.a, b: 2 }); } }, [step]); return null; } function Child2({ step }) { const value = useSyncExternalStore(store.subscribe, getSnapshotA); return ; } let setStep; function App() { const [step, _setStep] = useState(0); setStep = _setStep; return ( <> ); } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['A1']); expect(container.textContent).to.equal('A1'); // This will cause a layout effect, and in the layout effect we'll update // the store await act(() => { setStep(1); }); assertLog([ 'A1', // This updates B, but since Child2 doesn't subscribe to B, it doesn't // need to re-render. 'Update B in commit phase' // No re-render ]); expect(container.textContent).to.equal('A1'); }); it("does not bail out if the previous update hasn't finished yet", async () => { const store = createExternalStore(0); function Child1() { const value = useSyncExternalStore(store.subscribe, store.getState); useLayoutEffect(() => { if (value === 1) { Scheduler.log('Reset back to 0'); store.set(0); } }, [value]); return ; } function Child2() { const value = useSyncExternalStore(store.subscribe, store.getState); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render( <> ) ); assertLog([0, 0]); expect(container.textContent).to.equal('00'); await act(() => { store.set(1); }); // Preact logs differ from React here cuz of how we do rerendering. We // rerender subtrees and then commit effects so Child2 never sees the // update to 1 cuz Child1 rerenders and runs its layout effects first. assertLog([1, /*1,*/ 'Reset back to 0', 0, 0]); expect(container.textContent).to.equal('00'); }); it('uses the latest getSnapshot, even if it changed in the same batch as a store update', async () => { const store = createExternalStore({ a: 0, b: 0 }); const getSnapshotA = () => store.getState().a; const getSnapshotB = () => store.getState().b; let setGetSnapshot; function App() { const [getSnapshot, _setGetSnapshot] = useState(() => getSnapshotA); setGetSnapshot = _setGetSnapshot; const text = useSyncExternalStore(store.subscribe, getSnapshot); return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog([0]); // Update the store and getSnapshot at the same time await act(() => { setGetSnapshot(() => getSnapshotB); store.set({ a: 1, b: 2 }); }); // It should read from B instead of A assertLog([2]); expect(container.textContent).to.equal('2'); }); it('handles errors thrown by getSnapshot', async () => { class ErrorBoundary extends React.Component { state = { error: null }; static getDerivedStateFromError(error) { return { error }; } render() { if (this.state.error) { return ; } return this.props.children; } } const store = createExternalStore({ value: 0, throwInGetSnapshot: false, throwInIsEqual: false }); function App() { const { value } = useSyncExternalStore(store.subscribe, () => { const state = store.getState(); if (state.throwInGetSnapshot) { throw new Error('Error in getSnapshot'); } return state; }); return ; } const errorBoundary = React.createRef(); const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render( ) ); assertLog([0]); expect(container.textContent).to.equal('0'); // Update that throws in a getSnapshot. We can catch it with an error boundary. await act(() => { store.set({ value: 1, throwInGetSnapshot: true, throwInIsEqual: false }); }); assertLog(['Error in getSnapshot']); expect(container.textContent).to.equal('Error in getSnapshot'); }); it('getSnapshot can return NaN without infinite loop warning', async () => { const store = createExternalStore('not a number'); function App() { const value = useSyncExternalStore(store.subscribe, () => parseInt(store.getState(), 10) ); return ; } const container = document.createElement('div'); const root = createRoot(container); // Initial render that reads a snapshot of NaN. This is OK because we use // Object.is algorithm to compare values. await act(() => root.render()); expect(container.textContent).to.equal('NaN'); // Update to real number await act(() => store.set(123)); expect(container.textContent).to.equal('123'); // Update back to NaN await act(() => store.set('not a number')); expect(container.textContent).to.equal('NaN'); }); it('regression test for facebook/react#23150', async () => { const store = createExternalStore('Initial'); function App() { const text = useSyncExternalStore(store.subscribe, store.getState); const [derivedText, setDerivedText] = useState(text); useEffect(() => {}, []); if (derivedText !== text.toUpperCase()) { setDerivedText(text.toUpperCase()); } return ; } const container = document.createElement('div'); const root = createRoot(container); await act(() => root.render()); assertLog(['INITIAL']); expect(container.textContent).to.equal('INITIAL'); await act(() => { store.set('Updated'); }); assertLog(['UPDATED']); expect(container.textContent).to.equal('UPDATED'); }); }); }); preactjs-preact-389c7bc/compat/test/ts/000077500000000000000000000000001523334003000201065ustar00rootroot00000000000000preactjs-preact-389c7bc/compat/test/ts/forward-ref.tsx000066400000000000000000000012051523334003000230620ustar00rootroot00000000000000import React from '../../src'; const MyInput: React.ForwardFn<{ id: string }, { focus(): void }> = ( props, ref ) => { const inputRef = React.useRef(null); React.useImperativeHandle(ref, () => ({ focus: () => { if (inputRef.current) { inputRef.current.focus(); } } })); return ; }; export const foo = React.forwardRef(MyInput); export const Bar = React.forwardRef( (props, ref) => { return
{props.children}
; } ); export const baz = ( ref: React.ForwardedRef ): React.Ref => ref; preactjs-preact-389c7bc/compat/test/ts/index.tsx000066400000000000000000000024071523334003000217600ustar00rootroot00000000000000import React from '../../src'; React.render(
, document.createElement('div')); React.render(
, document.createDocumentFragment()); React.render(
, document.body.shadowRoot!); React.hydrate(
, document.createElement('div')); React.hydrate(
, document.createDocumentFragment()); React.hydrate(
, document.body.shadowRoot!); React.unmountComponentAtNode(document.createElement('div')); React.unmountComponentAtNode(document.createDocumentFragment()); React.unmountComponentAtNode(document.body.shadowRoot!); React.createPortal(
, document.createElement('div')); React.createPortal(
, document.createDocumentFragment()); React.createPortal(
, document.body.shadowRoot!); const Ctx = React.createContext({ contextValue: '' }); class SimpleComponentWithContextAsProvider extends React.Component { componentProp = 'componentProp'; render() { // Render inside div to ensure standard JSX elements still work return (
{/* Ensure context still works */} {({ contextValue }) => contextValue.toLowerCase()}
); } } React.render( , document.createElement('div') ); preactjs-preact-389c7bc/compat/test/ts/lazy.tsx000066400000000000000000000005101523334003000216210ustar00rootroot00000000000000import * as React from '../../src'; export interface LazyProps { isProp: boolean; } interface LazyState { forState: string; } export default class IsLazyComponent extends React.Component< LazyProps, LazyState > { render({ isProp }: LazyProps) { return
{isProp ? 'Super Lazy TRUE' : 'Super Lazy FALSE'}
; } } preactjs-preact-389c7bc/compat/test/ts/memo.tsx000066400000000000000000000034141523334003000216050ustar00rootroot00000000000000import * as React from '../../src'; import { expectType } from './utils'; interface MemoProps { required: string; optional?: string; defaulted: string; } interface MemoPropsExceptDefaults { required: string; optional?: string; } const ComponentExceptDefaults = () =>
; const ReadonlyBaseComponent = (props: Readonly) => (
{props.required + props.optional + props.defaulted}
); ReadonlyBaseComponent.defaultProps = { defaulted: '' }; const BaseComponent = (props: MemoProps) => (
{props.required + props.optional + props.defaulted}
); BaseComponent.defaultProps = { defaulted: '' }; // memo for readonly component with default comparison const MemoedReadonlyComponent = React.memo(ReadonlyBaseComponent); expectType>(MemoedReadonlyComponent); export const memoedReadonlyComponent = ( ); // memo for non-readonly component with default comparison const MemoedComponent = React.memo(BaseComponent); expectType>(MemoedComponent); export const memoedComponent = ; // memo with custom comparison const CustomMemoedComponent = React.memo(BaseComponent, (a, b) => { expectType(a); expectType(b); return a.required === b.required; }); expectType>(CustomMemoedComponent); export const customMemoedComponent = ; const MemoedComponentExceptDefaults = React.memo( ComponentExceptDefaults ); expectType>( MemoedComponentExceptDefaults ); export const memoedComponentExceptDefaults = ( ); preactjs-preact-389c7bc/compat/test/ts/react-default.tsx000066400000000000000000000001631523334003000233660ustar00rootroot00000000000000import React from '../../src'; class ReactIsh extends React.Component { render() { return
Text
; } } preactjs-preact-389c7bc/compat/test/ts/react-star.tsx000066400000000000000000000002321523334003000227100ustar00rootroot00000000000000// import React from '../../src'; import * as React from '../../src'; class ReactIsh extends React.Component { render() { return
Text
; } } preactjs-preact-389c7bc/compat/test/ts/scheduler.ts000066400000000000000000000010641523334003000224350ustar00rootroot00000000000000import { unstable_runWithPriority, unstable_NormalPriority, unstable_LowPriority, unstable_IdlePriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_now } from '../../src'; const noop = () => null; unstable_runWithPriority(unstable_IdlePriority, noop); unstable_runWithPriority(unstable_LowPriority, noop); unstable_runWithPriority(unstable_NormalPriority, noop); unstable_runWithPriority(unstable_UserBlockingPriority, noop); unstable_runWithPriority(unstable_ImmediatePriority, noop); if (typeof unstable_now() === 'number') { } preactjs-preact-389c7bc/compat/test/ts/suspense.tsx000066400000000000000000000027631523334003000225230ustar00rootroot00000000000000import * as React from '../../src'; interface LazyProps { isProp: boolean; } const IsLazyFunctional = (props: LazyProps) => (
{props.isProp ? 'Super Lazy TRUE' : 'Super Lazy FALSE'}
); const FallBack = () =>
Still working...
; /** * Have to mock dynamic import as import() throws a syntax error in the test runner */ const componentPromise = new Promise<{ default: typeof IsLazyFunctional }>( resolve => { setTimeout(() => { resolve({ default: IsLazyFunctional }); }, 800); } ); /** * For usage with import: * const IsLazyComp = lazy(() => import('./lazy')); */ const IsLazyFunc = React.lazy(() => componentPromise); // Suspense using lazy component class ReactSuspensefulFunc extends React.Component { render() { return ( }> ); } } //SuspenseList using lazy components function ReactSuspenseListTester(_props: any) { return ( }> }> ); } const Comp = () =>

Hello world

; const importComponent = async () => { return { MyComponent: Comp }; }; const Lazy = React.lazy(() => importComponent().then(mod => ({ default: mod.MyComponent })) ); // eslint-disable-next-line function App() { return ; } preactjs-preact-389c7bc/compat/test/ts/tsconfig.json000066400000000000000000000006031523334003000226140ustar00rootroot00000000000000{ "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "lib": ["es6", "dom"], "strict": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "noEmit": true, "allowSyntheticDefaultImports": true, "paths": { "preact": ["../../../src/index.d.ts"] } }, "include": ["./**/*.ts", "./**/*.tsx"] } preactjs-preact-389c7bc/compat/test/ts/utils.ts000066400000000000000000000001571523334003000216210ustar00rootroot00000000000000/** * Assert the parameter is of a specific type. */ export const expectType = (_: T): void => undefined; preactjs-preact-389c7bc/config/000077500000000000000000000000001523334003000164635ustar00rootroot00000000000000preactjs-preact-389c7bc/config/codemod-const.js000066400000000000000000000022551523334003000215630ustar00rootroot00000000000000/* eslint no-console:0 */ /** Find constants (identified by ALL_CAPS_DECLARATIONS), and inline them globally. * This is safe because Preact *only* uses global constants. */ export default (file, api) => { let j = api.jscodeshift, code = j(file.source), constants = {}, found = 0; code .find(j.VariableDeclaration) .filter(decl => { for (let i = decl.value.declarations.length; i--; ) { let node = decl.value.declarations[i], name = node.id && node.id.name, init = node.init; if (name && init && name.match(/^[A-Z0-9_$]+$/g) && !init.regex) { if (init.type === 'Literal') { // console.log(`Inlining constant: ${name}=${init.raw}`); found++; constants[name] = init; // remove declaration decl.value.declarations.splice(i, 1); // if it's the last, we'll remove the whole statement return !decl.value.declarations.length; } } } return false; }) .remove(); code .find(j.Identifier) .filter( path => path.value.name && constants.hasOwnProperty(path.value.name) ) .replaceWith(path => (found++, constants[path.value.name])); return found ? code.toSource({ quote: 'single' }) : null; }; preactjs-preact-389c7bc/config/codemod-let-name.js000066400000000000000000000007011523334003000221310ustar00rootroot00000000000000/** * Restores var names transformed by babel's let block scoping */ export default (file, api) => { let j = api.jscodeshift; let code = j(file.source); // @TODO unsafe, but without it we gain 20b gzipped: https://www.diffchecker.com/bVrOJWTO code .findVariableDeclarators() .filter(d => /^_i/.test(d.value.id.name)) .renameTo('i'); code.findVariableDeclarators('_key').renameTo('key'); return code.toSource({ quote: 'single' }); }; preactjs-preact-389c7bc/config/codemod-strip-tdz.js000066400000000000000000000025721523334003000223770ustar00rootroot00000000000000/* eslint no-console:0 */ // parent node types that we don't want to remove pointless initializations from (because it breaks hoisting) const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement' /** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */ export default (file, api) => { let { jscodeshift } = api, found = 0; let code = jscodeshift(file.source) .find(jscodeshift.VariableDeclaration) .forEach(handleDeclaration); function handleDeclaration(decl) { let p = decl, remove = true; while ((p = p.parentPath)) { if (~BLOCKED.indexOf(p.value.type)) { remove = false; break; } } decl.value.declarations.filter(isPointless).forEach(node => { if (remove === false) { console.log( `> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}` ); } else { removeNodeInitialization(node); } }); } function removeNodeInitialization(node) { node.init = null; found++; } function isPointless(node) { let { init } = node; if (init) { if ( init.type === 'UnaryExpression' && init.operator === 'void' && init.argument.value == 0 ) { return true; } if (init.type === 'Identifier' && init.name === 'undefined') { return true; } } return false; } return found ? code.toSource({ quote: 'single' }) : null; }; preactjs-preact-389c7bc/config/compat-entries.js000066400000000000000000000011751523334003000217570ustar00rootroot00000000000000const path = require('path'); const fs = require('fs'); const kl = require('kolorist'); const pkgFiles = new Set(require('../package.json').files); const compatDir = path.join(__dirname, '..', 'compat'); const files = fs.readdirSync(compatDir); let missing = 0; for (const file of files) { const expected = 'compat/' + file; if (/\.(js|mjs)$/.test(file) && !pkgFiles.has(expected)) { missing++; const filePath = kl.cyan('compat/' + file); const label = kl.inverse(kl.red(' ERROR ')); console.error( `${label} File ${filePath} is missing in "files" entry in package.json` ); } } if (missing > 0) { process.exit(1); } preactjs-preact-389c7bc/config/node-13-exports.js000066400000000000000000000014231523334003000216710ustar00rootroot00000000000000const fs = require('fs'); const subRepositories = [ 'compat', 'debug', 'devtools', 'hooks', 'jsx-runtime', 'test-utils' ]; const snakeCaseToCamelCase = str => str.replace(/([-_][a-z])/g, group => group.toUpperCase().replace('-', '')); const copyPreact = () => { // Copy .module.js --> .mjs for Node 13 compat. fs.writeFileSync( `${process.cwd()}/dist/preact.mjs`, fs.readFileSync(`${process.cwd()}/dist/preact.module.js`) ); }; const copy = name => { // Copy .module.js --> .mjs for Node 13 compat. const filename = name.includes('-') ? snakeCaseToCamelCase(name) : name; fs.writeFileSync( `${process.cwd()}/${name}/dist/${filename}.mjs`, fs.readFileSync(`${process.cwd()}/${name}/dist/${filename}.module.js`) ); }; copyPreact(); subRepositories.forEach(copy); preactjs-preact-389c7bc/debug/000077500000000000000000000000001523334003000163045ustar00rootroot00000000000000preactjs-preact-389c7bc/debug/LICENSE000066400000000000000000000020771523334003000173170ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-present Jason Miller 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. preactjs-preact-389c7bc/debug/mangle.json000066400000000000000000000012541523334003000204440ustar00rootroot00000000000000{ "help": { "what is this file?": "It controls protected/private property mangling so that minified builds have consistent property names.", "why are there duplicate minified properties?": "Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size." }, "minify": { "mangle": { "properties": { "regex": "^_[^_]", "reserved": [ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", "__REACT_DEVTOOLS_GLOBAL_HOOK__", "__PREACT_DEVTOOLS__", "_renderers", "__source", "__self" ] } } } } preactjs-preact-389c7bc/debug/package.json000066400000000000000000000011601523334003000205700ustar00rootroot00000000000000{ "name": "preact-debug", "amdName": "preactDebug", "version": "1.0.0", "private": true, "description": "Preact extensions for development", "main": "dist/debug.js", "module": "dist/debug.module.js", "umd:main": "dist/debug.umd.js", "source": "src/index.js", "license": "MIT", "mangle": { "regex": "^(?!_renderer)^_" }, "peerDependencies": { "preact": "^10.0.0" }, "exports": { ".": { "types": "./src/index.d.ts", "browser": "./dist/debug.module.js", "umd": "./dist/debug.umd.js", "import": "./dist/debug.mjs", "require": "./dist/debug.js" } } } preactjs-preact-389c7bc/debug/src/000077500000000000000000000000001523334003000170735ustar00rootroot00000000000000preactjs-preact-389c7bc/debug/src/check-props.js000066400000000000000000000025331523334003000216520ustar00rootroot00000000000000const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; let loggedTypeFailures = {}; /** * Reset the history of which prop type warnings have been logged. */ export function resetPropWarnings() { loggedTypeFailures = {}; } /** * Assert that the values match with the type specs. * Error messages are memorized and will only be shown once. * * Adapted from https://github.com/facebook/prop-types/blob/master/checkPropTypes.js * * @param {object} typeSpecs Map of name to a ReactPropType * @param {object} values Runtime values that need to be type-checked * @param {string} location e.g. "prop", "context", "child context" * @param {string} componentName Name of the component for error messages. * @param {?Function} getStack Returns the component stack. */ export function checkPropTypes( typeSpecs, values, location, componentName, getStack ) { Object.keys(typeSpecs).forEach(typeSpecName => { let error; try { error = typeSpecs[typeSpecName]( values, typeSpecName, componentName, location, null, ReactPropTypesSecret ); } catch (e) { error = e; } if (error && !(error.message in loggedTypeFailures)) { loggedTypeFailures[error.message] = true; console.error( `Failed ${location} type: ${error.message}${ (getStack && `\n${getStack()}`) || '' }` ); } }); } preactjs-preact-389c7bc/debug/src/component-stack.js000066400000000000000000000074511523334003000225450ustar00rootroot00000000000000import { options, Fragment } from 'preact'; /** * Get human readable name of the component/dom node * @param {import('./internal').VNode} vnode * @param {import('./internal').VNode} vnode * @returns {string} */ export function getDisplayName(vnode) { if (vnode.type === Fragment) { return 'Fragment'; } else if (typeof vnode.type == 'function') { return vnode.type.displayName || vnode.type.name; } else if (typeof vnode.type == 'string') { return vnode.type; } return '#text'; } /** * Used to keep track of the currently rendered `vnode` and print it * in debug messages. */ let renderStack = []; /** * Keep track of the current owners. An owner describes a component * which was responsible to render a specific `vnode`. This exclude * children that are passed via `props.children`, because they belong * to the parent owner. * * ```jsx * const Foo = props =>
{props.children}
// div's owner is Foo * const Bar = props => { * return ( * // Foo's owner is Bar, span's owner is Bar * ) * } * ``` * * Note: A `vnode` may be hoisted to the root scope due to compiler * optimiztions. In these cases the `_owner` will be different. */ let ownerStack = []; /** * Get the currently rendered `vnode` * @returns {import('./internal').VNode | null} */ export function getCurrentVNode() { return renderStack.length > 0 ? renderStack[renderStack.length - 1] : null; } /** * If the user doesn't have `@babel/plugin-transform-react-jsx-source` * somewhere in his tool chain we can't print the filename and source * location of a component. In that case we just omit that, but we'll * print a helpful message to the console, notifying the user of it. */ let showJsxSourcePluginWarning = true; /** * Check if a `vnode` is a possible owner. * @param {import('./internal').VNode} vnode */ function isPossibleOwner(vnode) { return typeof vnode.type == 'function' && vnode.type != Fragment; } /** * Return the component stack that was captured up to this point. * @param {import('./internal').VNode} vnode * @returns {string} */ export function getOwnerStack(vnode) { const stack = [vnode]; let next = vnode; while (next._owner != null) { stack.push(next._owner); next = next._owner; } return stack.reduce((acc, owner) => { acc += ` in ${getDisplayName(owner)}`; const source = owner.__source; if (source) { acc += ` (at ${source.fileName}:${source.lineNumber})`; } else if (showJsxSourcePluginWarning) { console.warn( 'Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.' ); } showJsxSourcePluginWarning = false; return (acc += '\n'); }, ''); } /** * Setup code to capture the component trace while rendering. Note that * we cannot simply traverse `vnode._parent` upwards, because we have some * debug messages for `this.setState` where the `vnode` is `undefined`. */ export function setupComponentStack() { let oldDiff = options._diff; let oldDiffed = options.diffed; let oldRoot = options._root; let oldVNode = options.vnode; let oldRender = options._render; options.diffed = vnode => { if (isPossibleOwner(vnode)) { ownerStack.pop(); } renderStack.pop(); if (oldDiffed) oldDiffed(vnode); }; options._diff = vnode => { if (isPossibleOwner(vnode)) { renderStack.push(vnode); } if (oldDiff) oldDiff(vnode); }; options._root = (vnode, parent) => { ownerStack = []; if (oldRoot) oldRoot(vnode, parent); }; options.vnode = vnode => { vnode._owner = ownerStack.length > 0 ? ownerStack[ownerStack.length - 1] : null; if (oldVNode) oldVNode(vnode); }; options._render = vnode => { if (isPossibleOwner(vnode)) { ownerStack.push(vnode); } if (oldRender) oldRender(vnode); }; } preactjs-preact-389c7bc/debug/src/constants.js000066400000000000000000000001511523334003000214420ustar00rootroot00000000000000export const ELEMENT_NODE = 1; export const DOCUMENT_NODE = 9; export const DOCUMENT_FRAGMENT_NODE = 11; preactjs-preact-389c7bc/debug/src/debug.js000066400000000000000000000417471523334003000205340ustar00rootroot00000000000000import { checkPropTypes } from './check-props'; import { options, Component } from 'preact'; import { ELEMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE } from './constants'; import { getOwnerStack, setupComponentStack, getCurrentVNode, getDisplayName } from './component-stack'; import { assign, isNaN } from './util'; const isWeakMapSupported = typeof WeakMap == 'function'; /** * @param {import('./internal').VNode} vnode * @returns {Array} */ function getDomChildren(vnode) { let domChildren = []; if (!vnode._children) return domChildren; vnode._children.forEach(child => { if (child && typeof child.type === 'function') { domChildren.push.apply(domChildren, getDomChildren(child)); } else if (child && typeof child.type === 'string') { domChildren.push(child.type); } }); return domChildren; } /** * @param {import('./internal').VNode} parent * @returns {string} */ function getClosestDomNodeParentName(parent) { if (!parent) return ''; if (typeof parent.type == 'function') { if (parent._parent == null) { if (parent._dom != null && parent._dom.parentNode != null) { return parent._dom.parentNode.localName; } return ''; } return getClosestDomNodeParentName(parent._parent); } return /** @type {string} */ (parent.type); } export function initDebug() { setupComponentStack(); let hooksAllowed = false; /* eslint-disable no-console */ let oldBeforeDiff = options._diff; let oldDiffed = options.diffed; let oldVnode = options.vnode; let oldRender = options._render; let oldCatchError = options._catchError; let oldRoot = options._root; let oldHook = options._hook; const warnedComponents = !isWeakMapSupported ? null : { useEffect: new WeakMap(), useLayoutEffect: new WeakMap(), lazyPropTypes: new WeakMap() }; const deprecations = []; options._catchError = (error, vnode, oldVNode, errorInfo) => { let component = vnode && vnode._component; if (component && typeof error.then == 'function') { const promise = error; error = new Error( `Missing Suspense. The throwing component was: ${getDisplayName(vnode)}` ); let parent = vnode; for (; parent; parent = parent._parent) { if (parent._component && parent._component._childDidSuspend) { error = promise; break; } } // We haven't recovered and we know at this point that there is no // Suspense component higher up in the tree if (error instanceof Error) { throw error; } } try { errorInfo = errorInfo || {}; errorInfo.componentStack = getOwnerStack(vnode); oldCatchError(error, vnode, oldVNode, errorInfo); // when an error was handled by an ErrorBoundary we will nonetheless emit an error // event on the window object. This is to make up for react compatibility in dev mode // and thus make the Next.js dev overlay work. if (typeof error.then != 'function') { setTimeout(() => { throw error; }); } } catch (e) { throw e; } }; options._root = (vnode, parentNode) => { if (!parentNode) { throw new Error( 'Undefined parent passed to render(), this is the second argument.\n' + 'Check if the element is available in the DOM/has the correct id.' ); } let isValid; switch (parentNode.nodeType) { case ELEMENT_NODE: case DOCUMENT_FRAGMENT_NODE: case DOCUMENT_NODE: isValid = true; break; default: isValid = false; } if (!isValid) { let componentName = getDisplayName(vnode); throw new Error( `Expected a valid HTML node as a second argument to render. Received ${parentNode} instead: render(<${componentName} />, ${parentNode});` ); } if (oldRoot) oldRoot(vnode, parentNode); }; options._diff = vnode => { let { type } = vnode; hooksAllowed = true; if (type === undefined) { throw new Error( 'Undefined component passed to createElement()\n\n' + 'You likely forgot to export your component or might have mixed up default and named imports' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } else if (type != null && typeof type == 'object') { if (type._children !== undefined && type._dom !== undefined) { throw new Error( `Invalid type passed to createElement(): ${type}\n\n` + 'Did you accidentally pass a JSX literal as JSX twice?\n\n' + ` let My${getDisplayName(vnode)} = ${serializeVNode(type)};\n` + ` let vnode = ;\n\n` + 'This usually happens when you export a JSX literal and not the component.' + `\n\n${getOwnerStack(vnode)}` ); } throw new Error( 'Invalid type passed to createElement(): ' + (Array.isArray(type) ? 'array' : type) ); } if ( vnode.ref !== undefined && typeof vnode.ref != 'function' && typeof vnode.ref != 'object' && !('$$typeof' in vnode) // allow string refs when preact-compat is installed ) { throw new Error( `Component's "ref" property should be a function, or an object created ` + `by createRef(), but got [${typeof vnode.ref}] instead\n` + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } if (typeof vnode.type == 'string') { for (const key in vnode.props) { if ( key[0] === 'o' && key[1] === 'n' && typeof vnode.props[key] != 'function' && vnode.props[key] != null ) { throw new Error( `Component's "${key}" property should be a function, ` + `but got [${typeof vnode.props[key]}] instead\n` + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } } } // Check prop-types if available if (typeof vnode.type == 'function' && vnode.type.propTypes) { if ( vnode.type.displayName === 'Lazy' && warnedComponents && !warnedComponents.lazyPropTypes.has(vnode.type) ) { const m = 'PropTypes are not supported on lazy(). Use propTypes on the wrapped component itself. '; try { const lazyVNode = vnode.type(); warnedComponents.lazyPropTypes.set(vnode.type, true); console.warn( m + `Component wrapped in lazy() is ${getDisplayName(lazyVNode)}` ); } catch (promise) { console.warn( m + "We will log the wrapped component's name once it is loaded." ); } } let values = vnode.props; if (vnode.type._forwarded) { values = assign({}, values); delete values.ref; } checkPropTypes( vnode.type.propTypes, values, 'prop', getDisplayName(vnode), () => getOwnerStack(vnode) ); } if (oldBeforeDiff) oldBeforeDiff(vnode); }; let renderCount = 0; let currentComponent; options._render = vnode => { if (oldRender) { oldRender(vnode); } hooksAllowed = true; const nextComponent = vnode._component; if (nextComponent === currentComponent) { renderCount++; } else { renderCount = 1; } if (renderCount >= 25) { throw new Error( `Too many re-renders. This is limited to prevent an infinite loop ` + `which may lock up your browser. The component causing this is: ${getDisplayName( vnode )}` ); } currentComponent = nextComponent; }; options._hook = (comp, index, type) => { if (!comp || !hooksAllowed) { throw new Error('Hook can only be invoked from render methods.'); } if (oldHook) oldHook(comp, index, type); }; // Ideally we'd want to print a warning once per component, but we // don't have access to the vnode that triggered it here. As a // compromise and to avoid flooding the console with warnings we // print each deprecation warning only once. const warn = (property, message) => ({ get() { const key = 'get' + property + message; if (deprecations && deprecations.indexOf(key) < 0) { deprecations.push(key); console.warn(`getting vnode.${property} is deprecated, ${message}`); } }, set() { const key = 'set' + property + message; if (deprecations && deprecations.indexOf(key) < 0) { deprecations.push(key); console.warn(`setting vnode.${property} is not allowed, ${message}`); } } }); const deprecatedAttributes = { nodeName: warn('nodeName', 'use vnode.type'), attributes: warn('attributes', 'use vnode.props'), children: warn('children', 'use vnode.props.children') }; const deprecatedProto = Object.create({}, deprecatedAttributes); options.vnode = vnode => { const props = vnode.props; if ( vnode.type !== null && props != null && ('__source' in props || '__self' in props) ) { const newProps = (vnode.props = {}); for (let i in props) { const v = props[i]; if (i === '__source') vnode.__source = v; else if (i === '__self') vnode.__self = v; else newProps[i] = v; } } // eslint-disable-next-line vnode.__proto__ = deprecatedProto; if (oldVnode) oldVnode(vnode); }; options.diffed = vnode => { const { type, _parent: parent } = vnode; // Check if the user passed plain objects as children. Note that we cannot // move this check into `options.vnode` because components can receive // children in any shape they want (e.g. // `{{ foo: 123, bar: "abc" }}`). // Putting this check in `options.diffed` ensures that // `vnode._children` is set and that we only validate the children // that were actually rendered. if (vnode._children) { vnode._children.forEach(child => { if (typeof child === 'object' && child && child.type === undefined) { const keys = Object.keys(child).join(','); throw new Error( `Objects are not valid as a child. Encountered an object with the keys {${keys}}.` + `\n\n${getOwnerStack(vnode)}` ); } }); } if (vnode._component === currentComponent) { renderCount = 0; } if ( typeof type === 'string' && (isTableElement(type) || type === 'p' || type === 'a' || type === 'button') ) { // Avoid false positives when Preact only partially rendered the // HTML tree. Whilst we attempt to include the outer DOM in our // validation, this wouldn't work on the server for // `preact-render-to-string`. There we'd otherwise flood the terminal // with false positives, which we'd like to avoid. let domParentName = getClosestDomNodeParentName(parent); if (domParentName !== '' && isTableElement(type)) { if ( type === 'table' && // Tables can be nested inside each other if it's inside a cell. // See https://developer.mozilla.org/en-US/docs/Learn/HTML/Tables/Advanced#nesting_tables domParentName !== 'td' && isTableElement(domParentName) ) { console.error( 'Improper nesting of table. Your should not have a table-node parent.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } else if ( (type === 'thead' || type === 'tfoot' || type === 'tbody') && domParentName !== 'table' ) { console.error( 'Improper nesting of table. Your should have a
parent.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } else if ( type === 'tr' && domParentName !== 'thead' && domParentName !== 'tfoot' && domParentName !== 'tbody' ) { console.error( 'Improper nesting of table. Your should have a parent.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } else if (type === 'td' && domParentName !== 'tr') { console.error( 'Improper nesting of table. Your parent.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } else if (type === 'th' && domParentName !== 'tr') { console.error( 'Improper nesting of table. Your .' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } } else if (type === 'p') { let illegalDomChildrenTypes = getDomChildren(vnode).filter(childType => ILLEGAL_PARAGRAPH_CHILD_ELEMENTS.test(childType) ); if (illegalDomChildrenTypes.length) { console.error( 'Improper nesting of paragraph. Your

should not have ' + illegalDomChildrenTypes.join(', ') + ' as child-elements.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } } else if (type === 'a' || type === 'button') { if (getDomChildren(vnode).indexOf(type) !== -1) { console.error( `Improper nesting of interactive content. Your <${type}>` + ` should not have other ${type === 'a' ? 'anchor' : 'button'}` + ' tags as child-elements.' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); } } } hooksAllowed = false; if (oldDiffed) oldDiffed(vnode); if (vnode._children != null) { const keys = []; for (let i = 0; i < vnode._children.length; i++) { const child = vnode._children[i]; if (!child || child.key == null) continue; const key = child.key; if (keys.indexOf(key) !== -1) { console.error( 'Following component has two or more children with the ' + `same key attribute: "${key}". This may cause glitches and misbehavior ` + 'in rendering process. Component: \n\n' + serializeVNode(vnode) + `\n\n${getOwnerStack(vnode)}` ); // Break early to not spam the console break; } keys.push(key); } } if (vnode._component != null && vnode._component.__hooks != null) { // Validate that none of the hooks in this component contain arguments that are NaN. // This is a common mistake that can be hard to debug, so we want to catch it early. const hooks = vnode._component.__hooks._list; if (hooks) { for (let i = 0; i < hooks.length; i += 1) { const hook = hooks[i]; if (hook._args) { for (let j = 0; j < hook._args.length; j++) { const arg = hook._args[j]; if (isNaN(arg)) { const componentName = getDisplayName(vnode); console.warn( `Invalid argument passed to hook. Hooks should not be called with NaN in the dependency array. Hook index ${i} in component ${componentName} was called with NaN.` ); } } } } } } }; } const setState = Component.prototype.setState; Component.prototype.setState = function (update, callback) { if (this._vnode == null) { // `this._vnode` will be `null` during componentWillMount. But it // is perfectly valid to call `setState` during cWM. So we // need an additional check to verify that we are dealing with a // call inside constructor. if (this.state == null) { console.warn( `Calling "this.setState" inside the constructor of a component is a ` + `no-op and might be a bug in your application. Instead, set ` + `"this.state = {}" directly.\n\n${getOwnerStack(getCurrentVNode())}` ); } } return setState.call(this, update, callback); }; function isTableElement(type) { return ( type === 'table' || type === 'tfoot' || type === 'tbody' || type === 'thead' || type === 'td' || type === 'tr' || type === 'th' ); } const ILLEGAL_PARAGRAPH_CHILD_ELEMENTS = /^(address|article|aside|blockquote|details|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|main|menu|nav|ol|p|pre|search|section|table|ul)$/; const forceUpdate = Component.prototype.forceUpdate; Component.prototype.forceUpdate = function (callback) { if (this._vnode == null) { console.warn( `Calling "this.forceUpdate" inside the constructor of a component is a ` + `no-op and might be a bug in your application.\n\n${getOwnerStack( getCurrentVNode() )}` ); } else if (this._parentDom == null) { console.warn( `Can't call "this.forceUpdate" on an unmounted component. This is a no-op, ` + `but it indicates a memory leak in your application. To fix, cancel all ` + `subscriptions and asynchronous tasks in the componentWillUnmount method.` + `\n\n${getOwnerStack(this._vnode)}` ); } return forceUpdate.call(this, callback); }; /** * Serialize a vnode tree to a string * @param {import('./internal').VNode} vnode * @returns {string} */ export function serializeVNode(vnode) { let { props } = vnode; let name = getDisplayName(vnode); let attrs = ''; for (let prop in props) { if (props.hasOwnProperty(prop) && prop !== 'children') { let value = props[prop]; // If it is an object but doesn't have toString(), use Object.toString if (typeof value == 'function') { value = `function ${value.displayName || value.name}() {}`; } value = Object(value) === value && !value.toString ? Object.prototype.toString.call(value) : value + ''; attrs += ` ${prop}=${JSON.stringify(value)}`; } } let children = props.children; return `<${name}${attrs}${ children && children.length ? '>..' : ' />' }`; } options._hydrationMismatch = (newVNode, excessDomChildren) => { const { type } = newVNode; const availableTypes = excessDomChildren .map(child => child && child.localName) .filter(Boolean); console.error( `Expected a DOM node of type "${type}" but found "${availableTypes.join(', ')}" as available DOM-node(s), this is caused by the SSR'd HTML containing different DOM-nodes compared to the hydrated one.\n\n${getOwnerStack(newVNode)}` ); }; preactjs-preact-389c7bc/debug/src/index.d.ts000066400000000000000000000012101523334003000207660ustar00rootroot00000000000000import { VNode } from 'preact'; /** * Get the currently rendered `vnode` */ export function getCurrentVNode(): VNode | null; /** * Return the component stack that was captured up to this point. */ export function getOwnerStack(vnode: VNode): string; /** * Setup code to capture the component trace while rendering. Note that * we cannot simply traverse `vnode._parent` upwards, because we have some * debug messages for `this.setState` where the `vnode` is `undefined`. */ export function setupComponentStack(): void; /** * Reset the history of which prop type warnings have been logged. */ export function resetPropWarnings(): void; preactjs-preact-389c7bc/debug/src/index.js000066400000000000000000000003311523334003000205350ustar00rootroot00000000000000import { initDebug } from './debug'; import 'preact/devtools'; initDebug(); export { resetPropWarnings } from './check-props'; export { getCurrentVNode, getDisplayName, getOwnerStack } from './component-stack'; preactjs-preact-389c7bc/debug/src/internal.d.ts000066400000000000000000000043761523334003000215130ustar00rootroot00000000000000import { Component, PreactElement, VNode, Options } from '../../src/internal'; export { Component, PreactElement, VNode, Options }; export interface DevtoolsInjectOptions { /** 1 = DEV, 0 = production */ bundleType: 1 | 0; /** The devtools enable different features for different versions of react */ version: string; /** Informative string, currently unused in the devtools */ rendererPackageName: string; /** Find the root dom node of a vnode */ findHostInstanceByFiber(vnode: VNode): HTMLElement | null; /** Find the closest vnode given a dom node */ findFiberByHostInstance(instance: HTMLElement): VNode | null; } export interface DevtoolsUpdater { setState(objOrFn: any): void; forceUpdate(): void; setInState(path: Array, value: any): void; setInProps(path: Array, value: any): void; setInContext(): void; } export type NodeType = 'Composite' | 'Native' | 'Wrapper' | 'Text'; export interface DevtoolData { nodeType: NodeType; // Component type type: any; name: string; ref: any; key: string | number; updater: DevtoolsUpdater | null; text: string | number | null; state: any; props: any; children: VNode[] | string | number | null; publicInstance: PreactElement | Text | Component; memoizedInteractions: any[]; actualDuration: number; actualStartTime: number; treeBaseDuration: number; } export type EventType = | 'unmount' | 'rootCommitted' | 'root' | 'mount' | 'update' | 'updateProfileTimes'; export interface DevtoolsEvent { data?: DevtoolData; internalInstance: VNode; renderer: string; type: EventType; } export interface DevtoolsHook { _renderers: Record; _roots: Set; on(ev: string, listener: () => void): void; emit(ev: string, data?: object): void; helpers: Record; getFiberRoots(rendererId: string): Set; inject(config: DevtoolsInjectOptions): string; onCommitFiberRoot(rendererId: string, root: VNode): void; onCommitFiberUnmount(rendererId: string, vnode: VNode): void; } export interface DevtoolsWindow extends Window { /** * If the devtools extension is installed it will inject this object into * the dom. This hook handles all communications between preact and the * devtools panel. */ __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevtoolsHook; } preactjs-preact-389c7bc/debug/src/util.js000066400000000000000000000006121523334003000204050ustar00rootroot00000000000000/** * Assign properties from `props` to `obj` * @template O, P The obj and props types * @param {O} obj The object to copy properties to * @param {P} props The object to copy properties from * @returns {O & P} */ export function assign(obj, props) { for (let i in props) obj[i] = props[i]; return /** @type {O & P} */ (obj); } export function isNaN(value) { return value !== value; } preactjs-preact-389c7bc/debug/test/000077500000000000000000000000001523334003000172635ustar00rootroot00000000000000preactjs-preact-389c7bc/debug/test/browser/000077500000000000000000000000001523334003000207465ustar00rootroot00000000000000preactjs-preact-389c7bc/debug/test/browser/component-stack-2.test.jsx000066400000000000000000000022741523334003000257230ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import 'preact/debug'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ // This test is not part of component-stack.test.js to avoid it being // transpiled with '@babel/plugin-transform-react-jsx-source' enabled. describe('component stack', () => { /** @type {HTMLDivElement} */ let scratch; let errors = []; let warnings = []; beforeEach(() => { scratch = setupScratch(); errors = []; warnings = []; sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { console.error.restore(); console.warn.restore(); teardown(scratch); }); it('should print a warning when "@babel/plugin-transform-react-jsx-source" is not installed', () => { function Foo() { return ; } class Thrower extends Component { constructor(props) { super(props); this.setState({ foo: 1 }); } render() { return

foo
; } } render(, scratch); expect( warnings[0].indexOf('@babel/plugin-transform-react-jsx-source') > -1 ).to.equal(true); }); }); preactjs-preact-389c7bc/debug/test/browser/component-stack.test.jsx000066400000000000000000000041031523334003000255550ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import 'preact/debug'; import { vi } from 'vitest'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ describe('component stack', () => { /** @type {HTMLDivElement} */ let scratch; let errors = []; let warnings = []; const getStack = arr => arr[0].split('\n\n')[1]; beforeEach(() => { scratch = setupScratch(); errors = []; warnings = []; vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); }); afterEach(() => { vi.resetAllMocks(); teardown(scratch); }); it('should print component stack', () => { function Foo() { return ; } class Thrower extends Component { constructor(props) { super(props); this.setState({ foo: 1 }); } render() { return
foo
; } } render(, scratch); // This has a JSX transform warning, so we need to remove it warnings.shift(); let lines = getStack(warnings).split('\n'); expect(lines[0].indexOf('Thrower') > -1).to.equal(true); expect(lines[1].indexOf('Foo') > -1).to.equal(true); }); it('should only print owners', () => { function Foo(props) { return
{props.children}
; } function Bar() { return ( ); } class Thrower extends Component { render() { return (
should have a
should have a
foo
); } } render(, scratch); let lines = getStack(errors).split('\n'); expect(lines[0].indexOf('tr') > -1).to.equal(true); expect(lines[1].indexOf('Thrower') > -1).to.equal(true); expect(lines[2].indexOf('Bar') > -1).to.equal(true); }); it('should not print a warning when "@babel/plugin-transform-react-jsx-source" is installed', () => { function Thrower() { throw new Error('foo'); } try { render(, scratch); } catch {} expect(warnings.join(' ')).to.not.include( '@babel/plugin-transform-react-jsx-source' ); }); }); preactjs-preact-389c7bc/debug/test/browser/debug-compat.test.jsx000066400000000000000000000037201523334003000250230ustar00rootroot00000000000000import { createElement, render, createRef } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import './fakeDevTools'; import 'preact/debug'; import * as PropTypes from 'prop-types'; // eslint-disable-next-line no-duplicate-imports import { resetPropWarnings } from 'preact/debug'; import { forwardRef, createPortal } from 'preact/compat'; const h = createElement; /** @jsx createElement */ describe('debug compat', () => { let scratch; let root; let errors = []; let warnings = []; beforeEach(() => { errors = []; warnings = []; scratch = setupScratch(); sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); root = document.createElement('div'); document.body.appendChild(root); }); afterEach(() => { /** @type {*} */ console.error.restore(); console.warn.restore(); teardown(scratch); document.body.removeChild(root); }); describe('portals', () => { it('should not throw an invalid render argument for a portal.', () => { function Foo(props) { return
{createPortal(props.children, root)}
; } expect(() => render(foobar, scratch)).not.to.throw(); }); }); describe('PropTypes', () => { beforeEach(() => { resetPropWarnings(); }); it('should not fail if ref is passed to comp wrapped in forwardRef', () => { // This test ensures compat with airbnb/prop-types-exact, mui exact prop types util, etc. const Foo = forwardRef(function Foo(props, ref) { return

{props.text}

; }); Foo.propTypes = { text: PropTypes.string.isRequired, ref(props) { if ('ref' in props) { throw new Error( 'ref should not be passed to prop-types valiation!' ); } } }; const ref = createRef(); render(, scratch); expect(console.error).not.been.called; expect(ref.current).to.not.be.undefined; }); }); }); preactjs-preact-389c7bc/debug/test/browser/debug-hooks.test.jsx000066400000000000000000000046661523334003000246750ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { useState, useEffect } from 'preact/hooks'; import 'preact/debug'; import { act } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ describe('debug with hooks', () => { let scratch; let errors = []; let warnings = []; beforeEach(() => { errors = []; warnings = []; scratch = setupScratch(); sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { console.error.restore(); console.warn.restore(); teardown(scratch); }); it('should throw an error when using a hook outside a render', () => { class Foo extends Component { componentWillMount() { useState(); } render() { return this.props.children; } } class App extends Component { render() { return

test

; } } const fn = () => act(() => render( , scratch ) ); expect(fn).to.throw(/Hook can only be invoked from render/); }); it('should throw an error when invoked outside of a component', () => { function foo() { useEffect(() => {}); // Pretend to use a hook return

test

; } const fn = () => act(() => { render(foo(), scratch); }); expect(fn).to.throw(/Hook can only be invoked from render/); }); it('should throw an error when invoked outside of a component before render', () => { function Foo(props) { useEffect(() => {}); // Pretend to use a hook return props.children; } const fn = () => act(() => { useState(); render(Hello!, scratch); }); expect(fn).to.throw(/Hook can only be invoked from render/); }); it('should throw an error when invoked outside of a component after render', () => { function Foo(props) { useEffect(() => {}); // Pretend to use a hook return props.children; } const fn = () => act(() => { render(Hello!, scratch); useState(); }); expect(fn).to.throw(/Hook can only be invoked from render/); }); it('should throw an error when invoked inside an effect callback', () => { function Foo(props) { useEffect(() => { useState(); }); return props.children; } const fn = () => act(() => { render(Hello!, scratch); }); expect(fn).to.throw(/Hook can only be invoked from render/); }); }); preactjs-preact-389c7bc/debug/test/browser/debug-suspense.test.jsx000066400000000000000000000115251523334003000254070ustar00rootroot00000000000000import { createElement, render, lazy, Suspense } from 'preact/compat'; import 'preact/debug'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown, serializeHtml } from '../../../test/_util/helpers'; /** @jsx createElement */ describe('debug with suspense', () => { /** @type {HTMLDivElement} */ let scratch; let rerender; let errors = []; let warnings = []; beforeEach(() => { errors = []; warnings = []; scratch = setupScratch(); rerender = setupRerender(); sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { console.error.restore(); console.warn.restore(); teardown(scratch); }); it('should throw on missing ', () => { function Foo() { throw Promise.resolve(); } expect(() => render(, scratch)).to.throw; }); it('should throw an error when using lazy and missing Suspense', () => { const Foo = () =>
Foo
; const LazyComp = lazy( () => new Promise(resolve => resolve({ default: Foo })) ); const fn = () => { render(, scratch); }; expect(fn).to.throw(/Missing Suspense/gi); }); describe('PropTypes', () => { it('should validate propTypes inside lazy()', () => { function Baz(props) { return

{props.unhappy}

; } Baz.propTypes = { unhappy: function alwaysThrows(obj, key) { if (obj[key] === 'signal') throw Error('got prop inside lazy()'); } }; const loader = Promise.resolve({ default: Baz }); const LazyBaz = lazy(() => loader); const suspense = ( fallback...
}> ); render(suspense, scratch); rerender(); // render fallback expect(console.error).to.not.be.called; expect(serializeHtml(scratch)).to.equal('
fallback...
'); return loader.then(() => { rerender(); expect(errors.length).to.equal(1); expect(errors[0].includes('got prop')).to.equal(true); expect(serializeHtml(scratch)).to.equal('

signal

'); }); }); describe('warn for PropTypes on lazy()', () => { it('should log the function name', () => { const loader = Promise.resolve({ default: function MyLazyLoaded() { return
Hi there
; } }); const FakeLazy = lazy(() => loader); FakeLazy.propTypes = {}; const suspense = ( fallback...
}> ); render(suspense, scratch); rerender(); // Render fallback expect(serializeHtml(scratch)).to.equal('
fallback...
'); return loader.then(() => { rerender(); expect(console.warn).to.be.calledTwice; expect(warnings[1].includes('MyLazyLoaded')).to.equal(true); expect(serializeHtml(scratch)).to.equal('
Hi there
'); }); }); it('should log the displayName', () => { function MyLazyLoadedComponent() { return
Hi there
; } MyLazyLoadedComponent.displayName = 'HelloLazy'; const loader = Promise.resolve({ default: MyLazyLoadedComponent }); const FakeLazy = lazy(() => loader); FakeLazy.propTypes = {}; const suspense = ( fallback...
}> ); render(suspense, scratch); rerender(); // Render fallback expect(serializeHtml(scratch)).to.equal('
fallback...
'); return loader.then(() => { rerender(); expect(console.warn).to.be.calledTwice; expect(warnings[1].includes('HelloLazy')).to.equal(true); expect(serializeHtml(scratch)).to.equal('
Hi there
'); }); }); it("should not log a component if lazy loader's Promise rejects", () => { const loader = Promise.reject(new Error('Hey there')); const FakeLazy = lazy(() => loader); FakeLazy.propTypes = {}; render( fallback...
}> , scratch ); rerender(); // Render fallback expect(serializeHtml(scratch)).to.equal('
fallback...
'); return loader.catch(() => { try { rerender(); } catch (e) { // Ignore the loader's bubbling error } // Called once on initial render, and again when promise rejects expect(console.warn).to.be.calledTwice; }); }); it("should not log a component if lazy's loader throws", () => { const FakeLazy = lazy(() => { throw new Error('Hello'); }); FakeLazy.propTypes = {}; let error; try { render( fallback...
}> , scratch ); } catch (e) { error = e; } expect(console.warn).to.be.calledOnce; expect(error).not.to.be.undefined; expect(error.message).to.eql('Hello'); }); }); }); }); preactjs-preact-389c7bc/debug/test/browser/debug.options.test.jsx000066400000000000000000000057601523334003000252420ustar00rootroot00000000000000import { vnodeSpy, rootSpy, beforeDiffSpy, hookSpy, afterDiffSpy, catchErrorSpy } from '../../../test/_util/optionSpies'; import { createElement, render, Component } from 'preact'; import { useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; import 'preact/debug'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ describe('debug options', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; /** @type {(count: number) => void} */ let setCount; /** @type {import('sinon').SinonFakeTimers | undefined} */ let clock; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); vnodeSpy.resetHistory(); rootSpy.resetHistory(); beforeDiffSpy.resetHistory(); hookSpy.resetHistory(); afterDiffSpy.resetHistory(); catchErrorSpy.resetHistory(); }); afterEach(() => { teardown(scratch); if (clock) clock.restore(); }); class ClassApp extends Component { constructor() { super(); this.state = { count: 0 }; setCount = count => this.setState({ count }); } render() { return
{this.state.count}
; } } it('should call old options on mount', () => { render(, scratch); expect(vnodeSpy).to.have.been.called; expect(rootSpy).to.have.been.called; expect(beforeDiffSpy).to.have.been.called; expect(afterDiffSpy).to.have.been.called; }); it('should call old options on update', () => { render(, scratch); setCount(1); rerender(); expect(vnodeSpy).to.have.been.called; expect(rootSpy).to.have.been.called; expect(beforeDiffSpy).to.have.been.called; expect(afterDiffSpy).to.have.been.called; }); it('should call old options on unmount', () => { render(, scratch); render(null, scratch); expect(vnodeSpy).to.have.been.called; expect(rootSpy).to.have.been.called; expect(beforeDiffSpy).to.have.been.called; expect(afterDiffSpy).to.have.been.called; }); it('should call old hook options for hook components', () => { function HookApp() { const [count, realSetCount] = useState(0); setCount = realSetCount; return
{count}
; } render(, scratch); expect(hookSpy).to.have.been.called; }); it('should call old options on error', () => { const e = new Error('test'); class ErrorApp extends Component { constructor() { super(); this.state = { error: true }; } componentDidCatch() { this.setState({ error: false }); } render() { return ; } } function Throw({ error }) { if (error) { throw e; } else { return
no error
; } } clock = sinon.useFakeTimers(); render(, scratch); rerender(); expect(catchErrorSpy).to.have.been.called; // we expect to throw after setTimeout to trigger a window.onerror // this is to ensure react compat (i.e. with next.js' dev overlay) expect(() => clock.tick(0)).to.throw(e); }); }); preactjs-preact-389c7bc/debug/test/browser/debug.test.jsx000066400000000000000000000453211523334003000235450ustar00rootroot00000000000000import { createElement, render, createRef, Component, Fragment, hydrate } from 'preact'; import { useState } from 'preact/hooks'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import './fakeDevTools'; import 'preact/debug'; import { setupRerender } from 'preact/test-utils'; const h = createElement; /** @jsx createElement */ describe('debug', () => { /** @type {HTMLDivElement} */ let scratch; let errors = []; let warnings = []; let rerender; beforeEach(() => { errors = []; warnings = []; scratch = setupScratch(); rerender = setupRerender(); sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { /** @type {*} */ console.error.restore(); console.warn.restore(); teardown(scratch); }); it('should initialize devtools', () => { expect(window.__PREACT_DEVTOOLS__.attachPreact).to.have.been.called; }); it('should print an error on rendering on undefined parent', () => { let fn = () => render(
, undefined); expect(fn).to.throw(/render/); }); it('should print an error on rendering on invalid parent', () => { let fn = () => render(
, 6); expect(fn).to.throw(/valid HTML node/); expect(fn).to.throw(/
{ const App = () =>
; let fn = () => render(, 6); expect(fn).to.throw(/ render(, {}); expect(fn).to.throw(/ render(, 'badroot'); expect(fn).to.throw(/ { class App extends Component { render() { return
; } } let fn = () => render(, 6); expect(fn).to.throw(/ { let fn = () => render(h(undefined), scratch); expect(fn).to.throw(/createElement/); }); it('should print an error on invalid object component', () => { let fn = () => render(h({}), scratch); expect(fn).to.throw(/createElement/); }); it('should print an error when component is an array', () => { let fn = () => render(h([
]), scratch); expect(fn).to.throw(/createElement/); }); it('should print an error on double jsx conversion', () => { let Foo =
; let fn = () => render(h(), scratch); expect(fn).to.throw(/JSX twice/); }); it('should add __source to the vnode in debug mode.', () => { const vnode = h('div', { __source: { fileName: 'div.jsx', lineNumber: 3 } }); expect(vnode.__source).to.deep.equal({ fileName: 'div.jsx', lineNumber: 3 }); expect(vnode.props.__source).to.be.undefined; }); it('should add __self to the vnode in debug mode.', () => { const vnode = h('div', { __self: {} }); expect(vnode.__self).to.deep.equal({}); expect(vnode.props.__self).to.be.undefined; }); it('should warn when accessing certain attributes', () => { const vnode = h('div', null); // Push into an array to avoid empty statements being dead code eliminated const res = []; res.push(vnode); res.push(vnode.attributes); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match(/use vnode.props/); res.push(vnode.nodeName); expect(console.warn).to.be.calledTwice; expect(console.warn.args[1]).to.match(/use vnode.type/); res.push(vnode.children); expect(console.warn).to.be.calledThrice; expect(console.warn.args[2]).to.match(/use vnode.props.children/); // Should only warn once res.push(vnode.attributes); expect(console.warn).to.be.calledThrice; res.push(vnode.nodeName); expect(console.warn).to.be.calledThrice; res.push(vnode.children); expect(console.warn).to.be.calledThrice; vnode.attributes = {}; expect(console.warn.args[3]).to.match(/use vnode.props/); vnode.nodeName = ''; expect(console.warn.args[4]).to.match(/use vnode.type/); vnode.children = []; expect(console.warn.args[5]).to.match(/use vnode.props.children/); // Should only warn once vnode.attributes = {}; expect(console.warn.args.length).to.equal(6); vnode.nodeName = ''; expect(console.warn.args.length).to.equal(6); vnode.children = []; expect(console.warn.args.length).to.equal(6); // Mark res as used, otherwise it will be dead code eliminated expect(res.length).to.equal(7); }); it('should warn when calling setState inside the constructor', () => { class Foo extends Component { constructor(props) { super(props); this.setState({ foo: true }); } render() { return
foo
; } } render(, scratch); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match(/no-op/); }); it('should NOT warn when calling setState inside the cWM', () => { class Foo extends Component { componentWillMount() { this.setState({ foo: true }); } render() { return
foo
; } } render(, scratch); expect(console.warn).to.not.be.called; }); it('should warn when calling forceUpdate inside the constructor', () => { class Foo extends Component { constructor(props) { super(props); this.forceUpdate(); } render() { return
foo
; } } render(, scratch); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match(/no-op/); }); it('should warn when calling forceUpdate on an unmounted Component', () => { let forceUpdate; class Foo extends Component { constructor(props) { super(props); forceUpdate = () => this.forceUpdate(); } render() { return
foo
; } } render(, scratch); forceUpdate(); expect(console.warn).to.not.be.called; render(null, scratch); forceUpdate(); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match(/no-op/); }); it('should print an error when child is a plain object', () => { let fn = () => render(
{{}}
, scratch); expect(fn).to.throw(/not valid/); }); it('should print an error on invalid refs', () => { let fn = () => render(
, scratch); expect(fn).to.throw(/createRef/); }); it('should not print for null as a handler', () => { let fn = () => render(
, scratch); expect(fn).not.to.throw(); }); it('should not print for undefined as a handler', () => { let fn = () => render(
, scratch); expect(fn).not.to.throw(); }); it('should not print for attributes starting with on for Components', () => { const Comp = () =>

online

; let fn = () => render(, scratch); expect(fn).not.to.throw(); }); it('should print an error on invalid handler', () => { let fn = () => render(
, scratch); expect(fn).to.throw(/"onclick" property should be a function/); }); it('should NOT print an error on valid refs', () => { let noop = () => {}; render(
, scratch); let ref = createRef(); render(
, scratch); expect(console.error).to.not.be.called; }); it('throws an error if a component rerenders too many times', () => { let rerenderCount = 0; function TestComponent({ loop = false }) { const [count, setCount] = useState(0); if (loop) { setCount(count + 1); } if (count > 30) { expect.fail( 'Repeated rerenders did not cause the expected error. This test is failing.' ); } rerenderCount += 1; return
; } expect(() => { render( , scratch ); }).to.throw(/Too many re-renders/); // 1 for first TestComponent + 24 for second TestComponent expect(rerenderCount).to.equal(25); }); it('does not throw an error if a component renders many times in different cycles', () => { let set; function TestComponent() { const [count, setCount] = useState(0); set = () => setCount(count + 1); return
{count}
; } render(, scratch); for (let i = 0; i < 30; i++) { set(); rerender(); } expect(scratch.innerHTML).to.equal('
30
'); }); describe('duplicate keys', () => { const List = props =>
    {props.children}
; const ListItem = props =>
  • {props.children}
  • ; it('should print an error on duplicate keys with DOM nodes', () => { render(
    , scratch ); expect(console.error).to.be.calledOnce; }); it('should allow distinct object keys', () => { const A = { is: 'A' }; const B = { is: 'B' }; render(
    , scratch ); expect(console.error).not.to.be.called; }); it('should print an error for duplicate object keys', () => { const A = { is: 'A' }; render(
    , scratch ); expect(console.error).to.be.calledOnce; }); it('should print an error on duplicate keys with Components', () => { function App() { return ( a b d d ); } render(, scratch); expect(console.error).to.be.calledOnce; }); it('should print an error on duplicate keys with Fragments', () => { function App() { return ( a b {/* Should be okay to duplicate keys since these are inside a Fragment */} c d e f
    sibling
    ); } render(, scratch); expect(console.error).to.be.calledTwice; }); }); describe('table markup', () => { it('missing ///', () => { const Table = () => (
    ); render(
    hi
    , scratch); expect(console.error).to.be.calledOnce; }); it('missing
    with ', () => { const Table = () => (
    ); render(
    hi
    , scratch); expect(console.error).to.be.calledOnce; }); it('missing
    with ', () => { const Table = () => (
    ); render(
    hi
    , scratch); expect(console.error).to.be.calledOnce; }); it('missing
    with ', () => { const Table = () => (
    ); render(
    hi
    , scratch); expect(console.error).to.be.calledOnce; }); it('missing ', () => { const Table = () => (
    Hi
    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('missing with td component', () => { const Cell = ({ children }) => ; const Table = () => (
    {children}
    Hi
    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('missing with th component', () => { const Cell = ({ children }) => ; const Table = () => (
    {children}
    Hi
    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('Should accept ', () => { const Table = () => (
    instead of in
    Hi
    ); render(, scratch); expect(console.error).to.not.be.called; }); it('Accepts well formed table with TD components', () => { const Cell = ({ children }) => ; const Table = () => (
    {children}
    Body
    Head
    Body
    ); render(, scratch); expect(console.error).to.not.be.called; }); it('Accepts well formed table', () => { const Table = () => (
    Head
    Body
    Body
    ); render(, scratch); expect(console.error).to.not.be.called; }); it('Accepts minimal well formed table', () => { const Table = () => (
    Head
    Body
    ); render(, scratch); expect(console.error).to.not.be.called; }); it('should include DOM parents outside of root node', () => { const Table = () => ( ); const table = document.createElement('table'); scratch.appendChild(table); render(
    Head
    , table); expect(console.error).to.not.be.called; }); it('should warn for improper nested table', () => { const Table = () => (
    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('accepts valid nested tables', () => { const Table = () => (
    foo
    cell1 cell2 cell3
    bar
    ); render(, scratch); expect(console.error).to.not.be.called; }); }); describe('paragraph nesting', () => { it('should not warn a regular text paragraph', () => { const Paragraph = () =>

    Hello world

    ; render(, scratch); expect(console.error).to.not.be.called; }); it('should not crash for an empty pragraph', () => { const Paragraph = () =>

    ; render(, scratch); expect(console.error).to.not.be.called; }); it('should warn for nesting illegal dom-nodes under a paragraph', () => { const Paragraph = () => (

    Hello world

    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('should warn for nesting illegal dom-nodes under a paragraph with a parent', () => { const Paragraph = () => (

    Hello world

    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('should warn for nesting illegal dom-nodes under a paragraph as func', () => { const Title = ({ children }) =>

    {children}

    ; const Paragraph = () => (

    Hello world

    ); render(, scratch); expect(console.error).to.be.calledOnce; }); it('should not warn for nesting span under a paragraph', () => { const Paragraph = () => (

    Hello world

    ); render(, scratch); expect(console.error).to.not.be.called; }); }); describe('button nesting', () => { it('should not warn on a regular button', () => { const Button = () => ; render( ); render(; const Button = () => ( ); render( ); render( ); render(, scratch); expect(console.error).to.not.be.called; }); }); describe('Hydration mismatches', () => { it('Should warn us for a node mismatch', () => { scratch.innerHTML = '
    foo/div>'; const App = () => (

    foo

    ); hydrate(, scratch); expect(console.error).to.be.calledOnce; expect(console.error).to.be.calledOnceWith( sinon.match(/Expected a DOM node of type "p" but found "span"/) ); }); it('Should not warn for a text-node mismatch', () => { scratch.innerHTML = '
    foo bar baz/div>'; const App = () => (
    foo {'bar'} {'baz'}
    ); hydrate(, scratch); expect(console.error).to.not.be.called; }); it('Should not warn for a well-formed tree', () => { scratch.innerHTML = '
    foobar
    '; const App = () => (
    foo bar
    ); hydrate(, scratch); expect(console.error).to.not.be.called; }); }); }); preactjs-preact-389c7bc/debug/test/browser/fakeDevTools.js000066400000000000000000000000741523334003000236730ustar00rootroot00000000000000window.__PREACT_DEVTOOLS__ = { attachPreact: sinon.spy() }; preactjs-preact-389c7bc/debug/test/browser/prop-types.test.js000066400000000000000000000062171523334003000244120ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { setupScratch, teardown, serializeHtml } from '../../../test/_util/helpers'; import './fakeDevTools'; import { resetPropWarnings } from 'preact/debug'; import * as PropTypes from 'prop-types'; import { jsxDEV as jsxDev } from 'preact/jsx-runtime'; describe('PropTypes', () => { /** @type {HTMLDivElement} */ let scratch; let errors = []; let warnings = []; beforeEach(() => { errors = []; warnings = []; scratch = setupScratch(); sinon.stub(console, 'error').callsFake(e => errors.push(e)); sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { /** @type {*} */ console.error.restore(); console.warn.restore(); teardown(scratch); }); beforeEach(() => { resetPropWarnings(); }); it("should fail if props don't match prop-types", () => { function Foo(props) { return jsxDev('h1', { children: props.text }); } Foo.propTypes = { text: PropTypes.string.isRequired }; render( jsxDev( Foo, { text: 123 }, null, // @ts-ignore false, // @ts-ignore { fileName: './debug/test/browser/debug.test.js', lineNumber: 41 }, // @ts-ignore 'self' ), scratch ); expect(console.error).to.be.calledOnce; // The message here may change when the "prop-types" library is updated, // but we check it exactly to make sure all parameters were supplied // correctly. expect(console.error).to.have.been.calledOnceWith( sinon.match( /^Failed prop type: Invalid prop `text` of type `number` supplied to `Foo`, expected `string`\.\n {2}in Foo \(at (.*)[/\\]debug[/\\]test[/\\]browser[/\\]debug\.test\.js:[0-9]+\)$/m ) ); }); it('should only log a given prop type error once', () => { function Foo(props) { return jsxDev('h1', { children: props.text }); } Foo.propTypes = { text: PropTypes.string.isRequired, count: PropTypes.number }; // Trigger the same error twice. The error should only be logged // once. render(jsxDev(Foo, { text: 123 }), scratch); render(jsxDev(Foo, { text: 123 }), scratch); expect(console.error).to.be.calledOnce; // Trigger a different error. This should result in a new log // message. console.error.resetHistory(); render(jsxDev(Foo, { text: 'ok', count: '123' }), scratch); expect(console.error).to.be.calledOnce; }); it('should render with error logged when validator gets signal and throws exception', () => { function Baz(props) { return jsxDev('h1', { children: props.unhappy }); } Baz.propTypes = { unhappy: function alwaysThrows(obj, key) { if (obj[key] === 'signal') throw Error('got prop'); } }; render(jsxDev(Baz, { unhappy: 'signal' }), scratch); expect(console.error).to.be.calledOnce; expect(errors[0].includes('got prop')).to.equal(true); expect(serializeHtml(scratch)).to.equal('

    signal

    '); }); it('should not print to console when types are correct', () => { function Bar(props) { return jsxDev('h1', { children: props.text }); } Bar.propTypes = { text: PropTypes.string.isRequired }; render(jsxDev(Bar, { text: 'foo' }), scratch); expect(console.error).to.not.be.called; }); }); preactjs-preact-389c7bc/debug/test/browser/serializeVNode.test.jsx000066400000000000000000000032701523334003000253770ustar00rootroot00000000000000import { createElement, Component } from 'preact'; import { serializeVNode } from '../../src/debug'; /** @jsx createElement */ describe('serializeVNode', () => { it("should prefer a function component's displayName", () => { function Foo() { return
    ; } Foo.displayName = 'Bar'; expect(serializeVNode()).to.equal(''); }); it("should prefer a class component's displayName", () => { class Bar extends Component { render() { return
    ; } } Bar.displayName = 'Foo'; expect(serializeVNode()).to.equal(''); }); it('should serialize vnodes without children', () => { expect(serializeVNode(
    )).to.equal('
    '); }); it('should serialize vnodes with children', () => { expect(serializeVNode(
    Hello World
    )).to.equal('
    ..
    '); }); it('should serialize components', () => { function Foo() { return
    ; } expect(serializeVNode()).to.equal(''); }); it('should serialize props', () => { expect(serializeVNode(
    )).to.equal('
    '); // Ensure that we have a predictable function name. Our test runner // creates an all inclusive bundle per file and the identifier // "noop" may have already been used. // eslint-disable-next-line func-style let noop = function noopFn() {}; expect(serializeVNode(
    )).to.equal( '
    ' ); function Foo(props) { return props.foo; } expect(serializeVNode()).to.equal( '' ); expect(serializeVNode(
    )).to.equal( '
    ' ); }); }); preactjs-preact-389c7bc/debug/test/browser/validateHookArgs.test.jsx000066400000000000000000000041351523334003000257040ustar00rootroot00000000000000import { createElement, render, createRef } from 'preact'; import { useState, useEffect, useLayoutEffect, useCallback, useMemo, useImperativeHandle } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import 'preact/debug'; /** @jsx createElement */ describe('Hook argument validation', () => { /** * @param {string} name * @param {(arg: number) => void} hook */ function validateHook(name, hook) { const TestComponent = ({ initialValue }) => { const [value, setValue] = useState(initialValue); hook(value); return ( ); }; it(`should error if ${name} is mounted with NaN as an argument`, async () => { render(, scratch); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match( /Hooks should not be called with NaN in the dependency array/ ); }); it(`should error if ${name} is updated with NaN as an argument`, async () => { render(, scratch); scratch.querySelector('button').click(); rerender(); expect(console.warn).to.be.calledOnce; expect(console.warn.args[0]).to.match( /Hooks should not be called with NaN in the dependency array/ ); }); } /** @type {HTMLElement} */ let scratch; /** @type {() => void} */ let rerender; let warnings = []; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); warnings = []; sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); }); afterEach(() => { teardown(scratch); console.warn.restore(); }); validateHook('useEffect', arg => useEffect(() => {}, [arg])); validateHook('useLayoutEffect', arg => useLayoutEffect(() => {}, [arg])); validateHook('useCallback', arg => useCallback(() => {}, [arg])); validateHook('useMemo', arg => useMemo(() => {}, [arg])); const ref = createRef(); validateHook('useImperativeHandle', arg => { useImperativeHandle(ref, () => undefined, [arg]); }); }); preactjs-preact-389c7bc/demo/000077500000000000000000000000001523334003000161425ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/contenteditable.jsx000066400000000000000000000010111523334003000220250ustar00rootroot00000000000000import { useState } from 'preact/hooks'; export default function Contenteditable() { const [value, setValue] = useState("Hey there
    I'm editable!"); return (
    setValue(e.currentTarget.innerHTML)} dangerouslySetInnerHTML={{ __html: value }} />
    ); } preactjs-preact-389c7bc/demo/context.jsx000066400000000000000000000023701523334003000203560ustar00rootroot00000000000000// eslint-disable-next-line no-unused-vars import { Component, createContext } from 'preact'; const { Provider, Consumer } = createContext(); class ThemeProvider extends Component { state = { value: this.props.value }; onClick = () => { this.setState(prev => ({ value: prev.value === this.props.value ? this.props.next : this.props.value })); }; render() { return (
    {this.props.children}
    ); } } class Child extends Component { shouldComponentUpdate() { return false; } render() { return ( <>

    (blocked update)

    {this.props.children} ); } } export default class ContextDemo extends Component { render() { return ( {data => (

    current theme: {data}

    {data => (

    current sub theme: {data}

    )}
    )}
    ); } } preactjs-preact-389c7bc/demo/devtools.jsx000066400000000000000000000012001523334003000205200ustar00rootroot00000000000000import { Component, memo, Suspense, lazy } from 'react'; function Foo() { return
    I'm memoed
    ; } function LazyComp() { return
    I'm (fake) lazy loaded
    ; } const Lazy = lazy(() => Promise.resolve({ default: LazyComp })); const Memoed = memo(Foo); export default class DevtoolsDemo extends Component { render() { return (

    memo()

    functional component:

    lazy()

    functional component:

    Loading (fake) lazy loaded component...
    }>
    ); } } preactjs-preact-389c7bc/demo/fragments.jsx000066400000000000000000000007101523334003000206540ustar00rootroot00000000000000import { Component } from 'preact'; export default class FragmentComp extends Component { state = { number: 0 }; componentDidMount() { setInterval(_ => this.updateChildren(), 1000); } updateChildren() { this.setState(state => ({ number: state.number + 1 })); } render(props, state) { return (
    {state.number}
    <>
    one
    {state.number}
    three
    ); } } preactjs-preact-389c7bc/demo/index.html000066400000000000000000000005241523334003000201400ustar00rootroot00000000000000 Preact Demo
    preactjs-preact-389c7bc/demo/index.jsx000066400000000000000000000127431523334003000200060ustar00rootroot00000000000000import { render, Component, Fragment } from 'preact'; // import renderToString from 'preact-render-to-string'; import './style.scss'; import { Router, Link } from 'preact-router'; import Pythagoras from './pythagoras'; import Spiral from './spiral'; import Reorder from './reorder'; import Todo from './todo'; import Fragments from './fragments'; import Context from './context'; import installLogger from './logger'; import ProfilerDemo from './profiler'; import KeyBug from './key_bug'; import StateOrderBug from './stateOrderBug'; import PeopleBrowser from './people'; import StyledComp from './styled-components'; import { initDevTools } from 'preact/devtools/src/devtools'; import { initDebug } from 'preact/debug/src/debug'; import DevtoolsDemo from './devtools'; import SuspenseDemo from './suspense'; import Redux from './redux'; import TextFields from './textFields'; import ReduxBug from './reduxUpdate'; import SuspenseRouterBug from './suspense-router'; import NestedSuspenseBug from './nested-suspense'; import Contenteditable from './contenteditable'; import { MobXDemo } from './mobx'; import Zustand from './zustand'; import ReduxToolkit from './redux-toolkit'; let isBenchmark = /(\/spiral|\/pythagoras|[#&]bench)/g.test( window.location.href ); if (!isBenchmark) { // eslint-disable-next-line no-console console.log('Enabling devtools and debug'); initDevTools(); initDebug(); } // mobx-state-tree fix window.setImmediate = setTimeout; class Home extends Component { render() { return (

    Hello

    ); } } class DevtoolsWarning extends Component { onClick = () => { window.location.reload(); }; render() { return ( ); } } class App extends Component { render({ url }) { return (
    {!isBenchmark ? : }
    {!isBenchmark ? : }
    ); } } function EmptyFragment() { return ; } // document.body.innerHTML = renderToString(); // document.body.firstChild.setAttribute('is-ssr', 'true'); installLogger( String(localStorage.LOG) === 'true' || location.href.match(/logger/), String(localStorage.CONSOLE) === 'true' || location.href.match(/console/) ); render(, document.body); preactjs-preact-389c7bc/demo/key_bug.jsx000066400000000000000000000010731523334003000203160ustar00rootroot00000000000000import { Component } from 'preact'; function Foo(props) { return
    This is: {props.children}
    ; } export default class KeyBug extends Component { constructor() { super(); this.onClick = this.onClick.bind(this); this.state = { active: false }; } onClick() { this.setState(prev => ({ active: !prev.active })); } render() { return (
    {this.state.active && foo}

    Hello World


    bar bar
    ); } } preactjs-preact-389c7bc/demo/list.jsx000066400000000000000000000026341523334003000176500ustar00rootroot00000000000000import { h, render } from 'preact'; import htm from 'htm'; import './style.css'; const html = htm.bind(h); const createRoot = parent => ({ render: v => render(v, parent) }); function List({ items, renders, useKeys, useCounts, update }) { const toggleKeys = () => update({ useKeys: !useKeys }); const toggleCounts = () => update({ useCounts: !useCounts }); const swap = () => { const u = { items: items.slice() }; u.items[1] = items[8]; u.items[8] = items[1]; update(u); }; return html`
      ${items.map( (item, i) => html`
    • ${item.name} ${useCounts ? ` (${renders} renders)` : ''}
    • ` )}
    `; } const root = createRoot(document.body); let data = { items: new Array(1000).fill(null).map((x, i) => ({ name: `Item ${i + 1}` })), renders: 0, useKeys: false, useCounts: false }; function update(partial) { if (partial) Object.assign(data, partial); data.renders++; data.update = update; root.render(List(data)); } update(); preactjs-preact-389c7bc/demo/logger.jsx000066400000000000000000000100361523334003000201470ustar00rootroot00000000000000export default function logger(logStats, logConsole) { if (!logStats && !logConsole) { return; } const consoleBuffer = new ConsoleBuffer(); let calls = {}; let lock = true; function serialize(obj) { if (obj instanceof Text) return '#text'; if (obj instanceof Element) return `<${obj.localName}>`; if (obj === document) return 'document'; return Object.prototype.toString.call(obj).replace(/(^\[object |\]$)/g, ''); } function count(key) { if (lock === true) return; calls[key] = (calls[key] || 0) + 1; if (logConsole) { consoleBuffer.log(key); } } function logCall(obj, method, name) { let old = obj[method]; obj[method] = function () { let c = ''; for (let i = 0; i < arguments.length; i++) { if (c) c += ', '; c += serialize(arguments[i]); } count(`${serialize(this)}.${method}(${c})`); return old.apply(this, arguments); }; } logCall(document, 'createElement'); logCall(document, 'createElementNS'); logCall(Element.prototype, 'remove'); logCall(Element.prototype, 'appendChild'); logCall(Element.prototype, 'removeChild'); logCall(Element.prototype, 'insertBefore'); logCall(Element.prototype, 'replaceChild'); logCall(Element.prototype, 'setAttribute'); logCall(Element.prototype, 'setAttributeNS'); logCall(Element.prototype, 'removeAttribute'); logCall(Element.prototype, 'removeAttributeNS'); let d = Object.getOwnPropertyDescriptor(CharacterData.prototype, 'data') || Object.getOwnPropertyDescriptor(Node.prototype, 'data'); Object.defineProperty(Text.prototype, 'data', { get() { let value = d.get.call(this); count(`get #text.data`); return value; }, set(v) { count(`set #text.data`); return d.set.call(this, v); } }); let root; function setup() { if (!logStats) return; lock = true; root = document.createElement('table'); root.style.cssText = 'position: fixed; right: 0; top: 0; z-index:999; background: #000; font-size: 12px; color: #FFF; opacity: 0.9; white-space: nowrap;'; let header = document.createElement('thead'); header.innerHTML = '
    '; root.tableBody = document.createElement('tbody'); root.appendChild(root.tableBody); root.appendChild(header); document.documentElement.appendChild(root); let btn = document.getElementById('clear-logs'); btn.addEventListener('click', () => { for (let key in calls) { calls[key] = 0; } }); lock = false; } let rows = {}; function createRow(id) { let row = document.createElement('tr'); row.key = document.createElement('td'); row.key.textContent = id; row.appendChild(row.key); row.value = document.createElement('td'); row.value.textContent = ' '; row.appendChild(row.value); root.tableBody.appendChild(row); return (rows[id] = row); } function insertInto(parent) { parent.appendChild(root); } function remove() { clearInterval(updateTimer); } function update() { if (!logStats) return; lock = true; for (let i in calls) { if (calls.hasOwnProperty(i)) { let row = rows[i] || createRow(i); row.value.firstChild.nodeValue = calls[i]; } } lock = false; } let updateTimer = setInterval(update, 50); setup(); lock = false; return { insertInto, update, remove }; } /** * Logging to the console significantly affects performance. * Buffer calls to console and replay them at the end of the * current stack * @extends {Console} */ class ConsoleBuffer { constructor() { /** @type {Array<[string, any[]]>} */ this.buffer = []; this.deferred = null; for (let methodName of Object.keys(console)) { this[methodName] = this.proxy(methodName); } } proxy(methodName) { return (...args) => { this.buffer.push([methodName, args]); this.deferFlush(); }; } deferFlush() { if (this.deferred == null) { this.deferred = Promise.resolve() .then(() => this.flush()) .then(() => (this.deferred = null)); } } flush() { let method; while ((method = this.buffer.shift())) { let [name, args] = method; console[name](...args); } } } preactjs-preact-389c7bc/demo/mobx.jsx000066400000000000000000000026131523334003000176370ustar00rootroot00000000000000import React, { forwardRef, useRef, useState } from 'react'; import { decorate, observable } from 'mobx'; import { observer, useObserver } from 'mobx-react'; import 'mobx-react-lite/batchingForReactDom'; class Todo { constructor() { this.id = Math.random(); this.title = 'initial'; this.finished = false; } } decorate(Todo, { title: observable, finished: observable }); const Forward = observer( // eslint-disable-next-line react/display-name forwardRef(({ todo }, ref) => { return (

    Forward: "{todo.title}" {'' + todo.finished}

    ); }) ); const todo = new Todo(); const TodoView = observer(({ todo }) => { return (

    Todo View: "{todo.title}" {'' + todo.finished}

    ); }); const HookView = ({ todo }) => { return useObserver(() => { return (

    Todo View: "{todo.title}" {'' + todo.finished}

    ); }); }; export function MobXDemo() { const ref = useRef(null); let [v, set] = useState(0); const success = ref.current && ref.current.nodeName === 'P'; return (
    { todo.title = e.target.value; set(v + 1); }} />

    {success ? 'SUCCESS' : 'FAIL'}

    ); } preactjs-preact-389c7bc/demo/nested-suspense/000077500000000000000000000000001523334003000212675ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/nested-suspense/addnewcomponent.jsx000066400000000000000000000002421523334003000252000ustar00rootroot00000000000000import { createElement } from 'react'; export default function AddNewComponent({ appearance }) { return
    AddNewComponent (component #{appearance})
    ; } preactjs-preact-389c7bc/demo/nested-suspense/component-container.jsx000066400000000000000000000006061523334003000260010ustar00rootroot00000000000000import { lazy } from 'react'; const pause = timeout => new Promise(d => setTimeout(d, timeout), console.log(timeout)); const SubComponent = lazy(() => pause(Math.random() * 1000).then(() => import('./subcomponent.jsx')) ); export default function ComponentContainer({ appearance }) { return (
    GenerateComponents (component #{appearance})
    ); } preactjs-preact-389c7bc/demo/nested-suspense/dropzone.jsx000066400000000000000000000002241523334003000236530ustar00rootroot00000000000000import { createElement } from 'react'; export default function DropZone({ appearance }) { return
    DropZone (component #{appearance})
    ; } preactjs-preact-389c7bc/demo/nested-suspense/editor.jsx000066400000000000000000000002131523334003000232770ustar00rootroot00000000000000import { createElement } from 'react'; export default function Editor({ children }) { return
    {children}
    ; } preactjs-preact-389c7bc/demo/nested-suspense/index.jsx000066400000000000000000000032461523334003000231310ustar00rootroot00000000000000import { createElement, Suspense, lazy, Component } from 'react'; const Loading = function () { return
    Loading...
    ; }; const Error = function ({ resetState }) { return (
    Error!  Reset app
    ); }; const pause = timeout => new Promise(d => setTimeout(d, timeout), console.log(timeout)); const DropZone = lazy(() => pause(Math.random() * 1000).then(() => import('./dropzone.jsx')) ); const Editor = lazy(() => pause(Math.random() * 1000).then(() => import('./editor.jsx')) ); const AddNewComponent = lazy(() => pause(Math.random() * 1000).then(() => import('./addnewcomponent.jsx')) ); const GenerateComponents = lazy(() => pause(Math.random() * 1000).then(() => import('./component-container.jsx')) ); export default class App extends Component { state = { hasError: false }; static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. console.warn(error); return { hasError: true }; } render() { return this.state.hasError ? ( this.setState({ hasError: false })} /> ) : ( }>
    }>
    Footer here
    ); } } preactjs-preact-389c7bc/demo/nested-suspense/subcomponent.jsx000066400000000000000000000002141523334003000245260ustar00rootroot00000000000000import { createElement } from 'react'; export default function SubComponent({ onClick }) { return
    Lazy loaded sub component
    ; } preactjs-preact-389c7bc/demo/old.js.bak000066400000000000000000000042471523334003000200210ustar00rootroot00000000000000 // function createRoot(title) { // let div = document.createElement('div'); // let h2 = document.createElement('h2'); // h2.textContent = title; // div.appendChild(h2); // document.body.appendChild(div); // return div; // } /* function logCall(obj, method, name) { let old = obj[method]; obj[method] = function(...args) { console.log(`<${this.localName}>.`+(name || `${method}(${args})`)); return old.apply(this, args); }; } logCall(HTMLElement.prototype, 'appendChild'); logCall(HTMLElement.prototype, 'removeChild'); logCall(HTMLElement.prototype, 'insertBefore'); logCall(HTMLElement.prototype, 'replaceChild'); logCall(HTMLElement.prototype, 'setAttribute'); logCall(HTMLElement.prototype, 'removeAttribute'); let d = Object.getOwnPropertyDescriptor(Node.prototype, 'nodeValue'); Object.defineProperty(Text.prototype, 'nodeValue', { get() { let value = d.get.call(this); console.log('get Text#nodeValue: ', value); return value; }, set(v) { console.log('set Text#nodeValue', v); return d.set.call(this, v); } }); render((

    This is a test.

    ), createRoot('Stateful component update demo:')); class Foo extends Component { componentDidMount() { console.log('mounted'); this.timer = setInterval( () => { this.setState({ time: Date.now() }); }, 5000); } componentWillUnmount() { clearInterval(this.timer); } render(props, state, context) { // console.log('rendering', props, state, context); return } } render((

    This is a test.

    ), createRoot('Stateful component update demo:')); let items = []; let count = 0; let three = createRoot('Top-level render demo:'); setInterval( () => { if (count++ %20 < 10 ) { items.push(
  • item #{items.length}
  • ); } else { items.shift(); } render((

    This is a test.

      {items}
    ), three); }, 5000); // Mount the top-level component to the DOM: render(
    , document.body); */ preactjs-preact-389c7bc/demo/package-lock.json000066400000000000000000003147261523334003000213730ustar00rootroot00000000000000{ "name": "demo", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "demo", "dependencies": { "@material-ui/core": "4.9.5", "@reduxjs/toolkit": "^2.2.3", "d3-scale": "^1.0.7", "d3-selection": "^1.2.0", "htm": "2.1.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "mobx-state-tree": "^3.16.0", "preact-render-to-string": "^5.0.2", "preact-router": "^3.0.0", "react-redux": "^7.1.0", "react-router": "^5.0.1", "react-router-dom": "^5.0.1", "redux": "^4.0.1", "styled-components": "^4.2.0", "zustand": "^4.5.2" }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.0.0-beta.55", "@babel/plugin-proposal-decorators": "^7.4.0", "vite": "^5.4.10" } }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dependencies": { "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", "dev": true, "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", "dev": true, "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.18.6", "@babel/helper-compilation-targets": "^7.18.6", "@babel/helper-module-transforms": "^7.18.6", "@babel/helpers": "^7.18.6", "@babel/parser": "^7.18.6", "@babel/template": "^7.18.6", "@babel/traverse": "^7.18.6", "@babel/types": "^7.18.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.1", "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/babel" } }, "node_modules/@babel/generator": { "version": "7.18.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", "dependencies": { "@babel/types": "^7.18.7", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", "dev": true, "peer": true, "dependencies": { "@babel/compat-data": "^7.18.6", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz", "integrity": "sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.6", "@babel/helper-function-name": "^7.18.6", "@babel/helper-member-expression-to-functions": "^7.18.6", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-replace-supers": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", "dependencies": { "@babel/template": "^7.18.6", "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz", "integrity": "sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng==", "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz", "integrity": "sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA==", "dev": true, "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", "@babel/template": "^7.18.6", "@babel/traverse": "^7.18.8", "@babel/types": "^7.18.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz", "integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz", "integrity": "sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.6", "@babel/helper-member-expression-to-functions": "^7.18.6", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/traverse": "^7.18.6", "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, "peer": true, "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dependencies": { "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true, "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", "dev": true, "peer": true, "dependencies": { "@babel/template": "^7.18.6", "@babel/traverse": "^7.18.6", "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.8.tgz", "integrity": "sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA==", "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-decorators": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.6.tgz", "integrity": "sha512-gAdhsjaYmiZVxx5vTMiRfj31nB7LhwBJFMSLzeDxc7X4tKLixup0+k9ughn0RcpBrv9E3PBaXJW7jF5TCihAOg==", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6", "@babel/helper-replace-supers": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/plugin-syntax-decorators": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-decorators": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/runtime": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==", "dependencies": { "regenerator-runtime": "^0.13.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.18.6", "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.8.tgz", "integrity": "sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg==", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.18.7", "@babel/helper-environment-visitor": "^7.18.6", "@babel/helper-function-name": "^7.18.6", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/parser": "^7.18.8", "@babel/types": "^7.18.8", "debug": "^4.1.0", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.8.tgz", "integrity": "sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw==", "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", "dependencies": { "@emotion/memoize": "0.7.4" } }, "node_modules/@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" }, "node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, "optional": true, "os": [ "aix" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/android-arm": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, "optional": true, "os": [ "android" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/android-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "android" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/android-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "android" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/darwin-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "darwin" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/darwin-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "darwin" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/freebsd-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "freebsd" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/freebsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "freebsd" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-arm": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-ia32": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-loong64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-mips64el": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-riscv64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-s390x": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "linux" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "netbsd" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "openbsd" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/sunos-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "sunos" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "win32" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-ia32": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, "optional": true, "os": [ "win32" ], "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "win32" ], "engines": { "node": ">=12" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, "peer": true, "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, "optional": true, "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "optional": true, "peer": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.14", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@material-ui/core": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.5.tgz", "integrity": "sha512-hVuUqw6847jcgRsUqzCiYCXcIJYhPUfM3gS9sNehTsbI0SF3tufLNO2B2Cgkuns8uOGy0nicD4p3L7JqhnEElg==", "deprecated": "You can now upgrade to @mui/material. See the guide: https://mui.com/guides/migration-v4/", "dependencies": { "@babel/runtime": "^7.4.4", "@material-ui/styles": "^4.9.0", "@material-ui/system": "^4.9.3", "@material-ui/types": "^5.0.0", "@material-ui/utils": "^4.7.1", "@types/react-transition-group": "^4.2.0", "clsx": "^1.0.2", "hoist-non-react-statics": "^3.3.2", "popper.js": "^1.14.1", "prop-types": "^15.7.2", "react-is": "^16.8.0", "react-transition-group": "^4.3.0" }, "engines": { "node": ">=8.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/material-ui" }, "peerDependencies": { "@types/react": "^16.8.6", "react": "^16.8.0", "react-dom": "^16.8.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true } } }, "node_modules/@material-ui/styles": { "version": "4.11.5", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", "dependencies": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.8.0", "@material-ui/types": "5.1.0", "@material-ui/utils": "^4.11.3", "clsx": "^1.0.4", "csstype": "^2.5.2", "hoist-non-react-statics": "^3.3.2", "jss": "^10.5.1", "jss-plugin-camel-case": "^10.5.1", "jss-plugin-default-unit": "^10.5.1", "jss-plugin-global": "^10.5.1", "jss-plugin-nested": "^10.5.1", "jss-plugin-props-sort": "^10.5.1", "jss-plugin-rule-value-function": "^10.5.1", "jss-plugin-vendor-prefixer": "^10.5.1", "prop-types": "^15.7.2" }, "engines": { "node": ">=8.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/material-ui" }, "peerDependencies": { "@types/react": "^16.8.6 || ^17.0.0", "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true } } }, "node_modules/@material-ui/system": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", "dependencies": { "@babel/runtime": "^7.4.4", "@material-ui/utils": "^4.11.3", "csstype": "^2.5.2", "prop-types": "^15.7.2" }, "engines": { "node": ">=8.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/material-ui" }, "peerDependencies": { "@types/react": "^16.8.6 || ^17.0.0", "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true } } }, "node_modules/@material-ui/types": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", "peerDependencies": { "@types/react": "*" }, "peerDependenciesMeta": { "@types/react": { "optional": true } } }, "node_modules/@material-ui/utils": { "version": "4.11.3", "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", "dependencies": { "@babel/runtime": "^7.4.4", "prop-types": "^15.7.2", "react-is": "^16.8.0 || ^17.0.0" }, "engines": { "node": ">=8.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0" } }, "node_modules/@reduxjs/toolkit": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.3.tgz", "integrity": "sha512-76dll9EnJXg4EVcI5YNxZA/9hSAmZsFqzMmNRHvIlzw2WS/twfcVX3ysYrWGJMClwEmChQFC4yRq74tn6fdzRA==", "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.0.1" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "peerDependenciesMeta": { "react": { "optional": true }, "react-redux": { "optional": true } } }, "node_modules/@reduxjs/toolkit/node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" }, "node_modules/@reduxjs/toolkit/node_modules/redux-thunk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", "peerDependencies": { "redux": "^5.0.0" } }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz", "integrity": "sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA==", "cpu": [ "arm" ], "dev": true, "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz", "integrity": "sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz", "integrity": "sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz", "integrity": "sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz", "integrity": "sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz", "integrity": "sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz", "integrity": "sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA==", "cpu": [ "arm" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz", "integrity": "sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ==", "cpu": [ "arm" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz", "integrity": "sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz", "integrity": "sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz", "integrity": "sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA==", "cpu": [ "ppc64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz", "integrity": "sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA==", "cpu": [ "riscv64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz", "integrity": "sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ==", "cpu": [ "s390x" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz", "integrity": "sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz", "integrity": "sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz", "integrity": "sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz", "integrity": "sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg==", "cpu": [ "ia32" ], "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz", "integrity": "sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, "node_modules/@types/node": { "version": "18.0.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.4.tgz", "integrity": "sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==", "dev": true, "optional": true, "peer": true }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "16.14.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.28.tgz", "integrity": "sha512-83zBE6+XUVXsdL3iFzOyUewdauaU+KviKCHEGOgSW52coAuqW7tEKQM0E9+ZC0Zk6TELQ2/JgogPvp7FavzFwg==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-redux": { "version": "7.1.24", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", "dependencies": { "@types/hoist-non-react-statics": "^3.3.0", "@types/react": "*", "hoist-non-react-statics": "^3.3.0", "redux": "^4.0.0" } }, "node_modules/@types/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react/node_modules/csstype": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "node_modules/acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "optional": true, "peer": true, "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "optional": true, "peer": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" }, "engines": { "node": ">= 8" } }, "node_modules/babel-plugin-styled-components": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz", "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.16.0", "@babel/helper-module-imports": "^7.16.0", "babel-plugin-syntax-jsx": "^6.18.0", "lodash": "^4.17.11", "picomatch": "^2.3.0" }, "peerDependencies": { "styled-components": ">= 2" } }, "node_modules/babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "optional": true, "peer": true, "engines": { "node": ">=8" } }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "optional": true, "peer": true, "dependencies": { "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz", "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" } ], "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001366", "electron-to-chromium": "^1.4.188", "node-releases": "^2.0.6", "update-browserslist-db": "^1.0.4" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "optional": true, "peer": true }, "node_modules/camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==" }, "node_modules/caniuse-lite": { "version": "1.0.30001366", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz", "integrity": "sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } ], "peer": true }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { "type": "individual", "url": "https://paulmillr.com/funding/" } ], "optional": true, "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "engines": { "node": ">=6" } }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "peer": true, "dependencies": { "safe-buffer": "~5.1.1" } }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", "engines": { "node": ">=4" } }, "node_modules/css-to-react-native": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz", "integrity": "sha512-VOFaeZA053BqvvvqIA8c9n0+9vFppVBAHCp6JgFTtTMU3Mzi+XnelJ9XC9ul3BqFzZyQ5N+H0SnwsWT2Ebchxw==", "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", "postcss-value-parser": "^3.3.0" } }, "node_modules/css-to-react-native/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, "node_modules/css-vendor": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", "dependencies": { "@babel/runtime": "^7.8.3", "is-in-browser": "^1.0.2" } }, "node_modules/csstype": { "version": "2.6.20", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" }, "node_modules/d3-array": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" }, "node_modules/d3-collection": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" }, "node_modules/d3-color": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, "node_modules/d3-format": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" }, "node_modules/d3-interpolate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", "dependencies": { "d3-color": "1" } }, "node_modules/d3-scale": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", "dependencies": { "d3-array": "^1.2.0", "d3-collection": "1", "d3-color": "1", "d3-format": "1", "d3-interpolate": "1", "d3-time": "1", "d3-time-format": "2" } }, "node_modules/d3-selection": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" }, "node_modules/d3-time": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" }, "node_modules/d3-time-format": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", "dependencies": { "d3-time": "1" } }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "node_modules/dom-helpers/node_modules/csstype": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, "node_modules/electron-to-chromium": { "version": "1.4.189", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.189.tgz", "integrity": "sha512-dQ6Zn4ll2NofGtxPXaDfY2laIa6NyCQdqXYHdwH90GJQW0LpJJib0ZU/ERtbb0XkBEmUD2eJtagbOie3pdMiPg==", "dev": true, "peer": true }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { "node": ">=12" }, "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "peer": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { "node": ">=0.8.0" } }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "optional": true, "peer": true, "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "optional": true, "peer": true, "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "engines": { "node": ">=4" } }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { "node": ">=4" } }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", "dependencies": { "@babel/runtime": "^7.1.2", "loose-envify": "^1.2.0", "resolve-pathname": "^3.0.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0", "value-equal": "^1.0.1" } }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { "react-is": "^16.7.0" } }, "node_modules/htm": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/htm/-/htm-2.1.1.tgz", "integrity": "sha512-jgvB9nvlOE5xpI1W+juW8DPCe+Pn7mRDNaE4EOQNFqROm0O3l5deOvefK8C8hUaL1OHIX45mDEBSj7BMt+22VQ==", "peerDependencies": { "preact": "*" } }, "node_modules/hyphenate-style-name": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" }, "node_modules/immer": { "version": "10.0.4", "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.4.tgz", "integrity": "sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" } }, "node_modules/immutable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", "dev": true, "optional": true, "peer": true }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "optional": true, "peer": true, "dependencies": { "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "optional": true, "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "optional": true, "peer": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "optional": true, "peer": true, "engines": { "node": ">=0.12.0" } }, "node_modules/is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "bin": { "jsesc": "bin/jsesc" }, "engines": { "node": ">=4" } }, "node_modules/json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, "peer": true, "bin": { "json5": "lib/cli.js" }, "engines": { "node": ">=6" } }, "node_modules/jss": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz", "integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==", "dependencies": { "@babel/runtime": "^7.3.1", "csstype": "^3.0.2", "is-in-browser": "^1.1.3", "tiny-warning": "^1.0.2" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/jss" } }, "node_modules/jss-plugin-camel-case": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz", "integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==", "dependencies": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", "jss": "10.9.0" } }, "node_modules/jss-plugin-default-unit": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz", "integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==", "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.9.0" } }, "node_modules/jss-plugin-global": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz", "integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==", "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.9.0" } }, "node_modules/jss-plugin-nested": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz", "integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==", "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.9.0", "tiny-warning": "^1.0.2" } }, "node_modules/jss-plugin-props-sort": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz", "integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==", "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.9.0" } }, "node_modules/jss-plugin-rule-value-function": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz", "integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==", "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.9.0", "tiny-warning": "^1.0.2" } }, "node_modules/jss-plugin-vendor-prefixer": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz", "integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==", "dependencies": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.8", "jss": "10.9.0" } }, "node_modules/jss/node_modules/csstype": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" }, "node_modules/merge-anything": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-2.4.4.tgz", "integrity": "sha512-l5XlriUDJKQT12bH+rVhAHjwIuXWdAIecGwsYjv2LJo+dA1AeRTmeQS+3QBpO6lEthBMDi2IUMpLC1yyRvGlwQ==", "dependencies": { "is-what": "^3.3.1" } }, "node_modules/mini-create-react-context": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", "dependencies": { "@babel/runtime": "^7.12.1", "tiny-warning": "^1.0.3" }, "peerDependencies": { "prop-types": "^15.0.0", "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/mobx": { "version": "5.15.7", "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.7.tgz", "integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" } }, "node_modules/mobx-react": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-6.3.1.tgz", "integrity": "sha512-IOxdJGnRSNSJrL2uGpWO5w9JH5q5HoxEqwOF4gye1gmZYdjoYkkMzSGMDnRCUpN/BNzZcFoMdHXrjvkwO7KgaQ==", "dependencies": { "mobx-react-lite": "^2.2.0" }, "peerDependencies": { "mobx": "^5.15.4 || ^4.15.4", "react": "^16.8.0 || 16.9.0-alpha.0" } }, "node_modules/mobx-react-lite": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-2.2.2.tgz", "integrity": "sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg==", "peerDependencies": { "mobx": "^4.0.0 || ^5.0.0", "react": "^16.8.0" }, "peerDependenciesMeta": { "react-dom": { "optional": true }, "react-native": { "optional": true } } }, "node_modules/mobx-state-tree": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/mobx-state-tree/-/mobx-state-tree-3.17.3.tgz", "integrity": "sha512-ph4ee/Lh1qUJqHEGkfdWdBAUGdG+VAu7xZbYX/+4qem5hSSpdeZYAJOcN3bhtgEH8Wh/ZxRpQVOLM0aMFXfBSw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" }, "peerDependencies": { "mobx": ">=4.8.0 <5.0.0 || >=5.8.0 <6.0.0" } }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true, "peer": true }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "optional": true, "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, "node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dependencies": { "isarray": "0.0.1" } }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/postcss": { "version": "8.4.48", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.48.tgz", "integrity": "sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/preact": { "version": "10.10.0", "resolved": "https://registry.npmjs.org/preact/-/preact-10.10.0.tgz", "integrity": "sha512-fszkg1iJJjq68I4lI8ZsmBiaoQiQHbxf1lNq+72EmC/mZOsFF5zn3k1yv9QGoFgIXzgsdSKtYymLJsrJPoamjQ==", "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, "node_modules/preact-render-to-string": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.1.tgz", "integrity": "sha512-Wp3ner1aIVBpKg02C4AoLdBiw4kNaiFSYHr4wUF+fR7FWKAQzNri+iPfPp31sEhAtBfWoJrSxiEFzd5wp5zCgQ==", "dependencies": { "pretty-format": "^3.8.0" }, "peerDependencies": { "preact": ">=10" } }, "node_modules/preact-router": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/preact-router/-/preact-router-3.2.1.tgz", "integrity": "sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==", "peerDependencies": { "preact": ">=10" } }, "node_modules/pretty-format": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "node_modules/react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", "scheduler": "^0.19.1" }, "peerDependencies": { "react": "^16.14.0" } }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-redux": { "version": "7.2.8", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz", "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==", "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", "react-is": "^17.0.2" }, "peerDependencies": { "react": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "react-dom": { "optional": true }, "react-native": { "optional": true } } }, "node_modules/react-redux/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-router": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.3.tgz", "integrity": "sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==", "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", "mini-create-react-context": "^0.4.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.2", "react-is": "^16.6.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" }, "peerDependencies": { "react": ">=15" } }, "node_modules/react-router-dom": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.3.tgz", "integrity": "sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==", "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", "react-router": "5.3.3", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" }, "peerDependencies": { "react": ">=15" } }, "node_modules/react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "optional": true, "peer": true, "dependencies": { "picomatch": "^2.2.1" }, "engines": { "node": ">=8.10.0" } }, "node_modules/redux": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", "dependencies": { "@babel/runtime": "^7.9.2" } }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "node_modules/reselect": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz", "integrity": "sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg==" }, "node_modules/resolve-pathname": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, "node_modules/rollup": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.25.0.tgz", "integrity": "sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg==", "dev": true, "dependencies": { "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.25.0", "@rollup/rollup-android-arm64": "4.25.0", "@rollup/rollup-darwin-arm64": "4.25.0", "@rollup/rollup-darwin-x64": "4.25.0", "@rollup/rollup-freebsd-arm64": "4.25.0", "@rollup/rollup-freebsd-x64": "4.25.0", "@rollup/rollup-linux-arm-gnueabihf": "4.25.0", "@rollup/rollup-linux-arm-musleabihf": "4.25.0", "@rollup/rollup-linux-arm64-gnu": "4.25.0", "@rollup/rollup-linux-arm64-musl": "4.25.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.25.0", "@rollup/rollup-linux-riscv64-gnu": "4.25.0", "@rollup/rollup-linux-s390x-gnu": "4.25.0", "@rollup/rollup-linux-x64-gnu": "4.25.0", "@rollup/rollup-linux-x64-musl": "4.25.0", "@rollup/rollup-win32-arm64-msvc": "4.25.0", "@rollup/rollup-win32-ia32-msvc": "4.25.0", "@rollup/rollup-win32-x64-msvc": "4.25.0", "fsevents": "~2.3.2" } }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "peer": true }, "node_modules/sass": { "version": "1.53.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.53.0.tgz", "integrity": "sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ==", "dev": true, "optional": true, "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { "sass": "sass.js" }, "engines": { "node": ">=12.0.0" } }, "node_modules/scheduler": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, "peer": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true, "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "optional": true, "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/styled-components": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.4.1.tgz", "integrity": "sha512-RNqj14kYzw++6Sr38n7197xG33ipEOktGElty4I70IKzQF1jzaD1U4xQ+Ny/i03UUhHlC5NWEO+d8olRCDji6g==", "hasInstallScript": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.0.0", "@emotion/is-prop-valid": "^0.8.1", "@emotion/unitless": "^0.7.0", "babel-plugin-styled-components": ">= 1", "css-to-react-native": "^2.2.2", "memoize-one": "^5.0.0", "merge-anything": "^2.2.4", "prop-types": "^15.5.4", "react-is": "^16.6.0", "stylis": "^3.5.0", "stylis-rule-sheet": "^0.0.10", "supports-color": "^5.5.0" }, "peerDependencies": { "react": ">= 16.3.0", "react-dom": ">= 16.3.0" } }, "node_modules/stylis": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==" }, "node_modules/stylis-rule-sheet": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==", "peerDependencies": { "stylis": "^3.5.0" } }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/terser": { "version": "5.14.2", "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "optional": true, "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" }, "engines": { "node": ">=10" } }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "optional": true, "peer": true }, "node_modules/tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "engines": { "node": ">=4" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "optional": true, "peer": true, "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/update-browserslist-db": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" } ], "peer": true, "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" }, "bin": { "browserslist-lint": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" } }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" }, "node_modules/vite": { "version": "5.4.10", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "less": { "optional": true }, "lightningcss": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true } } }, "node_modules/zustand": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", "dependencies": { "use-sync-external-store": "1.2.0" }, "engines": { "node": ">=12.7.0" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, "immer": { "optional": true }, "react": { "optional": true } } } } } preactjs-preact-389c7bc/demo/package.json000066400000000000000000000015561523334003000204370ustar00rootroot00000000000000{ "name": "demo", "main": "index.js", "scripts": { "start": "vite", "dev": "vite", "build": "vite build", "preview": "vite preview" }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.0.0-beta.55", "@babel/plugin-proposal-decorators": "^7.4.0", "vite": "^5.4.10" }, "dependencies": { "@material-ui/core": "4.9.5", "@reduxjs/toolkit": "^2.2.3", "d3-scale": "^1.0.7", "d3-selection": "^1.2.0", "htm": "2.1.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "mobx-state-tree": "^3.16.0", "preact-render-to-string": "^5.0.2", "preact-router": "^3.0.0", "react-redux": "^7.1.0", "react-router": "^5.0.1", "react-router-dom": "^5.0.1", "redux": "^4.0.1", "styled-components": "^4.2.0", "zustand": "^4.5.2" }, "volta": { "extends": "../package.json" } } preactjs-preact-389c7bc/demo/people/000077500000000000000000000000001523334003000174265ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/people/Readme.md000066400000000000000000000004161523334003000211460ustar00rootroot00000000000000# People demo page This section of our demo was originally made by [phaux](https://github.com/phaux) in the [web-app-boilerplate](https://github.com/phaux/web-app-boilerplate) repo. It has been slightly modified from it's original to better work inside of our demo app preactjs-preact-389c7bc/demo/people/index.tsx000066400000000000000000000026161523334003000213020ustar00rootroot00000000000000import { observer } from 'mobx-react'; import { Component } from 'preact'; import { Profile } from './profile'; import { Link, Route, Router } from './router'; import { store } from './store'; import './styles/index.scss'; @observer export default class App extends Component { componentDidMount() { store.loadUsers().catch(console.error); } render() { return (
    ); } } preactjs-preact-389c7bc/demo/people/profile.tsx000066400000000000000000000024171523334003000216320ustar00rootroot00000000000000import { computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Component } from 'preact'; import { RouteChildProps } from './router'; import { store } from './store'; export type ProfileProps = RouteChildProps; @observer export class Profile extends Component { @observable id = ''; @observable busy = false; componentDidMount() { this.id = this.props.route; } componentWillReceiveProps(props: ProfileProps) { this.id = props.route; } render() { const user = this.user; if (user == null) return null; return (

    {user.name.first} {user.name.last}

    {user.gender === 'female' ? '👩' : '👨'} {user.id}

    🖂 {user.email}

    ); } @computed get user() { return store.users.find(u => u.id === this.id); } remove = async () => { this.busy = true; await new Promise(cb => setTimeout(cb, 1500)); store.deleteUser(this.id); this.busy = false; }; } preactjs-preact-389c7bc/demo/people/router.tsx000066400000000000000000000074721523334003000215200ustar00rootroot00000000000000import { ComponentChild, ComponentFactory, createContext, FunctionalComponent, h, JSX } from 'preact'; import { useCallback, useContext, useEffect, useMemo, useState } from 'preact/hooks'; export type RouterData = { match: string[]; path: string[]; navigate(path: string): void; }; const RouterContext = createContext({ match: [], path: [], navigate() {} }); export const useRouter = () => useContext(RouterContext); const useLocation = (cb: () => void) => { useEffect(() => { window.addEventListener('popstate', cb); return () => { window.removeEventListener('popstate', cb); }; }, [cb]); }; export const Router: FunctionalComponent = props => { const [path, setPath] = useState(location.pathname); const update = useCallback(() => { setPath(location.pathname); }, [setPath]); useLocation(update); const navigate = useCallback( (path: string) => { history.pushState(null, '', path); update(); }, [update] ); const router = useMemo( () => ({ match: [], navigate, path: path.split('/').filter(Boolean) }), [navigate, path] ); return ; }; export type RouteChildProps = { route: string }; export type RouteProps = { component?: ComponentFactory; match: string; render?(route: string): ComponentChild; }; export const Route: FunctionalComponent = props => { const router = useRouter(); const [dir, ...subpath] = router.path; if (dir == null) return null; if (props.match !== '*' && dir !== props.match) return null; const children = useMemo(() => { if (props.component) return ; if (props.render) return props.render(dir); return props.children; }, [props.component, props.render, props.children, dir]); const innerRouter = useMemo( () => ({ ...router, match: [...router.match, dir], path: subpath }), [router.match, dir, subpath.join('/')] ); return ; }; export type LinkProps = JSX.HTMLAttributes & { active?: boolean | string; }; export const Link: FunctionalComponent = props => { const router = useRouter(); const classProps = [props.class, props.className]; const originalClasses = useMemo(() => { const classes = []; for (const prop of classProps) if (prop) classes.push(...prop.split(/\s+/)); return classes; }, classProps); const activeClass = useMemo(() => { if (!props.active || props.href == null) return undefined; const href = props.href.split('/').filter(Boolean); const path = props.href[0] === '/' ? [...router.match, ...router.path] : router.path; const isMatch = href.every((dir, i) => dir === path[i]); if (isMatch) return props.active === true ? 'active' : props.active; }, [originalClasses, props.active, props.href, router.match, router.path]); const classes = activeClass == null ? originalClasses : [...originalClasses, activeClass]; const getHref = useCallback(() => { if (props.href == null || props.href[0] === '/') return props.href; const path = props.href.split('/').filter(Boolean); return '/' + [...router.match, ...path].join('/'); }, [router.match, props.href]); const handleClick = useCallback( (ev: MouseEvent) => { const href = getHref(); if (props.onClick != null) props.onClick(ev); if (ev.defaultPrevented) return; if (href == null) return; if (ev.button !== 0) return; if (props.target != null && props.target !== '_self') return; if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) return; ev.preventDefault(); router.navigate(href); }, [getHref, router.navigate, props.onClick, props.target] ); return ( ); }; preactjs-preact-389c7bc/demo/people/store.ts000066400000000000000000000042741523334003000211410ustar00rootroot00000000000000import { flow, Instance, types } from 'mobx-state-tree'; const cmp = (fn: (x: T) => U) => (a: T, b: T): number => fn(a) > fn(b) ? 1 : -1; const User = types.model({ email: types.string, gender: types.enumeration(['male', 'female']), id: types.identifier, name: types.model({ first: types.string, last: types.string }), picture: types.model({ large: types.string }) }); const Store = types .model({ users: types.array(User), usersOrder: types.enumeration(['name', 'id']) }) .views(self => ({ getSortedUsers() { if (self.usersOrder === 'name') return self.users.slice().sort(cmp(x => x.name.first)); if (self.usersOrder === 'id') return self.users.slice().sort(cmp(x => x.id)); throw Error(`Unknown ordering ${self.usersOrder}`); } })) .actions(self => ({ addUser: flow(function* () { const data = yield fetch('https://randomuser.me/api?results=1') .then(res => res.json()) .then(data => data.results.map((user: any) => ({ ...user, id: user.login.username })) ); self.users.push(...data); }), loadUsers: flow(function* () { const data = yield fetch( `https://randomuser.me/api?seed=${12321}&results=12` ) .then(res => res.json()) .then(data => data.results.map((user: any) => ({ ...user, id: user.login.username })) ); self.users.replace(data); }), deleteUser(id: string) { const user = self.users.find(u => u.id === id); if (user != null) self.users.remove(user); }, setUsersOrder(order: 'name' | 'id') { self.usersOrder = order; } })); export type StoreType = Instance; export const store = Store.create({ usersOrder: 'name', users: [] }); // const { Provider, Consumer } = createContext(undefined as any) // export const StoreProvider: FunctionalComponent = props => { // const store = Store.create({}) // return // } // export type StoreProps = {store: StoreType} // export function injectStore(Child: AnyComponent): FunctionalComponent { // return props => }/> // } preactjs-preact-389c7bc/demo/people/styles/000077500000000000000000000000001523334003000207515ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/people/styles/animations.scss000066400000000000000000000006571523334003000240200ustar00rootroot00000000000000@keyframes popup { from { box-shadow: 0 0 0 black; opacity: 0; transform: scale(0.9); } to { box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5); opacity: 1; transform: none; } } @keyframes zoom { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: none; } } @keyframes appear-from-left { from { opacity: 0; transform: translateX(-25px); } to { opacity: 1; transform: none; } } preactjs-preact-389c7bc/demo/people/styles/app.scss000066400000000000000000000034621523334003000224330ustar00rootroot00000000000000#people-app { position: relative; overflow: hidden; min-height: 100vh; animation: popup 300ms cubic-bezier(0.3, 0.7, 0.3, 1) forwards; background: var(--app-background); --menu-width: 260px; --menu-item-height: 50px; @media (min-width: 1280px) { max-width: 1280px; min-height: calc(100vh - 64px); margin: 32px auto; border-radius: 10px; } > nav { position: absolute; display: flow-root; width: var(--menu-width); height: 100%; background-color: var(--app-background-secondary); overflow-x: hidden; overflow-y: auto; } > nav h4 { padding-left: 16px; font-weight: normal; text-transform: uppercase; } > nav ul { position: relative; } > nav li { position: absolute; width: 100%; animation: zoom 200ms forwards; opacity: 0; transition: top 200ms; } > nav li > a { position: relative; display: flex; overflow: hidden; flex-flow: row; align-items: center; margin-left: 16px; border-right: 2px solid transparent; border-bottom-left-radius: 48px; border-top-left-radius: 48px; text-transform: capitalize; transition: border 500ms; } > nav li > a:hover { background-color: var(--app-highlight); } > nav li > a::after { position: absolute; top: 0; right: -2px; bottom: 0; left: 0; background-image: radial-gradient( circle, var(--app-ripple) 1%, transparent 1% ); background-position: center; background-repeat: no-repeat; background-size: 10000%; content: ''; opacity: 0; transition: opacity 700ms, background 300ms; } > nav li > a:active::after { background-size: 100%; opacity: 0.5; transition: none; } > nav li > a.active { border-color: var(--app-primary); background-color: var(--app-highlight); } > nav li > a > * { margin: 8px; } #people-main { padding-left: var(--menu-width); } } preactjs-preact-389c7bc/demo/people/styles/avatar.scss000066400000000000000000000006401523334003000231240ustar00rootroot00000000000000#people-app { .avatar { display: inline-block; overflow: hidden; width: var(--avatar-size, 32px); height: var(--avatar-size, 32px); background-color: var(--avatar-color, var(--app-primary)); border-radius: 50%; font-size: calc(var(--avatar-size, 32px) * 0.5); line-height: var(--avatar-size, 32px); object-fit: cover; text-align: center; text-transform: uppercase; white-space: nowrap; } } preactjs-preact-389c7bc/demo/people/styles/button.scss000066400000000000000000000043021523334003000231600ustar00rootroot00000000000000#people-app { button { position: relative; overflow: hidden; min-width: 36px; height: 36px; padding: 0 16px; border: none; background-color: transparent; border-radius: 4px; color: var(--app-text); font-family: 'Montserrat', sans-serif; font-size: 14px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; transition: background 300ms, color 200ms; white-space: nowrap; } button::before { position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: var(--app-ripple); content: ''; opacity: 0; transition: opacity 200ms; } button:hover:not(:disabled)::before { opacity: 0.3; transition: opacity 100ms; } button:active:not(:disabled)::before { opacity: 0.7; transition: none; } button::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-image: radial-gradient( circle, var(--app-ripple) 1%, transparent 1% ); background-position: center; background-repeat: no-repeat; background-size: 20000%; content: ''; opacity: 0; transition: opacity 700ms, background 400ms; } button:active:not(:disabled)::after { background-size: 100%; opacity: 1; transition: none; } button.primary { background-color: var(--app-primary); box-shadow: 0 2px 6px var(--app-shadow); } button.secondary { background-color: var(--app-secondary); box-shadow: 0 2px 6px var(--app-shadow); } button:disabled { color: var(--app-text-secondary); } button.busy { animation: stripes 500ms linear infinite; background-image: repeating-linear-gradient( 45deg, var(--app-shadow) 0%, var(--app-shadow) 25%, transparent 25%, transparent 50%, var(--app-shadow) 50%, var(--app-shadow) 75%, transparent 75%, transparent 100% ); color: var(--app-text); /* letter-spacing: -.7em; */ } button:disabled:not(.primary):not(.secondary).busy, button:disabled.primary:not(.busy), button:disabled.secondary:not(.busy) { background-color: var(--app-background-disabled); } @keyframes stripes { from { background-position-x: 0; background-size: 16px 16px; } to { background-position-x: 16px; background-size: 16px 16px; } } } preactjs-preact-389c7bc/demo/people/styles/index.scss000066400000000000000000000075671523334003000227740ustar00rootroot00000000000000@import 'app.scss'; @import 'animations.scss'; @import 'avatar.scss'; @import 'profile.scss'; @import 'button.scss'; // :root { #people-app { --app-background: #2f2b43; --app-background-secondary: #353249; --app-background-disabled: #555366; --app-highlight: rgba(255, 255, 255, 0.1); --app-ripple: rgba(255, 255, 255, 0.5); --app-shadow: rgba(0, 0, 0, 0.15); --app-text: #fff; --app-text-secondary: #807e97; --app-primary: #ff0087; --app-secondary: #4d7cfe; --app-tertiary: #00ec97; --app-danger: #f3c835; --spinner-size: 200px; } * { box-sizing: border-box; } // body { #people-app { // display: flow-root; // overflow: auto; // min-height: 100vh; // margin: 0; // animation: background-light 5s ease-out forwards; // /* very fancy background */ // background: radial-gradient( // circle 15px at 150px 90vh, // rgba(255, 255, 255, 0.35), // rgba(255, 255, 255, 0.35) 90%, // transparent // ), // radial-gradient( // circle 9px at 60px 50vh, // rgba(255, 255, 255, 0.55), // rgba(255, 255, 255, 0.55) 90%, // transparent // ), // radial-gradient( // circle 19px at 40vw 70px, // rgba(255, 255, 255, 0.3), // rgba(255, 255, 255, 0.3) 90%, // transparent // ), // radial-gradient( // circle 12px at 80vw 80px, // rgba(255, 255, 255, 0.4), // rgba(255, 255, 255, 0.4) 90%, // transparent // ), // radial-gradient( // circle 7px at 55vw calc(100vh - 95px), // rgba(255, 255, 255, 0.6), // rgba(255, 255, 255, 0.6) 90%, // transparent // ), // radial-gradient( // circle 14px at 25vw calc(100vh - 35px), // rgba(255, 255, 255, 0.4), // rgba(255, 255, 255, 0.4) 90%, // transparent // ), // radial-gradient( // circle 11px at calc(100vw - 95px) 55vh, // rgba(255, 255, 255, 0.45), // rgba(255, 255, 255, 0.45) 90%, // transparent // ), // radial-gradient( // circle 13px at calc(100vw - 35px) 85vh, // rgba(255, 255, 255, 0.4), // rgba(255, 255, 255, 0.4) 90%, // transparent // ), // radial-gradient( // circle 50vw at 0 -25%, // rgba(255, 255, 255, 0.07), // rgba(255, 255, 255, 0.07) 100%, // transparent // ), // radial-gradient( // circle 80vw at top left, // rgba(255, 255, 255, 0.07), // rgba(255, 255, 255, 0.07) 100%, // transparent // ), // radial-gradient(circle at bottom right, #ef2fb8, transparent), // radial-gradient(circle at top right, #c45af3, transparent), // linear-gradient(#ee66ca, #ff47a6); color: var(--app-text); font-family: 'Montserrat', sans-serif; } #people-app { .spinner { position: absolute; top: 200px; left: calc(50% - var(--spinner-size) / 2); width: var(--spinner-size); height: var(--spinner-size); animation: zoom 250ms 500ms forwards ease-out; opacity: 0; transition: opacity 200ms, transform 200ms ease-in; } .spinner.exit { opacity: 0; transform: scale(0.5); } .spinner::before, .spinner::after { position: absolute; top: 0; left: 0; width: calc(var(--spinner-size) / 3); height: calc(var(--spinner-size) / 3); animation: spinner 2s infinite ease-in-out; background-color: rgba(255, 255, 255, 0.6); content: ''; } .spinner::after { animation-delay: -1s; } @keyframes spinner { 25% { transform: translateX(calc(var(--spinner-size) / 3 * 2 - 1px)) rotate(-90deg) scale(0.5); } 50% { transform: translateX(calc(var(--spinner-size) / 3 * 2 - 1px)) translateY(calc(var(--spinner-size) / 3 * 2 - 1px)) rotate(-179deg); } 50.1% { transform: translateX(calc(var(--spinner-size) / 3 * 2 - 1px)) translateY(calc(var(--spinner-size) / 3 * 2 - 1px)) rotate(-180deg); } 75% { transform: translateX(0) translateY(calc(var(--spinner-size) / 3 * 2 - 1px)) rotate(-270deg) scale(0.5); } 100% { transform: rotate(-360deg); } } ul, ol { padding-left: 0; list-style: none; } a { color: inherit; text-decoration: none; } } preactjs-preact-389c7bc/demo/people/styles/profile.scss000066400000000000000000000006231523334003000233070ustar00rootroot00000000000000#people-app { .profile { display: flex; flex-flow: column; align-items: center; margin: 32px 0; animation: appear-from-left 0.5s forwards; --avatar-size: 80px; } .profile h2 { text-transform: capitalize; } .profile .details { display: flex; flex-flow: column; align-items: stretch; margin: 16px auto; } .profile .details p { margin-top: 8px; margin-bottom: 8px; } } preactjs-preact-389c7bc/demo/preact.jsx000066400000000000000000000016611523334003000201520ustar00rootroot00000000000000import { options, createElement, cloneElement, Component as CevicheComponent, render } from 'preact'; options.vnode = vnode => { vnode.nodeName = vnode.type; vnode.attributes = vnode.props; vnode.children = vnode._children || [].concat(vnode.props.children || []); }; function asArray(arr) { return Array.isArray(arr) ? arr : [arr]; } function normalize(obj) { if (Array.isArray(obj)) { return obj.map(normalize); } if ('type' in obj && !('attributes' in obj)) { obj.attributes = obj.props; } return obj; } export function Component(props, context) { CevicheComponent.call(this, props, context); const render = this.render; this.render = function (props, state, context) { if (props.children) props.children = asArray(normalize(props.children)); return render.call(this, props, state, context); }; } Component.prototype = new CevicheComponent(); export { createElement, createElement as h, cloneElement, render }; preactjs-preact-389c7bc/demo/profiler.jsx000066400000000000000000000031141523334003000205110ustar00rootroot00000000000000import { createElement, Component, options } from 'preact'; function getPrimes(max) { let sieve = [], i, j, primes = []; for (i = 2; i <= max; ++i) { if (!sieve[i]) { // i has not been marked -- it is prime primes.push(i); for (j = i << 1; j <= max; j += i) { sieve[j] = true; } } } return primes.join(''); } function Foo(props) { return
    {props.children}
    ; } function Bar() { getPrimes(10000); return (
    ...yet another component
    ); } function PrimeNumber(props) { // Slow down rendering of this component getPrimes(10); return (
    I'm a slow component
    {props.children}
    ); } export default class ProfilerDemo extends Component { constructor() { super(); this.onClick = this.onClick.bind(this); this.state = { counter: 0 }; } componentDidMount() { options._diff = vnode => (vnode.startTime = performance.now()); options.diffed = vnode => (vnode.endTime = performance.now()); } componentWillUnmount() { delete options._diff; delete options.diffed; } onClick() { this.setState(prev => ({ counter: ++prev.counter })); } render() { return (

    ⚛ Preact

    Devtools Profiler integration 🕒

    I'm a fast component I'm the fastest component 🎉 Counter: {this.state.counter}

    ); } } preactjs-preact-389c7bc/demo/pythagoras/000077500000000000000000000000001523334003000203235ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/pythagoras/index.jsx000066400000000000000000000031741523334003000221650ustar00rootroot00000000000000import { Component } from 'preact'; import { select as d3select, mouse as d3mouse } from 'd3-selection'; import { scaleLinear } from 'd3-scale'; import Pythagoras from './pythagoras'; export default class PythagorasDemo extends Component { svg = { width: 1280, height: 600 }; state = { currentMax: 0, baseW: 80, heightFactor: 0, lean: 0 }; realMax = 11; svgRef = c => { this.svgElement = c; }; scaleFactor = scaleLinear().domain([this.svg.height, 0]).range([0, 0.8]); scaleLean = scaleLinear() .domain([0, this.svg.width / 2, this.svg.width]) .range([0.5, 0, -0.5]); onMouseMove = event => { let [x, y] = d3mouse(this.svgElement); this.setState({ heightFactor: this.scaleFactor(y), lean: this.scaleLean(x) }); }; restart = () => { this.setState({ currentMax: 0 }); this.next(); }; next = () => { let { currentMax } = this.state; if (currentMax < this.realMax) { this.setState({ currentMax: currentMax + 1 }); this.timer = setTimeout(this.next, 500); } }; componentDidMount() { this.selected = d3select(this.svgElement).on('mousemove', this.onMouseMove); this.next(); } componentWillUnmount() { this.selected.on('mousemove', null); clearTimeout(this.timer); } render({}, { currentMax, baseW, heightFactor, lean }) { let { width, height } = this.svg; return (
    ); } } preactjs-preact-389c7bc/demo/pythagoras/pythagoras.jsx000066400000000000000000000031461523334003000232360ustar00rootroot00000000000000import { interpolateViridis } from 'd3-scale'; Math.deg = function (radians) { return radians * (180 / Math.PI); }; const memoizedCalc = (function () { const memo = {}; const key = ({ w, heightFactor, lean }) => `${w}-${heightFactor}-${lean}`; return args => { let memoKey = key(args); if (memo[memoKey]) { return memo[memoKey]; } let { w, heightFactor, lean } = args; let trigH = heightFactor * w; let result = { nextRight: Math.sqrt(trigH ** 2 + (w * (0.5 + lean)) ** 2), nextLeft: Math.sqrt(trigH ** 2 + (w * (0.5 - lean)) ** 2), A: Math.deg(Math.atan(trigH / ((0.5 - lean) * w))), B: Math.deg(Math.atan(trigH / ((0.5 + lean) * w))) }; memo[memoKey] = result; return result; }; })(); export default function Pythagoras({ w, x, y, heightFactor, lean, left, right, lvl, maxlvl }) { if (lvl >= maxlvl || w < 1) { return null; } const { nextRight, nextLeft, A, B } = memoizedCalc({ w, heightFactor, lean }); let rotate = ''; if (left) { rotate = `rotate(${-A} 0 ${w})`; } else if (right) { rotate = `rotate(${B} ${w} ${w})`; } return ( ); } preactjs-preact-389c7bc/demo/redux-toolkit.jsx000066400000000000000000000023531523334003000215050ustar00rootroot00000000000000import { createElement } from 'preact'; import { Provider, useSelector } from 'react-redux'; import { configureStore, createSlice } from '@reduxjs/toolkit'; const initialState = { value: 0 }; const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: state => { state.value += 1; }, decrement: state => { state.value -= 1; } } }); const store = configureStore({ reducer: { counter: counterSlice.reducer } }); function Counter({ number }) { const count = useSelector(state => state.counter.value); return (
    Counter #{number}:{count}
    ); } export default function ReduxToolkit() { function increment() { store.dispatch(counterSlice.actions.increment()); } function decrement() { store.dispatch(counterSlice.actions.decrement()); } function incrementAsync() { setTimeout(() => { store.dispatch(counterSlice.actions.increment()); }, 1000); } return (

    Redux Toolkit

    Counter

    ); } preactjs-preact-389c7bc/demo/redux.jsx000066400000000000000000000021331523334003000200160ustar00rootroot00000000000000import { createElement } from 'preact'; import React from 'react'; import { createStore } from 'redux'; import { connect, Provider } from 'react-redux'; const store = createStore((state = { value: 0 }, action) => { switch (action.type) { case 'increment': return { value: state.value + 1 }; case 'decrement': return { value: state.value - 1 }; default: return state; } }); class Child extends React.Component { render() { return (
    Child #1: {this.props.foo}
    ); } } const ConnectedChild = connect(store => ({ foo: store.value }))(Child); class Child2 extends React.Component { render() { return
    Child #2: {this.props.foo}
    ; } } const ConnectedChild2 = connect(store => ({ foo: store.value }))(Child2); export default function Redux() { return (

    Counter


    ); } preactjs-preact-389c7bc/demo/reduxUpdate.jsx000066400000000000000000000025241523334003000211650ustar00rootroot00000000000000import { createElement, Component } from 'preact'; import { connect, Provider } from 'react-redux'; import { createStore } from 'redux'; import { HashRouter, Route, Link } from 'react-router-dom'; const store = createStore( (state, action) => ({ ...state, display: action.display }), { display: false } ); function _Redux({ showMe, counter }) { if (!showMe) return null; return
    showMe {counter}
    ; } const Redux = connect( state => console.log('injecting', state.display) || { showMe: state.display } )(_Redux); let display = false; class Test extends Component { componentDidUpdate(prevProps) { if (this.props.start != prevProps.start) { this.setState({ f: (this.props.start || 0) + 1 }); setTimeout(() => this.setState({ i: (this.state.i || 0) + 1 })); } } render() { const { f } = this.state; return (
    Click me
    ); } } function App() { return ( } /> ); } export default App; preactjs-preact-389c7bc/demo/reorder.jsx000066400000000000000000000044121523334003000203330ustar00rootroot00000000000000import { createElement, Component } from 'preact'; function createItems(count = 10) { let items = []; for (let i = 0; i < count; i++) { items.push({ label: `Item #${i + 1}`, key: i + 1 }); } return items; } function random() { return Math.random() < 0.5 ? 1 : -1; } export default class Reorder extends Component { state = { items: createItems(), count: 1, useKeys: false }; shuffle = () => { this.setState({ items: this.state.items.slice().sort(random) }); }; swapTwo = () => { let items = this.state.items.slice(), first = Math.floor(Math.random() * items.length), second; do { second = Math.floor(Math.random() * items.length); } while (second === first); let other = items[first]; items[first] = items[second]; items[second] = other; this.setState({ items }); }; reverse = () => { this.setState({ items: this.state.items.slice().reverse() }); }; setCount = e => { this.setState({ count: Math.round(e.target.value) }); }; rotate = () => { let { items, count } = this.state; items = items.slice(count).concat(items.slice(0, count)); this.setState({ items }); }; rotateBackward = () => { let { items, count } = this.state, len = items.length; items = items.slice(len - count, len).concat(items.slice(0, len - count)); this.setState({ items }); }; toggleKeys = () => { this.setState({ useKeys: !this.state.useKeys }); }; renderItem = item => (
  • {item.label}
  • ); render({}, { items, count, useKeys }) { return (
      {items.map(this.renderItem)}
    ); } } preactjs-preact-389c7bc/demo/spiral.jsx000066400000000000000000000062741523334003000201730ustar00rootroot00000000000000import { createElement, Component } from 'preact'; const COUNT = 500; const LOOPS = 6; // Component.debounce = requestAnimationFrame; export default class Spiral extends Component { state = { x: 0, y: 0, big: false, counter: 0 }; handleClick = e => { console.log('click'); }; increment = () => { if (this.stop) return; // this.setState({ counter: this.state.counter + 1 }, this.increment); this.setState({ counter: this.state.counter + 1 }); // this.forceUpdate(); requestAnimationFrame(this.increment); }; setMouse({ pageX: x, pageY: y }) { this.setState({ x, y }); return false; } setBig(big) { this.setState({ big }); } componentDidMount() { console.log('mount'); // let touch = navigator.maxTouchPoints > 1; let touch = false; // set mouse position state on move: addEventListener(touch ? 'touchmove' : 'mousemove', e => { this.setMouse(e.touches ? e.touches[0] : e); }); // holding the mouse down enables big mode: addEventListener(touch ? 'touchstart' : 'mousedown', e => { this.setBig(true); e.preventDefault(); }); addEventListener(touch ? 'touchend' : 'mouseup', e => this.setBig(false)); requestAnimationFrame(this.increment); } componentWillUnmount() { console.log('unmount'); this.stop = true; } // componentDidUpdate() { // // invoking setState() in componentDidUpdate() creates an animation loop: // this.increment(); // } // builds and returns a brand new DOM (every time) render(props, { x, y, big, counter }) { let max = COUNT + Math.round(Math.sin((counter / 90) * 2 * Math.PI) * COUNT * 0.5), cursors = []; // the advantage of JSX is that you can use the entirety of JS to "template": for (let i = max; i--; ) { let f = (i / max) * LOOPS, θ = f * 2 * Math.PI, m = 20 + i * 2, hue = (f * 255 + counter * 10) % 255; cursors[i] = ( ); } return (
    {cursors}
    ); } } /** Represents a single coloured dot. */ class Cursor extends Component { // get shared/pooled class object getClass(big, label) { let cl = 'cursor'; if (big) cl += ' big'; if (label) cl += ' label'; return cl; } // skip any pointless re-renders shouldComponentUpdate(props) { for (let i in props) if (i !== 'children' && props[i] !== this.props[i]) return true; return false; } // first argument is "props", the attributes passed to render({ x, y, label, color, big }) { let inner = null; if (label) inner = ( {x},{y} ); return (
    {inner}
    ); } } // Addendum: disable dragging on mobile addEventListener('touchstart', e => (e.preventDefault(), false)); preactjs-preact-389c7bc/demo/stateOrderBug.jsx000066400000000000000000000030171523334003000214430ustar00rootroot00000000000000import htm from 'htm'; import { h } from 'preact'; import { useState, useCallback } from 'preact/hooks'; const html = htm.bind(h); // configuration used to show behavior vs. workaround let childFirst = true; const Config = () => html` `; const Child = ({ items, setItems }) => { let [pendingId, setPendingId] = useState(null); if (!pendingId) { setPendingId((pendingId = Math.random().toFixed(20).slice(2))); } const onInput = useCallback( evt => { let val = evt.target.value, _items = [...items, { _id: pendingId, val }]; if (childFirst) { setPendingId(null); setItems(_items); } else { setItems(_items); setPendingId(null); } }, [childFirst, setPendingId, setItems, items, pendingId] ); return html`
    ${items.map( (item, idx) => html` { let val = evt.target.value, _items = [...items]; _items.splice(idx, 1, { ...item, val }); setItems(_items); }} /> ` )}
    `; }; const Parent = () => { let [items, setItems] = useState([]); return html`
    <${Config} /><${Child} items=${items} setItems=${setItems} />
    `; }; export default Parent; preactjs-preact-389c7bc/demo/style.css000066400000000000000000000005441523334003000200170ustar00rootroot00000000000000html, body { font: 14px system-ui, sans-serif; } .list { list-style: none; padding: 0; } .list > li { position: relative; padding: 5px 10px; animation: fadeIn 1s ease; } @keyframes fadeIn { 0% { box-shadow: inset 0 0 2px 2px red, 0 0 2px 2px red; } } .list > .odd { background-color: #def; } .list > .even { background-color: #fed; } preactjs-preact-389c7bc/demo/style.scss000066400000000000000000000044721523334003000202060ustar00rootroot00000000000000html, body { height: 100%; margin: 0; background: #eee; font: 400 16px/1.3 'Helvetica Neue', helvetica, sans-serif; text-rendering: optimizeSpeed; color: #444; } .app { display: block; flex-direction: column; height: 100%; > header { flex: 0; background: #f9f9f9; box-shadow: inset 0 -0.5px 0 0 rgba(0, 0, 0, 0.2), 0 0.5px 0 0 rgba(255, 255, 255, 0.6); nav { display: inline-block; padding: 4px 7px; a { display: inline-block; margin: 2px; padding: 4px 10px; background-color: rgba(255, 255, 255, 0); border-radius: 1em; color: #6b1d8f; text-decoration: none; // transition: all 250ms ease; transition: all 250ms cubic-bezier(0.2, 0, 0.4, 2); &:hover { background-color: rgba(255, 255, 255, 1); box-shadow: 0 0 0 2px #6b1d8f; } &.active { background-color: #6b1d8f; color: white; } } } } > main { flex: 1; padding: 10px; } } h1 { margin: 0; color: #6b1d8f; font-weight: 300; font-size: 250%; } input, textarea { box-sizing: border-box; margin: 1px; padding: 0.25em 0.5em; background: #fff; border: 1px solid #999; border-radius: 3px; font: inherit; color: #000; outline: none; &:focus { border-color: #6b1d8f; } } button, input[type='submit'], input[type='reset'], input[type='button'] { box-sizing: border-box; margin: 1px; padding: 0.25em 0.8em; background: #6b1d8f; border: 1px solid #6b1d8f; // border: none; border-radius: 1.5em; font: inherit; color: white; outline: none; cursor: pointer; } .cursor { position: absolute; left: 0; top: 0; width: 8px; height: 8px; margin: -5px 0 0 -5px; border: 2px solid #f00; border-radius: 50%; transform-origin: 50% 50%; pointer-events: none; overflow: hidden; font-size: 9px; line-height: 25px; text-indent: 15px; white-space: nowrap; &:not(.label) { contain: strict; } &.label { overflow: visible; } // &.big { // transform: scale(2); // // width: 24px; // // height: 24px; // // margin: -13px 0 0 -13px; // } .label { position: absolute; left: 0; top: 0; //transform: translateZ(0); // z-index: 10; } } .animation-picker { position: fixed; display: inline-block; right: 0; top: 0; padding: 10px; background: #000; color: #bbb; z-index: 1000; select { font-size: 100%; margin-left: 5px; } } preactjs-preact-389c7bc/demo/styled-components.jsx000066400000000000000000000011011523334003000223500ustar00rootroot00000000000000import { createElement } from 'preact'; import styled, { css } from 'styled-components'; const Button = styled.button` background: transparent; border-radius: 3px; border: 2px solid palevioletred; color: palevioletred; margin: 0.5em 1em; padding: 0.25em 1em; ${props => props.primary && css` background: palevioletred; color: white; `} `; const Container = styled.div` text-align: center; `; export default function StyledComp() { return ( ); } preactjs-preact-389c7bc/demo/suspense-router/000077500000000000000000000000001523334003000213255ustar00rootroot00000000000000preactjs-preact-389c7bc/demo/suspense-router/bye.jsx000066400000000000000000000002231523334003000226270ustar00rootroot00000000000000import { Link } from './simple-router'; export default function Bye() { return (
    Bye! Go to Hello!
    ); } preactjs-preact-389c7bc/demo/suspense-router/hello.jsx000066400000000000000000000002301523334003000231510ustar00rootroot00000000000000import { Link } from './simple-router'; export default function Hello() { return (
    Hello! Go to Bye!
    ); } preactjs-preact-389c7bc/demo/suspense-router/index.jsx000066400000000000000000000011261523334003000231620ustar00rootroot00000000000000import { Suspense, lazy } from 'react'; import { Router, Route, Switch } from './simple-router'; let Hello = lazy(() => import('./hello.jsx')); let Bye = lazy(() => import('./bye.jsx')); function Loading() { return
    Hey! This is a fallback because we're loading things! :D
    ; } export default function SuspenseRouterBug() { return (

    Suspense Router bug

    }>
    ); } preactjs-preact-389c7bc/demo/suspense-router/simple-router.jsx000066400000000000000000000033241523334003000246640ustar00rootroot00000000000000import { createContext, useState, useContext, Children, useLayoutEffect } from 'react'; const memoryHistory = { /** * @typedef {{ pathname: string }} Location * @typedef {(location: Location) => void} HistoryListener * @type {HistoryListener[]} */ listeners: [], /** * @param {HistoryListener} listener */ listen(listener) { const newLength = this.listeners.push(listener); return () => this.listeners.splice(newLength - 1, 1); }, /** * @param {Location} to */ navigate(to) { this.listeners.forEach(listener => listener(to)); } }; /** @type {import('react').Context<{ history: typeof memoryHistory; location: Location }>} */ const RouterContext = createContext(null); export function Router({ history = memoryHistory, children }) { const [location, setLocation] = useState({ pathname: '/' }); useLayoutEffect(() => { return history.listen(newLocation => setLocation(newLocation)); }, []); return ( {children} ); } export function Switch(props) { const { location } = useContext(RouterContext); let element = null; Children.forEach(props.children, child => { if (element == null && child.props.path == location.pathname) { element = child; } }); return element; } /** * @param {{ children: any; path: string; exact?: boolean; }} props */ export function Route({ children, path, exact }) { return children; } export function Link({ to, children }) { const { history } = useContext(RouterContext); const onClick = event => { event.preventDefault(); event.stopPropagation(); history.navigate({ pathname: to }); }; return (
    {children} ); } preactjs-preact-389c7bc/demo/suspense.jsx000066400000000000000000000033451523334003000205420ustar00rootroot00000000000000// eslint-disable-next-line no-unused-vars import { createElement, Component, memo, Fragment, Suspense, lazy } from 'react'; function LazyComp() { return
    I'm (fake) lazy loaded
    ; } const Lazy = lazy(() => Promise.resolve({ default: LazyComp })); function createSuspension(name, timeout, error) { let done = false; let prom; return { name, timeout, start: () => { if (!prom) { prom = new Promise((res, rej) => { setTimeout(() => { done = true; if (error) { rej(error); } else { res(); } }, timeout); }); } return prom; }, getPromise: () => prom, isDone: () => done }; } function CustomSuspense({ isDone, start, timeout, name }) { if (!isDone()) { throw start(); } return (
    Hello from CustomSuspense {name}, loaded after {timeout / 1000}s
    ); } function init() { return { s1: createSuspension('1', 1000, null), s2: createSuspension('2', 2000, null), s3: createSuspension('3', 3000, null) }; } export default class DevtoolsDemo extends Component { constructor(props) { super(props); this.state = init(); this.onRerun = this.onRerun.bind(this); } onRerun() { this.setState(init()); } render(props, state) { return (

    lazy()

    Loading (fake) lazy loaded component...
    }>

    Suspense

    Fallback 1}> Fallback 2}> ); } } preactjs-preact-389c7bc/demo/textFields.jsx000066400000000000000000000012471523334003000210070ustar00rootroot00000000000000import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; const PatchedTextField = props => { const [value, set] = useState(props.value); return ( set(e.target.value)} /> ); }; const TextFields = () => (
    ); export default TextFields; preactjs-preact-389c7bc/demo/todo.jsx000066400000000000000000000020341523334003000176340ustar00rootroot00000000000000import { createElement, Component } from 'preact'; let counter = 0; export default class TodoList extends Component { state = { todos: [], text: '' }; setText = e => { this.setState({ text: e.target.value }); }; addTodo = () => { let { todos, text } = this.state; todos = todos.concat({ text, id: ++counter }); this.setState({ todos, text: '' }); }; removeTodo = e => { let id = e.target.getAttribute('data-id'); this.setState({ todos: this.state.todos.filter(t => t.id != id) }); }; render({}, { todos, text }) { return (
    ); } } class TodoItems extends Component { render({ todos, removeTodo }) { return todos.map(todo => (
  • {' '} {todo.text}
  • )); } } preactjs-preact-389c7bc/demo/tsconfig.json000066400000000000000000000004741523334003000206560ustar00rootroot00000000000000{ "compilerOptions": { "experimentalDecorators": true, "jsx": "react", "jsxFactory": "h", "baseUrl": ".", "target": "es2018", "module": "es2015", "moduleResolution": "node", "paths": { "preact/hooks": ["../hooks/src/index.js"], "preact": ["../src/index.js"] } } } preactjs-preact-389c7bc/demo/vite.config.js000066400000000000000000000021471523334003000207170ustar00rootroot00000000000000import { defineConfig } from 'vite'; import path from 'node:path'; const root = path.join(__dirname, '..'); const resolvePkg = (...parts) => path.join(root, ...parts, 'src', 'index.js'); // https://vitejs.dev/config/ /** @type {import('vite').UserConfig} */ export default defineConfig({ optimizeDeps: { exclude: [ 'preact', 'preact/compat', 'preact/debug', 'preact/hooks', 'preact/devtools', 'preact/jsx-runtime', 'preact/jsx-dev-runtime', 'preact-router', 'react', 'react-dom' ] }, resolve: { alias: { 'preact/debug/src/debug': path.join(root, 'debug', 'src', 'debug'), 'preact/devtools/src/devtools': path.join( root, 'devtools', 'src', 'devtools' ), //'preact/debug': resolvePkg('debug'), 'preact/devtools': resolvePkg('devtools'), 'preact/hooks': resolvePkg('hooks'), 'preact/jsx-runtime': resolvePkg('jsx-runtime'), 'preact/jsx-dev-runtime': resolvePkg('jsx-runtime'), preact: resolvePkg(''), 'react-dom': resolvePkg('compat'), react: resolvePkg('compat') } }, esbuild: { jsx: 'automatic', jsxImportSource: 'preact' } }); preactjs-preact-389c7bc/demo/zustand.jsx000066400000000000000000000023241523334003000203610ustar00rootroot00000000000000import { createElement } from 'preact'; import create from 'zustand'; const useStore = create(set => ({ value: 0, text: 'John', setText: text => set(state => ({ ...state, text })), increment: () => set(state => ({ value: state.value + 1 })), decrement: () => set(state => ({ value: state.value - 1 })), incrementAsync: async () => { await new Promise(resolve => setTimeout(resolve, 1000)); set(state => ({ value: state.value + 1 })); } })); function Counter({ number }) { const value = useStore(state => state.value); return (
    Counter #{number}: {value}
    ); } function Text() { const text = useStore(state => state.text); const { setText } = useStore(); function handleInput(e) { setText(e.target.value); } return (
    Text: {text}
    ); } export default function ZustandComponent() { const { increment, decrement, incrementAsync } = useStore(); return (

    Zustand

    Counter

    ); } preactjs-preact-389c7bc/devtools/000077500000000000000000000000001523334003000170555ustar00rootroot00000000000000preactjs-preact-389c7bc/devtools/LICENSE000066400000000000000000000020771523334003000200700ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-present Jason Miller 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. preactjs-preact-389c7bc/devtools/mangle.json000066400000000000000000000012541523334003000212150ustar00rootroot00000000000000{ "help": { "what is this file?": "It controls protected/private property mangling so that minified builds have consistent property names.", "why are there duplicate minified properties?": "Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size." }, "minify": { "mangle": { "properties": { "regex": "^_[^_]", "reserved": [ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", "__REACT_DEVTOOLS_GLOBAL_HOOK__", "__PREACT_DEVTOOLS__", "_renderers", "__source", "__self" ] } } } } preactjs-preact-389c7bc/devtools/package.json000066400000000000000000000011651523334003000213460ustar00rootroot00000000000000{ "name": "preact-devtools", "amdName": "preactDevtools", "version": "1.0.0", "private": true, "description": "Preact bridge for Preact devtools", "main": "dist/devtools.js", "module": "dist/devtools.module.js", "umd:main": "dist/devtools.umd.js", "source": "src/index.js", "license": "MIT", "types": "src/index.d.ts", "peerDependencies": { "preact": "^10.0.0" }, "exports": { ".": { "types": "./src/index.d.ts", "browser": "./dist/devtools.module.js", "umd": "./dist/devtools.umd.js", "import": "./dist/devtools.mjs", "require": "./dist/devtools.js" } } } preactjs-preact-389c7bc/devtools/src/000077500000000000000000000000001523334003000176445ustar00rootroot00000000000000preactjs-preact-389c7bc/devtools/src/devtools.js000066400000000000000000000006521523334003000220440ustar00rootroot00000000000000import { Component, Fragment, options } from 'preact'; export function initDevTools() { const globalVar = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : undefined; if ( globalVar !== null && globalVar !== undefined && globalVar.__PREACT_DEVTOOLS__ ) { globalVar.__PREACT_DEVTOOLS__.attachPreact('10.29.8', options, { Fragment, Component }); } } preactjs-preact-389c7bc/devtools/src/index.d.ts000066400000000000000000000003511523334003000215440ustar00rootroot00000000000000/** * Customize the displayed name of a useState, useReducer or useRef hook * in the devtools panel. * * @param value Wrapped native hook. * @param name Custom name */ export function addHookName(value: T, name: string): T; preactjs-preact-389c7bc/devtools/src/index.js000066400000000000000000000005201523334003000213060ustar00rootroot00000000000000import { options } from 'preact'; import { initDevTools } from './devtools'; initDevTools(); /** * Display a custom label for a custom hook for the devtools panel * @type {(value: T, name: string) => T} */ export function addHookName(value, name) { if (options._addHookName) { options._addHookName(name); } return value; } preactjs-preact-389c7bc/devtools/test/000077500000000000000000000000001523334003000200345ustar00rootroot00000000000000preactjs-preact-389c7bc/devtools/test/browser/000077500000000000000000000000001523334003000215175ustar00rootroot00000000000000preactjs-preact-389c7bc/devtools/test/browser/addHookName.test.jsx000066400000000000000000000020651523334003000254000ustar00rootroot00000000000000import { createElement, render, options } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useState } from 'preact/hooks'; import { addHookName } from 'preact/devtools'; /** @jsx createElement */ describe('addHookName', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); delete options._addHookName; }); it('should do nothing when no options hook is present', () => { function useFoo() { return addHookName(useState(0), 'foo'); } function App() { let [v] = useFoo(); return
    {v}
    ; } expect(() => render(, scratch)).to.not.throw(); }); it('should call options hook with value', () => { let spy = (options._addHookName = sinon.spy()); function useFoo() { return addHookName(useState(0), 'foo'); } function App() { let [v] = useFoo(); return
    {v}
    ; } render(, scratch); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith('foo'); }); }); preactjs-preact-389c7bc/hooks/000077500000000000000000000000001523334003000163415ustar00rootroot00000000000000preactjs-preact-389c7bc/hooks/LICENSE000066400000000000000000000020771523334003000173540ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-present Jason Miller 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. preactjs-preact-389c7bc/hooks/mangle.json000066400000000000000000000012541523334003000205010ustar00rootroot00000000000000{ "help": { "what is this file?": "It controls protected/private property mangling so that minified builds have consistent property names.", "why are there duplicate minified properties?": "Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size." }, "minify": { "mangle": { "properties": { "regex": "^_[^_]", "reserved": [ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", "__REACT_DEVTOOLS_GLOBAL_HOOK__", "__PREACT_DEVTOOLS__", "_renderers", "__source", "__self" ] } } } } preactjs-preact-389c7bc/hooks/package.json000066400000000000000000000016431523334003000206330ustar00rootroot00000000000000{ "name": "preact-hooks", "amdName": "preactHooks", "version": "0.1.0", "private": true, "description": "Hook addon for Preact", "main": "dist/hooks.js", "module": "dist/hooks.module.js", "umd:main": "dist/hooks.umd.js", "source": "src/index.js", "license": "MIT", "types": "src/index.d.ts", "scripts": { "build": "microbundle build --raw", "dev": "microbundle watch --raw --format cjs", "test": "npm-run-all build --parallel test:karma", "test:karma": "karma start test/karma.conf.js --single-run", "test:karma:watch": "karma start test/karma.conf.js --no-single-run" }, "peerDependencies": { "preact": "^10.0.0" }, "mangle": { "regex": "^_" }, "exports": { ".": { "types": "./src/index.d.ts", "browser": "./dist/hooks.module.js", "umd": "./dist/hooks.umd.js", "import": "./dist/hooks.mjs", "require": "./dist/hooks.js" } } } preactjs-preact-389c7bc/hooks/src/000077500000000000000000000000001523334003000171305ustar00rootroot00000000000000preactjs-preact-389c7bc/hooks/src/index.d.ts000066400000000000000000000131261523334003000210340ustar00rootroot00000000000000// Intentionally not using a relative path to take advantage of // the TS version resolution mechanism import { ErrorInfo, PreactContext, Ref, RefObject } from 'preact'; type Inputs = ReadonlyArray; export type Dispatch = (value: A) => void; export type StateUpdater = S | ((prevState: S) => S); /** * Returns a stateful value, and a function to update it. * @param initialState The initial value (or a function that returns the initial value) */ export function useState( initialState: S | (() => S) ): [S, Dispatch>]; export function useState(): [ S | undefined, Dispatch> ]; export type Reducer = (prevState: S, action: A) => S; /** * An alternative to `useState`. * * `useReducer` is usually preferable to `useState` when you have complex state logic that involves * multiple sub-values. It also lets you optimize performance for components that trigger deep * updates because you can pass `dispatch` down instead of callbacks. * @param reducer Given the current state and an action, returns the new state * @param initialState The initial value to store as state */ export function useReducer( reducer: Reducer, initialState: S ): [S, Dispatch]; /** * An alternative to `useState`. * * `useReducer` is usually preferable to `useState` when you have complex state logic that involves * multiple sub-values. It also lets you optimize performance for components that trigger deep * updates because you can pass `dispatch` down instead of callbacks. * @param reducer Given the current state and an action, returns the new state * @param initialArg The initial argument to pass to the `init` function * @param init A function that, given the `initialArg`, returns the initial value to store as state */ export function useReducer( reducer: Reducer, initialArg: I, init: (arg: I) => S ): [S, Dispatch]; /** @deprecated Use the `Ref` type instead. */ type PropRef = MutableRef; interface MutableRef { current: T; } /** * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument * (`initialValue`). The returned object will persist for the full lifetime of the component. * * Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable * value around similar to how you’d use instance fields in classes. * * @param initialValue the initial value to store in the ref object */ export function useRef(initialValue: T): MutableRef; export function useRef(initialValue: T | null): RefObject; export function useRef(): MutableRef; type EffectCallback = () => void | (() => void); /** * Accepts a function that contains imperative, possibly effectful code. * The effects run after browser paint, without blocking it. * * @param effect Imperative function that can return a cleanup function * @param inputs If present, effect will only activate if the values in the list change (using ===). */ export function useEffect(effect: EffectCallback, inputs?: Inputs): void; type CreateHandle = () => object; /** * @param ref The ref that will be mutated * @param create The function that will be executed to get the value that will be attached to * ref.current * @param inputs If present, effect will only activate if the values in the list change (using ===). */ export function useImperativeHandle( ref: Ref, create: () => R, inputs?: Inputs ): void; /** * Accepts a function that contains imperative, possibly effectful code. * Use this to read layout from the DOM and synchronously re-render. * Updates scheduled inside `useLayoutEffect` will be flushed synchronously, after all DOM mutations but before the browser has a chance to paint. * Prefer the standard `useEffect` hook when possible to avoid blocking visual updates. * * @param effect Imperative function that can return a cleanup function * @param inputs If present, effect will only activate if the values in the list change (using ===). */ export function useLayoutEffect(effect: EffectCallback, inputs?: Inputs): void; /** * Returns a memoized version of the callback that only changes if one of the `inputs` * has changed (using ===). */ export function useCallback(callback: T, inputs: Inputs): T; /** * Pass a factory function and an array of inputs. * useMemo will only recompute the memoized value when one of the inputs has changed. * This optimization helps to avoid expensive calculations on every render. * If no array is provided, a new value will be computed whenever a new function instance is passed as the first argument. */ // for `inputs`, allow undefined, but don't make it optional as that is very likely a mistake export function useMemo(factory: () => T, inputs: Inputs | undefined): T; /** * Returns the current context value, as given by the nearest context provider for the given context. * When the provider updates, this Hook will trigger a rerender with the latest context value. * * @param context The context you want to use */ export function useContext(context: PreactContext): T; /** * Customize the displayed value in the devtools panel. * * @param value Custom hook name or object that is passed to formatter * @param formatter Formatter to modify value before sending it to the devtools */ export function useDebugValue(value: T, formatter?: (value: T) => any): void; export function useErrorBoundary( callback?: (error: any, errorInfo: ErrorInfo) => Promise | void ): [any, () => void]; export function useId(): string; preactjs-preact-389c7bc/hooks/src/index.js000066400000000000000000000357031523334003000206050ustar00rootroot00000000000000import { options as _options } from 'preact'; /** @type {number} */ let currentIndex; /** @type {import('./internal').Component} */ let currentComponent; /** @type {import('./internal').Component} */ let previousComponent; /** @type {number} */ let currentHook = 0; /** @type {Array} */ let afterPaintEffects = []; // Cast to use internal Options type const options = /** @type {import('./internal').Options} */ (_options); let oldBeforeDiff = options._diff; let oldBeforeRender = options._render; let oldAfterDiff = options.diffed; let oldCommit = options._commit; let oldBeforeUnmount = options.unmount; let oldRoot = options._root; // We take the minimum timeout for requestAnimationFrame to ensure that // the callback is invoked after the next frame. 35ms is based on a 30hz // refresh rate, which is the minimum rate for a smooth user experience. const RAF_TIMEOUT = 35; let prevRaf; /** @type {(vnode: import('./internal').VNode) => void} */ options._diff = vnode => { currentComponent = null; if (oldBeforeDiff) oldBeforeDiff(vnode); }; options._root = (vnode, parentDom) => { if (vnode && parentDom._children && parentDom._children._mask) { vnode._mask = parentDom._children._mask; } if (oldRoot) oldRoot(vnode, parentDom); }; /** @type {(vnode: import('./internal').VNode) => void} */ options._render = vnode => { if (oldBeforeRender) oldBeforeRender(vnode); currentComponent = vnode._component; currentIndex = 0; const hooks = currentComponent.__hooks; if (hooks) { if (previousComponent === currentComponent) { hooks._pendingEffects = []; currentComponent._renderCallbacks = []; hooks._list.some(hookItem => { if (hookItem._nextValue) { hookItem._value = hookItem._nextValue; } hookItem._pendingArgs = hookItem._nextValue = undefined; }); } else { hooks._pendingEffects.some(invokeCleanup); hooks._pendingEffects.some(invokeEffect); hooks._pendingEffects = []; currentIndex = 0; } } previousComponent = currentComponent; }; /** @type {(vnode: import('./internal').VNode) => void} */ options.diffed = vnode => { if (oldAfterDiff) oldAfterDiff(vnode); const c = vnode._component; if (c && c.__hooks) { if (c.__hooks._pendingEffects.length) afterPaint(afterPaintEffects.push(c)); c.__hooks._list.some(hookItem => { if (hookItem._pendingArgs) { hookItem._args = hookItem._pendingArgs; hookItem._pendingArgs = undefined; } }); } previousComponent = currentComponent = null; }; // TODO: Improve typing of commitQueue parameter /** @type {(vnode: import('./internal').VNode, commitQueue: any) => void} */ options._commit = (vnode, commitQueue) => { commitQueue.some(component => { try { component._renderCallbacks.some(invokeCleanup); component._renderCallbacks = component._renderCallbacks.filter(cb => cb._value ? invokeEffect(cb) : true ); } catch (e) { commitQueue.some(c => { if (c._renderCallbacks) c._renderCallbacks = []; }); commitQueue = []; options._catchError(e, component._vnode); } }); if (oldCommit) oldCommit(vnode, commitQueue); }; /** @type {(vnode: import('./internal').VNode) => void} */ options.unmount = vnode => { if (oldBeforeUnmount) oldBeforeUnmount(vnode); const c = vnode._component; if (c && c.__hooks) { let hasErrored; c.__hooks._list.some(s => { try { invokeCleanup(s); } catch (e) { hasErrored = e; } }); c.__hooks = undefined; if (hasErrored) options._catchError(hasErrored, c._vnode); } }; /** * Get a hook's state from the currentComponent * @param {number} index The index of the hook to get * @param {number} type The index of the hook to get * @returns {any} */ function getHookState(index, type) { if (options._hook) { options._hook(currentComponent, index, currentHook || type); } currentHook = 0; // Largely inspired by: // * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs // * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs // Other implementations to look at: // * https://codesandbox.io/s/mnox05qp8 const hooks = currentComponent.__hooks || (currentComponent.__hooks = { _list: [], _pendingEffects: [] }); if (index >= hooks._list.length) { hooks._list.push({}); } return hooks._list[index]; } /** * @template {unknown} S * @param {import('./index').Dispatch>} [initialState] * @returns {[S, (state: S) => void]} */ export function useState(initialState) { currentHook = 1; return useReducer(invokeOrReturn, initialState); } /** * @template {unknown} S * @template {unknown} A * @param {import('./index').Reducer} reducer * @param {import('./index').Dispatch>} initialState * @param {(initialState: any) => void} [init] * @returns {[ S, (state: S) => void ]} */ export function useReducer(reducer, initialState, init) { /** @type {import('./internal').ReducerHookState} */ const hookState = getHookState(currentIndex++, 2); hookState._reducer = reducer; if (!hookState._component) { hookState._value = [ !init ? invokeOrReturn(undefined, initialState) : init(initialState), action => { const currentValue = hookState._nextValue ? hookState._nextValue[0] : hookState._value[0]; const nextValue = hookState._reducer(currentValue, action); if (currentValue !== nextValue) { hookState._nextValue = [nextValue, hookState._value[1]]; hookState._component.setState({}); } } ]; hookState._component = currentComponent; if (!currentComponent._hasScuFromHooks) { currentComponent._hasScuFromHooks = true; let prevScu = currentComponent.shouldComponentUpdate; const prevCWU = currentComponent.componentWillUpdate; // If we're dealing with a forced update `shouldComponentUpdate` will // not be called. But we use that to update the hook values, so we // need to call it. currentComponent.componentWillUpdate = function (p, s, c) { if (this._force) { let tmp = prevScu; // Clear to avoid other sCU hooks from being called prevScu = undefined; updateHookState(p, s, c); prevScu = tmp; } if (prevCWU) prevCWU.call(this, p, s, c); }; // This SCU has the purpose of bailing out after repeated updates // to stateful hooks. // we store the next value in _nextValue[0] and keep doing that for all // state setters, if we have next states and // all next states within a component end up being equal to their original state // we are safe to bail out for this specific component. /** * * @type {import('./internal').Component["shouldComponentUpdate"]} */ // @ts-ignore - We don't use TS to downtranspile // eslint-disable-next-line no-inner-declarations function updateHookState(p, s, c) { if (!hookState._component.__hooks) return true; // We check whether we have components with a nextValue set that // have values that aren't equal to one another this pushes // us to update further down the tree let updatedHook = false; let shouldUpdate = hookState._component.props !== p; hookState._component.__hooks._list.some(hookItem => { if (hookItem._nextValue) { updatedHook = true; const currentValue = hookItem._value[0]; hookItem._value = hookItem._nextValue; hookItem._nextValue = undefined; if (currentValue !== hookItem._value[0]) shouldUpdate = true; } }); if (prevScu) { const result = prevScu.call(this, p, s, c); return updatedHook ? result || shouldUpdate : result; } return !updatedHook || shouldUpdate; } currentComponent.shouldComponentUpdate = updateHookState; } } return hookState._nextValue || hookState._value; } /** * @param {import('./internal').Effect} callback * @param {unknown[]} args * @returns {void} */ export function useEffect(callback, args) { /** @type {import('./internal').EffectHookState} */ const state = getHookState(currentIndex++, 3); if (!options._skipEffects && argsChanged(state._args, args)) { state._value = callback; state._pendingArgs = args; currentComponent.__hooks._pendingEffects.push(state); } } /** * @param {import('./internal').Effect} callback * @param {unknown[]} args * @returns {void} */ export function useLayoutEffect(callback, args) { /** @type {import('./internal').EffectHookState} */ const state = getHookState(currentIndex++, 4); if (!options._skipEffects && argsChanged(state._args, args)) { state._value = callback; state._pendingArgs = args; currentComponent._renderCallbacks.push(state); } } /** @type {(initialValue: unknown) => unknown} */ export function useRef(initialValue) { currentHook = 5; return useMemo(() => ({ current: initialValue }), []); } /** * @param {object} ref * @param {() => object} createHandle * @param {unknown[]} args * @returns {void} */ export function useImperativeHandle(ref, createHandle, args) { currentHook = 6; useLayoutEffect( () => { if (typeof ref == 'function') { const result = ref(createHandle()); return () => { ref(null); if (result && typeof result == 'function') result(); }; } else if (ref) { ref.current = createHandle(); return () => (ref.current = null); } }, args == null ? args : args.concat(ref) ); } /** * @template {unknown} T * @param {() => T} factory * @param {unknown[]} args * @returns {T} */ export function useMemo(factory, args) { /** @type {import('./internal').MemoHookState} */ const state = getHookState(currentIndex++, 7); if (argsChanged(state._args, args)) { state._value = factory(); state._args = args; state._factory = factory; } return state._value; } /** * @param {() => void} callback * @param {unknown[]} args * @returns {() => void} */ export function useCallback(callback, args) { currentHook = 8; return useMemo(() => callback, args); } /** * @param {import('./internal').PreactContext} context */ export function useContext(context) { const provider = currentComponent.context[context._id]; // We could skip this call here, but than we'd not call // `options._hook`. We need to do that in order to make // the devtools aware of this hook. /** @type {import('./internal').ContextHookState} */ const state = getHookState(currentIndex++, 9); // The devtools needs access to the context object to // be able to pull of the default value when no provider // is present in the tree. state._context = context; if (!provider) return context._defaultValue; // This is probably not safe to convert to "!" if (state._value == null) { state._value = true; provider.sub(currentComponent); } return provider.props.value; } /** * Display a custom label for a custom hook for the devtools panel * @type {(value: T, cb?: (value: T) => string | number) => void} */ export function useDebugValue(value, formatter) { if (options.useDebugValue) { options.useDebugValue( formatter ? formatter(value) : /** @type {any}*/ (value) ); } } /** * @param {(error: unknown, errorInfo: import('preact').ErrorInfo) => void} cb * @returns {[unknown, () => void]} */ export function useErrorBoundary(cb) { /** @type {import('./internal').ErrorBoundaryHookState} */ const state = getHookState(currentIndex++, 10); const errState = useState(); state._value = cb; if (!currentComponent.componentDidCatch) { currentComponent.componentDidCatch = (err, errorInfo) => { if (state._value) state._value(err, errorInfo); errState[1](err); }; } return [ errState[0], () => { errState[1](undefined); } ]; } /** @type {() => string} */ export function useId() { /** @type {import('./internal').IdHookState} */ const state = getHookState(currentIndex++, 11); if (!state._value) { // Grab either the root node or the nearest async boundary node. /** @type {import('./internal').VNode} */ let root = currentComponent._vnode; while (root !== null && !root._mask && root._parent !== null) { root = root._parent; } let mask = root._mask || (root._mask = [0, 0]); state._value = 'P' + mask[0] + '-' + mask[1]++; } return state._value; } /** * After paint effects consumer. */ function flushAfterPaintEffects() { let component; while ((component = afterPaintEffects.shift())) { const hooks = component.__hooks; if (!component._parentDom || !hooks) continue; try { hooks._pendingEffects.some(invokeCleanup); hooks._pendingEffects.some(invokeEffect); hooks._pendingEffects = []; } catch (e) { hooks._pendingEffects = []; options._catchError(e, component._vnode); } } } let HAS_RAF = typeof requestAnimationFrame == 'function'; /** * Schedule a callback to be invoked after the browser has a chance to paint a new frame. * Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after * the next browser frame. * * Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked * even if RAF doesn't fire (for example if the browser tab is not visible) * * @param {() => void} callback */ function afterNextFrame(callback) { const done = () => { clearTimeout(timeout); if (HAS_RAF) cancelAnimationFrame(raf); setTimeout(callback); }; const timeout = setTimeout(done, RAF_TIMEOUT); let raf; if (HAS_RAF) { raf = requestAnimationFrame(done); } } // Note: if someone used options.debounceRendering = requestAnimationFrame, // then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay. // Perhaps this is not such a big deal. /** * Schedule afterPaintEffects flush after the browser paints * @param {number} newQueueLength * @returns {void} */ function afterPaint(newQueueLength) { if (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) { prevRaf = options.requestAnimationFrame; (prevRaf || afterNextFrame)(flushAfterPaintEffects); } } /** * @param {import('./internal').HookState} hook * @returns {void} */ function invokeCleanup(hook) { // A hook cleanup can introduce a call to render which creates a new root, this will call options.vnode // and move the currentComponent away. const comp = currentComponent; let cleanup = hook._cleanup; if (typeof cleanup == 'function') { hook._cleanup = undefined; cleanup(); } currentComponent = comp; } /** * Invoke a Hook's effect * @param {import('./internal').EffectHookState} hook * @returns {void} */ function invokeEffect(hook) { // A hook call can introduce a call to render which creates a new root, this will call options.vnode // and move the currentComponent away. const comp = currentComponent; hook._cleanup = hook._value(); currentComponent = comp; } /** * @param {unknown[]} oldArgs * @param {unknown[]} newArgs * @returns {boolean} */ function argsChanged(oldArgs, newArgs) { return ( !oldArgs || oldArgs.length !== newArgs.length || newArgs.some((arg, index) => arg !== oldArgs[index]) ); } /** * @template Arg * @param {Arg} arg * @param {(arg: Arg) => any} f * @returns {any} */ function invokeOrReturn(arg, f) { return typeof f == 'function' ? f(arg) : f; } preactjs-preact-389c7bc/hooks/src/internal.d.ts000066400000000000000000000052651523334003000215460ustar00rootroot00000000000000import { Options as PreactOptions, Component as PreactComponent, VNode as PreactVNode, PreactContext, HookType, ErrorInfo } from '../../src/internal'; import { Reducer, StateUpdater } from '.'; export { PreactContext }; export interface Options extends PreactOptions { /** Attach a hook that is invoked before a vnode is diffed. */ _diff?(vnode: VNode): void; diffed?(vnode: VNode): void; /** Attach a hook that is invoked before a vnode has rendered. */ _render?(vnode: VNode): void; /** Attach a hook that is invoked after a tree was mounted or was updated. */ _commit?(vnode: VNode, commitQueue: Component[]): void; _unmount?(vnode: VNode): void; /** Attach a hook that is invoked before a hook's state is queried. */ _hook?(component: Component, index: number, type: HookType): void; } // Hook tracking export interface ComponentHooks { /** The list of hooks a component uses */ _list: HookState[]; /** List of Effects to be invoked after the next frame is rendered */ _pendingEffects: EffectHookState[]; } export interface Component extends Omit< PreactComponent, '_renderCallbacks' > { __hooks?: ComponentHooks; // Extend to include HookStates _renderCallbacks?: Array void)>; _hasScuFromHooks?: boolean; } export interface VNode extends Omit { _mask?: [number, number]; _component?: Component; // Override with our specific Component type } export type HookState = | EffectHookState | MemoHookState | ReducerHookState | ContextHookState | ErrorBoundaryHookState | IdHookState; interface BaseHookState { _value?: unknown; _nextValue?: unknown; _pendingValue?: unknown; _args?: unknown; _pendingArgs?: unknown; _component?: unknown; _cleanup?: unknown; } export type Effect = () => void | Cleanup; export type Cleanup = () => void; export interface EffectHookState extends BaseHookState { _value?: Effect; _args?: unknown[]; _pendingArgs?: unknown[]; _cleanup?: Cleanup | void; } export interface MemoHookState extends BaseHookState { _value?: T; _pendingValue?: T; _args?: unknown[]; _pendingArgs?: unknown[]; _factory?: () => T; } export interface ReducerHookState< S = unknown, A = unknown > extends BaseHookState { _nextValue?: [S, StateUpdater]; _value?: [S, StateUpdater]; _component?: Component; _reducer?: Reducer; } export interface ContextHookState extends BaseHookState { /** Whether this hooks as subscribed to updates yet */ _value?: boolean; _context?: PreactContext; } export interface ErrorBoundaryHookState extends BaseHookState { _value?: (error: unknown, errorInfo: ErrorInfo) => void; } export interface IdHookState extends BaseHookState { _value?: string; } preactjs-preact-389c7bc/hooks/test/000077500000000000000000000000001523334003000173205ustar00rootroot00000000000000preactjs-preact-389c7bc/hooks/test/_util/000077500000000000000000000000001523334003000204345ustar00rootroot00000000000000preactjs-preact-389c7bc/hooks/test/_util/useEffectUtil.js000066400000000000000000000002721523334003000235420ustar00rootroot00000000000000export function scheduleEffectAssert(assertFn) { return new Promise(resolve => { requestAnimationFrame(() => setTimeout(() => { assertFn(); resolve(); }, 0) ); }); } preactjs-preact-389c7bc/hooks/test/browser/000077500000000000000000000000001523334003000210035ustar00rootroot00000000000000preactjs-preact-389c7bc/hooks/test/browser/combinations.test.jsx000066400000000000000000000301651523334003000252010ustar00rootroot00000000000000import { setupRerender, act } from 'preact/test-utils'; import { createElement, render, Component, createContext } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useState, useReducer, useEffect, useLayoutEffect, useRef, useMemo, useContext } from 'preact/hooks'; import { scheduleEffectAssert } from '../_util/useEffectUtil'; /** @jsx createElement */ describe('combinations', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('can mix useState hooks', () => { const states = {}; const setStates = {}; function Parent() { const [state1, setState1] = useState(1); const [state2, setState2] = useState(2); Object.assign(states, { state1, state2 }); Object.assign(setStates, { setState1, setState2 }); return ; } function Child() { const [state3, setState3] = useState(3); const [state4, setState4] = useState(4); Object.assign(states, { state3, state4 }); Object.assign(setStates, { setState3, setState4 }); return null; } render(, scratch); expect(states).to.deep.equal({ state1: 1, state2: 2, state3: 3, state4: 4 }); setStates.setState2(n => n * 10); setStates.setState3(n => n * 10); rerender(); expect(states).to.deep.equal({ state1: 1, state2: 20, state3: 30, state4: 4 }); }); it('can rerender asynchronously from within an effect', () => { const didRender = sinon.spy(); function Comp() { const [counter, setCounter] = useState(0); useEffect(() => { if (counter === 0) setCounter(1); }); didRender(counter); return null; } render(, scratch); return scheduleEffectAssert(() => { rerender(); expect(didRender).to.have.been.calledTwice.and.calledWith(1); }); }); it('can rerender synchronously from within a layout effect', () => { const didRender = sinon.spy(); function Comp() { const [counter, setCounter] = useState(0); useLayoutEffect(() => { if (counter === 0) setCounter(1); }); didRender(counter); return null; } render(, scratch); rerender(); expect(didRender).to.have.been.calledTwice.and.calledWith(1); }); it('can access refs from within a layout effect callback', () => { let refAtLayoutTime; function Comp() { const input = useRef(); useLayoutEffect(() => { refAtLayoutTime = input.current; }); return ; } render(, scratch); expect(refAtLayoutTime.value).to.equal('hello'); }); it('can use multiple useState and useReducer hooks', () => { let states = []; let dispatchState4; function reducer1(state, action) { switch (action.type) { case 'increment': return state + action.count; } } function reducer2(state, action) { switch (action.type) { case 'increment': return state + action.count * 2; } } function Comp() { const [state1] = useState(0); const [state2] = useReducer(reducer1, 10); const [state3] = useState(1); const [state4, dispatch] = useReducer(reducer2, 20); dispatchState4 = dispatch; states.push(state1, state2, state3, state4); return null; } render(, scratch); expect(states).to.deep.equal([0, 10, 1, 20]); states = []; dispatchState4({ type: 'increment', count: 10 }); rerender(); expect(states).to.deep.equal([0, 10, 1, 40]); }); it('ensures useEffect always schedule after the next paint following a redraw effect, when using the default debounce strategy', () => { let effectCount = 0; function Comp() { const [counter, setCounter] = useState(0); useEffect(() => { if (counter === 0) setCounter(1); effectCount++; }); return null; } render(, scratch); return scheduleEffectAssert(() => { expect(effectCount).to.equal(1); }); }); it('should not reuse functional components with hooks', () => { let updater = { first: undefined, second: undefined }; function Foo(props) { let [v, setter] = useState(0); updater[props.id] = () => setter(++v); return
    {v}
    ; } let updateParent; class App extends Component { constructor(props) { super(props); this.state = { active: true }; updateParent = () => this.setState(p => ({ active: !p.active })); } render() { return (
    {this.state.active && }
    ); } } render(, scratch); act(() => updater.second()); expect(scratch.textContent).to.equal('01'); updateParent(); rerender(); expect(scratch.textContent).to.equal('1'); updateParent(); rerender(); expect(scratch.textContent).to.equal('01'); }); it('should have a right call order with correct dom ref', () => { let i = 0, set; const calls = []; function Inner() { useLayoutEffect(() => { calls.push('layout inner call ' + scratch.innerHTML); return () => calls.push('layout inner dispose ' + scratch.innerHTML); }); useEffect(() => { calls.push('effect inner call ' + scratch.innerHTML); return () => calls.push('effect inner dispose ' + scratch.innerHTML); }); return hello {i}; } function Outer() { i++; const [state, setState] = useState(false); set = () => setState(!state); useLayoutEffect(() => { calls.push('layout outer call ' + scratch.innerHTML); return () => calls.push('layout outer dispose ' + scratch.innerHTML); }); useEffect(() => { calls.push('effect outer call ' + scratch.innerHTML); return () => calls.push('effect outer dispose ' + scratch.innerHTML); }); return ; } act(() => render(, scratch)); expect(calls).to.deep.equal([ 'layout inner call hello 1', 'layout outer call hello 1', 'effect inner call hello 1', 'effect outer call hello 1' ]); // NOTE: this order is (at the time of writing) intentionally different from // React. React calls all disposes across all components, and then invokes all // effects across all components. We call disposes and effects in order of components: // for each component, call its disposes and then its effects. If presented with a // compelling use case to support inter-component dispose dependencies, then rewrite this // test to test React's order. In other words, if there is a use case to support calling // all disposes across components then re-order the lines below to demonstrate the desired behavior. act(() => set()); expect(calls).to.deep.equal([ 'layout inner call hello 1', 'layout outer call hello 1', 'effect inner call hello 1', 'effect outer call hello 1', 'layout inner dispose hello 2', 'layout inner call hello 2', 'layout outer dispose hello 2', 'layout outer call hello 2', 'effect inner dispose hello 2', 'effect inner call hello 2', 'effect outer dispose hello 2', 'effect outer call hello 2' ]); }); // TODO: I actually think this is an acceptable failure, because we update child first and then parent // the effects are out of order it.skip('should run effects child-first even for children separated by memoization', () => { let ops = []; /** @type {() => void} */ let updateChild; /** @type {() => void} */ let updateParent; function Child() { const [, setCount] = useState(0); updateChild = () => setCount(c => c + 1); useEffect(() => { ops.push('child effect'); }); return
    Child
    ; } function Parent() { const [, setCount] = useState(0); updateParent = () => setCount(c => c + 1); const memoedChild = useMemo(() => , []); useEffect(() => { ops.push('parent effect'); }); return (
    Parent
    {memoedChild}
    ); } act(() => render(, scratch)); expect(ops).to.deep.equal(['child effect', 'parent effect']); ops = []; updateChild(); updateParent(); act(() => rerender()); expect(ops).to.deep.equal(['child effect', 'parent effect']); }); it('should not block hook updates when context updates are enqueued', () => { const Ctx = createContext({ value: 0, setValue: /** @type {*} */ () => {} }); let triggerSubmit = () => {}; function Child() { const ctx = useContext(Ctx); const [shouldSubmit, setShouldSubmit] = useState(false); triggerSubmit = () => setShouldSubmit(true); useEffect(() => { if (shouldSubmit) { // Update parent state and child state at the same time ctx.setValue(v => v + 1); setShouldSubmit(false); } }, [shouldSubmit]); return

    {ctx.value}

    ; } function App() { const [value, setValue] = useState(0); const ctx = useMemo(() => { return { value, setValue }; }, [value]); return ( ); } act(() => { render(, scratch); }); expect(scratch.textContent).to.equal('0'); act(() => { triggerSubmit(); }); expect(scratch.textContent).to.equal('1'); // This is where the update wasn't applied act(() => { triggerSubmit(); }); expect(scratch.textContent).to.equal('2'); }); it('parent and child refs should be set before all effects', () => { const anchorId = 'anchor'; const tooltipId = 'tooltip'; const effectLog = []; let useRef2 = sinon.spy(init => { const realRef = useRef(init); const ref = useRef(init); Object.defineProperty(ref, 'current', { get: () => realRef.current, set: value => { realRef.current = value; effectLog.push('set ref ' + value?.tagName); } }); return ref; }); function Tooltip({ anchorRef, children }) { // For example, used to manually position the tooltip const tooltipRef = useRef2(null); useLayoutEffect(() => { expect(anchorRef.current?.id).to.equal(anchorId); expect(tooltipRef.current?.id).to.equal(tooltipId); effectLog.push('tooltip layout effect'); }, [anchorRef, tooltipRef]); useEffect(() => { expect(anchorRef.current?.id).to.equal(anchorId); expect(tooltipRef.current?.id).to.equal(tooltipId); effectLog.push('tooltip effect'); }, [anchorRef, tooltipRef]); return (
    {children}
    ); } function App() { // For example, used to define what element to anchor the tooltip to const anchorRef = useRef2(null); useLayoutEffect(() => { expect(anchorRef.current?.id).to.equal(anchorId); effectLog.push('anchor layout effect'); }, [anchorRef]); useEffect(() => { expect(anchorRef.current?.id).to.equal(anchorId); effectLog.push('anchor effect'); }, [anchorRef]); return (

    More info

    a tooltip
    ); } act(() => { render(, scratch); }); expect(effectLog).to.deep.equal([ 'set ref P', 'set ref DIV', 'tooltip layout effect', 'anchor layout effect', 'tooltip effect', 'anchor effect' ]); }); it('should not loop infinitely', () => { const actions = []; let toggle; function App() { const [value, setValue] = useState(false); const data = useMemo(() => { actions.push('memo'); return {}; }, [value]); const [prevData, setPreviousData] = useState(data); if (prevData !== data) { setPreviousData(data); } actions.push('render'); toggle = () => setValue(!value); return
    Value: {JSON.stringify(value)}
    ; } act(() => { render(, scratch); }); expect(actions).to.deep.equal(['memo', 'render']); expect(scratch.innerHTML).to.deep.equal('
    Value: false
    '); act(() => { toggle(); }); expect(actions).to.deep.equal([ 'memo', 'render', 'memo', 'render', 'render' ]); expect(scratch.innerHTML).to.deep.equal('
    Value: true
    '); }); }); preactjs-preact-389c7bc/hooks/test/browser/componentDidCatch.test.jsx000066400000000000000000000022221523334003000260730ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { act } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useEffect } from 'preact/hooks'; /** @jsx createElement */ describe('errorInfo', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should pass errorInfo on hook unmount error', () => { let info; let update; class Receiver extends Component { constructor(props) { super(props); this.state = { error: null, i: 0 }; update = this.setState.bind(this); } componentDidCatch(error, errorInfo) { info = errorInfo; this.setState({ error }); } render() { if (this.state.error) return
    ; if (this.state.i === 0) return ; return null; } } function ThrowErr() { useEffect(() => { return () => { throw new Error('fail'); }; }, []); return

    ; } act(() => { render(, scratch); }); act(() => { update({ i: 1 }); }); expect(info).to.deep.equal({}); }); }); preactjs-preact-389c7bc/hooks/test/browser/errorBoundary.test.jsx000066400000000000000000000125171523334003000253520ustar00rootroot00000000000000import { Fragment, createElement, render } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useErrorBoundary, useLayoutEffect, useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; /** @jsx createElement */ describe('errorBoundary', () => { /** @type {HTMLDivElement} */ let scratch, rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('catches errors', () => { let resetErr, success = false; const Throws = () => { throw new Error('test'); }; const App = props => { const [err, reset] = useErrorBoundary(); resetErr = reset; return err ?

    Error

    : success ?

    Success

    : ; }; render(, scratch); rerender(); expect(scratch.innerHTML).to.equal('

    Error

    '); success = true; resetErr(); rerender(); expect(scratch.innerHTML).to.equal('

    Success

    '); }); it('calls the errorBoundary callback', () => { const spy = sinon.spy(); const error = new Error('test'); const Throws = () => { throw error; }; const App = props => { const [err] = useErrorBoundary(spy); return err ?

    Error

    : ; }; render(, scratch); rerender(); expect(scratch.innerHTML).to.equal('

    Error

    '); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith(error, {}); }); it('returns error', () => { const error = new Error('test'); const Throws = () => { throw error; }; let returned; const App = () => { const [err] = useErrorBoundary(); returned = err; return err ?

    Error

    : ; }; render(, scratch); rerender(); expect(returned).to.equal(error); }); it('does not leave a stale closure', () => { const spy = sinon.spy(), spy2 = sinon.spy(); let resetErr; const error = new Error('test'); const Throws = () => { throw error; }; const App = props => { const [err, reset] = useErrorBoundary(props.onError); resetErr = reset; return err ?

    Error

    : ; }; render(, scratch); rerender(); expect(scratch.innerHTML).to.equal('

    Error

    '); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith(error); resetErr(); render(, scratch); rerender(); expect(spy).to.be.calledOnce; expect(spy2).to.be.calledOnce; expect(spy2).to.be.calledWith(error); expect(scratch.innerHTML).to.equal('

    Error

    '); }); it('does not invoke old effects when a cleanup callback throws an error and is handled', () => { let throwErr = false; let thrower = sinon.spy(() => { if (throwErr) { throw new Error('test'); } }); let badEffect = sinon.spy(() => thrower); let goodEffect = sinon.spy(); function EffectThrowsError() { useLayoutEffect(badEffect); return Test; } function Child({ children }) { useLayoutEffect(goodEffect); return children; } function App() { const [err] = useErrorBoundary(); return err ? (

    Error

    ) : ( ); } render(, scratch); expect(scratch.innerHTML).to.equal('Test'); expect(badEffect).to.be.calledOnce; expect(goodEffect).to.be.calledOnce; throwErr = true; render(, scratch); rerender(); expect(scratch.innerHTML).to.equal('

    Error

    '); expect(thrower).to.be.calledOnce; expect(badEffect).to.be.calledOnce; expect(goodEffect).to.be.calledOnce; }); it('should not duplicate in lists where an item throws and the parent catches and returns a differing type', () => { const baseTodos = [ { text: 'first item', completed: false }, { text: 'Test the feature', completed: false }, { text: 'another item', completed: false } ]; function TodoList() { const [todos, setTodos] = useState([...baseTodos]); return (
      {todos.map((todo, index) => ( { todos[index] = { ...todos[index], completed: !todos[index].completed }; setTodos([...todos]); }} todo={todo} index={index} /> ))}
    ); } function TodoItem(props) { const [error] = useErrorBoundary(); if (error) { return
  • An error occurred: {error}
  • ; } return ; } let set; function TodoItemInner({ todo, index, toggleTodo }) { if (todo.completed) { throw new Error('Todo completed!'); } if (index === 1) { set = toggleTodo; } return (
  • ); } render(, scratch); expect(scratch.innerHTML).to.equal( '
    ' ); set(); rerender(); expect(scratch.innerHTML).to.equal( '
    • An error occurred:
    ' ); }); }); preactjs-preact-389c7bc/hooks/test/browser/hooks.options.test.jsx000066400000000000000000000061641523334003000253330ustar00rootroot00000000000000import { afterDiffSpy, beforeRenderSpy, unmountSpy, hookSpy } from '../../../test/_util/optionSpies'; import { setupRerender, act } from 'preact/test-utils'; import { createElement, render, createContext, options } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useState, useReducer, useEffect, useLayoutEffect, useRef, useImperativeHandle, useMemo, useCallback, useContext, useErrorBoundary } from 'preact/hooks'; /** @jsx createElement */ describe('hook options', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; /** @type {() => void} */ let increment; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); afterDiffSpy.resetHistory(); unmountSpy.resetHistory(); beforeRenderSpy.resetHistory(); hookSpy.resetHistory(); }); afterEach(() => { teardown(scratch); }); function App() { const [count, setCount] = useState(0); increment = () => setCount(prevCount => prevCount + 1); return
    {count}
    ; } it('should call old options on mount', () => { render(, scratch); expect(beforeRenderSpy).to.have.been.called; expect(afterDiffSpy).to.have.been.called; }); it('should call old options.diffed on update', () => { render(, scratch); increment(); rerender(); expect(beforeRenderSpy).to.have.been.called; expect(afterDiffSpy).to.have.been.called; }); it('should call old options on unmount', () => { render(, scratch); render(null, scratch); expect(unmountSpy).to.have.been.called; }); it('should detect hooks', () => { const USE_STATE = 1; const USE_REDUCER = 2; const USE_EFFECT = 3; const USE_LAYOUT_EFFECT = 4; const USE_REF = 5; const USE_IMPERATIVE_HANDLE = 6; const USE_MEMO = 7; const USE_CALLBACK = 8; const USE_CONTEXT = 9; const USE_ERROR_BOUNDARY = 10; const Ctx = createContext(null); function App() { useState(0); useReducer(x => x, 0); useEffect(() => null, []); useLayoutEffect(() => null, []); const ref = useRef(null); useImperativeHandle(ref, () => null); useMemo(() => null, []); useCallback(() => null, []); useContext(Ctx); useErrorBoundary(() => null); } render( , scratch ); expect(hookSpy.args.map(arg => [arg[1], arg[2]])).to.deep.equal([ [0, USE_STATE], [1, USE_REDUCER], [2, USE_EFFECT], [3, USE_LAYOUT_EFFECT], [4, USE_REF], [5, USE_IMPERATIVE_HANDLE], [6, USE_MEMO], [7, USE_CALLBACK], [8, USE_CONTEXT], [9, USE_ERROR_BOUNDARY], // Belongs to useErrorBoundary that uses multiple native hooks. [10, USE_STATE] ]); }); describe('Effects', () => { beforeEach(() => { options._skipEffects = options.__s = true; }); afterEach(() => { options._skipEffects = options.__s = false; }); it('should skip effect hooks', () => { const spy = sinon.spy(); function App() { useEffect(spy, []); useLayoutEffect(spy, []); return null; } act(() => { render(, scratch); }); expect(spy.callCount).to.equal(0); }); }); }); preactjs-preact-389c7bc/hooks/test/browser/useCallback.test.jsx000066400000000000000000000017151523334003000247240ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useCallback } from 'preact/hooks'; /** @jsx createElement */ describe('useCallback', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('only recomputes the callback when inputs change', () => { const callbacks = []; function Comp({ a, b }) { const cb = useCallback(() => a + b, [a, b]); callbacks.push(cb); return null; } render(, scratch); render(, scratch); expect(callbacks[0]).to.equal(callbacks[1]); expect(callbacks[0]()).to.equal(2); render(, scratch); render(, scratch); expect(callbacks[1]).to.not.equal(callbacks[2]); expect(callbacks[2]).to.equal(callbacks[3]); expect(callbacks[2]()).to.equal(3); }); }); preactjs-preact-389c7bc/hooks/test/browser/useContext.test.jsx000066400000000000000000000201141523334003000246460ustar00rootroot00000000000000import { createElement, render, createContext, Component } from 'preact'; import { act } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useContext, useEffect, useState } from 'preact/hooks'; import { vi } from 'vitest'; /** @jsx createElement */ describe('useContext', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('gets values from context', () => { const values = []; const Context = createContext(13); function Comp() { const value = useContext(Context); values.push(value); return null; } render(, scratch); render( , scratch ); render( , scratch ); expect(values).to.deep.equal([13, 42, 69]); }); it('should use default value', () => { const Foo = createContext(42); const spy = sinon.spy(); function App() { spy(useContext(Foo)); return
    ; } render(, scratch); expect(spy).to.be.calledWith(42); }); it('should update when value changes with nonUpdating Component on top', async () => { const spy = sinon.spy(); const Ctx = createContext(0); class NoUpdate extends Component { shouldComponentUpdate() { return false; } render() { return this.props.children; } } function App(props) { return ( ); } function Comp() { const value = useContext(Ctx); spy(value); return

    {value}

    ; } render(, scratch); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith(0); render(, scratch); return new Promise(resolve => { // Wait for enqueued hook update setTimeout(() => { // Should not be called a third time expect(spy).to.be.calledTwice; expect(spy).to.be.calledWith(1); resolve(); }, 0); }); }); it('should only update when value has changed', async () => { const spy = sinon.spy(); const Ctx = createContext(0); function App(props) { return ( ); } function Comp() { const value = useContext(Ctx); spy(value); return

    {value}

    ; } render(, scratch); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith(0); render(, scratch); expect(spy).to.be.calledTwice; expect(spy).to.be.calledWith(1); return new Promise(resolve => { // Wait for enqueued hook update setTimeout(() => { // Should not be called a third time expect(spy).to.be.calledTwice; resolve(); }, 0); }); }); it('should allow multiple context hooks at the same time', () => { const Foo = createContext(0); const Bar = createContext(10); const spy = sinon.spy(); const unmountspy = sinon.spy(); function Comp() { const foo = useContext(Foo); const bar = useContext(Bar); spy(foo, bar); useEffect(() => () => unmountspy()); return
    ; } render( , scratch ); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith(0, 10); render( , scratch ); expect(spy).to.be.calledTwice; expect(unmountspy).not.to.be.called; }); it('should only subscribe a component once', () => { const values = []; const Context = createContext(13); let provider, subSpy; function Comp() { const value = useContext(Context); values.push(value); return null; } render(, scratch); render( (provider = p)} value={42}> , scratch ); subSpy = sinon.spy(provider, 'sub'); render( , scratch ); expect(subSpy).to.not.have.been.called; expect(values).to.deep.equal([13, 42, 69]); }); it('should only subscribe a component once (non-provider)', () => { const values = []; const Context = createContext(13); let provider, subSpy; function Comp() { const value = useContext(Context); values.push(value); return null; } render(, scratch); render( (provider = p)} value={42}> , scratch ); subSpy = sinon.spy(provider, 'sub'); render( , scratch ); expect(subSpy).to.not.have.been.called; expect(values).to.deep.equal([13, 42, 69]); }); it('should maintain context', async () => { const context = createContext(null); const { Provider } = context; const first = { name: 'first' }; const second = { name: 'second' }; const Input = () => { const config = useContext(context); // Avoid eslint complaining about unused first value const state = useState('initial'); const set = state[1]; useEffect(() => { // Schedule the update on the next frame requestAnimationFrame(() => { set('irrelevant'); }); }, [config]); return
    {config.name}
    ; }; const App = props => { const [config, setConfig] = useState({}); useEffect(() => { setConfig(props.config); }, [props.config]); return ( ); }; act(() => { render(, scratch); // Create a new div to append the `second` case const div = scratch.appendChild(document.createElement('div')); render(, div); }); return new Promise(resolve => { // Push the expect into the next frame requestAnimationFrame(() => { expect(scratch.innerHTML).equal( '
    first
    second
    ' ); resolve(); }); }); }); it('should not rerender consumers that have been unmounted', () => { const context = createContext(0); const Provider = context.Provider; const Inner = vi.fn(() => { const value = useContext(context); return
    {value}
    ; }); let toggleConsumer; let changeValue; class App extends Component { constructor() { super(); this.state = { value: 0, show: true }; changeValue = value => this.setState({ value }); toggleConsumer = () => this.setState(({ show }) => ({ show: !show })); } render(props, state) { return (
    {state.show ? : null}
    ); } } render(, scratch); expect(scratch.innerHTML).to.equal('
    0
    '); expect(Inner).toHaveBeenCalledOnce(); act(() => changeValue(1)); expect(scratch.innerHTML).to.equal('
    1
    '); expect(Inner).toHaveBeenCalledTimes(2); act(() => toggleConsumer()); expect(scratch.innerHTML).to.equal('
    '); expect(Inner).toHaveBeenCalledTimes(2); act(() => changeValue(2)); expect(scratch.innerHTML).to.equal('
    '); expect(Inner).toHaveBeenCalledTimes(2); }); it('should rerender when reset to defaultValue', () => { const defaultValue = { state: 'hi' }; const context = createContext(defaultValue); let set; const Consumer = () => { const ctx = useContext(context); return

    {ctx.state}

    ; }; class NoUpdate extends Component { shouldComponentUpdate() { return false; } render() { return ; } } const Provider = () => { const [state, setState] = useState(defaultValue); set = setState; return ( ); }; render(, scratch); expect(scratch.innerHTML).to.equal('

    hi

    '); act(() => { set({ state: 'bye' }); }); expect(scratch.innerHTML).to.equal('

    bye

    '); act(() => { set(defaultValue); }); expect(scratch.innerHTML).to.equal('

    hi

    '); }); }); preactjs-preact-389c7bc/hooks/test/browser/useDebugValue.test.jsx000066400000000000000000000026271523334003000252560ustar00rootroot00000000000000import { createElement, render, options } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useDebugValue, useState } from 'preact/hooks'; /** @jsx createElement */ describe('useDebugValue', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); delete options.useDebugValue; }); it('should do nothing when no options hook is present', () => { function useFoo() { useDebugValue('foo'); return useState(0); } function App() { let [v] = useFoo(); return
    {v}
    ; } expect(() => render(, scratch)).to.not.throw(); }); it('should call options hook with value', () => { let spy = (options.useDebugValue = sinon.spy()); function useFoo() { useDebugValue('foo'); return useState(0); } function App() { let [v] = useFoo(); return
    {v}
    ; } render(, scratch); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith('foo'); }); it('should apply optional formatter', () => { let spy = (options.useDebugValue = sinon.spy()); function useFoo() { useDebugValue('foo', x => x + 'bar'); return useState(0); } function App() { let [v] = useFoo(); return
    {v}
    ; } render(, scratch); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWith('foobar'); }); }); preactjs-preact-389c7bc/hooks/test/browser/useEffect.test.jsx000066400000000000000000000337201523334003000244250ustar00rootroot00000000000000import { act, teardown as teardownAct } from 'preact/test-utils'; import { createElement, render, Fragment, Component } from 'preact'; import { useEffect, useState, useRef } from 'preact/hooks'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useEffectAssertions } from './useEffectAssertions'; import { scheduleEffectAssert } from '../_util/useEffectUtil'; /** @jsx createElement */ describe('useEffect', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); useEffectAssertions(useEffect, scheduleEffectAssert); it('calls the effect immediately if another render is about to start', () => { const cleanupFunction = sinon.spy(); const callback = sinon.spy(() => cleanupFunction); function Comp() { useEffect(callback); return null; } render(, scratch); render(, scratch); expect(cleanupFunction).to.be.not.called; expect(callback).to.be.calledOnce; render(, scratch); expect(cleanupFunction).to.be.calledOnce; expect(callback).to.be.calledTwice; }); it('cancels the effect when the component get unmounted before it had the chance to run it', () => { const cleanupFunction = sinon.spy(); const callback = sinon.spy(() => cleanupFunction); function Comp() { useEffect(callback); return null; } render(, scratch); render(null, scratch); return scheduleEffectAssert(() => { expect(cleanupFunction).to.not.be.called; expect(callback).to.not.be.called; }); }); it('should execute multiple effects in same component in the right order', () => { let executionOrder = []; const App = ({ i }) => { executionOrder = []; useEffect(() => { executionOrder.push('action1'); return () => executionOrder.push('cleanup1'); }, [i]); useEffect(() => { executionOrder.push('action2'); return () => executionOrder.push('cleanup2'); }, [i]); return

    Test

    ; }; act(() => render(, scratch)); act(() => render(, scratch)); expect(executionOrder).to.deep.equal([ 'cleanup1', 'cleanup2', 'action1', 'action2' ]); }); it('should execute effects in parent if child throws in effect', async () => { let executionOrder = []; const Child = () => { useEffect(() => { executionOrder.push('child'); throw new Error('test'); }, []); useEffect(() => { executionOrder.push('child after throw'); return () => executionOrder.push('child after throw cleanup'); }, []); return

    Test

    ; }; const Parent = () => { useEffect(() => { executionOrder.push('parent'); return () => executionOrder.push('parent cleanup'); }, []); return ; }; class ErrorBoundary extends Component { componentDidCatch(error) { this.setState({ error }); } render({ children }, { error }) { return error ?
    error
    : children; } } act(() => render( , scratch ) ); expect(executionOrder).to.deep.equal(['child', 'parent', 'parent cleanup']); expect(scratch.innerHTML).to.equal('
    error
    '); }); it('should throw an error upwards', () => { const spy = sinon.spy(); let errored = false; const Page1 = () => { const [state, setState] = useState('loading'); useEffect(() => { setState('loaded'); }, []); return

    {state}

    ; }; const Page2 = () => { useEffect(() => { throw new Error('err'); }, []); return

    invisible

    ; }; class App extends Component { componentDidCatch(err) { spy(); errored = err; this.forceUpdate(); } render(props, state) { if (errored) { return

    Error

    ; } return {props.page === 1 ? : }; } } act(() => render(, scratch)); expect(spy).to.not.be.called; expect(scratch.innerHTML).to.equal('

    loaded

    '); act(() => render(, scratch)); expect(spy).to.be.calledOnce; expect(scratch.innerHTML).to.equal('

    Error

    '); errored = false; act(() => render(, scratch)); expect(spy).to.be.calledOnce; expect(scratch.innerHTML).to.equal('

    loaded

    '); }); it('should throw an error upwards from return', () => { const spy = sinon.spy(); let errored = false; const Page1 = () => { const [state, setState] = useState('loading'); useEffect(() => { setState('loaded'); }, []); return

    {state}

    ; }; const Page2 = () => { useEffect(() => { return () => { throw new Error('err'); }; }, []); return

    Load

    ; }; class App extends Component { componentDidCatch(err) { spy(); errored = err; this.forceUpdate(); } render(props, state) { if (errored) { return

    Error

    ; } return {props.page === 1 ? : }; } } act(() => render(, scratch)); expect(scratch.innerHTML).to.equal('

    Load

    '); act(() => render(, scratch)); expect(spy).to.be.calledOnce; expect(scratch.innerHTML).to.equal('

    Error

    '); }); it('catches errors when error is invoked during render', () => { const spy = sinon.spy(); let errored; function Comp() { useEffect(() => { throw new Error('hi'); }); return null; } class App extends Component { componentDidCatch(err) { spy(); errored = err; this.forceUpdate(); } render(props, state) { if (errored) { return

    Error

    ; } return ; } } render(, scratch); act(() => { render(, scratch); }); expect(spy).to.be.calledOnce; expect(errored).to.be.an('Error').with.property('message', 'hi'); expect(scratch.innerHTML).to.equal('

    Error

    '); }); it('should allow creating a new root', () => { const root = document.createElement('div'); const global = document.createElement('div'); scratch.appendChild(root); scratch.appendChild(global); const Modal = props => { let [, setCanProceed] = useState(true); let ChildProp = props.content; return (
    ); }; const Inner = () => { useEffect(() => { render(
    global
    , global); }, []); return
    Inner
    ; }; act(() => { render( { props.setCanProceed(false); return ; }} />, root ); }); expect(scratch.innerHTML).to.equal( '
    Inner
    global
    ' ); }); it('should not crash when effect returns truthy non-function value', () => { const callback = sinon.spy(() => 'truthy'); function Comp() { useEffect(callback); return null; } render(, scratch); render(, scratch); expect(callback).to.have.been.calledOnce; render(
    Replacement
    , scratch); }); it('support render roots from an effect', async () => { let promise, increment; const Counter = () => { const [count, setCount] = useState(0); const renderRoot = useRef(); useEffect(() => { if (count > 0) { const div = renderRoot.current; return () => render(, div); } return () => 'test'; }, [count]); increment = () => { setCount(x => x + 1); promise = new Promise(res => { setTimeout(() => { setCount(x => x + 1); res(); }); }); }; return (
    Count: {count}
    ); }; const Dummy = () =>
    dummy
    ; render(, scratch); expect(scratch.innerHTML).to.equal( '
    Count: 0
    ' ); act(() => { increment(); }); await promise; act(() => {}); expect(scratch.innerHTML).to.equal( '
    Count: 2
    dummy
    ' ); }); it('hooks should be called in right order', async () => { teardownAct(); let increment; const Counter = () => { const [count, setCount] = useState(0); useState('binggo!!'); const renderRoot = useRef(); useEffect(() => { const div = renderRoot.current; render(, div); }, [count]); increment = () => { setCount(x => x + 1); return Promise.resolve().then(() => setCount(x => x + 1)); }; return (
    Count: {count}
    ); }; const Dummy = () => { useState(); return
    dummy
    ; }; render(, scratch); expect(scratch.innerHTML).to.equal( '
    Count: 0
    ' ); /** Using the act function will affect the timing of the useEffect */ await increment(); expect(scratch.innerHTML).to.equal( '
    Count: 2
    dummy
    ' ); }); it('handles errors correctly', () => { class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { error: null }; } componentDidCatch(error) { this.setState({ error: 'oh no' }); } render() { return this.state.error ? (

    Error! {this.state.error}

    ) : ( this.props.children ); } } let update; const firstEffectSpy = sinon.spy(); const firstEffectcleanup = sinon.spy(); const secondEffectSpy = sinon.spy(); const secondEffectcleanup = sinon.spy(); const MainContent = () => { const [val, setVal] = useState(false); update = () => setVal(!val); useEffect(() => { firstEffectSpy(); return () => { firstEffectcleanup(); throw new Error('oops'); }; }, [val]); useEffect(() => { secondEffectSpy(); return () => { secondEffectcleanup(); }; }, []); return

    Hello world

    ; }; act(() => { render( , scratch ); }); expect(firstEffectSpy).to.be.calledOnce; expect(secondEffectSpy).to.be.calledOnce; act(() => { update(); }); expect(firstEffectSpy).to.be.calledOnce; expect(secondEffectSpy).to.be.calledOnce; expect(firstEffectcleanup).to.be.calledOnce; expect(secondEffectcleanup).to.be.calledOnce; }); it('orders effects effectively', () => { const calls = []; const GrandChild = ({ id }) => { useEffect(() => { calls.push(`${id} - Effect`); return () => { calls.push(`${id} - Cleanup`); }; }, [id]); return

    {id}

    ; }; const Child = ({ id }) => { useEffect(() => { calls.push(`${id} - Effect`); return () => { calls.push(`${id} - Cleanup`); }; }, [id]); return ( ); }; function Parent() { useEffect(() => { calls.push('Parent - Effect'); return () => { calls.push('Parent - Cleanup'); }; }, []); return (
    ); } act(() => { render(, scratch); }); expect(calls).to.deep.equal([ 'Child-1-GrandChild-1 - Effect', 'Child-1-GrandChild-2 - Effect', 'Child-1 - Effect', 'Child-2-GrandChild-1 - Effect', 'Child-2-GrandChild-2 - Effect', 'Child-2 - Effect', 'Child-3-GrandChild-1 - Effect', 'Child-3-GrandChild-2 - Effect', 'Child-3 - Effect', 'Parent - Effect' ]); }); it('should cancel effects from a disposed render', () => { const calls = []; const App = () => { const [greeting, setGreeting] = useState('bye'); useEffect(() => { calls.push('doing effect' + greeting); return () => { calls.push('cleaning up' + greeting); }; }, [greeting]); if (greeting === 'bye') { setGreeting('hi'); } return

    {greeting}

    ; }; act(() => { render(, scratch); }); expect(calls.length).to.equal(1); expect(calls).to.deep.equal(['doing effecthi']); }); it('should not rerun committed effects', () => { const calls = []; const App = ({ i }) => { const [greeting, setGreeting] = useState('hi'); useEffect(() => { calls.push('doing effect' + greeting); return () => { calls.push('cleaning up' + greeting); }; }, []); if (i === 2) { setGreeting('bye'); } return

    {greeting}

    ; }; act(() => { render(, scratch); }); expect(calls.length).to.equal(1); expect(calls).to.deep.equal(['doing effecthi']); act(() => { render(, scratch); }); }); it('should not schedule effects that have no change', () => { const calls = []; let set; const App = ({ i }) => { const [greeting, setGreeting] = useState('hi'); set = setGreeting; useEffect(() => { calls.push('doing effect' + greeting); return () => { calls.push('cleaning up' + greeting); }; }, [greeting]); if (greeting === 'bye') { setGreeting('hi'); } return

    {greeting}

    ; }; act(() => { render(, scratch); }); expect(calls.length).to.equal(1); expect(calls).to.deep.equal(['doing effecthi']); act(() => { set('bye'); }); expect(calls.length).to.equal(1); expect(calls).to.deep.equal(['doing effecthi']); }); it('should not crash when effect throws and component is unmounted by render(null) during flush', () => { // In flushAfterPaintEffects(): // 1. Guard checks component.__hooks — truthy, passes // 2. invokeEffect runs the effect callback // 3. The callback calls render(null, scratch) which unmounts the tree // → options.unmount sets component.__hooks = undefined // 4. Resetting the hooks array to an empty array would throw an error let setVal; function App() { const [val, _setVal] = useState(0); setVal = _setVal; useEffect(() => { if (val === 1) { render(null, scratch); } }, [val]); return
    val: {val}
    ; } act(() => { render(, scratch); }); act(() => { setVal(1); }); }); }); preactjs-preact-389c7bc/hooks/test/browser/useEffectAssertions.jsx000066400000000000000000000072441523334003000255240ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ // Common behaviors between all effect hooks export function useEffectAssertions(useEffect, scheduleEffectAssert) { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('performs the effect after every render by default', () => { const callback = sinon.spy(); function Comp() { useEffect(callback); return null; } render(, scratch); return scheduleEffectAssert(() => expect(callback).to.be.calledOnce) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledOnce)) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledTwice) ); }); it('performs the effect only if one of the inputs changed', () => { const callback = sinon.spy(); function Comp(props) { useEffect(callback, [props.a, props.b]); return null; } render(, scratch); return scheduleEffectAssert(() => expect(callback).to.be.calledOnce) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledOnce)) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledTwice) ) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledTwice) ); }); it('performs the effect at mount time and never again if an empty input Array is passed', () => { const callback = sinon.spy(); function Comp() { useEffect(callback, []); return null; } render(, scratch); render(, scratch); expect(callback).to.be.calledOnce; return scheduleEffectAssert(() => expect(callback).to.be.calledOnce) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledOnce) ); }); it('calls the cleanup function followed by the effect after each render', () => { const cleanupFunction = sinon.spy(); const callback = sinon.spy(() => cleanupFunction); function Comp() { useEffect(callback); return null; } render(, scratch); return scheduleEffectAssert(() => { expect(cleanupFunction).to.be.not.called; expect(callback).to.be.calledOnce; }) .then(() => scheduleEffectAssert(() => expect(callback).to.be.calledOnce)) .then(() => render(, scratch)) .then(() => scheduleEffectAssert(() => { expect(cleanupFunction).to.be.calledOnce; expect(callback).to.be.calledTwice; expect(callback.lastCall.calledAfter(cleanupFunction.lastCall)); }) ); }); it('cleanups the effect when the component get unmounted if the effect was called before', () => { const cleanupFunction = sinon.spy(); const callback = sinon.spy(() => cleanupFunction); function Comp() { useEffect(callback); return null; } render(, scratch); return scheduleEffectAssert(() => { render(null, scratch); rerender(); expect(cleanupFunction).to.be.calledOnce; }); }); it('works with closure effect callbacks capturing props', () => { const values = []; function Comp(props) { useEffect(() => values.push(props.value)); return null; } render(, scratch); render(, scratch); return scheduleEffectAssert(() => expect(values).to.deep.equal([1, 2])); }); } preactjs-preact-389c7bc/hooks/test/browser/useId.test.jsx000066400000000000000000000232051523334003000235620ustar00rootroot00000000000000import { createElement, Fragment, hydrate, render } from 'preact'; import { useId, useReducer, useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; import { render as rts } from 'preact-render-to-string'; import { setupScratch, teardown } from '../../../test/_util/helpers'; /** @jsx createElement */ describe('useId', () => { /** @type {HTMLDivElement} */ let scratch, rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('keeps the id consistent after an update', () => { function Comp() { const id = useId(); return
    ; } render(, scratch); const id = scratch.firstChild.getAttribute('id'); expect(scratch.firstChild.getAttribute('id')).to.equal(id); render(, scratch); expect(scratch.firstChild.getAttribute('id')).to.equal(id); }); it('ids are unique according to dom-depth', () => { function Child() { const id = useId(); const spanId = useId(); return (
    h
    ); } function Comp() { const id = useId(); return (
    ); } render(, scratch); expect(scratch.innerHTML).to.equal( '
    h
    ' ); render(, scratch); expect(scratch.innerHTML).to.equal( '
    h
    ' ); }); it('ids are unique across siblings', () => { function Child() { const id = useId(); return h; } function Comp() { const id = useId(); return (
    ); } render(, scratch); expect(scratch.innerHTML).to.equal( '
    hhh
    ' ); render(, scratch); expect(scratch.innerHTML).to.equal( '
    hhh
    ' ); }); it('correctly handles new elements', () => { let set; function Child() { const id = useId(); return h; } function Stateful() { const [state, setState] = useState(false); set = setState; return (
    {state && }
    ); } function Comp() { const id = useId(); return (
    ); } render(, scratch); expect(scratch.innerHTML).to.equal( '
    h
    ' ); set(true); rerender(); expect(scratch.innerHTML).to.equal( '
    hh
    ' ); }); it('matches with rts', () => { const ChildFragmentReturn = ({ children }) => { return {children}; }; const ChildReturn = ({ children }) => { return children; }; const SomeMessage = ({ msg }) => { const id = useId(); return (

    {msg} {id}

    ); }; const Stateful = () => { const [count, add] = useReducer(c => c + 1, 0); const id = useId(); return (
    id: {id}, count: {count}
    ); }; const Component = ({ showStateful = false }) => { const rootId = useId(); const paragraphId = useId(); return (
    ID: {rootId}

    Hello world id: {paragraphId}

    {showStateful ? ( ) : ( )}
    ); }; const rtsOutput = rts(); render(, scratch); expect(rtsOutput === scratch.innerHTML).to.equal(true); }); it('matches with rts after hydration', () => { const ChildFragmentReturn = ({ children }) => { return {children}; }; const ChildReturn = ({ children }) => { return children; }; const SomeMessage = ({ msg }) => { const id = useId(); return (

    {msg} {id}

    ); }; const Stateful = () => { const [count, add] = useReducer(c => c + 1, 0); const id = useId(); return (
    id: {id}, count: {count}
    ); }; const Component = ({ showStateful = false }) => { const rootId = useId(); const paragraphId = useId(); return (
    ID: {rootId}

    Hello world id: {paragraphId}

    {showStateful ? ( ) : ( )}
    ); }; const rtsOutput = rts(); scratch.innerHTML = rtsOutput; hydrate(, scratch); expect(rtsOutput).to.equal(scratch.innerHTML); }); it('should be unique across Fragments', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return

    {id}

    ; } function App() { return (
    ); } render(, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should match implicite Fragments with RTS', () => { function Foo() { const id = useId(); return

    {id}

    ; } function Bar(props) { return props.children; } function App() { return ( ); } const rtsOutput = rts(); scratch.innerHTML = rtsOutput; hydrate(, scratch); expect(rtsOutput).to.equal(scratch.innerHTML); }); it('should skip component top level Fragment child', () => { const Wrapper = ({ children }) => { return {children}; }; const ids = []; function Foo() { const id = useId(); ids.push(id); return

    {id}

    ; } function App() { const id = useId(); ids.push(id); return (

    {id}

    ); } render(, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should skip over HTML', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return

    {id}

    ; } function App() { return (
    ); } render(, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should reset for each renderToString roots', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return

    {id}

    ; } function App() { return (
    ); } const res1 = rts(); const res2 = rts(); expect(res1).to.equal(res2); }); it('should work with conditional components', () => { function Foo() { const id = useId(); return

    {id}

    ; } function Bar() { const id = useId(); return

    {id}

    ; } let update; function App() { const [v, setV] = useState(false); update = setV; return
    {!v ? : }
    ; } render(, scratch); const first = scratch.innerHTML; update(v => !v); rerender(); expect(first).not.to.equal(scratch.innerHTML); }); it('should return a unique id across invocations of render', () => { const Id = () => { const id = useId(); return
    My id is {id}
    ; }; const App = props => { return (
    {props.secondId ? : null}
    ); }; render(createElement(App, { secondId: false }), scratch); expect(scratch.innerHTML).to.equal('
    My id is P0-0
    '); render(createElement(App, { secondId: true }), scratch); expect(scratch.innerHTML).to.equal( '
    My id is P0-0
    My id is P0-1
    ' ); }); it('should not crash for rendering null after a non-null render', () => { const Id = () => { const id = useId(); return
    My id is {id}
    ; }; const App = props => { return (
    {props.secondId ? : null}
    ); }; render(createElement(App, { secondId: false }), scratch); expect(scratch.innerHTML).to.equal('
    My id is P0-0
    '); render(null, scratch); expect(scratch.innerHTML).to.equal(''); }); }); preactjs-preact-389c7bc/hooks/test/browser/useImperativeHandle.test.jsx000066400000000000000000000133561523334003000264550ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { setupScratch, teardown } from '../../../test/_util/helpers'; import { useImperativeHandle, useRef, useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; /** @jsx createElement */ describe('useImperativeHandle', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('Mutates given ref', () => { let ref; function Comp() { ref = useRef({}); useImperativeHandle(ref, () => ({ test: () => 'test' }), []); return

    Test

    ; } render(, scratch); expect(ref.current).to.have.property('test'); expect(ref.current.test()).to.equal('test'); }); it('Calls ref unmounting function', () => { let ref; const unmount = sinon.spy(); function Comp() { useImperativeHandle( r => { ref = r; return unmount; }, () => ({ test: () => 'test' }), [] ); return

    Test

    ; } render(, scratch); expect(ref).to.have.property('test'); expect(ref.test()).to.equal('test'); render(null, scratch); expect(unmount).to.be.calledOnce; expect(ref).to.equal(null); }); it('calls createHandle after every render by default', () => { let ref, createHandleSpy = sinon.spy(); function Comp() { ref = useRef({}); useImperativeHandle(ref, createHandleSpy); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; render(, scratch); expect(createHandleSpy).to.have.been.calledTwice; render(, scratch); expect(createHandleSpy).to.have.been.calledThrice; }); it('calls createHandle only on mount if an empty array is passed', () => { let ref, createHandleSpy = sinon.spy(); function Comp() { ref = useRef({}); useImperativeHandle(ref, createHandleSpy, []); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; }); it('Updates given ref when args change', () => { let ref, createHandleSpy = sinon.spy(); function Comp({ a }) { ref = useRef({}); useImperativeHandle(ref, () => { createHandleSpy(); return { test: () => 'test' + a }; }, [a]); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref.current).to.have.property('test'); expect(ref.current.test()).to.equal('test0'); render(, scratch); expect(createHandleSpy).to.have.been.calledTwice; expect(ref.current).to.have.property('test'); expect(ref.current.test()).to.equal('test1'); render(, scratch); expect(createHandleSpy).to.have.been.calledThrice; expect(ref.current).to.have.property('test'); expect(ref.current.test()).to.equal('test0'); }); it('Updates given ref when passed-in ref changes', () => { let ref1, ref2; /** @type {(arg: any) => void} */ let setRef; /** @type {() => void} */ let updateState; const createHandleSpy = sinon.spy(() => ({ test: () => 'test' })); function Comp() { ref1 = useRef({}); ref2 = useRef({}); const [ref, setRefInternal] = useState(ref1); setRef = setRefInternal; let [value, setState] = useState(0); updateState = () => setState((value + 1) % 2); useImperativeHandle(ref, createHandleSpy, []); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; updateState(); rerender(); expect(createHandleSpy).to.have.been.calledOnce; setRef(ref2); rerender(); expect(createHandleSpy).to.have.been.calledTwice; updateState(); rerender(); expect(createHandleSpy).to.have.been.calledTwice; setRef(ref1); rerender(); expect(createHandleSpy).to.have.been.calledThrice; }); it('should not update ref when args have not changed', () => { let ref, createHandleSpy = sinon.spy(() => ({ test: () => 'test' })); function Comp() { ref = useRef({}); useImperativeHandle(ref, createHandleSpy, [1]); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref.current.test()).to.equal('test'); render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref.current.test()).to.equal('test'); }); it('should not throw with nullish ref', () => { function Comp() { useImperativeHandle(null, () => ({ test: () => 'test' }), [1]); return

    Test

    ; } expect(() => render(, scratch)).to.not.throw(); }); it('should reset ref object to null when the component get unmounted', () => { let ref, createHandleSpy = sinon.spy(() => ({ test: () => 'test' })); function Comp() { ref = useRef({}); useImperativeHandle(ref, createHandleSpy, [1]); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref.current).to.not.equal(null); render(
    , scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref.current).to.equal(null); }); it('should reset ref callback to null when the component get unmounted', () => { const ref = sinon.spy(); const handle = { test: () => 'test' }; const createHandleSpy = sinon.spy(() => handle); function Comp() { useImperativeHandle(ref, createHandleSpy, [1]); return

    Test

    ; } render(, scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref).to.have.been.calledWith(handle); ref.resetHistory(); render(
    , scratch); expect(createHandleSpy).to.have.been.calledOnce; expect(ref).to.have.been.calledWith(null); }); }); preactjs-preact-389c7bc/hooks/test/browser/useLayoutEffect.test.jsx000066400000000000000000000257041523334003000256260ustar00rootroot00000000000000import { act } from 'preact/test-utils'; import { createElement, render, Fragment, Component } from 'preact'; import { setupScratch, teardown, serializeHtml } from '../../../test/_util/helpers'; import { useEffectAssertions } from './useEffectAssertions'; import { useLayoutEffect, useRef, useState } from 'preact/hooks'; /** @jsx createElement */ describe('useLayoutEffect', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); // Layout effects fire synchronously const scheduleEffectAssert = assertFn => new Promise(resolve => { assertFn(); resolve(); }); useEffectAssertions(useLayoutEffect, scheduleEffectAssert); it('calls the effect immediately after render', () => { const cleanupFunction = sinon.spy(); const callback = sinon.spy(() => cleanupFunction); function Comp() { useLayoutEffect(callback); return null; } render(, scratch); render(, scratch); expect(cleanupFunction).to.be.calledOnce; expect(callback).to.be.calledTwice; render(, scratch); expect(cleanupFunction).to.be.calledTwice; expect(callback).to.be.calledThrice; }); it('works on a nested component', () => { const callback = sinon.spy(); function Parent() { return (
    ); } function Child() { useLayoutEffect(callback); return null; } render(, scratch); expect(callback).to.be.calledOnce; }); it('should execute multiple layout effects in same component in the right order', () => { let executionOrder = []; const App = ({ i }) => { executionOrder = []; useLayoutEffect(() => { executionOrder.push('action1'); return () => executionOrder.push('cleanup1'); }, [i]); useLayoutEffect(() => { executionOrder.push('action2'); return () => executionOrder.push('cleanup2'); }, [i]); return

    Test

    ; }; render(, scratch); render(, scratch); expect(executionOrder).to.deep.equal([ 'cleanup1', 'cleanup2', 'action1', 'action2' ]); }); it('should correctly display DOM', () => { function AutoResizeTextareaLayoutEffect(props) { const ref = useRef(null); useLayoutEffect(() => { // IE & Edge put textarea's value as child of textarea when reading innerHTML so use // cross browser serialize helper const actualHtml = serializeHtml(scratch); const expectedHTML = `

    ${props.value}

    `; expect(actualHtml).to.equal(expectedHTML); expect(document.body.contains(ref.current)).to.equal(true); }); return (

    {props.value}

    '; hydrate('; hydrate( ); } } function Suspender() { throw promise; } scratch.innerHTML = ''; expect(() => { hydrate(, scratch); }).to.not.throw(); expect(caught).to.equal(promise); }); it('should pass the original error to boundaries when hydrating without excess DOM children', () => { let caught; const error = new Error('real error'); class Boundary extends Component { componentDidCatch(error) { caught = error; this.setState({ error }); } render() { return this.state && this.state.error ?
    Error!
    : ; } } function Wrapper() { return ( ); } function Boom() { throw error; } scratch.innerHTML = ''; hydrate(, scratch); rerender(); expect(caught).to.equal(error); expect(caught.message).to.equal('real error'); }); }); preactjs-preact-389c7bc/test/browser/isValidElement.test.jsx000066400000000000000000000003201523334003000242640ustar00rootroot00000000000000import { createElement, isValidElement, Component } from 'preact'; import { isValidElementTests } from '../shared/isValidElementTests'; isValidElementTests(expect, isValidElement, createElement, Component); preactjs-preact-389c7bc/test/browser/keys.test.jsx000066400000000000000000000772101523334003000223460ustar00rootroot00000000000000import { createElement, Component, render, createRef, createContext, Fragment } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../_util/helpers'; import { logCall, clearLog, getLog } from '../_util/logCall'; import { div } from '../_util/dom'; /** @jsx createElement */ describe('keys', () => { /** @type {HTMLDivElement} */ let scratch; let rerender; /** @type {string[]} */ let ops; function createStateful(name) { return class Stateful extends Component { componentDidUpdate() { ops.push(`Update ${name}`); } componentDidMount() { ops.push(`Mount ${name}`); } componentWillUnmount() { ops.push(`Unmount ${name}`); } render() { return
    {name}
    ; } }; } /** @type {(props: {values: any[]}) => any} */ const List = props => (
      {props.values.map(value => (
    1. {value}
    2. ))}
    ); /** * Move an element in an array from one index to another * @param {any[]} values The array of values * @param {number} from The index to move from * @param {number} to The index to move to */ function move(values, from, to) { const value = values[from]; values.splice(from, 1); values.splice(to, 0, value); } let resetAppendChild; let resetInsertBefore; let resetRemoveChild; let resetRemove; beforeAll(() => { resetAppendChild = logCall(Element.prototype, 'appendChild'); resetInsertBefore = logCall(Element.prototype, 'insertBefore'); resetRemoveChild = logCall(Element.prototype, 'removeChild'); resetRemove = logCall(Element.prototype, 'remove'); }); afterAll(() => { resetAppendChild(); resetInsertBefore(); resetRemoveChild(); resetRemove(); }); beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); ops = []; }); afterEach(() => { teardown(scratch); clearLog(); }); // https://fb.me/react-special-props it('should not pass key in props', () => { const Foo = sinon.spy(function Foo() { return null; }); render(, scratch); expect(Foo.args[0][0]).to.deep.equal({}); }); it('should update in-place keyed DOM nodes', () => { render(
    • a
    • b
    • c
    , scratch ); expect(scratch.innerHTML).to.equal( '
    • a
    • b
    • c
    ' ); render(
    • x
    • y
    • z
    , scratch ); expect(scratch.innerHTML).to.equal( '
    • x
    • y
    • z
    ' ); }); // See preactjs/preact-compat#21 it('should remove orphaned keyed nodes', () => { render(
    1
  • a
  • b
  • , scratch ); render(
    2
  • b
  • c
  • , scratch ); expect(scratch.innerHTML).to.equal( '
    2
  • b
  • c
  • ' ); }); it('should remove keyed nodes (#232)', () => { class App extends Component { componentDidMount() { setTimeout(() => this.setState({ opened: true, loading: true }), 10); setTimeout(() => this.setState({ opened: true, loading: false }), 20); } render({ opened, loading }) { return (
    This div needs to be here for this to break
    {opened && !loading &&
    {[]}
    }
    ); } } class BusyIndicator extends Component { render({ children, busy }) { return (
    {children && children.length ? ( children ) : (
    )}
    indicator
    indicator
    indicator
    ); } } render(, scratch); render(, scratch); render(, scratch); const html = String(scratch.firstChild.innerHTML).replace(/ class=""/g, ''); expect(html).to.equal( '
    This div needs to be here for this to break
    indicator
    indicator
    indicator
    ' ); }); it('should append new keyed elements', () => { const values = ['a', 'b']; render(, scratch); expect(scratch.textContent).to.equal('ab'); values.push('c'); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abc'); expect(getLog()).to.deep.equal([ '
  • .appendChild(#text)', '
      ab.appendChild(
    1. c)' ]); }); it('should remove keyed elements from the end', () => { const values = ['a', 'b', 'c', 'd']; render(, scratch); expect(scratch.textContent).to.equal('abcd'); values.pop(); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abc'); expect(getLog()).to.deep.equal(['
    2. d.remove()']); }); it('should prepend keyed elements to the beginning', () => { const values = ['b', 'c']; render(, scratch); expect(scratch.textContent).to.equal('bc'); values.unshift('a'); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abc'); expect(getLog()).to.deep.equal([ '
    3. .appendChild(#text)', '
        bc.insertBefore(
      1. a,
      2. b)' ]); }); it('should remove keyed elements from the beginning', () => { const values = ['z', 'a', 'b', 'c']; render(, scratch); expect(scratch.textContent).to.equal('zabc'); values.shift(); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abc'); expect(getLog()).to.deep.equal(['
      3. z.remove()']); }); it('should insert new keyed children in the middle', () => { const values = ['a', 'c']; render(, scratch); expect(scratch.textContent).to.equal('ac'); values.splice(1, 0, 'b'); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abc'); expect(getLog()).to.deep.equal([ '
      4. .appendChild(#text)', '
          ac.insertBefore(
        1. b,
        2. c)' ]); }); it('should remove keyed children from the middle', () => { const values = ['a', 'b', 'x', 'y', 'z', 'c', 'd']; render(, scratch); expect(scratch.textContent).to.equal('abxyzcd'); values.splice(2, 3); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abcd'); expect(getLog()).to.deep.equal([ '
        3. x.remove()', '
        4. y.remove()', '
        5. z.remove()' ]); }); it('should move keyed children to the beginning', () => { const values = ['b', 'c', 'd', 'a']; render(, scratch); expect(scratch.textContent).to.equal('bcda'); move(values, values.length - 1, 0); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abcd'); expect(getLog()).to.deep.equal(['
            bcda.insertBefore(
          1. a,
          2. b)']); }); it('should move multiple keyed children to the beginning', () => { const values = ['c', 'd', 'e', 'a', 'b']; render(, scratch); expect(scratch.textContent).to.equal('cdeab'); move(values, values.length - 1, 0); move(values, values.length - 1, 0); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abcde'); expect(getLog()).to.deep.equal([ '
              cdeab.insertBefore(
            1. a,
            2. c)', '
                acdeb.insertBefore(
              1. b,
              2. c)' ]); }); it('should swap keyed children efficiently', () => { render(, scratch); expect(scratch.textContent).to.equal('ab'); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('ba'); expect(getLog()).to.deep.equal(['
                  ab.appendChild(
                1. a)']); }); it('should swap existing keyed children in the middle of a list efficiently', () => { const values = ['a', 'b', 'c', 'd']; render(, scratch); expect(scratch.textContent).to.equal('abcd'); // swap move(values, 1, 2); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('acbd', 'initial swap'); expect(getLog()).to.deep.equal( ['
                    abcd.insertBefore(
                  1. b,
                  2. d)'], 'initial swap' ); // swap back move(values, 2, 1); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abcd', 'swap back'); expect(getLog()).to.deep.equal( ['
                      acbd.insertBefore(
                    1. c,
                    2. d)'], 'swap back' ); }); it('should move keyed children to the end of the list', () => { const values = ['a', 'b', 'c', 'd']; render(, scratch); expect(scratch.textContent).to.equal('abcd'); // move to end move(values, 0, values.length - 1); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('bcda', 'move to end'); expect(getLog()).to.deep.equal( ['
                        abcd.appendChild(
                      1. a)'], 'move to end' ); // move to beginning move(values, values.length - 1, 0); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('abcd', 'move to beginning'); expect(getLog()).to.deep.equal( ['
                          bcda.insertBefore(
                        1. a,
                        2. b)'], 'move to beginning' ); }); it('should move keyed children to the beginning on longer list', () => { // Preact v10 worst case const values = ['a', 'b', 'c', 'd', 'e', 'f']; render(, scratch); expect(scratch.textContent).to.equal('abcdef'); move(values, 4, 1); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('aebcdf'); expect(getLog()).to.deep.equal(['
                            abcdef.insertBefore(
                          1. e,
                          2. b)']); }); it('should move keyed children to the end on longer list', () => { const values = ['a', 'b', 'c', 'd', 'e', 'f']; render(, scratch); expect(scratch.textContent).to.equal('abcdef'); move(values, 1, values.length - 2); clearLog(); render(, scratch); expect(scratch.textContent).to.equal('acdebf'); expect(getLog()).to.deep.equal(['
                              abcdef.insertBefore(
                            1. b,
                            2. f)']); }); it('should reverse keyed children effectively', () => { const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; render(, scratch); expect(scratch.textContent).to.equal(values.join('')); // reverse list values.reverse(); clearLog(); render(, scratch); expect(scratch.textContent).to.equal(values.join('')); // expect(getLog()).to.have.lengthOf(9); expect(getLog()).to.deep.equal([ '
                                abcdefghij.insertBefore(
                              1. j,
                              2. a)', '
                                  jabcdefghi.insertBefore(
                                1. i,
                                2. a)', '
                                    jiabcdefgh.insertBefore(
                                  1. h,
                                  2. a)', '
                                      jihabcdefg.insertBefore(
                                    1. g,
                                    2. a)', '
                                        jihgabcdef.insertBefore(
                                      1. f,
                                      2. a)', '
                                          jihgfabcde.insertBefore(
                                        1. e,
                                        2. a)', '
                                            jihgfeabcd.insertBefore(
                                          1. d,
                                          2. a)', '
                                              jihgfedabc.insertBefore(
                                            1. c,
                                            2. a)', '
                                                jihgfedcab.appendChild(
                                              1. a)' ]); }); it('should properly remove children of memoed components', () => { const values = [1, 2, 3, 4, 5, 6, 7, 8, 9]; class Item extends Component { shouldComponentUpdate(props) { return props.value !== this.props.value; } render() { return
                                              2. {this.props.value}
                                              3. ; } } function App({ values }) { return (
                                                  {values.map(value => ( ))}
                                                ); } render(, scratch); expect(scratch.textContent).to.equal(values.join('')); clearLog(); values.splice(3, 3); render(, scratch); expect(scratch.textContent).to.equal(values.join('')); expect(getLog()).to.deep.equal([ '
                                              4. 4.remove()', '
                                              5. 5.remove()', '
                                              6. 6.remove()' ]); }); it("should not preserve state when a component's keys are different", () => { const Stateful = createStateful('Stateful'); function Foo({ condition }) { return condition ? : ; } ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal(['Mount Stateful'], 'initial mount'); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal( ['Unmount Stateful', 'Mount Stateful'], 'switching keys 1' ); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal( ['Unmount Stateful', 'Mount Stateful'], 'switching keys 2' ); }); it('should not preserve state between an unkeyed and keyed component', () => { // React and Preact v8 behavior: https://codesandbox.io/s/57prmy5mx const Stateful = createStateful('Stateful'); function Foo({ keyed }) { return keyed ? : ; } ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal(['Mount Stateful'], 'initial mount with key'); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal( ['Unmount Stateful', 'Mount Stateful'], 'switching from keyed to unkeyed' ); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal('
                                                Stateful
                                                '); expect(ops).to.deep.equal( ['Unmount Stateful', 'Mount Stateful'], 'switching from unkeyed to keyed' ); }); it('should not preserve state when keys change with multiple children', () => { // React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj const Stateful1 = createStateful('Stateful1'); const Stateful2 = createStateful('Stateful2'); let Stateful1Ref; let Stateful2Ref; let Stateful1MovedRef; let Stateful2MovedRef; function Foo({ moved }) { return moved ? (
                                                1
                                                (Stateful1MovedRef = c)} />
                                                2
                                                (Stateful2MovedRef = c)} />
                                                ) : (
                                                1
                                                (Stateful1Ref = c)} />
                                                2
                                                (Stateful2Ref = c)} />
                                                ); } const expectedHtml = div([ div(1), div('Stateful1'), div(2), div('Stateful2') ]); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']); expect(Stateful1Ref).to.exist; expect(Stateful2Ref).to.exist; ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal([ 'Unmount Stateful1', 'Unmount Stateful2', 'Mount Stateful1', 'Mount Stateful2' ]); expect(Stateful1MovedRef).to.not.equal(Stateful1Ref); expect(Stateful2MovedRef).to.not.equal(Stateful2Ref); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal([ 'Unmount Stateful1', 'Unmount Stateful2', 'Mount Stateful1', 'Mount Stateful2' ]); expect(Stateful1Ref).to.not.equal(Stateful1MovedRef); expect(Stateful2Ref).to.not.equal(Stateful2MovedRef); }); it('should preserve state when moving keyed children components', () => { // React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj const Stateful1 = createStateful('Stateful1'); const Stateful2 = createStateful('Stateful2'); let Stateful1Ref; let Stateful2Ref; let Stateful1MovedRef; let Stateful2MovedRef; function Foo({ moved }) { return moved ? (
                                                1
                                                (c ? (Stateful2MovedRef = c) : undefined)} />
                                                2
                                                (c ? (Stateful1MovedRef = c) : undefined)} />
                                                ) : (
                                                1
                                                (c ? (Stateful1Ref = c) : undefined)} />
                                                2
                                                (c ? (Stateful2Ref = c) : undefined)} />
                                                ); } const htmlForFalse = div([ div(1), div('Stateful1'), div(2), div('Stateful2') ]); const htmlForTrue = div([ div(1), div('Stateful2'), div(2), div('Stateful1') ]); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(htmlForFalse); expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']); expect(Stateful1Ref).to.exist; expect(Stateful2Ref).to.exist; ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(htmlForTrue); expect(ops).to.deep.equal(['Update Stateful2', 'Update Stateful1']); expect(Stateful1MovedRef).to.equal(Stateful1Ref); expect(Stateful2MovedRef).to.equal(Stateful2Ref); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(htmlForFalse); expect(ops).to.deep.equal(['Update Stateful1', 'Update Stateful2']); expect(Stateful1Ref).to.equal(Stateful1MovedRef); expect(Stateful2Ref).to.equal(Stateful2MovedRef); }); it('should effectively iterate on large lists', async () => { const newItems = () => Array(100) .fill(0) .map((item, i) => i); let set, mutatedNodes = []; class App extends Component { constructor(props) { super(props); this.state = { items: newItems() }; set = this.set = this.set.bind(this); this.ref = createRef(); } componentDidMount() { const observer = new MutationObserver(listener); observer.observe(this.ref.current, { childList: true }); function listener(mutations) { for (const { addedNodes } of mutations) { for (const node of Array.from(addedNodes)) { mutatedNodes.push(node); } } } } set() { const currentItems = this.state.items; const items = newItems().filter(id => { const isVisible = currentItems.includes(id); return id >= 20 && id <= 80 ? !isVisible : isVisible; }); this.setState({ items }); } render() { return (
                                                {this.state.items.map(i => (
                                                {i}
                                                ))}
                                                ); } } render(, scratch); set(); rerender(); return new Promise(resolve => { setTimeout(() => { expect(mutatedNodes.length).to.equal(0); resolve(); }); }); }); it('should effectively iterate on large component lists', async () => { const newItems = () => Array(100) .fill(0) .map((item, i) => i); let set, mutatedNodes = []; const Row = ({ i }) =>

                                                {i}

                                                ; class App extends Component { constructor(props) { super(props); this.state = { items: newItems() }; set = this.set = this.set.bind(this); this.ref = createRef(); } componentDidMount() { const observer = new MutationObserver(listener); observer.observe(this.ref.current, { childList: true }); function listener(mutations) { for (const { addedNodes } of mutations) { for (const node of Array.from(addedNodes)) { mutatedNodes.push(node); } } } } set() { const currentItems = this.state.items; const items = newItems().filter(id => { const isVisible = currentItems.includes(id); return id >= 20 && id <= 80 ? !isVisible : isVisible; }); this.setState({ items }); } render() { return (
                                                {this.state.items.map(i => ( ))}
                                                ); } } render(, scratch); set(); rerender(); return new Promise(resolve => { setTimeout(() => { expect(mutatedNodes.length).to.equal(0); resolve(); }); }); }); it('should not preserve state when switching between keyed and unkeyed components as children', () => { // React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj const Stateful1 = createStateful('Stateful1'); const Stateful2 = createStateful('Stateful2'); let Stateful1Ref; let Stateful2Ref; let Stateful1MovedRef; let Stateful2MovedRef; function Foo({ unkeyed }) { return unkeyed ? (
                                                1
                                                (Stateful2MovedRef = c)} />
                                                2
                                                (Stateful1MovedRef = c)} />
                                                ) : (
                                                1
                                                (Stateful1Ref = c)} />
                                                2
                                                (Stateful2Ref = c)} />
                                                ); } const expectedHtml = div([ div(1), div('Stateful1'), div(2), div('Stateful2') ]); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']); expect(Stateful1Ref).to.exist; expect(Stateful2Ref).to.exist; ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal([ 'Unmount Stateful1', 'Unmount Stateful2', 'Mount Stateful1', 'Mount Stateful2' ]); expect(Stateful1MovedRef).to.not.equal(Stateful1Ref); expect(Stateful2MovedRef).to.not.equal(Stateful2Ref); ops = []; render(, scratch); expect(scratch.innerHTML).to.equal(expectedHtml); expect(ops).to.deep.equal([ 'Unmount Stateful1', 'Unmount Stateful2', 'Mount Stateful1', 'Mount Stateful2' ]); expect(Stateful1Ref).to.not.equal(Stateful1MovedRef); expect(Stateful2Ref).to.not.equal(Stateful2MovedRef); }); it('should handle full reorders', () => { const keys = { Apple: `Apple_1`, Orange: `Orange_1`, Banana: `Banana_1`, Grape: `Grape_1`, Kiwi: `Kiwi_1`, Cherry: `Cherry_1` }; let sort; class App extends Component { order; state = { items: ['Apple', 'Grape', 'Cherry', 'Orange', 'Banana'] }; sort() { this.order = this.order === 'ASC' ? 'DESC' : 'ASC'; const items = [...this.state.items].sort((a, b) => this.order === 'ASC' ? a.localeCompare(b) : b.localeCompare(a) ); this.setState({ items }); } render(_, { items }) { sort = this.sort.bind(this); return (
                                                {items.map(item => (
                                                {item}
                                                ))}
                                                ); } } const expected = values => { return values.map(key => `
                                                ${key}
                                                `).join(''); }; render(, scratch); expect(scratch.innerHTML).to.eq( `
                                                ${expected(['Apple', 'Grape', 'Cherry', 'Orange', 'Banana'])}
                                                ` ); let sorted = ['Apple', 'Grape', 'Cherry', 'Orange', 'Banana'].sort((a, b) => a.localeCompare(b) ); sort(); rerender(); expect(scratch.innerHTML).to.eq(`
                                                ${expected(sorted)}
                                                `); sorted = ['Apple', 'Grape', 'Cherry', 'Orange', 'Banana'].sort((a, b) => b.localeCompare(a) ); sort(); rerender(); expect(scratch.innerHTML).to.eq(`
                                                ${expected(sorted)}
                                                `); }); it('should handle keyed replacements', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
                                                Hello
                                                ; } } const App = props => { return (
                                                {false}
                                                ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal([ 'mounted 1', 'mounted 2', 'mounted 3', 'mounted 1' ]); }); it('should handle hole prepend', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
                                                Hello
                                                ; } } const App = props => { return props.y === '2' ? (
                                                ) : (
                                                {null}
                                                ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); }); it('should handle hole replace', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
                                                Hello
                                                ; } } const App = props => { return props.y === '1' ? (
                                                ) : (
                                                {null}
                                                ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal([ 'mounted 1', 'mounted 2', 'mounted 3', 'mounted 2' ]); }); it('should handle hole insert', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
                                                Hello
                                                ; } } const App = props => { return props.y === '2' ? (
                                                ) : (
                                                {null}
                                                ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); }); it('should handle hole apppend', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
                                                Hello
                                                ; } } const App = props => { return props.y === '2' ? (
                                                ) : (
                                                {null}
                                                ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); }); // Issue #4973: Test growing list diff it('should correctly diff a growing list of keyed children', () => { let values = [0, 1, 2, 3, 4]; render(, scratch); expect(scratch.textContent).to.equal('01234'); values = [2, 3, 4, 5, 6]; clearLog(); render(, scratch); expect(scratch.textContent).to.equal('23456'); expect(getLog()).to.deep.equal([ '
                                              7. 0.remove()', '
                                              8. 1.remove()', '
                                              9. .appendChild(#text)', '
                                                  234.appendChild(
                                                1. 5)', '
                                                2. .appendChild(#text)', '
                                                    2345.appendChild(
                                                  1. 6)' ]); }); // https://github.com/preactjs/preact/issues/5065 it('should maintain correct DOM order with conditional ContextProvider and inner keys', () => { const Ctx = createContext(null); class Label extends Component { render({ todo }) { return
                                                  2. {todo.text}
                                                  3. ; } } class App extends Component { render() { const { todos } = this.props; return (
                                                      {todos.map((todo, index) => todo.text === '1' || todo.text === '2' || todo.text === '3' ? ( ) : (
                                                    ); } } // Initial: 3 collapsed rows render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('123'); // Expand row 1: adds sub-items 1A, 1B, 1C (not wrapped in Provider) render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('11A1B1C23'); // Expand row 2: adds sub-items 2A, 2B, 2C render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('11A1B1C22A2B2C3'); // Collapse row 1: removes 1A, 1B, 1C render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('122A2B2C3'); }); // https://github.com/preactjs/preact/issues/5065 it('should maintain correct DOM order when collapsing row 2 with conditional Provider', () => { const Ctx = createContext(null); class Label extends Component { render({ todo }) { return
                                                  4. {todo.text}
                                                  5. ; } } class App extends Component { render() { const { todos } = this.props; return (
                                                      {todos.map((todo, index) => todo.text === '1' || todo.text === '2' || todo.text === '3' ? ( ) : (
                                                    ); } } // Both rows expanded render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('11A1B1C22A2B2C3'); // Collapse row 2: removes 2A, 2B, 2C render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('11A1B1C23'); }); // https://github.com/preactjs/preact/issues/5065 it('should maintain correct DOM order expanding then collapsing with conditional Provider', () => { const Ctx = createContext(null); class Label extends Component { render({ todo }) { return
                                                  6. {todo.text}
                                                  7. ; } } class App extends Component { render() { const { todos } = this.props; return (
                                                      {todos.map((todo, index) => todo.text === '1' || todo.text === '2' || todo.text === '3' ? ( ) : (
                                                    ); } } // Initial: 3 rows collapsed render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('123'); // Expand row 1 render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('11A1B1C23'); // Collapse row 1 render( , scratch ); expect(scratch.querySelector('ul').textContent).to.equal('123'); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/000077500000000000000000000000001523334003000220025ustar00rootroot00000000000000preactjs-preact-389c7bc/test/browser/lifecycles/componentDidCatch.test.jsx000066400000000000000000000416231523334003000271020ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component, Fragment } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /* eslint-disable react/display-name */ /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentDidCatch', () => { /** @type {Error} */ let expectedError; /** @type {typeof import('../../../').Component} */ let ThrowErr; class Receiver extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? String(this.state.error) : this.props.children; } } let thrower; sinon.spy(Receiver.prototype, 'componentDidCatch'); sinon.spy(Receiver.prototype, 'render'); function throwExpectedError() { throw (expectedError = new Error('Error!')); } beforeEach(() => { ThrowErr = class ThrowErr extends Component { constructor(props) { super(props); thrower = this; } componentDidCatch() { expect.fail("Throwing component should not catch it's own error."); } render() { return
                                                    ThrowErr: componentDidCatch
                                                    ; } }; sinon.spy(ThrowErr.prototype, 'componentDidCatch'); expectedError = undefined; Receiver.prototype.componentDidCatch.resetHistory(); Receiver.prototype.render.resetHistory(); }); afterEach(() => { expect( ThrowErr.prototype.componentDidCatch, "Throwing component should not catch it's own error." ).to.not.be.called; thrower = undefined; }); it('should be called when child fails in constructor', () => { class ThrowErr extends Component { constructor(props, context) { super(props, context); throwExpectedError(); } componentDidCatch() { expect.fail("Throwing component should not catch it's own error"); } render() { return
                                                    ; } } render( , scratch ); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); // https://github.com/preactjs/preact/issues/1570 it('should handle double child throws', () => { const Child = ({ i }) => { throw new Error(`error! ${i}`); }; const fn = () => render( {[1, 2].map(i => ( ))} , scratch ); expect(fn).to.not.throw(); rerender(); expect(scratch.innerHTML).to.equal('Error: error! 2'); }); it('should be called when child fails in componentWillMount', () => { ThrowErr.prototype.componentWillMount = throwExpectedError; render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in render', () => { ThrowErr.prototype.render = throwExpectedError; render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should discard commit callbacks from a partially rendered subtree', () => { let componentDidMount = sinon.spy(); class NullBoundary extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? null : this.props.children; } } class MountingChild extends Component { componentDidMount() { componentDidMount(); } render() { return null; } } function Parent() { return [ , // Throw from diffing after MountingChild has queued its mount // callback, but before Parent has completed rendering. createElement('div', { 'bad name': 'x' }) ]; } render( , scratch ); expect(componentDidMount).not.to.have.been.called; rerender(); expect(componentDidMount).not.to.have.been.called; expect(scratch.innerHTML).to.equal(''); }); it('should clean up pending updates in a partially rendered subtree when rendering null', () => { let child; let childRenders = 0; let componentDidMount = sinon.spy(); class NullBoundary extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? null : this.props.children; } } class NullChild extends Component { constructor(props) { super(props); child = this; } componentDidMount() { componentDidMount(); } render() { childRenders++; if (!this.state.tick) this.setState({ tick: 1 }); return null; } } function Parent() { return [ , // Throw from diffing after NullChild has been diffed, but before // Parent has completed rendering its children. createElement('div', { 'bad name': 'x' }) ]; } render( , scratch ); expect(childRenders).to.equal(1); expect(scratch.innerHTML).to.equal(''); expect(() => rerender()).not.to.throw(); expect(childRenders).to.equal(1); expect(componentDidMount).not.to.have.been.called; expect(child._parentDom).to.equal(null); expect(scratch.innerHTML).to.equal(''); }); it('should be called when child fails in componentDidMount', () => { ThrowErr.prototype.componentDidMount = throwExpectedError; render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in getDerivedStateFromProps', () => { ThrowErr.getDerivedStateFromProps = throwExpectedError; sinon.spy(ThrowErr.prototype, 'render'); render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); expect(ThrowErr.prototype.render).not.to.have.been.called; }); it('should be called when child fails in getSnapshotBeforeUpdate', () => { ThrowErr.prototype.getSnapshotBeforeUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentDidUpdate', () => { ThrowErr.prototype.componentDidUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillUpdate', () => { ThrowErr.prototype.componentWillUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillReceiveProps', () => { ThrowErr.prototype.componentWillReceiveProps = throwExpectedError; let receiver; class Receiver extends Component { constructor() { super(); this.state = { foo: 'bar' }; receiver = this; } componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver.prototype, 'componentDidCatch'); render(, scratch); receiver.setState({ foo: 'baz' }); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in shouldComponentUpdate', () => { ThrowErr.prototype.shouldComponentUpdate = throwExpectedError; let receiver; class Receiver extends Component { constructor() { super(); this.state = { foo: 'bar' }; receiver = this; } componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver.prototype, 'componentDidCatch'); render(, scratch); receiver.setState({ foo: 'baz' }); rerender(); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillUnmount', () => { ThrowErr.prototype.componentWillUnmount = throwExpectedError; render( , scratch ); render(
                                                    , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when applying a Component ref', () => { const Foo = () =>
                                                    ; const ref = value => { if (value) { throwExpectedError(); } }; // In React, an error boundary handles it's own refs: // https://codesandbox.io/s/react-throwing-refs-lk958 class Receiver extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver.prototype, 'componentDidCatch'); render(, scratch); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when applying a DOM ref', () => { const ref = value => { if (value) { throwExpectedError(); } }; // In React, an error boundary handles it's own refs: // https://codesandbox.io/s/react-throwing-refs-lk958 class Receiver extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return this.state.error ? ( String(this.state.error) ) : (
                                                    ); } } sinon.spy(Receiver.prototype, 'componentDidCatch'); render(, scratch); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when unmounting a ref', () => { const ref = value => { if (value == null) { throwExpectedError(); } }; ThrowErr.prototype.render = () =>
                                                    ; render( , scratch ); render(
                                                    , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledOnceWith( expectedError ); }); it('should be called when functional child fails', () => { function ThrowErr() { throwExpectedError(); } render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should be called when child inside a Fragment fails', () => { function ThrowErr() { throwExpectedError(); } render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should re-render with new content', () => { class ThrowErr extends Component { componentWillMount() { throw new Error('Error contents'); } render() { return 'No error!?!?'; } } render( , scratch ); rerender(); expect(scratch).to.have.property('textContent', 'Error: Error contents'); }); it('should be able to adapt and rethrow errors', () => { let adaptedError; class Adapter extends Component { componentDidCatch(error) { throw (adaptedError = new Error( 'Adapted ' + String(error && 'message' in error ? error.message : error) )); } render() { return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throwExpectedError(); } sinon.spy(Adapter.prototype, 'componentDidCatch'); render( , scratch ); expect(Adapter.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( adaptedError ); rerender(); expect(scratch).to.have.property('textContent', 'Error: Adapted Error!'); }); it('should bubble on repeated errors', () => { class Adapter extends Component { componentDidCatch(error) { // Try to handle the error this.setState({ error }); } render() { // But fail at doing so if (this.state.error) { throw this.state.error; } return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throwExpectedError(); } sinon.spy(Adapter.prototype, 'componentDidCatch'); render( , scratch ); rerender(); expect(Adapter.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should bubble on ignored errors', () => { class Adapter extends Component { componentDidCatch() { // Ignore the error } render() { return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throw new Error('Error!'); } sinon.spy(Adapter.prototype, 'componentDidCatch'); render( , scratch ); rerender(); expect(Adapter.prototype.componentDidCatch, 'Adapter').to.have.been .called; expect(Receiver.prototype.componentDidCatch, 'Receiver').to.have.been .called; expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should not bubble on caught errors', () => { class TopReceiver extends Component { componentDidCatch(error) { this.setState({ error }); } render() { return (
                                                    {this.state.error ? String(this.state.error) : this.props.children}
                                                    ); } } function ThrowErr() { throwExpectedError(); } sinon.spy(TopReceiver.prototype, 'componentDidCatch'); render( , scratch ); rerender(); expect(TopReceiver.prototype.componentDidCatch).not.to.have.been.called; expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should be called through non-component parent elements', () => { ThrowErr.prototype.render = throwExpectedError; render(
                                                    , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it('should bubble up when ref throws on component that is not an error boundary', () => { const ref = value => { if (value) { throwExpectedError(); } }; function ThrowErr() { return
                                                    ; } render( , scratch ); expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith( expectedError ); }); it.skip('should successfully unmount constantly throwing ref', () => { const buggyRef = throwExpectedError; function ThrowErr() { return
                                                    ThrowErr
                                                    ; } render( , scratch ); rerender(); expect(scratch.innerHTML).to.equal('
                                                    Error: Error!
                                                    '); }); it('should pass errorInfo on render error', () => { let info; class Receiver extends Component { constructor(props) { super(props); this.state = { error: null }; } componentDidCatch(error, errorInfo) { info = errorInfo; this.setState({ error }); } render() { if (this.state.error) return
                                                    ; return this.props.children; } } function ThrowErr() { throw new Error('fail'); } render( , scratch ); expect(info).to.deep.equal({}); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentDidMount.test.jsx000066400000000000000000000025161523334003000271600ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentDidMount', () => { it('is invoked after refs are set', () => { const spy = sinon.spy(); class App extends Component { componentDidMount() { expect(spy).to.have.been.calledOnceWith(scratch.firstChild); } render() { return
                                                    ; } } render(, scratch); expect(spy).to.have.been.calledOnceWith(scratch.firstChild); }); it('supports multiple setState callbacks', () => { const spy = sinon.spy(); class App extends Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { // eslint-disable-next-line this.setState({ count: 1 }, spy); // eslint-disable-next-line this.setState({ count: 2 }, spy); } render() { return
                                                    ; } } render(, scratch); rerender(); expect(spy).to.have.been.calledTwice; }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentDidUpdate.test.jsx000066400000000000000000000234621523334003000273030ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentDidUpdate', () => { it('should be passed previous props and state', () => { /** @type {() => void} */ let updateState; let prevPropsArg; let prevStateArg; let snapshotArg; let curProps; let curState; class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; updateState = () => this.setState({ value: this.state.value + 1 }); } static getDerivedStateFromProps(props, state) { // NOTE: Don't do this in real production code! // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html return { value: state.value + 1 }; } componentDidUpdate(prevProps, prevState, snapshot) { // These object references might be updated later so copy // object so we can assert their values at this snapshot in time prevPropsArg = { ...prevProps }; prevStateArg = { ...prevState }; snapshotArg = snapshot; curProps = { ...this.props }; curState = { ...this.state }; } render() { return
                                                    {this.state.value}
                                                    ; } } // Expectation: // `prevState` in componentDidUpdate should be // the state before setState and getDerivedStateFromProps was called. // `this.state` in componentDidUpdate should be // the updated state after getDerivedStateFromProps was called. // Initial render // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP render(, scratch); expect(scratch.firstChild.textContent).to.be.equal('1'); expect(prevPropsArg).to.be.undefined; expect(prevStateArg).to.be.undefined; expect(snapshotArg).to.be.undefined; expect(curProps).to.be.undefined; expect(curState).to.be.undefined; // New props // state.value: 1 -> 2 in gDSFP render(, scratch); expect(scratch.firstChild.textContent).to.be.equal('2'); expect(prevPropsArg).to.deep.equal({ foo: 'foo' }); expect(prevStateArg).to.deep.equal({ value: 1 }); expect(snapshotArg).to.be.undefined; expect(curProps).to.deep.equal({ foo: 'bar' }); expect(curState).to.deep.equal({ value: 2 }); // New state // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP updateState(); rerender(); expect(scratch.firstChild.textContent).to.be.equal('4'); expect(prevPropsArg).to.deep.equal({ foo: 'bar' }); expect(prevStateArg).to.deep.equal({ value: 2 }); expect(snapshotArg).to.be.undefined; expect(curProps).to.deep.equal({ foo: 'bar' }); expect(curState).to.deep.equal({ value: 4 }); }); it('cDU should not be called when sDU returned false', () => { let spy = sinon.spy(); let c; class App extends Component { constructor() { super(); c = this; } shouldComponentUpdate() { return false; } componentDidUpdate(prevProps) { spy(prevProps); } } render(, scratch); c.setState({}); rerender(); expect(spy).to.not.be.called; }); it("prevState argument should be the same object if state doesn't change", () => { let changeProps, cduPrevState, cduCurrentState; class PropsProvider extends Component { constructor() { super(); this.state = { value: 0 }; changeProps = this.changeReceiverProps.bind(this); } changeReceiverProps() { let value = (this.state.value + 1) % 2; this.setState({ value }); } render() { return ; } } class PropsReceiver extends Component { componentDidUpdate(prevProps, prevState) { cduPrevState = prevState; cduCurrentState = this.state; } render({ value }) { return
                                                    {value}
                                                    ; } } render(, scratch); changeProps(); rerender(); expect(cduPrevState).to.equal(cduCurrentState); }); it('prevState argument should be a different object if state does change', () => { let updateState, cduPrevState, cduCurrentState; class Foo extends Component { constructor() { super(); this.state = { value: 0 }; updateState = this.updateState.bind(this); } updateState() { let value = (this.state.value + 1) % 2; this.setState({ value }); } componentDidUpdate(prevProps, prevState) { cduPrevState = prevState; cduCurrentState = this.state; } render() { return
                                                    {this.state.value}
                                                    ; } } render(, scratch); updateState(); rerender(); expect(cduPrevState).to.not.equal(cduCurrentState); }); it("prevProps argument should be the same object if props don't change", () => { let updateState, cduPrevProps, cduCurrentProps; class Foo extends Component { constructor() { super(); this.state = { value: 0 }; updateState = this.updateState.bind(this); } updateState() { let value = (this.state.value + 1) % 2; this.setState({ value }); } componentDidUpdate(prevProps) { cduPrevProps = prevProps; cduCurrentProps = this.props; } render() { return
                                                    {this.state.value}
                                                    ; } } render(, scratch); updateState(); rerender(); expect(cduPrevProps).to.equal(cduCurrentProps); }); it('prevProps argument should be a different object if props do change', () => { let changeProps, cduPrevProps, cduCurrentProps; class PropsProvider extends Component { constructor() { super(); this.state = { value: 0 }; changeProps = this.changeReceiverProps.bind(this); } changeReceiverProps() { let value = (this.state.value + 1) % 2; this.setState({ value }); } render() { return ; } } class PropsReceiver extends Component { componentDidUpdate(prevProps) { cduPrevProps = prevProps; cduCurrentProps = this.props; } render({ value }) { return
                                                    {value}
                                                    ; } } render(, scratch); changeProps(); rerender(); expect(cduPrevProps).to.not.equal(cduCurrentProps); }); it('is invoked after refs are set', () => { const spy = sinon.spy(); let inst; let i = 0; class App extends Component { componentDidUpdate() { expect(spy).to.have.been.calledOnceWith(scratch.firstChild); } render() { let ref = null; if (i > 0) { // Add ref after mount (i > 0) ref = spy; } i++; inst = this; return
                                                    ; } } render(, scratch); expect(spy).not.to.have.been.called; inst.setState({}); rerender(); expect(spy).to.have.been.calledOnceWith(scratch.firstChild); }); it('should be called after children are mounted', () => { let log = []; class Inner extends Component { componentDidMount() { log.push('Inner mounted'); // Verify that the component is actually mounted when this // callback is invoked. expect(scratch.querySelector('#inner')).to.equalNode(this.base); } render() { return
                                                    ; } } class Outer extends Component { componentDidUpdate() { log.push('Outer updated'); } render(props) { return props.renderInner ? :
                                                    ; } } render(, scratch); render(, scratch); expect(log).to.deep.equal(['Inner mounted', 'Outer updated']); }); it('should be called after parent DOM elements are updated', () => { let setValue; let outerChildText; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; setValue = i => this.setState({ i }); } render(props, { i }) { return (

                                                    Outer: {i}

                                                    ); } } class Inner extends Component { componentDidUpdate() { // At this point, the parent's

                                                    tag should've been updated with the latest value outerChildText = scratch.querySelector('#parent-child').textContent; } render(props, { i }) { return

                                                    Inner: {i}
                                                    ; } } sinon.spy(Inner.prototype, 'componentDidUpdate'); // Initial render render(, scratch); expect(Inner.prototype.componentDidUpdate).to.not.have.been.called; // Set state with a new i const newValue = 5; setValue(newValue); rerender(); expect(Inner.prototype.componentDidUpdate).to.have.been.called; expect(outerChildText).to.equal(`Outer: ${newValue.toString()}`); }); it('should not interfere with setState callbacks', () => { let invocation; class Child extends Component { componentDidMount() { this.props.setValue(10); } render() { return

                                                    Hello world

                                                    ; } } class App extends Component { constructor(props) { super(props); this.state = { show: false, count: null }; } componentDidMount() { // eslint-disable-next-line this.setState({ show: true }); } componentDidUpdate() {} render() { if (this.state.show) { return ( this.setState({ count: i }, () => { invocation = this.state; }) } /> ); } return null; } } render(, scratch); rerender(); expect(invocation.count).to.equal(10); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentWillMount.test.jsx000066400000000000000000000033651523334003000273720ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentWillMount', () => { it('should update state when called setState in componentWillMount', () => { let componentState; class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; } componentWillMount() { this.setState({ value: 1 }); } render() { componentState = this.state; return
                                                    ; } } render(, scratch); expect(componentState).to.deep.equal({ value: 1 }); }); it('should invoke setState callbacks when setState is called in componentWillMount', () => { let componentState; let callback = sinon.spy(); class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; } componentWillMount() { this.setState({ value: 1 }, callback); this.setState({ value: 2 }, () => { callback(); this.setState({ value: 3 }, callback); }); } render() { componentState = this.state; return
                                                    ; } } render(, scratch); expect(componentState).to.deep.equal({ value: 2 }); expect(callback).to.have.been.calledTwice; rerender(); expect(componentState).to.deep.equal({ value: 3 }); expect(callback).to.have.been.calledThrice; }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentWillReceiveProps.test.jsx000066400000000000000000000165701523334003000307000ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentWillReceiveProps', () => { it('should update state when called setState in componentWillReceiveProps', () => { let componentState; class Foo extends Component { constructor(props) { super(props); this.state = { dummy: 0 }; } componentDidMount() { // eslint-disable-next-line react/no-did-mount-set-state this.setState({ dummy: 1 }); } render() { return ; } } class Bar extends Component { constructor(props) { super(props); this.state = { value: 0 }; } componentWillReceiveProps() { this.setState({ value: 1 }); } render() { componentState = this.state; return
                                                    ; } } render(, scratch); rerender(); expect(componentState).to.deep.equal({ value: 1 }); const cWRP = Foo.prototype.componentWillReceiveProps; delete Foo.prototype.componentWillReceiveProps; Foo.prototype.shouldComponentUpdate = cWRP; render(null, scratch); render(, scratch); rerender(); expect(componentState, 'via shouldComponentUpdate').to.deep.equal({ value: 1 }); delete Foo.prototype.shouldComponentUpdate; Foo.prototype.componentWillUpdate = cWRP; render(null, scratch); render(, scratch); rerender(); expect(componentState, 'via componentWillUpdate').to.deep.equal({ value: 1 }); }); it('should NOT be called on initial render', () => { class ReceivePropsComponent extends Component { componentWillReceiveProps() {} render() { return
                                                    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillReceiveProps'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillReceiveProps).not.to .have.been.called; }); // See last paragraph of cWRP section https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops it('should not be called on setState or forceUpdate', () => { let spy = sinon.spy(); let spyInner = sinon.spy(); let c; class Inner extends Component { componentWillReceiveProps() { spyInner(); } render() { return
                                                    foo
                                                    ; } } class Outer extends Component { constructor() { super(); c = this; } componentWillReceiveProps() { spy(); } render() { return ; } } render(, scratch); expect(spy).to.not.be.called; c.setState({}); rerender(); expect(spy).to.not.be.called; expect(spyInner).to.be.calledOnce; spy.resetHistory(); spyInner.resetHistory(); c.forceUpdate(); rerender(); expect(spy).to.not.be.called; expect(spyInner).to.be.calledOnce; }); it('should be called when rerender with new props from parent', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentWillMount() { expect(this.props.i).to.be.equal(0); } componentWillReceiveProps(nextProps) { expect(nextProps.i).to.be.equal(1); } render() { return
                                                    ; } } sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Outer.prototype, 'componentDidMount'); // Initial render render(, scratch); expect(Inner.prototype.componentWillReceiveProps).not.to.have.been.called; // Rerender inner with new props doRender(); rerender(); expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; }); it('should be called when rerender with new props from parent even with setState/forceUpdate in child', () => { let setStateAndUpdateProps; let forceUpdateAndUpdateProps; let cWRPSpy = sinon.spy(); class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; this.update = this.update.bind(this); } update() { this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentDidMount() { expect(this.props.i).to.be.equal(0); setStateAndUpdateProps = () => { this.setState({}); this.props.update(); }; forceUpdateAndUpdateProps = () => { this.forceUpdate(); this.props.update(); }; } componentWillReceiveProps(nextProps) { cWRPSpy(nextProps.i); } render() { return
                                                    ; } } // Initial render render(, scratch); expect(cWRPSpy).not.to.have.been.called; // setState in inner component and update with new props setStateAndUpdateProps(); rerender(); expect(cWRPSpy).to.have.been.calledWith(1); // forceUpdate in inner component and update with new props forceUpdateAndUpdateProps(); rerender(); expect(cWRPSpy).to.have.been.calledWith(2); }); it('should be called in right execution order', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentDidUpdate() { expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; expect(Inner.prototype.componentWillUpdate).to.have.been.called; } componentWillReceiveProps() { expect(Inner.prototype.componentWillUpdate).not.to.have.been.called; expect(Inner.prototype.componentDidUpdate).not.to.have.been.called; } componentWillUpdate() { expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; expect(Inner.prototype.componentDidUpdate).not.to.have.been.called; } shouldComponentUpdate() { expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; expect(Inner.prototype.componentWillUpdate).not.to.have.been.called; return true; } render() { return
                                                    ; } } sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Inner.prototype, 'shouldComponentUpdate'); sinon.spy(Outer.prototype, 'componentDidMount'); render(, scratch); doRender(); rerender(); expect( Inner.prototype.componentWillReceiveProps ).to.have.been.calledBefore(Inner.prototype.componentWillUpdate); expect( Inner.prototype.componentWillReceiveProps ).to.have.been.calledBefore(Inner.prototype.shouldComponentUpdate); expect(Inner.prototype.componentWillUpdate).to.have.been.calledBefore( Inner.prototype.componentDidUpdate ); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentWillUnmount.test.jsx000066400000000000000000000034301523334003000277260ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); describe('top-level componentWillUnmount', () => { it('should invoke componentWillUnmount for top-level components', () => { class Foo extends Component { componentDidMount() {} componentWillUnmount() {} render() { return 'foo'; } } class Bar extends Component { componentDidMount() {} componentWillUnmount() {} render() { return 'bar'; } } sinon.spy(Foo.prototype, 'componentDidMount'); sinon.spy(Foo.prototype, 'componentWillUnmount'); sinon.spy(Foo.prototype, 'render'); sinon.spy(Bar.prototype, 'componentDidMount'); sinon.spy(Bar.prototype, 'componentWillUnmount'); sinon.spy(Bar.prototype, 'render'); render(, scratch); expect(Foo.prototype.componentDidMount, 'initial render').to.have.been .calledOnce; render(, scratch); expect(Foo.prototype.componentWillUnmount, 'when replaced').to.have.been .calledOnce; expect(Bar.prototype.componentDidMount, 'when replaced').to.have.been .calledOnce; render(
                                                    , scratch); expect(Bar.prototype.componentWillUnmount, 'when removed').to.have.been .calledOnce; }); it('should only remove dom after componentWillUnmount was called', () => { class Foo extends Component { componentWillUnmount() { expect(document.getElementById('foo')).to.not.equal(null); } render() { return
                                                    ; } } render(, scratch); render(null, scratch); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/componentWillUpdate.test.jsx000066400000000000000000000047121523334003000275070ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#componentWillUpdate', () => { it('should NOT be called on initial render', () => { class ReceivePropsComponent extends Component { componentWillUpdate() {} render() { return
                                                    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have .been.called; }); it('should be called when rerender with new props from parent', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentWillUpdate(nextProps, nextState) { expect(nextProps).to.be.deep.equal({ i: 1 }); expect(nextState).to.be.deep.equal({}); } render() { return
                                                    ; } } sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Outer.prototype, 'componentDidMount'); // Initial render render(, scratch); expect(Inner.prototype.componentWillUpdate).not.to.have.been.called; // Rerender inner with new props doRender(); rerender(); expect(Inner.prototype.componentWillUpdate).to.have.been.called; }); it('should be called on new state', () => { let doRender; class ReceivePropsComponent extends Component { componentWillUpdate() {} componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render() { return
                                                    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have .been.called; doRender(); rerender(); expect(ReceivePropsComponent.prototype.componentWillUpdate).to.have.been .called; }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/getDerivedStateFromError.test.jsx000066400000000000000000000334621523334003000304370ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /* eslint-disable react/display-name */ /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#getDerivedStateFromError', () => { /** @type {Error} */ let expectedError; /** @type {typeof import('../../../').Component} */ let ThrowErr; let thrower; class Receiver extends Component { static getDerivedStateFromError(error) { return { error }; } render() { return this.state.error ? String(this.state.error) : this.props.children; } } sinon.spy(Receiver, 'getDerivedStateFromError'); sinon.spy(Receiver.prototype, 'render'); function throwExpectedError() { throw (expectedError = new Error('Error!')); } beforeEach(() => { ThrowErr = class ThrowErr extends Component { constructor(props) { super(props); thrower = this; } static getDerivedStateFromError() { expect.fail("Throwing component should not catch it's own error."); return {}; } render() { return
                                                    ThrowErr: getDerivedStateFromError
                                                    ; } }; sinon.spy(ThrowErr, 'getDerivedStateFromError'); expectedError = undefined; Receiver.getDerivedStateFromError.resetHistory(); Receiver.prototype.render.resetHistory(); }); afterEach(() => { expect( ThrowErr.getDerivedStateFromError, "Throwing component should not catch it's own error." ).to.not.be.called; thrower = undefined; }); it('should be called when child fails in constructor', () => { class ThrowErr extends Component { constructor(props, context) { super(props, context); throwExpectedError(); } static getDerivedStateFromError() { expect.fail("Throwing component should not catch it's own error"); return {}; } render() { return
                                                    ; } } render( , scratch ); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); // https://github.com/preactjs/preact/issues/1570 it('should handle double child throws', () => { const Child = ({ i }) => { throw new Error(`error! ${i}`); }; const fn = () => render( {[1, 2].map(i => ( ))} , scratch ); expect(fn).to.not.throw(); rerender(); expect(scratch.innerHTML).to.equal('Error: error! 2'); }); it('should be called when child fails in componentWillMount', () => { ThrowErr.prototype.componentWillMount = throwExpectedError; render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in render', () => { ThrowErr.prototype.render = throwExpectedError; render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentDidMount', () => { ThrowErr.prototype.componentDidMount = throwExpectedError; render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in getDerivedStateFromProps', () => { ThrowErr.getDerivedStateFromProps = throwExpectedError; sinon.spy(ThrowErr.prototype, 'render'); render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); expect(ThrowErr.prototype.render).not.to.have.been.called; }); it('should be called when child fails in getSnapshotBeforeUpdate', () => { ThrowErr.prototype.getSnapshotBeforeUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentDidUpdate', () => { ThrowErr.prototype.componentDidUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillUpdate', () => { ThrowErr.prototype.componentWillUpdate = throwExpectedError; render( , scratch ); thrower.forceUpdate(); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillReceiveProps', () => { ThrowErr.prototype.componentWillReceiveProps = throwExpectedError; let receiver; class Receiver extends Component { constructor() { super(); this.state = { foo: 'bar' }; receiver = this; } static getDerivedStateFromError(error) { return { error }; } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver, 'getDerivedStateFromError'); render(, scratch); receiver.setState({ foo: 'baz' }); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in shouldComponentUpdate', () => { ThrowErr.prototype.shouldComponentUpdate = throwExpectedError; let receiver; class Receiver extends Component { constructor() { super(); this.state = { foo: 'bar' }; receiver = this; } static getDerivedStateFromError(error) { return { error }; } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver, 'getDerivedStateFromError'); render(, scratch); receiver.setState({ foo: 'baz' }); rerender(); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when child fails in componentWillUnmount', () => { ThrowErr.prototype.componentWillUnmount = throwExpectedError; render( , scratch ); render(
                                                    , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when applying a Component ref', () => { const Foo = () =>
                                                    ; const ref = value => { if (value) { throwExpectedError(); } }; // In React, an error boundary handles it's own refs: // https://codesandbox.io/s/react-throwing-refs-lk958 class Receiver extends Component { static getDerivedStateFromError(error) { return { error }; } render() { return this.state.error ? ( String(this.state.error) ) : ( ); } } sinon.spy(Receiver, 'getDerivedStateFromError'); render(, scratch); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when applying a DOM ref', () => { const ref = value => { if (value) { throwExpectedError(); } }; // In React, an error boundary handles it's own refs: // https://codesandbox.io/s/react-throwing-refs-lk958 class Receiver extends Component { static getDerivedStateFromError(error) { return { error }; } render() { return this.state.error ? ( String(this.state.error) ) : (
                                                    ); } } sinon.spy(Receiver, 'getDerivedStateFromError'); render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should be called when unmounting a ref', () => { const ref = value => { if (value == null) { throwExpectedError(); } }; ThrowErr.prototype.render = () =>
                                                    ; render( , scratch ); render(
                                                    , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledOnceWith( expectedError ); }); it('should be called when functional child fails', () => { function ThrowErr() { throwExpectedError(); } render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should re-render with new content', () => { class ThrowErr extends Component { componentWillMount() { throw new Error('Error contents'); } render() { return 'No error!?!?'; } } render( , scratch ); rerender(); expect(scratch).to.have.property('textContent', 'Error: Error contents'); }); it('should be able to adapt and rethrow errors', () => { let adaptedError; class Adapter extends Component { static getDerivedStateFromError(error) { throw (adaptedError = new Error( 'Adapted ' + String(error && 'message' in error ? error.message : error) )); } render() { return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throwExpectedError(); } sinon.spy(Adapter, 'getDerivedStateFromError'); render( , scratch ); expect(Adapter.getDerivedStateFromError).to.have.been.calledWith( expectedError ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( adaptedError ); rerender(); expect(scratch).to.have.property('textContent', 'Error: Adapted Error!'); }); it('should bubble on repeated errors', () => { class Adapter extends Component { static getDerivedStateFromError(error) { return { error }; } render() { // But fail at doing so if (this.state.error) { throw this.state.error; } return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throwExpectedError(); } sinon.spy(Adapter, 'getDerivedStateFromError'); render( , scratch ); rerender(); expect(Adapter.getDerivedStateFromError).to.have.been.calledWith( expectedError ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should bubble on ignored errors', () => { class Adapter extends Component { static getDerivedStateFromError(error) { // Ignore the error return null; } render() { return
                                                    {this.props.children}
                                                    ; } } function ThrowErr() { throw new Error('Error!'); } sinon.spy(Adapter, 'getDerivedStateFromError'); render( , scratch ); rerender(); expect(Adapter.getDerivedStateFromError).to.have.been.called; expect(Receiver.getDerivedStateFromError).to.have.been.called; expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should not bubble on caught errors', () => { class TopReceiver extends Component { static getDerivedStateFromError(error) { return { error }; } render() { return (
                                                    {this.state.error ? String(this.state.error) : this.props.children}
                                                    ); } } function ThrowErr() { throwExpectedError(); } sinon.spy(TopReceiver, 'getDerivedStateFromError'); render( , scratch ); rerender(); expect(TopReceiver.getDerivedStateFromError).not.to.have.been.called; expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); expect(scratch).to.have.property('textContent', 'Error: Error!'); }); it('should be called through non-component parent elements', () => { ThrowErr.prototype.render = throwExpectedError; render(
                                                    , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it('should bubble up when ref throws on component that is not an error boundary', () => { const ref = value => { if (value) { throwExpectedError(); } }; function ThrowErr() { return
                                                    ; } render( , scratch ); expect(Receiver.getDerivedStateFromError).to.have.been.calledWith( expectedError ); }); it.skip('should successfully unmount constantly throwing ref', () => { const buggyRef = throwExpectedError; function ThrowErr() { return
                                                    ThrowErr
                                                    ; } render( , scratch ); rerender(); expect(scratch.innerHTML).to.equal('
                                                    Error: Error!
                                                    '); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/getDerivedStateFromProps.test.jsx000066400000000000000000000252611523334003000304470ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('static getDerivedStateFromProps', () => { it('should set initial state with value returned from getDerivedStateFromProps', () => { class Foo extends Component { static getDerivedStateFromProps(props) { return { foo: props.foo, bar: 'bar' }; } render() { return
                                                    ; } } render(, scratch); expect(scratch.firstChild.className).to.be.equal('foo bar'); }); it('should update initial state with value returned from getDerivedStateFromProps', () => { class Foo extends Component { constructor(props, context) { super(props, context); this.state = { foo: 'foo', bar: 'bar' }; } static getDerivedStateFromProps(props, state) { return { foo: `not-${state.foo}` }; } render() { return
                                                    ; } } render(, scratch); expect(scratch.firstChild.className).to.equal('not-foo bar'); }); it("should update the instance's state with the value returned from getDerivedStateFromProps when props change", () => { class Foo extends Component { constructor(props, context) { super(props, context); this.state = { value: 'initial' }; } static getDerivedStateFromProps(props) { if (props.update) { return { value: 'updated' }; } return null; } componentDidMount() {} componentDidUpdate() {} render() { return
                                                    ; } } sinon.spy(Foo, 'getDerivedStateFromProps'); sinon.spy(Foo.prototype, 'componentDidMount'); sinon.spy(Foo.prototype, 'componentDidUpdate'); render(, scratch); expect(scratch.firstChild.className).to.equal('initial'); expect(Foo.getDerivedStateFromProps).to.have.callCount(1); expect(Foo.prototype.componentDidMount).to.have.callCount(1); // verify mount occurred expect(Foo.prototype.componentDidUpdate).to.have.callCount(0); render(, scratch); expect(scratch.firstChild.className).to.equal('updated'); expect(Foo.getDerivedStateFromProps).to.have.callCount(2); expect(Foo.prototype.componentDidMount).to.have.callCount(1); expect(Foo.prototype.componentDidUpdate).to.have.callCount(1); // verify update occurred }); it("should update the instance's state with the value returned from getDerivedStateFromProps when state changes", () => { class Foo extends Component { constructor(props, context) { super(props, context); this.state = { value: 'initial' }; } static getDerivedStateFromProps(props, state) { // Don't change state for call that happens after the constructor if (state.value === 'initial') { return null; } return { value: state.value + ' derived' }; } componentDidMount() { // eslint-disable-next-line react/no-did-mount-set-state this.setState({ value: 'updated' }); } render() { return
                                                    ; } } sinon.spy(Foo, 'getDerivedStateFromProps'); render(, scratch); expect(scratch.firstChild.className).to.equal('initial'); expect(Foo.getDerivedStateFromProps).to.have.been.calledOnce; rerender(); // call rerender to handle cDM setState call expect(scratch.firstChild.className).to.equal('updated derived'); expect(Foo.getDerivedStateFromProps).to.have.been.calledTwice; }); it('should NOT modify state if null is returned', () => { class Foo extends Component { constructor(props, context) { super(props, context); this.state = { foo: 'foo', bar: 'bar' }; } static getDerivedStateFromProps() { return null; } render() { return
                                                    ; } } sinon.spy(Foo, 'getDerivedStateFromProps'); render(, scratch); expect(scratch.firstChild.className).to.equal('foo bar'); expect(Foo.getDerivedStateFromProps).to.have.been.called; }); // NOTE: Difference from React // React v16.3.2 warns if undefined if returned from getDerivedStateFromProps it('should NOT modify state if undefined is returned', () => { class Foo extends Component { constructor(props, context) { super(props, context); this.state = { foo: 'foo', bar: 'bar' }; } static getDerivedStateFromProps() {} render() { return
                                                    ; } } sinon.spy(Foo, 'getDerivedStateFromProps'); render(, scratch); expect(scratch.firstChild.className).to.equal('foo bar'); expect(Foo.getDerivedStateFromProps).to.have.been.called; }); it('should NOT invoke deprecated lifecycles (cWM/cWRP) if new static gDSFP is present', () => { class Foo extends Component { static getDerivedStateFromProps() {} componentWillMount() {} componentWillReceiveProps() {} render() { return
                                                    ; } } sinon.spy(Foo, 'getDerivedStateFromProps'); sinon.spy(Foo.prototype, 'componentWillMount'); sinon.spy(Foo.prototype, 'componentWillReceiveProps'); render(, scratch); expect(Foo.getDerivedStateFromProps).to.have.been.called; expect(Foo.prototype.componentWillMount).to.not.have.been.called; expect(Foo.prototype.componentWillReceiveProps).to.not.have.been.called; }); it('is not called if neither state nor props have changed', () => { let logs = []; let childRef; class Parent extends Component { constructor(props) { super(props); this.state = { parentRenders: 0 }; } static getDerivedStateFromProps(props, state) { logs.push('parent getDerivedStateFromProps'); return state.parentRenders + 1; } render() { logs.push('parent render'); return ; } } class Child extends Component { constructor(props) { super(props); childRef = this; } render() { logs.push('child render'); return this.props.parentRenders; } } render(, scratch); expect(logs).to.deep.equal([ 'parent getDerivedStateFromProps', 'parent render', 'child render' ]); logs = []; childRef.setState({}); rerender(); expect(logs).to.deep.equal(['child render']); }); // TODO: Investigate this test: // [should not override state with stale values if prevState is spread within getDerivedStateFromProps](https://github.com/facebook/react/blob/25dda90c1ecb0c662ab06e2c80c1ee31e0ae9d36/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js#L1035) it('should be passed next props and state', () => { /** @type {() => void} */ let updateState; let propsArg; let stateArg; class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; updateState = () => this.setState({ value: this.state.value + 1 }); } static getDerivedStateFromProps(props, state) { // These object references might be updated later so copy // object so we can assert their values at this snapshot in time propsArg = { ...props }; stateArg = { ...state }; // NOTE: Don't do this in real production code! // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html return { value: state.value + 1 }; } render() { return
                                                    {this.state.value}
                                                    ; } } // Initial render // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP render(, scratch); let element = scratch.firstChild; expect(element.textContent).to.be.equal('1'); expect(propsArg).to.deep.equal({ foo: 'foo' }); expect(stateArg).to.deep.equal({ value: 0 }); // New Props // state.value: 1 -> 2 in gDSFP render(, scratch); expect(element.textContent).to.be.equal('2'); expect(propsArg).to.deep.equal({ foo: 'bar' }); expect(stateArg).to.deep.equal({ value: 1 }); // New state // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP updateState(); rerender(); expect(element.textContent).to.be.equal('4'); expect(propsArg).to.deep.equal({ foo: 'bar' }); expect(stateArg).to.deep.equal({ value: 3 }); // New Props (see #1446) // 4 -> 5 in gDSFP render(, scratch); expect(element.textContent).to.be.equal('5'); expect(stateArg).to.deep.equal({ value: 4 }); // New Props (see #1446) // 5 -> 6 in gDSFP render(, scratch); expect(element.textContent).to.be.equal('6'); expect(stateArg).to.deep.equal({ value: 5 }); }); // From preactjs/preact#1170 it('should NOT mutate state on mount, only create new versions', () => { const stateConstant = {}; let componentState; class Stateful extends Component { static getDerivedStateFromProps() { return { key: 'value' }; } constructor() { super(...arguments); this.state = stateConstant; } componentDidMount() { componentState = this.state; } render() { return
                                                    ; } } render(, scratch); // Verify captured object references didn't get mutated expect(componentState).to.deep.equal({ key: 'value' }); expect(stateConstant).to.deep.equal({}); }); it('should NOT mutate state on update, only create new versions', () => { const initialState = {}; const capturedStates = []; let setState; class Stateful extends Component { static getDerivedStateFromProps(props, state) { return { value: (state.value || 0) + 1 }; } constructor() { super(...arguments); this.state = initialState; capturedStates.push(this.state); setState = this.setState.bind(this); } componentDidMount() { capturedStates.push(this.state); } componentDidUpdate() { capturedStates.push(this.state); } render() { return
                                                    ; } } render(, scratch); setState({ value: 10 }); rerender(); // Verify captured object references didn't get mutated expect(capturedStates).to.deep.equal([{}, { value: 1 }, { value: 11 }]); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/getSnapshotBeforeUpdate.test.jsx000066400000000000000000000125451523334003000303020ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('#getSnapshotBeforeUpdate', () => { it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', () => { let log = []; class MyComponent extends Component { constructor(props) { super(props); this.state = { value: 0 }; } static getDerivedStateFromProps(nextProps, prevState) { return { value: prevState.value + 1 }; } getSnapshotBeforeUpdate(prevProps, prevState) { log.push( `getSnapshotBeforeUpdate() prevProps:${prevProps.value} prevState:${prevState.value}` ); return 'abc'; } componentDidUpdate(prevProps, prevState, snapshot) { log.push( `componentDidUpdate() prevProps:${prevProps.value} prevState:${prevState.value} snapshot:${snapshot}` ); } render() { log.push('render'); return null; } } render(, scratch); expect(log).to.deep.equal(['render']); log = []; render(, scratch); expect(log).to.deep.equal([ 'render', 'getSnapshotBeforeUpdate() prevProps:foo prevState:1', 'componentDidUpdate() prevProps:foo prevState:1 snapshot:abc' ]); log = []; render(, scratch); expect(log).to.deep.equal([ 'render', 'getSnapshotBeforeUpdate() prevProps:bar prevState:2', 'componentDidUpdate() prevProps:bar prevState:2 snapshot:abc' ]); log = []; render(
                                                    , scratch, scratch.firstChild); expect(log).to.deep.equal([]); }); it('should call getSnapshotBeforeUpdate before mutations are committed', () => { let log = []; class MyComponent extends Component { getSnapshotBeforeUpdate(prevProps) { log.push('getSnapshotBeforeUpdate'); expect(this.divRef.textContent).to.equal(`value:${prevProps.value}`); return 'foobar'; } componentDidUpdate(prevProps, prevState, snapshot) { log.push('componentDidUpdate'); expect(this.divRef.textContent).to.equal(`value:${this.props.value}`); expect(snapshot).to.equal('foobar'); } render() { log.push('render'); return (
                                                    (this.divRef = ref)} >{`value:${this.props.value}`}
                                                    ); } } render(, scratch); expect(log).to.deep.equal(['render']); log = []; render(, scratch); expect(log).to.deep.equal([ 'render', 'getSnapshotBeforeUpdate', 'componentDidUpdate' ]); }); it('should be passed the previous props and state', () => { /** @type {() => void} */ let updateState; let prevPropsArg; let prevStateArg; let curProps; let curState; class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; updateState = () => this.setState({ value: this.state.value + 1 }); } static getDerivedStateFromProps(props, state) { // NOTE: Don't do this in real production code! // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html return { value: state.value + 1 }; } getSnapshotBeforeUpdate(prevProps, prevState) { // These object references might be updated later so copy // object so we can assert their values at this snapshot in time prevPropsArg = { ...prevProps }; prevStateArg = { ...prevState }; curProps = { ...this.props }; curState = { ...this.state }; } render() { return
                                                    {this.state.value}
                                                    ; } } // Expectation: // `prevState` in getSnapshotBeforeUpdate should be // the state before setState or getDerivedStateFromProps was called. // `this.state` in getSnapshotBeforeUpdate should be // the updated state after getDerivedStateFromProps was called. // Initial render // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP render(, scratch); const element = scratch.firstChild; expect(element.textContent).to.be.equal('1'); expect(prevPropsArg).to.be.undefined; expect(prevStateArg).to.be.undefined; expect(curProps).to.be.undefined; expect(curState).to.be.undefined; // New props // state.value: 1 -> 2 in gDSFP render(, scratch); expect(element.textContent).to.be.equal('2'); expect(prevPropsArg).to.deep.equal({ foo: 'foo' }); expect(prevStateArg).to.deep.equal({ value: 1 }); expect(curProps).to.deep.equal({ foo: 'bar' }); expect(curState).to.deep.equal({ value: 2 }); // New state // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP updateState(); rerender(); expect(element.textContent).to.be.equal('4'); expect(prevPropsArg).to.deep.equal({ foo: 'bar' }); expect(prevStateArg).to.deep.equal({ value: 2 }); expect(curProps).to.deep.equal({ foo: 'bar' }); expect(curState).to.deep.equal({ value: 4 }); }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/lifecycle.test.jsx000066400000000000000000000401501523334003000254450ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('should call nested new lifecycle methods in the right order', () => { let updateOuterState; let updateInnerState; let forceUpdateOuter; let forceUpdateInner; let log; function logger(msg) { return function () { // return true for shouldComponentUpdate log.push(msg); return true; }; } class Outer extends Component { static getDerivedStateFromProps() { log.push('outer getDerivedStateFromProps'); return null; } constructor() { super(); log.push('outer constructor'); this.state = { value: 0 }; forceUpdateOuter = () => this.forceUpdate(() => log.push('outer forceUpdate callback')); updateOuterState = () => this.setState( prevState => ({ value: prevState.value % 2 }), () => log.push('outer setState callback') ); } render() { log.push('outer render'); return (
                                                    ); } } Object.assign(Outer.prototype, { componentDidMount: logger('outer componentDidMount'), shouldComponentUpdate: logger('outer shouldComponentUpdate'), getSnapshotBeforeUpdate: logger('outer getSnapshotBeforeUpdate'), componentDidUpdate: logger('outer componentDidUpdate'), componentWillUnmount: logger('outer componentWillUnmount') }); class Inner extends Component { static getDerivedStateFromProps() { log.push('inner getDerivedStateFromProps'); return null; } constructor() { super(); log.push('inner constructor'); this.state = { value: 0 }; forceUpdateInner = () => this.forceUpdate(() => log.push('inner forceUpdate callback')); updateInnerState = () => this.setState( prevState => ({ value: prevState.value % 2 }), () => log.push('inner setState callback') ); } render() { log.push('inner render'); return ( {this.props.x} {this.props.outerValue} {this.state.value} ); } } Object.assign(Inner.prototype, { componentDidMount: logger('inner componentDidMount'), shouldComponentUpdate: logger('inner shouldComponentUpdate'), getSnapshotBeforeUpdate: logger('inner getSnapshotBeforeUpdate'), componentDidUpdate: logger('inner componentDidUpdate'), componentWillUnmount: logger('inner componentWillUnmount') }); // Constructor & mounting log = []; render(, scratch); expect(log).to.deep.equal([ 'outer constructor', 'outer getDerivedStateFromProps', 'outer render', 'inner constructor', 'inner getDerivedStateFromProps', 'inner render', 'inner componentDidMount', 'outer componentDidMount' ]); // Outer & Inner props update log = []; render(, scratch); // Note: we differ from react here in that we apply changes to the dom // as we find them while diffing. React on the other hand separates this // into specific phases, meaning changes to the dom are only flushed // once the whole diff-phase is complete. This is why // "outer getSnapshotBeforeUpdate" is called just before the "inner" hooks. // For react this call would be right before "outer componentDidUpdate" expect(log).to.deep.equal([ 'outer getDerivedStateFromProps', 'outer shouldComponentUpdate', 'outer render', 'outer getSnapshotBeforeUpdate', 'inner getDerivedStateFromProps', 'inner shouldComponentUpdate', 'inner render', 'inner getSnapshotBeforeUpdate', 'inner componentDidUpdate', 'outer componentDidUpdate' ]); // Outer state update & Inner props update log = []; updateOuterState(); rerender(); expect(log).to.deep.equal([ 'outer getDerivedStateFromProps', 'outer shouldComponentUpdate', 'outer render', 'outer getSnapshotBeforeUpdate', 'inner getDerivedStateFromProps', 'inner shouldComponentUpdate', 'inner render', 'inner getSnapshotBeforeUpdate', 'inner componentDidUpdate', 'outer componentDidUpdate', 'outer setState callback' ]); // Inner state update log = []; updateInnerState(); rerender(); expect(log).to.deep.equal([ 'inner getDerivedStateFromProps', 'inner shouldComponentUpdate', 'inner render', 'inner getSnapshotBeforeUpdate', 'inner componentDidUpdate', 'inner setState callback' ]); // Force update Outer log = []; forceUpdateOuter(); rerender(); expect(log).to.deep.equal([ 'outer getDerivedStateFromProps', 'outer render', 'outer getSnapshotBeforeUpdate', 'inner getDerivedStateFromProps', 'inner shouldComponentUpdate', 'inner render', 'inner getSnapshotBeforeUpdate', 'inner componentDidUpdate', 'outer forceUpdate callback', 'outer componentDidUpdate' ]); // Force update Inner log = []; forceUpdateInner(); rerender(); expect(log).to.deep.equal([ 'inner getDerivedStateFromProps', 'inner render', 'inner getSnapshotBeforeUpdate', 'inner forceUpdate callback', 'inner componentDidUpdate' ]); // Unmounting Outer & Inner log = []; render(
  • Stats
    , scratch); expect(log).to.deep.equal([ 'outer componentWillUnmount', 'inner componentWillUnmount' ]); }); describe('#constructor and component(Did|Will)(Mount|Unmount)', () => { let setState; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { show: true }; setState = s => this.setState(s); } render(props, { show }) { return
    {show && }
    ; } } class LifecycleTestComponent extends Component { componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render() { return
    ; } } class Inner extends LifecycleTestComponent { render() { return (
    ); } } class InnerMost extends LifecycleTestComponent { render() { return
    ; } } let spies = [ 'componentWillMount', 'componentDidMount', 'componentWillUnmount' ]; let verifyLifecycleMethods = TestComponent => { let proto = TestComponent.prototype; spies.forEach(s => sinon.spy(proto, s)); let reset = () => spies.forEach(s => proto[s].resetHistory()); it('should be invoked for components on initial render', () => { reset(); render(, scratch); expect(proto.componentDidMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; }); it('should be invoked for components on unmount', () => { reset(); setState({ show: false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); it('should be invoked for components on re-render', () => { reset(); setState({ show: true }); rerender(); expect(proto.componentDidMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; }); }; describe('inner components', () => { verifyLifecycleMethods(Inner); }); describe('innermost components', () => { verifyLifecycleMethods(InnerMost); }); describe('when shouldComponentUpdate() returns false', () => { let setState; class Outer extends Component { constructor() { super(); this.state = { show: true }; setState = s => this.setState(s); } render(props, { show }) { return (
    {show && (
    )}
    ); } } class Inner extends Component { shouldComponentUpdate() { return false; } componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render() { return
    ; } } let proto = Inner.prototype; let spies = [ 'componentWillMount', 'componentDidMount', 'componentWillUnmount' ]; spies.forEach(s => sinon.spy(proto, s)); let reset = () => spies.forEach(s => proto[s].resetHistory()); beforeEach(() => reset()); it('should be invoke normally on initial mount', () => { render(, scratch); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; }); it('should be invoked normally on unmount', () => { setState({ show: false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); it('should still invoke mount for shouldComponentUpdate():false', () => { setState({ show: true }); rerender(); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; }); it('should still invoke unmount for shouldComponentUpdate():false', () => { setState({ show: false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); }); }); describe('#setState', () => { // From preactjs/preact#1170 it('should NOT mutate state, only create new versions', () => { const stateConstant = {}; let didMount = false; let componentState; class Stateful extends Component { constructor() { super(...arguments); this.state = stateConstant; } componentDidMount() { didMount = true; // eslint-disable-next-line react/no-did-mount-set-state this.setState({ key: 'value' }, () => { componentState = this.state; }); } render() { return
    ; } } render(, scratch); rerender(); expect(didMount).to.equal(true); expect(componentState).to.deep.equal({ key: 'value' }); expect(stateConstant).to.deep.equal({}); }); // This feature is not mentioned in the docs, but is part of the release // notes for react v16.0.0: https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes it('should abort if updater function returns null', () => { let updateState; class Foo extends Component { constructor() { super(); this.state = { value: 0 }; updateState = () => this.setState(prev => { prev.value++; return null; }); } render() { return 'value: ' + this.state.value; } } let renderSpy = sinon.spy(Foo.prototype, 'render'); render(, scratch); renderSpy.resetHistory(); updateState(); rerender(); expect(renderSpy).to.not.be.called; }); it('should call callback with correct this binding', () => { let inst; let updateState; class Foo extends Component { constructor() { super(); updateState = () => this.setState({}, this.onUpdate); } onUpdate() { inst = this; } } render(, scratch); updateState(); rerender(); expect(inst).to.be.instanceOf(Foo); }); }); describe('Lifecycle DOM Timing', () => { it('should be invoked when dom does (DidMount, WillUnmount) or does not (WillMount, DidUnmount) exist', () => { let setState; class Outer extends Component { constructor() { super(); this.state = { show: true }; setState = s => { this.setState(s); this.forceUpdate(); }; } componentWillMount() { expect( document.getElementById('OuterDiv'), 'Outer componentWillMount' ).to.not.exist; } componentDidMount() { expect(document.getElementById('OuterDiv'), 'Outer componentDidMount') .to.exist; } componentWillUnmount() { expect( document.getElementById('OuterDiv'), 'Outer componentWillUnmount' ).to.exist; setTimeout(() => { expect( document.getElementById('OuterDiv'), 'Outer after componentWillUnmount' ).to.not.exist; }, 0); } render(props, { show }) { return (
    {show && (
    )}
    ); } } class Inner extends Component { componentWillMount() { expect( document.getElementById('InnerDiv'), 'Inner componentWillMount' ).to.not.exist; } componentDidMount() { expect(document.getElementById('InnerDiv'), 'Inner componentDidMount') .to.exist; } componentWillUnmount() { // @TODO Component mounted into elements (non-components) // are currently unmounted after those elements, so their // DOM is unmounted prior to the method being called. //expect(document.getElementById('InnerDiv'), 'Inner componentWillUnmount').to.exist; setTimeout(() => { expect( document.getElementById('InnerDiv'), 'Inner after componentWillUnmount' ).to.not.exist; }, 0); } render() { return
    ; } } let proto = Inner.prototype; let spies = [ 'componentWillMount', 'componentDidMount', 'componentWillUnmount' ]; spies.forEach(s => sinon.spy(proto, s)); let reset = () => spies.forEach(s => proto[s].resetHistory()); render(, scratch); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; reset(); setState({ show: false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; reset(); setState({ show: true }); rerender(); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore( proto.componentDidMount ); expect(proto.componentDidMount).to.have.been.called; }); it('should be able to use getDerivedStateFromError and componentDidCatch together', () => { let didCatch = sinon.spy(), getDerived = sinon.spy(); const error = new Error('hi'); class Boundary extends Component { static getDerivedStateFromError(err) { getDerived(err); return { err }; } componentDidCatch(err) { didCatch(err); } render() { return this.state.err ?
    : this.props.children; } } const ThrowErr = () => { throw error; }; render( , scratch ); rerender(); expect(didCatch).to.have.been.calledWith(error); expect(getDerived).to.have.been.calledWith(error); }); it('should remove this.base for HOC', () => { let createComponent = (name, fn) => { class C extends Component { componentWillUnmount() { expect(this.base, `${name}.componentWillUnmount`).to.exist; setTimeout(() => { expect(this.base, `after ${name}.componentWillUnmount`).not.to .exist; }, 0); } render(props) { return fn(props); } } sinon.spy(C.prototype, 'componentWillUnmount'); sinon.spy(C.prototype, 'render'); return C; }; class Wrapper extends Component { render({ children }) { return
    {children}
    ; } } let One = createComponent('One', () => one); let Two = createComponent('Two', () => two); let Three = createComponent('Three', () => three); let components = [One, Two, Three]; let Selector = createComponent('Selector', ({ page }) => { let Child = components[page]; return Child && ; }); let app; class App extends Component { constructor() { super(); app = this; } render(_, { page }) { return ; } } render(, scratch); for (let i = 0; i < 20; i++) { app.setState({ page: i % components.length }); app.forceUpdate(); } }); }); }); preactjs-preact-389c7bc/test/browser/lifecycles/shouldComponentUpdate.test.jsx000066400000000000000000000562611523334003000300440ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component, Fragment } from 'preact'; import { vi } from 'vitest'; import { setupScratch, teardown } from '../../_util/helpers'; import { logCall, getLog, clearLog } from '../../_util/logCall'; /** @jsx createElement */ describe('Lifecycle methods', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; // function expectDomLogToBe(expectedOperations, message) { // expect(getLog()).to.deep.equal(expectedOperations, message); // } let resetInsertBefore; let resetRemoveChild; let resetRemove; beforeAll(() => { resetInsertBefore = logCall(Element.prototype, 'insertBefore'); resetRemoveChild = logCall(Element.prototype, 'appendChild'); resetRemove = logCall(Element.prototype, 'removeChild'); }); afterAll(() => { resetInsertBefore(); resetRemoveChild(); resetRemove(); }); beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); clearLog(); }); afterEach(() => { teardown(scratch); }); describe('#shouldComponentUpdate', () => { let setState; class Should extends Component { constructor() { super(); this.state = { show: true }; setState = s => this.setState(s); } render() { return this.state.show ?
    : null; } } class ShouldNot extends Should { shouldComponentUpdate() { return false; } } beforeEach(() => { vi.spyOn(Should.prototype, 'render'); vi.spyOn(ShouldNot.prototype, 'render'); vi.spyOn(ShouldNot.prototype, 'shouldComponentUpdate'); }); it('should rerender component on change by default', () => { render(, scratch); setState({ show: false }); rerender(); expect(Should.prototype.render).toHaveBeenCalledTimes(2); }); it('should not rerender component if shouldComponentUpdate returns false', () => { render(, scratch); setState({ show: false }); rerender(); expect(ShouldNot.prototype.shouldComponentUpdate).toHaveBeenCalledOnce(); expect(ShouldNot.prototype.render).toHaveBeenCalledOnce(); }); it('should not traverse a retained subtree after bailing out', () => { const Inner = () => ( A B ); class Bailout extends Component { shouldComponentUpdate() { return false; } render() { return ; } } render(
    C
    , scratch ); const root = scratch._children; const bailoutVNode = root._children[0]._children[0]; const innerVNode = bailoutVNode._children[0]; const children = innerVNode._children; let reads = 0; bailoutVNode._children[0] = new Proxy(innerVNode, { get(target, property, receiver) { const value = Reflect.get(target, property, receiver); if (value === children) reads++; return value; } }); render(
    C
    , scratch ); expect(scratch.textContent).to.equal('ABC'); expect(reads).to.equal(0); }); it('should reorder non-updating text children', () => { const rows = [ { id: '1', a: 5, b: 100 }, { id: '2', a: 50, b: 10 }, { id: '3', a: 25, b: 1000 } ]; class Row extends Component { shouldComponentUpdate(nextProps) { return nextProps.id !== this.props.id; } render() { return this.props.id; } } const App = ({ sortBy }) => (
    {rows .sort((a, b) => (a[sortBy] > b[sortBy] ? -1 : 1)) .map(row => ( ))}
    ); render(, scratch); expect(scratch.innerHTML).to.equal('
    231
    '); render(, scratch); expect(scratch.innerHTML).to.equal('
    312
    '); }); it('should rerender when sCU returned false before', () => { let c; let spy = sinon.spy(); class App extends Component { constructor() { super(); c = this; } shouldComponentUpdate(_, nextState) { return !!nextState.update; } render() { spy(); return
    foo
    ; } } render(, scratch); c.setState({}); rerender(); spy.resetHistory(); c.setState({ update: true }); rerender(); expect(spy).to.be.calledOnce; }); it('should be called with nextState', () => { let c; let spy = sinon.spy(); class App extends Component { constructor() { super(); c = this; this.state = { a: false }; } shouldComponentUpdate(_, nextState) { return this.state !== nextState; } render() { spy(); return
    foo
    ; } } render(, scratch); c.setState({}); rerender(); spy.resetHistory(); c.setState({ a: true }); rerender(); expect(spy).to.be.calledOnce; }); it('should clear renderCallbacks', () => { const spy = sinon.spy(); let c, renders = 0; class App extends Component { constructor() { super(); c = this; this.state = { a: false }; } shouldComponentUpdate(_, nextState) { return false; } render() { renders += 1; return
    foo
    ; } } render(, scratch); expect(renders).to.equal(1); c.setState({}, spy); rerender(); expect(renders).to.equal(1); expect(spy).to.be.calledOnce; }); it('should not be called on forceUpdate', () => { let Comp; class Foo extends Component { constructor() { super(); Comp = this; } shouldComponentUpdate() { return false; } render() { return ; } } sinon.spy(Foo.prototype, 'shouldComponentUpdate'); sinon.spy(Foo.prototype, 'render'); render(, scratch); Comp.forceUpdate(); rerender(); expect(Foo.prototype.shouldComponentUpdate).to.not.have.been.called; expect(Foo.prototype.render).to.have.been.calledTwice; }); it('should not be called on forceUpdate followed by setState', () => { let Comp; class Foo extends Component { constructor() { super(); Comp = this; } shouldComponentUpdate() { return false; } render() { return ; } } sinon.spy(Foo.prototype, 'shouldComponentUpdate'); sinon.spy(Foo.prototype, 'render'); render(, scratch); Comp.forceUpdate(); Comp.setState({}); rerender(); expect(Foo.prototype.render).to.have.been.calledTwice; expect(Foo.prototype.shouldComponentUpdate).to.not.have.been.called; }); it('should not block queued child forceUpdate', () => { let i = 0; let updateInner; class Inner extends Component { shouldComponentUpdate() { return i === 0; } render() { updateInner = () => this.forceUpdate(); return
    {++i}
    ; } } let updateOuter; class Outer extends Component { shouldComponentUpdate() { return i === 0; } render() { updateOuter = () => this.forceUpdate(); return ; } } class App extends Component { render() { return ; } } render(, scratch); updateOuter(); updateInner(); rerender(); expect(scratch.textContent).to.equal('2'); // The inner sCU should return false on second render because // it was not enqueued via forceUpdate updateOuter(); rerender(); expect(scratch.textContent).to.equal('2'); }); it('should be passed next props and state', () => { /** @type {() => void} */ let updateState; let curProps; let curState; let nextPropsArg; let nextStateArg; class Foo extends Component { constructor(props) { super(props); this.state = { value: 0 }; updateState = () => this.setState({ value: this.state.value + 1 }); } static getDerivedStateFromProps(props, state) { // NOTE: Don't do this in real production code! // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html return { value: state.value + 1 }; } shouldComponentUpdate(nextProps, nextState) { nextPropsArg = { ...nextProps }; nextStateArg = { ...nextState }; curProps = { ...this.props }; curState = { ...this.state }; return true; } render() { return
    {this.state.value}
    ; } } // Expectation: // `this.state` in shouldComponentUpdate should be // the state before setState or getDerivedStateFromProps was called // `nextState` in shouldComponentUpdate should be // the updated state after getDerivedStateFromProps was called // Initial render // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP render(, scratch); expect(scratch.firstChild.textContent).to.be.equal('1'); expect(curProps).to.be.undefined; expect(curState).to.be.undefined; expect(nextPropsArg).to.be.undefined; expect(nextStateArg).to.be.undefined; // New props // state.value: 1 -> 2 in gDSFP render(, scratch); expect(scratch.firstChild.textContent).to.be.equal('2'); expect(curProps).to.deep.equal({ foo: 'foo' }); expect(curState).to.deep.equal({ value: 1 }); expect(nextPropsArg).to.deep.equal({ foo: 'bar' }); expect(nextStateArg).to.deep.equal({ value: 2 }); // New state // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP updateState(); rerender(); expect(scratch.firstChild.textContent).to.be.equal('4'); expect(curProps).to.deep.equal({ foo: 'bar' }); expect(curState).to.deep.equal({ value: 2 }); expect(nextPropsArg).to.deep.equal({ foo: 'bar' }); expect(nextStateArg).to.deep.equal({ value: 4 }); }); it('should update props reference when sCU returns false', () => { let spy = sinon.spy(); let updateState; class Foo extends Component { constructor() { super(); updateState = () => this.setState({}); } shouldComponentUpdate(nextProps) { if (nextProps !== this.props) { spy(); return false; } return true; } } render(, scratch); render(, scratch); expect(spy).to.be.calledOnce; updateState(); rerender(); expect(spy).to.be.calledOnce; }); it('should update state reference when sCU returns false', () => { let spy = sinon.spy(); let updateState; class Foo extends Component { constructor() { super(); this.state = { foo: 1 }; updateState = () => this.setState({ foo: 2 }); } shouldComponentUpdate(_, nextState) { if (nextState !== this.state) { spy(this.state, nextState); return false; } return true; } } render(, scratch); updateState(); rerender(); expect(spy).to.be.calledOnce; expect(spy).to.be.calledWithMatch({ foo: 1 }, { foo: 2 }); updateState(); rerender(); expect(spy).to.be.calledWithMatch({ foo: 2 }, { foo: 2 }); expect(spy).to.be.calledTwice; }); // issue #1864 it('should update dom pointers correctly when returning an empty string', () => { function Child({ showMe, counter }) { return showMe ?
    Counter: {counter}
    : ''; } class Parent extends Component { shouldComponentUpdate() { return false; } render() { return ; } } let updateChild = () => null; class Inner extends Component { constructor(props) { super(props); this.state = { showMe: false }; updateChild = () => { this.setState({ showMe: (display = !display) }); }; } render() { return ; } } let display = false; let updateApp = () => null; class App extends Component { constructor(props) { super(props); updateApp = () => this.setState({}); } render() { return (
    ); } } render(, scratch); expect(scratch.textContent).to.equal(''); updateChild(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateApp(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateChild(); rerender(); expect(scratch.textContent).to.equal(''); updateApp(); rerender(); expect(scratch.textContent).to.equal(''); }); // issue #1864 second case it('should update dom pointers correctly when returning a string', () => { function Child({ showMe, counter }) { return showMe ?
    Counter: {counter}
    : 'foo'; } class Parent extends Component { shouldComponentUpdate() { return false; } render() { return ; } } let updateChild = () => null; class Inner extends Component { constructor(props) { super(props); this.state = { showMe: false }; updateChild = () => { this.setState({ showMe: (display = !display) }); }; } render() { return ; } } let display = false; let updateApp = () => null; class App extends Component { constructor(props) { super(props); updateApp = () => this.setState({}); } render() { return (
    ); } } render(, scratch); expect(scratch.textContent).to.equal('foo'); updateChild(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateApp(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateChild(); rerender(); expect(scratch.textContent).to.equal('foo'); updateApp(); rerender(); expect(scratch.textContent).to.equal('foo'); }); it('should correctly update nested children', () => { let hideThree, incrementThree; class One extends Component { shouldComponentUpdate() { return false; } render(p) { return p.children; } } class Two extends Component { constructor(props) { super(props); this.state = { hideMe: false }; hideThree = () => this.setState(s => ({ hideMe: !s.hideMe })); } shouldComponentUpdate(nextProps, nextState) { return this.state.hideMe !== nextState.hideMe; } render(p, { hideMe }) { return hideMe ? : p.children; } } class Three extends Component { constructor(props) { super(props); this.state = { counter: 1 }; incrementThree = () => this.setState(s => ({ counter: s.counter + 1 })); } render(p, { counter }) { return {counter}; } } render( , scratch ); expect(scratch.innerHTML).to.equal('1'); hideThree(); rerender(); expect(scratch.innerHTML).to.equal(''); hideThree(); rerender(); expect(scratch.innerHTML).to.equal('1'); incrementThree(); rerender(); expect(scratch.innerHTML).to.equal('2'); }); // issue #1864 third case it('should update dom pointers correctly without siblings', () => { function Child({ showMe, counter }) { return showMe ?
    Counter: {counter}
    : 'foo'; } class Parent extends Component { shouldComponentUpdate() { return false; } render() { return ; } } let updateChild = () => null; class Inner extends Component { constructor(props) { super(props); this.state = { showMe: false }; updateChild = () => { this.setState({ showMe: (display = !display) }); }; } render() { return ; } } let display = false; let updateApp = () => null; class App extends Component { constructor(props) { super(props); updateApp = () => this.setState({}); } render() { return (
    ); } } render(, scratch); expect(scratch.textContent).to.equal('foo'); updateChild(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateApp(); rerender(); expect(scratch.textContent).to.equal('Counter: 0'); updateChild(); rerender(); expect(scratch.textContent).to.equal('foo'); updateApp(); rerender(); expect(scratch.textContent).to.equal('foo'); }); }); it('should correctly render when sCU component has null children', () => { class App extends Component { shouldComponentUpdate() { return false; } render() { return [null,
    Hello World!
    , null]; } } render(, scratch); expect(scratch.innerHTML).to.equal('
    Hello World!
    '); render(, scratch); expect(scratch.innerHTML).to.equal('
    Hello World!
    '); render(, scratch); expect(scratch.innerHTML).to.equal('
    Hello World!
    '); }); it('should support nested update with strict-equal vnodes', () => { let wrapperSetState, childSetState; class Child extends Component { constructor(props) { super(props); this.state = { foo: 'baz' }; } render() { childSetState = this.setState.bind(this); return

    {this.state.foo}

    ; } } class Wrapper extends Component { render() { wrapperSetState = this.setState.bind(this); return this.props.children; } } const App = () => ( ); render(, scratch); expect(scratch.innerHTML).to.equal('

    baz

    '); wrapperSetState({ hi: 'world' }); childSetState({ foo: 'bar' }); rerender(); expect(scratch.innerHTML).to.equal('

    bar

    '); }); it('should reorder non-updating nested Fragment children', () => { const rows = [ { id: '1', a: 5, b: 100 }, { id: '2', a: 50, b: 10 }, { id: '3', a: 25, b: 1000 } ]; function Cell({ id, a, b }) { // Return an array to really test out the reordering algorithm :) return (
    id: {id}
    a: {a}
    b: {b}
    ); } class Row extends Component { shouldComponentUpdate(nextProps) { return nextProps.id !== this.props.id; } render(props) { return ; } } const App = ({ sortBy }) => (
    {rows .sort((a, b) => (a[sortBy] > b[sortBy] ? -1 : 1)) .map(row => ( ))}
    ); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${[ '
    id: 2
    a: 50
    b: 10
    ', '
    id: 3
    a: 25
    b: 1000
    ', '
    id: 1
    a: 5
    b: 100
    ' ].join('')}
    ` ); clearLog(); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${[ '
    id: 3
    a: 25
    b: 1000
    ', '
    id: 1
    a: 5
    b: 100
    ', '
    id: 2
    a: 50
    b: 10
    ' ].join('')}
    ` ); // TODO: these tests pass in isolation but not when all tests are running, figure out why logCall stops appending to log. // expectDomLogToBe([ // 'id: 2a: 50b: 10id: 3a: 25b: 1000id: 1a: 5b: 100.insertBefore(
    id: 3,
    id: 2)', // '
    id: 3id: 2a: 50b: 10a: 25b: 1000id: 1a: 5b: 100.insertBefore(
    a: 25,
    id: 2)', // '
    id: 3a: 25id: 2a: 50b: 10b: 1000id: 1a: 5b: 100.insertBefore(
    b: 1000,
    id: 2)', // '
    id: 3a: 25b: 1000id: 2a: 50b: 10id: 1a: 5b: 100.insertBefore(
    id: 1,
    id: 2)', // '
    id: 3a: 25b: 1000id: 1id: 2a: 50b: 10a: 5b: 100.insertBefore(
    a: 5,
    id: 2)', // '
    id: 3a: 25b: 1000id: 1a: 5id: 2a: 50b: 10b: 100.insertBefore(
    b: 100,
    id: 2)' // ]); }); it('should correctly handle double state updates', () => { let updateParent, updateChild; class Parent extends Component { state = { text: 'parent-old' }; componentDidMount() { updateParent = () => this.setState({ text: 'Parent-NEW' }); } render() { return ( {this.props.children} and {this.state.text} ); } } class Child extends Component { state = { text: 'child-old' }; shouldComponentUpdate(nextProps, nextState) { return this.state.text !== nextState.text; } componentDidMount() { updateChild = () => this.setState({ text: 'Child-NEW' }); } render() { return

    {this.state.text}

    ; } } render( , scratch ); updateParent(); updateChild(); rerender(); expect(scratch.innerHTML).to.equal('

    Child-NEW

    and Parent-NEW'); }); it('should maintain the order if memoised component initially rendered empty content', () => { let showText, updateParent; class Child extends Component { constructor(props) { super(props); this.state = { show: false }; showText = () => this.setState({ show: true }); } render() { if (!this.state.show) return null; return
    Component
    ; } } class Memoized extends Component { shouldComponentUpdate() { return false; } render() { return ; } } class Parent extends Component { constructor(props) { super(props); updateParent = () => this.setState({}); } render() { return (
    Before
    After
    ); } } render(, scratch); expect(scratch.innerHTML).to.equal(`
    Before
    After
    `); updateParent(); rerender(); expect(scratch.innerHTML).to.equal(`
    Before
    After
    `); showText(); rerender(); expect(scratch.innerHTML).to.equal( `
    Before
    Component
    After
    ` ); }); it('should not re-insert memoized items that keep their relative order after swap', () => { class MemoizedItem extends Component { shouldComponentUpdate(nextProps) { return nextProps.value !== this.props.value; } render() { return
    {this.props.value}
    ; } } const App = ({ items }) => (
    {items.map(value => ( ))}
    ); render(, scratch); function renderItemsAndAssert({ items, expectedLog }) { clearLog(); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${items.map(value => `
    ${value}
    `).join('')}
    ` ); expect(getLog()).to.deep.equal(expectedLog); } // Swap 1 and 7 renderItemsAndAssert({ items: [7, 2, 3, 4, 5, 6, 1], expectedLog: [ '
    1234567.insertBefore(
    7,
    1)', '
    7123456.appendChild(
    1)' ] }); // Swap 2 and 6 renderItemsAndAssert({ items: [7, 6, 3, 4, 5, 2, 1], expectedLog: [ '
    7234561.insertBefore(
    6,
    2)', '
    7623451.insertBefore(
    2,
    1)' ] }); // Swap 3 and 5 renderItemsAndAssert({ items: [7, 6, 5, 4, 3, 2, 1], expectedLog: [ '
    7634521.insertBefore(
    5,
    3)', '
    7653421.insertBefore(
    3,
    2)' ] }); }); }); preactjs-preact-389c7bc/test/browser/mathml.test.jsx000066400000000000000000000041601523334003000226470ustar00rootroot00000000000000import { createElement, Component, render } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../_util/helpers'; /** @jsx createElement */ describe('mathml', () => { let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should render with the correct namespace URI', () => { render(, scratch); let namespace = scratch.querySelector('math').namespaceURI; expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); }); it('should render children with the correct namespace URI', () => { render( , scratch ); let namespace = scratch.querySelector('mrow').namespaceURI; expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); }); it('should inherit correct namespace URI from parent', () => { const math = document.createElementNS( 'http://www.w3.org/1998/Math/MathML', 'math' ); scratch.appendChild(math); render(, scratch.firstChild); let namespace = scratch.querySelector('mrow').namespaceURI; expect(namespace).to.equal('http://www.w3.org/1998/Math/MathML'); }); it('should inherit correct namespace URI from parent upon updating', () => { setupRerender(); const math = document.createElementNS( 'http://www.w3.org/1998/Math/MathML', 'math' ); scratch.appendChild(math); class App extends Component { state = { show: true }; componentDidMount() { // eslint-disable-next-line this.setState({ show: false }, () => { expect(scratch.querySelector('mo').namespaceURI).to.equal( 'http://www.w3.org/1998/Math/MathML' ); }); } render() { return this.state.show ? 1 : 2; } } render(, scratch.firstChild); }); it('should transition from DOM to MathML and back', () => { render(
    c 2
    , scratch ); expect(scratch.firstChild).to.be.an('HTMLDivElement'); expect(scratch.firstChild.firstChild).to.be.an('MathMLElement'); }); }); preactjs-preact-389c7bc/test/browser/placeholders.test.jsx000066400000000000000000000264761523334003000240500ustar00rootroot00000000000000import { createElement, Component, render, createRef, Fragment } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown } from '../_util/helpers'; import { logCall, clearLog, getLog } from '../_util/logCall'; import { div } from '../_util/dom'; /** @jsx createElement */ describe('null placeholders', () => { /** @type {HTMLDivElement} */ let scratch; /** @type {() => void} */ let rerender; /** @type {string[]} */ let ops; function createNullable(name) { return function Nullable(props) { return props.show ? name : null; }; } /** * @param {string} name * @returns {[import('preact').ComponentClass, import('preact').RefObject<{ toggle(): void }>]} */ function createStatefulNullable(name) { let ref = createRef(); class Nullable extends Component { constructor(props) { super(props); this.state = { show: props.initialShow || true }; ref.current = this; } toggle() { this.setState({ show: !this.state.show }); } componentDidUpdate() { ops.push(`Update ${name}`); } componentDidMount() { ops.push(`Mount ${name}`); } componentWillUnmount() { ops.push(`Unmount ${name}`); } render() { return this.state.show ?
    {name}
    : null; } } return [Nullable, ref]; } let resetAppendChild; let resetInsertBefore; let resetRemoveChild; let resetRemove; beforeAll(() => { resetAppendChild = logCall(Element.prototype, 'appendChild'); resetInsertBefore = logCall(Element.prototype, 'insertBefore'); resetRemoveChild = logCall(Element.prototype, 'removeChild'); resetRemove = logCall(Element.prototype, 'remove'); }); afterAll(() => { resetAppendChild(); resetInsertBefore(); resetRemoveChild(); resetRemove(); }); beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); ops = []; }); afterEach(() => { teardown(scratch); clearLog(); }); it('should treat undefined as a hole', () => { let Bar = () =>
    bar
    ; function Foo(props) { let sibling; if (props.condition) { sibling = ; } return (
    Hello
    {sibling}
    ); } render(, scratch); expect(scratch.innerHTML).to.equal( '
    Hello
    bar
    ' ); clearLog(); render(, scratch); expect(scratch.innerHTML).to.equal('
    Hello
    '); expect(getLog()).to.deep.equal(['
    bar.remove()']); }); it('should preserve state of Components when using null or booleans as placeholders', () => { // Must be the same class for all children in for this test to be valid class Stateful extends Component { constructor(props) { super(props); this.state = { count: 0 }; } increment() { this.setState({ count: this.state.count + 1 }); } componentDidUpdate() { ops.push(`Update ${this.props.name}`); } componentDidMount() { ops.push(`Mount ${this.props.name}`); } componentWillUnmount() { ops.push(`Unmount ${this.props.name}`); } render() { return (
    {this.props.name}: {this.state.count}
    ); } } const s1ref = createRef(); const s2ref = createRef(); const s3ref = createRef(); function App({ first = null, second = false }) { return [first, second, ]; } // Mount third stateful - Initial render render(, scratch); expect(scratch.innerHTML).to.equal('
    third: 0
    '); expect(ops).to.deep.equal(['Mount third'], 'mount third'); // Update third stateful ops = []; s3ref.current.increment(); rerender(); expect(scratch.innerHTML).to.equal('
    third: 1
    '); expect(ops).to.deep.equal(['Update third'], 'update third'); // Mount first stateful ops = []; render(} />, scratch); expect(scratch.innerHTML).to.equal( '
    first: 0
    third: 1
    ' ); expect(ops).to.deep.equal(['Mount first', 'Update third'], 'mount first'); // Update first stateful ops = []; s1ref.current.increment(); s3ref.current.increment(); rerender(); expect(scratch.innerHTML).to.equal( '
    first: 1
    third: 2
    ' ); expect(ops).to.deep.equal(['Update first', 'Update third'], 'update first'); // Mount second stateful ops = []; render( } second={} />, scratch ); expect(scratch.innerHTML).to.equal( '
    first: 1
    second: 0
    third: 2
    ' ); expect(ops).to.deep.equal( ['Update first', 'Mount second', 'Update third'], 'mount second' ); // Update second stateful ops = []; s1ref.current.increment(); s2ref.current.increment(); s3ref.current.increment(); rerender(); expect(scratch.innerHTML).to.equal( '
    first: 2
    second: 1
    third: 3
    ' ); expect(ops).to.deep.equal( ['Update first', 'Update second', 'Update third'], 'update second' ); }); it('should efficiently replace self-updating null placeholders', () => { // These Nullable components replace themselves with null without the parent re-rendering const [Nullable, ref] = createStatefulNullable('Nullable'); const [Nullable2, ref2] = createStatefulNullable('Nullable2'); function App() { return (
    1
    3
    ); } render(, scratch); expect(scratch.innerHTML).to.equal( div([div(1), div('Nullable'), div(3), div('Nullable2')]) ); clearLog(); ref2.current.toggle(); ref.current.toggle(); rerender(); expect(scratch.innerHTML).to.equal(div([div(1), div(3)])); expect(getLog()).to.deep.equal([ '
    Nullable2.remove()', '
    Nullable.remove()' ]); clearLog(); ref2.current.toggle(); ref.current.toggle(); rerender(); expect(scratch.innerHTML).to.equal( div([div(1), div('Nullable'), div(3), div('Nullable2')]) ); expect(getLog()).to.deep.equal([ '
    .appendChild(#text)', '
    13.appendChild(
    Nullable2)', '
    .appendChild(#text)', '
    13Nullable2.insertBefore(
    Nullable,
    3)' ]); }); // See preactjs/preact#2350 it('should efficiently replace null placeholders in parent rerenders (#2350)', () => { // This Nullable only changes when it's parent rerenders const Nullable1 = createNullable('Nullable 1'); const Nullable2 = createNullable('Nullable 2'); /** @type {() => void} */ let toggle; class App extends Component { constructor(props) { super(props); this.state = { show: false }; toggle = () => this.setState({ show: !this.state.show }); } render() { return (
    {this.state.show.toString()}
    the middle
    ); } } render(, scratch); expect(scratch.innerHTML).to.equal(div([div('false'), div('the middle')])); clearLog(); toggle(); rerender(); expect(scratch.innerHTML).to.equal( div([div('true'), 'Nullable 1', div('the middle'), 'Nullable 2']) ); expect(getLog()).to.deep.equal([ '
    truethe middle.insertBefore(#text,
    the middle)', '
    trueNullable 1the middle.appendChild(#text)' ]); clearLog(); toggle(); rerender(); expect(scratch.innerHTML).to.equal(div([div('false'), div('the middle')])); expect(getLog()).to.deep.equal([ '#text.remove()', // '
    falsethe middleNullable 2.appendChild(
    the middle)', '#text.remove()' ]); }); it('when set through the children prop (#4074)', () => { /** @type {(state: { value: boolean }) => void} */ let setState; const Iframe = () => { // Using a div here to make debugging tests in devtools a little less // noisy. The dom ops still assert that the iframe isn't moved. // // return
    ' ); clearLog(); setState({ value: false }); rerender(); expect(scratch.innerHTML).to.equal( '
    ' ); expect(getLog()).to.deep.equal([ '
    Test3.remove()', '
    Test2.remove()' ]); }); it('should not cause infinite loop with referentially equal props', () => { let i = 0; let prevDiff = options._diff; options._diff = () => { if (++i > 10) { options._diff = prevDiff; throw new Error('Infinite loop'); } }; function App({ children, ...rest }) { return (
    {children}
    ); } render(10, scratch); expect(scratch.textContent).to.equal('10'); options._diff = prevDiff; }); it('should not call options.debounceRendering unnecessarily', () => { /** @type {A} */ let comp; class A extends Component { constructor(props) { super(props); this.state = { updates: 0 }; comp = this; } render() { return
    {this.state.updates}
    ; } } render(, scratch); expect(scratch.innerHTML).to.equal('
    0
    '); const sandbox = sinon.createSandbox(); try { sandbox.spy(options, 'debounceRendering'); comp.setState({ updates: 1 }, () => { comp.setState({ updates: 2 }); }); rerender(); expect(scratch.innerHTML).to.equal('
    2
    '); expect(options.debounceRendering).to.have.been.calledOnce; } finally { sandbox.restore(); } }); it('should remove attributes on pre-existing DOM', () => { const div = document.createElement('div'); div.setAttribute('class', 'red'); const span = document.createElement('span'); const text = document.createTextNode('Hi'); span.appendChild(text); div.appendChild(span); scratch.appendChild(div); const App = () => (
    Bye
    ); render(, scratch); expect(serializeHtml(scratch)).to.equal('
    Bye
    '); }); it('should remove class attributes', () => { const App = props => (
    Bye
    ); render(, scratch); expect(scratch.innerHTML).to.equal( '
    Bye
    ' ); render(, scratch); expect(serializeHtml(scratch)).to.equal('
    Bye
    '); }); it('should not read DOM attributes on render without existing DOM', () => { const attributesSpy = spyOnElementAttributes(); render(
    Page 1
    , scratch ); expect(scratch.innerHTML).to.equal( '
    Page 1
    ' ); // IE11 doesn't allow modifying Element.prototype functions properly. // Custom spies will never be called. if (!isIE11) { expect(attributesSpy.get).to.not.have.been.called; } render(
    Page 2
    , scratch ); expect(scratch.innerHTML).to.equal( '
    Page 2
    ' ); // IE11 doesn't allow modifying Element.prototype functions properly. // Custom spies will never be called. if (!isIE11) { expect(attributesSpy.get).to.not.have.been.called; } }); // #2926 it('should not throw when changing contentEditable to undefined or null', () => { render(

    foo

    , scratch); expect(() => render(

    foo

    , scratch) ).to.not.throw(); expect(scratch.firstChild.contentEditable).to.equal('inherit'); expect(() => render(

    foo

    , scratch) ).to.not.throw(); expect(scratch.firstChild.contentEditable).to.equal('inherit'); }); // #2926 Part 2 it('should allow setting contentEditable to false', () => { render(
    editable

    not editable

    , scratch ); expect(scratch.firstChild.contentEditable).to.equal('true'); expect(scratch.querySelector('p').contentEditable).to.equal('false'); }); // #3060 it('should reset tabindex on undefined/null', () => { const defaultValue = isIE11 ? 0 : -1; render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(0); render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(defaultValue); render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(defaultValue); render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(0); render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(defaultValue); render(
    , scratch); expect(scratch.firstChild.tabIndex).to.equal(defaultValue); }); // #4137 it('should unset role if null || undefined', () => { render(
    role="status"
    role="undefined"
    role="null"
    , scratch ); const divs = scratch.querySelectorAll('div'); expect(divs[0].hasAttribute('role')).to.equal(true); expect(divs[1].hasAttribute('role')).to.equal(false); expect(divs[2].hasAttribute('role')).to.equal(false); }); it('should not crash or repeatedly add the same child when replacing a matched vnode with null', () => { const B = () =>
    B
    ; /** @type {() => void} */ let update; class App extends Component { constructor(props) { super(props); this.state = { show: true }; update = () => { this.setState(state => ({ show: !state.show })); }; } render() { if (this.state.show) { return (
    ); } return (
    {null}
    ); } } render(, scratch); expect(scratch.innerHTML).to.equal('
    B
    '); update(); rerender(); expect(scratch.innerHTML).to.equal('
    B
    '); update(); rerender(); expect(scratch.innerHTML).to.equal('
    B
    '); update(); rerender(); expect(scratch.innerHTML).to.equal('
    B
    '); }); it('should reconcile children in right order', () => { let data = ['A', 'B', 'C', 'D', 'E']; render(
      {data.map(d => (
    • {d}
    • ))}
    , scratch ); expect(scratch.textContent).to.equal('ABCDE'); data = ['B', 'E', 'C', 'D']; render(
      {data.map(d => (
    • {d}
    • ))}
    , scratch ); expect(scratch.textContent).to.equal('BECD'); }); it('should reconcile children in right order #2', () => { let data = ['A', 'B', 'C', 'D', 'E']; render(
      {data.map(d => (
    • {d}
    • ))}
    , scratch ); expect(scratch.textContent).to.equal('ABCDE'); data = ['B', 'E', 'D', 'C']; render(
      {data.map(d => (
    • {d}
    • ))}
    , scratch ); expect(scratch.textContent).to.equal('BEDC'); }); it('should reconcile children in right order #3', () => { render(

    _A1

    _A2

    _A3

    _A4

    _A5

    _A6

    _A7

    _A8

    , scratch ); render(

    _B1

    _B2

    _B3

    _B4

    _B5

    _B6

    _B7

    _B8

    , scratch ); expect(serializeHtml(scratch)).to.equal( '

    _B1

    _B2

    _B3

    _B4

    _B5

    _B6

    _B7

    _B8

    ' ); }); it('should reconcile children in right order #4', () => { render(

    _A1

    _A2

    _A3

    _A4

    _A5

    _A6

    _A7

    _A8

    _A9

    _A10

    _A11

    _A12
    , scratch ); render(

    _B1

    _B2

    _B3

    _B4

    _B5

    _B6

    _B7

    _B8

    _B9

    _B10

    _B11

    _B12

    _B13

    , scratch ); expect(serializeHtml(scratch)).to.equal( '

    _B1

    _B2

    _B3

    _B4

    _B5

    _B6

    _B7

    _B8

    _B9

    _B10

    _B11

    _B12

    _B13

    ' ); }); it('should not crash or repeatedly add the same child when replacing a matched vnode with null (mixed dom-types)', () => { const B = () =>
    B
    ; /** @type {() => void} */ let update; class App extends Component { constructor(props) { super(props); this.state = { show: true }; update = () => { this.setState(state => ({ show: !state.show })); }; } render() { if (this.state.show) { return (
    C
    ); } return (
    A {null}
    C
    ); } } render(, scratch); expect(scratch.innerHTML).to.equal('
    B
    C
    '); update(); rerender(); expect(scratch.innerHTML).to.equal( '
    A
    B
    C
    ' ); update(); rerender(); expect(scratch.innerHTML).to.equal('
    B
    C
    '); update(); rerender(); expect(scratch.innerHTML).to.equal( '
    A
    B
    C
    ' ); }); it('should shrink lists', () => { function RenderedItem({ item }) { if (item.renderAsNullInComponent) { return null; } return
    {item.id}
    ; } function App({ list }) { return (
    {list.map(item => ( ))}
    ); } const firstList = [ { id: 'One' }, { id: 'Two' }, { id: 'Three' }, { id: 'Four' } ]; const secondList = [ { id: 'One' }, { id: 'Four', renderAsNullInComponent: true }, { id: 'Six' }, { id: 'Seven' } ]; render(, scratch); expect(scratch.innerHTML).to.equal( '
    One
    Two
    Three
    Four
    ' ); render(, scratch); expect(scratch.innerHTML).to.equal( '
    One
    Six
    Seven
    ' ); }); it('handles shuffled child-ordering', function () { const App = ({ items }) => (
    {items.map(key => (
    {key}
    ))}
    ); const a = ['0', '1', '2', '3', '4', '5', '6']; const b = ['1', '3', '5', '2', '6', '4', '0']; const c = ['11', '3', '1', '4', '6', '2', '5', '0', '9', '10']; render(, scratch); clearLog(); expect(scratch.innerHTML).to.equal( `
    ${a.map(n => `
    ${n}
    `).join('')}
    ` ); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${b.map(n => `
    ${n}
    `).join('')}
    ` ); expect(getLog()).to.deep.equal([ '
    0123456.insertBefore(
    2,
    6)', '
    0134526.appendChild(
    4)', '
    0135264.appendChild(
    0)' ]); clearLog(); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${c.map(n => `
    ${n}
    `).join('')}
    ` ); expect(getLog()).to.deep.equal([ '
    .appendChild(#text)', '
    1352640.insertBefore(
    11,
    1)', '
    111352640.insertBefore(
    1,
    5)', '
    113152640.insertBefore(
    6,
    0)', '
    113152460.insertBefore(
    2,
    0)', '
    113154620.insertBefore(
    5,
    0)', '
    .appendChild(#text)', '
    113146250.appendChild(
    9)', '
    .appendChild(#text)', '
    1131462509.appendChild(
    10)' ]); clearLog(); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${a.map(n => `
    ${n}
    `).join('')}
    ` ); expect(getLog()).to.deep.equal([ '
    11.remove()', '
    9.remove()', '
    10.remove()', '
    3146250.insertBefore(
    0,
    3)', '
    0314625.insertBefore(
    1,
    3)', '
    0134625.insertBefore(
    2,
    3)', '
    0123465.insertBefore(
    5,
    6)' ]); clearLog(); }); it('should shift keyed lists with wrapping fragment-like children', () => { const ItemA = ({ text }) =>
    A: {text}
    ; const ItemB = ({ text }) =>
    B: {text}
    ; const Item = ({ text, type }) => { return type === 'B' ? : ; }; let set; class App extends Component { constructor(props) { super(props); this.state = { items: a, mapping: mappingA }; set = (items, mapping) => { this.setState({ items, mapping }); }; } render() { return (
      {this.state.items.map((key, i) => ( ))}
    ); } } const a = ['4', '1', '2', '3']; const mappingA = ['A', 'A', 'B', 'B']; const b = ['1', '2', '4', '3']; const mappingB = ['B', 'A', 'A', 'A']; const c = ['4', '2', '1', '3']; const mappingC = ['A', 'B', 'B', 'A']; render(, scratch); expect(scratch.innerHTML).to.equal( '
      A: 4
      A: 1
      B: 2
      B: 3
    ' ); set(b, mappingB); rerender(); expect(scratch.innerHTML).to.equal( '
      B: 1
      A: 2
      A: 4
      A: 3
    ' ); set(c, mappingC); rerender(); expect(scratch.innerHTML).to.equal( '
      A: 4
      B: 2
      B: 1
      A: 3
    ' ); }); it('handle shuffled array children (moving to the front)', () => { const App = ({ items }) => (
    {items.map(key => (
    {key}
    ))}
    ); const a = ['0', '2', '7', '6', '1', '3', '5', '4']; const b = ['1', '0', '6', '7', '5', '2', '4', '3']; const c = ['0', '7', '2', '1', '3', '5', '6', '4']; render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${a.map(n => `
    ${n}
    `).join('')}
    ` ); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${b.map(n => `
    ${n}
    `).join('')}
    ` ); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${c.map(n => `
    ${n}
    `).join('')}
    ` ); }); // #2949 it.skip('should not swap unkeyed chlildren', () => { class X extends Component { constructor(props) { super(props); this.name = props.name; } render() { return

    {this.name}

    ; } } function Foo({ condition }) { return (
    {condition ? '' : } {condition ? : ''}
    ); } render(, scratch); expect(scratch.textContent).to.equal('A'); render(, scratch); expect(scratch.textContent).to.equal('B'); render(, scratch); expect(scratch.textContent).to.equal('A'); }); // #2949 it.skip('should not swap unkeyed chlildren', () => { const calls = []; class X extends Component { constructor(props) { super(props); calls.push(props.name); this.name = props.name; } render() { return

    {this.name}

    ; } } function Foo({ condition }) { return (
    {condition ? '' : } {condition ? : ''}
    ); } render(, scratch); expect(scratch.textContent).to.equal('1AC'); expect(calls).to.deep.equal(['1', 'A', 'C']); render(, scratch); expect(scratch.textContent).to.equal('1BC'); expect(calls).to.deep.equal(['1', 'A', 'C', 'B']); render(, scratch); expect(scratch.textContent).to.equal('1AC'); expect(calls).to.deep.equal(['1', 'A', 'C', 'B', 'A']); }); it('should retain state for inserted children', () => { class X extends Component { constructor(props) { super(props); this.name = props.name; } render() { return

    {this.name}

    ; } } function Foo({ condition }) { // We swap the prop from A to B but we don't expect this to // reflect in text-content as we are testing whether the // state is retained for a skew that matches the original children. // // We insert which should amount to a skew of -1 which should // make us correctly match the X component. return condition ? (
    ) : (
    ); } render(, scratch); expect(scratch.textContent).to.equal('A'); render(, scratch); expect(scratch.textContent).to.equal('A'); render(, scratch); expect(scratch.textContent).to.equal('A'); }); it('handle shuffled (stress test)', () => { function randomize(arr) { for (let i = arr.length - 1; i > 0; i--) { let j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr; } const App = ({ items }) => (
    {items.map(key => (
    {key}
    ))}
    ); const a = Array.from({ length: 8 }).map((_, i) => `${i}`); for (let i = 0; i < 10000; i++) { const aa = randomize(a); render(, scratch); expect(scratch.innerHTML).to.equal( `
    ${aa.map(n => `
    ${n}
    `).join('')}
    ` ); } }); it('should work with document', () => { document.textContent = ''; const App = () => ( Test

    Test

    ); render(, document); expect(document.documentElement.innerHTML.trim()).to.equal( 'Test

    Test

    ' ); }); it('should not remount components when replacing a component with a falsy value in-between', () => { const actions = []; class Comp extends Component { componentDidMount() { actions.push('mounted ' + this.props.i); } render() { return
    Hello
    ; } } const App = props => { return (
    {props.y === '1' ? :
    } {false}
    ); }; render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); render(, scratch); expect(actions).to.deep.equal(['mounted 1', 'mounted 2', 'mounted 3']); }); it('Should render intercepted component', () => { class Test { constructor(text) { this.text = text; } } const TestValue = props => { return props.text; }; Object.defineProperties(Test.prototype, { constructor: { configurable: true, value: undefined }, type: { configurable: true, value: TestValue }, props: { configurable: true, get() { return { text: this.text }; } }, _depth: { configurable: true, value: 1 } }); const test = new Test('hello world'); const App = () => {test}; render(, scratch); expect(scratch.innerHTML).to.equal('hello world'); }); }); preactjs-preact-389c7bc/test/browser/replaceNode.test.jsx000066400000000000000000000137021523334003000236100ustar00rootroot00000000000000import { createElement, render, Component } from 'preact'; import { setupScratch, teardown, serializeHtml, sortAttributes } from '../_util/helpers'; /** @jsx createElement */ describe('replaceNode parameter in render()', () => { let scratch; /** * @param {HTMLDivElement} container * @returns {HTMLDivElement[]} */ function setupABCDom(container) { return ['a', 'b', 'c'].map(id => { const child = document.createElement('div'); child.id = id; container.appendChild(child); return child; }); } beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should use replaceNode as render root and not inject into it', () => { setupABCDom(scratch); const childA = scratch.querySelector('#a'); render(
    contents
    , scratch, childA); expect(scratch.querySelector('#a')).to.equalNode(childA); expect(childA.innerHTML).to.equal('contents'); }); it('should not remove siblings of replaceNode', () => { setupABCDom(scratch); const childA = scratch.querySelector('#a'); render(
    , scratch, childA); expect(scratch.innerHTML).to.equal( '
    ' ); }); it('should notice prop changes on replaceNode', () => { setupABCDom(scratch); const childA = scratch.querySelector('#a'); render(
    , scratch, childA); expect(sortAttributes(String(scratch.innerHTML))).to.equal( sortAttributes( '
    ' ) ); }); it('should unmount existing components', () => { const unmount = sinon.spy(); const mount = sinon.spy(); class App extends Component { componentDidMount() { mount(); } componentWillUnmount() { unmount(); } render() { return
    App
    ; } } render(
    , scratch ); expect(scratch.innerHTML).to.equal('
    App
    '); expect(mount).to.be.calledOnce; render(
    new
    , scratch, scratch.querySelector('#a')); expect(scratch.innerHTML).to.equal('
    new
    '); expect(unmount).to.be.calledOnce; }); it('should unmount existing components in prerendered HTML', () => { const unmount = sinon.spy(); const mount = sinon.spy(); class App extends Component { componentDidMount() { mount(); } componentWillUnmount() { unmount(); } render() { return App; } } scratch.innerHTML = `
    `; const childContainer = scratch.querySelector('#child'); render(, childContainer); expect(serializeHtml(childContainer)).to.equal('App'); expect(mount).to.be.calledOnce; render(
    , scratch, scratch.firstElementChild); expect(serializeHtml(scratch)).to.equal('
    '); expect(unmount).to.be.calledOnce; }); it('should render multiple render roots in one parentDom', () => { setupABCDom(scratch); const childA = scratch.querySelector('#a'); const childB = scratch.querySelector('#b'); const childC = scratch.querySelector('#c'); const expectedA = '
    childA
    '; const expectedB = '
    childB
    '; const expectedC = '
    childC
    '; render(
    childA
    , scratch, childA); render(
    childB
    , scratch, childB); render(
    childC
    , scratch, childC); expect(scratch.innerHTML).to.equal(`${expectedA}${expectedB}${expectedC}`); }); it('should call unmount when working with replaceNode', () => { const mountSpy = sinon.spy(); const unmountSpy = sinon.spy(); class MyComponent extends Component { componentDidMount() { mountSpy(); } componentWillUnmount() { unmountSpy(); } render() { return
    My Component
    ; } } const container = document.createElement('div'); scratch.appendChild(container); render(, scratch, container); expect(mountSpy).to.be.calledOnce; render(
    Not my component
    , document.body, container); expect(unmountSpy).to.be.calledOnce; }); it('should double replace', () => { const container = document.createElement('div'); scratch.appendChild(container); render(
    Hello
    , scratch, scratch.firstElementChild); expect(scratch.innerHTML).to.equal('
    Hello
    '); render(
    Hello
    , scratch, scratch.firstElementChild); expect(scratch.innerHTML).to.equal('
    Hello
    '); }); it('should replaceNode after rendering', () => { function App({ i }) { return

    {i}

    ; } render(, scratch); expect(scratch.innerHTML).to.equal('

    2

    '); render(, scratch, scratch.firstChild); expect(scratch.innerHTML).to.equal('

    3

    '); }); it("shouldn't remove elements on subsequent renders with replaceNode", () => { const placeholder = document.createElement('div'); scratch.appendChild(placeholder); const App = () => (
    New content
    ); render(, scratch, placeholder); expect(scratch.innerHTML).to.equal( '
    New content
    ' ); render(, scratch, placeholder); expect(scratch.innerHTML).to.equal( '
    New content
    ' ); }); it('should remove redundant elements on subsequent renders with replaceNode', () => { const placeholder = document.createElement('div'); scratch.appendChild(placeholder); const App = () => (
    New content
    ); render(, scratch, placeholder); expect(scratch.innerHTML).to.equal( '
    New content
    ' ); placeholder.appendChild(document.createElement('span')); expect(scratch.innerHTML).to.equal( '
    New content
    ' ); render(, scratch, placeholder); expect(scratch.innerHTML).to.equal( '
    New content
    ' ); }); }); preactjs-preact-389c7bc/test/browser/select.test.jsx000066400000000000000000000026511523334003000226470ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { setupScratch, teardown } from '../_util/helpers'; /** @jsx createElement */ describe('Select', () => { let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should set ); } render(, scratch); expect(scratch.firstChild.value).to.equal('B'); }); it('should set value with selected', () => { function App() { return ( ); } render(, scratch); expect(scratch.firstChild.value).to.equal('B'); }); it('should work with multiple selected', () => { function App() { return ( ); } render(, scratch); Array.prototype.slice.call(scratch.firstChild.childNodes).forEach(node => { if (node.value === 'B' || node.value === 'C') { expect(node.selected).to.equal(true); } }); expect(scratch.firstChild.value).to.equal('B'); }); }); preactjs-preact-389c7bc/test/browser/spec.test.jsx000066400000000000000000000073441523334003000223260ustar00rootroot00000000000000import { setupRerender } from 'preact/test-utils'; import { createElement, render, Component } from 'preact'; import { setupScratch, teardown } from '../_util/helpers'; /** @jsx createElement */ describe('Component spec', () => { let scratch, rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); describe('defaultProps', () => { it('should apply default props on initial render', () => { class WithDefaultProps extends Component { constructor(props, context) { super(props, context); expect(props).to.be.deep.equal({ fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2 }); } render() { return
    ; } } WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 }; render(, scratch); }); it('should apply default props on rerender', () => { /** @type {() => void} */ let doRender; class Outer extends Component { constructor() { super(); this.state = { i: 1 }; } componentDidMount() { doRender = () => this.setState({ i: 2 }); } render(props, { i }) { return ; } } class WithDefaultProps extends Component { constructor(props, context) { super(props, context); this.ctor(props, context); } ctor() {} componentWillReceiveProps() {} render() { return
    ; } } WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 }; let proto = WithDefaultProps.prototype; sinon.spy(proto, 'ctor'); sinon.spy(proto, 'componentWillReceiveProps'); sinon.spy(proto, 'render'); render(, scratch); doRender(); const PROPS1 = { fieldA: 1, fieldB: 1, fieldC: 1, fieldD: 1 }; const PROPS2 = { fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2 }; expect(proto.ctor).to.have.been.calledWithMatch(PROPS1); expect(proto.render).to.have.been.calledWithMatch(PROPS1); rerender(); // expect(proto.ctor).to.have.been.calledWith(PROPS2); expect(proto.componentWillReceiveProps).to.have.been.calledWithMatch( PROPS2 ); expect(proto.render).to.have.been.calledWithMatch(PROPS2); }); }); describe('forceUpdate', () => { it('should force a rerender', () => { /** @type {() => void} */ let forceUpdate; class ForceUpdateComponent extends Component { componentWillUpdate() {} componentDidMount() { forceUpdate = () => this.forceUpdate(); } render() { return
    ; } } sinon.spy(ForceUpdateComponent.prototype, 'componentWillUpdate'); sinon.spy(ForceUpdateComponent.prototype, 'forceUpdate'); render(, scratch); expect(ForceUpdateComponent.prototype.componentWillUpdate).not.to.have .been.called; forceUpdate(); rerender(); expect(ForceUpdateComponent.prototype.componentWillUpdate).to.have.been .called; expect(ForceUpdateComponent.prototype.forceUpdate).to.have.been.called; }); it('should add callback to renderCallbacks', () => { /** @type {() => void} */ let forceUpdate; let callback = sinon.spy(); class ForceUpdateComponent extends Component { componentDidMount() { forceUpdate = () => this.forceUpdate(callback); } render() { return
    ; } } sinon.spy(ForceUpdateComponent.prototype, 'forceUpdate'); render(, scratch); forceUpdate(); rerender(); expect(ForceUpdateComponent.prototype.forceUpdate).to.have.been.called; expect( ForceUpdateComponent.prototype.forceUpdate ).to.have.been.calledWith(callback); expect(callback).to.have.been.called; }); }); }); preactjs-preact-389c7bc/test/browser/style.test.jsx000066400000000000000000000204611523334003000225270ustar00rootroot00000000000000import { createElement, render } from 'preact'; import { setupScratch, teardown, sortCss } from '../_util/helpers'; /** @jsx createElement */ describe('style attribute', () => { /** @type {HTMLElement} */ let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should apply style as String', () => { render(
    , scratch); expect(scratch.childNodes[0].style.cssText).to.equal( 'top: 5px; position: relative;' ); }); it('should not call CSSStyleDeclaration.setProperty for style strings', () => { render(
    , scratch); sinon.stub(scratch.firstChild.style, 'setProperty'); render(
    , scratch); expect(scratch.firstChild.style.setProperty).to.not.be.called; }); it('should properly switch from string styles to object styles and back', () => { render(
    test
    , scratch); let style = scratch.firstChild.style; expect(style.cssText).to.equal('display: inline;'); render(
    , scratch); expect(style.cssText).to.equal('color: red;'); render(
    , scratch); expect(style.cssText).to.equal('color: blue;'); render(
    , scratch); expect(style.cssText).to.equal('color: yellow;'); render(
    , scratch); expect(style.cssText).to.equal('display: block;'); }); it('should serialize style objects', () => { const styleObj = { color: 'rgb(255, 255, 255)', background: 'rgb(255, 100, 0)', backgroundPosition: '10px 10px', 'background-size': 'cover', gridRowStart: 1, padding: 5, top: 100, left: '100%' }; render(
    test
    , scratch); let style = scratch.firstChild.style; expect(style.color).to.equal('rgb(255, 255, 255)'); expect(style.background).to.contain('rgb(255, 100, 0)'); expect(style.backgroundPosition).to.equal('10px 10px'); expect(style.backgroundSize).to.equal('cover'); expect(style.padding).to.equal('5px'); expect(style.top).to.equal('100px'); expect(style.left).to.equal('100%'); // Only check for this in browsers that support css grids if (typeof scratch.style.grid == 'string') { expect(style.gridRowStart).to.equal('1'); } }); it('should support opacity 0', () => { render(
    Test
    , scratch); let style = scratch.firstChild.style; expect(style).to.have.property('opacity').that.equals('1'); render(
    Test
    , scratch); style = scratch.firstChild.style; expect(style).to.have.property('opacity').that.equals('0'); }); it('should support animation-iteration-count as number', () => { render(
    Test
    , scratch); let style = scratch.firstChild.style; expect(style).to.have.property('animationIterationCount').that.equals('1'); render(
    Test
    , scratch); style = scratch.firstChild.style; expect(style) .to.have.property('animationIterationCount') .that.equals('2.5'); }); it('should replace previous style objects', () => { render(
    test
    , scratch); let style = scratch.firstChild.style; expect(style.cssText).to.equal('display: inline;'); expect(style).to.have.property('display').that.equals('inline'); expect(style).to.have.property('color').that.equals(''); expect(style.zIndex.toString()).to.equal(''); render(
    test
    , scratch ); style = scratch.firstChild.style; expect(style.cssText).to.equal('color: rgb(0, 255, 255); z-index: 3;'); expect(style).to.have.property('display').that.equals(''); expect(style).to.have.property('color').that.equals('rgb(0, 255, 255)'); // IE stores numeric z-index values as a number expect(style.zIndex.toString()).to.equal('3'); render(
    test
    , scratch ); style = scratch.firstChild.style; expect(style.cssText).to.equal('color: rgb(0, 255, 255); display: inline;'); expect(style).to.have.property('display').that.equals('inline'); expect(style).to.have.property('color').that.equals('rgb(0, 255, 255)'); expect(style.zIndex.toString()).to.equal(''); }); it('should remove old styles', () => { render(
    , scratch); render(
    , scratch); expect(scratch.firstChild.style.color).to.equal(''); expect(scratch.firstChild.style.backgroundColor).to.equal('blue'); }); // Issue #1850 it('should remove empty styles', () => { render(
    , scratch); expect(scratch.firstChild.style.visibility).to.equal('hidden'); render(
    , scratch); expect(scratch.firstChild.style.visibility).to.equal(''); }); // Skip test if the currently running browser doesn't support CSS Custom Properties if (window.CSS && CSS.supports('color', 'var(--fake-var)')) { it('should support css custom properties', () => { render(
    test
    , scratch ); expect(sortCss(scratch.firstChild.style.cssText)).to.equal( '--foo: red; color: var(--foo);' ); expect(window.getComputedStyle(scratch.firstChild).color).to.equal( 'rgb(255, 0, 0)' ); }); it('should not add "px" suffix for custom properties', () => { render(
    test
    , scratch ); expect(sortCss(scratch.firstChild.style.cssText)).to.equal( '--foo: 100px; width: var(--foo);' ); }); it('css vars should not be transformed into dash-separated', () => { render(
    test
    , scratch ); expect(sortCss(scratch.firstChild.style.cssText)).to.equal( '--foo-baz: 2; --fooBar: 1; opacity: var(--fooBar); z-index: var(--foo-baz);' ); }); it('should call CSSStyleDeclaration.setProperty for css vars', () => { render(
    , scratch); sinon.stub(scratch.firstChild.style, 'setProperty'); render(
    , scratch ); expect(scratch.firstChild.style.setProperty).to.be.calledWith( '--foo', '10px' ); }); it('should clear css properties when passed an empty string, null, or undefined', () => { const red = 'rgb(255, 0, 0)'; const blue = 'rgb(0, 0, 255)'; const green = 'rgb(0, 128, 0)'; const yellow = 'rgb(255, 255, 0)'; const violet = 'rgb(238, 130, 238)'; function App({ color }) { return (
    Hello World!
    ); } const getDiv = () => /** @type {HTMLDivElement} */ (scratch.firstChild); const getSpan = () => /** @type {HTMLSpanElement} */ (scratch.firstChild.firstChild); const getCSSVariableValue = () => getDiv().style.getPropertyValue('--color'); const getTextColor = () => window.getComputedStyle(getSpan()).color; render(, scratch); expect(getCSSVariableValue()).to.equal('blue'); expect(getTextColor()).to.equal(blue); render(, scratch); expect(getCSSVariableValue(), 'empty string').to.equal(''); expect(getTextColor(), 'empty string').to.equal(red); render(, scratch); expect(getCSSVariableValue()).to.equal('green'); expect(getTextColor()).to.equal(green); render(, scratch); expect(getCSSVariableValue(), 'undefined').to.equal(''); expect(getTextColor(), 'undefined').to.equal(red); render(, scratch); expect(getCSSVariableValue()).to.equal('yellow'); expect(getTextColor()).to.equal(yellow); render(, scratch); expect(getCSSVariableValue(), 'null').to.equal(''); expect(getTextColor(), 'null').to.equal(red); render(, scratch); expect(getCSSVariableValue()).to.equal('violet'); expect(getTextColor()).to.equal(violet); }); } }); preactjs-preact-389c7bc/test/browser/svg.test.jsx000066400000000000000000000161071523334003000221700ustar00rootroot00000000000000import { createElement, Component, render } from 'preact'; import { setupRerender } from 'preact/test-utils'; import { setupScratch, teardown, sortAttributes } from '../_util/helpers'; /** @jsx createElement */ describe('svg', () => { let scratch; beforeEach(() => { scratch = setupScratch(); }); afterEach(() => { teardown(scratch); }); it('should render SVG to string', () => { render( , scratch ); let html = sortAttributes( String(scratch.innerHTML).replace( ' xmlns="http://www.w3.org/2000/svg"', '' ) ); expect(html).to.equal( sortAttributes( ` `.replace(/[\n\t]+/g, '') ) ); }); it('should support svg xlink:href attribute', () => { render( createElement('svg', {}, createElement('use', { 'xlink:href': '#foo' })), scratch ); expect(scratch.innerHTML).to.contain(` href="#foo"`); }); it('should support svg attributes', () => { const Demo = ({ url }) => ( ); render(, scratch); let html = String(scratch.innerHTML).replace( ' xmlns="http://www.w3.org/2000/svg"', '' ); html = sortAttributes( html.replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', '') ); expect(html).to.equal( sortAttributes( ` `.replace(/[\n\t]+/g, '') ) ); render(, scratch); html = String(scratch.innerHTML).replace( ' xmlns="http://www.w3.org/2000/svg"', '' ); html = sortAttributes( html.replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', '') ); expect(html).to.equal( sortAttributes( ` `.replace(/[\n\t]+/g, '') ) ); }); it('should render SVG to DOM', () => { const Demo = () => ( ); render(, scratch); let html = sortAttributes( String(scratch.innerHTML).replace( ' xmlns="http://www.w3.org/2000/svg"', '' ) ); expect(html).to.equal( sortAttributes( '' ) ); }); it('should render with the correct namespace URI', () => { render(, scratch); let namespace = scratch.querySelector('svg').namespaceURI; expect(namespace).to.equal('http://www.w3.org/2000/svg'); }); it('should render children with the correct namespace URI', () => { render( , scratch ); let namespace = scratch.querySelector('text').namespaceURI; expect(namespace).to.equal('http://www.w3.org/2000/svg'); }); it('should inherit correct namespace URI from parent', () => { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); scratch.appendChild(svg); render(, scratch.firstChild); let namespace = scratch.querySelector('text').namespaceURI; expect(namespace).to.equal('http://www.w3.org/2000/svg'); }); it('should inherit correct namespace URI from parent upon updating', () => { setupRerender(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); scratch.appendChild(svg); class App extends Component { state = { show: true }; componentDidMount() { // eslint-disable-next-line this.setState({ show: false }, () => { expect(scratch.querySelector('circle').namespaceURI).to.equal( 'http://www.w3.org/2000/svg' ); }); } render() { return this.state.show ? : ; } } render(, scratch.firstChild); }); it('should use attributes for className', () => { const Demo = ({ c }) => ( ); render(, scratch); let root = scratch.firstChild; sinon.spy(root, 'removeAttribute'); render(, scratch); expect(root.removeAttribute).to.have.been.calledOnce.and.calledWith( 'class' ); root.removeAttribute.restore(); render(
    , scratch); render(, scratch); root = scratch.firstChild; sinon.spy(root, 'setAttribute'); render(, scratch); expect(root.setAttribute).to.have.been.calledOnce.and.calledWith( 'class', 'foo_2' ); root.setAttribute.restore(); }); it('should still support class attribute', () => { render(, scratch); expect(scratch.innerHTML).to.contain(` class="foo bar"`); }); it('should still support className attribute', () => { render(, scratch); expect(scratch.innerHTML).to.contain(` class="foo bar"`); }); it('should switch back to HTML for ', () => { render( test , scratch ); expect(scratch.getElementsByTagName('a')) .to.have.property('0') .that.is.a('HTMLAnchorElement'); }); it('should render foreignObject as an svg element', () => { render( test , scratch ); expect(scratch.querySelector('foreignObject').localName).to.equal( 'foreignObject' ); }); it('should transition from DOM to SVG and back', () => { render(
    , scratch ); expect(scratch.firstChild).to.be.an('HTMLDivElement'); expect(scratch.firstChild.firstChild).to.be.an('SVGSVGElement'); }); }); preactjs-preact-389c7bc/test/browser/toChildArray.test.jsx000066400000000000000000000117651523334003000237630ustar00rootroot00000000000000import { createElement, render, toChildArray } from 'preact'; import { setupScratch, teardown, getMixedArray, mixedArrayHTML } from '../_util/helpers'; /** @jsx createElement */ describe('toChildArray', () => { /** @type {HTMLDivElement} */ let scratch; let children; let Foo = props => { children = toChildArray(props.children); return
    {children}
    ; }; let Bar = () => Bar; beforeEach(() => { scratch = setupScratch(); children = undefined; }); afterEach(() => { teardown(scratch); }); it('returns an empty array with no child', () => { render(, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(0); expect(scratch.innerHTML).to.equal('
    '); }); it('returns an empty array with null as a child', () => { render({null}, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(0); expect(scratch.innerHTML).to.equal('
    '); }); it('returns an empty array with false as a child', () => { render({false}, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(0); expect(scratch.innerHTML).to.equal('
    '); }); it('returns an empty array with true as a child', () => { render({true}, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(0); expect(scratch.innerHTML).to.equal('
    '); }); it('should skip a function child', () => { const child = num => num.toFixed(2); render({child}, scratch); expect(children).to.be.an('array'); expect(scratch.innerHTML).to.equal('
    '); }); it('returns an array containing a VNode with a text child', () => { render(text, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(1); expect(children[0]).to.equal('text'); expect(scratch.innerHTML).to.equal('
    text
    '); }); it('returns an array containing a VNode with a number child', () => { render({1}, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(1); expect(children[0]).to.equal(1); expect(scratch.innerHTML).to.equal('
    1
    '); }); it('returns an array containing a VNode with a DOM node child', () => { render( , scratch ); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(1); expect(children[0].type).to.equal('span'); expect(scratch.innerHTML).to.equal('
    '); }); it('returns an array containing a VNode with a Component child', () => { render( , scratch ); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(1); expect(children[0].type).to.equal(Bar); expect(scratch.innerHTML).to.equal('
    Bar
    '); }); it('returns an array with multiple children', () => { render( 0
    1 , scratch ); expect(children).to.be.an('array'); expect(children[0]).to.equal('0'); expect(children[1].type).to.equal('span'); expect(children[2].type).to.equal('input'); expect(children[3].type).to.equal('div'); expect(children[4]).to.equal('1'); expect(scratch.innerHTML).to.equal( `
    0
    1
    ` ); }); it('returns an array with non-renderables removed with a mixed array as children', () => { const mixedArray = getMixedArray(); render({mixedArray}, scratch); expect(children).to.be.an('array'); expect(children).to.have.lengthOf(8); // Length of flattened mixedArray with non-renderables removed expect(scratch.innerHTML).to.equal(`
    ${mixedArrayHTML}
    `); function filterAndReduceChildren(acc, child) { if (Array.isArray(child)) { return child.reduce(filterAndReduceChildren, acc); } if ( child != null && typeof child != 'boolean' && typeof child != 'function' ) { acc.push(child); } return acc; } let renderableArray = filterAndReduceChildren([], mixedArray); expect(children).to.have.lengthOf(renderableArray.length); for (let i = 0; i < renderableArray.length; i++) { let originalChild = renderableArray[i]; let actualChild = children[i]; if ( typeof originalChild == 'string' || typeof originalChild == 'number' ) { expect(actualChild).to.equal(originalChild); } else { expect(actualChild.type).to.equal(originalChild.type); } } }); it('flattens sibling and nested arrays', () => { const list1 = [0, 1]; const list2 = [2, 3]; const list3 = [4, 5]; const list4 = [6, 7]; const list5 = [8, 9]; const flatList = [...list1, ...list2, ...list3, ...list4, ...list5]; render( {[list1, list2]} {[list3, list4]} {list5} , scratch ); expect(children).to.be.an('array'); expect(scratch.innerHTML).to.equal('
    0123456789
    '); for (let i = 0; i < flatList.length; i++) { expect(children[i]).to.equal(flatList[i]); } }); }); preactjs-preact-389c7bc/test/fixtures/000077500000000000000000000000001523334003000200465ustar00rootroot00000000000000preactjs-preact-389c7bc/test/fixtures/preact.js000066400000000000000000000450361523334003000216720ustar00rootroot00000000000000!(function () { 'use strict'; function h(nodeName, attributes) { var lastSimple, child, simple, i, children = EMPTY_CHILDREN; for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]); if (attributes && null != attributes.children) { if (!stack.length) stack.push(attributes.children); delete attributes.children; } while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else { if ('boolean' == typeof child) child = null; if ((simple = 'function' != typeof nodeName)) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1; if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [child]; else children.push(child); lastSimple = simple; } var p = new VNode(); p.nodeName = nodeName; p.children = children; p.attributes = null == attributes ? void 0 : attributes; p.key = null == attributes ? void 0 : attributes.key; if (void 0 !== options.vnode) options.vnode(p); return p; } function extend(obj, props) { for (var i in props) obj[i] = props[i]; return obj; } function applyRef(ref, value) { if ('function' == typeof ref) ref(value); else if (null != ref) ref.current = value; } function cloneElement(vnode, props) { return h( vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children ); } function enqueueRender(component) { if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender); } function rerender() { var p; while ((p = items.pop())) if (p.__d) renderComponent(p); } function isSameNodeType(node, vnode, hydrating) { if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText; if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName; } function isNamedNode(node, nodeName) { return ( node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase() ); } function getNodeProps(vnode) { var props = extend({}, vnode.attributes); props.children = vnode.children; var defaultProps = vnode.nodeName.defaultProps; if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i]; return props; } function createNode(nodeName, isSvg) { var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName); node.__n = nodeName; return node; } function removeNode(node) { var parentNode = node.parentNode; if (parentNode) parentNode.removeChild(node); } function setAccessor(node, name, old, value, isSvg) { if ('className' === name) name = 'class'; if ('key' === name); else if ('ref' === name) { applyRef(old, null); applyRef(value, node); } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) { if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || ''; if (value && 'object' == typeof value) { if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = ''; for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i]; } } else if ('dangerouslySetInnerHTML' === name) { if (value) node.innerHTML = value.__html || ''; } else if ('o' == name[0] && 'n' == name[1]) { var useCapture = name !== (name = name.replace(/Capture$/, '')); name = name.toLowerCase().substring(2); if (value) { if (!old) node.addEventListener(name, eventProxy, useCapture); } else node.removeEventListener(name, eventProxy, useCapture); (node.__l || (node.__l = {}))[name] = value; } else if ('list' !== name && 'type' !== name && !isSvg && name in node) { try { node[name] = null == value ? '' : value; } catch (e) {} if ((null == value || !1 === value) && 'spellcheck' != name) node.removeAttribute(name); } else { var ns = isSvg && name !== (name = name.replace(/^xlink:?/, '')); if (null == value || !1 === value) if (ns) node.removeAttributeNS( 'http://www.w3.org/1999/xlink', name.toLowerCase() ); else node.removeAttribute(name); else if ('function' != typeof value) if (ns) node.setAttributeNS( 'http://www.w3.org/1999/xlink', name.toLowerCase(), value ); else node.setAttribute(name, value); } } function eventProxy(e) { return this.__l[e.type]((options.event && options.event(e)) || e); } function flushMounts() { var c; while ((c = mounts.pop())) { if (options.afterMount) options.afterMount(c); if (c.componentDidMount) c.componentDidMount(); } } function diff(dom, vnode, context, mountAll, parent, componentRoot) { if (!diffLevel++) { isSvgMode = null != parent && void 0 !== parent.ownerSVGElement; hydrating = null != dom && !('__preactattr_' in dom); } var ret = idiff(dom, vnode, context, mountAll, componentRoot); if (parent && ret.parentNode !== parent) parent.appendChild(ret); if (!--diffLevel) { hydrating = !1; if (!componentRoot) flushMounts(); } return ret; } function idiff(dom, vnode, context, mountAll, componentRoot) { var out = dom, prevSvgMode = isSvgMode; if (null == vnode || 'boolean' == typeof vnode) vnode = ''; if ('string' == typeof vnode || 'number' == typeof vnode) { if ( dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot) ) { if (dom.nodeValue != vnode) dom.nodeValue = vnode; } else { out = document.createTextNode(vnode); if (dom) { if (dom.parentNode) dom.parentNode.replaceChild(out, dom); recollectNodeTree(dom, !0); } } out.__preactattr_ = !0; return out; } var vnodeName = vnode.nodeName; if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll); isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode; vnodeName = String(vnodeName); if (!dom || !isNamedNode(dom, vnodeName)) { out = createNode(vnodeName, isSvgMode); if (dom) { while (dom.firstChild) out.appendChild(dom.firstChild); if (dom.parentNode) dom.parentNode.replaceChild(out, dom); recollectNodeTree(dom, !0); } } var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children; if (null == props) { props = out.__preactattr_ = {}; for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value; } if ( !hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling ) { if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0]; } else if ((vchildren && vchildren.length) || null != fc) innerDiffNode( out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML ); diffAttributes(out, vnode.attributes, props); isSvgMode = prevSvgMode; return out; } function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { var j, c, f, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0; if (0 !== len) for (var i = 0; i < len; i++) { var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null; if (null != key) { keyedLen++; keyed[key] = _child; } else if ( props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating) ) children[childrenLen++] = _child; } if (0 !== vlen) for (var i = 0; i < vlen; i++) { vchild = vchildren[i]; child = null; var key = vchild.key; if (null != key) { if (keyedLen && void 0 !== keyed[key]) { child = keyed[key]; keyed[key] = void 0; keyedLen--; } } else if (min < childrenLen) for (j = min; j < childrenLen; j++) if ( void 0 !== children[j] && isSameNodeType((c = children[j]), vchild, isHydrating) ) { child = c; children[j] = void 0; if (j === childrenLen - 1) childrenLen--; if (j === min) min++; break; } child = idiff(child, vchild, context, mountAll); f = originalChildren[i]; if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f); } if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1); while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1); } function recollectNodeTree(node, unmountOnly) { var component = node._component; if (component) unmountComponent(component); else { if (null != node.__preactattr_) applyRef(node.__preactattr_.ref, null); if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node); removeChildren(node); } } function removeChildren(node) { node = node.lastChild; while (node) { var next = node.previousSibling; recollectNodeTree(node, !0); node = next; } } function diffAttributes(dom, attrs, old) { var name; for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], (old[name] = void 0), isSvgMode); for (name in attrs) if ( !( 'children' === name || 'innerHTML' === name || (name in old && attrs[name] === ('value' === name || 'checked' === name ? dom[name] : old[name])) ) ) setAccessor(dom, name, old[name], (old[name] = attrs[name]), isSvgMode); } function createComponent(Ctor, props, context) { var inst, i = recyclerComponents.length; if (Ctor.prototype && Ctor.prototype.render) { inst = new Ctor(props, context); Component.call(inst, props, context); } else { inst = new Component(props, context); inst.constructor = Ctor; inst.render = doRender; } while (i--) if (recyclerComponents[i].constructor === Ctor) { inst.__b = recyclerComponents[i].__b; recyclerComponents.splice(i, 1); return inst; } return inst; } function doRender(props, state, context) { return this.constructor(props, context); } function setComponentProps(component, props, renderMode, context, mountAll) { if (!component.__x) { component.__x = !0; component.__r = props.ref; component.__k = props.key; delete props.ref; delete props.key; if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) { if (component.componentWillMount) component.componentWillMount(); } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context); if (context && context !== component.context) { if (!component.__c) component.__c = component.context; component.context = context; } if (!component.__p) component.__p = component.props; component.props = props; component.__x = !1; if (0 !== renderMode) if ( 1 === renderMode || !1 !== options.syncComponentUpdates || !component.base ) renderComponent(component, 1, mountAll); else enqueueRender(component); applyRef(component.__r, component); } } function renderComponent(component, renderMode, mountAll, isChild) { if (!component.__x) { var rendered, inst, cbase, props = component.props, state = component.state, context = component.context, previousProps = component.__p || props, previousState = component.__s || state, previousContext = component.__c || context, isUpdate = component.base, nextBase = component.__b, initialBase = isUpdate || nextBase, initialChildComponent = component._component, skip = !1, snapshot = previousContext; if (component.constructor.getDerivedStateFromProps) { state = extend( extend({}, state), component.constructor.getDerivedStateFromProps(props, state) ); component.state = state; } if (isUpdate) { component.props = previousProps; component.state = previousState; component.context = previousContext; if ( 2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context) ) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context); component.props = props; component.state = state; component.context = context; } component.__p = component.__s = component.__c = component.__b = null; component.__d = !1; if (!skip) { rendered = component.render(props, state, context); if (component.getChildContext) context = extend(extend({}, context), component.getChildContext()); if (isUpdate && component.getSnapshotBeforeUpdate) snapshot = component.getSnapshotBeforeUpdate( previousProps, previousState ); var toUnmount, base, childComponent = rendered && rendered.nodeName; if ('function' == typeof childComponent) { var childProps = getNodeProps(rendered); inst = initialChildComponent; if ( inst && inst.constructor === childComponent && childProps.key == inst.__k ) setComponentProps(inst, childProps, 1, context, !1); else { toUnmount = inst; component._component = inst = createComponent( childComponent, childProps, context ); inst.__b = inst.__b || nextBase; inst.__u = component; setComponentProps(inst, childProps, 0, context, !1); renderComponent(inst, 1, mountAll, !0); } base = inst.base; } else { cbase = initialBase; toUnmount = initialChildComponent; if (toUnmount) cbase = component._component = null; if (initialBase || 1 === renderMode) { if (cbase) cbase._component = null; base = diff( cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0 ); } } if ( initialBase && base !== initialBase && inst !== initialChildComponent ) { var baseParent = initialBase.parentNode; if (baseParent && base !== baseParent) { baseParent.replaceChild(base, initialBase); if (!toUnmount) { initialBase._component = null; recollectNodeTree(initialBase, !1); } } } if (toUnmount) unmountComponent(toUnmount); component.base = base; if (base && !isChild) { var componentRef = component, t = component; while ((t = t.__u)) (componentRef = t).base = base; base._component = componentRef; base._componentConstructor = componentRef.constructor; } } if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) { if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot); if (options.afterUpdate) options.afterUpdate(component); } while (component.__h.length) component.__h.pop().call(component); if (!diffLevel && !isChild) flushMounts(); } } function buildComponentFromVNode(dom, vnode, context, mountAll) { var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode); while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName; if (c && isOwner && (!mountAll || c._component)) { setComponentProps(c, props, 3, context, mountAll); dom = c.base; } else { if (originalComponent && !isDirectOwner) { unmountComponent(originalComponent); dom = oldDom = null; } c = createComponent(vnode.nodeName, props, context); if (dom && !c.__b) { c.__b = dom; oldDom = null; } setComponentProps(c, props, 1, context, mountAll); dom = c.base; if (oldDom && dom !== oldDom) { oldDom._component = null; recollectNodeTree(oldDom, !1); } } return dom; } function unmountComponent(component) { if (options.beforeUnmount) options.beforeUnmount(component); var base = component.base; component.__x = !0; if (component.componentWillUnmount) component.componentWillUnmount(); component.base = null; var inner = component._component; if (inner) unmountComponent(inner); else if (base) { if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null); component.__b = base; removeNode(base); recyclerComponents.push(component); removeChildren(base); } applyRef(component.__r, null); } function Component(props, context) { this.__d = !0; this.context = context; this.props = props; this.state = this.state || {}; this.__h = []; } function render(vnode, parent, merge) { return diff(merge, vnode, {}, !1, parent, !1); } function createRef() { return {}; } var VNode = function () {}; var options = {}; var stack = []; var EMPTY_CHILDREN = []; var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout; var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i; var items = []; var mounts = []; var diffLevel = 0; var isSvgMode = !1; var hydrating = !1; var recyclerComponents = []; extend(Component.prototype, { setState: function (state, callback) { if (!this.__s) this.__s = this.state; this.state = extend( extend({}, this.state), 'function' == typeof state ? state(this.state, this.props) : state ); if (callback) this.__h.push(callback); enqueueRender(this); }, forceUpdate: function (callback) { if (callback) this.__h.push(callback); renderComponent(this, 2); }, render: function () {} }); var preact = { h: h, createElement: h, cloneElement: cloneElement, createRef: createRef, Component: Component, render: render, rerender: rerender, options: options }; if ('undefined' != typeof module) module.exports = preact; else self.preact = preact; })(); //# sourceMappingURL=preact.js.map preactjs-preact-389c7bc/test/node/000077500000000000000000000000001523334003000171225ustar00rootroot00000000000000preactjs-preact-389c7bc/test/node/index.test.js000066400000000000000000000010411523334003000215410ustar00rootroot00000000000000import { expect } from 'chai'; import * as preact from '../../'; describe('build artifact', () => { // #1075 Check that the build artifact has the correct exports it('should have exported properties', () => { expect(typeof preact).to.equal('object'); expect(preact).to.have.property('createElement'); expect(preact).to.have.property('h'); expect(preact).to.have.property('Component'); expect(preact).to.have.property('render'); expect(preact).to.have.property('hydrate'); // expect(preact).to.have.property('options'); }); }); preactjs-preact-389c7bc/test/node/package.json000066400000000000000000000000311523334003000214020ustar00rootroot00000000000000{ "type": "commonjs" } preactjs-preact-389c7bc/test/shared/000077500000000000000000000000001523334003000174435ustar00rootroot00000000000000preactjs-preact-389c7bc/test/shared/createContext.test.jsx000066400000000000000000000015431523334003000237620ustar00rootroot00000000000000import { createElement, createContext } from '../../src/index'; import { expect } from 'chai'; /** @jsx createElement */ /* eslint-env browser, mocha */ describe('createContext', () => { it('should return a Provider and a Consumer', () => { const context = createContext(); expect(context).to.have.property('Provider'); expect(context).to.have.property('Consumer'); }); it('should return a valid Provider Component', () => { const { Provider } = createContext(); const contextValue = { value: 'test' }; const children = [
    child1
    ,
    child2
    ]; const providerComponent = {children}; //expect(providerComponent).to.have.property('tag', 'Provider'); expect(providerComponent.props.value).to.equal(contextValue.value); expect(providerComponent.props.children).to.equal(children); }); }); preactjs-preact-389c7bc/test/shared/createElement.test.jsx000066400000000000000000000173571523334003000237410ustar00rootroot00000000000000import { createElement } from '../../src/index'; import { expect } from 'chai'; const h = createElement; /** @jsx createElement */ /*eslint-env browser, mocha */ // const buildVNode = (nodeName, attributes, children=[]) => ({ // nodeName, // children, // attributes, // key: attributes && attributes.key // }); describe('createElement(jsx)', () => { it('should return a VNode', () => { let r; expect(() => (r = h('foo', null))).not.to.throw(); expect(r).to.be.an('object'); // expect(r).to.be.an.instanceof(VNode); expect(r).to.have.property('type', 'foo'); expect(r).to.have.property('props').that.eql({}); // expect(r).to.have.deep.property('props.children').that.eql(null); }); it('should set VNode#type property', () => { expect(
    ).to.have.property('type', 'div'); function Test() { return
    ; } expect().to.have.property('type', Test); }); it('should set VNode.constructor property to prevent json injection', () => { const vnode = ; expect(vnode.constructor).to.equal(undefined); }); it('should set VNode#props property', () => { const props = {}; expect(h('div', props).props).to.deep.equal(props); }); it('should set VNode#key property', () => { expect(
    ).to.have.property('key').that.does.not.exist; expect(
    ).to.have.property('key').that.does.not.exist; expect(
    ).to.have.property('key', '1'); }); it('should not set VNode#props.key property', () => { expect(
    ).to.not.have.nested.property('props.key'); expect(
    ).to.not.have.nested.property('props.key'); expect(
    ).to.not.have.nested.property('props.key'); expect(
    ).to.not.have.nested.property('props.key'); }); it('should set VNode#ref property', () => { expect(
    ).to.have.property('ref').that.does.not.exist; expect(
    ).to.have.property('ref').that.does.not.exist; const emptyFunction = () => {}; expect(
    ).to.have.property('ref', emptyFunction); }); it('should not set VNode#props.ref property', () => { expect(
    ).to.not.have.nested.property('props.ref'); expect(
    {}} />).to.not.have.nested.property('props.ref'); }); it('should have ordered VNode properties', () => { expect(Object.keys(
    ).filter(key => !/^_/.test(key))).to.deep.equal([ 'type', 'props', 'key', 'ref', 'constructor' ]); }); it('should preserve raw props', () => { let props = { foo: 'bar', baz: 10, func: () => {} }, r = h('foo', props); expect(r).to.be.an('object').with.property('props').that.deep.equals(props); }); it('should support element children', () => { let kid1 = h('bar', null); let kid2 = h('baz', null); let r = h('foo', null, kid1, kid2); expect(r) .to.be.an('object') .with.nested.deep.property('props.children', [kid1, kid2]); }); it('should support multiple element children, given as arg list', () => { let kid1 = h('bar', null); let kid3 = h('test', null); let kid2 = h('baz', null, kid3); let r = h('foo', null, kid1, kid2); expect(r) .to.be.an('object') .with.nested.deep.property('props.children', [kid1, kid2]); }); it('should handle multiple element children, given as an array', () => { let kid1 = h('bar', null); let kid3 = h('test', null); let kid2 = h('baz', null, kid3); let r = h('foo', null, [kid1, kid2]); expect(r) .to.be.an('object') .with.nested.deep.property('props.children', [kid1, kid2]); }); it('should support nested children', () => { const m = x => { const result = h(x, null); delete result._original; return result; }; expect(h('foo', null, m('a'), [m('b'), m('c')], m('d'))) .to.have.nested.property('props.children') .that.eql([m('a'), [m('b'), m('c')], m('d')]); expect(h('foo', null, [m('a'), [m('b'), m('c')], m('d')])) .to.have.nested.property('props.children') .that.eql([m('a'), [m('b'), m('c')], m('d')]); expect(h('foo', { children: [m('a'), [m('b'), m('c')], m('d')] })) .to.have.nested.property('props.children') .that.eql([m('a'), [m('b'), m('c')], m('d')]); expect(h('foo', { children: [[m('a'), [m('b'), m('c')], m('d')]] })) .to.have.nested.property('props.children') .that.eql([[m('a'), [m('b'), m('c')], m('d')]]); expect(h('foo', { children: m('a') })) .to.have.nested.property('props.children') .that.eql(m('a')); expect(h('foo', { children: 'a' })) .to.have.nested.property('props.children') .that.eql('a'); }); it('should support text children', () => { let r = h('foo', null, 'textstuff'); expect(r) .to.be.an('object') .with.nested.property('props.children') .that.equals('textstuff'); }); it('should override children if null is provided as an argument', () => { let r = h('foo', { foo: 'bar', children: 'baz' }, null); expect(r).to.be.an('object').to.deep.nested.include({ 'props.foo': 'bar', 'props.children': null }); }); it('should NOT set children prop when unspecified', () => { let r = h('foo', { foo: 'bar' }); expect(r) .to.be.an('object') .to.have.nested.property('props.foo') .not.to.have.nested.property('props.children'); }); it('should NOT merge adjacent text children', () => { const bar = h('bar', null); const barClone = h('bar', null); const baz = h('baz', null); const bazClone = h('baz', null); const baz2 = h('baz', null); const baz2Clone = h('baz', null); delete bar._original; delete barClone._original; delete baz._original; delete bazClone._original; delete baz2._original; delete baz2Clone._original; let r = h( 'foo', null, 'one', 'two', bar, 'three', baz, baz2, 'four', null, 'five', 'six' ); expect(r) .to.be.an('object') .with.nested.property('props.children') .that.deep.equals([ 'one', 'two', barClone, 'three', bazClone, baz2Clone, 'four', null, 'five', 'six' ]); }); it('should not merge nested adjacent text children', () => { let r = h( 'foo', null, 'one', ['two', null, 'three'], null, ['four', null, 'five', null], 'six', null ); expect(r) .to.be.an('object') .with.nested.property('props.children') .that.deep.equals([ 'one', ['two', null, 'three'], null, ['four', null, 'five', null], 'six', null ]); }); it('should not merge children that are boolean values', () => { let r = h('foo', null, 'one', true, 'two', false, 'three'); expect(r) .to.be.an('object') .with.nested.property('props.children') .that.deep.equals(['one', true, 'two', false, 'three']); }); it('should not merge children of components', () => { let Component = ({ children }) => children; let r = h(Component, null, 'x', 'y'); expect(r) .to.be.an('object') .with.nested.property('props.children') .that.deep.equals(['x', 'y']); }); it('should respect defaultProps', () => { const Component = ({ children }) => children; Component.defaultProps = { foo: 'bar' }; expect(h(Component, null).props).to.deep.equal({ foo: 'bar' }); }); it('should respect defaultProps when props are null', () => { const Component = ({ children }) => children; Component.defaultProps = { foo: 'bar' }; expect(h(Component, { foo: null }).props).to.deep.equal({ foo: null }); }); it('should override defaultProps', () => { const Component = ({ children }) => children; Component.defaultProps = { foo: 'default' }; expect(h(Component, { foo: 'bar' }).props).to.deep.equal({ foo: 'bar' }); }); it('should ignore props.children if children are manually specified', () => { const element = (
    c
    ); const childrenless =
    c
    ; delete element._original; delete childrenless._original; expect(element).to.eql(childrenless); }); }); preactjs-preact-389c7bc/test/shared/exports.test.js000066400000000000000000000014321523334003000224630ustar00rootroot00000000000000import { createElement, h, createContext, Component, Fragment, render, hydrate, cloneElement, options, createRef, toChildArray, isValidElement } from '../../src/index'; import { expect } from 'chai'; describe('preact', () => { it('should be available as named exports', () => { expect(h).to.be.a('function'); expect(createElement).to.be.a('function'); expect(Component).to.be.a('function'); expect(Fragment).to.exist; expect(render).to.be.a('function'); expect(hydrate).to.be.a('function'); expect(cloneElement).to.be.a('function'); expect(createContext).to.be.a('function'); expect(options).to.exist.and.be.an('object'); expect(createRef).to.be.a('function'); expect(isValidElement).to.be.a('function'); expect(toChildArray).to.be.a('function'); }); }); preactjs-preact-389c7bc/test/shared/isValidElement.test.js000066400000000000000000000003601523334003000236630ustar00rootroot00000000000000import { createElement, isValidElement, Component } from '../../src/index'; import { expect } from 'chai'; import { isValidElementTests } from './isValidElementTests'; isValidElementTests(expect, isValidElement, createElement, Component); preactjs-preact-389c7bc/test/shared/isValidElementTests.jsx000066400000000000000000000020621523334003000241210ustar00rootroot00000000000000/** @jsx createElement */ export function isValidElementTests( expect, isValidElement, createElement, Component ) { describe('isValidElement', () => { it('should check if the argument is a valid vnode', () => { // Failure cases expect(isValidElement(123)).to.equal(false); expect(isValidElement(0)).to.equal(false); expect(isValidElement('')).to.equal(false); expect(isValidElement('abc')).to.equal(false); expect(isValidElement(null)).to.equal(false); expect(isValidElement(undefined)).to.equal(false); expect(isValidElement(true)).to.equal(false); expect(isValidElement(false)).to.equal(false); expect(isValidElement([])).to.equal(false); expect(isValidElement([123])).to.equal(false); expect(isValidElement([null])).to.equal(false); // Success cases expect(isValidElement(
    )).to.equal(true); const Foo = () => 123; expect(isValidElement()).to.equal(true); class Bar extends Component { render() { return
    ; } } expect(isValidElement()).to.equal(true); }); }); } preactjs-preact-389c7bc/test/shared/package.json000066400000000000000000000000311523334003000217230ustar00rootroot00000000000000{ "type": "commonjs" } preactjs-preact-389c7bc/test/ts/000077500000000000000000000000001523334003000166235ustar00rootroot00000000000000preactjs-preact-389c7bc/test/ts/Component-test.tsx000066400000000000000000000072151523334003000223070ustar00rootroot00000000000000import 'mocha'; import { expect } from 'chai'; import { createElement, Component, RenderableProps, Fragment } from '../../'; export class ContextComponent extends Component<{ foo: string }> { getChildContext() { return { something: 2 }; } render() { return null; } } export interface SimpleComponentProps { initialName: string | null; } export interface SimpleState { name: string | null; } export class SimpleComponent extends Component< SimpleComponentProps, SimpleState > { constructor(props: SimpleComponentProps) { super(props); this.state = { name: props.initialName }; } render() { if (!this.state.name) { return null; } const { initialName, children } = this.props; return (
    {initialName} / {this.state.name} {children}
    ); } } class DestructuringRenderPropsComponent extends Component< SimpleComponentProps, SimpleState > { constructor(props: SimpleComponentProps) { super(props); this.state = { name: props.initialName }; } render({ initialName, children }: RenderableProps) { if (!this.state.name) { return null; } return ( {this.props.initialName} / {this.state.name} ); } } interface RandomChildrenComponentProps { num?: number; val?: string; span?: boolean; } class RandomChildrenComponent extends Component { render() { const { num, val, span } = this.props; if (num) { return num; } if (val) { return val; } if (span) { return hi; } return null; } } class StaticComponent extends Component { static getDerivedStateFromProps( props: SimpleComponentProps, state: SimpleState ): Partial { return { ...props, ...state }; } static getDerivedStateFromError(err: Error) { return { name: err.message }; } render() { return null; } } function MapperItem(props: { foo: number }) { return
    ; } function Mapper() { return [1, 2, 3].map(x => ); } class Button extends Component { handleClick(this: HTMLButtonElement, event: MouseEvent) { event.preventDefault(); if (event.target instanceof HTMLElement) { console.log(event.target.localName); } } render() { return ; } } describe('Component', () => { const component = new SimpleComponent({ initialName: 'da name' }); it('has state', () => { expect(component.state.name).to.eq('da name'); }); it('has props', () => { expect(component.props.initialName).to.eq('da name'); }); it('has no base when not mounted', () => { expect(component.base).to.not.exist; }); describe('setState', () => { // No need to execute these tests. because we only need to check if // the types are working. Executing them would require the DOM. // TODO: Run TS tests in our standard karma setup it.skip('can be used with an object', () => { component.setState({ name: 'another name' }); }); it.skip('can be used with a function', () => { const updater = (state: any, props: any) => ({ name: `${state.name} - ${props.initialName}` }); component.setState(updater); }); }); describe('render', () => { it('can return null', () => { const comp = new SimpleComponent({ initialName: null }); const actual = comp.render(); expect(actual).to.eq(null); }); }); describe('Fragment', () => { it('should render nested Fragments', () => { var vnode = ( foo bar ); expect(vnode.type).to.be.equal(Fragment); }); }); }); preactjs-preact-389c7bc/test/ts/VNode-test.tsx000066400000000000000000000121201523334003000213470ustar00rootroot00000000000000import 'mocha'; import { expect } from 'chai'; import { createElement, Component, toChildArray, FunctionalComponent, ComponentConstructor, ComponentFactory, VNode, ComponentChildren, cloneElement, ComponentChild } from '../../'; function getDisplayType(vnode: VNode | string | number) { if (typeof vnode === 'string' || typeof vnode == 'number') { return vnode.toString(); } else if (typeof vnode.type == 'string') { return vnode.type; } else { return vnode.type.displayName; } } class SimpleComponent extends Component<{}, {}> { render() { return
    {this.props.children}
    ; } } const SimpleFunctionalComponent = () =>
    ; const a: ComponentFactory = SimpleComponent; const b: ComponentFactory = SimpleFunctionalComponent; describe('VNode TS types', () => { it('is returned by h', () => { const actual =
    ; expect(actual).to.include.all.keys('type', 'props', 'key'); }); it('is returned by h', () => { const actual =
    ; expect(actual).to.include.all.keys('type', 'props', 'key'); }); it('createElement conforms to the VNode type', () => { const arr: VNode[] = []; arr.push(createElement('div', null)); expect(true).to.be.true; }); it('has a nodeName equal to the construction function when SFC', () => { const sfc = ; expect(sfc.type).to.be.instanceOf(Function); const constructor = sfc.type as FunctionalComponent; expect(constructor.name).to.eq('SimpleFunctionalComponent'); }); it('has a nodeName equal to the constructor of a component', () => { const sfc = ; expect(sfc.type).to.be.instanceOf(Function); const constructor = sfc.type as ComponentConstructor; expect(constructor.name).to.eq('SimpleComponent'); }); it('has children which is an array of string or other vnodes', () => { const comp = ( child1 child2 ); expect(comp.props.children).to.be.instanceOf(Array); expect(comp.props.children[1]).to.be.a('string'); }); it('children type should work with toChildArray', () => { const comp: VNode = child1 {1}; const children = toChildArray(comp.props.children); expect(children).to.have.lengthOf(2); }); it('toChildArray should filter out some types', () => { const compChild = ; const comp: VNode = ( a{null} {true} {false} {2} {undefined} {['b', 'c']} {compChild} ); const children = toChildArray(comp.props.children); expect(children).to.deep.equal(['a', 2, 'b', 'c', compChild]); }); it('functions like getDisplayType should work', () => { function TestComp(props: { children?: ComponentChildren }) { return
    {props.children}
    ; } TestComp.displayName = 'TestComp'; const compChild = ; const comp: VNode = ( a{null} {true} {false} {2} {undefined} {['b', 'c']} {compChild} ); const types = toChildArray(comp.props.children).map(getDisplayType); expect(types).to.deep.equal(['a', '2', 'b', 'c', 'TestComp']); }); it('component should work with cloneElement', () => { const comp: VNode = (
    child 1
    ); const clone: VNode = cloneElement(comp); expect(comp.type).to.equal(clone.type); expect(comp.props).not.to.equal(clone.props); expect(comp.props).to.deep.equal(clone.props); }); it('component should work with cloneElement using generics', () => { const comp: VNode = ; const clone: VNode = cloneElement(comp); expect(comp.type).to.equal(clone.type); expect(comp.props).not.to.equal(clone.props); expect(comp.props).to.deep.equal(clone.props); }); }); class ComponentWithFunctionChild extends Component<{ children: (num: number) => string; }> { render() { return null; } } {num => num.toFixed(2)} ; class ComponentWithStringChild extends Component<{ children: string }> { render() { return null; } } child; class ComponentWithNumberChild extends Component<{ children: number }> { render() { return null; } } {1}; class ComponentWithBooleanChild extends Component<{ children: boolean }> { render() { return null; } } {false}; class ComponentWithNullChild extends Component<{ children: null }> { render() { return null; } } {null}; class ComponentWithNumberChildren extends Component<{ children: number[] }> { render() { return null; } } {1} {2} ; const ComponentReturningComponentChildren = ({ children }: { children: ComponentChild; }) => children; 123; preactjs-preact-389c7bc/test/ts/custom-elements.tsx000066400000000000000000000053101523334003000225060ustar00rootroot00000000000000import { createElement, Component, createContext } from '../../'; declare module '../../' { namespace createElement.JSX { interface IntrinsicElements { // Custom element can use JSX EventHandler definitions 'clickable-ce': { optionalAttr?: string; onClick?: MouseEventHandler; }; // Custom Element that extends HTML attributes 'color-picker': HTMLAttributes & { // Required attribute space: 'rgb' | 'hsl' | 'hsv'; // Optional attribute alpha?: boolean; }; // Custom Element with custom interface definition 'custom-whatever': WhateveElAttributes; } } } // Whatever Element definition interface WhateverElement { instanceProp: string; } interface WhateverElementEvent { eventProp: number; } // preact.JSX.HTMLAttributes also appears to work here but for consistency, // let's use createElement.JSX interface WhateveElAttributes extends createElement.JSX.HTMLAttributes { someattribute?: string; onsomeevent?: (this: WhateverElement, ev: WhateverElementEvent) => void; } // Ensure context still works const Ctx = createContext({ contextValue: '' }); // Sample component that uses custom elements class SimpleComponent extends Component { componentProp = 'componentProp'; render() { // Render inside div to ensure standard JSX elements still work return (
    { // `this` should be instance of SimpleComponent since this is an // arrow function console.log(this.componentProp); // Validate `currentTarget` is HTMLElement console.log('clicked ', e.currentTarget.style.display); }} > {/* Ensure context still works */} {({ contextValue }) => contextValue.toLowerCase()}
    ); } } const component = ; class SimpleComponentWithContextAsProvider extends Component { componentProp = 'componentProp'; render() { // Render inside div to ensure standard JSX elements still work return (
    {/* Ensure context still works */} {({ contextValue }) => contextValue.toLowerCase()}
    ); } } const component2 = ; preactjs-preact-389c7bc/test/ts/dom-attributes-test.tsx000066400000000000000000000030631523334003000233050ustar00rootroot00000000000000import { createElement, Fragment, JSX } from 'preact'; function createSignal(value: T): JSX.SignalLike { return { value, peek() { return value; }, subscribe() { return () => {}; } }; } // @ts-expect-error We should correctly type aria attributes like autocomplete const badAriaValues =
    ; const validAriaValues =
    ; const undefAriaValues =
    ; const noAriaValues =
    ; const signalBadAriaValues = ( // @ts-expect-error We should correctly type aria attributes like autocomplete
    ); const signalValidAriaValues = (
    ); const signalValidAriaValues2 = (
    )} /> ); const validRole =
    ; // @ts-expect-error We should correctly type aria roles const invalidRole =
    ; // @ts-expect-error We should disallow `generic` as it should not ever be explicitly set const invalidRole2 =
    ; const fallbackRole =
    ; const booleanishTest = ( <>
    ); const dangerouslySetInnerHTML = ( <>
    ); preactjs-preact-389c7bc/test/ts/hoc-test.tsx000066400000000000000000000021101523334003000211030ustar00rootroot00000000000000import { expect } from 'chai'; import { createElement, ComponentFactory, ComponentConstructor, Component } from '../../'; import { SimpleComponent, SimpleComponentProps } from './Component-test'; export interface highlightedProps { isHighlighted: boolean; } export function highlighted( Wrappable: ComponentFactory ): ComponentConstructor { return class extends Component { constructor(props: T & highlightedProps) { super(props); } render() { let className = this.props.isHighlighted ? 'highlighted' : ''; return (
    ); } toString() { return `Highlighted ${Wrappable.name}`; } }; } const HighlightedSimpleComponent = highlighted(SimpleComponent); describe('hoc', () => { it('wraps the given component', () => { const highlight = new HighlightedSimpleComponent({ initialName: 'initial name', isHighlighted: true }); expect(highlight.toString()).to.eq('Highlighted SimpleComponent'); }); }); preactjs-preact-389c7bc/test/ts/jsx-namespacce-test.tsx000066400000000000000000000004751523334003000232470ustar00rootroot00000000000000import { createElement, Component } from '../../'; // declare global JSX types that should not be mixed with preact's internal types declare global { namespace JSX { interface Element { unknownProperty: string; } } } class SimpleComponent extends Component { render() { return
    It works
    ; } } preactjs-preact-389c7bc/test/ts/package.json000066400000000000000000000000311523334003000211030ustar00rootroot00000000000000{ "type": "commonjs" } preactjs-preact-389c7bc/test/ts/preact-global-test.tsx000066400000000000000000000003021523334003000230470ustar00rootroot00000000000000import { createElement } from '../../src'; // Test that preact types are available via the global `preact` namespace. let component: preact.ComponentChild; component =
    Hello World
    ; preactjs-preact-389c7bc/test/ts/preact.tsx000066400000000000000000000242451523334003000206500ustar00rootroot00000000000000import { createElement, render, Component, ComponentProps, FunctionalComponent, AnyComponent, h, createRef, JSX } from '../../'; function createSignal(value: T): JSX.SignalLike { return { value, peek() { return value; }, subscribe() { return () => {}; } }; } interface DummyProps { initialInput: string; } interface DummyState { input: string; } class DummyComponent extends Component { constructor(props: DummyProps) { super(props); this.state = { input: `x${this.props}x` }; } private setRef = (el: AnyComponent) => { console.log(el); }; render({ initialInput }: DummyProps, { input }: DummyState) { return (
    {/* Can specify all Preact attributes on a typed FunctionalComponent */}
    ); } } interface DummerComponentProps extends DummyProps, DummyState {} function DummerComponent({ input, initialInput }: DummerComponentProps) { return (
    Input: {input}, initial: {initialInput}
    ); } render(createElement('div', { title: 'test', key: '1' }), document); render( createElement(DummyComponent, { initialInput: 'The input', key: '1' }), document ); render( createElement(DummerComponent, { initialInput: 'The input', input: 'New input', key: '1' }), document ); render(h('div', { title: 'test', key: '1' }), document); render(h(DummyComponent, { initialInput: 'The input', key: '1' }), document); render( h(DummerComponent, { initialInput: 'The input', input: 'New input', key: '1' }), document ); // Mounting into different types of Nodes render(h('div', {}), document.createElement('div')); render(h('div', {}), document); render(h('div', {}), document.createElement('div').shadowRoot!); render(h('div', {}), document.createDocumentFragment()); // From https://gist.github.com/developit/f4c67a2ede71dc2fab7f357f39cff28c, modified to be TypeScript compliant function createRootFragment(parent: Element, replaceNode: Element | Element[]) { const replaceNodes: Element[] = ([] as Element[]).concat(replaceNode); const s = replaceNodes[replaceNodes.length - 1].nextSibling; function insert(c: Node, r: Node | null) { return parent.insertBefore(c, r || s); } return ((parent as any).__k = { nodeType: 1, parentNode: parent, firstChild: replaceNodes[0], childNodes: replaceNodes, contains: (c: Node) => parent.contains(c), insertBefore: insert, appendChild: (c: Node) => insert(c, null), removeChild: function (c: Node) { return parent.removeChild(c); } }); } render( h('div', {}), createRootFragment( document.createElement('div'), document.createElement('div') ) ); // Accessing children const ComponentWithChildren: FunctionalComponent = ({ input, initialInput, children }) => { return (
    {initialInput} {input} {children}
    ); }; const UseOfComponentWithChildren = () => { return ( child 1 child 2 ); }; const DummyChildren: FunctionalComponent = ({ children }) => { return children; }; function ReturnChildren(props: { children: preact.ComponentChildren }) { return props.children; } function TestUndefinedChildren() { return ( Hello ); } // using ref and or jsx class ComponentUsingRef extends Component { private array: string[]; private refs: (Element | null)[] = []; constructor() { super(); this.array = ['1', '2']; } render() { this.refs = []; return (
    {this.array.map(el => ( {el} ))} {/* Can specify Preact attributes on a component */}
    ); } private setRef = (el: Element | null) => { this.refs.push(el); }; } // using lifecycles class ComponentWithLifecycle extends Component { render() { return
    Hi
    ; } componentWillMount() { console.log('componentWillMount'); } componentDidMount() { console.log('componentDidMount'); } componentWillUnmount() { console.log('componentWillUnmount'); } componentWillReceiveProps(nextProps: DummyProps, nextCtx: any) { const { initialInput } = nextProps; console.log('componentWillReceiveProps', initialInput, nextCtx); } shouldComponentUpdate( nextProps: DummyProps, nextState: DummyState, nextContext: any ) { return false; } componentWillUpdate( nextProps: DummyProps, nextState: DummyState, nextContext: any ) { console.log('componentWillUpdate', nextProps, nextState, nextContext); } componentDidUpdate( previousProps: DummyProps, previousState: DummyState, previousContext: any ) { console.log( 'componentDidUpdate', previousProps, previousState, previousContext ); } } // Default props: JSX.LibraryManagedAttributes class DefaultProps extends Component<{ text: string; bool: boolean }> { static defaultProps = { text: 'hello' }; render() { return
    {this.props.text}
    ; } } const d1 = ; const d2 = ; class DefaultPropsWithUnion extends Component< { default: boolean } & ( | { type: 'string'; str: string; } | { type: 'number'; num: number; } ) > { static defaultProps = { default: true }; render() { return
    ; } } const d3 = ; const d4 = ; const d5 = ; const d6 = ; class DefaultUnion extends Component< | { type: 'number'; num: number; } | { type: 'string'; str: string; } > { static defaultProps = { type: 'number', num: 1 }; render() { return
    ; } } const d7 = ; const d8 = ; const d9 = ; const d10 = ; class ComponentWithDefaultProps extends Component<{ value: string }> { static defaultProps = { value: '' }; render() { return
    {this.props.value}
    ; } } const withDefaultProps = ; interface PartialState { foo: string; bar: number; } class ComponentWithPartialSetState extends Component<{}, PartialState> { render({}, { foo, bar }: PartialState) { return ( ); } handleClick = (value: keyof PartialState) => { this.setState({ [value]: 'updated' }); }; } const withPartialSetState = ; let functionalProps: ComponentProps = { initialInput: '', input: '' }; let classProps: ComponentProps = { initialInput: '' }; let elementProps: ComponentProps<'button'> = { type: 'button' }; // Typing of style property const acceptsNumberAsLength =
    ; const acceptsStringAsLength =
    ; const ReturnNull: FunctionalComponent = () => null; // Should accept arbitrary properties outside of JSX.HTMLAttributes h('option', { x: 'foo' }); createElement('option', { value: 'foo' }); // Refs should work on elements const ref = createRef(); createElement('div', { ref: ref }, 'hi'); h('div', { ref: ref }, 'hi'); // Refs should work on functions const functionRef = createRef(); const RefComponentTest = () =>

    hi

    ; createElement(RefComponentTest, { ref: functionRef }, 'hi'); h(RefComponentTest, { ref: functionRef }, 'hi'); // Should accept onInput const onInput = (e: h.JSX.TargetedInputEvent) => {}; ; e.currentTarget.value} />; createElement('input', { onInput: onInput }); h('input', { onInput: onInput }); // Should accept onBeforeInput const onBeforeInput = (e: h.JSX.TargetedInputEvent) => {}; e.currentTarget.value} />; createElement('input', { onBeforeInput: onBeforeInput }); h('input', { onBeforeInput: onBeforeInput }); // Should accept onSubmit const onSubmit = (e: h.JSX.TargetedSubmitEvent) => {};
    e.currentTarget.elements} />; createElement('form', { onSubmit: onSubmit }); h('form', { onSubmit: onSubmit }); // Should accept onToggle const onToggle = (e: h.JSX.TargetedToggleEvent) => {}; ({ newState: e.newState, oldState: e.oldState })} />; createElement('dialog', { onToggle: onToggle }); h('dialog', { onToggle: onToggle }); // Should default to correct event target element for the attribute interface h('input', { onClick: e => e.currentTarget.capture }); createElement('input', { onClick: e => e.currentTarget.capture }); const inputAttributesWithSignalType: JSX.InputHTMLAttributes = { type: createSignal('text' as const) }; createElement('input', { type: createSignal('email' as const) }); e.currentTarget.capture} />; function Checkbox({ onChange }: JSX.HTMLAttributes) { function handleChange( this: void, event: JSX.TargetedEvent ) { onChange?.call(this, event); } return ; } // `AllHTMLAttributes` should support all interfaces used within `JSX.IntrinsicElements` const allHTMLAttributes: JSX.AllHTMLAttributes = { // Global HTMLAttributes class: 'foo', // Per-element attributes autoCapitalize: 'off', dateTime: '2021-01-01', href: createSignal('https://example.com'), itemID: 'foo', maxlength: createSignal(10), playsInline: true, // ClassAttributes ref: createRef(), // DOMAttributes onClick: (e: JSX.TargetedEvent) => {}, // AriaAttributes 'aria-colcount': 1 }; preactjs-preact-389c7bc/test/ts/refs.tsx000066400000000000000000000026101523334003000203210ustar00rootroot00000000000000import { createElement, Component, createRef, FunctionalComponent, Fragment, RefObject, RefCallback } from '../../'; // Test Fixtures const Foo: FunctionalComponent = () => Foo; class Bar extends Component { render() { return Bar; } } // Using Refs class CallbackRef extends Component { divRef: RefCallback = div => { if (div !== null) { console.log(div.tagName); } }; fooRef: RefCallback = foo => { if (foo !== null) { console.log(foo.base); } }; barRef: RefCallback = bar => { if (bar !== null) { console.log(bar.base); } }; render() { return (
    ); } } class CreateRefComponent extends Component { private divRef: RefObject = createRef(); private fooRef: RefObject = createRef(); private barRef: RefObject = createRef(); componentDidMount() { if (this.divRef.current != null) { console.log(this.divRef.current.tagName); } if (this.fooRef.current != null) { console.log(this.fooRef.current.base); } if (this.barRef.current != null) { console.log(this.barRef.current.base); } } render() { return (
    ); } } preactjs-preact-389c7bc/test/ts/tsconfig.json000066400000000000000000000007121523334003000213320ustar00rootroot00000000000000{ "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "lib": ["es6", "dom"], "strict": true, "typeRoots": ["../../"], "types": [], "forceConsistentCasingInFileNames": true, "jsx": "react", "jsxFactory": "createElement", "jsxFragmentFactory": "Fragment", "paths": { "preact": ["../../"], "preact/*": ["../../*"] } }, "include": ["./**/*.ts", "./**/*.tsx"] } preactjs-preact-389c7bc/vitest.config.mjs000066400000000000000000000134711523334003000205210ustar00rootroot00000000000000import { defineConfig } from 'vitest/config'; import { transformAsync } from '@babel/core'; import { readFileSync } from 'node:fs'; import path from 'node:path'; const MINIFY = process.env.MINIFY === 'true'; const COVERAGE = process.env.COVERAGE === 'true'; const root = path.resolve(__dirname); const alias = { '^react$': path.join( root, MINIFY ? 'compat/dist/compat.mjs' : 'compat/src/index.js' ), '^react-dom$': path.join( root, MINIFY ? 'compat/dist/compat.mjs' : 'compat/src/index.js' ), '^preact$': path.join(root, MINIFY ? 'dist/preact.mjs' : 'src/index.js'), '^preact/compat$': path.join( root, MINIFY ? 'compat/dist/compat.mjs' : 'compat/src/index.js' ), '^preact/jsx-runtime$': path.join( root, MINIFY ? 'jsx-runtime/dist/jsxRuntime.mjs' : 'jsx-runtime/src/index.js' ), '^preact/jsx-runtime/src$': path.join( root, MINIFY ? 'jsx-runtime/dist/jsxRuntime.mjs' : 'jsx-runtime/src' ), '^preact/jsx-dev-runtime$': path.join( root, MINIFY ? 'jsx-dev-runtime/dist/jsx-dev-runtime.js' : 'jsx-dev-runtime/src/index.js' ), '^preact/debug$': path.join( root, MINIFY ? 'debug/dist/debug.mjs' : 'debug/src/index.js' ), '^preact/devtools$': path.join( root, MINIFY ? 'devtools/dist/devtools.js' : 'devtools/src/index.js' ), '^preact/hooks$': path.join( root, MINIFY ? 'hooks/dist/hooks.mjs' : 'hooks/src/index.js' ), '^preact/test-utils$': path.join( root, MINIFY ? 'test-utils/dist/testUtils.mjs' : 'test-utils/src/index.js' ) }; const rollupAlias = [ { find: /^react$/, replacement: MINIFY ? path.join(root, 'compat/dist/compat.mjs') : path.join(root, 'compat/src/index.js') }, { find: /^react-dom$/, replacement: MINIFY ? path.join(root, 'compat/dist/compat.mjs') : path.join(root, 'compat/src/index.js') }, { find: /^preact$/, replacement: path.join(root, 'src/index.js') }, { find: /^preact\/compat$/, replacement: MINIFY ? path.join(root, 'compat/dist/compat.mjs') : path.join(root, 'compat/src/index.js') }, { find: /^preact\/jsx-runtime$/, replacement: MINIFY ? path.join(root, 'jsx-runtime/dist/jsxRuntime.mjs') : path.join(root, 'jsx-runtime/src/index.js') }, { find: /^preact\/jsx-runtime\/src$/, replacement: MINIFY ? path.join(root, 'jsx-runtime/dist/jsxRuntime.mjs') : path.join(root, 'jsx-runtime/src') }, { find: /^preact\/jsx-dev-runtime$/, replacement: MINIFY ? path.join(root, 'jsx-runtime/dist/jsxRuntime.mjs') : path.join(root, 'jsx-runtime/src/index.js') }, { find: /^preact\/debug$/, replacement: MINIFY ? path.join(root, 'debug/dist/debug.mjs') : path.join(root, 'debug/src/index.js') }, { find: /^preact\/devtools$/, replacement: MINIFY ? path.join(root, 'devtools/dist/devtools.mjs') : path.join(root, 'devtools/src/index.js') }, { find: /^preact\/hooks$/, replacement: MINIFY ? path.join(root, 'hooks/dist/hooks.mjs') : path.join(root, 'hooks/src/index.js') }, { find: /^preact\/test-utils$/, replacement: MINIFY ? path.join(root, 'test-utils/dist/testUtils.mjs') : path.join(root, 'test-utils/src/index.js') } ]; const rename = {}; const mangle = readFileSync('./mangle.json', 'utf8'); const mangleJson = JSON.parse(mangle); for (let prop in mangleJson.props.props) { let name = prop; if (name[0] === '$') { name = name.slice(1); } rename[name] = mangleJson.props.props[prop]; } const cache = new Map(); const pending = new Map(); export default defineConfig({ resolve: { alias: rollupAlias, dedupe: ['preact'] }, esbuild: { loader: 'jsx', include: /.*\.js$/, exclude: ['node_nodules'], jsx: 'transform', jsxImportSource: 'preact', jsxDev: true }, plugins: [ { name: 'rename-mangle-properties', async transform(code, id) { if (id.includes('node_modules')) { return null; } const shouldTransform = id.includes('src') || id.includes('test'); if (!shouldTransform) { return null; } const transformed = await transformAsync(code, { filename: id, configFile: false, plugins: [ '@babel/plugin-transform-react-jsx', [ 'babel-plugin-transform-rename-properties', { rename } ] ], include: ['**/src/**/*.js', '**/test/**/*.js', '**/test/**/*.jsx'] }); return { code: transformed.code, map: transformed.map }; } } ], optimizeDeps: { include: ['preact-render-to-string', 'prop-types'], exclude: [ 'preact', 'preact/compat', 'preact/test-utils', 'preact/debug', 'preact/hooks', 'preact/devtools', 'preact/jsx-runtime', 'preact/jsx-dev-runtime', 'preact-router', 'react', 'react-dom' ], esbuildOptions: { alias } }, test: { include: [ '{debug,devtools,hooks,compat,test-utils,jsx-runtime}/test/{browser,shared}/**/*.test.{js,jsx}', './test/{browser,shared}/**/*.test.{js,jsx}' ], cache: false, coverage: { enabled: COVERAGE, include: MINIFY ? [ '**/dist/preact.mjs', '**/compat/dist/compat.mjs', '**/devtools/dist/devtools.mjs', '**/jsx-runtime/dist/jsxRuntime.mjs', '**/debug/dist/debug.mjs', '**/hooks/dist/hooks.mjs', '**/test-utils/dist/testUtils.mjs' ] : [ '**/src/**/*', '**/debug/src/**/*', '**/devtools/src/**/*', '**/hooks/src/**/*', '**/compeat/src/**/*', '**/jsx-runtime/src/**/*', '**/test-utils/src/**/*' ], extension: ['.js'], provider: 'istanbul', reporter: ['html', 'lcovonly', 'text-summary'], reportsDirectory: './coverage' }, setupFiles: ['./vitest.setup.js'], globals: true, // dangerouslyIgnoreUnhandledErrors: true, browser: { // TODO: isolate doesn't work it leaks across all pages // isolate: false, provider: 'playwright', enabled: true, screenshotFailures: false, headless: true, instances: [{ browser: 'chromium' }] } } }); preactjs-preact-389c7bc/vitest.setup.js000066400000000000000000000024701523334003000202340ustar00rootroot00000000000000import sinon from 'sinon'; import { expect, chai, describe } from 'vitest'; import sinonChai from 'sinon-chai'; chai.use(sinonChai); globalThis.context = describe; globalThis.sinon = sinon; window.addEventListener('error', () => {}); // Something that's loaded before this file polyfills Symbol object. // We need to verify that it works in IE without that. if (/Trident/.test(window.navigator.userAgent)) { window.Symbol = undefined; } // Fix Function#name on browsers that do not support it (IE). // Taken from: https://stackoverflow.com/a/17056530/755391 if (!function f() {}.name) { Object.defineProperty(Function.prototype, 'name', { get() { let name = (this.toString().match(/^function\s*([^\s(]+)/) || [])[1]; // For better performance only parse once, and then cache the // result through a new accessor for repeated access. Object.defineProperty(this, 'name', { value: name }); return name; } }); } expect.extend({ equalNode: (obj, expected) => { if (expected == null) { return { pass: obj == null, message: () => `expected node to "== null" but got ${obj} instead.` }; } else { return { pass: obj.tagName === expected.tagName && obj === expected, message: () => `expected node to have tagName ${expected.tagName} but got ${obj.tagName} instead.` }; } } });