├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github ├── CONTRIBUTING.md └── workflows │ ├── node-aught.yml │ ├── node-pretest.yml │ ├── node-tens.yml │ ├── publish-on-tag.yml │ ├── rebase.yml │ └── require-allow-edits.yml ├── .gitignore ├── .npmrc ├── .nycrc ├── CHANGELOG.md ├── LICENSE-MIT.txt ├── README.md ├── auto.js ├── implementation.js ├── index.js ├── package.json ├── polyfill.js ├── shim.js └── test ├── index.js ├── shimmed.js └── tests.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | insert_final_newline = true 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | 4 | "extends": "@ljharb", 5 | 6 | "rules": { 7 | "id-length": "off", 8 | "new-cap": ["error", { 9 | "capIsNewExceptions": [ 10 | "IsRegExp", 11 | "RequireObjectCoercible", 12 | "StringCharCodeAt", 13 | "ToIntegerOrInfinity", 14 | "ToString", 15 | ], 16 | }], 17 | }, 18 | 19 | "overrides": [ 20 | { 21 | "files": "test/**/*", 22 | "rules": { 23 | "max-lines-per-function": "off", 24 | }, 25 | }, 26 | ], 27 | } 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## For maintainers 4 | 5 | ### How to publish a new release 6 | 7 | 1. On the `main` branch, bump the version number in `package.json`: 8 | 9 | ```sh 10 | npm version patch 11 | ``` 12 | 13 | Instead of `patch`, use `minor` or `major` [as needed](https://semver.org/). 14 | 15 | Note that this produces a Git commit + tag. 16 | 17 | 1. Push the release commit and tag: 18 | 19 | ```sh 20 | git push && git push --tags 21 | ``` 22 | 23 | Our CI then automatically publishes the new release to npm. 24 | -------------------------------------------------------------------------------- /.github/workflows/node-aught.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js < 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node.yml@main 8 | with: 9 | range: '< 10' 10 | type: minors 11 | command: npm run tests-only 12 | 13 | node: 14 | name: 'node < 10' 15 | needs: [tests] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - run: 'echo tests completed' 19 | -------------------------------------------------------------------------------- /.github/workflows/node-pretest.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: pretest/posttest' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/pretest.yml@main 8 | -------------------------------------------------------------------------------- /.github/workflows/node-tens.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js >= 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node.yml@main 8 | with: 9 | range: '>= 10' 10 | type: minors 11 | command: npm run tests-only 12 | 13 | node: 14 | name: 'node >= 10' 15 | needs: [tests] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - run: 'echo tests completed' 19 | -------------------------------------------------------------------------------- /.github/workflows/publish-on-tag.yml: -------------------------------------------------------------------------------- 1 | name: publish-on-tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | if: ${{ !github.event.repository.fork }} 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - uses: ljharb/actions/node/install@main 16 | - name: Test 17 | run: npm test 18 | - name: Publish 19 | env: 20 | NPM_TOKEN: ${{secrets.NPM_TOKEN}} 21 | run: | 22 | npm config set registry 'https://wombat-dressing-room.appspot.com/' 23 | npm config set '//wombat-dressing-room.appspot.com/:_authToken' '${NPM_TOKEN}' 24 | npm publish 25 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | 3 | on: [pull_request_target] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | tests: 10 | uses: ljharb/actions/.github/workflows/pretest.yml@main 11 | -------------------------------------------------------------------------------- /.github/workflows/require-allow-edits.yml: -------------------------------------------------------------------------------- 1 | name: Require “Allow Edits” 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Require “Allow Edits”" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: ljharb/require-allow-edits@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Coverage report 2 | coverage 3 | .nyc_output/ 4 | 5 | # Installed npm modules 6 | node_modules 7 | package-lock.json 8 | 9 | # Folder view configuration files 10 | .DS_Store 11 | Desktop.ini 12 | 13 | # Thumbnail cache files 14 | ._* 15 | Thumbs.db 16 | 17 | # Files that might appear on external disks 18 | .Spotlight-V100 19 | .Trashes 20 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | allow-same-version=true 3 | message=v%s 4 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "check-coverage": false, 4 | "reporter": ["text-summary", "text", "html", "json"], 5 | "lines": 86, 6 | "statements": 85.93, 7 | "functions": 82.43, 8 | "branches": 76.06, 9 | "exclude": [ 10 | "coverage", 11 | "test" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [v1.0.2](https://github.com/mathiasbynens/String.prototype.endsWith/compare/v1.0.1...v1.0.2) - 2024-03-18 9 | 10 | ### Commits 11 | 12 | - [Deps] update `call-bind`, `define-properties`, `es-abstract` [`38e22e6`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/38e22e69450af8a3f4ffa4f50a836b78a6204ffc) 13 | - [eslint] fix linting errors [`44cfb77`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/44cfb77f14f5ad2e8be69d6eb41d90c510d07c09) 14 | - [Dev Deps] update `aud`, `function-bind`, `tape` [`6f50b14`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/6f50b145b90c2a7faee01a924c26e8b9b487fc7d) 15 | - [Refactor] use `es-object-atoms` instead of `es-abstract` when possible [`42aa444`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/42aa4443737685d4fd2229cef1ecd03f83e8018d) 16 | - [meta] add missing `engines.node` [`bccb5bb`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/bccb5bb26e7e5c1a874f5c2558af8d8f7b7ede7f) 17 | 18 | ## [v1.0.1](https://github.com/mathiasbynens/String.prototype.endsWith/compare/v1.0.0...v1.0.1) - 2023-09-11 19 | 20 | ### Commits 21 | 22 | - [eslint] add eslint [`fb8cc46`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/fb8cc4665fd53eb2c91bb9da9cb4571c5a61f949) 23 | - [Tests] Switch from travis to GH actions [`b35ea44`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/b35ea44919003637de2bc13ee9a44b0e1247ba81) 24 | - [meta] add `auto-changelog` [`b6da653`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/b6da653669763483e3653fe3c62f7f699c3df535) 25 | - Set up automated `npm publish` [`b9be2b4`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/b9be2b462ad6c125dbe0de55d8f1d53891bcd4d4) 26 | - [readme] move maintainer instructions to CONTRIBUTING.md [`46da5f5`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/46da5f56fc6cb50915cd90f5f50cabb22b2c4ade) 27 | - [readme] update badges [`64e2a03`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/64e2a035f9fc8b086e3f1320771fe2f8996a287d) 28 | - [Deps] update `define-properties`, `es-abstract`; add `call-bind` [`185ac54`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/185ac546e6cc4840b185f30fda48c9fd12fac313) 29 | - [Tests] switch from istanbul to nyc [`8d33676`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/8d336769e74e1083c9caa353b55a6610a01ef33c) 30 | - [actions] use reusable rebase action [`a53f174`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/a53f17412eacf0d6161efe3a2f87938c474c62df) 31 | - [actions] fix publish workflow [`84d19c3`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/84d19c3c019ed8b727ec8a337144a6e24cc9cec6) 32 | - [Dev Deps] update `@es-shims/api`, `functions-have-names`, `tape` [`9543e15`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/9543e15819960a7823ee8117ef13cd76bd652f29) 33 | - [meta] remove broken `getPolyfill` entry point; add correct `polyfill` entry point [`0d5242c`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/0d5242c56c72c0f28961eb859be107eca0ea2d80) 34 | - [Tests] rename `tests` to `test` [`5e344d8`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/5e344d8a15c9ca4c6bf5097bb0c3f0598390ea43) 35 | - [Tests] add `aud` via `npm run posttest` [`699cf8c`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/699cf8ce416bb8b3904a476e485129d1d7d242a5) 36 | 37 | ## [v1.0.0](https://github.com/mathiasbynens/String.prototype.endsWith/compare/v0.2.0...v1.0.0) - 2020-05-21 38 | 39 | ### Commits 40 | 41 | - Merge pull request #9 from mathiasbynens/nr/es-shim-api [`272752d`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/272752d0bd32884fb6acb9bcf5e05f2e46c8310b) 42 | - Use `tape` in tests and run them on every Node.js version [`8030508`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/8030508ea2fcebf20ec3c1b7f30cf525a3d26185) 43 | - [breaking] Implement the es-shim-api interface [`c6eb152`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/c6eb1525f9a405c72c3f63d7fb3453c7b10f2ad6) 44 | - Use HTTPS URLs where possible [`a0eea7f`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/a0eea7f368c6f3f00a1f7a53f01f8c05db325e12) 45 | - Update readme [`15ba134`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/15ba134cabae6af361afc60865abce6bbe6c2482) 46 | - Test that it respects the es-shim-api interface [`1baf690`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/1baf69037e3b852fcc238a4bda85884a0a99b136) 47 | - Update repo config files [`e5b7310`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/e5b73100fe51f680f0207fc77f7d22682e46cd8d) 48 | - Improve error messages [`6853d40`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/6853d40f69ed13350deb2cd3007eb508407f7665) 49 | 50 | ## [v0.2.0](https://github.com/mathiasbynens/String.prototype.endsWith/compare/v0.1.0...v0.2.0) - 2014-04-10 51 | 52 | ### Fixed 53 | 54 | - Follow spec order [`#5`](https://github.com/mathiasbynens/String.prototype.endsWith/issues/5) 55 | 56 | ### Commits 57 | 58 | - Avoid IE8’s broken `Object.defineProperty` [`e27c8f2`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/e27c8f24a761e88792e5a850834e103cc01b5c3e) 59 | - Add more tests [`cc63a25`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/cc63a259d2d2814d28745ec6e543c8596879dbcd) 60 | - Make the `endsWith` property non-enumerable [`bffbff9`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/bffbff90911cabf88ff0541ad44205018b5e9ab8) 61 | - Avoid `lastIndexOf` [`bb6501c`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/bb6501c9fab6c2f380dc1a45a8efd024a0719bca) 62 | - Tag the v0.2.0 release [`ca2dddb`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/ca2dddb7e3a6f7fe8d996b02eec6ee908c958663) 63 | - README: Explicitly request SVG badges [`c20c83a`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/c20c83a5efadc594431de9ba4dcfb58786409007) 64 | - Perform RegExp check after `ToString(this)` [`f375ac0`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/f375ac092cc7f5e52b2f6216c9808c8f8e09de50) 65 | - README: Mention polyfills for similar functions [`960fccb`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/960fccb4d7139bd2361e8d91c74e7688074c5bb9) 66 | - Use `charCodeAt` instead of `charAt` [`bb1901f`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/bb1901f9173c5df8ca7444919cfbe18457ebfa3f) 67 | - Avoid `isNaN` [`dd38046`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/dd3804682838a24c10cb25371fc2b4a95f693d37) 68 | - README: Update Traceur link [`c34bff5`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/c34bff5eaecf484658ced6e291c08bf074975f23) 69 | - README: Update es6-shim link [`f51af73`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/f51af73a18afaef7b51264cb8cf81525d457c449) 70 | - Fix typo in test [`e10ff32`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/e10ff323b6a1b3489cfcd8776c3248a4db617ac4) 71 | - README: Update link [`c28d87c`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/c28d87cf41d32c27d52e1dc97cd02c3bf34199b4) 72 | - README: Note that Traceur now uses this polyfill [`2e2d880`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/2e2d880a24c42754937e5039ada1a9b938f2f16f) 73 | 74 | ## v0.1.0 - 2013-12-17 75 | 76 | ### Commits 77 | 78 | - Initial commit [`4bf8ebd`](https://github.com/mathiasbynens/String.prototype.endsWith/commit/4bf8ebdbc0e77cb7be77ca69dc5b29c734ed7e12) 79 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Copyright Mathias Bynens 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # string.prototype.endswith [![Version Badge][npm-version-svg]][package-url] 2 | 3 | [![dependency status][deps-svg]][deps-url] 4 | [![dev dependency status][dev-deps-svg]][dev-deps-url] 5 | [![License][license-image]][license-url] 6 | [![Downloads][downloads-image]][downloads-url] 7 | 8 | [![npm badge][npm-badge-png]][package-url] 9 | 10 | An ESnext spec-compliant `String.prototype.endsWith` shim/polyfill/replacement that works as far down as ES3. 11 | 12 | This package implements the [es-shim API](https://github.com/es-shims/api) interface. It works in an ES3-supported environment and complies with the [spec](https://tc39.es/ecma262/#sec-string.prototype.endswith). 13 | 14 | Because `String.prototype.endsWith` depends on a receiver (the `this` value), the main export takes the string to operate on as the first argument. 15 | 16 | Other polyfills for `String.prototype.endsWith` are available: 17 | 18 | * by [Paul Miller](http://paulmillr.com/) (~~fails some tests: [1](https://github.com/paulmillr/es6-shim/issues/168), [2](https://github.com/paulmillr/es6-shim/issues/175)~~ passes all tests) 19 | * by Google (~~[fails a lot of tests](https://github.com/google/traceur-compiler/pull/555)~~ now uses this polyfill and passes all tests) 20 | 21 | ## Installation 22 | 23 | Via [npm](http://npmjs.org/): 24 | 25 | ```bash 26 | npm install string.prototype.endswith 27 | ``` 28 | 29 | Then, in [Node.js](http://nodejs.org/): 30 | 31 | ```js 32 | var endsWith = require('string.prototype.endswith'); 33 | ``` 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | > **NOTE**: It's recommended that you install this module using a package manager 40 | > such as `npm`, because loading multiple polyfills from a CDN (such as `bundle.run`) 41 | > will lead to duplicated code. 42 | 43 | ## Notes 44 | 45 | Polyfills + test suites for [`String.prototype.startsWith`](https://mths.be/startswith) and [`String.prototype.contains`](https://mths.be/contains) are available, too. 46 | 47 | ## Author 48 | 49 | | [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | 50 | |---| 51 | | [Mathias Bynens](https://mathiasbynens.be/) | 52 | 53 | ## License 54 | 55 | This polyfill is available under the [MIT](https://mths.be/mit) license. 56 | 57 | ## Tests 58 | Simply clone the repo, `npm install`, and run `npm test` 59 | 60 | [package-url]: https://npmjs.org/package/string.prototype.endswith 61 | [npm-version-svg]: https://versionbadg.es/es-shims/String.prototype.endsWith.svg 62 | [deps-svg]: https://david-dm.org/es-shims/String.prototype.endsWith.svg 63 | [deps-url]: https://david-dm.org/es-shims/String.prototype.endsWith 64 | [dev-deps-svg]: https://david-dm.org/es-shims/String.prototype.endsWith/dev-status.svg 65 | [dev-deps-url]: https://david-dm.org/es-shims/String.prototype.endsWith#info=devDependencies 66 | [npm-badge-png]: https://nodei.co/npm/string.prototype.endswith.png?downloads=true&stars=true 67 | [license-image]: https://img.shields.io/npm/l/string.prototype.endswith.svg 68 | [license-url]: LICENSE 69 | [downloads-image]: https://img.shields.io/npm/dm/string.prototype.endswith.svg 70 | [downloads-url]: https://npm-stat.com/charts.html?package=string.prototype.endswith 71 | -------------------------------------------------------------------------------- /auto.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./shim')(); 4 | -------------------------------------------------------------------------------- /implementation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var RequireObjectCoercible = require('es-object-atoms/RequireObjectCoercible'); 4 | var ToString = require('es-abstract/2024/ToString'); 5 | var IsRegExp = require('es-abstract/2024/IsRegExp'); 6 | var ToIntegerOrInfinity = require('es-abstract/2024/ToIntegerOrInfinity'); 7 | 8 | var callBound = require('call-bind/callBound'); 9 | 10 | var StringCharCodeAt = callBound('String.prototype.charCodeAt'); 11 | var max = Math.max; 12 | var min = Math.min; 13 | 14 | module.exports = function endsWith(searchString) { 15 | var O = RequireObjectCoercible(this); 16 | var S = ToString(O); 17 | if (IsRegExp(searchString)) { 18 | throw new TypeError('Argument to String.prototype.endsWith cannot be a RegExp'); 19 | } 20 | var searchStr = ToString(searchString); 21 | 22 | var len = S.length; 23 | var searchLength = searchStr.length; 24 | var endPosition = arguments.length > 1 ? arguments[1] : undefined; 25 | var pos = endPosition === undefined ? len : ToIntegerOrInfinity(endPosition); 26 | 27 | var end = min(max(pos, 0), len); 28 | var start = end - searchLength; 29 | if (start < 0) { 30 | return false; 31 | } 32 | var index = -1; 33 | while (++index < searchLength) { // eslint-disable-line no-plusplus 34 | if (StringCharCodeAt(S, start + index) !== StringCharCodeAt(searchStr, index)) { 35 | return false; 36 | } 37 | } 38 | return true; 39 | }; 40 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/endswith v1.0.0 by @mathias */ 2 | 3 | 'use strict'; 4 | 5 | var callBind = require('call-bind'); 6 | var define = require('define-properties'); 7 | 8 | var implementation = require('./implementation'); 9 | var getPolyfill = require('./polyfill'); 10 | var shim = require('./shim'); 11 | 12 | var boundEndsWith = callBind(getPolyfill()); 13 | 14 | define(boundEndsWith, { 15 | getPolyfill: getPolyfill, 16 | implementation: implementation, 17 | shim: shim 18 | }); 19 | 20 | module.exports = boundEndsWith; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "string.prototype.endswith", 3 | "version": "1.0.2", 4 | "description": "A robust & optimized `String.prototype.endsWith` polyfill, based on the ECMAScript 6 specification.", 5 | "homepage": "https://mths.be/endswith", 6 | "main": "index.js", 7 | "exports": { 8 | ".": "./index.js", 9 | "./auto": "./auto.js", 10 | "./polyfill": "./polyfill.js", 11 | "./implementation": "./implementation.js", 12 | "./shim": "./shim.js", 13 | "./package.json": "./package.json" 14 | }, 15 | "keywords": [ 16 | "string", 17 | "endswith", 18 | "es6", 19 | "ecmascript", 20 | "polyfill" 21 | ], 22 | "license": "MIT", 23 | "author": { 24 | "name": "Mathias Bynens", 25 | "url": "https://mathiasbynens.be/" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/mathiasbynens/String.prototype.endsWith.git" 30 | }, 31 | "bugs": "https://github.com/mathiasbynens/String.prototype.endsWith/issues", 32 | "scripts": { 33 | "lint": "eslint --ext=js,mjs .", 34 | "postlint": "es-shim-api --bound", 35 | "pretest": "npm run lint", 36 | "test": "npm run tests-only", 37 | "tests-only": "nyc tape 'test/*.js'", 38 | "posttest": "aud --production", 39 | "version": "auto-changelog && git add CHANGELOG.md", 40 | "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" 41 | }, 42 | "dependencies": { 43 | "call-bind": "^1.0.7", 44 | "define-properties": "^1.2.1", 45 | "es-abstract": "^1.23.2", 46 | "es-object-atoms": "^1.0.0" 47 | }, 48 | "devDependencies": { 49 | "@es-shims/api": "^2.4.2", 50 | "@ljharb/eslint-config": "^21.1.0", 51 | "aud": "^2.0.4", 52 | "auto-changelog": "^2.4.0", 53 | "eslint": "=8.8.0", 54 | "function-bind": "^1.1.2", 55 | "functions-have-names": "^1.2.3", 56 | "nyc": "^10.3.2", 57 | "tape": "^5.7.5" 58 | }, 59 | "auto-changelog": { 60 | "output": "CHANGELOG.md", 61 | "template": "keepachangelog", 62 | "unreleased": false, 63 | "commitLimit": false, 64 | "backfillLimit": false, 65 | "hideCredit": true 66 | }, 67 | "engines": { 68 | "node": ">= 0.4" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /polyfill.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/endswith v1.0.0 by @mathias */ 2 | 3 | 'use strict'; 4 | 5 | var implementation = require('./implementation'); 6 | 7 | module.exports = function getPolyfill() { 8 | return String.prototype.endsWith || implementation; 9 | }; 10 | -------------------------------------------------------------------------------- /shim.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/endswith v1.0.0 by @mathias */ 2 | 3 | 'use strict'; 4 | 5 | var define = require('define-properties'); 6 | 7 | var getPolyfill = require('./polyfill'); 8 | 9 | module.exports = function shimEndsWith() { 10 | var polyfill = getPolyfill(); 11 | 12 | if (String.prototype.endsWith !== polyfill) { 13 | define(String.prototype, { endsWith: polyfill }); 14 | } 15 | 16 | return polyfill; 17 | }; 18 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var endsWith = require('../'); 4 | var test = require('tape'); 5 | 6 | var runTests = require('./tests'); 7 | 8 | test('as a function', function (t) { 9 | runTests(endsWith, t); 10 | 11 | t.end(); 12 | }); 13 | -------------------------------------------------------------------------------- /test/shimmed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var endsWith = require('../'); 4 | endsWith.shim(); 5 | 6 | var test = require('tape'); 7 | var defineProperties = require('define-properties'); 8 | var bind = require('function-bind'); 9 | var isEnumerable = Object.prototype.propertyIsEnumerable; 10 | var functionsHaveNames = require('functions-have-names')(); 11 | 12 | var runTests = require('./tests'); 13 | 14 | test('shimmed', function (t) { 15 | t.equal(String.prototype.endsWith.length, 1, 'String#endsWith has a length of 1'); 16 | 17 | t.test('Function name', { skip: !functionsHaveNames }, function (st) { 18 | st.equal(String.prototype.endsWith.name, 'endsWith', 'String#endsWith has name "endsWith"'); 19 | st.end(); 20 | }); 21 | 22 | t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (st) { 23 | st.equal(false, isEnumerable.call(String.prototype, 'endsWith'), 'String#endsWith is not enumerable'); 24 | st.end(); 25 | }); 26 | 27 | runTests(bind.call(Function.call, String.prototype.endsWith), t); 28 | 29 | t.end(); 30 | }); 31 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (endsWith, t) { 4 | t.test('nullish search string', function (st) { 5 | st.equal(endsWith('undefined'), true); 6 | st.equal(endsWith('undefined', undefined), true); 7 | st.equal(endsWith('undefined', null), false); 8 | st.equal(endsWith('null'), false); 9 | st.equal(endsWith('null', undefined), false); 10 | st.equal(endsWith('null', null), true); 11 | st.end(); 12 | }); 13 | 14 | t.test('basic support', function (st) { 15 | st.equal(endsWith('abc'), false); 16 | st.equal(endsWith('abc', ''), true); 17 | st.equal(endsWith('abc', '\0'), false); 18 | st.equal(endsWith('abc', 'c'), true); 19 | st.equal(endsWith('abc', 'b'), false); 20 | st.equal(endsWith('abc', 'ab'), false); 21 | st.equal(endsWith('abc', 'bc'), true); 22 | st.equal(endsWith('abc', 'abc'), true); 23 | st.equal(endsWith('abc', 'bcd'), false); 24 | st.equal(endsWith('abc', 'abcd'), false); 25 | st.equal(endsWith('abc', 'bcde'), false); 26 | st.end(); 27 | }); 28 | 29 | t.test('position argument - NaN', function (st) { 30 | st.equal(endsWith('abc', '', NaN), true); 31 | st.equal(endsWith('abc', '\0', NaN), false); 32 | st.equal(endsWith('abc', 'c', NaN), false); 33 | st.equal(endsWith('abc', 'b', NaN), false); 34 | st.equal(endsWith('abc', 'a', NaN), false); 35 | st.equal(endsWith('abc', 'ab', NaN), false); 36 | st.equal(endsWith('abc', 'bc', NaN), false); 37 | st.equal(endsWith('abc', 'abc', NaN), false); 38 | st.equal(endsWith('abc', 'bcd', NaN), false); 39 | st.equal(endsWith('abc', 'abcd', NaN), false); 40 | st.equal(endsWith('abc', 'bcde', NaN), false); 41 | st.end(); 42 | }); 43 | 44 | t.test('position argument - false', function (st) { 45 | st.equal(endsWith('abc', '', false), true); 46 | st.equal(endsWith('abc', '\0', false), false); 47 | st.equal(endsWith('abc', 'c', false), false); 48 | st.equal(endsWith('abc', 'b', false), false); 49 | st.equal(endsWith('abc', 'a', false), false); 50 | st.equal(endsWith('abc', 'ab', false), false); 51 | st.equal(endsWith('abc', 'bc', false), false); 52 | st.equal(endsWith('abc', 'abc', false), false); 53 | st.equal(endsWith('abc', 'bcd', false), false); 54 | st.equal(endsWith('abc', 'abcd', false), false); 55 | st.equal(endsWith('abc', 'bcde', false), false); 56 | st.end(); 57 | }); 58 | 59 | t.test('position argument - undefined', function (st) { 60 | st.equal(endsWith('abc', '', undefined), true); 61 | st.equal(endsWith('abc', '\0', undefined), false); 62 | st.equal(endsWith('abc', 'c', undefined), true); 63 | st.equal(endsWith('abc', 'b', undefined), false); 64 | st.equal(endsWith('abc', 'a', undefined), false); 65 | st.equal(endsWith('abc', 'ab', undefined), false); 66 | st.equal(endsWith('abc', 'bc', undefined), true); 67 | st.equal(endsWith('abc', 'abc', undefined), true); 68 | st.equal(endsWith('abc', 'bcd', undefined), false); 69 | st.equal(endsWith('abc', 'abcd', undefined), false); 70 | st.equal(endsWith('abc', 'bcde', undefined), false); 71 | st.end(); 72 | }); 73 | 74 | t.test('position argument - null', function (st) { 75 | st.equal(endsWith('abc', '', null), true); 76 | st.equal(endsWith('abc', '\0', null), false); 77 | st.equal(endsWith('abc', 'c', null), false); 78 | st.equal(endsWith('abc', 'b', null), false); 79 | st.equal(endsWith('abc', 'a', null), false); 80 | st.equal(endsWith('abc', 'ab', null), false); 81 | st.equal(endsWith('abc', 'bc', null), false); 82 | st.equal(endsWith('abc', 'abc', null), false); 83 | st.equal(endsWith('abc', 'bcd', null), false); 84 | st.equal(endsWith('abc', 'abcd', null), false); 85 | st.equal(endsWith('abc', 'bcde', null), false); 86 | st.end(); 87 | }); 88 | 89 | t.test('position argument - -Infinity', function (st) { 90 | st.equal(endsWith('abc', '', -Infinity), true); 91 | st.equal(endsWith('abc', '\0', -Infinity), false); 92 | st.equal(endsWith('abc', 'c', -Infinity), false); 93 | st.equal(endsWith('abc', 'b', -Infinity), false); 94 | st.equal(endsWith('abc', 'a', -Infinity), false); 95 | st.equal(endsWith('abc', 'ab', -Infinity), false); 96 | st.equal(endsWith('abc', 'bc', -Infinity), false); 97 | st.equal(endsWith('abc', 'abc', -Infinity), false); 98 | st.equal(endsWith('abc', 'bcd', -Infinity), false); 99 | st.equal(endsWith('abc', 'abcd', -Infinity), false); 100 | st.equal(endsWith('abc', 'bcde', -Infinity), false); 101 | st.end(); 102 | }); 103 | 104 | t.test('position argument - -1', function (st) { 105 | st.equal(endsWith('abc', '', -1), true); 106 | st.equal(endsWith('abc', '\0', -1), false); 107 | st.equal(endsWith('abc', 'c', -1), false); 108 | st.equal(endsWith('abc', 'b', -1), false); 109 | st.equal(endsWith('abc', 'a', -1), false); 110 | st.equal(endsWith('abc', 'ab', -1), false); 111 | st.equal(endsWith('abc', 'bc', -1), false); 112 | st.equal(endsWith('abc', 'abc', -1), false); 113 | st.equal(endsWith('abc', 'bcd', -1), false); 114 | st.equal(endsWith('abc', 'abcd', -1), false); 115 | st.equal(endsWith('abc', 'bcde', -1), false); 116 | st.end(); 117 | }); 118 | 119 | t.test('position argument - -0', function (st) { 120 | st.equal(endsWith('abc', '', -0), true); 121 | st.equal(endsWith('abc', '\0', -0), false); 122 | st.equal(endsWith('abc', 'c', -0), false); 123 | st.equal(endsWith('abc', 'b', -0), false); 124 | st.equal(endsWith('abc', 'a', -0), false); 125 | st.equal(endsWith('abc', 'ab', -0), false); 126 | st.equal(endsWith('abc', 'bc', -0), false); 127 | st.equal(endsWith('abc', 'abc', -0), false); 128 | st.equal(endsWith('abc', 'bcd', -0), false); 129 | st.equal(endsWith('abc', 'abcd', -0), false); 130 | st.equal(endsWith('abc', 'bcde', -0), false); 131 | st.end(); 132 | }); 133 | 134 | t.test('position argument - +0', function (st) { 135 | st.equal(endsWith('abc', '', +0), true); 136 | st.equal(endsWith('abc', '\0', +0), false); 137 | st.equal(endsWith('abc', 'c', +0), false); 138 | st.equal(endsWith('abc', 'b', +0), false); 139 | st.equal(endsWith('abc', 'a', +0), false); 140 | st.equal(endsWith('abc', 'ab', +0), false); 141 | st.equal(endsWith('abc', 'bc', +0), false); 142 | st.equal(endsWith('abc', 'abc', +0), false); 143 | st.equal(endsWith('abc', 'bcd', +0), false); 144 | st.equal(endsWith('abc', 'abcd', +0), false); 145 | st.equal(endsWith('abc', 'bcde', +0), false); 146 | st.end(); 147 | }); 148 | 149 | t.test('position argument - 1', function (st) { 150 | st.equal(endsWith('abc', '', 1), true); 151 | st.equal(endsWith('abc', '\0', 1), false); 152 | st.equal(endsWith('abc', 'c', 1), false); 153 | st.equal(endsWith('abc', 'b', 1), false); 154 | st.equal(endsWith('abc', 'a', 1), true); 155 | st.equal(endsWith('abc', 'ab', 1), false); 156 | st.equal(endsWith('abc', 'bc', 1), false); 157 | st.equal(endsWith('abc', 'abc', 1), false); 158 | st.equal(endsWith('abc', 'bcd', 1), false); 159 | st.equal(endsWith('abc', 'abcd', 1), false); 160 | st.equal(endsWith('abc', 'bcde', 1), false); 161 | st.end(); 162 | }); 163 | 164 | t.test('position argument - common integer', function (st) { 165 | st.equal(endsWith('abc', '', 2), true); 166 | st.equal(endsWith('abc', '\0', 2), false); 167 | st.equal(endsWith('abc', 'c', 2), false); 168 | st.equal(endsWith('abc', 'b', 2), true); 169 | st.equal(endsWith('abc', 'a', 2), false); 170 | st.equal(endsWith('abc', 'ab', 2), true); 171 | st.equal(endsWith('abc', 'bc', 2), false); 172 | st.equal(endsWith('abc', 'abc', 2), false); 173 | st.equal(endsWith('abc', 'bcd', 2), false); 174 | st.equal(endsWith('abc', 'abcd', 2), false); 175 | st.equal(endsWith('abc', 'bcde', 2), false); 176 | st.end(); 177 | }); 178 | 179 | t.test('position argument - +Infinity', function (st) { 180 | st.equal(endsWith('abc', '', Number(Infinity)), true); 181 | st.equal(endsWith('abc', '\0', Number(Infinity)), false); 182 | st.equal(endsWith('abc', 'c', Number(Infinity)), true); 183 | st.equal(endsWith('abc', 'b', Number(Infinity)), false); 184 | st.equal(endsWith('abc', 'a', Number(Infinity)), false); 185 | st.equal(endsWith('abc', 'ab', Number(Infinity)), false); 186 | st.equal(endsWith('abc', 'bc', Number(Infinity)), true); 187 | st.equal(endsWith('abc', 'abc', Number(Infinity)), true); 188 | st.equal(endsWith('abc', 'bcd', Number(Infinity)), false); 189 | st.equal(endsWith('abc', 'abcd', Number(Infinity)), false); 190 | st.equal(endsWith('abc', 'bcde', Number(Infinity)), false); 191 | st.end(); 192 | }); 193 | 194 | t.test('position argument - true', function (st) { 195 | st.equal(endsWith('abc', '', true), true); 196 | st.equal(endsWith('abc', '\0', true), false); 197 | st.equal(endsWith('abc', 'c', true), false); 198 | st.equal(endsWith('abc', 'b', true), false); 199 | st.equal(endsWith('abc', 'a', true), true); 200 | st.equal(endsWith('abc', 'ab', true), false); 201 | st.equal(endsWith('abc', 'bc', true), false); 202 | st.equal(endsWith('abc', 'abc', true), false); 203 | st.equal(endsWith('abc', 'bcd', true), false); 204 | st.equal(endsWith('abc', 'abcd', true), false); 205 | st.equal(endsWith('abc', 'bcde', true), false); 206 | st.end(); 207 | }); 208 | 209 | t.test('position argument - string', function (st) { 210 | st.equal(endsWith('abc', '', 'x'), true); 211 | st.equal(endsWith('abc', '\0', 'x'), false); 212 | st.equal(endsWith('abc', 'c', 'x'), false); 213 | st.equal(endsWith('abc', 'b', 'x'), false); 214 | st.equal(endsWith('abc', 'a', 'x'), false); 215 | st.equal(endsWith('abc', 'ab', 'x'), false); 216 | st.equal(endsWith('abc', 'bc', 'x'), false); 217 | st.equal(endsWith('abc', 'abc', 'x'), false); 218 | st.equal(endsWith('abc', 'bcd', 'x'), false); 219 | st.equal(endsWith('abc', 'abcd', 'x'), false); 220 | st.equal(endsWith('abc', 'bcde', 'x'), false); 221 | st.end(); 222 | }); 223 | 224 | t.test('search regexp', function (st) { 225 | st.equal(endsWith('[a-z]+(bar)?', '(bar)?'), true); 226 | st['throws'](function () { endsWith('[a-z]+(bar)?', /(bar)?/); }, TypeError); 227 | st.equal(endsWith('[a-z]+(bar)?', '[a-z]+', 6), true); 228 | st['throws'](function () { endsWith('[a-z]+(bar)?', /(bar)?/); }, TypeError); 229 | st['throws'](function () { endsWith('[a-z]+/(bar)?/', /(bar)?/); }, TypeError); 230 | st.end(); 231 | }); 232 | 233 | t.test('astral code points', function (st) { 234 | // https://mathiasbynens.be/notes/javascript-unicode#poo-test 235 | var string = 'I\xF1t\xEBrn\xE2ti\xF4n\xE0liz\xE6ti\xF8n\u2603\uD83D\uDCA9'; 236 | st.equal(string.endsWith(''), true); 237 | st.equal(string.endsWith('\xF1t\xEBr'), false); 238 | st.equal(string.endsWith('\xF1t\xEBr', 5), true); 239 | st.equal(string.endsWith('\xE0liz\xE6'), false); 240 | st.equal(string.endsWith('\xE0liz\xE6', 16), true); 241 | st.equal(string.endsWith('\xF8n\u2603\uD83D\uDCA9'), true); 242 | st.equal(string.endsWith('\xF8n\u2603\uD83D\uDCA9', 23), true); 243 | st.equal(string.endsWith('\u2603'), false); 244 | st.equal(string.endsWith('\u2603', 21), true); 245 | st.equal(string.endsWith('\uD83D\uDCA9'), true); 246 | st.equal(string.endsWith('\uD83D\uDCA9', 23), true); 247 | st.end(); 248 | }); 249 | 250 | t.test('nullish this object', function (st) { 251 | st['throws'](function () { endsWith(undefined); }, TypeError); 252 | st['throws'](function () { endsWith(undefined, 'b'); }, TypeError); 253 | st['throws'](function () { endsWith(undefined, 'b', 4); }, TypeError); 254 | st['throws'](function () { endsWith(null); }, TypeError); 255 | st['throws'](function () { endsWith(null, 'b'); }, TypeError); 256 | st['throws'](function () { endsWith(null, 'b', 4); }, TypeError); 257 | st.end(); 258 | }); 259 | 260 | t.test('cast this object', function (st) { 261 | st.equal(endsWith(42, '2'), true); 262 | st.equal(endsWith(42, '4'), false); 263 | st.equal(endsWith(42, 'b', 4), false); 264 | st.equal(endsWith(42, '2', 1), false); 265 | st.equal(endsWith(42, '2', 4), true); 266 | st.equal(endsWith({ toString: function () { return 'abc'; } }, 'b', 0), false); 267 | st.equal(endsWith({ toString: function () { return 'abc'; } }, 'b', 1), false); 268 | st.equal(endsWith({ toString: function () { return 'abc'; } }, 'b', 2), true); 269 | st['throws'](function () { endsWith({ toString: function () { throw new RangeError(); } }, /./); }, RangeError); 270 | st['throws'](function () { endsWith({ toString: function () { return 'abc'; } }, /./); }, TypeError); 271 | st.end(); 272 | }); 273 | }; 274 | --------------------------------------------------------------------------------