├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── browser-tests.yml │ ├── browserstack.yml │ ├── codeql-analysis.yml │ ├── filestash.yml │ └── node.js.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .mailmap ├── .npmignore ├── .npmrc ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── build ├── command.js ├── release.js └── tasks │ ├── build.js │ ├── compare_size.js │ ├── dist.js │ ├── lib │ ├── getTimestamp.js │ └── isCleanWorkingDir.js │ ├── minify.js │ └── npmcopy.js ├── dist-module └── wrappers │ └── jquery-migrate.node-module-wrapper.js ├── dist ├── package.json └── wrappers │ └── jquery-migrate.bundler-require-wrapper.js ├── eslint.config.js ├── jtr-ci.yml ├── jtr-local.yml ├── package-lock.json ├── package.json ├── src ├── compareVersions.js ├── disablePatches.js ├── jquery │ ├── ajax.js │ ├── attributes.js │ ├── core.js │ ├── css.js │ ├── data.js │ ├── deferred.js │ ├── effects.js │ ├── event.js │ ├── manipulation.js │ └── selector.js ├── main.js ├── migrate.js ├── migratemute.js ├── utils.js ├── version.js ├── wrapper-esm.js └── wrapper.js ├── test ├── bundler_smoke_tests │ ├── lib │ │ ├── run-rollup.js │ │ ├── run-webpack.js │ │ └── utils.js │ ├── rollup-commonjs.config.js │ ├── rollup-pure-esm.config.js │ ├── run-jsdom-tests.js │ ├── src-esm-commonjs │ │ ├── jquery-migrate-require.cjs │ │ └── main.js │ ├── src-pure-esm │ │ └── main.js │ ├── test.html │ └── webpack.config.cjs ├── data │ ├── 1x1.jpg │ ├── ajax-jsonp-callback-name.html │ ├── compareVersions.js │ ├── core-jquery3.html │ ├── empty.json │ ├── event-lateload.html │ ├── event-ready.html │ ├── iframeTest.js │ ├── jquery-3.7.1.js │ ├── jsonpScript.js │ ├── null.json │ ├── qunit-start.js │ ├── simple.json │ ├── test-utils.js │ └── testinit.js ├── index.html ├── node_smoke_tests │ └── node_smoke_tests.cjs └── unit │ ├── jquery │ ├── ajax.js │ ├── attributes.js │ ├── core.js │ ├── css.js │ ├── data.js │ ├── deferred.js │ ├── effects.js │ ├── event.js │ ├── manipulation.js │ └── selector.js │ └── migrate.js └── warnings.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.{yml}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # JS files must always use LF for tools to work 5 | *.js eol=lf 6 | *.json eol=lf 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | 8 | # Group all dependabot version update PRs into one 9 | groups: 10 | github-actions: 11 | applies-to: version-updates 12 | patterns: 13 | - "*" 14 | -------------------------------------------------------------------------------- /.github/workflows/browser-tests.yml: -------------------------------------------------------------------------------- 1 | name: Browser Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | env: 10 | NODE_VERSION: 22.x 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | name: ${{ matrix.BROWSER }} (${{ matrix.MIGRATE_VERSION }} Migrate) 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | BROWSER: ["chrome", "firefox"] 20 | MIGRATE_VERSION: ["min"] 21 | include: 22 | - NAME: "Node" 23 | BROWSER: "chrome" 24 | MIGRATE_VERSION: "esmodules" 25 | - NAME: "Firefox ESR (new)" 26 | BROWSER: "firefox" 27 | MIGRATE_VERSION: "min" 28 | - NAME: "Firefox ESR (old)" 29 | BROWSER: "firefox" 30 | MIGRATE_VERSION: "min" 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 34 | 35 | - name: Use Node.js ${{ env.NODE_VERSION }} 36 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 37 | with: 38 | node-version: ${{ env.NODE_VERSION }} 39 | 40 | - name: Cache 41 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 42 | with: 43 | path: ~/.npm 44 | key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 45 | restore-keys: | 46 | ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- 47 | 48 | - name: Set download URL for Firefox ESR (old) 49 | run: | 50 | echo "FIREFOX_SOURCE_URL=https://download.mozilla.org/?product=firefox-esr-latest-ssl&lang=en-US&os=linux64" >> "$GITHUB_ENV" 51 | if: contains(matrix.NAME, 'Firefox ESR (old)') 52 | 53 | - name: Set download URL for Firefox ESR (new) 54 | run: | 55 | echo "FIREFOX_SOURCE_URL=https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&lang=en-US&os=linux64" >> "$GITHUB_ENV" 56 | if: contains(matrix.NAME, 'Firefox ESR (new)') 57 | 58 | - name: Install Firefox ESR 59 | run: | 60 | wget --no-verbose $FIREFOX_SOURCE_URL -O - | tar -jx -C ${HOME} 61 | echo "PATH=${HOME}/firefox:$PATH" >> "$GITHUB_ENV" 62 | echo "FIREFOX_BIN=${HOME}/firefox/firefox" >> "$GITHUB_ENV" 63 | if: contains(matrix.NAME, 'Firefox ESR') 64 | 65 | - name: Install dependencies 66 | run: npm install 67 | 68 | - name: Run tests 69 | run: | 70 | npm run pretest 71 | npm run build:all 72 | npm run test:unit -- -c jtr-local.yml \ 73 | --headless -b ${{ matrix.BROWSER }} -f plugin=${{ matrix.MIGRATE_VERSION }} 74 | 75 | ie: 76 | runs-on: windows-latest 77 | name: Edge in IE mode (min Migrate) 78 | env: 79 | MIGRATE_VERSION: "min" 80 | steps: 81 | - name: Checkout 82 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 83 | 84 | - name: Use Node.js ${{ env.NODE_VERSION }} 85 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 86 | with: 87 | node-version: ${{ env.NODE_VERSION }} 88 | 89 | - name: Cache 90 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 91 | with: 92 | path: ~/.npm 93 | key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 94 | restore-keys: | 95 | ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- 96 | 97 | - name: Install dependencies 98 | run: npm install 99 | 100 | - name: Run tests 101 | shell: cmd 102 | run: npm run test:ie -- -c jtr-local.yml -f plugin=${{ env.MIGRATE_VERSION }} 103 | 104 | safari: 105 | runs-on: macos-latest 106 | name: Safari (min Migrate) 107 | env: 108 | MIGRATE_VERSION: "min" 109 | steps: 110 | - name: Checkout 111 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 112 | 113 | - name: Use Node.js ${{ env.NODE_VERSION }} 114 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 115 | with: 116 | node-version: ${{ env.NODE_VERSION }} 117 | 118 | - name: Cache 119 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 120 | with: 121 | path: ~/.npm 122 | key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 123 | restore-keys: | 124 | ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- 125 | 126 | - name: Install dependencies 127 | run: npm install 128 | 129 | - name: Run tests 130 | run: npm run test:safari -- -c jtr-local.yml -f plugin=${{ env.MIGRATE_VERSION }} 131 | -------------------------------------------------------------------------------- /.github/workflows/browserstack.yml: -------------------------------------------------------------------------------- 1 | name: Browserstack 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | # Once a week every Tuesday 8 | schedule: 9 | - cron: "42 1 * * 2" 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | environment: browserstack 15 | env: 16 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} 17 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 18 | NODE_VERSION: 22.x 19 | name: ${{ matrix.BROWSER }} 20 | concurrency: 21 | group: ${{ matrix.BROWSER }} - ${{ github.sha }} 22 | timeout-minutes: 30 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | BROWSER: 27 | - 'IE_11' 28 | - 'Safari_latest' 29 | - 'Safari_latest-1' 30 | - 'Chrome_latest' 31 | - 'Chrome_latest-1' 32 | - 'Opera_latest' 33 | - 'Edge_latest' 34 | - 'Edge_latest-1' 35 | - 'Firefox_latest' 36 | - 'Firefox_latest-1' 37 | - '__iOS_18' 38 | - '__iOS_17' 39 | - '__iOS_16' 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 43 | 44 | - name: Use Node.js ${{ env.NODE_VERSION }} 45 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 46 | with: 47 | node-version: ${{ env.NODE_VERSION }} 48 | 49 | - name: Cache 50 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 51 | with: 52 | path: ~/.npm 53 | key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 54 | restore-keys: | 55 | ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- 56 | 57 | - name: Install dependencies 58 | run: npm install 59 | 60 | - name: Build jQuery 61 | run: npm run build:all 62 | 63 | - name: Pretest script 64 | run: npm run pretest 65 | 66 | - name: Test 67 | run: | 68 | npm run test:unit -- -v -c jtr-ci.yml \ 69 | --browserstack "${{ matrix.BROWSER }}" \ 70 | --run-id ${{ github.run_id }} \ 71 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Code scanning - action" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches-ignore: "dependabot/**" 7 | schedule: 8 | - cron: "0 4 * * 6" 9 | 10 | permissions: 11 | contents: read # to fetch code (actions/checkout) 12 | 13 | jobs: 14 | CodeQL-Build: 15 | permissions: 16 | contents: read # to fetch code (actions/checkout) 17 | security-events: write # (github/codeql-action/autobuild) 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 24 | with: 25 | # We must fetch at least the immediate parents so that if this is 26 | # a pull request then we can checkout the head. 27 | fetch-depth: 2 28 | 29 | # If this run was triggered by a pull request event, then checkout 30 | # the head of the pull request instead of the merge commit. 31 | - run: git checkout HEAD^2 32 | if: ${{ github.event_name == 'pull_request' }} 33 | 34 | # Initializes the CodeQL tools for scanning. 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 37 | # Override language selection by uncommenting this and choosing your languages 38 | # with: 39 | # languages: go, javascript, csharp, python, cpp, java 40 | 41 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 42 | # If this step fails, then you should remove it and run the build manually (see below) 43 | - name: Autobuild 44 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 45 | 46 | # ℹ️ Command-line programs to run using the OS shell. 47 | # 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 48 | 49 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 50 | # and modify them (or add more) to build your code if your project 51 | # uses a compiled language 52 | 53 | #- run: | 54 | # make bootstrap 55 | # make release 56 | 57 | - name: Perform CodeQL Analysis 58 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 59 | -------------------------------------------------------------------------------- /.github/workflows/filestash.yml: -------------------------------------------------------------------------------- 1 | name: Filestash 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read # to fetch code (actions/checkout) 10 | 11 | jobs: 12 | update: 13 | runs-on: ubuntu-latest 14 | environment: filestash 15 | env: 16 | NODE_VERSION: 22.x 17 | name: Update Filestash 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | 22 | - name: Use Node.js ${{ env.NODE_VERSION }} 23 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 24 | with: 25 | node-version: ${{ env.NODE_VERSION }} 26 | 27 | - name: Cache 28 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 29 | with: 30 | path: ~/.npm 31 | key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 32 | restore-keys: | 33 | ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- 34 | 35 | - name: Install dependencies 36 | run: npm install 37 | 38 | - name: Build 39 | run: npm run build 40 | 41 | - name: Set up SSH 42 | run: | 43 | install --directory ~/.ssh --mode 700 44 | base64 --decode <<< "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 45 | chmod 600 ~/.ssh/id_ed25519 46 | ssh-keyscan -t ed25519 -H "${{ secrets.FILESTASH_SERVER }}" >> ~/.ssh/known_hosts 47 | 48 | - name: Upload to Filestash 49 | run: | 50 | rsync dist/jquery-migrate.js filestash@"${{ secrets.FILESTASH_SERVER }}":jquery-migrate-git.js 51 | rsync dist/jquery-migrate.min.js filestash@"${{ secrets.FILESTASH_SERVER }}":jquery-migrate-git.min.js 52 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Node 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches-ignore: "dependabot/**" 7 | 8 | jobs: 9 | build-and-test: 10 | runs-on: ubuntu-latest 11 | name: ${{ matrix.NPM_SCRIPT }} - ${{ matrix.NAME }} (${{ matrix.NODE_VERSION }}) 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | NAME: ["Node"] 16 | NODE_VERSION: [18.x, 20.x, 22.x, 23.x] 17 | NPM_SCRIPT: ["test:browserless"] 18 | include: 19 | - NAME: "Node" 20 | NODE_VERSION: "22.x" 21 | NPM_SCRIPT: "lint" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Use Node.js ${{ env.NODE_VERSION }} 27 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 28 | with: 29 | node-version: ${{ matrix.NODE_VERSION }} 30 | 31 | - name: Cache 32 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 33 | with: 34 | path: ~/.npm 35 | key: ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} 36 | restore-keys: | 37 | ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}-npm-lock- 38 | 39 | - name: Install dependencies 40 | run: npm install 41 | 42 | - name: Run tests 43 | run: npm run ${{ matrix.NPM_SCRIPT }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | CDN 3 | .project 4 | .settings 5 | *~ 6 | *.diff 7 | *.patch 8 | /*.html 9 | .DS_Store 10 | .sizecache.json 11 | .eslintcache 12 | 13 | # Ignore built files in `dist` folder 14 | # Leave the package.json and wrappers 15 | /dist/* 16 | !/dist/package.json 17 | !/dist/wrappers 18 | 19 | # Ignore built files in `dist-module` folder 20 | # Leave the package.json and wrappers 21 | /dist-module/* 22 | !/dist-module/package.json 23 | !/dist-module/wrappers 24 | 25 | /external 26 | /node_modules 27 | 28 | # Ignore BrowserStack files 29 | local.log 30 | browserstack.err 31 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitplease .git/COMMIT_EDITMSG 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run lint 5 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Igor Kalashnikov 2 | Michał Gołębiowski-Owczarek 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | CDN 2 | coverage 3 | .project 4 | .settings 5 | *~ 6 | *.diff 7 | *.patch 8 | /*.html 9 | .DS_Store 10 | external 11 | node_modules 12 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to jQuery Migrate 2 | 3 | 1. [Getting Involved](#getting-involved) 4 | 2. [Questions and Discussion](#questions-and-discussion) 5 | 3. [How To Report Bugs](#how-to-report-bugs) 6 | 4. [Tips for Bug Patching](#tips-for-bug-patching) 7 | 8 | 9 | ## Getting Involved 10 | 11 | [API design principles](https://github.com/jquery/jquery/wiki/API-design-guidelines) 12 | 13 | We're always looking for help [identifying bugs](#how-to-report-bugs), writing and reducing test cases, and improving documentation. And although new features are rare, anything passing our [guidelines](https://github.com/jquery/jquery/wiki/Adding-new-features) will be considered. 14 | 15 | More information on how to contribute to this and other jQuery organization projects is at [contribute.jquery.org](https://contribute.jquery.org), including a short guide with tips, tricks, and ideas on [getting started with open source](https://contribute.jquery.org/open-source/). Please review our [commit & pull request guide](https://contribute.jquery.org/commits-and-pull-requests/) and [style guides](https://contribute.jquery.org/style-guide/) for instructions on how to maintain a fork and submit patches. 16 | 17 | When opening a pull request, you'll be asked to sign our Contributor License Agreement. Both the Corporate and Individual agreements can be [previewed on GitHub](https://github.com/openjs-foundation/easycla). 18 | 19 | If you're looking for some good issues to start with, [here are some issues labeled "help wanted" or "patch welcome"](https://github.com/jquery/jquery-migrate/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22%2C%22Patch+Welcome%22). 20 | 21 | ## Questions and Discussion 22 | 23 | ### Looking for help? 24 | 25 | jQuery is so popular that many developers have knowledge of its capabilities and limitations. Most questions about using jQuery can be answered on popular forums such as [Stack Overflow](https://stackoverflow.com). Please start there when you have questions, even if you think you've found a bug. 26 | 27 | The jQuery Core team watches [jQuery GitHub Discussions](https://github.com/jquery/jquery/discussions). If you have longer posts or questions that can't be answered in places such as Stack Overflow, please feel free to post them there. If you think you've found a bug, please [file it in the bug tracker](#how-to-report-bugs). The Core team can be found in the [#jquery/dev](https://matrix.to/#/#jquery_dev:gitter.im) Matrix channel on gitter.im. 28 | 29 | ### Weekly Status Meetings 30 | 31 | The jQuery Core team has a weekly meeting to discuss the progress of current work. The meeting is held in the [#jquery/meeting](hhttps://matrix.to/#/#jquery_meeting:gitter.im) Matrix channel on gitter.im at [Noon EST](https://www.timeanddate.com/worldclock/fixedtime.html?month=10&day=7&year=2024&hour=12&min=0&sec=0&p1=43) on Mondays. 32 | 33 | [jQuery Core Meeting Notes](https://meetings.jquery.org/category/core/) 34 | 35 | 36 | ## How to Report Bugs 37 | 38 | ### Make sure it is a jQuery Migrate bug 39 | 40 | Most bugs reported to our bug tracker are actually bugs in user code, not in jQuery Migrate code. Keep in mind that just because your code throws an error inside of jQuery Migrate, this does *not* mean the bug is a jQuery Migrate bug. 41 | 42 | Ask for help first on a discussion forum like [Stack Overflow](https://stackoverflow.com/). You will get much quicker support, and you will help avoid tying up the jQuery team with invalid bug reports. 43 | 44 | ### Disable browser extensions 45 | 46 | Make sure you have reproduced the bug with all browser extensions and add-ons disabled, as these can sometimes cause things to break in interesting and unpredictable ways. Try using incognito, stealth or anonymous browsing modes. 47 | 48 | ### Try the latest version of jQuery Migrate 49 | 50 | Bugs in old versions of jQuery Migrate may have already been fixed. In order to avoid reporting known issues, make sure you are always testing against the [latest build](https://releases.jquery.com/git/jquery-migrate-git.js). We cannot fix bugs in older released files, if a bug has been fixed in a subsequent version of jQuery Migrate the site should upgrade. 51 | 52 | ### Simplify the test case 53 | 54 | When experiencing a problem, [reduce your code](https://webkit.org/test-case-reduction/) to the bare minimum required to reproduce the issue. This makes it *much* easier to isolate and fix the offending code. Bugs reported without reduced test cases take on average 9001% longer to fix than bugs that are submitted with them, so you really should try to do this if at all possible. 55 | 56 | ### Search for related or duplicate issues 57 | 58 | Go to the [jQuery Migrate issue tracker](https://github.com/jquery/jquery-migrate/issues) and make sure the problem hasn't already been reported. If not, create a new issue there and include your test case. 59 | 60 | 61 | ## Tips For Bug Patching 62 | 63 | We *love* when people contribute back to the project by patching the bugs they find. Since jQuery is used by so many people, we are cautious about the patches we accept and want to be sure they don't have a negative impact on the millions of people using jQuery each day. For that reason it can take a while for any suggested patch to work its way through the review and release process. The reward for you is knowing that the problem you fixed will improve things for millions of sites and billions of visits per day. 64 | 65 | ### Build a Local Copy of jQuery Migrate 66 | 67 | Create a fork of the jQuery Migrate repo on GitHub at https://github.com/jquery/jquery-migrate 68 | 69 | Clone your jQuery fork to work locally: 70 | 71 | ```bash 72 | $ git clone git@github.com:username/jquery-migrate.git 73 | ``` 74 | 75 | Change directory to the newly created dir `jquery-migrate/`: 76 | 77 | ```bash 78 | $ cd jquery-migrate 79 | ``` 80 | 81 | Add the jQuery Migrate `main` as a remote (e.g. `upstream`): 82 | 83 | ```bash 84 | $ git remote add upstream git@github.com:jquery/jquery-migrate.git 85 | ``` 86 | 87 | Get in the habit of pulling in the "upstream" main to stay up to date as jQuery Migrate receives new commits: 88 | 89 | ```bash 90 | $ git pull upstream main 91 | ``` 92 | 93 | Install the necessary dependencies: 94 | 95 | ```bash 96 | $ npm install 97 | ``` 98 | 99 | Build all jQuery Migrate files: 100 | 101 | ```bash 102 | $ npm run build 103 | ``` 104 | 105 | Start a test server: 106 | 107 | ```bash 108 | $ npm run test:server 109 | ``` 110 | 111 | Now open the jQuery test suite in a browser at http://localhost:3000/test/. 112 | 113 | Success! You just built and tested jQuery Migrate! 114 | 115 | ### Test Suite Tips... 116 | 117 | During the process of writing your patch, you will run the test suite MANY times. You can speed up the process by narrowing the running test suite down to the module you are testing by either double-clicking the title of the test or appending it to the url. The following examples assume you're working on a local repo, hosted on your localhost server. 118 | 119 | Example: 120 | 121 | http://localhost:3000/test/?module=css 122 | 123 | This will only run the "css" module tests. This will significantly speed up your development and debugging. 124 | 125 | **ALWAYS RUN THE FULL SUITE BEFORE COMMITTING AND PUSHING A PATCH!** 126 | 127 | #### Change the test server port 128 | 129 | The default port for the test server is 3000. You can change the port by setting the `PORT` environment variable. 130 | 131 | ```bash 132 | $ PORT=3001 npm run test:server 133 | ``` 134 | 135 | #### Loading changes on the test page 136 | 137 | Rather than rebuilding jQuery Migrate with `npm run build` every time you make a change, you can use the included watch task to rebuild distribution files whenever a file is saved. 138 | 139 | ```bash 140 | $ npm start 141 | ``` 142 | 143 | Alternatively, you can **load tests as ECMAScript modules** to avoid the need for rebuilding altogether. 144 | 145 | Set the `jquery-migrate:` field to `esmodules` after loading the test page. 146 | 147 | #### Running the test suite from the command line 148 | 149 | You can also run the test suite from the command line. 150 | 151 | First, prepare the tests: 152 | 153 | ```bash 154 | $ npm run pretest 155 | ``` 156 | 157 | Make sure jQuery Migrate is built (`npm run build`) and run the tests: 158 | 159 | ```bash 160 | $ npm run test:unit 161 | ``` 162 | 163 | This will run each module in its own browser instance and report the results in the terminal. 164 | 165 | View the full help for the test suite for more info on running the tests from the command line: 166 | 167 | ```bash 168 | $ npm run test:unit -- --help 169 | ``` 170 | 171 | ### Repo organization 172 | 173 | The jQuery Migrate source is organized with ECMAScript modules and then compiled into one file at build time. 174 | 175 | ### Browser support 176 | 177 | Remember that jQuery Migrate supports multiple browsers and their versions; any contributed code must work in all of them. You can refer to the ["Version compatibility" section in Migrate README](https://github.com/jquery/jquery-migrate/blob/main/README.md#version-compatibility) for the current list of supported browsers. 178 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery-migrate 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI Status](https://github.com/jquery/jquery-migrate/actions/workflows/node.js.yml/badge.svg?branch=main) 2 | 3 | #### NOTE: To upgrade to jQuery 3.0, you first need version 1.12.x or 2.2.x. If you're using an older version, first upgrade to one of these versions using [jQuery Migrate 1.x](https://github.com/jquery/jquery-migrate/tree/1.x-stable#readme), to resolve any compatibility issues. For more information about the changes made in jQuery 3.0, see the [upgrade guide](https://jquery.com/upgrade-guide/3.0/) and [blog post](https://blog.jquery.com/2016/06/09/jquery-3-0-final-released/). 4 | 5 | # jQuery Migrate 6 | Upgrading libraries such as jQuery can be a lot of work, when breaking changes have been introduced. jQuery Migrate makes this easier, by restoring the APIs that were removed, and additionally shows warnings in the browser console (development version of jQuery Migrate only) when removed and/or deprecated APIs are used. 7 | 8 | That way you can spot and fix what otherwise would have been errors, until you no longer need jQuery Migrate and can remove it. 9 | 10 | ## Version compatibility 11 | 12 | The following table indicates which jQuery Migrate versions can be used with which jQuery versions: 13 | 14 | | jQuery version | jQuery Migrate version | 15 | |----------------|------------------------| 16 | | 1.x | 1.x | 17 | | 2.x | 1.x | 18 | | 3.x | 3.x | 19 | | 4.x | 4.x | 20 | 21 | Each jQuery Migrate version supports the same browsers that the jQuery version used with it. See the [jQuery Browser Support page](https://jquery.com/browser-support/) for more information. 22 | 23 | ## Usage 24 | 25 | In your web page, load this plugin *after* the script tag for jQuery, for example: 26 | 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | ## Download 33 | 34 | ### Development vs. Production versions 35 | 36 | The production build is minified and does not generate console warnings. It will only generate a console log message upon loading, or if it detects an error such as an outdated version of jQuery that it does not support. Do not use this file for development or debugging, it will make your life miserable. 37 | 38 | | | Development | Production | 39 | |--|-------------|------------| 40 | | Debugging enabled |

| | 41 | | Minified | |

| 42 | | Latest release (*may be hotlinked if desired*) | [jquery-migrate-3.5.2.js](https://code.jquery.com/jquery-migrate-3.5.2.js) | [jquery-migrate-3.5.2.min.js](https://code.jquery.com/jquery-migrate-3.5.2.min.js) | 43 | | \* Latest work-in-progress build | [jquery-migrate-git.js](https://releases.jquery.com/git/jquery-migrate-git.js) | [jquery-migrate-git.min.js](https://releases.jquery.com/git/jquery-migrate-git.min.js) | 44 | 45 | 46 | \* **Work-in-progress build:** Although this file represents the most recent updates to the plugin, it may not have been thoroughly tested. We do not recommend using this file on production sites since it may be unstable; use the released production version instead. 47 | 48 | 49 | ## Debugging 50 | 51 | The development version of the plugin displays warnings in the browser console. Developers can also inspect the `jQuery.migrateMessages` array to see what error messages have been generated. 52 | 53 | All warnings generated by this plugin start with the string "JQMIGRATE". A list of the warnings you may see are in [warnings.md](https://github.com/jquery/jquery-migrate/blob/main/warnings.md). 54 | 55 | 56 | ## Migrate Plugin API 57 | 58 | This plugin adds some properties to the `jQuery` object that can be used to programmatically control and examine its behavior: 59 | 60 | `jQuery.migrateMessages`: This property is an array of string warning messages that have been generated by the code on the page, in the order they were generated. Messages appear in the array only once, even if the condition has occurred multiple times, unless `jQuery.migrateReset()` is called. 61 | 62 | `jQuery.migrateMute`: Set this property to `true` to prevent console warnings from being generated in the development version. The `jQuery.migrateMessages` array is still maintained when this property is set, which allows programmatic inspection without console output. 63 | 64 | `jQuery.migrateTrace`: Set this property to `false` if you want warnings but do not want stack traces to appear on the console. 65 | 66 | `jQuery.migrateReset()`: This method clears the `jQuery.migrateMessages` array and "forgets" the list of messages that have been seen already. 67 | 68 | `jQuery.migrateVersion`: This string property indicates the version of Migrate in use. 69 | 70 | `jQuery.migrateDeduplicateMessages`: By default, Migrate only gives a specific warning once. If you set this property to `false` it will give a warning for every occurrence each time it happens. Note that this can generate a lot of output, for example when a warning occurs in a loop. 71 | 72 | `jQuery.migrateDisablePatches`: Disables patches by their codes. You can find a code for each patch in square brackets in [warnings.md](https://github.com/jquery/jquery-migrate/blob/main/warnings.md). A limited number of warnings doesn't have codes defined and cannot be disabled. These are mostly setup issues like using an incorrect version of jQuery or loading Migrate multiple times. 73 | 74 | `jQuery.migrateDisablePatches`: Disables patches by their codes. 75 | 76 | `jQuery.migrateIsPatchEnabled`: Returns `true` if a patch of a provided code is enabled and `false` otherwise. 77 | 78 | `jQuery.UNSAFE_restoreLegacyHtmlPrefilter`: A deprecated alias of `jQuery.migrateEnablePatches( "self-closed-tags" )` 79 | 80 | ## Reporting problems 81 | 82 | Bugs that only occur when the jQuery Migrate plugin is used should be reported in the [jQuery Migrate Issue Tracker](https://github.com/jquery/jquery-migrate/issues) and should be accompanied by an executable test case that demonstrates the bug. The easiest way to do this is via an online test tool such as [jsFiddle.net](https://jsFiddle.net/) or [jsbin.com](https://jsbin.com). Use the development version when you are reporting bugs. 83 | 84 | Bugs in jQuery itself should be reported on the [jQuery Core bug tracker](https://bugs.jquery.com/) and again should be accompanied by a test case from [jsFiddle.net](https://jsFiddle.net/) or [jsbin.com](http://jsbin.com) so that we can reproduce the issue. 85 | 86 | For other questions about the plugin that aren't bugs, ask on the [jQuery Forum](http://forum.jquery.com). 87 | 88 | Build and run tests: 89 | ==================================================== 90 | 91 | ## Build with `npm` commands 92 | 93 | ```sh 94 | $ git clone git://github.com/jquery/jquery-migrate.git 95 | $ cd jquery-migrate 96 | $ npm install 97 | $ npm run build 98 | ``` 99 | 100 | ### Run tests 101 | 102 | ```sh 103 | $ npm test 104 | ``` 105 | 106 | ### Or 107 | 108 | ```sh 109 | $ npm run test:server 110 | ``` 111 | 112 | and open http://localhost:3000/test/ in your browser. 113 | -------------------------------------------------------------------------------- /build/command.js: -------------------------------------------------------------------------------- 1 | import yargs from "yargs/yargs"; 2 | import { build } from "./tasks/build.js"; 3 | 4 | const argv = yargs( process.argv.slice( 2 ) ) 5 | .version( false ) 6 | .command( { 7 | command: "[options]", 8 | describe: "Build a jQuery Migrate bundle" 9 | } ) 10 | .option( "filename", { 11 | alias: "f", 12 | type: "string", 13 | description: 14 | "Set the filename of the built file. Defaults to jquery.js." 15 | } ) 16 | .option( "dir", { 17 | alias: "d", 18 | type: "string", 19 | description: 20 | "Set the dir to which to output the built file. Defaults to /dist." 21 | } ) 22 | .option( "version", { 23 | alias: "v", 24 | type: "string", 25 | description: 26 | "Set the version to include in the built file. " + 27 | "Defaults to the version in package.json plus the " + 28 | "short commit SHA and any excluded modules." 29 | } ) 30 | .option( "watch", { 31 | alias: "w", 32 | type: "boolean", 33 | description: 34 | "Watch the source files and rebuild when they change." 35 | } ) 36 | .option( "esm", { 37 | type: "boolean", 38 | description: 39 | "Build an ES module (ESM) bundle. " + 40 | "By default, a UMD bundle is built." 41 | } ) 42 | .help() 43 | .argv; 44 | 45 | build( argv ); 46 | -------------------------------------------------------------------------------- /build/release.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * JQuery Migrate Plugin Release Management 4 | */ 5 | 6 | // Debugging variables 7 | var dryrun = false, 8 | skipRemote = false; 9 | 10 | import fs from "node:fs"; 11 | import child from "node:child_process"; 12 | import path from "node:path"; 13 | import chalk from "chalk"; 14 | import enquirer from "enquirer"; 15 | import { build } from "./tasks/build.js"; 16 | 17 | var releaseVersion, 18 | nextVersion, 19 | isBeta, 20 | pkg, 21 | 22 | prompt = enquirer.prompt, 23 | 24 | repoURL = "git@github.com:jquery/jquery-migrate.git", 25 | branch = "main", 26 | 27 | // Windows needs the .cmd version but will find the non-.cmd 28 | // On Windows, also ensure the HOME environment variable is set 29 | npmCmd = process.platform === "win32" ? "npm.cmd" : "npm", 30 | 31 | readmeFile = "README.md", 32 | packageFile = "package.json", 33 | versionFile = path.join( "src", "version.js" ), 34 | 35 | releaseDir = "CDN", 36 | distDir = "dist"; 37 | 38 | steps( 39 | initialize, 40 | checkGitStatus, 41 | buildRelease, 42 | updateVersions, 43 | tagReleaseVersion, 44 | buildRelease, 45 | makeReleaseCopies, 46 | publishToNPM, 47 | setNextVersion, 48 | pushToRemote, 49 | remindAboutCDN, 50 | remindAboutSites, 51 | exit 52 | ); 53 | 54 | function initialize( next ) { 55 | 56 | // -d dryrun mode, no commands are executed at all 57 | if ( process.argv[ 2 ] === "-d" ) { 58 | process.argv.shift(); 59 | dryrun = true; 60 | console.warn( "=== DRY RUN MODE ===" ); 61 | } 62 | 63 | // -r skip remote mode, no remote commands are executed 64 | // (git push, npm publish, cdn copy) 65 | // Reset with `git reset --hard HEAD~2 && git tag -d (version) && npm run build` 66 | if ( process.argv[ 2 ] === "-r" ) { 67 | process.argv.shift(); 68 | skipRemote = true; 69 | console.warn( "=== SKIPREMOTE MODE ===" ); 70 | } 71 | 72 | // First arg should be the version number being released; this is a proper subset 73 | // of a full semver, see https://github.com/mojombo/semver/issues/32 74 | // Examples: 1.0.1, 1.0.1-pre, 1.0.1-rc1, 1.0.1-rc1.1 75 | var newver, oldver, 76 | rsemver = /^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?$/, 77 | version = rsemver.exec( process.argv[ 2 ] || "" ) || [], 78 | major = version[ 1 ], 79 | minor = version[ 2 ], 80 | patch = version[ 3 ], 81 | xbeta = version[ 4 ]; 82 | 83 | releaseVersion = process.argv[ 2 ]; 84 | isBeta = !!xbeta; 85 | 86 | if ( !releaseVersion ) { 87 | log( "Usage: release [ -d -r ] releaseVersion" ); 88 | log( " -d Dry-run; no commands are executed at all" ); 89 | log( " -r Skip-remote; nothing is pushed externally" ); 90 | die( "Invalid args" ); 91 | } 92 | if ( !version.length ) { 93 | die( "'" + releaseVersion + "' is not a valid semver!" ); 94 | } 95 | if ( xbeta === "pre" ) { 96 | die( "Cannot release a 'pre' version!" ); 97 | } 98 | if ( !( fs.existsSync || path.existsSync )( packageFile ) ) { 99 | die( "No " + packageFile + " in this directory" ); 100 | } 101 | pkg = JSON.parse( fs.readFileSync( packageFile, "utf8" ) ); 102 | 103 | status( "Current version is " + pkg.version + "; generating release " + releaseVersion ); 104 | version = rsemver.exec( pkg.version ); 105 | oldver = ( +version[ 1 ] ) * 10000 + ( +version[ 2 ] * 100 ) + ( +version[ 3 ] ); 106 | newver = ( +major ) * 10000 + ( +minor * 100 ) + ( +patch ); 107 | if ( newver < oldver ) { 108 | die( "Next version is older than current version!" ); 109 | } 110 | 111 | nextVersion = major + "." + minor + "." + ( isBeta ? patch : +patch + 1 ) + "-pre"; 112 | next(); 113 | } 114 | 115 | //TODO: Check that remote doesn't have newer commits: 116 | // git fetch repoURL 117 | // git remote show repoURL 118 | // (look for " BRANCH pushes to BRANCH (up to date)") 119 | 120 | function checkGitStatus( next ) { 121 | child.execFile( "git", [ "status" ], function( error, stdout ) { 122 | if ( error ) { 123 | throw error; 124 | } 125 | var onBranch = ( ( stdout || "" ).match( /On branch (\S+)/ ) || [] )[ 1 ]; 126 | if ( onBranch !== branch ) { 127 | die( "Branches don't match: Wanted " + branch + ", got " + onBranch ); 128 | } 129 | if ( /Changes to be committed/i.test( stdout ) ) { 130 | die( "Please commit changed files before attemping to push a release." ); 131 | } 132 | if ( /Changes not staged for commit/i.test( stdout ) ) { 133 | die( "Please stash files before attempting to push a release." ); 134 | } 135 | next(); 136 | } ); 137 | } 138 | 139 | function tagReleaseVersion( next ) { 140 | git( [ "commit", "-a", "--no-verify", "-m", "Tagging the " + releaseVersion + " release." ], 141 | function() { 142 | git( [ "tag", releaseVersion ], next ); 143 | } 144 | ); 145 | } 146 | 147 | function updateVersions( next ) { 148 | updateSourceVersion( releaseVersion ); 149 | updateReadmeVersion( releaseVersion ); 150 | updatePackageVersion( releaseVersion ); 151 | next(); 152 | } 153 | 154 | async function buildRelease( next ) { 155 | await build( { version: releaseVersion } ); 156 | next(); 157 | } 158 | 159 | function makeReleaseCopies( next ) { 160 | if ( !fs.existsSync( releaseDir ) ) { 161 | fs.mkdirSync( releaseDir ); 162 | } 163 | var passThrough = function( t ) { 164 | return t; 165 | }; 166 | var releaseFiles = { 167 | "jquery-migrate-VER.js": passThrough, 168 | "jquery-migrate-VER.min.js": fixMinRef, 169 | "jquery-migrate-VER.min.map": fixMapRef 170 | }; 171 | Object.keys( releaseFiles ).forEach( function( key ) { 172 | var distFile = key.replace( /-VER/g, "" ), 173 | distPath = path.join( distDir, distFile ), 174 | releaseFile = key.replace( /VER/g, releaseVersion ), 175 | releasePath = path.join( releaseDir, releaseFile ); 176 | 177 | // Remove Windows CRLF if it's there, on *nix this is a no-op 178 | log( "Processing " + distPath + " => " + releasePath ); 179 | var distText = fs.readFileSync( distPath, "utf8" ).replace( /\r\n/g, "\n" ); 180 | var releaseText = releaseFiles[ key ]( distText, releaseFile ); 181 | if ( !dryrun ) { 182 | fs.writeFileSync( releasePath, releaseText ); 183 | } 184 | } ); 185 | next(); 186 | } 187 | 188 | async function publishToNPM( next ) { 189 | const { input: otp } = await prompt( { 190 | type: "input", 191 | name: "input", 192 | message: "Enter one-time password if you have 2FA enabled and press Enter.\n" + 193 | "Otherwise, just press Enter." 194 | } ); 195 | 196 | // Don't update "latest" if this is a beta 197 | if ( isBeta ) { 198 | exec( npmCmd, [ 199 | "publish", 200 | "--tag", 201 | "beta", 202 | ...( otp ? [ "--otp", otp ] : [] ) 203 | ], next, skipRemote ); 204 | } else { 205 | exec( npmCmd, [ 206 | "publish", 207 | ...( otp ? [ "--otp", otp ] : [] ) 208 | ], next, skipRemote ); 209 | } 210 | } 211 | 212 | function setNextVersion( next ) { 213 | updateSourceVersion( nextVersion ); 214 | updatePackageVersion( nextVersion, "main" ); 215 | git( [ "commit", "-a", "--no-verify", "-m", "Updating the source version to " + nextVersion ], 216 | next ); 217 | } 218 | 219 | function pushToRemote( next ) { 220 | git( [ "push", "--tags", repoURL, branch ], next, skipRemote ); 221 | } 222 | 223 | function remindAboutCDN( next ) { 224 | console.log( chalk.red( "TODO: Update CDN with jquery-migrate." + 225 | releaseVersion + " files (min and regular)" ) ); 226 | console.log( chalk.red( " clone codeorigin.jquery.com, git add files, commit, push" ) ); 227 | next(); 228 | } 229 | 230 | function remindAboutSites( next ) { 231 | console.log( chalk.red( "TODO: Update jquery.com download page to " + releaseVersion ) ); 232 | next(); 233 | } 234 | 235 | //============================== 236 | 237 | function steps() { 238 | var cur = 0, 239 | steps = arguments; 240 | ( function next() { 241 | process.nextTick( function() { 242 | steps[ cur++ ]( next ); 243 | } ); 244 | } )(); 245 | } 246 | 247 | function updatePackageVersion( ver, blobVer ) { 248 | status( "Updating " + packageFile + " version to " + ver ); 249 | blobVer = blobVer || ver; 250 | pkg.version = ver; 251 | pkg.author.url = setBlobVersion( pkg.author.url, blobVer ); 252 | writeJsonSync( packageFile, pkg ); 253 | } 254 | 255 | function updateSourceVersion( ver ) { 256 | var stmt = "\njQuery.migrateVersion = \"" + ver + "\";\n"; 257 | 258 | status( "Updating " + stmt.replace( /\n/g, "" ) ); 259 | if ( !dryrun ) { 260 | fs.writeFileSync( versionFile, stmt ); 261 | } 262 | } 263 | 264 | function updateReadmeVersion() { 265 | var readme = fs.readFileSync( readmeFile, "utf8" ); 266 | 267 | // Change version references from the old version to the new one. 268 | // The regex can update beta versions in case it was changed manually. 269 | if ( isBeta ) { 270 | status( "Skipping " + readmeFile + " update (beta release)" ); 271 | } else { 272 | status( "Updating " + readmeFile ); 273 | readme = readme.replace( 274 | /jquery-migrate-\d+\.\d+\.\d+(?:-\w+)?/g, 275 | "jquery-migrate-" + releaseVersion 276 | ); 277 | if ( !dryrun ) { 278 | fs.writeFileSync( readmeFile, readme ); 279 | } 280 | } 281 | } 282 | 283 | function setBlobVersion( s, v ) { 284 | return s.replace( /\/blob\/(?:(\d+\.\d+[^\/]+)|main)/, "/blob/" + v ); 285 | } 286 | 287 | function writeJsonSync( fname, json ) { 288 | if ( dryrun ) { 289 | console.log( JSON.stringify( json, null, " " ) ); 290 | } else { 291 | fs.writeFileSync( fname, JSON.stringify( json, null, "\t" ) + "\n" ); 292 | } 293 | } 294 | 295 | function fixMinRef( oldText ) { 296 | var mapRef = new RegExp( "^//# sourceMappingURL=jquery-migrate.min.map\\n?", "m" ); 297 | 298 | // Remove the ref for now rather than try to fix it 299 | var newText = oldText.replace( mapRef, "" ); 300 | if ( oldText === newText ) { 301 | throw Error( "fixMinRef: Unable to find the sourceMappingURL" ); 302 | } 303 | return newText; 304 | } 305 | 306 | function fixMapRef( oldText, newFile ) { 307 | var mapJSON = JSON.parse( oldText ); 308 | var sources = mapJSON.sources; 309 | if ( sources.join() !== "../src/migratemute.js,jquery-migrate.js" ) { 310 | throw Error( "fixMapRef: Unexpected sources entry: " + sources ); 311 | } 312 | 313 | // This file isn't published, not sure the best way to deal with that 314 | sources[ 0 ] = "migratemute.js"; 315 | sources[ 1 ] = newFile.replace( /\.map$/, ".js" ); 316 | return JSON.stringify( mapJSON ); 317 | } 318 | 319 | function git( args, fn, skip ) { 320 | exec( "git", args, fn, skip ); 321 | } 322 | 323 | function exec( cmd, args, fn, skip ) { 324 | if ( dryrun || skip ) { 325 | log( chalk.black.bgBlue( "# " + cmd + " " + args.join( " " ) ) ); 326 | fn(); 327 | } else { 328 | log( chalk.green( cmd + " " + args.join( " " ) ) ); 329 | child.execFile( cmd, args, { env: process.env }, 330 | function( err, stdout, stderr ) { 331 | if ( err ) { 332 | die( stderr || stdout || err ); 333 | } 334 | fn(); 335 | } 336 | ); 337 | } 338 | } 339 | 340 | function status( msg ) { 341 | console.log( chalk.black.bgGreen( msg ) ); 342 | } 343 | 344 | function log( msg ) { 345 | console.log( msg ); 346 | } 347 | 348 | function die( msg ) { 349 | console.error( chalk.red( "ERROR: " + msg ) ); 350 | process.exit( 1 ); 351 | } 352 | 353 | function exit() { 354 | process.exit( 0 ); 355 | } 356 | -------------------------------------------------------------------------------- /build/tasks/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A build task that compiles jQuery Migrate JS modules into one bundle. 3 | */ 4 | 5 | import path from "node:path"; 6 | import { mkdir, readFile, writeFile } from "node:fs/promises"; 7 | import * as rollup from "rollup"; 8 | import { minify } from "./minify.js"; 9 | import { getTimestamp } from "./lib/getTimestamp.js"; 10 | import { compareSize } from "./compare_size.js"; 11 | import util from "node:util"; 12 | import { exec as nodeExec } from "node:child_process"; 13 | import { isCleanWorkingDir } from "./lib/isCleanWorkingDir.js"; 14 | 15 | const exec = util.promisify( nodeExec ); 16 | 17 | function read( filename ) { 18 | return readFile( filename, "utf8" ); 19 | } 20 | 21 | async function readJSON( filename ) { 22 | return JSON.parse( await read( filename ) ); 23 | } 24 | 25 | async function getOutputRollupOptions( { 26 | esm = false 27 | } = {} ) { 28 | const wrapperFilePath = path.join( "src", `wrapper${ 29 | esm ? "-esm" : "" 30 | }.js` ); 31 | 32 | const wrapperSource = await read( wrapperFilePath ); 33 | 34 | // Catch `// @CODE` and subsequent comment lines event if they don't start 35 | // in the first column. 36 | const wrapper = wrapperSource.split( 37 | /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ 38 | ); 39 | 40 | return { 41 | 42 | // The ESM format is not actually used as we strip it during the 43 | // build, inserting our own wrappers; it's just that it doesn't 44 | // generate any extra wrappers so there's nothing for us to remove. 45 | format: "esm", 46 | 47 | intro: wrapper[ 0 ].replace( /\n*$/, "" ), 48 | outro: wrapper[ 1 ].replace( /^\n*/, "" ) 49 | }; 50 | } 51 | 52 | async function writeCompiled( { code, dir, filename, version } ) { 53 | const compiledContents = code 54 | 55 | // Embed Version 56 | .replace( /@VERSION/g, version ) 57 | 58 | // Embed Date 59 | // yyyy-mm-ddThh:mmZ 60 | .replace( /@DATE/g, new Date().toISOString().replace( /:\d+\.\d+Z$/, "Z" ) ); 61 | 62 | await writeFile( path.join( dir, filename ), compiledContents ); 63 | console.log( `[${ getTimestamp() }] ${ filename } v${ version } created.` ); 64 | } 65 | 66 | export async function build( { 67 | dir = "dist", 68 | filename = "jquery-migrate.js", 69 | esm = false, 70 | watch = false, 71 | version 72 | } = {} ) { 73 | 74 | const pkg = await readJSON( "package.json" ); 75 | 76 | // Add the short commit hash to the version string 77 | // when the version is not for a release. 78 | if ( !version ) { 79 | const { stdout } = await exec( "git rev-parse --short HEAD" ); 80 | const isClean = await isCleanWorkingDir(); 81 | 82 | // "+SHA" is semantically correct 83 | // Add ".dirty" as well if the working dir is not clean 84 | version = `${ pkg.version }+${ stdout.trim() }${ 85 | isClean ? "" : ".dirty" 86 | }`; 87 | } 88 | 89 | const inputRollupOptions = {}; 90 | const outputRollupOptions = await getOutputRollupOptions( { esm } ); 91 | const src = "src/migrate.js"; 92 | 93 | inputRollupOptions.input = path.resolve( src ); 94 | 95 | await mkdir( dir, { recursive: true } ); 96 | 97 | if ( watch ) { 98 | const watcher = rollup.watch( { 99 | ...inputRollupOptions, 100 | output: [ outputRollupOptions ], 101 | watch: { 102 | include: "src/**", 103 | skipWrite: true 104 | } 105 | } ); 106 | 107 | watcher.on( "event", async( event ) => { 108 | switch ( event.code ) { 109 | case "ERROR": 110 | console.error( event.error ); 111 | break; 112 | case "BUNDLE_END": 113 | const { 114 | output: [ { code } ] 115 | } = await event.result.generate( outputRollupOptions ); 116 | 117 | await writeCompiled( { 118 | code, 119 | dir, 120 | filename, 121 | version 122 | } ); 123 | break; 124 | } 125 | } ); 126 | 127 | return watcher; 128 | } else { 129 | const bundle = await rollup.rollup( inputRollupOptions ); 130 | 131 | const { 132 | output: [ { code } ] 133 | } = await bundle.generate( outputRollupOptions ); 134 | 135 | await writeCompiled( { code, dir, filename, version } ); 136 | await minify( { dir, filename, version } ); 137 | } 138 | } 139 | 140 | export async function buildDefaultFiles( { 141 | version = process.env.VERSION, 142 | watch 143 | } = {} ) { 144 | await Promise.all( [ 145 | build( { version, watch } ), 146 | build( { 147 | dir: "dist-module", 148 | filename: "jquery-migrate.module.js", 149 | esm: true, 150 | version, 151 | watch 152 | } ) 153 | ] ); 154 | 155 | if ( watch ) { 156 | console.log( "Watching files..." ); 157 | } else { 158 | return compareSize( { 159 | files: [ 160 | "dist/jquery-migrate.min.js", 161 | "dist-module/jquery-migrate.module.min.js" 162 | ] 163 | } ); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /build/tasks/compare_size.js: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import fs from "node:fs/promises"; 3 | import { promisify } from "node:util"; 4 | import zlib from "node:zlib"; 5 | import { exec as nodeExec } from "node:child_process"; 6 | import { isCleanWorkingDir } from "./lib/isCleanWorkingDir.js"; 7 | 8 | const VERSION = 1; 9 | const lastRunBranch = " last run"; 10 | 11 | const gzip = promisify( zlib.gzip ); 12 | const exec = promisify( nodeExec ); 13 | 14 | async function getBranchName() { 15 | const { stdout } = await exec( "git rev-parse --abbrev-ref HEAD" ); 16 | return stdout.trim(); 17 | } 18 | 19 | async function getCommitHash() { 20 | const { stdout } = await exec( "git rev-parse HEAD" ); 21 | return stdout.trim(); 22 | } 23 | 24 | function getBranchHeader( branch, commit ) { 25 | let branchHeader = branch.trim(); 26 | if ( commit ) { 27 | branchHeader = chalk.bold( branchHeader ) + chalk.gray( ` @${ commit }` ); 28 | } else { 29 | branchHeader = chalk.italic( branchHeader ); 30 | } 31 | return branchHeader; 32 | } 33 | 34 | async function getCache( loc ) { 35 | let cache; 36 | try { 37 | const contents = await fs.readFile( loc, "utf8" ); 38 | cache = JSON.parse( contents ); 39 | } catch ( err ) { 40 | return {}; 41 | } 42 | 43 | const lastRun = cache[ lastRunBranch ]; 44 | if ( !lastRun || !lastRun.meta || lastRun.meta.version !== VERSION ) { 45 | console.log( "Compare cache version mismatch. Rewriting..." ); 46 | return {}; 47 | } 48 | return cache; 49 | } 50 | 51 | function cacheResults( results ) { 52 | const files = Object.create( null ); 53 | results.forEach( function( result ) { 54 | files[ result.filename ] = { 55 | raw: result.raw, 56 | gz: result.gz 57 | }; 58 | } ); 59 | return files; 60 | } 61 | 62 | function saveCache( loc, cache ) { 63 | 64 | // Keep cache readable for manual edits 65 | return fs.writeFile( loc, JSON.stringify( cache, null, " " ) + "\n" ); 66 | } 67 | 68 | function compareSizes( existing, current, padLength ) { 69 | if ( typeof current !== "number" ) { 70 | return chalk.grey( `${ existing }`.padStart( padLength ) ); 71 | } 72 | const delta = current - existing; 73 | if ( delta > 0 ) { 74 | return chalk.red( `+${ delta }`.padStart( padLength ) ); 75 | } 76 | return chalk.green( `${ delta }`.padStart( padLength ) ); 77 | } 78 | 79 | function sortBranches( a, b ) { 80 | if ( a === lastRunBranch ) { 81 | return 1; 82 | } 83 | if ( b === lastRunBranch ) { 84 | return -1; 85 | } 86 | if ( a < b ) { 87 | return -1; 88 | } 89 | if ( a > b ) { 90 | return 1; 91 | } 92 | return 0; 93 | } 94 | 95 | export async function compareSize( { cache = ".sizecache.json", files } = {} ) { 96 | if ( !files || !files.length ) { 97 | throw new Error( "No files specified" ); 98 | } 99 | 100 | const branch = await getBranchName(); 101 | const commit = await getCommitHash(); 102 | const sizeCache = await getCache( cache ); 103 | 104 | let rawPadLength = 0; 105 | let gzPadLength = 0; 106 | const results = await Promise.all( 107 | files.map( async function( filename ) { 108 | 109 | let contents = await fs.readFile( filename, "utf8" ); 110 | 111 | // Remove the short SHA and .dirty from comparisons. 112 | // The short SHA so commits can be compared against each other 113 | // and .dirty to compare with the existing branch during development. 114 | const sha = /jQuery Migrate v\d+.\d+.\d+(?:-\w+)?(?:\+slim\.|\+)?([^ \.]+(?:\.dirty)?)?/.exec( contents )[ 1 ]; 115 | contents = contents.replace( new RegExp( sha, "g" ), "" ); 116 | 117 | const size = Buffer.byteLength( contents, "utf8" ); 118 | const gzippedSize = ( await gzip( contents ) ).length; 119 | 120 | // Add one to give space for the `+` or `-` in the comparison 121 | rawPadLength = Math.max( rawPadLength, size.toString().length + 1 ); 122 | gzPadLength = Math.max( gzPadLength, gzippedSize.toString().length + 1 ); 123 | 124 | return { filename, raw: size, gz: gzippedSize }; 125 | } ) 126 | ); 127 | 128 | const sizeHeader = "raw".padStart( rawPadLength ) + 129 | "gz".padStart( gzPadLength + 1 ) + 130 | " Filename"; 131 | 132 | const sizes = results.map( function( result ) { 133 | const rawSize = result.raw.toString().padStart( rawPadLength ); 134 | const gzSize = result.gz.toString().padStart( gzPadLength ); 135 | return `${ rawSize } ${ gzSize } ${ result.filename }`; 136 | } ); 137 | 138 | const comparisons = Object.keys( sizeCache ).sort( sortBranches ).map( function( branch ) { 139 | const meta = sizeCache[ branch ].meta || {}; 140 | const commit = meta.commit; 141 | 142 | const files = sizeCache[ branch ].files; 143 | const branchSizes = Object.keys( files ).map( function( filename ) { 144 | const branchResult = files[ filename ]; 145 | const compareResult = results.find( function( result ) { 146 | return result.filename === filename; 147 | } ) || {}; 148 | 149 | const compareRaw = compareSizes( branchResult.raw, compareResult.raw, rawPadLength ); 150 | const compareGz = compareSizes( branchResult.gz, compareResult.gz, gzPadLength ); 151 | return `${ compareRaw } ${ compareGz } ${ filename }`; 152 | } ); 153 | 154 | return [ 155 | "", // New line before each branch 156 | getBranchHeader( branch, commit ), 157 | sizeHeader, 158 | ...branchSizes 159 | ].join( "\n" ); 160 | } ); 161 | 162 | const output = [ 163 | "", // Opening new line 164 | chalk.bold( "Sizes" ), 165 | sizeHeader, 166 | ...sizes, 167 | ...comparisons, 168 | "" // Closing new line 169 | ].join( "\n" ); 170 | 171 | console.log( output ); 172 | 173 | // Always save the last run 174 | // Save version under last run 175 | sizeCache[ lastRunBranch ] = { 176 | meta: { version: VERSION }, 177 | files: cacheResults( results ) 178 | }; 179 | 180 | // Only save cache for the current branch 181 | // if the working directory is clean. 182 | if ( await isCleanWorkingDir() ) { 183 | sizeCache[ branch ] = { 184 | meta: { commit }, 185 | files: cacheResults( results ) 186 | }; 187 | console.log( `Saved cache for ${ branch }.` ); 188 | } 189 | 190 | await saveCache( cache, sizeCache ); 191 | 192 | return results; 193 | } 194 | -------------------------------------------------------------------------------- /build/tasks/dist.js: -------------------------------------------------------------------------------- 1 | 2 | // Process files for distribution. 3 | export function processForDist( text, filename ) { 4 | if ( !text ) { 5 | throw new Error( "text required for processForDist" ); 6 | } 7 | 8 | if ( !filename ) { 9 | throw new Error( "filename required for processForDist" ); 10 | } 11 | 12 | // Ensure files use only \n for line endings, not \r\n 13 | if ( /\x0d\x0a/.test( text ) ) { 14 | throw new Error( filename + ": Incorrect line endings (\\r\\n)" ); 15 | } 16 | 17 | // Ensure only ASCII chars so script tags don't need a charset attribute 18 | if ( text.length !== Buffer.byteLength( text, "utf8" ) ) { 19 | let message = filename + ": Non-ASCII characters detected:\n"; 20 | for ( let i = 0; i < text.length; i++ ) { 21 | const c = text.charCodeAt( i ); 22 | if ( c > 127 ) { 23 | message += "- position " + i + ": " + c + "\n"; 24 | message += "==> " + text.substring( i - 20, i + 20 ); 25 | break; 26 | } 27 | } 28 | throw new Error( message ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /build/tasks/lib/getTimestamp.js: -------------------------------------------------------------------------------- 1 | export function getTimestamp() { 2 | const now = new Date(); 3 | const hours = now.getHours().toString().padStart( 2, "0" ); 4 | const minutes = now.getMinutes().toString().padStart( 2, "0" ); 5 | const seconds = now.getSeconds().toString().padStart( 2, "0" ); 6 | return `${ hours }:${ minutes }:${ seconds }`; 7 | } 8 | -------------------------------------------------------------------------------- /build/tasks/lib/isCleanWorkingDir.js: -------------------------------------------------------------------------------- 1 | import util from "node:util"; 2 | import { exec as nodeExec } from "node:child_process"; 3 | const exec = util.promisify( nodeExec ); 4 | 5 | export async function isCleanWorkingDir() { 6 | const { stdout } = await exec( "git status --untracked-files=no --porcelain" ); 7 | return !stdout.trim(); 8 | } 9 | -------------------------------------------------------------------------------- /build/tasks/minify.js: -------------------------------------------------------------------------------- 1 | import UglifyJS from "uglify-js"; 2 | import path from "node:path"; 3 | import { getTimestamp } from "./lib/getTimestamp.js"; 4 | import { readFile, writeFile } from "node:fs/promises"; 5 | import { processForDist } from "./dist.js"; 6 | 7 | const rjs = /\.js$/; 8 | 9 | export async function minify( { dir, filename, version } ) { 10 | 11 | // Prepend migratemute.js to the minified file 12 | const muteFilename = "migratemute.js"; 13 | const muteContents = await readFile( path.join( "src", muteFilename ), "utf8" ); 14 | 15 | const contents = await readFile( path.join( dir, filename ), "utf8" ); 16 | const banner = 17 | `/*! jQuery Migrate v${ version }` + 18 | " | (c) OpenJS Foundation and other contributors" + 19 | " | jquery.com/license */"; 20 | 21 | const minFilename = filename.replace( rjs, ".min.js" ); 22 | const mapFilename = filename.replace( rjs, ".min.map" ); 23 | 24 | const { 25 | code, 26 | error, 27 | map, 28 | warning 29 | } = UglifyJS.minify( 30 | { 31 | "../src/migratemute.js": muteContents, 32 | [ filename ]: contents 33 | }, 34 | { 35 | ie: true, 36 | compress: { 37 | hoist_funs: false, 38 | loops: false 39 | }, 40 | output: { 41 | ascii_only: true, 42 | preamble: banner 43 | }, 44 | sourceMap: { 45 | filename: minFilename, 46 | url: mapFilename 47 | } 48 | } 49 | ); 50 | 51 | if ( error ) { 52 | throw new Error( error ); 53 | } 54 | 55 | if ( warning ) { 56 | console.warn( warning ); 57 | } 58 | 59 | await Promise.all( [ 60 | writeFile( path.join( dir, minFilename ), code ), 61 | writeFile( path.join( dir, mapFilename ), map ) 62 | ] ); 63 | 64 | processForDist( muteContents, muteFilename ); 65 | processForDist( contents, filename ); 66 | processForDist( code, minFilename ); 67 | processForDist( map, mapFilename ); 68 | 69 | console.log( 70 | `[${ getTimestamp() }] ${ minFilename } ${ version } with ${ mapFilename } created.` 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /build/tasks/npmcopy.js: -------------------------------------------------------------------------------- 1 | import { copyFile, mkdir } from "node:fs/promises"; 2 | import path from "node:path"; 3 | 4 | const projectDir = path.resolve( "." ); 5 | 6 | const files = { 7 | "npo/npo.js": "native-promise-only/lib/npo.src.js", 8 | 9 | "qunit/qunit.js": "qunit/qunit/qunit.js", 10 | "qunit/qunit.css": "qunit/qunit/qunit.css", 11 | "qunit/LICENSE.txt": "qunit/LICENSE.txt", 12 | 13 | "sinon/sinon.js": "sinon/pkg/sinon.js", 14 | "sinon/LICENSE.txt": "sinon/LICENSE" 15 | }; 16 | 17 | async function npmcopy() { 18 | await mkdir( path.resolve( projectDir, "external" ), { 19 | recursive: true 20 | } ); 21 | for ( const [ dest, source ] of Object.entries( files ) ) { 22 | const from = path.resolve( projectDir, "node_modules", source ); 23 | const to = path.resolve( projectDir, "external", dest ); 24 | const toDir = path.dirname( to ); 25 | await mkdir( toDir, { recursive: true } ); 26 | await copyFile( from, to ); 27 | console.log( `${ source } → ${ dest }` ); 28 | } 29 | } 30 | 31 | npmcopy(); 32 | -------------------------------------------------------------------------------- /dist-module/wrappers/jquery-migrate.node-module-wrapper.js: -------------------------------------------------------------------------------- 1 | // Node.js is able to import from a CommonJS module in an ESM one. 2 | import jQuery from "../../dist/jquery-migrate.js"; 3 | 4 | export { jQuery, jQuery as $ }; 5 | export default jQuery; 6 | -------------------------------------------------------------------------------- /dist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /dist/wrappers/jquery-migrate.bundler-require-wrapper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Bundlers are able to synchronously require an ESM module from a CommonJS one. 4 | const { jQuery } = require( "../../dist-module/jquery-migrate.module.js" ); 5 | module.exports = jQuery; 6 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import jqueryConfig from "eslint-config-jquery"; 2 | import importPlugin from "eslint-plugin-import"; 3 | import globals from "globals"; 4 | 5 | export default [ 6 | { 7 | 8 | // Only global ignores will bypass the parser 9 | // and avoid JS parsing errors 10 | // See https://github.com/eslint/eslint/discussions/17412 11 | ignores: [ "external", "null.json", "simple.json" ] 12 | }, 13 | 14 | { 15 | files: [ 16 | "eslint.config.js", 17 | ".release-it.cjs", 18 | "build/**", 19 | "test/node_smoke_tests/**", 20 | "test/bundler_smoke_tests/**/*" 21 | ], 22 | languageOptions: { 23 | ecmaVersion: "latest", 24 | globals: { 25 | ...globals.node 26 | } 27 | }, 28 | rules: jqueryConfig.rules 29 | }, 30 | 31 | { 32 | files: [ "src/**" ], 33 | plugins: { 34 | import: importPlugin 35 | }, 36 | languageOptions: { 37 | ecmaVersion: 2015, 38 | 39 | // The browser env is not enabled on purpose so that code takes 40 | // all browser-only globals from window instead of assuming 41 | // they're available as globals. This makes it possible to use 42 | // jQuery with tools like jsdom which provide a custom window 43 | // implementation. 44 | globals: { 45 | jQuery: false, 46 | window: false 47 | } 48 | }, 49 | rules: { 50 | ...jqueryConfig.rules, 51 | "import/extensions": [ "error", "always" ], 52 | "import/no-cycle": "error", 53 | indent: [ 54 | "error", 55 | "tab", 56 | { 57 | outerIIFEBody: 0, 58 | 59 | // This makes it so code within if statements checking 60 | // for jQuery features is not indented. 61 | ignoredNodes: [ "Program > IfStatement > *" ] 62 | } 63 | ], 64 | "one-var": [ "error", { var: "always" } ], 65 | strict: [ "error", "function" ] 66 | } 67 | }, 68 | 69 | { 70 | files: [ 71 | "src/wrapper.js", 72 | "src/wrapper-esm.js", 73 | "src/wrapper-factory.js", 74 | "src/wrapper-factory-esm.js" 75 | ], 76 | languageOptions: { 77 | globals: { 78 | jQuery: false 79 | } 80 | }, 81 | rules: { 82 | "no-unused-vars": "off", 83 | indent: [ 84 | "error", 85 | "tab", 86 | { 87 | 88 | // This makes it so code within the wrapper is not indented. 89 | ignoredNodes: [ 90 | "Program > FunctionDeclaration > *" 91 | ] 92 | } 93 | ] 94 | } 95 | }, 96 | 97 | { 98 | files: [ "src/wrapper.js" ], 99 | languageOptions: { 100 | sourceType: "script", 101 | globals: { 102 | define: false, 103 | module: false 104 | } 105 | }, 106 | rules: { 107 | indent: [ 108 | "error", 109 | "tab", 110 | { 111 | 112 | // This makes it so code within the wrapper is not indented. 113 | ignoredNodes: [ 114 | "Program > ExpressionStatement > CallExpression > :last-child > *" 115 | ] 116 | } 117 | ] 118 | } 119 | }, 120 | 121 | { 122 | files: [ "test/unit/**" ], 123 | languageOptions: { 124 | ecmaVersion: 5, 125 | sourceType: "script", 126 | globals: { 127 | ...globals.browser, 128 | Promise: false, 129 | Symbol: false, 130 | jQuery: false, 131 | QUnit: false, 132 | sinon: false, 133 | url: false, 134 | expectMessage: false, 135 | expectNoMessage: false, 136 | compareVersions: false, 137 | jQueryVersionSince: false, 138 | startIframeTest: false, 139 | TestManager: false 140 | } 141 | }, 142 | rules: { 143 | ...jqueryConfig.rules, 144 | "no-unused-vars": [ 145 | "error", 146 | { args: "after-used", argsIgnorePattern: "^_" } 147 | ] 148 | } 149 | }, 150 | 151 | { 152 | files: [ "test/data/**" ], 153 | ignores: [ "test/data/jquery-*.js", "test/data/qunit-start.js" ], 154 | languageOptions: { 155 | ecmaVersion: 5, 156 | sourceType: "script", 157 | globals: { 158 | ...globals.browser, 159 | Promise: false, 160 | Symbol: false, 161 | global: false, 162 | jQuery: false, 163 | QUnit: false, 164 | url: true, 165 | compareVersions: true, 166 | jQueryVersionSince: false, 167 | expectMessage: true, 168 | expectNoMessage: true, 169 | startIframeTest: true, 170 | TestManager: true 171 | } 172 | }, 173 | rules: { 174 | ...jqueryConfig.rules, 175 | strict: [ "error", "global" ] 176 | } 177 | }, 178 | 179 | { 180 | files: [ "test/runner/**" ], 181 | languageOptions: { 182 | ecmaVersion: "latest", 183 | globals: { 184 | ...globals.node 185 | }, 186 | sourceType: "module" 187 | }, 188 | rules: { 189 | ...jqueryConfig.rules 190 | } 191 | }, 192 | 193 | { 194 | files: [ "test/runner/listeners.js" ], 195 | languageOptions: { 196 | ecmaVersion: 5, 197 | globals: { 198 | ...globals.browser, 199 | QUnit: false, 200 | Symbol: false 201 | }, 202 | sourceType: "script" 203 | }, 204 | rules: { 205 | ...jqueryConfig.rules, 206 | strict: [ "error", "function" ] 207 | } 208 | }, 209 | 210 | { 211 | files: [ "dist/jquery-migrate.js" ], 212 | languageOptions: { 213 | globals: { 214 | define: false, 215 | jQuery: false, 216 | module: false, 217 | Proxy: false, 218 | Reflect: false, 219 | window: false 220 | } 221 | }, 222 | rules: { 223 | ...jqueryConfig.rules, 224 | strict: [ "error", "function" ], 225 | 226 | // These are fine for the built version 227 | "no-multiple-empty-lines": "off", 228 | "one-var": "off" 229 | } 230 | }, 231 | 232 | { 233 | files: [ "dist/**" ], 234 | languageOptions: { 235 | ecmaVersion: 5, 236 | sourceType: "script" 237 | } 238 | }, 239 | 240 | { 241 | files: [ "dist-module/**" ], 242 | languageOptions: { 243 | ecmaVersion: 2015, 244 | sourceType: "module" 245 | } 246 | }, 247 | 248 | { 249 | files: [ "dist/wrappers/*.js" ], 250 | languageOptions: { 251 | ecmaVersion: 2015, 252 | sourceType: "commonjs" 253 | } 254 | } 255 | ]; 256 | -------------------------------------------------------------------------------- /jtr-ci.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | runs: 4 | jquery: 5 | - git 6 | - git.min 7 | - git.slim 8 | - git.slim.min 9 | - 4.0.0-beta.2 10 | - 4.0.0-beta.2.slim 11 | plugin: 12 | - dev 13 | - min 14 | 15 | retries: 2 16 | hard-retries: 1 17 | -------------------------------------------------------------------------------- /jtr-local.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | runs: 4 | jquery: 5 | - git 6 | - git.min 7 | - git.slim 8 | - git.slim.min 9 | - 4.0.0-beta.2 10 | - 4.0.0-beta.2.slim 11 | 12 | retries: 1 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-migrate", 3 | "title": "jQuery Migrate", 4 | "description": "Migrate older jQuery code to jQuery 4.x", 5 | "version": "4.0.0-pre", 6 | "type": "module", 7 | "exports": { 8 | "node": { 9 | "import": "./dist-module/wrappers/jquery-migrate.node-module-wrapper.js", 10 | "default": "./dist/jquery-migrate.js" 11 | }, 12 | "module": { 13 | "import": "./dist-module/jquery-migrate.module.js", 14 | "default": "./dist/wrappers/jquery-migrate.bundler-require-wrapper.js" 15 | }, 16 | "import": "./dist-module/jquery-migrate.module.js", 17 | "default": "./dist/jquery-migrate.js" 18 | }, 19 | "main": "dist/jquery-migrate.js", 20 | "homepage": "https://github.com/jquery/jquery-migrate", 21 | "author": { 22 | "name": "OpenJS Foundation and other contributors", 23 | "url": "https://openjsf.org" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/jquery/jquery-migrate.git" 28 | }, 29 | "bugs": { 30 | "url": "http://bugs.jquery.com/" 31 | }, 32 | "license": "MIT", 33 | "scripts": { 34 | "build": "node ./build/command.js", 35 | "build:all": "node --input-type=module -e \"import { buildDefaultFiles } from './build/tasks/build.js'; buildDefaultFiles()\"", 36 | "build:clean": "rimraf --glob dist/*.{js,map} --glob dist-module/*.{js,map}", 37 | "build:main": "node --input-type=module -e \"import { build } from './build/tasks/build.js'; build()\"", 38 | "lint": "eslint --cache .", 39 | "npmcopy": "node build/tasks/npmcopy.js", 40 | "prepare": "husky", 41 | "pretest": "npm run npmcopy", 42 | "start": "node --input-type=module -e \"import { buildDefaultFiles } from './build/tasks/build.js'; buildDefaultFiles({ watch: true })\"", 43 | "test:browser": "npm run pretest && npm run build:all && npm run test:unit -- -b chrome -b firefox --headless", 44 | "test:browserless": "npm run pretest && npm run build:all && node test/bundler_smoke_tests/run-jsdom-tests.js && node test/node_smoke_tests/node_smoke_tests.cjs", 45 | "test:ie": "npm run pretest && npm run build:all && npm run test:unit -- -v -b ie", 46 | "test:bundlers": "npm run pretest && npm run build:all && node test/bundler_smoke_tests/run-jsdom-tests.js", 47 | "test:node_smoke_tests": "npm run pretest && npm run build:all && node test/node_smoke_tests/node_smoke_tests.cjs", 48 | "test:safari": "npm run pretest && npm run build:all && npm run test:unit -- -v -b safari", 49 | "test:server": "jtr serve", 50 | "test:esm": "npm run pretest && npm run build:main && npm run test:unit -- -f plugin=esmodules --headless ", 51 | "test:unit": "jtr", 52 | "test": "npm run build:all && npm run lint && npm run test:browserless && npm run test:browser && npm run test:esm" 53 | }, 54 | "peerDependencies": { 55 | "jquery": ">=4 <5" 56 | }, 57 | "devDependencies": { 58 | "@rollup/plugin-commonjs": "28.0.3", 59 | "@rollup/plugin-node-resolve": "16.0.1", 60 | "chalk": "5.4.1", 61 | "commitplease": "3.2.0", 62 | "enquirer": "2.4.1", 63 | "eslint": "8.57.1", 64 | "eslint-config-jquery": "3.0.2", 65 | "eslint-plugin-import": "2.31.0", 66 | "globals": "15.15.0", 67 | "husky": "9.1.7", 68 | "jquery": "4.0.0-beta.2", 69 | "jquery-test-runner": "0.2.5", 70 | "jsdom": "26.0.0", 71 | "native-promise-only": "0.8.1", 72 | "qunit": "2.24.1", 73 | "rollup": "4.34.8", 74 | "sinon": "9.2.4", 75 | "uglify-js": "3.19.3", 76 | "webpack": "5.98.0", 77 | "yargs": "^17.7.2" 78 | }, 79 | "keywords": [ 80 | "jquery", 81 | "javascript", 82 | "browser", 83 | "plugin", 84 | "migrate" 85 | ], 86 | "commitplease": { 87 | "nohook": true, 88 | "components": [ 89 | "Docs", 90 | "Tests", 91 | "Build", 92 | "Release", 93 | "Core", 94 | "Ajax", 95 | "Attributes", 96 | "Callbacks", 97 | "CSS", 98 | "Data", 99 | "Deferred", 100 | "Deprecated", 101 | "Dimensions", 102 | "Effects", 103 | "Event", 104 | "Manipulation", 105 | "Offset", 106 | "Queue", 107 | "Selector", 108 | "Serialize", 109 | "Traversing", 110 | "Wrap" 111 | ] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/compareVersions.js: -------------------------------------------------------------------------------- 1 | // Returns 0 if v1 == v2, -1 if v1 < v2, 1 if v1 > v2 2 | function compareVersions( v1, v2 ) { 3 | var i, 4 | rVersionParts = /^(\d+)\.(\d+)\.(\d+)/, 5 | v1p = rVersionParts.exec( v1 ) || [ ], 6 | v2p = rVersionParts.exec( v2 ) || [ ]; 7 | 8 | for ( i = 1; i <= 3; i++ ) { 9 | if ( +v1p[ i ] > +v2p[ i ] ) { 10 | return 1; 11 | } 12 | if ( +v1p[ i ] < +v2p[ i ] ) { 13 | return -1; 14 | } 15 | } 16 | return 0; 17 | } 18 | 19 | export function jQueryVersionSince( version ) { 20 | return compareVersions( jQuery.fn.jquery, version ) >= 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/disablePatches.js: -------------------------------------------------------------------------------- 1 | // A map from disabled patch codes to `true`. This should really 2 | // be a `Set` but those are unsupported in IE. 3 | var disabledPatches = Object.create( null ); 4 | 5 | // Don't apply patches for specified codes. Helpful for code bases 6 | // where some Migrate warnings have been addressed and it's desirable 7 | // to avoid needless patches or false positives. 8 | jQuery.migrateDisablePatches = function() { 9 | var i; 10 | for ( i = 0; i < arguments.length; i++ ) { 11 | disabledPatches[ arguments[ i ] ] = true; 12 | } 13 | }; 14 | 15 | // Allow enabling patches disabled via `jQuery.migrateDisablePatches`. 16 | // Helpful if you want to disable a patch only for some code that won't 17 | // be updated soon to be able to focus on other warnings - and enable it 18 | // immediately after such a call: 19 | // ```js 20 | // jQuery.migrateDisablePatches( "workaroundA" ); 21 | // elem.pluginViolatingWarningA( "pluginMethod" ); 22 | // jQuery.migrateEnablePatches( "workaroundA" ); 23 | // ``` 24 | jQuery.migrateEnablePatches = function() { 25 | var i; 26 | for ( i = 0; i < arguments.length; i++ ) { 27 | delete disabledPatches[ arguments[ i ] ]; 28 | } 29 | }; 30 | 31 | jQuery.migrateIsPatchEnabled = function( patchCode ) { 32 | return !disabledPatches[ patchCode ]; 33 | }; 34 | -------------------------------------------------------------------------------- /src/jquery/ajax.js: -------------------------------------------------------------------------------- 1 | import { migrateWarn } from "../main.js"; 2 | 3 | // Support jQuery slim which excludes the ajax module 4 | if ( jQuery.ajax ) { 5 | 6 | var oldCallbacks = [], 7 | guid = "migrate-" + Date.now(), 8 | origJsonpCallback = jQuery.ajaxSettings.jsonpCallback, 9 | rjsonp = /(=)\?(?=&|$)|\?\?/, 10 | rquery = /\?/; 11 | 12 | jQuery.ajaxSetup( { 13 | jsonpCallback: function() { 14 | 15 | // Source is virtually the same as in Core, but we need to duplicate 16 | // to maintain a proper `oldCallbacks` reference. 17 | if ( jQuery.migrateIsPatchEnabled( "jsonp-promotion" ) ) { 18 | var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( guid++ ) ); 19 | this[ callback ] = true; 20 | return callback; 21 | } else { 22 | return origJsonpCallback.apply( this, arguments ); 23 | } 24 | } 25 | } ); 26 | 27 | // Register this prefilter before the jQuery one. Otherwise, a promoted 28 | // request is transformed into one with the script dataType, and we can't 29 | // catch it anymore. 30 | // 31 | // Code mostly from: 32 | // https://github.com/jquery/jquery/blob/fa0058af426c4e482059214c29c29f004254d9a1/src/ajax/jsonp.js#L20-L97 33 | jQuery.ajaxPrefilter( "+json", function( s, originalSettings, jqXHR ) { 34 | 35 | if ( !jQuery.migrateIsPatchEnabled( "jsonp-promotion" ) ) { 36 | return; 37 | } 38 | 39 | var callbackName, overwritten, responseContainer, 40 | jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? 41 | "url" : 42 | typeof s.data === "string" && 43 | ( s.contentType || "" ) 44 | .indexOf( "application/x-www-form-urlencoded" ) === 0 && 45 | rjsonp.test( s.data ) && "data" 46 | ); 47 | 48 | // Handle iff the expected data type is "jsonp" or we have a parameter to set 49 | if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { 50 | migrateWarn( "jsonp-promotion", "JSON-to-JSONP auto-promotion is removed" ); 51 | 52 | // Get callback name, remembering preexisting value associated with it 53 | callbackName = s.jsonpCallback = typeof s.jsonpCallback === "function" ? 54 | s.jsonpCallback() : 55 | s.jsonpCallback; 56 | 57 | // Insert callback into url or form data 58 | if ( jsonProp ) { 59 | s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); 60 | } else if ( s.jsonp !== false ) { 61 | s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; 62 | } 63 | 64 | // Use data converter to retrieve json after script execution 65 | s.converters[ "script json" ] = function() { 66 | if ( !responseContainer ) { 67 | jQuery.error( callbackName + " was not called" ); 68 | } 69 | return responseContainer[ 0 ]; 70 | }; 71 | 72 | // Force json dataType 73 | s.dataTypes[ 0 ] = "json"; 74 | 75 | // Install callback 76 | overwritten = window[ callbackName ]; 77 | window[ callbackName ] = function() { 78 | responseContainer = arguments; 79 | }; 80 | 81 | // Clean-up function (fires after converters) 82 | jqXHR.always( function() { 83 | 84 | // If previous value didn't exist - remove it 85 | if ( overwritten === undefined ) { 86 | jQuery( window ).removeProp( callbackName ); 87 | 88 | // Otherwise restore preexisting value 89 | } else { 90 | window[ callbackName ] = overwritten; 91 | } 92 | 93 | // Save back as free 94 | if ( s[ callbackName ] ) { 95 | 96 | // Make sure that re-using the options doesn't screw things around 97 | s.jsonpCallback = originalSettings.jsonpCallback; 98 | 99 | // Save the callback name for future use 100 | oldCallbacks.push( callbackName ); 101 | } 102 | 103 | // Call if it was a function and we have a response 104 | if ( responseContainer && typeof overwritten === "function" ) { 105 | overwritten( responseContainer[ 0 ] ); 106 | } 107 | 108 | responseContainer = overwritten = undefined; 109 | } ); 110 | 111 | // Delegate to script 112 | return "script"; 113 | } 114 | } ); 115 | 116 | // Don't trigger the above logic by default as the JSON-to-JSONP auto-promotion 117 | // behavior is gone in jQuery 4.0 and as it has security implications, we don't 118 | // want to restore the legacy behavior by default. 119 | jQuery.migrateDisablePatches( "jsonp-promotion" ); 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/jquery/attributes.js: -------------------------------------------------------------------------------- 1 | import { migratePatchFunc, migrateWarn } from "../main.js"; 2 | 3 | var oldJQueryAttr = jQuery.attr, 4 | oldToggleClass = jQuery.fn.toggleClass, 5 | booleans = "checked|selected|async|autofocus|autoplay|controls|defer|" + 6 | "disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", 7 | rbooleans = new RegExp( "^(?:" + booleans + ")$", "i" ), 8 | 9 | // Some formerly boolean attributes gained new values with special meaning. 10 | // Skip the old boolean attr logic for those values. 11 | extraBoolAttrValues = { 12 | hidden: [ "until-found" ] 13 | }; 14 | 15 | // HTML boolean attributes have special behavior: 16 | // we consider the lowercase name to be the only valid value, so 17 | // getting (if the attribute is present) normalizes to that, as does 18 | // setting to any non-`false` value (and setting to `false` removes the attribute). 19 | // See https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes 20 | jQuery.each( booleans.split( "|" ), function( _i, name ) { 21 | var origAttrHooks = jQuery.attrHooks[ name ] || {}; 22 | jQuery.attrHooks[ name ] = { 23 | get: origAttrHooks.get || function( elem ) { 24 | var attrValue; 25 | 26 | if ( jQuery.migrateIsPatchEnabled( "boolean-attributes" ) ) { 27 | attrValue = elem.getAttribute( name ); 28 | 29 | if ( attrValue !== name && attrValue != null && 30 | ( extraBoolAttrValues[ name ] || [] ) 31 | .indexOf( String( attrValue ).toLowerCase() ) === -1 32 | ) { 33 | migrateWarn( "boolean-attributes", 34 | "Boolean attribute '" + name + 35 | "' value is different from its lowercased name" ); 36 | 37 | return name.toLowerCase(); 38 | } 39 | } 40 | 41 | return null; 42 | }, 43 | 44 | set: origAttrHooks.set || function( elem, value, name ) { 45 | if ( jQuery.migrateIsPatchEnabled( "boolean-attributes" ) ) { 46 | if ( value !== name && 47 | ( extraBoolAttrValues[ name ] || [] ) 48 | .indexOf( String( value ).toLowerCase() ) === -1 49 | ) { 50 | if ( value !== false ) { 51 | migrateWarn( "boolean-attributes", 52 | "Boolean attribute '" + name + 53 | "' is not set to its lowercased name" ); 54 | } 55 | 56 | if ( value === false ) { 57 | 58 | // Remove boolean attributes when set to false 59 | jQuery.removeAttr( elem, name ); 60 | } else { 61 | elem.setAttribute( name, name ); 62 | } 63 | return name; 64 | } 65 | } 66 | } 67 | }; 68 | } ); 69 | 70 | migratePatchFunc( jQuery, "attr", function( elem, name, value ) { 71 | var nType = elem.nodeType; 72 | 73 | // Fallback to the original method on text, comment and attribute nodes 74 | // and when attributes are not supported. 75 | if ( nType === 3 || nType === 8 || nType === 2 || 76 | typeof elem.getAttribute === "undefined" ) { 77 | return oldJQueryAttr.apply( this, arguments ); 78 | } 79 | 80 | if ( value === false && name.toLowerCase().indexOf( "aria-" ) !== 0 && 81 | !rbooleans.test( name ) ) { 82 | migrateWarn( "attr-false", 83 | "Setting the non-ARIA non-boolean attribute '" + name + 84 | "' to false" ); 85 | 86 | jQuery.attr( elem, name, "false" ); 87 | return; 88 | } 89 | 90 | return oldJQueryAttr.apply( this, arguments ); 91 | }, "attr-false" ); 92 | 93 | migratePatchFunc( jQuery.fn, "toggleClass", function( state ) { 94 | 95 | // Only deprecating no-args or single boolean arg 96 | if ( state !== undefined && typeof state !== "boolean" ) { 97 | 98 | return oldToggleClass.apply( this, arguments ); 99 | } 100 | 101 | migrateWarn( "toggleClass-bool", "jQuery.fn.toggleClass( [ boolean ] ) is removed" ); 102 | 103 | // Toggle entire class name of each element 104 | return this.each( function() { 105 | var className = this.getAttribute && this.getAttribute( "class" ) || ""; 106 | 107 | if ( className ) { 108 | jQuery.data( this, "__className__", className ); 109 | } 110 | 111 | // If the element has a class name or if we're passed `false`, 112 | // then remove the whole classname (if there was one, the above saved it). 113 | // Otherwise bring back whatever was previously saved (if anything), 114 | // falling back to the empty string if nothing was stored. 115 | if ( this.setAttribute ) { 116 | this.setAttribute( "class", 117 | className || state === false ? 118 | "" : 119 | jQuery.data( this, "__className__" ) || "" 120 | ); 121 | } 122 | } ); 123 | }, "toggleClass-bool" ); 124 | -------------------------------------------------------------------------------- /src/jquery/core.js: -------------------------------------------------------------------------------- 1 | import { migratePatchAndWarnFunc, migratePatchAndInfoFunc } from "../main.js"; 2 | import "../disablePatches.js"; 3 | 4 | var arr = [], 5 | push = arr.push, 6 | sort = arr.sort, 7 | splice = arr.splice, 8 | class2type = {}, 9 | 10 | // Require that the "whitespace run" starts from a non-whitespace 11 | // to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position. 12 | rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g; 13 | 14 | migratePatchAndWarnFunc( jQuery, "parseJSON", function() { 15 | return JSON.parse.apply( null, arguments ); 16 | }, "parseJSON", 17 | "jQuery.parseJSON is removed; use JSON.parse" ); 18 | 19 | migratePatchAndInfoFunc( jQuery, "holdReady", jQuery.holdReady, 20 | "holdReady", "jQuery.holdReady() is deprecated" ); 21 | 22 | migratePatchAndWarnFunc( jQuery, "unique", jQuery.uniqueSort, 23 | "unique", "jQuery.unique() is removed; use jQuery.uniqueSort()" ); 24 | 25 | migratePatchAndWarnFunc( jQuery, "trim", function( text ) { 26 | return text == null ? 27 | "" : 28 | ( text + "" ).replace( rtrim, "$1" ); 29 | }, "trim", 30 | "jQuery.trim() is removed; use String.prototype.trim" ); 31 | 32 | migratePatchAndWarnFunc( jQuery, "nodeName", function( elem, name ) { 33 | return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); 34 | }, "nodeName", 35 | "jQuery.nodeName() is removed" ); 36 | 37 | migratePatchAndWarnFunc( jQuery, "isArray", Array.isArray, "isArray", 38 | "jQuery.isArray() is removed; use Array.isArray()" 39 | ); 40 | 41 | migratePatchAndWarnFunc( jQuery, "isNumeric", 42 | function( obj ) { 43 | 44 | // As of jQuery 3.0, isNumeric is limited to 45 | // strings and numbers (primitives or objects) 46 | // that can be coerced to finite numbers (gh-2662) 47 | var type = typeof obj; 48 | return ( type === "number" || type === "string" ) && 49 | 50 | // parseFloat NaNs numeric-cast false positives ("") 51 | // ...but misinterprets leading-number strings, e.g. hex literals ("0x...") 52 | // subtraction forces infinities to NaN 53 | !isNaN( obj - parseFloat( obj ) ); 54 | }, "isNumeric", 55 | "jQuery.isNumeric() is removed" 56 | ); 57 | 58 | // Populate the class2type map 59 | jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol". 60 | split( " " ), 61 | function( _, name ) { 62 | class2type[ "[object " + name + "]" ] = name.toLowerCase(); 63 | } ); 64 | 65 | migratePatchAndWarnFunc( jQuery, "type", function( obj ) { 66 | if ( obj == null ) { 67 | return obj + ""; 68 | } 69 | 70 | return typeof obj === "object" ? 71 | class2type[ Object.prototype.toString.call( obj ) ] || "object" : 72 | typeof obj; 73 | }, "type", 74 | "jQuery.type() is removed" ); 75 | 76 | migratePatchAndWarnFunc( jQuery, "isFunction", function( obj ) { 77 | return typeof obj === "function"; 78 | }, "isFunction", 79 | "jQuery.isFunction() is removed" ); 80 | 81 | migratePatchAndWarnFunc( jQuery, "isWindow", 82 | function( obj ) { 83 | return obj != null && obj === obj.window; 84 | }, "isWindow", 85 | "jQuery.isWindow() is removed" 86 | ); 87 | 88 | // Bind a function to a context, optionally partially applying any 89 | // arguments. 90 | // jQuery.proxy is deprecated to promote standards (specifically Function#bind) 91 | // However, it is not slated for removal any time soon 92 | migratePatchAndInfoFunc( jQuery, "proxy", jQuery.proxy, 93 | "proxy", "jQuery.proxy() is deprecated" ); 94 | 95 | migratePatchAndWarnFunc( jQuery.fn, "push", push, "push", 96 | "jQuery.fn.push() is removed; use .add() or convert to an array" ); 97 | migratePatchAndWarnFunc( jQuery.fn, "sort", sort, "sort", 98 | "jQuery.fn.sort() is removed; convert to an array before sorting" ); 99 | migratePatchAndWarnFunc( jQuery.fn, "splice", splice, "splice", 100 | "jQuery.fn.splice() is removed; use .slice() or .not() with .eq()" ); 101 | -------------------------------------------------------------------------------- /src/jquery/css.js: -------------------------------------------------------------------------------- 1 | import { migrateWarn, migratePatchFunc } from "../main.js"; 2 | import { camelCase } from "../utils.js"; 3 | 4 | var origFnCss, internalCssNumber, 5 | ralphaStart = /^[a-z]/, 6 | 7 | // The regex visualized: 8 | // 9 | // /----------\ 10 | // | | /-------\ 11 | // | / Top \ | | | 12 | // /--- Border ---+-| Right |-+---+- Width -+---\ 13 | // | | Bottom | | 14 | // | \ Left / | 15 | // | | 16 | // | /----------\ | 17 | // | /-------------\ | | |- END 18 | // | | | | / Top \ | | 19 | // | | / Margin \ | | | Right | | | 20 | // |---------+-| |-+---+-| Bottom |-+----| 21 | // | \ Padding / \ Left / | 22 | // BEGIN -| | 23 | // | /---------\ | 24 | // | | | | 25 | // | | / Min \ | / Width \ | 26 | // \--------------+-| |-+---| |---/ 27 | // \ Max / \ Height / 28 | rautoPx = /^(?:Border(?:Top|Right|Bottom|Left)?(?:Width|)|(?:Margin|Padding)?(?:Top|Right|Bottom|Left)?|(?:Min|Max)?(?:Width|Height))$/; 29 | 30 | if ( typeof Proxy !== "undefined" ) { 31 | jQuery.cssProps = new Proxy( jQuery.cssProps || {}, { 32 | set: function() { 33 | migrateWarn( "cssProps", "jQuery.cssProps is removed" ); 34 | return Reflect.set.apply( this, arguments ); 35 | } 36 | } ); 37 | } 38 | 39 | // `jQuery.cssNumber` is missing in jQuery >=4; fill it with the latest 3.x version: 40 | // https://github.com/jquery/jquery/blob/3.7.1/src/css.js#L216-L246 41 | // This way, number values for the CSS properties below won't start triggering 42 | // Migrate warnings when jQuery gets updated to >=4.0.0 (gh-438). 43 | // 44 | // We need to keep this as a local variable as we need it internally 45 | // in a `jQuery.fn.css` patch and this usage shouldn't warn. 46 | internalCssNumber = { 47 | animationIterationCount: true, 48 | aspectRatio: true, 49 | borderImageSlice: true, 50 | columnCount: true, 51 | flexGrow: true, 52 | flexShrink: true, 53 | fontWeight: true, 54 | gridArea: true, 55 | gridColumn: true, 56 | gridColumnEnd: true, 57 | gridColumnStart: true, 58 | gridRow: true, 59 | gridRowEnd: true, 60 | gridRowStart: true, 61 | lineHeight: true, 62 | opacity: true, 63 | order: true, 64 | orphans: true, 65 | scale: true, 66 | widows: true, 67 | zIndex: true, 68 | zoom: true, 69 | 70 | // SVG-related 71 | fillOpacity: true, 72 | floodOpacity: true, 73 | stopOpacity: true, 74 | strokeMiterlimit: true, 75 | strokeOpacity: true 76 | }; 77 | 78 | if ( typeof Proxy !== "undefined" ) { 79 | jQuery.cssNumber = new Proxy( internalCssNumber, { 80 | get: function() { 81 | migrateWarn( "css-number", "jQuery.cssNumber is deprecated" ); 82 | return Reflect.get.apply( this, arguments ); 83 | }, 84 | set: function() { 85 | migrateWarn( "css-number", "jQuery.cssNumber is deprecated" ); 86 | return Reflect.set.apply( this, arguments ); 87 | } 88 | } ); 89 | } else { 90 | 91 | // Support: IE 9-11+ 92 | // IE doesn't support proxies, but we still want to restore the legacy 93 | // jQuery.cssNumber there. 94 | jQuery.cssNumber = internalCssNumber; 95 | } 96 | 97 | function isAutoPx( prop ) { 98 | 99 | // The first test is used to ensure that: 100 | // 1. The prop starts with a lowercase letter (as we uppercase it for the second regex). 101 | // 2. The prop is not empty. 102 | return ralphaStart.test( prop ) && 103 | rautoPx.test( prop[ 0 ].toUpperCase() + prop.slice( 1 ) ); 104 | } 105 | 106 | origFnCss = jQuery.fn.css; 107 | 108 | migratePatchFunc( jQuery.fn, "css", function( name, value ) { 109 | var camelName, 110 | origThis = this; 111 | 112 | if ( name && typeof name === "object" && !Array.isArray( name ) ) { 113 | jQuery.each( name, function( n, v ) { 114 | jQuery.fn.css.call( origThis, n, v ); 115 | } ); 116 | return this; 117 | } 118 | 119 | if ( typeof value === "number" ) { 120 | camelName = camelCase( name ); 121 | 122 | // Use `internalCssNumber` to avoid triggering our warnings in this 123 | // internal check. 124 | if ( !isAutoPx( camelName ) && !internalCssNumber[ camelName ] ) { 125 | migrateWarn( "css-number", 126 | "Auto-appending 'px' to number-typed values " + 127 | "for jQuery.fn.css( \"" + name + "\", value ) is removed" ); 128 | } 129 | } 130 | 131 | return origFnCss.apply( this, arguments ); 132 | }, "css-number" ); 133 | -------------------------------------------------------------------------------- /src/jquery/data.js: -------------------------------------------------------------------------------- 1 | import { migratePatchFunc } from "../main.js"; 2 | import { patchProto } from "../utils.js"; 3 | 4 | function patchDataProto( original, options ) { 5 | var warningId = options.warningId, 6 | apiName = options.apiName, 7 | isInstanceMethod = options.isInstanceMethod; 8 | 9 | return function apiWithProtoPatched() { 10 | var result = original.apply( this, arguments ); 11 | 12 | if ( arguments.length !== ( isInstanceMethod ? 0 : 1 ) || result === undefined ) { 13 | return result; 14 | } 15 | 16 | patchProto( result, { 17 | warningId: warningId, 18 | apiName: apiName 19 | } ); 20 | 21 | return result; 22 | }; 23 | } 24 | 25 | migratePatchFunc( jQuery, "data", 26 | patchDataProto( jQuery.data, { 27 | warningId: "data-null-proto", 28 | apiName: "jQuery.data()", 29 | isInstanceMethod: false 30 | } ), 31 | "data-null-proto" ); 32 | migratePatchFunc( jQuery, "_data", 33 | patchDataProto( jQuery._data, { 34 | warningId: "data-null-proto", 35 | apiName: "jQuery._data()", 36 | isInstanceMethod: false 37 | } ), 38 | "data-null-proto" ); 39 | migratePatchFunc( jQuery.fn, "data", 40 | patchDataProto( jQuery.fn.data, { 41 | warningId: "data-null-proto", 42 | apiName: "jQuery.fn.data()", 43 | isInstanceMethod: true 44 | } ), 45 | "data-null-proto" ); 46 | -------------------------------------------------------------------------------- /src/jquery/deferred.js: -------------------------------------------------------------------------------- 1 | import { 2 | migratePatchFunc, 3 | migratePatchAndInfoFunc, 4 | migrateWarn 5 | } from "../main.js"; 6 | 7 | // Support jQuery slim which excludes the deferred module in jQuery 4.0+ 8 | if ( jQuery.Deferred ) { 9 | 10 | var unpatchedGetStackHookValue, 11 | oldDeferred = jQuery.Deferred; 12 | 13 | migratePatchFunc( jQuery, "Deferred", function( func ) { 14 | var deferred = oldDeferred(), 15 | promise = deferred.promise(); 16 | 17 | migratePatchAndInfoFunc( deferred, "pipe", deferred.pipe, "deferred-pipe", 18 | "deferred.pipe() is deprecated" ); 19 | migratePatchAndInfoFunc( promise, "pipe", promise.pipe, "deferred-pipe", 20 | "deferred.pipe() is deprecated" ); 21 | 22 | if ( func ) { 23 | func.call( deferred, deferred ); 24 | } 25 | 26 | return deferred; 27 | }, "deferred-pipe" ); 28 | 29 | // Preserve handler of uncaught exceptions in promise chains 30 | jQuery.Deferred.exceptionHook = oldDeferred.exceptionHook; 31 | 32 | // Preserve the optional hook to record the error, if defined 33 | jQuery.Deferred.getErrorHook = oldDeferred.getErrorHook; 34 | 35 | // We want to mirror jQuery.Deferred.getErrorHook here, so we cannot use 36 | // existing Migrate utils. 37 | Object.defineProperty( jQuery.Deferred, "getStackHook", { 38 | configurable: true, 39 | enumerable: true, 40 | get: function() { 41 | if ( jQuery.migrateIsPatchEnabled( "deferred-getStackHook" ) ) { 42 | migrateWarn( "deferred-getStackHook", 43 | "jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook" ); 44 | return jQuery.Deferred.getErrorHook; 45 | } else { 46 | return unpatchedGetStackHookValue; 47 | } 48 | }, 49 | set: function( newValue ) { 50 | if ( jQuery.migrateIsPatchEnabled( "deferred-getStackHook" ) ) { 51 | 52 | // Only warn if `getErrorHook` wasn't set to the same value first. 53 | if ( jQuery.Deferred.getErrorHook !== newValue ) { 54 | migrateWarn( "deferred-getStackHook", 55 | "jQuery.Deferred.getStackHook is removed; use jQuery.Deferred.getErrorHook" ); 56 | jQuery.Deferred.getErrorHook = newValue; 57 | } 58 | } else { 59 | unpatchedGetStackHookValue = newValue; 60 | } 61 | } 62 | } ); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/jquery/effects.js: -------------------------------------------------------------------------------- 1 | import { migrateWarn } from "../main.js"; 2 | import "../disablePatches.js"; 3 | 4 | // Support jQuery slim which excludes the effects module 5 | if ( jQuery.fx ) { 6 | 7 | var intervalValue = jQuery.fx.interval, 8 | intervalMsg = "jQuery.fx.interval is removed"; 9 | 10 | // Don't warn if document is hidden, jQuery uses setTimeout (gh-292) 11 | Object.defineProperty( jQuery.fx, "interval", { 12 | configurable: true, 13 | enumerable: true, 14 | get: function() { 15 | if ( !window.document.hidden ) { 16 | migrateWarn( "fx-interval", intervalMsg ); 17 | } 18 | 19 | // Only fallback to the default if patch is enabled 20 | if ( !jQuery.migrateIsPatchEnabled( "fx-interval" ) ) { 21 | return intervalValue; 22 | } 23 | return intervalValue === undefined ? 13 : intervalValue; 24 | }, 25 | set: function( newValue ) { 26 | migrateWarn( "fx-interval", intervalMsg ); 27 | intervalValue = newValue; 28 | } 29 | } ); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/jquery/event.js: -------------------------------------------------------------------------------- 1 | import { 2 | migrateWarn, 3 | migrateWarnProp, 4 | migratePatchAndInfoFunc, 5 | migratePatchFunc, 6 | migratePatchProp 7 | } from "../main.js"; 8 | import "../disablePatches.js"; 9 | import { patchProto } from "../utils.js"; 10 | 11 | var oldEventAdd = jQuery.event.add; 12 | 13 | jQuery.event.props = []; 14 | jQuery.event.fixHooks = {}; 15 | 16 | migratePatchFunc( jQuery.event, "add", function( elem, types ) { 17 | 18 | // This misses the multiple-types case but that seems awfully rare 19 | if ( elem === window && types === "load" && window.document.readyState === "complete" ) { 20 | migrateWarn( "load-after-event", 21 | "jQuery(window).on('load'...) called after load event occurred" ); 22 | } 23 | return oldEventAdd.apply( this, arguments ); 24 | }, "load-after-event" ); 25 | 26 | jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + 27 | "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + 28 | "change select submit keydown keypress keyup contextmenu" ).split( " " ), 29 | function( _i, name ) { 30 | 31 | // Handle event binding 32 | migratePatchAndInfoFunc( jQuery.fn, name, jQuery.fn[ name ], "shorthand-deprecated-v3", 33 | "jQuery.fn." + name + "() event shorthand is deprecated" ); 34 | } ); 35 | 36 | migratePatchAndInfoFunc( jQuery.fn, "bind", jQuery.fn.bind, 37 | "pre-on-methods", "jQuery.fn.bind() is deprecated" ); 38 | migratePatchAndInfoFunc( jQuery.fn, "unbind", jQuery.fn.unbind, 39 | "pre-on-methods", "jQuery.fn.unbind() is deprecated" ); 40 | migratePatchAndInfoFunc( jQuery.fn, "delegate", jQuery.fn.delegate, 41 | "pre-on-methods", "jQuery.fn.delegate() is deprecated" ); 42 | migratePatchAndInfoFunc( jQuery.fn, "undelegate", jQuery.fn.undelegate, 43 | "pre-on-methods", "jQuery.fn.undelegate() is deprecated" ); 44 | 45 | migratePatchAndInfoFunc( jQuery.fn, "hover", jQuery.fn.hover, 46 | "hover", "jQuery.fn.hover() is deprecated" ); 47 | 48 | migrateWarnProp( jQuery.event, "global", {}, "event-global", 49 | "jQuery.event.global is removed" ); 50 | 51 | migratePatchProp( jQuery.event, "special", 52 | patchProto( jQuery.extend( Object.create( null ), jQuery.event.special ), { 53 | warningId: "event-special-null-proto", 54 | apiName: "jQuery.event.special" 55 | } ), 56 | "event-special-null-proto" ); 57 | -------------------------------------------------------------------------------- /src/jquery/manipulation.js: -------------------------------------------------------------------------------- 1 | import { migratePatchFunc, migrateWarn } from "../main.js"; 2 | import "../disablePatches.js"; 3 | 4 | var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, 5 | makeMarkup = function( html ) { 6 | var doc = window.document.implementation.createHTMLDocument( "" ); 7 | doc.body.innerHTML = html; 8 | return doc.body && doc.body.innerHTML; 9 | }, 10 | warnIfChanged = function( html ) { 11 | var changed = html.replace( rxhtmlTag, "<$1>" ); 12 | if ( changed !== html && makeMarkup( html ) !== makeMarkup( changed ) ) { 13 | migrateWarn( "self-closed-tags", 14 | "HTML tags must be properly nested and closed: " + html ); 15 | } 16 | }; 17 | 18 | migratePatchFunc( jQuery, "htmlPrefilter", function( html ) { 19 | warnIfChanged( html ); 20 | return html.replace( rxhtmlTag, "<$1>" ); 21 | }, "self-closed-tags" ); 22 | 23 | // This patch needs to be disabled by default as it re-introduces 24 | // security issues (CVE-2020-11022, CVE-2020-11023). 25 | jQuery.migrateDisablePatches( "self-closed-tags" ); 26 | -------------------------------------------------------------------------------- /src/jquery/selector.js: -------------------------------------------------------------------------------- 1 | import { migratePatchFunc, migrateInfoProp, migrateInfo } from "../main.js"; 2 | 3 | // Now jQuery.expr.pseudos is the standard incantation 4 | migrateInfoProp( jQuery.expr, "filters", jQuery.expr.pseudos, "expr-pre-pseudos", 5 | "jQuery.expr.filters is deprecated; use jQuery.expr.pseudos" ); 6 | migrateInfoProp( jQuery.expr, ":", jQuery.expr.pseudos, "expr-pre-pseudos", 7 | "jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos" ); 8 | 9 | function markFunction( fn ) { 10 | fn[ jQuery.expando ] = true; 11 | return fn; 12 | } 13 | 14 | migratePatchFunc( jQuery.expr.filter, "PSEUDO", function( pseudo, argument ) { 15 | 16 | // pseudo-class names are case-insensitive 17 | // https://www.w3.org/TR/selectors/#pseudo-classes 18 | // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters 19 | // Remember that setFilters inherits from pseudos 20 | var args, 21 | fn = jQuery.expr.pseudos[ pseudo ] || 22 | jQuery.expr.setFilters[ pseudo.toLowerCase() ] || 23 | jQuery.error( 24 | "Syntax error, unrecognized expression: unsupported pseudo: " + 25 | pseudo ); 26 | 27 | // The user may use createPseudo to indicate that 28 | // arguments are needed to create the filter function 29 | // just as jQuery does 30 | if ( fn[ jQuery.expando ] ) { 31 | return fn( argument ); 32 | } 33 | 34 | // But maintain support for old signatures 35 | if ( fn.length > 1 ) { 36 | migrateInfo( "legacy-custom-pseudos", 37 | "Pseudos with multiple arguments are deprecated; " + 38 | "use jQuery.expr.createPseudo()" ); 39 | args = [ pseudo, pseudo, "", argument ]; 40 | return jQuery.expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? 41 | markFunction( function( seed, matches ) { 42 | var idx, 43 | matched = fn( seed, argument ), 44 | i = matched.length; 45 | while ( i-- ) { 46 | idx = Array.prototype.indexOf.call( seed, matched[ i ] ); 47 | seed[ idx ] = !( matches[ idx ] = matched[ i ] ); 48 | } 49 | } ) : 50 | function( elem ) { 51 | return fn( elem, 0, args ); 52 | }; 53 | } 54 | 55 | return fn; 56 | }, "legacy-custom-pseudos" ); 57 | 58 | if ( typeof Proxy !== "undefined" ) { 59 | jQuery.each( [ "pseudos", "setFilters" ], function( _, api ) { 60 | jQuery.expr[ api ] = new Proxy( jQuery.expr[ api ], { 61 | set: function( _target, _prop, fn ) { 62 | if ( typeof fn === "function" && !fn[ jQuery.expando ] && fn.length > 1 ) { 63 | migrateInfo( "legacy-custom-pseudos", 64 | "Pseudos with multiple arguments are deprecated; " + 65 | "use jQuery.expr.createPseudo()" ); 66 | } 67 | return Reflect.set.apply( this, arguments ); 68 | } 69 | } ); 70 | } ); 71 | } 72 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { jQueryVersionSince } from "./compareVersions.js"; 2 | import "./disablePatches.js"; 3 | 4 | ( function() { 5 | 6 | // Need jQuery 4.x and no older Migrate loaded 7 | if ( !jQuery || !jQueryVersionSince( "4.0.0" ) || 8 | jQueryVersionSince( "5.0.0" ) ) { 9 | window.console.log( "JQMIGRATE: jQuery 4.x REQUIRED" ); 10 | } 11 | if ( jQuery.migrateMessages ) { 12 | window.console.log( "JQMIGRATE: Migrate plugin loaded multiple times" ); 13 | } 14 | 15 | // Show a message on the console so devs know we're active 16 | window.console.log( "JQMIGRATE: Migrate is installed" + 17 | ( jQuery.migrateMute ? "" : " with logging active" ) + 18 | ", version " + jQuery.migrateVersion ); 19 | 20 | } )(); 21 | 22 | var messagesLogged = Object.create( null ); 23 | 24 | // List of warnings already given; public read only 25 | jQuery.migrateMessages = []; 26 | 27 | // By default, each warning is only reported once. 28 | if ( jQuery.migrateDeduplicateMessages === undefined ) { 29 | jQuery.migrateDeduplicateMessages = true; 30 | } 31 | 32 | // Set to `false` to disable traces that appear with warnings 33 | if ( jQuery.migrateTrace === undefined ) { 34 | jQuery.migrateTrace = true; 35 | } 36 | 37 | // Forget any warnings we've already given; public 38 | jQuery.migrateReset = function() { 39 | messagesLogged = Object.create( null ); 40 | jQuery.migrateMessages.length = 0; 41 | }; 42 | 43 | function migrateMessageInternal( code, msg, consoleMethod ) { 44 | var console = window.console; 45 | 46 | if ( jQuery.migrateIsPatchEnabled( code ) && 47 | ( !jQuery.migrateDeduplicateMessages || !messagesLogged[ msg ] ) ) { 48 | messagesLogged[ msg ] = true; 49 | jQuery.migrateMessages.push( consoleMethod.toUpperCase() + ": " + 50 | msg + " [" + code + "]" ); 51 | 52 | if ( console[ consoleMethod ] && !jQuery.migrateMute ) { 53 | console[ consoleMethod ]( "JQMIGRATE: " + msg ); 54 | 55 | if ( jQuery.migrateTrace ) { 56 | 57 | // Label the trace so that filtering messages in DevTools 58 | // doesn't hide traces. Note that IE ignores the label. 59 | console.trace( "JQMIGRATE: " + msg ); 60 | } 61 | } 62 | } 63 | } 64 | 65 | export function migrateWarn( code, msg ) { 66 | migrateMessageInternal( code, msg, "warn" ); 67 | } 68 | 69 | export function migrateInfo( code, msg ) { 70 | migrateMessageInternal( code, msg, "info" ); 71 | } 72 | 73 | function migratePatchPropInternal( 74 | obj, prop, value, code, msg, migrateMessageFn 75 | ) { 76 | var orig = obj[ prop ]; 77 | Object.defineProperty( obj, prop, { 78 | configurable: true, 79 | enumerable: true, 80 | 81 | get: function() { 82 | if ( jQuery.migrateIsPatchEnabled( code ) ) { 83 | 84 | // If `msg` not provided, do not message; more sophisticated 85 | // messaging logic is most likely embedded in `value`. 86 | if ( msg ) { 87 | migrateMessageFn( code, msg ); 88 | } 89 | 90 | return value; 91 | } 92 | 93 | return orig; 94 | }, 95 | 96 | set: function( newValue ) { 97 | if ( jQuery.migrateIsPatchEnabled( code ) ) { 98 | 99 | // See the comment in the getter. 100 | if ( msg ) { 101 | migrateMessageFn( code, msg ); 102 | } 103 | } 104 | 105 | // If a new value was set, apply it regardless if 106 | // the patch is later disabled or not. 107 | orig = value = newValue; 108 | } 109 | } ); 110 | } 111 | 112 | export function migrateWarnProp( obj, prop, value, code, msg ) { 113 | if ( !msg ) { 114 | throw new Error( "No warning message provided" ); 115 | } 116 | migratePatchPropInternal( obj, prop, value, code, msg, migrateWarn ); 117 | } 118 | 119 | export function migrateInfoProp( obj, prop, value, code, msg ) { 120 | if ( !msg ) { 121 | throw new Error( "No warning message provided" ); 122 | } 123 | migratePatchPropInternal( obj, prop, value, code, msg, migrateInfo ); 124 | } 125 | 126 | export function migratePatchProp( obj, prop, value, code ) { 127 | migratePatchPropInternal( obj, prop, value, code ); 128 | } 129 | 130 | // The value of the "Func" APIs is that for method we want to allow 131 | // checking for the method existence without triggering a warning. 132 | // For other deprecated properties, we do need to warn on access. 133 | function migratePatchFuncInternal( 134 | obj, prop, newFunc, code, msg, migrateMessageFn 135 | ) { 136 | 137 | function wrappedNewFunc() { 138 | 139 | // If `msg` not provided, do not warn; more sophisticated warnings 140 | // logic is most likely embedded in `newFunc`, in that case here 141 | // we just care about the logic choosing the proper implementation 142 | // based on whether the patch is disabled or not. 143 | if ( msg ) { 144 | migrateMessageFn( code, msg ); 145 | } 146 | 147 | return newFunc.apply( this, arguments ); 148 | } 149 | 150 | migratePatchPropInternal( obj, prop, wrappedNewFunc, code ); 151 | } 152 | 153 | export function migratePatchAndWarnFunc( obj, prop, newFunc, code, msg ) { 154 | if ( !msg ) { 155 | throw new Error( "No warning message provided" ); 156 | } 157 | return migratePatchFuncInternal( obj, prop, newFunc, code, msg, migrateWarn ); 158 | } 159 | 160 | export function migratePatchAndInfoFunc( obj, prop, newFunc, code, msg ) { 161 | if ( !msg ) { 162 | throw new Error( "No info message provided" ); 163 | } 164 | return migratePatchFuncInternal( obj, prop, newFunc, code, msg, migrateInfo ); 165 | } 166 | 167 | export function migratePatchFunc( obj, prop, newFunc, code ) { 168 | return migratePatchFuncInternal( obj, prop, newFunc, code ); 169 | } 170 | 171 | if ( window.document.compatMode === "BackCompat" ) { 172 | 173 | // jQuery has never supported or tested Quirks Mode 174 | migrateWarn( "quirks", "jQuery is not compatible with Quirks Mode" ); 175 | } 176 | -------------------------------------------------------------------------------- /src/migrate.js: -------------------------------------------------------------------------------- 1 | import "./version.js"; 2 | import "./compareVersions.js"; 3 | import "./main.js"; 4 | import "./jquery/core.js"; 5 | import "./jquery/selector.js"; 6 | import "./jquery/ajax.js"; 7 | import "./jquery/attributes.js"; 8 | import "./jquery/css.js"; 9 | import "./jquery/data.js"; 10 | import "./jquery/effects.js"; 11 | import "./jquery/event.js"; 12 | import "./jquery/manipulation.js"; 13 | import "./jquery/deferred.js"; 14 | -------------------------------------------------------------------------------- /src/migratemute.js: -------------------------------------------------------------------------------- 1 | // Included only in the minified build, via Uglify2 2 | // Only turn warnings off if not already overridden 3 | if ( typeof jQuery.migrateMute === "undefined" ) { 4 | jQuery.migrateMute = true; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import { migrateWarn } from "./main.js"; 2 | 3 | export function camelCase( string ) { 4 | return string.replace( /-([a-z])/g, function( _, letter ) { 5 | return letter.toUpperCase(); 6 | } ); 7 | } 8 | 9 | // Make `object` inherit from `Object.prototype` via an additional object 10 | // in between; that intermediate object proxies properties 11 | // to `Object.prototype`, warning about their usage first. 12 | export function patchProto( object, options ) { 13 | var i, 14 | warningId = options.warningId, 15 | apiName = options.apiName, 16 | 17 | // `Object.prototype` keys are not enumerable so list the 18 | // official ones here. An alternative would be wrapping 19 | // objects with a Proxy but that creates additional issues 20 | // like breaking object identity on subsequent calls. 21 | objProtoKeys = [ 22 | "__proto__", 23 | "__defineGetter__", 24 | "__defineSetter__", 25 | "__lookupGetter__", 26 | "__lookupSetter__", 27 | "hasOwnProperty", 28 | "isPrototypeOf", 29 | "propertyIsEnumerable", 30 | "toLocaleString", 31 | "toString", 32 | "valueOf" 33 | ], 34 | 35 | // Use a null prototype at the beginning so that we can define our 36 | // `__proto__` getter & setter. We'll reset the prototype afterward. 37 | intermediateObj = Object.create( null ); 38 | 39 | for ( i = 0; i < objProtoKeys.length; i++ ) { 40 | ( function( key ) { 41 | Object.defineProperty( intermediateObj, key, { 42 | get: function() { 43 | migrateWarn( warningId, 44 | "Accessing properties from " + apiName + 45 | " inherited from Object.prototype is removed" ); 46 | return ( key + "__cache" ) in intermediateObj ? 47 | intermediateObj[ key + "__cache" ] : 48 | Object.prototype[ key ]; 49 | }, 50 | set: function( value ) { 51 | migrateWarn( warningId, 52 | "Setting properties from " + apiName + 53 | " inherited from Object.prototype is removed" ); 54 | intermediateObj[ key + "__cache" ] = value; 55 | } 56 | } ); 57 | } )( objProtoKeys[ i ] ); 58 | } 59 | 60 | Object.setPrototypeOf( intermediateObj, Object.prototype ); 61 | Object.setPrototypeOf( object, intermediateObj ); 62 | 63 | return object; 64 | } 65 | -------------------------------------------------------------------------------- /src/version.js: -------------------------------------------------------------------------------- 1 | 2 | jQuery.migrateVersion = "3.5.3-pre"; 3 | -------------------------------------------------------------------------------- /src/wrapper-esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Migrate - v@VERSION - @DATE 3 | * Copyright OpenJS Foundation and other contributors 4 | */ 5 | import $ from "jquery"; 6 | 7 | // For ECMAScript module environments where a proper `window` 8 | // is present, execute the factory and get jQuery. 9 | function jQueryFactory( jQuery, window ) { 10 | 11 | // @CODE 12 | // build.js inserts compiled jQuery here 13 | 14 | return jQuery; 15 | } 16 | 17 | var jQuery = jQueryFactory( $, window ); 18 | 19 | export { jQuery, jQuery as $ }; 20 | 21 | export default jQuery; 22 | -------------------------------------------------------------------------------- /src/wrapper.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Migrate - v@VERSION - @DATE 3 | * Copyright OpenJS Foundation and other contributors 4 | */ 5 | ( function( factory ) { 6 | "use strict"; 7 | 8 | if ( typeof define === "function" && define.amd ) { 9 | 10 | // AMD. Register as an anonymous module. 11 | define( [ "jquery" ], function( jQuery ) { 12 | return factory( jQuery, window ); 13 | } ); 14 | } else if ( typeof module === "object" && module.exports ) { 15 | 16 | // Node/CommonJS 17 | // eslint-disable-next-line no-undef 18 | module.exports = factory( require( "jquery" ), window ); 19 | } else { 20 | 21 | // Browser globals 22 | factory( jQuery, window ); 23 | } 24 | } )( function( jQuery, window ) { 25 | "use strict"; 26 | 27 | // @CODE 28 | // build.js inserts compiled jQuery here 29 | 30 | return jQuery; 31 | } ); 32 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/lib/run-rollup.js: -------------------------------------------------------------------------------- 1 | import { rollup } from "rollup"; 2 | 3 | import { loadConfigFile } from "rollup/loadConfigFile"; 4 | import path from "node:path"; 5 | import { fileURLToPath } from "node:url"; 6 | 7 | const dirname = path.dirname( fileURLToPath( import.meta.url ) ); 8 | const pureEsmConfigPath = path.resolve( 9 | dirname, "..", "rollup-pure-esm.config.js" ); 10 | const commonJSConfigPath = path.resolve( 11 | dirname, "..", "rollup-commonjs.config.js" ); 12 | 13 | // See https://rollupjs.org/javascript-api/#programmatically-loading-a-config-file 14 | async function runRollup( name, configPath ) { 15 | 16 | console.log( `Running Rollup, version: ${ name }` ); 17 | 18 | // options is an array of "inputOptions" objects with an additional 19 | // "output" property that contains an array of "outputOptions". 20 | // We generate a single output so the array only has one element. 21 | const { 22 | options: [ optionsObj ], 23 | warnings 24 | } = await loadConfigFile( configPath, {} ); 25 | 26 | // "warnings" wraps the default `onwarn` handler passed by the CLI. 27 | // This prints all warnings up to this point: 28 | warnings.flush(); 29 | 30 | const bundle = await rollup( optionsObj ); 31 | await Promise.all( optionsObj.output.map( bundle.write ) ); 32 | 33 | console.log( `Build completed: Rollup, version: ${ name }` ); 34 | } 35 | 36 | export async function runRollupPureEsm() { 37 | await runRollup( "pure ESM", pureEsmConfigPath ); 38 | } 39 | 40 | export async function runRollupEsmAndCommonJs() { 41 | await runRollup( "ESM + CommonJS", commonJSConfigPath ); 42 | } 43 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/lib/run-webpack.js: -------------------------------------------------------------------------------- 1 | import webpack from "webpack"; 2 | 3 | // See https://webpack.js.org/api/node/#webpack 4 | export async function runWebpack() { 5 | return new Promise( async( resolve, reject ) => { 6 | console.log( "Running Webpack" ); 7 | 8 | const { default: config } = await import( "../webpack.config.cjs" ); 9 | 10 | webpack( config, ( err, stats ) => { 11 | if ( err || stats.hasErrors() ) { 12 | console.error( "Errors detected during Webpack compilation" ); 13 | reject( err ); 14 | return; 15 | } 16 | 17 | console.log( "Build completed: Webpack" ); 18 | resolve(); 19 | } ); 20 | } ); 21 | } 22 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/lib/utils.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | import path from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const dirname = path.dirname( fileURLToPath( import.meta.url ) ); 6 | const TMP_BUNDLERS_DIR = path.resolve( dirname, "..", "tmp" ); 7 | 8 | export async function cleanTmpBundlersDir() { 9 | await fs.rm( TMP_BUNDLERS_DIR, { 10 | force: true, 11 | recursive: true 12 | } ); 13 | } 14 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/rollup-commonjs.config.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import { fileURLToPath } from "node:url"; 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | import commonjs from "@rollup/plugin-commonjs"; 5 | 6 | const dirname = path.dirname( fileURLToPath( import.meta.url ) ); 7 | 8 | export default { 9 | input: `${ dirname }/src-esm-commonjs/main.js`, 10 | output: { 11 | dir: `${ dirname }/tmp/rollup-commonjs`, 12 | format: "iife", 13 | sourcemap: true 14 | }, 15 | plugins: [ 16 | resolve(), 17 | commonjs() 18 | ] 19 | }; 20 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/rollup-pure-esm.config.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import { fileURLToPath } from "node:url"; 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | 5 | const dirname = path.dirname( fileURLToPath( import.meta.url ) ); 6 | 7 | export default { 8 | input: `${ dirname }/src-pure-esm/main.js`, 9 | output: { 10 | dir: `${ dirname }/tmp/rollup-pure-esm`, 11 | format: "iife", 12 | sourcemap: true 13 | }, 14 | plugins: [ 15 | resolve() 16 | ] 17 | }; 18 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/run-jsdom-tests.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | import jsdom, { JSDOM } from "jsdom"; 3 | import path from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | import { runRollupEsmAndCommonJs, runRollupPureEsm } from "./lib/run-rollup.js"; 6 | import { runWebpack } from "./lib/run-webpack.js"; 7 | import { cleanTmpBundlersDir } from "./lib/utils.js"; 8 | 9 | const dirname = path.dirname( fileURLToPath( import.meta.url ) ); 10 | 11 | async function runJSDOMTest( { title, folder } ) { 12 | console.log( "Running bundlers tests:", title ); 13 | 14 | const template = await fs.readFile( `${ dirname }/test.html`, "utf-8" ); 15 | const scriptSource = await fs.readFile( 16 | `${ dirname }/tmp/${ folder }/main.js`, "utf-8" ); 17 | 18 | const html = template 19 | .replace( /@TITLE\b/, () => title ) 20 | .replace( /@SCRIPT\b/, () => scriptSource ); 21 | 22 | const virtualConsole = new jsdom.VirtualConsole(); 23 | virtualConsole.sendTo( console ); 24 | virtualConsole.on( "assert", ( success ) => { 25 | if ( !success ) { 26 | process.exitCode = 1; 27 | } 28 | } ); 29 | 30 | new JSDOM( html, { 31 | resources: "usable", 32 | runScripts: "dangerously", 33 | virtualConsole 34 | } ); 35 | 36 | if ( process.exitCode === 0 || process.exitCode == null ) { 37 | console.log( "Bundlers tests passed for:", title ); 38 | } else { 39 | console.error( "Bundlers tests failed for:", title ); 40 | } 41 | } 42 | 43 | async function buildAndTest() { 44 | await cleanTmpBundlersDir(); 45 | 46 | await Promise.all( [ 47 | runRollupPureEsm(), 48 | runRollupEsmAndCommonJs(), 49 | runWebpack() 50 | ] ); 51 | 52 | await Promise.all( [ 53 | runJSDOMTest( { 54 | title: "Rollup with pure ESM setup", 55 | folder: "rollup-pure-esm" 56 | } ), 57 | 58 | runJSDOMTest( { 59 | title: "Rollup with ESM + CommonJS", 60 | folder: "rollup-commonjs" 61 | } ), 62 | 63 | runJSDOMTest( { 64 | title: "Webpack", 65 | folder: "webpack" 66 | } ) 67 | ] ); 68 | 69 | // The directory won't be cleaned in case of failures; this may aid debugging. 70 | await cleanTmpBundlersDir(); 71 | } 72 | 73 | await buildAndTest(); 74 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/src-esm-commonjs/jquery-migrate-require.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const $ = require( "jquery-migrate" ); 4 | 5 | module.exports.$required = $; 6 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/src-esm-commonjs/main.js: -------------------------------------------------------------------------------- 1 | import { $ as $imported } from "jquery-migrate"; 2 | 3 | import { $required } from "./jquery-migrate-require.cjs"; 4 | 5 | console.assert( $required === $imported, 6 | "Only one copy of full jQuery should exist" ); 7 | console.assert( /^jQuery/.test( $imported.expando ), 8 | "jQuery.expando should be detected on full jQuery" ); 9 | console.assert( typeof $imported.migrateVersion === "string" && $imported.migrateVersion.length > 0, 10 | "jQuery.migrateVersion was not detected, the jQuery Migrate bootstrap process has failed" ); 11 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/src-pure-esm/main.js: -------------------------------------------------------------------------------- 1 | import { $ } from "jquery-migrate"; 2 | 3 | console.assert( /^jQuery/.test( $.expando ), 4 | "jQuery.expando should be detected on full jQuery" ); 5 | console.assert( typeof $.migrateVersion === "string" && $.migrateVersion.length > 0, 6 | "jQuery.migrateVersion was not detected, the jQuery Migrate bootstrap process has failed" ); 7 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bundlers tests: @TITLE 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/bundler_smoke_tests/webpack.config.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | entry: `${ __dirname }/src-esm-commonjs/main.js`, 5 | output: { 6 | filename: "main.js", 7 | path: `${ __dirname }/tmp/webpack` 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/data/1x1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jquery/jquery-migrate/1c51545119309249cca5952d1ada1df822ec230f/test/data/1x1.jpg -------------------------------------------------------------------------------- /test/data/ajax-jsonp-callback-name.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Migrate test for re-using JSONP callback name with `dataType: "json"` 6 | 7 | 8 | 9 | 12 | 13 | 17 | 83 | 84 | 85 |

jQuery Migrate test for re-using JSONP callback name

86 | 87 | 88 | -------------------------------------------------------------------------------- /test/data/compareVersions.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Returns 0 if v1 == v2, -1 if v1 < v2, 1 if v1 > v2 4 | window.compareVersions = function compareVersions( v1, v2 ) { 5 | var i, 6 | rVersionParts = /^(\d+)\.(\d+)\.(\d+)/, 7 | v1p = rVersionParts.exec( v1 ) || [ ], 8 | v2p = rVersionParts.exec( v2 ) || [ ]; 9 | 10 | for ( i = 1; i <= 3; i++ ) { 11 | if ( +v1p[ i ] > +v2p[ i ] ) { 12 | return 1; 13 | } 14 | if ( +v1p[ i ] < +v2p[ i ] ) { 15 | return -1; 16 | } 17 | } 18 | return 0; 19 | }; 20 | 21 | window.jQueryVersionSince = function jQueryVersionSince( version ) { 22 | return compareVersions( jQuery.fn.jquery, version ) >= 0; 23 | }; 24 | -------------------------------------------------------------------------------- /test/data/core-jquery3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Migrate old-jQuery initialization test 6 | 7 | 8 | 9 | 10 | 11 | 15 | 19 | 20 | 21 |

jQuery Migrate old-jQuery initialization test

22 | 23 | 24 | -------------------------------------------------------------------------------- /test/data/empty.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jquery/jquery-migrate/1c51545119309249cca5952d1ada1df822ec230f/test/data/empty.json -------------------------------------------------------------------------------- /test/data/event-lateload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Migrate late load event binding test 6 | 7 | 8 | 9 | 10 | 13 | 14 | 18 | 37 | 38 | 39 |

jQuery Migrate late load event binding test

40 | 41 | 42 | -------------------------------------------------------------------------------- /test/data/event-ready.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Migrate `ready` event iframe test 6 | 7 | 8 | 9 | 12 | 13 | 17 | 23 | 24 | 25 |

jQuery Migrate `ready` event iframe test

26 | 27 | 28 | -------------------------------------------------------------------------------- /test/data/iframeTest.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* 4 | * Iframe-based unit tests support 5 | * Load this file immediately after jQuery, before jQuery Migrate 6 | */ 7 | 8 | // Capture output so the test in the parent window can inspect it, and so the 9 | // initialization for the iframe doesn't show another Migrate startup header. 10 | var logOutput = ""; 11 | 12 | window.console.log = function() { 13 | logOutput += Array.prototype.join.call( arguments, " " ) + "\n"; 14 | }; 15 | 16 | // Callback gets ( jQuery, window, document, log [, startIframe args ] ) 17 | window.startIframeTest = function() { 18 | var args = Array.prototype.slice.call( arguments ); 19 | 20 | // Note: jQuery may be undefined if page did not load it 21 | args.unshift( window.jQuery, window, document, logOutput ); 22 | window.parent.TestManager.iframeCallback.apply( null, args ); 23 | }; 24 | -------------------------------------------------------------------------------- /test/data/jsonpScript.js: -------------------------------------------------------------------------------- 1 | /* global customJsonpCallback */ 2 | 3 | "use strict"; 4 | 5 | customJsonpCallback( { answer: 42 } ); 6 | -------------------------------------------------------------------------------- /test/data/null.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /test/data/qunit-start.js: -------------------------------------------------------------------------------- 1 | QUnit.start(); 2 | -------------------------------------------------------------------------------- /test/data/simple.json: -------------------------------------------------------------------------------- 1 | {"foo":"bar","gibson":42} -------------------------------------------------------------------------------- /test/data/test-utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* exported expectMessage, expectNoMessage */ 4 | 5 | window.expectMessage = function expectMessage( assert, name, expected, fn ) { 6 | var result; 7 | if ( !fn ) { 8 | fn = expected; 9 | expected = null; 10 | } 11 | jQuery.migrateReset(); 12 | result = fn(); 13 | 14 | function check() { 15 | 16 | // Special-case for 0 messages expected 17 | if ( expected === 0 ) { 18 | assert.deepEqual( jQuery.migrateMessages, [], name + ": did not message" ); 19 | 20 | // Simple numeric equality assertion for messages matching an explicit count 21 | } else if ( expected && jQuery.migrateMessages.length === expected ) { 22 | assert.equal( jQuery.migrateMessages.length, expected, name + ": messaged" ); 23 | 24 | // Simple ok assertion when we saw at least one message and weren't looking for an explict count 25 | } else if ( !expected && jQuery.migrateMessages.length ) { 26 | assert.ok( true, name + ": messaged" ); 27 | 28 | // Failure; use deepEqual to show the messages that *were* generated and the expectation 29 | } else { 30 | assert.deepEqual( jQuery.migrateMessages, 31 | "", name + ": messaged" 32 | ); 33 | } 34 | } 35 | 36 | if ( result && result.then ) { 37 | return Promise.resolve( 38 | result.then( function() { 39 | check(); 40 | } ) 41 | ); 42 | } else { 43 | check(); 44 | return Promise.resolve(); 45 | } 46 | }; 47 | 48 | window.expectNoMessage = function expectNoMessage( assert, name, expected, fn ) { 49 | 50 | // Expected is present only for signature compatibility with expectMessage 51 | return expectMessage( assert, name, 0, fn || expected ); 52 | }; 53 | -------------------------------------------------------------------------------- /test/data/testinit.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | ( typeof global != "undefined" ? global : window ).TestManager = { 4 | 5 | /** 6 | * Load a version of a file based on URL parameters. 7 | * 8 | * dev Uncompressed development version: source files in the project /dist dir 9 | * esmodules Non-combined dev version: source files from the project /src dir 10 | * min Minified version in the project /dist dir 11 | * VER Version from code.jquery.com, e.g.: git, 4.0.0.min or 4.0.0-beta.2 12 | * else Full or relative path to be used for script src 13 | */ 14 | loadProject: function( projectName, defaultVersion, isSelf ) { 15 | var file, 16 | urlTag = this.projects[ projectName ].urlTag, 17 | matcher = new RegExp( "\\b" + urlTag + "=([^&]+)" ), 18 | projectRoot = this.baseURL + ( isSelf ? "../.." : "../../../" + projectName ), 19 | version = ( matcher.exec( document.location.search ) || {} )[ 1 ] || defaultVersion; 20 | 21 | // The esmodules mode requires the browser to support ES modules 22 | // so it won't run in IE. 23 | if ( version === "esmodules" ) { 24 | 25 | // This is the main source file that imports all the others. 26 | file = projectRoot + "/src/migrate.js"; 27 | } else if ( version === "dev" ) { 28 | file = projectRoot + "/dist/" + projectName + ".js"; 29 | } else if ( version === "min" ) { 30 | file = projectRoot + "/dist/" + projectName + ".min.js"; 31 | } else if ( version.indexOf( "git" ) === 0 ) { 32 | file = "https://releases.jquery.com/git/" + projectName + "-" + version + ".js"; 33 | } else if ( /^[\w\.\-]+$/.test( version ) ) { 34 | file = "https://code.jquery.com/" + projectName + "-" + version + ".js"; 35 | } else { 36 | file = version; 37 | } 38 | this.loaded.push( { 39 | projectName: projectName, 40 | tag: version, 41 | file: file 42 | } ); 43 | 44 | if ( version === "esmodules" ) { 45 | document.write( "" ); 46 | } else { 47 | document.write( "" ); 48 | } 49 | }, 50 | 51 | /** 52 | * Load jQuery Migrate tests. In esmodules mode it loads all tests as 53 | * ES modules so that they get executed in the correct order. 54 | */ 55 | loadTests: function() { 56 | var esmodules = QUnit.config.plugin === "esmodules" || 57 | QUnit.urlParams.plugin === "esmodules", 58 | testFiles = [ 59 | "data/test-utils.js", 60 | "unit/migrate.js", 61 | "unit/jquery/core.js", 62 | "unit/jquery/ajax.js", 63 | "unit/jquery/attributes.js", 64 | "unit/jquery/css.js", 65 | "unit/jquery/data.js", 66 | "unit/jquery/deferred.js", 67 | "unit/jquery/effects.js", 68 | "unit/jquery/event.js", 69 | "unit/jquery/manipulation.js", 70 | "unit/jquery/selector.js" 71 | ]; 72 | 73 | testFiles.forEach( function( testFile ) { 74 | document.write( "" ); 76 | } ); 77 | 78 | document.write( "" ); 81 | 82 | }, 83 | 84 | /** 85 | * Iframe tests that require setup not covered in the standard unit test 86 | * 87 | * Note that options passed into the standard unit tests will also be passed to 88 | * the iframe, but the iframe html page is responsible for processing them 89 | * as appropriate (for example by calling TestManager.loadProject) 90 | */ 91 | runIframeTest: function( title, url, func ) { 92 | var that = this, 93 | esmodules = QUnit.config.plugin === "esmodules" || 94 | QUnit.urlParams.plugin === "esmodules"; 95 | 96 | // Skip iframe tests in esmodules mode as that mode is not compatible with how 97 | // they are written. 98 | if ( esmodules ) { 99 | QUnit.skip( title ); 100 | return; 101 | } 102 | 103 | QUnit.test( title, function( assert ) { 104 | var iframe, 105 | query = window.location.search.slice( 1 ), 106 | done = assert.async(); 107 | 108 | that.iframeCallback = function() { 109 | var args = Array.prototype.slice.call( arguments ); 110 | 111 | args.unshift( assert ); 112 | 113 | setTimeout( function() { 114 | that.iframeCallback = undefined; 115 | 116 | func.apply( this, args ); 117 | func = function() {}; 118 | iframe.remove(); 119 | 120 | done(); 121 | } ); 122 | }; 123 | iframe = jQuery( "
" ) 124 | .css( { position: "absolute", width: "500px", left: "-600px" } ) 125 | .append( jQuery( "