├── .eslintignore ├── .eslintrc.yml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── History.md ├── LICENSE ├── Readme.md ├── appveyor.yml ├── benchmark ├── index.js ├── wrapfunction.js └── wrapproperty.js ├── files └── message.png ├── index.js ├── lib └── browser │ └── index.js ├── package.json └── test ├── .eslintrc.yml ├── browserify.js ├── fixtures ├── libs │ ├── cool.js │ ├── index.js │ ├── multi.js │ ├── my.js │ ├── new.js │ ├── old.js │ ├── strict.js │ ├── thing.js │ └── trace.js └── script.js ├── support └── capture-stderr.js └── test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | extends: standard 3 | rules: 4 | no-param-reassign: error 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | name: 13 | - Node.js 0.8 14 | - Node.js 0.10 15 | - Node.js 0.12 16 | - io.js 1.x 17 | - io.js 2.x 18 | - io.js 3.x 19 | - Node.js 4.x 20 | - Node.js 5.x 21 | - Node.js 6.x 22 | - Node.js 7.x 23 | - Node.js 8.x 24 | - Node.js 9.x 25 | - Node.js 10.x 26 | - Node.js 11.x 27 | - Node.js 12.x 28 | - Node.js 13.x 29 | - Node.js 14.x 30 | - Node.js 15.x 31 | 32 | include: 33 | - name: Node.js 0.8 34 | node-version: "0.8" 35 | npm-i: mocha@2.5.3 36 | npm-rm: nyc 37 | 38 | - name: Node.js 0.10 39 | node-version: "0.10" 40 | npm-i: browserify@15 mocha@3.5.3 nyc@10.3.2 41 | 42 | - name: Node.js 0.12 43 | node-version: "0.12" 44 | npm-i: browserify@15 mocha@3.5.3 nyc@10.3.2 45 | 46 | - name: io.js 1.x 47 | node-version: "1.8" 48 | npm-i: browserify@15 mocha@3.5.3 nyc@10.3.2 49 | 50 | - name: io.js 2.x 51 | node-version: "2.5" 52 | npm-i: browserify@15 mocha@3.5.3 nyc@10.3.2 53 | 54 | - name: io.js 3.x 55 | node-version: "3.3" 56 | npm-i: browserify@15 mocha@3.5.3 nyc@10.3.2 57 | 58 | - name: Node.js 4.x 59 | node-version: "4.9" 60 | npm-i: browserify@15 mocha@5.2.0 nyc@11.9.0 61 | 62 | - name: Node.js 5.x 63 | node-version: "5.12" 64 | npm-i: browserify@15 mocha@5.2.0 nyc@11.9.0 65 | 66 | - name: Node.js 6.x 67 | node-version: "6.17" 68 | npm-i: browserify@15 mocha@6.2.2 nyc@14.1.1 69 | 70 | - name: Node.js 7.x 71 | node-version: "7.10" 72 | npm-i: browserify@15 mocha@6.2.2 nyc@14.1.1 73 | 74 | - name: Node.js 8.x 75 | node-version: "8.17" 76 | npm-i: browserify@15 mocha@7.2.0 77 | 78 | - name: Node.js 9.x 79 | node-version: "9.11" 80 | npm-i: browserify@15 mocha@7.2.0 81 | 82 | - name: Node.js 10.x 83 | node-version: "10.23" 84 | npm-i: browserify@15 85 | 86 | - name: Node.js 11.x 87 | node-version: "11.15" 88 | npm-i: browserify@15 89 | 90 | - name: Node.js 12.x 91 | node-version: "12.20" 92 | npm-i: browserify@15 93 | 94 | - name: Node.js 13.x 95 | node-version: "13.14" 96 | npm-i: browserify@15 97 | 98 | - name: Node.js 14.x 99 | node-version: "14.15" 100 | npm-i: browserify@15 101 | 102 | - name: Node.js 15.x 103 | node-version: "15.8" 104 | npm-i: browserify@15 105 | 106 | steps: 107 | - uses: actions/checkout@v2 108 | 109 | - name: Install Node.js ${{ matrix.node-version }} 110 | shell: bash -eo pipefail -l {0} 111 | run: | 112 | nvm install --default ${{ matrix.node-version }} 113 | if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then 114 | nvm install --alias=npm 0.10 115 | nvm use ${{ matrix.node-version }} 116 | sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" 117 | npm config set strict-ssl false 118 | fi 119 | dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" 120 | 121 | - name: Configure npm 122 | run: npm config set shrinkwrap false 123 | 124 | - name: Remove non-test npm modules 125 | run: npm rm --silent --save-dev benchmark beautify-benchmark 126 | 127 | - name: Remove npm module(s) ${{ matrix.npm-rm }} 128 | run: npm rm --silent --save-dev ${{ matrix.npm-rm }} 129 | if: matrix.npm-rm != '' 130 | 131 | - name: Install npm module(s) ${{ matrix.npm-i }} 132 | run: npm install --save-dev ${{ matrix.npm-i }} 133 | if: matrix.npm-i != '' 134 | 135 | - name: Setup Node.js version-specific dependencies 136 | shell: bash 137 | run: | 138 | # eslint for linting 139 | # - remove on Node.js < 10 140 | if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then 141 | node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ 142 | grep -E '^eslint(-|$)' | \ 143 | sort -r | \ 144 | xargs -n1 npm rm --silent --save-dev 145 | fi 146 | 147 | - name: Install Node.js dependencies 148 | run: npm install 149 | 150 | - name: List environment 151 | id: list_env 152 | shell: bash 153 | run: | 154 | echo "node@$(node -v)" 155 | echo "npm@$(npm -v)" 156 | npm -s ls ||: 157 | (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }' 158 | 159 | - name: Run tests 160 | shell: bash 161 | run: | 162 | if npm -ps ls nyc | grep -q nyc; then 163 | npm run test-ci 164 | cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" 165 | else 166 | npm test 167 | fi 168 | 169 | - name: Lint code 170 | if: steps.list_env.outputs.eslint != '' 171 | run: npm run lint 172 | 173 | - name: Collect code coverage 174 | if: steps.list_env.outputs.nyc != '' 175 | run: | 176 | if [[ -d ./coverage ]]; then 177 | mv ./coverage "./${{ matrix.name }}" 178 | mkdir ./coverage 179 | mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" 180 | fi 181 | 182 | - name: Upload code coverage 183 | uses: actions/upload-artifact@v2 184 | if: steps.list_env.outputs.nyc != '' 185 | with: 186 | name: coverage 187 | path: ./coverage 188 | retention-days: 1 189 | 190 | coverage: 191 | needs: test 192 | runs-on: ubuntu-latest 193 | steps: 194 | - uses: actions/checkout@v2 195 | 196 | - name: Install lcov 197 | shell: bash 198 | run: sudo apt-get -y install lcov 199 | 200 | - name: Collect coverage reports 201 | uses: actions/download-artifact@v2 202 | with: 203 | name: coverage 204 | path: ./coverage 205 | 206 | - name: Merge coverage reports 207 | shell: bash 208 | run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info 209 | 210 | - name: Upload coverage report 211 | uses: coverallsapp/github-action@master 212 | with: 213 | github-token: ${{ secrets.github_token }} 214 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | coverage/ 3 | node_modules/ 4 | npm-debug.log 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2.0.0 / 2018-10-26 2 | ================== 3 | 4 | * Drop support for Node.js 0.6 5 | * Replace internal `eval` usage with `Function` constructor 6 | * Use instance methods on `process` to check for listeners 7 | 8 | 1.1.2 / 2018-01-11 9 | ================== 10 | 11 | * perf: remove argument reassignment 12 | * Support Node.js 0.6 to 9.x 13 | 14 | 1.1.1 / 2017-07-27 15 | ================== 16 | 17 | * Remove unnecessary `Buffer` loading 18 | * Support Node.js 0.6 to 8.x 19 | 20 | 1.1.0 / 2015-09-14 21 | ================== 22 | 23 | * Enable strict mode in more places 24 | * Support io.js 3.x 25 | * Support io.js 2.x 26 | * Support web browser loading 27 | - Requires bundler like Browserify or webpack 28 | 29 | 1.0.1 / 2015-04-07 30 | ================== 31 | 32 | * Fix `TypeError`s when under `'use strict'` code 33 | * Fix useless type name on auto-generated messages 34 | * Support io.js 1.x 35 | * Support Node.js 0.12 36 | 37 | 1.0.0 / 2014-09-17 38 | ================== 39 | 40 | * No changes 41 | 42 | 0.4.5 / 2014-09-09 43 | ================== 44 | 45 | * Improve call speed to functions using the function wrapper 46 | * Support Node.js 0.6 47 | 48 | 0.4.4 / 2014-07-27 49 | ================== 50 | 51 | * Work-around v8 generating empty stack traces 52 | 53 | 0.4.3 / 2014-07-26 54 | ================== 55 | 56 | * Fix exception when global `Error.stackTraceLimit` is too low 57 | 58 | 0.4.2 / 2014-07-19 59 | ================== 60 | 61 | * Correct call site for wrapped functions and properties 62 | 63 | 0.4.1 / 2014-07-19 64 | ================== 65 | 66 | * Improve automatic message generation for function properties 67 | 68 | 0.4.0 / 2014-07-19 69 | ================== 70 | 71 | * Add `TRACE_DEPRECATION` environment variable 72 | * Remove non-standard grey color from color output 73 | * Support `--no-deprecation` argument 74 | * Support `--trace-deprecation` argument 75 | * Support `deprecate.property(fn, prop, message)` 76 | 77 | 0.3.0 / 2014-06-16 78 | ================== 79 | 80 | * Add `NO_DEPRECATION` environment variable 81 | 82 | 0.2.0 / 2014-06-15 83 | ================== 84 | 85 | * Add `deprecate.property(obj, prop, message)` 86 | * Remove `supports-color` dependency for node.js 0.8 87 | 88 | 0.1.0 / 2014-06-15 89 | ================== 90 | 91 | * Add `deprecate.function(fn, message)` 92 | * Add `process.on('deprecation', fn)` emitter 93 | * Automatically generate message when omitted from `deprecate()` 94 | 95 | 0.0.1 / 2014-06-15 96 | ================== 97 | 98 | * Fix warning for dynamic calls at singe call site 99 | 100 | 0.0.0 / 2014-06-15 101 | ================== 102 | 103 | * Initial implementation 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014-2018 Douglas Christopher Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # depd 2 | 3 | [![NPM Version][npm-version-image]][npm-url] 4 | [![NPM Downloads][npm-downloads-image]][npm-url] 5 | [![Node.js Version][node-image]][node-url] 6 | [![Linux Build][github-actions-ci-image]][github-actions-ci-url] 7 | [![Windows Build][appveyor-image]][appveyor-url] 8 | [![Coverage Status][coveralls-image]][coveralls-url] 9 | 10 | Deprecate all the things 11 | 12 | > With great modules comes great responsibility; mark things deprecated! 13 | 14 | ## Install 15 | 16 | This module is installed directly using `npm`: 17 | 18 | ```sh 19 | $ npm install depd 20 | ``` 21 | 22 | This module can also be bundled with systems like 23 | [Browserify](http://browserify.org/) or [webpack](https://webpack.github.io/), 24 | though by default this module will alter it's API to no longer display or 25 | track deprecations. 26 | 27 | ## API 28 | 29 | 30 | 31 | ```js 32 | var deprecate = require('depd')('my-module') 33 | ``` 34 | 35 | This library allows you to display deprecation messages to your users. 36 | This library goes above and beyond with deprecation warnings by 37 | introspection of the call stack (but only the bits that it is interested 38 | in). 39 | 40 | Instead of just warning on the first invocation of a deprecated 41 | function and never again, this module will warn on the first invocation 42 | of a deprecated function per unique call site, making it ideal to alert 43 | users of all deprecated uses across the code base, rather than just 44 | whatever happens to execute first. 45 | 46 | The deprecation warnings from this module also include the file and line 47 | information for the call into the module that the deprecated function was 48 | in. 49 | 50 | **NOTE** this library has a similar interface to the `debug` module, and 51 | this module uses the calling file to get the boundary for the call stacks, 52 | so you should always create a new `deprecate` object in each file and not 53 | within some central file. 54 | 55 | ### depd(namespace) 56 | 57 | Create a new deprecate function that uses the given namespace name in the 58 | messages and will display the call site prior to the stack entering the 59 | file this function was called from. It is highly suggested you use the 60 | name of your module as the namespace. 61 | 62 | ### deprecate(message) 63 | 64 | Call this function from deprecated code to display a deprecation message. 65 | This message will appear once per unique caller site. Caller site is the 66 | first call site in the stack in a different file from the caller of this 67 | function. 68 | 69 | If the message is omitted, a message is generated for you based on the site 70 | of the `deprecate()` call and will display the name of the function called, 71 | similar to the name displayed in a stack trace. 72 | 73 | ### deprecate.function(fn, message) 74 | 75 | Call this function to wrap a given function in a deprecation message on any 76 | call to the function. An optional message can be supplied to provide a custom 77 | message. 78 | 79 | ### deprecate.property(obj, prop, message) 80 | 81 | Call this function to wrap a given property on object in a deprecation message 82 | on any accessing or setting of the property. An optional message can be supplied 83 | to provide a custom message. 84 | 85 | The method must be called on the object where the property belongs (not 86 | inherited from the prototype). 87 | 88 | If the property is a data descriptor, it will be converted to an accessor 89 | descriptor in order to display the deprecation message. 90 | 91 | ### process.on('deprecation', fn) 92 | 93 | This module will allow easy capturing of deprecation errors by emitting the 94 | errors as the type "deprecation" on the global `process`. If there are no 95 | listeners for this type, the errors are written to STDERR as normal, but if 96 | there are any listeners, nothing will be written to STDERR and instead only 97 | emitted. From there, you can write the errors in a different format or to a 98 | logging source. 99 | 100 | The error represents the deprecation and is emitted only once with the same 101 | rules as writing to STDERR. The error has the following properties: 102 | 103 | - `message` - This is the message given by the library 104 | - `name` - This is always `'DeprecationError'` 105 | - `namespace` - This is the namespace the deprecation came from 106 | - `stack` - This is the stack of the call to the deprecated thing 107 | 108 | Example `error.stack` output: 109 | 110 | ``` 111 | DeprecationError: my-cool-module deprecated oldfunction 112 | at Object. ([eval]-wrapper:6:22) 113 | at Module._compile (module.js:456:26) 114 | at evalScript (node.js:532:25) 115 | at startup (node.js:80:7) 116 | at node.js:902:3 117 | ``` 118 | 119 | ### process.env.NO_DEPRECATION 120 | 121 | As a user of modules that are deprecated, the environment variable `NO_DEPRECATION` 122 | is provided as a quick solution to silencing deprecation warnings from being 123 | output. The format of this is similar to that of `DEBUG`: 124 | 125 | ```sh 126 | $ NO_DEPRECATION=my-module,othermod node app.js 127 | ``` 128 | 129 | This will suppress deprecations from being output for "my-module" and "othermod". 130 | The value is a list of comma-separated namespaces. To suppress every warning 131 | across all namespaces, use the value `*` for a namespace. 132 | 133 | Providing the argument `--no-deprecation` to the `node` executable will suppress 134 | all deprecations (only available in Node.js 0.8 or higher). 135 | 136 | **NOTE** This will not suppress the deperecations given to any "deprecation" 137 | event listeners, just the output to STDERR. 138 | 139 | ### process.env.TRACE_DEPRECATION 140 | 141 | As a user of modules that are deprecated, the environment variable `TRACE_DEPRECATION` 142 | is provided as a solution to getting more detailed location information in deprecation 143 | warnings by including the entire stack trace. The format of this is the same as 144 | `NO_DEPRECATION`: 145 | 146 | ```sh 147 | $ TRACE_DEPRECATION=my-module,othermod node app.js 148 | ``` 149 | 150 | This will include stack traces for deprecations being output for "my-module" and 151 | "othermod". The value is a list of comma-separated namespaces. To trace every 152 | warning across all namespaces, use the value `*` for a namespace. 153 | 154 | Providing the argument `--trace-deprecation` to the `node` executable will trace 155 | all deprecations (only available in Node.js 0.8 or higher). 156 | 157 | **NOTE** This will not trace the deperecations silenced by `NO_DEPRECATION`. 158 | 159 | ## Display 160 | 161 | ![message](files/message.png) 162 | 163 | When a user calls a function in your library that you mark deprecated, they 164 | will see the following written to STDERR (in the given colors, similar colors 165 | and layout to the `debug` module): 166 | 167 | ``` 168 | bright cyan bright yellow 169 | | | reset cyan 170 | | | | | 171 | ▼ ▼ ▼ ▼ 172 | my-cool-module deprecated oldfunction [eval]-wrapper:6:22 173 | ▲ ▲ ▲ ▲ 174 | | | | | 175 | namespace | | location of mycoolmod.oldfunction() call 176 | | deprecation message 177 | the word "deprecated" 178 | ``` 179 | 180 | If the user redirects their STDERR to a file or somewhere that does not support 181 | colors, they see (similar layout to the `debug` module): 182 | 183 | ``` 184 | Sun, 15 Jun 2014 05:21:37 GMT my-cool-module deprecated oldfunction at [eval]-wrapper:6:22 185 | ▲ ▲ ▲ ▲ ▲ 186 | | | | | | 187 | timestamp of message namespace | | location of mycoolmod.oldfunction() call 188 | | deprecation message 189 | the word "deprecated" 190 | ``` 191 | 192 | ## Examples 193 | 194 | ### Deprecating all calls to a function 195 | 196 | This will display a deprecated message about "oldfunction" being deprecated 197 | from "my-module" on STDERR. 198 | 199 | ```js 200 | var deprecate = require('depd')('my-cool-module') 201 | 202 | // message automatically derived from function name 203 | // Object.oldfunction 204 | exports.oldfunction = deprecate.function(function oldfunction () { 205 | // all calls to function are deprecated 206 | }) 207 | 208 | // specific message 209 | exports.oldfunction = deprecate.function(function () { 210 | // all calls to function are deprecated 211 | }, 'oldfunction') 212 | ``` 213 | 214 | ### Conditionally deprecating a function call 215 | 216 | This will display a deprecated message about "weirdfunction" being deprecated 217 | from "my-module" on STDERR when called with less than 2 arguments. 218 | 219 | ```js 220 | var deprecate = require('depd')('my-cool-module') 221 | 222 | exports.weirdfunction = function () { 223 | if (arguments.length < 2) { 224 | // calls with 0 or 1 args are deprecated 225 | deprecate('weirdfunction args < 2') 226 | } 227 | } 228 | ``` 229 | 230 | When calling `deprecate` as a function, the warning is counted per call site 231 | within your own module, so you can display different deprecations depending 232 | on different situations and the users will still get all the warnings: 233 | 234 | ```js 235 | var deprecate = require('depd')('my-cool-module') 236 | 237 | exports.weirdfunction = function () { 238 | if (arguments.length < 2) { 239 | // calls with 0 or 1 args are deprecated 240 | deprecate('weirdfunction args < 2') 241 | } else if (typeof arguments[0] !== 'string') { 242 | // calls with non-string first argument are deprecated 243 | deprecate('weirdfunction non-string first arg') 244 | } 245 | } 246 | ``` 247 | 248 | ### Deprecating property access 249 | 250 | This will display a deprecated message about "oldprop" being deprecated 251 | from "my-module" on STDERR when accessed. A deprecation will be displayed 252 | when setting the value and when getting the value. 253 | 254 | ```js 255 | var deprecate = require('depd')('my-cool-module') 256 | 257 | exports.oldprop = 'something' 258 | 259 | // message automatically derives from property name 260 | deprecate.property(exports, 'oldprop') 261 | 262 | // explicit message 263 | deprecate.property(exports, 'oldprop', 'oldprop >= 0.10') 264 | ``` 265 | 266 | ## License 267 | 268 | [MIT](LICENSE) 269 | 270 | [appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/nodejs-depd/master?label=windows 271 | [appveyor-url]: https://ci.appveyor.com/project/dougwilson/nodejs-depd 272 | [coveralls-image]: https://badgen.net/coveralls/c/github/dougwilson/nodejs-depd/master 273 | [coveralls-url]: https://coveralls.io/r/dougwilson/nodejs-depd?branch=master 274 | [github-actions-ci-image]: https://badgen.net/github/checks/dougwilson/nodejs-depd/master?label=linux 275 | [github-actions-ci-url]: https://github.com/jshttp/dougwilson/nodejs-depd?query=workflow%3Aci 276 | [node-image]: https://badgen.net/npm/node/depd 277 | [node-url]: https://nodejs.org/en/download/ 278 | [npm-downloads-image]: https://badgen.net/npm/dm/depd 279 | [npm-url]: https://npmjs.org/package/depd 280 | [npm-version-image]: https://badgen.net/npm/v/depd 281 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "0.10" 4 | - nodejs_version: "0.12" 5 | - nodejs_version: "1.8" 6 | - nodejs_version: "2.5" 7 | - nodejs_version: "3.3" 8 | - nodejs_version: "4.9" 9 | - nodejs_version: "5.12" 10 | - nodejs_version: "6.17" 11 | - nodejs_version: "7.10" 12 | - nodejs_version: "8.17" 13 | - nodejs_version: "9.11" 14 | - nodejs_version: "10.23" 15 | - nodejs_version: "11.15" 16 | - nodejs_version: "12.20" 17 | - nodejs_version: "13.14" 18 | - nodejs_version: "14.15" 19 | - nodejs_version: "15.8" 20 | cache: 21 | - node_modules 22 | install: 23 | # Install Node.js 24 | - ps: >- 25 | try { Install-Product node $env:nodejs_version -ErrorAction Stop } 26 | catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) } 27 | # Configure npm 28 | - ps: | 29 | # Skip updating shrinkwrap / lock 30 | npm config set shrinkwrap false 31 | # Remove all non-test dependencies 32 | - ps: | 33 | # Remove benchmark dependencies 34 | npm rm --silent --save-dev benchmark beautify-benchmark 35 | # Remove coverage dependency 36 | npm rm --silent --save-dev nyc 37 | # Remove lint dependencies 38 | cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | ` 39 | sls "^eslint(-|$)" | ` 40 | %{ npm rm --silent --save-dev $_ } 41 | # Setup Node.js version-specific dependencies 42 | - ps: | 43 | # mocha for testing 44 | # - use 2.x for Node.js < 0.10 45 | # - use 3.x for Node.js < 4 46 | # - use 5.x for Node.js < 6 47 | # - use 6.x for Node.js < 8 48 | # - use 7.x for Node.js < 10 49 | if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) { 50 | npm install --silent --save-dev mocha@2.5.3 51 | } elseif ([int]$env:nodejs_version.split(".")[0] -lt 4) { 52 | npm install --silent --save-dev mocha@3.5.3 53 | } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { 54 | npm install --silent --save-dev mocha@5.2.0 55 | } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { 56 | npm install --silent --save-dev mocha@6.2.2 57 | } elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) { 58 | npm install --silent --save-dev mocha@7.2.0 59 | } 60 | # Update Node.js modules 61 | - ps: | 62 | # Prune & rebuild node_modules 63 | if (Test-Path -Path node_modules) { 64 | npm prune 65 | npm rebuild 66 | } 67 | # Install Node.js modules 68 | - npm install 69 | build: off 70 | test_script: 71 | # Output version data 72 | - ps: | 73 | node --version 74 | npm --version 75 | # Run test script 76 | - npm test 77 | version: "{build}" 78 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var spawn = require('child_process').spawn 4 | 5 | var exe = process.argv[0] 6 | var cwd = process.cwd() 7 | 8 | runScripts(fs.readdirSync(__dirname)) 9 | 10 | function runScripts (fileNames) { 11 | var fileName = fileNames.shift() 12 | 13 | if (!fileName) return 14 | if (!/\.js$/i.test(fileName)) return runScripts(fileNames) 15 | if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames) 16 | 17 | var fullPath = path.join(__dirname, fileName) 18 | 19 | console.log('> %s %s', exe, path.relative(cwd, fullPath)) 20 | 21 | var proc = spawn(exe, [fullPath], { 22 | stdio: 'inherit' 23 | }) 24 | 25 | proc.on('exit', function () { 26 | runScripts(fileNames) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /benchmark/wrapfunction.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var benchmark = require('benchmark') 7 | var benchmarks = require('beautify-benchmark') 8 | 9 | /** 10 | * Globals for benchmark.js 11 | */ 12 | 13 | process.env.NO_DEPRECATION = 'my-lib' 14 | global.mylib = require('../test/fixtures/libs/my') 15 | 16 | var suite = new benchmark.Suite() 17 | 18 | suite.add({ 19 | name: 'function', 20 | minSamples: 100, 21 | fn: 'mylib.fn()' 22 | }) 23 | 24 | suite.add({ 25 | name: 'wrapped', 26 | minSamples: 100, 27 | fn: 'mylib.oldfn()' 28 | }) 29 | 30 | suite.add({ 31 | name: 'call log', 32 | minSamples: 100, 33 | fn: 'mylib.old()' 34 | }) 35 | 36 | suite.on('cycle', function onCycle (event) { 37 | benchmarks.add(event.target) 38 | }) 39 | 40 | suite.on('complete', function onComplete () { 41 | benchmarks.log() 42 | }) 43 | 44 | suite.run({ async: false }) 45 | -------------------------------------------------------------------------------- /benchmark/wrapproperty.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var benchmark = require('benchmark') 7 | var benchmarks = require('beautify-benchmark') 8 | 9 | /** 10 | * Globals for benchmark.js 11 | */ 12 | 13 | process.env.NO_DEPRECATION = 'my-lib' 14 | global.mylib = require('../test/fixtures/libs/my') 15 | 16 | var suite = new benchmark.Suite() 17 | 18 | suite.add({ 19 | name: 'property', 20 | minSamples: 100, 21 | fn: 'mylib.prop = mylib.prop' 22 | }) 23 | 24 | suite.add({ 25 | name: 'wrapped', 26 | minSamples: 100, 27 | fn: 'mylib.propa = mylib.propa' 28 | }) 29 | 30 | suite.on('cycle', function onCycle (event) { 31 | benchmarks.add(event.target) 32 | }) 33 | 34 | suite.on('complete', function onComplete () { 35 | benchmarks.log() 36 | }) 37 | 38 | suite.run({ async: false }) 39 | -------------------------------------------------------------------------------- /files/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dougwilson/nodejs-depd/7ad9a48dff1e7fe52d4298da7c1235944073bfc9/files/message.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * depd 3 | * Copyright(c) 2014-2018 Douglas Christopher Wilson 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Module dependencies. 9 | */ 10 | 11 | var relative = require('path').relative 12 | 13 | /** 14 | * Module exports. 15 | */ 16 | 17 | module.exports = depd 18 | 19 | /** 20 | * Get the path to base files on. 21 | */ 22 | 23 | var basePath = process.cwd() 24 | 25 | /** 26 | * Determine if namespace is contained in the string. 27 | */ 28 | 29 | function containsNamespace (str, namespace) { 30 | var vals = str.split(/[ ,]+/) 31 | var ns = String(namespace).toLowerCase() 32 | 33 | for (var i = 0; i < vals.length; i++) { 34 | var val = vals[i] 35 | 36 | // namespace contained 37 | if (val && (val === '*' || val.toLowerCase() === ns)) { 38 | return true 39 | } 40 | } 41 | 42 | return false 43 | } 44 | 45 | /** 46 | * Convert a data descriptor to accessor descriptor. 47 | */ 48 | 49 | function convertDataDescriptorToAccessor (obj, prop, message) { 50 | var descriptor = Object.getOwnPropertyDescriptor(obj, prop) 51 | var value = descriptor.value 52 | 53 | descriptor.get = function getter () { return value } 54 | 55 | if (descriptor.writable) { 56 | descriptor.set = function setter (val) { return (value = val) } 57 | } 58 | 59 | delete descriptor.value 60 | delete descriptor.writable 61 | 62 | Object.defineProperty(obj, prop, descriptor) 63 | 64 | return descriptor 65 | } 66 | 67 | /** 68 | * Create arguments string to keep arity. 69 | */ 70 | 71 | function createArgumentsString (arity) { 72 | var str = '' 73 | 74 | for (var i = 0; i < arity; i++) { 75 | str += ', arg' + i 76 | } 77 | 78 | return str.substr(2) 79 | } 80 | 81 | /** 82 | * Create stack string from stack. 83 | */ 84 | 85 | function createStackString (stack) { 86 | var str = this.name + ': ' + this.namespace 87 | 88 | if (this.message) { 89 | str += ' deprecated ' + this.message 90 | } 91 | 92 | for (var i = 0; i < stack.length; i++) { 93 | str += '\n at ' + stack[i].toString() 94 | } 95 | 96 | return str 97 | } 98 | 99 | /** 100 | * Create deprecate for namespace in caller. 101 | */ 102 | 103 | function depd (namespace) { 104 | if (!namespace) { 105 | throw new TypeError('argument namespace is required') 106 | } 107 | 108 | var stack = getStack() 109 | var site = callSiteLocation(stack[1]) 110 | var file = site[0] 111 | 112 | function deprecate (message) { 113 | // call to self as log 114 | log.call(deprecate, message) 115 | } 116 | 117 | deprecate._file = file 118 | deprecate._ignored = isignored(namespace) 119 | deprecate._namespace = namespace 120 | deprecate._traced = istraced(namespace) 121 | deprecate._warned = Object.create(null) 122 | 123 | deprecate.function = wrapfunction 124 | deprecate.property = wrapproperty 125 | 126 | return deprecate 127 | } 128 | 129 | /** 130 | * Determine if event emitter has listeners of a given type. 131 | * 132 | * The way to do this check is done three different ways in Node.js >= 0.8 133 | * so this consolidates them into a minimal set using instance methods. 134 | * 135 | * @param {EventEmitter} emitter 136 | * @param {string} type 137 | * @returns {boolean} 138 | * @private 139 | */ 140 | 141 | function eehaslisteners (emitter, type) { 142 | var count = typeof emitter.listenerCount !== 'function' 143 | ? emitter.listeners(type).length 144 | : emitter.listenerCount(type) 145 | 146 | return count > 0 147 | } 148 | 149 | /** 150 | * Determine if namespace is ignored. 151 | */ 152 | 153 | function isignored (namespace) { 154 | if (process.noDeprecation) { 155 | // --no-deprecation support 156 | return true 157 | } 158 | 159 | var str = process.env.NO_DEPRECATION || '' 160 | 161 | // namespace ignored 162 | return containsNamespace(str, namespace) 163 | } 164 | 165 | /** 166 | * Determine if namespace is traced. 167 | */ 168 | 169 | function istraced (namespace) { 170 | if (process.traceDeprecation) { 171 | // --trace-deprecation support 172 | return true 173 | } 174 | 175 | var str = process.env.TRACE_DEPRECATION || '' 176 | 177 | // namespace traced 178 | return containsNamespace(str, namespace) 179 | } 180 | 181 | /** 182 | * Display deprecation message. 183 | */ 184 | 185 | function log (message, site) { 186 | var haslisteners = eehaslisteners(process, 'deprecation') 187 | 188 | // abort early if no destination 189 | if (!haslisteners && this._ignored) { 190 | return 191 | } 192 | 193 | var caller 194 | var callFile 195 | var callSite 196 | var depSite 197 | var i = 0 198 | var seen = false 199 | var stack = getStack() 200 | var file = this._file 201 | 202 | if (site) { 203 | // provided site 204 | depSite = site 205 | callSite = callSiteLocation(stack[1]) 206 | callSite.name = depSite.name 207 | file = callSite[0] 208 | } else { 209 | // get call site 210 | i = 2 211 | depSite = callSiteLocation(stack[i]) 212 | callSite = depSite 213 | } 214 | 215 | // get caller of deprecated thing in relation to file 216 | for (; i < stack.length; i++) { 217 | caller = callSiteLocation(stack[i]) 218 | callFile = caller[0] 219 | 220 | if (callFile === file) { 221 | seen = true 222 | } else if (callFile === this._file) { 223 | file = this._file 224 | } else if (seen) { 225 | break 226 | } 227 | } 228 | 229 | var key = caller 230 | ? depSite.join(':') + '__' + caller.join(':') 231 | : undefined 232 | 233 | if (key !== undefined && key in this._warned) { 234 | // already warned 235 | return 236 | } 237 | 238 | this._warned[key] = true 239 | 240 | // generate automatic message from call site 241 | var msg = message 242 | if (!msg) { 243 | msg = callSite === depSite || !callSite.name 244 | ? defaultMessage(depSite) 245 | : defaultMessage(callSite) 246 | } 247 | 248 | // emit deprecation if listeners exist 249 | if (haslisteners) { 250 | var err = DeprecationError(this._namespace, msg, stack.slice(i)) 251 | process.emit('deprecation', err) 252 | return 253 | } 254 | 255 | // format and write message 256 | var format = process.stderr.isTTY 257 | ? formatColor 258 | : formatPlain 259 | var output = format.call(this, msg, caller, stack.slice(i)) 260 | process.stderr.write(output + '\n', 'utf8') 261 | } 262 | 263 | /** 264 | * Get call site location as array. 265 | */ 266 | 267 | function callSiteLocation (callSite) { 268 | var file = callSite.getFileName() || '' 269 | var line = callSite.getLineNumber() 270 | var colm = callSite.getColumnNumber() 271 | 272 | if (callSite.isEval()) { 273 | file = callSite.getEvalOrigin() + ', ' + file 274 | } 275 | 276 | var site = [file, line, colm] 277 | 278 | site.callSite = callSite 279 | site.name = callSite.getFunctionName() 280 | 281 | return site 282 | } 283 | 284 | /** 285 | * Generate a default message from the site. 286 | */ 287 | 288 | function defaultMessage (site) { 289 | var callSite = site.callSite 290 | var funcName = site.name 291 | 292 | // make useful anonymous name 293 | if (!funcName) { 294 | funcName = '' 295 | } 296 | 297 | var context = callSite.getThis() 298 | var typeName = context && callSite.getTypeName() 299 | 300 | // ignore useless type name 301 | if (typeName === 'Object') { 302 | typeName = undefined 303 | } 304 | 305 | // make useful type name 306 | if (typeName === 'Function') { 307 | typeName = context.name || typeName 308 | } 309 | 310 | return typeName && callSite.getMethodName() 311 | ? typeName + '.' + funcName 312 | : funcName 313 | } 314 | 315 | /** 316 | * Format deprecation message without color. 317 | */ 318 | 319 | function formatPlain (msg, caller, stack) { 320 | var timestamp = new Date().toUTCString() 321 | 322 | var formatted = timestamp + 323 | ' ' + this._namespace + 324 | ' deprecated ' + msg 325 | 326 | // add stack trace 327 | if (this._traced) { 328 | for (var i = 0; i < stack.length; i++) { 329 | formatted += '\n at ' + stack[i].toString() 330 | } 331 | 332 | return formatted 333 | } 334 | 335 | if (caller) { 336 | formatted += ' at ' + formatLocation(caller) 337 | } 338 | 339 | return formatted 340 | } 341 | 342 | /** 343 | * Format deprecation message with color. 344 | */ 345 | 346 | function formatColor (msg, caller, stack) { 347 | var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' + // bold cyan 348 | ' \x1b[33;1mdeprecated\x1b[22;39m' + // bold yellow 349 | ' \x1b[0m' + msg + '\x1b[39m' // reset 350 | 351 | // add stack trace 352 | if (this._traced) { 353 | for (var i = 0; i < stack.length; i++) { 354 | formatted += '\n \x1b[36mat ' + stack[i].toString() + '\x1b[39m' // cyan 355 | } 356 | 357 | return formatted 358 | } 359 | 360 | if (caller) { 361 | formatted += ' \x1b[36m' + formatLocation(caller) + '\x1b[39m' // cyan 362 | } 363 | 364 | return formatted 365 | } 366 | 367 | /** 368 | * Format call site location. 369 | */ 370 | 371 | function formatLocation (callSite) { 372 | return relative(basePath, callSite[0]) + 373 | ':' + callSite[1] + 374 | ':' + callSite[2] 375 | } 376 | 377 | /** 378 | * Get the stack as array of call sites. 379 | */ 380 | 381 | function getStack () { 382 | var limit = Error.stackTraceLimit 383 | var obj = {} 384 | var prep = Error.prepareStackTrace 385 | 386 | Error.prepareStackTrace = prepareObjectStackTrace 387 | Error.stackTraceLimit = Math.max(10, limit) 388 | 389 | // capture the stack 390 | Error.captureStackTrace(obj) 391 | 392 | // slice this function off the top 393 | var stack = obj.stack.slice(1) 394 | 395 | Error.prepareStackTrace = prep 396 | Error.stackTraceLimit = limit 397 | 398 | return stack 399 | } 400 | 401 | /** 402 | * Capture call site stack from v8. 403 | */ 404 | 405 | function prepareObjectStackTrace (obj, stack) { 406 | return stack 407 | } 408 | 409 | /** 410 | * Return a wrapped function in a deprecation message. 411 | */ 412 | 413 | function wrapfunction (fn, message) { 414 | if (typeof fn !== 'function') { 415 | throw new TypeError('argument fn must be a function') 416 | } 417 | 418 | var args = createArgumentsString(fn.length) 419 | var stack = getStack() 420 | var site = callSiteLocation(stack[1]) 421 | 422 | site.name = fn.name 423 | 424 | // eslint-disable-next-line no-new-func 425 | var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', 426 | '"use strict"\n' + 427 | 'return function (' + args + ') {' + 428 | 'log.call(deprecate, message, site)\n' + 429 | 'return fn.apply(this, arguments)\n' + 430 | '}')(fn, log, this, message, site) 431 | 432 | return deprecatedfn 433 | } 434 | 435 | /** 436 | * Wrap property in a deprecation message. 437 | */ 438 | 439 | function wrapproperty (obj, prop, message) { 440 | if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) { 441 | throw new TypeError('argument obj must be object') 442 | } 443 | 444 | var descriptor = Object.getOwnPropertyDescriptor(obj, prop) 445 | 446 | if (!descriptor) { 447 | throw new TypeError('must call property on owner object') 448 | } 449 | 450 | if (!descriptor.configurable) { 451 | throw new TypeError('property must be configurable') 452 | } 453 | 454 | var deprecate = this 455 | var stack = getStack() 456 | var site = callSiteLocation(stack[1]) 457 | 458 | // set site name 459 | site.name = prop 460 | 461 | // convert data descriptor 462 | if ('value' in descriptor) { 463 | descriptor = convertDataDescriptorToAccessor(obj, prop, message) 464 | } 465 | 466 | var get = descriptor.get 467 | var set = descriptor.set 468 | 469 | // wrap getter 470 | if (typeof get === 'function') { 471 | descriptor.get = function getter () { 472 | log.call(deprecate, message, site) 473 | return get.apply(this, arguments) 474 | } 475 | } 476 | 477 | // wrap setter 478 | if (typeof set === 'function') { 479 | descriptor.set = function setter () { 480 | log.call(deprecate, message, site) 481 | return set.apply(this, arguments) 482 | } 483 | } 484 | 485 | Object.defineProperty(obj, prop, descriptor) 486 | } 487 | 488 | /** 489 | * Create DeprecationError for deprecation 490 | */ 491 | 492 | function DeprecationError (namespace, message, stack) { 493 | var error = new Error() 494 | var stackString 495 | 496 | Object.defineProperty(error, 'constructor', { 497 | value: DeprecationError 498 | }) 499 | 500 | Object.defineProperty(error, 'message', { 501 | configurable: true, 502 | enumerable: false, 503 | value: message, 504 | writable: true 505 | }) 506 | 507 | Object.defineProperty(error, 'name', { 508 | enumerable: false, 509 | configurable: true, 510 | value: 'DeprecationError', 511 | writable: true 512 | }) 513 | 514 | Object.defineProperty(error, 'namespace', { 515 | configurable: true, 516 | enumerable: false, 517 | value: namespace, 518 | writable: true 519 | }) 520 | 521 | Object.defineProperty(error, 'stack', { 522 | configurable: true, 523 | enumerable: false, 524 | get: function () { 525 | if (stackString !== undefined) { 526 | return stackString 527 | } 528 | 529 | // prepare stack trace 530 | return (stackString = createStackString.call(this, stack)) 531 | }, 532 | set: function setter (val) { 533 | stackString = val 534 | } 535 | }) 536 | 537 | return error 538 | } 539 | -------------------------------------------------------------------------------- /lib/browser/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * depd 3 | * Copyright(c) 2015 Douglas Christopher Wilson 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict' 8 | 9 | /** 10 | * Module exports. 11 | * @public 12 | */ 13 | 14 | module.exports = depd 15 | 16 | /** 17 | * Create deprecate for namespace in caller. 18 | */ 19 | 20 | function depd (namespace) { 21 | if (!namespace) { 22 | throw new TypeError('argument namespace is required') 23 | } 24 | 25 | function deprecate (message) { 26 | // no-op in browser 27 | } 28 | 29 | deprecate._file = undefined 30 | deprecate._ignored = true 31 | deprecate._namespace = namespace 32 | deprecate._traced = false 33 | deprecate._warned = Object.create(null) 34 | 35 | deprecate.function = wrapfunction 36 | deprecate.property = wrapproperty 37 | 38 | return deprecate 39 | } 40 | 41 | /** 42 | * Return a wrapped function in a deprecation message. 43 | * 44 | * This is a no-op version of the wrapper, which does nothing but call 45 | * validation. 46 | */ 47 | 48 | function wrapfunction (fn, message) { 49 | if (typeof fn !== 'function') { 50 | throw new TypeError('argument fn must be a function') 51 | } 52 | 53 | return fn 54 | } 55 | 56 | /** 57 | * Wrap property in a deprecation message. 58 | * 59 | * This is a no-op version of the wrapper, which does nothing but call 60 | * validation. 61 | */ 62 | 63 | function wrapproperty (obj, prop, message) { 64 | if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) { 65 | throw new TypeError('argument obj must be object') 66 | } 67 | 68 | var descriptor = Object.getOwnPropertyDescriptor(obj, prop) 69 | 70 | if (!descriptor) { 71 | throw new TypeError('must call property on owner object') 72 | } 73 | 74 | if (!descriptor.configurable) { 75 | throw new TypeError('property must be configurable') 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "depd", 3 | "description": "Deprecate all the things", 4 | "version": "2.0.0", 5 | "author": "Douglas Christopher Wilson ", 6 | "license": "MIT", 7 | "keywords": [ 8 | "deprecate", 9 | "deprecated" 10 | ], 11 | "repository": "dougwilson/nodejs-depd", 12 | "browser": "lib/browser/index.js", 13 | "devDependencies": { 14 | "benchmark": "2.1.4", 15 | "beautify-benchmark": "0.2.4", 16 | "eslint": "7.21.0", 17 | "eslint-config-standard": "14.1.1", 18 | "eslint-plugin-import": "2.22.1", 19 | "eslint-plugin-markdown": "1.0.2", 20 | "eslint-plugin-node": "9.2.0", 21 | "eslint-plugin-promise": "4.2.1", 22 | "eslint-plugin-standard": "4.1.0", 23 | "nyc": "15.1.0", 24 | "mocha": "8.2.1", 25 | "safe-buffer": "5.1.2" 26 | }, 27 | "files": [ 28 | "lib/", 29 | "History.md", 30 | "LICENSE", 31 | "index.js", 32 | "Readme.md" 33 | ], 34 | "engines": { 35 | "node": ">= 0.8" 36 | }, 37 | "scripts": { 38 | "bench": "node benchmark/index.js", 39 | "lint": "eslint --plugin markdown --ext js,md .", 40 | "test": "mocha --reporter spec --bail test/", 41 | "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", 42 | "test-cov": "nyc --reporter=html --reporter=text npm test" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | mocha: true 3 | -------------------------------------------------------------------------------- /test/browserify.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | var browserify = tryRequire('browserify') 4 | var captureStderr = require('./support/capture-stderr') 5 | var depd = null 6 | var mylib = null 7 | var path = require('path') 8 | var run = browserify ? describe : describe.skip 9 | 10 | run('when browserified', function () { 11 | before(function (done) { 12 | var b = browserify() 13 | 14 | // require depd 15 | b.require(path.join(__dirname, '..'), { 16 | expose: 'depd' 17 | }) 18 | 19 | // require libs 20 | b.require(path.join(__dirname, 'fixtures', 'libs'), { 21 | expose: 'libs' 22 | }) 23 | 24 | b.bundle(function (err, buf) { 25 | if (err) return done(err) 26 | var require = eval(buf.toString()) // eslint-disable-line no-eval 27 | depd = require('depd') 28 | mylib = require('libs').my 29 | done() 30 | }) 31 | }) 32 | 33 | describe('depd(namespace)', function () { 34 | it('creates deprecated function', function () { 35 | assert.strictEqual(typeof depd('test'), 'function') 36 | }) 37 | 38 | it('requires namespace', function () { 39 | assert.throws(depd.bind(null), /namespace.*required/) 40 | }) 41 | }) 42 | 43 | describe('deprecate(message)', function () { 44 | it('should not log message', function () { 45 | function callold () { mylib.old() } 46 | assert.strictEqual(captureStderr(callold), '') 47 | }) 48 | 49 | describe('when message omitted', function () { 50 | it('should not log message', function () { 51 | function callold () { mylib.automsgnamed() } 52 | assert.strictEqual(captureStderr(callold), '') 53 | }) 54 | }) 55 | }) 56 | 57 | describe('deprecate.function(fn, message)', function () { 58 | it('should throw when not given function', function () { 59 | var deprecate = depd('test') 60 | assert.throws(deprecate.function.bind(deprecate, 2), /fn.*function/) 61 | }) 62 | 63 | it('should not log on call to function', function () { 64 | function callold () { mylib.oldfn() } 65 | assert.strictEqual(captureStderr(callold), '') 66 | }) 67 | 68 | it('should have same arity', function () { 69 | assert.strictEqual(mylib.oldfn.length, 2) 70 | }) 71 | 72 | it('should pass arguments', function () { 73 | var ret 74 | function callold () { ret = mylib.oldfn(1, 2) } 75 | assert.strictEqual(captureStderr(callold), '') 76 | assert.strictEqual(ret, 2) 77 | }) 78 | 79 | describe('when message omitted', function () { 80 | it('should not log message', function () { 81 | function callold () { mylib.oldfnauto() } 82 | assert.strictEqual(captureStderr(callold), '') 83 | }) 84 | }) 85 | }) 86 | 87 | describe('deprecate.property(obj, prop, message)', function () { 88 | it('should throw when given primitive', function () { 89 | var deprecate = depd('test') 90 | assert.throws(deprecate.property.bind(deprecate, 2), /obj.*object/) 91 | }) 92 | 93 | it('should throw when given missing property', function () { 94 | var deprecate = depd('test') 95 | var obj = {} 96 | assert.throws(deprecate.property.bind(deprecate, obj, 'blargh'), /property.*owner/) 97 | }) 98 | 99 | it('should throw when given non-configurable property', function () { 100 | var deprecate = depd('test') 101 | var obj = {} 102 | Object.defineProperty(obj, 'thing', { value: 'thingie' }) 103 | assert.throws(deprecate.property.bind(deprecate, obj, 'thing'), /property.*configurable/) 104 | }) 105 | 106 | it('should not log on access to property', function () { 107 | function callprop () { return mylib.propa } 108 | assert.strictEqual(captureStderr(callprop), '') 109 | }) 110 | 111 | it('should not log on setting property', function () { 112 | var val 113 | function callprop () { val = mylib.propa } 114 | function setprop () { mylib.propa = 'newval' } 115 | assert.strictEqual(captureStderr(setprop), '') 116 | assert.strictEqual(captureStderr(callprop), '') 117 | assert.strictEqual(val, 'newval') 118 | }) 119 | 120 | describe('when obj is a function', function () { 121 | it('should not log on access to property on function', function () { 122 | function callprop () { return mylib.fnprop.propa } 123 | assert.strictEqual(captureStderr(callprop), '') 124 | }) 125 | 126 | it('should not generate message on named function', function () { 127 | function callprop () { return mylib.fnprop.propautomsg } 128 | assert.strictEqual(captureStderr(callprop), '') 129 | }) 130 | }) 131 | 132 | describe('when value descriptor', function () { 133 | it('should not log on access and set', function () { 134 | function callold () { return mylib.propa } 135 | function setold () { mylib.propa = 'val' } 136 | assert.strictEqual(captureStderr(callold), '') 137 | assert.strictEqual(captureStderr(setold), '') 138 | }) 139 | 140 | it('should not log on set to non-writable', function () { 141 | function callold () { return mylib.propget } 142 | function setold () { mylib.propget = 'val' } 143 | assert.strictEqual(captureStderr(callold), '') 144 | assert.strictEqual(captureStderr(setold), '') 145 | }) 146 | }) 147 | 148 | describe('when accessor descriptor', function () { 149 | it('should log on access and set', function () { 150 | function callold () { return mylib.propdyn } 151 | function setold () { mylib.propdyn = 'val' } 152 | assert.strictEqual(captureStderr(callold), '') 153 | assert.strictEqual(captureStderr(setold), '') 154 | }) 155 | 156 | it('should not log on access when no accessor', function () { 157 | function callold () { return mylib.propsetter } 158 | assert.strictEqual(captureStderr(callold), '') 159 | }) 160 | 161 | it('should not log on set when no setter', function () { 162 | function callold () { mylib.propgetter = 'val' } 163 | assert.strictEqual(captureStderr(callold), '') 164 | }) 165 | }) 166 | 167 | describe('when message omitted', function () { 168 | it('should not generate message for method call on named function', function () { 169 | function callold () { return mylib.propauto } 170 | assert.strictEqual(captureStderr(callold), '') 171 | }) 172 | }) 173 | }) 174 | }) 175 | 176 | function tryRequire (name) { 177 | try { 178 | return require(name) 179 | } catch (e) { 180 | return undefined 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /test/fixtures/libs/cool.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate1 = require('../../..')('cool-lib') 3 | var deprecate2 = require('../../..')('neat-lib') 4 | 5 | exports.cool = function () { 6 | deprecate1('cool') 7 | } 8 | 9 | exports.neat = function () { 10 | deprecate2('neat') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/libs/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.my = require('./my') 3 | exports.strict = require('./strict') 4 | 5 | lazyRequireProperty(exports, 'cool', './cool') 6 | lazyRequireProperty(exports, 'multi', './multi') 7 | lazyRequireProperty(exports, 'new', './new') 8 | lazyRequireProperty(exports, 'old', './old') 9 | lazyRequireProperty(exports, 'thing', './thing') 10 | lazyRequireProperty(exports, 'trace', './trace') 11 | 12 | function lazyRequireProperty (obj, prop, path) { 13 | function get () { 14 | var val = require(path) 15 | 16 | Object.defineProperty(obj, prop, { 17 | configurable: true, 18 | enumerable: true, 19 | value: val 20 | }) 21 | 22 | return val 23 | } 24 | 25 | Object.defineProperty(obj, prop, { 26 | configurable: true, 27 | enumerable: true, 28 | get: get 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /test/fixtures/libs/multi.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate1 = require('../../..')('multi-lib') 3 | var deprecate2 = require('../../..')('multi-lib-other') 4 | 5 | exports.old = function () { 6 | deprecate1('old') 7 | } 8 | 9 | exports.old2 = function () { 10 | deprecate2('old2') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/libs/my.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate = require('../../..')('my-lib') 3 | 4 | exports.fn = fn 5 | exports.prop = 'thingie' 6 | 7 | exports.old = function () { 8 | deprecate('old') 9 | } 10 | 11 | exports.old2 = function () { 12 | deprecate('old2') 13 | } 14 | 15 | exports.oldfn = deprecate.function(fn, 'oldfn') 16 | 17 | exports.oldfnauto = deprecate.function(fn) 18 | 19 | exports.oldfnautoanon = deprecate.function(function () {}) 20 | 21 | exports.propa = 'thingie' 22 | exports.propauto = 'thingie' 23 | 24 | Object.defineProperty(exports, 'propget', { 25 | configurable: true, 26 | value: 'thingie', 27 | writable: false 28 | }) 29 | 30 | Object.defineProperty(exports, 'propdyn', { 31 | configurable: true, 32 | get: function () { return 'thingie' }, 33 | set: function () {} 34 | }) 35 | 36 | Object.defineProperty(exports, 'propgetter', { 37 | configurable: true, 38 | get: function () { return 'thingie' } 39 | }) 40 | 41 | // eslint-disable-next-line accessor-pairs 42 | Object.defineProperty(exports, 'propsetter', { 43 | configurable: true, 44 | set: function () {} 45 | }) 46 | 47 | deprecate.property(exports, 'propa', 'propa gone') 48 | deprecate.property(exports, 'propauto') 49 | deprecate.property(exports, 'propdyn') 50 | deprecate.property(exports, 'propget') 51 | deprecate.property(exports, 'propgetter') 52 | deprecate.property(exports, 'propsetter') 53 | 54 | exports.automsg = function () { 55 | deprecate() 56 | } 57 | 58 | exports.automsgnamed = function automsgnamed () { 59 | deprecate() 60 | } 61 | 62 | exports.automsganon = function () { 63 | (function () { deprecate() }()) 64 | } 65 | 66 | exports.fnprop = function thefn () {} 67 | exports.fnprop.propa = 'thingie' 68 | exports.fnprop.propautomsg = 'thingie' 69 | 70 | deprecate.property(exports.fnprop, 'propa', 'fn propa gone') 71 | deprecate.property(exports.fnprop, 'propautomsg') 72 | 73 | exports.layerfn = function () { 74 | exports.oldfn() 75 | } 76 | 77 | exports.layerprop = function () { 78 | return exports.propa 79 | } 80 | 81 | function fn (a1, a2) { 82 | return a2 83 | } 84 | -------------------------------------------------------------------------------- /test/fixtures/libs/new.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate = require('../../..')('New-Lib') 3 | 4 | exports.old = function () { 5 | deprecate('old') 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/libs/old.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate1 = require('../../..')('old-lib') 3 | var deprecate2 = require('../../..')('old-lib-other') 4 | var deprecate3 = require('../../..')('my-cool-module') 5 | 6 | exports.old = function () { 7 | deprecate1('old') 8 | } 9 | 10 | exports.old2 = function () { 11 | deprecate2('old2') 12 | } 13 | 14 | exports.oldfunction = function () { 15 | deprecate3('oldfunction') 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/libs/strict.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | 4 | var deprecate = require('../../..')('strict-lib') 5 | 6 | exports.old = function () { 7 | deprecate('old') 8 | } 9 | 10 | exports.oldfn = deprecate.function(fn, 'oldfn') 11 | 12 | exports.oldfnauto = deprecate.function(fn) 13 | 14 | exports.oldfnautoanon = deprecate.function(function () {}) 15 | 16 | exports.propa = 'thingie' 17 | exports.propauto = 'thingie' 18 | 19 | deprecate.property(exports, 'propa', 'propa gone') 20 | deprecate.property(exports, 'propauto') 21 | 22 | exports.automsg = function () { 23 | deprecate() 24 | } 25 | 26 | exports.automsgnamed = function automsgnamed () { 27 | deprecate() 28 | } 29 | 30 | exports.automsganon = function () { 31 | (function () { deprecate() }()) 32 | } 33 | 34 | exports.fnprop = function thefn () {} 35 | exports.fnprop.propa = 'thingie' 36 | exports.fnprop.propautomsg = 'thingie' 37 | 38 | deprecate.property(exports.fnprop, 'propa', 'fn propa gone') 39 | deprecate.property(exports.fnprop, 'propautomsg') 40 | 41 | exports.layerfn = function () { 42 | exports.oldfn() 43 | } 44 | 45 | exports.layerprop = function () { 46 | return exports.propa 47 | } 48 | 49 | function fn (a1, a2) { 50 | return a2 51 | } 52 | -------------------------------------------------------------------------------- /test/fixtures/libs/thing.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate = require('../../..')('thing-lib') 3 | 4 | exports.old = function () { 5 | deprecate('old') 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/libs/trace.js: -------------------------------------------------------------------------------- 1 | 2 | var deprecate1 = require('../../..')('trace-lib') 3 | var deprecate2 = require('../../..')('trace-lib-other') 4 | 5 | exports.old = function () { 6 | deprecate1('old') 7 | } 8 | 9 | exports.old2 = function () { 10 | deprecate2('old2') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/script.js: -------------------------------------------------------------------------------- 1 | 2 | var oldlib = require('./libs/old') 3 | 4 | run() 5 | 6 | function run () { 7 | oldlib.oldfunction() 8 | } 9 | -------------------------------------------------------------------------------- /test/support/capture-stderr.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * depd 3 | * Copyright(c) 2017 Douglas Christopher Wilson 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict' 8 | 9 | /** 10 | * Module dependencies. 11 | * @private 12 | */ 13 | 14 | var Buffer = require('safe-buffer').Buffer 15 | 16 | /** 17 | * Module exports. 18 | * @public 19 | */ 20 | 21 | module.exports = captureStderr 22 | 23 | /** 24 | * Capture STDERR output during a function invokation. 25 | * @public 26 | */ 27 | 28 | function captureStderr (fn, color) { 29 | var chunks = [] 30 | var isTTY = process.stderr.isTTY 31 | var write = process.stderr.write 32 | 33 | process.stderr.isTTY = Boolean(color) 34 | process.stderr.write = function write (chunk, encoding) { 35 | chunks.push(Buffer.from(chunk, encoding)) 36 | } 37 | 38 | try { 39 | fn() 40 | } finally { 41 | process.stderr.isTTY = isTTY 42 | process.stderr.write = write 43 | } 44 | 45 | return Buffer.concat(chunks).toString('utf8') 46 | } 47 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | var basename = require('path').basename 4 | var captureStderr = require('./support/capture-stderr') 5 | var depd = require('..') 6 | var libs = require('./fixtures/libs') 7 | var mylib = libs.my 8 | var path = require('path') 9 | var script = path.join(__dirname, 'fixtures', 'script.js') 10 | var spawn = require('child_process').spawn 11 | var strictlib = libs.strict 12 | 13 | describe('depd(namespace)', function () { 14 | it('creates deprecated function', function () { 15 | assert.strictEqual(typeof depd('test'), 'function') 16 | }) 17 | 18 | it('requires namespace', function () { 19 | assert.throws(depd.bind(null), /namespace.*required/) 20 | }) 21 | }) 22 | 23 | describe('deprecate(message)', function () { 24 | it('should log namespace', function () { 25 | function callold () { mylib.old() } 26 | assert.ok(captureStderr(callold).indexOf('my-lib') !== -1) 27 | }) 28 | 29 | it('should log deprecation', function () { 30 | function callold () { mylib.old() } 31 | assert.ok(captureStderr(callold).indexOf('deprecate') !== -1) 32 | }) 33 | 34 | it('should log message', function () { 35 | function callold () { mylib.old() } 36 | assert.ok(captureStderr(callold).indexOf('old') !== -1) 37 | }) 38 | 39 | it('should log call site', function () { 40 | function callold () { mylib.old() } 41 | var stderr = captureStderr(callold) 42 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 43 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 44 | }) 45 | 46 | it('should log call site from strict lib', function () { 47 | function callold () { strictlib.old() } 48 | var stderr = captureStderr(callold) 49 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 50 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 51 | }) 52 | 53 | it('should log call site regardless of Error.stackTraceLimit', function () { 54 | function callold () { mylib.old() } 55 | var limit = Error.stackTraceLimit 56 | try { 57 | Error.stackTraceLimit = 1 58 | var stderr = captureStderr(callold) 59 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 60 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 61 | } finally { 62 | Error.stackTraceLimit = limit 63 | } 64 | }) 65 | 66 | it('should log call site within eval', function () { 67 | function callold () { eval('mylib.old()') } // eslint-disable-line no-eval 68 | var stderr = captureStderr(callold) 69 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 70 | assert.ok(stderr.indexOf(':1:') !== -1) 71 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 72 | }) 73 | 74 | it('should log call site within strict', function () { 75 | function callold () { 'use strict'; mylib.old() } 76 | var stderr = captureStderr(callold) 77 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 78 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 79 | }) 80 | 81 | it('should only warn once per call site', function () { 82 | function callold () { 83 | for (var i = 0; i < 5; i++) { 84 | mylib.old() // single call site 85 | process.stderr.write('invoke ' + i + '\n') 86 | } 87 | } 88 | 89 | var stderr = captureStderr(callold) 90 | assert.strictEqual(stderr.split('deprecated').length, 2) 91 | assert.strictEqual(stderr.split('invoke').length, 6) 92 | }) 93 | 94 | it('should warn for different fns on same call site', function () { 95 | var prop 96 | 97 | function callold () { 98 | mylib[prop]() // call from same site 99 | } 100 | 101 | prop = 'old' 102 | assert.ok(captureStderr(callold).indexOf(basename(__filename)) !== -1) 103 | 104 | prop = 'old2' 105 | assert.ok(captureStderr(callold).indexOf(basename(__filename)) !== -1) 106 | }) 107 | 108 | it('should warn for different calls on same line', function () { 109 | function callold () { 110 | mylib.old(); mylib.old() 111 | } 112 | 113 | var stderr = captureStderr(callold) 114 | var fileline = stderr.match(/\.js:[0-9]+:/) 115 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 116 | assert.strictEqual(stderr.split('deprecated').length, 3) 117 | assert.strictEqual(stderr.split(fileline[0]).length, 3) 118 | }) 119 | 120 | describe('when message omitted', function () { 121 | it('should generate message for method call on named function', function () { 122 | function callold () { mylib.automsgnamed() } 123 | var stderr = captureStderr(callold) 124 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 125 | assert.ok(stderr.indexOf('deprecated') !== -1) 126 | assert.ok(stderr.indexOf(' automsgnamed ') !== -1) 127 | }) 128 | 129 | it('should generate message for function call on named function', function () { 130 | function callold () { 131 | var fn = mylib.automsgnamed 132 | fn() 133 | } 134 | var stderr = captureStderr(callold) 135 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 136 | assert.ok(stderr.indexOf('deprecated') !== -1) 137 | assert.ok(stderr.indexOf(' automsgnamed ') !== -1) 138 | }) 139 | 140 | it('should generate message for method call on unnamed function', function () { 141 | function callold () { mylib.automsg() } 142 | var stderr = captureStderr(callold) 143 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 144 | assert.ok(stderr.indexOf('deprecated') !== -1) 145 | assert.ok(stderr.indexOf(' exports.automsg ') !== -1) 146 | }) 147 | 148 | it('should generate message for function call on unnamed function', function () { 149 | function callold () { 150 | var fn = mylib.automsg 151 | fn() 152 | } 153 | var stderr = captureStderr(callold) 154 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 155 | assert.ok(stderr.indexOf('deprecated') !== -1) 156 | assert.ok(stderr.indexOf(' exports.automsg ') !== -1) 157 | }) 158 | 159 | it('should generate message for function call on anonymous function', function () { 160 | function callold () { mylib.automsganon() } 161 | var stderr = captureStderr(callold) 162 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 163 | assert.ok(stderr.indexOf('deprecated') !== -1) 164 | assert.ok(/ exports\.automsganon | /.test(stderr)) 165 | }) 166 | 167 | describe('in strict mode library', function () { 168 | it('should generate message for method call on named function', function () { 169 | function callold () { strictlib.automsgnamed() } 170 | var stderr = captureStderr(callold) 171 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 172 | assert.ok(stderr.indexOf('deprecated') !== -1) 173 | assert.ok(stderr.indexOf(' automsgnamed ') !== -1) 174 | }) 175 | 176 | it('should generate message for function call on named function', function () { 177 | function callold () { 178 | var fn = strictlib.automsgnamed 179 | fn() 180 | } 181 | var stderr = captureStderr(callold) 182 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 183 | assert.ok(stderr.indexOf('deprecated') !== -1) 184 | assert.ok(stderr.indexOf(' automsgnamed ') !== -1) 185 | }) 186 | 187 | it('should generate message for method call on unnamed function', function () { 188 | function callold () { strictlib.automsg() } 189 | var stderr = captureStderr(callold) 190 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 191 | assert.ok(stderr.indexOf('deprecated') !== -1) 192 | assert.ok(stderr.indexOf(' exports.automsg ') !== -1) 193 | }) 194 | 195 | it('should generate message for function call on unnamed function', function () { 196 | function callold () { 197 | var fn = strictlib.automsg 198 | fn() 199 | } 200 | var stderr = captureStderr(callold) 201 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 202 | assert.ok(stderr.indexOf('deprecated') !== -1) 203 | assert.ok(stderr.indexOf(' exports.automsg ') !== -1) 204 | }) 205 | 206 | it('should generate message for function call on anonymous function', function () { 207 | function callold () { strictlib.automsganon() } 208 | var stderr = captureStderr(callold) 209 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 210 | assert.ok(stderr.indexOf('deprecated') !== -1) 211 | assert.ok(/ exports\.automsganon | /.test(stderr)) 212 | }) 213 | }) 214 | }) 215 | 216 | describe('when output supports colors', function () { 217 | var stderr 218 | before(function () { 219 | function callold () { mylib.old() } 220 | stderr = captureStderr(callold, true) 221 | }) 222 | 223 | it('should log in color', function () { 224 | assert.notStrictEqual(stderr, '') 225 | assert.ok(stderr.indexOf('\x1b[') !== -1) 226 | }) 227 | 228 | it('should log namespace', function () { 229 | assert.ok(stderr.indexOf('my-lib') !== -1) 230 | }) 231 | 232 | it('should log deprecation', function () { 233 | assert.ok(stderr.indexOf('deprecate') !== -1) 234 | }) 235 | 236 | it('should log message', function () { 237 | assert.ok(stderr.indexOf('old') !== -1) 238 | }) 239 | 240 | it('should log call site', function () { 241 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 242 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 243 | }) 244 | }) 245 | 246 | describe('when output does not support colors', function () { 247 | var stderr 248 | before(function () { 249 | function callold () { mylib.old() } 250 | stderr = captureStderr(callold, false) 251 | }) 252 | 253 | it('should not log in color', function () { 254 | assert.notStrictEqual(stderr, '') 255 | assert.ok(stderr.indexOf('\x1b[') === -1) 256 | }) 257 | 258 | it('should log namespace', function () { 259 | assert.ok(stderr.indexOf('my-lib') !== -1) 260 | }) 261 | 262 | it('should log timestamp', function () { 263 | assert.ok(/\w+, \d+ \w+ \d{4} \d{2}:\d{2}:\d{2} \w{3}/.test(stderr)) 264 | }) 265 | 266 | it('should log deprecation', function () { 267 | assert.ok(stderr.indexOf('deprecate') !== -1) 268 | }) 269 | 270 | it('should log message', function () { 271 | assert.ok(stderr.indexOf('old') !== -1) 272 | }) 273 | 274 | it('should log call site', function () { 275 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 276 | assert.ok(/\.js:[0-9]+:[0-9]+/.test(stderr)) 277 | }) 278 | }) 279 | }) 280 | 281 | describe('deprecate.function(fn, message)', function () { 282 | it('should throw when not given function', function () { 283 | var deprecate = depd('test') 284 | assert.throws(deprecate.function.bind(deprecate, 2), /fn.*function/) 285 | }) 286 | 287 | it('should log on call to function', function () { 288 | function callold () { mylib.oldfn() } 289 | assert.ok(captureStderr(callold).indexOf(' oldfn ') !== -1) 290 | }) 291 | 292 | it('should have same arity', function () { 293 | assert.strictEqual(mylib.oldfn.length, 2) 294 | }) 295 | 296 | it('should pass arguments', function () { 297 | var ret 298 | function callold () { ret = mylib.oldfn(1, 2) } 299 | assert.ok(captureStderr(callold).indexOf(' oldfn ') !== -1) 300 | assert.strictEqual(ret, 2) 301 | }) 302 | 303 | it('should show call site outside scope', function () { 304 | function callold () { mylib.layerfn() } 305 | var stderr = captureStderr(callold) 306 | assert.ok(stderr.indexOf(' oldfn ') !== -1) 307 | assert.ok(/test.js:[0-9]+:[0-9]+/.test(stderr)) 308 | }) 309 | 310 | it('should show call site outside scope from strict lib', function () { 311 | function callold () { strictlib.layerfn() } 312 | var stderr = captureStderr(callold) 313 | assert.ok(stderr.indexOf(' oldfn ') !== -1) 314 | assert.ok(/test.js:[0-9]+:[0-9]+/.test(stderr)) 315 | }) 316 | 317 | it('should only warn once per call site', function () { 318 | function callold () { 319 | for (var i = 0; i < 5; i++) { 320 | mylib.oldfn() // single call site 321 | process.stderr.write('invoke ' + i + '\n') 322 | } 323 | } 324 | 325 | var stderr = captureStderr(callold) 326 | assert.strictEqual(stderr.split('deprecated').length, 2) 327 | assert.strictEqual(stderr.split('invoke').length, 6) 328 | }) 329 | 330 | it('should handle rapid calling of deprecated thing', function () { 331 | this.timeout(5000) 332 | 333 | function callold () { 334 | for (var i = 0; i < 10000; i++) { 335 | mylib.oldfn() 336 | } 337 | } 338 | 339 | var stderr = captureStderr(callold) 340 | assert.strictEqual(stderr.split('deprecated').length, 2) 341 | }) 342 | 343 | it('should warn for different calls on same line', function () { 344 | function callold () { 345 | mylib.oldfn(); mylib.oldfn() 346 | } 347 | 348 | var stderr = captureStderr(callold) 349 | var fileline = stderr.match(/\.js:[0-9]+:/) 350 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 351 | assert.strictEqual(stderr.split('deprecated').length, 3) 352 | assert.strictEqual(stderr.split(fileline[0]).length, 3) 353 | }) 354 | 355 | describe('when message omitted', function () { 356 | it('should generate message for method call on named function', function () { 357 | function callold () { mylib.oldfnauto() } 358 | var stderr = captureStderr(callold) 359 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 360 | assert.ok(stderr.indexOf('deprecated') !== -1) 361 | assert.ok(stderr.indexOf(' fn ') !== -1) 362 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 363 | }) 364 | 365 | it('should generate message for method call on anonymous function', function () { 366 | function callold () { mylib.oldfnautoanon() } 367 | var stderr = captureStderr(callold) 368 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 369 | assert.ok(stderr.indexOf('deprecated') !== -1) 370 | assert.ok(/ /.test(stderr)) 371 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 372 | }) 373 | 374 | describe('in strict mode library', function () { 375 | it('should generate message for method call on named function', function () { 376 | function callold () { strictlib.oldfnauto() } 377 | var stderr = captureStderr(callold) 378 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 379 | assert.ok(stderr.indexOf('deprecated') !== -1) 380 | assert.ok(stderr.indexOf(' fn ') !== -1) 381 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 382 | }) 383 | 384 | it('should generate message for method call on anonymous function', function () { 385 | function callold () { strictlib.oldfnautoanon() } 386 | var stderr = captureStderr(callold) 387 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 388 | assert.ok(stderr.indexOf('deprecated') !== -1) 389 | assert.ok(/ /.test(stderr)) 390 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 391 | }) 392 | }) 393 | }) 394 | }) 395 | 396 | describe('deprecate.property(obj, prop, message)', function () { 397 | it('should throw when given primitive', function () { 398 | var deprecate = depd('test') 399 | assert.throws(deprecate.property.bind(deprecate, 2), /obj.*object/) 400 | }) 401 | 402 | it('should throw when given missing property', function () { 403 | var deprecate = depd('test') 404 | var obj = {} 405 | assert.throws(deprecate.property.bind(deprecate, obj, 'blargh'), /property.*owner/) 406 | }) 407 | 408 | it('should throw when given non-configurable property', function () { 409 | var deprecate = depd('test') 410 | var obj = {} 411 | Object.defineProperty(obj, 'thing', { value: 'thingie' }) 412 | assert.throws(deprecate.property.bind(deprecate, obj, 'thing'), /property.*configurable/) 413 | }) 414 | 415 | it('should log on access to property', function () { 416 | function callprop () { return mylib.propa } 417 | var stderr = captureStderr(callprop) 418 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 419 | assert.ok(stderr.indexOf(' propa gone ') !== -1) 420 | }) 421 | 422 | it('should log on setting property', function () { 423 | var val 424 | function callprop () { val = mylib.propa } 425 | function setprop () { mylib.propa = 'newval' } 426 | var stderr = captureStderr(setprop) 427 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 428 | assert.ok(stderr.indexOf(' propa gone ') !== -1) 429 | assert.ok(captureStderr(callprop).indexOf(' deprecated ') !== -1) 430 | assert.strictEqual(val, 'newval') 431 | }) 432 | 433 | it('should only warn once per call site', function () { 434 | function callold () { 435 | for (var i = 0; i < 5; i++) { 436 | var v = mylib.propa || v // single call site 437 | process.stderr.write('access ' + i + '\n') 438 | } 439 | } 440 | 441 | var stderr = captureStderr(callold) 442 | assert.strictEqual(stderr.split('deprecated').length, 2) 443 | assert.strictEqual(stderr.split('access').length, 6) 444 | }) 445 | 446 | it('should warn for different accesses on same line', function () { 447 | function callold () { 448 | mylib.old(); mylib.old() 449 | } 450 | 451 | var stderr = captureStderr(callold) 452 | var fileline = stderr.match(/\.js:[0-9]+:/) 453 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 454 | assert.strictEqual(stderr.split('deprecated').length, 3) 455 | assert.strictEqual(stderr.split(fileline[0]).length, 3) 456 | }) 457 | 458 | it('should show call site outside scope', function () { 459 | function callold () { mylib.layerprop() } 460 | var stderr = captureStderr(callold) 461 | assert.ok(stderr.indexOf(' propa ') !== -1) 462 | assert.ok(/test.js:[0-9]+:[0-9]+/.test(stderr)) 463 | }) 464 | 465 | it('should show call site outside scope from strict lib', function () { 466 | function callold () { strictlib.layerprop() } 467 | var stderr = captureStderr(callold) 468 | assert.ok(stderr.indexOf(' propa ') !== -1) 469 | assert.ok(/test.js:[0-9]+:[0-9]+/.test(stderr)) 470 | }) 471 | 472 | describe('when obj is a function', function () { 473 | it('should log on access to property on function', function () { 474 | function callprop () { return mylib.fnprop.propa } 475 | var stderr = captureStderr(callprop) 476 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 477 | assert.ok(stderr.indexOf(' fn propa gone ') !== -1) 478 | }) 479 | 480 | it('should generate message on named function', function () { 481 | function callprop () { return mylib.fnprop.propautomsg } 482 | var stderr = captureStderr(callprop) 483 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 484 | assert.ok(stderr.indexOf(' thefn.propautomsg ') !== -1) 485 | }) 486 | 487 | describe('in strict mode library', function () { 488 | it('should log on access to property on function', function () { 489 | function callprop () { return strictlib.fnprop.propa } 490 | var stderr = captureStderr(callprop) 491 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 492 | assert.ok(stderr.indexOf(' fn propa gone ') !== -1) 493 | }) 494 | 495 | it('should generate message on named function', function () { 496 | function callprop () { return strictlib.fnprop.propautomsg } 497 | var stderr = captureStderr(callprop) 498 | assert.ok(stderr.indexOf(' deprecated ') !== -1) 499 | assert.ok(stderr.indexOf(' thefn.propautomsg ') !== -1) 500 | }) 501 | }) 502 | }) 503 | 504 | describe('when value descriptor', function () { 505 | it('should log on access and set', function () { 506 | function callold () { return mylib.propa } 507 | function setold () { mylib.propa = 'val' } 508 | assert.ok(captureStderr(callold).indexOf(' deprecated ') !== -1) 509 | assert.ok(captureStderr(setold).indexOf(' deprecated ') !== -1) 510 | }) 511 | 512 | it('should not log on set to non-writable', function () { 513 | function callold () { return mylib.propget } 514 | function setold () { mylib.propget = 'val' } 515 | assert.ok(captureStderr(callold).indexOf(' deprecated ') !== -1) 516 | assert.strictEqual(captureStderr(setold), '') 517 | }) 518 | }) 519 | 520 | describe('when accessor descriptor', function () { 521 | it('should log on access and set', function () { 522 | function callold () { return mylib.propdyn } 523 | function setold () { mylib.propdyn = 'val' } 524 | assert.ok(captureStderr(callold).indexOf(' deprecated ') !== -1) 525 | assert.ok(captureStderr(setold).indexOf(' deprecated ') !== -1) 526 | }) 527 | 528 | it('should not log on access when no accessor', function () { 529 | function callold () { return mylib.propsetter } 530 | assert.strictEqual(captureStderr(callold), '') 531 | }) 532 | 533 | it('should not log on set when no setter', function () { 534 | function callold () { mylib.propgetter = 'val' } 535 | assert.strictEqual(captureStderr(callold), '') 536 | }) 537 | }) 538 | 539 | describe('when message omitted', function () { 540 | it('should generate message for method call on named function', function () { 541 | function callold () { return mylib.propauto } 542 | var stderr = captureStderr(callold) 543 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 544 | assert.ok(stderr.indexOf('deprecated') !== -1) 545 | assert.ok(stderr.indexOf(' propauto ') !== -1) 546 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 547 | }) 548 | 549 | describe('in strict mode library', function () { 550 | it('should generate message for method call on named function', function () { 551 | function callold () { return strictlib.propauto } 552 | var stderr = captureStderr(callold) 553 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 554 | assert.ok(stderr.indexOf('deprecated') !== -1) 555 | assert.ok(stderr.indexOf(' propauto ') !== -1) 556 | assert.ok(/ at [^\\/]+[^:]+test\.js:/.test(stderr)) 557 | }) 558 | }) 559 | }) 560 | }) 561 | 562 | describe('process.on(\'deprecation\', fn)', function () { 563 | var error 564 | var stderr 565 | before(function () { 566 | process.on('deprecation', ondeprecation) 567 | function callold () { mylib.old() } 568 | stderr = captureStderr(callold) 569 | }) 570 | after(function () { 571 | process.removeListener('deprecation', ondeprecation) 572 | }) 573 | 574 | function ondeprecation (err) { error = err } 575 | 576 | it('should not write when listener exists', function () { 577 | assert.strictEqual(stderr, '') 578 | }) 579 | 580 | it('should emit error', function () { 581 | assert.ok(error) 582 | }) 583 | 584 | it('should emit DeprecationError', function () { 585 | assert.strictEqual(error.name, 'DeprecationError') 586 | }) 587 | 588 | it('should emit error with message', function () { 589 | assert.strictEqual(error.message, 'old') 590 | }) 591 | 592 | it('should emit error with namespace', function () { 593 | assert.strictEqual(error.namespace, 'my-lib') 594 | }) 595 | 596 | it('should emit error with proper [[Class]]', function () { 597 | assert.strictEqual(Object.prototype.toString.call(error), '[object Error]') 598 | }) 599 | 600 | it('should be instanceof Error', function () { 601 | assert.ok(error instanceof Error) 602 | }) 603 | 604 | it('should emit error with proper stack', function () { 605 | var stack = error.stack.split('\n') 606 | assert.strictEqual(stack[0], 'DeprecationError: my-lib deprecated old') 607 | assert.ok(/ {4}at callold \(.+test\.js:[0-9]+:[0-9]+\)/.test(stack[1])) 608 | }) 609 | 610 | it('should have writable properties', function () { 611 | error.name = 'bname' 612 | assert.strictEqual(error.name, 'bname') 613 | error.message = 'bmessage' 614 | assert.strictEqual(error.message, 'bmessage') 615 | error.stack = 'bstack' 616 | assert.strictEqual(error.stack, 'bstack') 617 | }) 618 | }) 619 | 620 | describe('process.env.NO_DEPRECATION', function () { 621 | var error 622 | function ondeprecation (err) { error = err } 623 | 624 | beforeEach(function () { 625 | error = null 626 | }) 627 | 628 | afterEach(function () { 629 | process.removeListener('deprecation', ondeprecation) 630 | }) 631 | 632 | after(function () { 633 | process.env.NO_DEPRECATION = '' 634 | }) 635 | 636 | it('should suppress given namespace', function () { 637 | process.env.NO_DEPRECATION = 'old-lib' 638 | var oldlib = libs.old 639 | assert.strictEqual(captureStderr(oldlib.old), '') 640 | assert.notStrictEqual(captureStderr(oldlib.old2), '') 641 | }) 642 | 643 | it('should suppress multiple namespaces', function () { 644 | process.env.NO_DEPRECATION = 'cool-lib,neat-lib' 645 | var coollib = libs.cool 646 | assert.strictEqual(captureStderr(coollib.cool), '') 647 | assert.strictEqual(captureStderr(coollib.neat), '') 648 | }) 649 | 650 | it('should be case-insensitive', function () { 651 | process.env.NO_DEPRECATION = 'NEW-LIB' 652 | var newlib = libs.new 653 | assert.strictEqual(captureStderr(newlib.old), '') 654 | }) 655 | 656 | it('should emit "deprecation" events anyway', function () { 657 | process.env.NO_DEPRECATION = 'thing-lib' 658 | var thinglib = libs.thing 659 | process.on('deprecation', ondeprecation) 660 | assert.strictEqual(captureStderr(thinglib.old), '') 661 | assert.strictEqual(error.namespace, 'thing-lib') 662 | }) 663 | 664 | describe('when *', function () { 665 | it('should suppress any namespace', function () { 666 | process.env.NO_DEPRECATION = '*' 667 | var multilib = libs.multi 668 | assert.strictEqual(captureStderr(multilib.old), '') 669 | assert.strictEqual(captureStderr(multilib.old2), '') 670 | }) 671 | }) 672 | }) 673 | 674 | describe('process.env.TRACE_DEPRECATION', function () { 675 | before(function () { 676 | process.env.TRACE_DEPRECATION = 'trace-lib' 677 | }) 678 | 679 | after(function () { 680 | process.env.TRACE_DEPRECATION = '' 681 | }) 682 | 683 | it('should trace given namespace', function () { 684 | var tracelib = libs.trace 685 | function callold () { tracelib.old() } 686 | assert.ok(captureStderr(callold).indexOf(' trace-lib deprecated old\n at callold (') !== -1) 687 | }) 688 | 689 | it('should not trace non-given namespace', function () { 690 | var tracelib = libs.trace 691 | function callold () { tracelib.old2() } 692 | assert.ok(captureStderr(callold).indexOf(' trace-lib-other deprecated old2 at ') !== -1) 693 | }) 694 | 695 | describe('when output supports colors', function () { 696 | var stderr 697 | before(function () { 698 | var tracelib = libs.trace 699 | function callold () { tracelib.old() } 700 | stderr = captureStderr(callold, true) 701 | }) 702 | 703 | it('should log in color', function () { 704 | assert.notStrictEqual(stderr, '') 705 | assert.ok(stderr.indexOf('\x1b[') !== -1) 706 | }) 707 | 708 | it('should log namespace', function () { 709 | assert.ok(stderr.indexOf('trace-lib') !== -1) 710 | }) 711 | 712 | it('should log call site in color', function () { 713 | assert.ok(stderr.indexOf(basename(__filename)) !== -1) 714 | assert.ok(/\x1b\[\d+mat callold \(/.test(stderr)) // eslint-disable-line no-control-regex 715 | }) 716 | }) 717 | }) 718 | 719 | describe('node script.js', function () { 720 | it('should display deprecation message', function (done) { 721 | captureChildStderr(script, [], function (err, stderr) { 722 | if (err) return done(err) 723 | var filename = path.relative(process.cwd(), script) 724 | assert.strictEqual(stderr, '__timestamp__ my-cool-module deprecated oldfunction at ' + filename + ':7:10\n') 725 | done() 726 | }) 727 | }) 728 | }) 729 | 730 | ;(function () { 731 | // --*-deprecation switches are 0.8+ 732 | // no good way to feature detect this sync 733 | var describe = /^v0\.6\./.test(process.version) 734 | ? global.describe.skip 735 | : global.describe 736 | 737 | describe('node --no-deprecation script.js', function () { 738 | it('should suppress deprecation message', function (done) { 739 | captureChildStderr(script, ['--no-deprecation'], function (err, stderr) { 740 | if (err) return done(err) 741 | assert.strictEqual(stderr, '') 742 | done() 743 | }) 744 | }) 745 | }) 746 | 747 | describe('node --trace-deprecation script.js', function () { 748 | it('should suppress deprecation message', function (done) { 749 | captureChildStderr(script, ['--trace-deprecation'], function (err, stderr) { 750 | if (err) return done(err) 751 | assert.ok(stderr.indexOf('__timestamp__ my-cool-module deprecated oldfunction\n at run (' + script + ':7:10)\n at') === 0) 752 | done() 753 | }) 754 | }) 755 | }) 756 | }()) 757 | 758 | function captureChildStderr (script, opts, callback) { 759 | var chunks = [] 760 | var env = { PATH: process.env.PATH } 761 | var exec = process.execPath 762 | 763 | var args = opts.concat(script) 764 | var proc = spawn(exec, args, { 765 | env: env 766 | }) 767 | 768 | proc.stdout.resume() 769 | proc.stderr.on('data', function ondata (chunk) { 770 | chunks.push(chunk) 771 | }) 772 | 773 | proc.on('error', callback) 774 | proc.on('exit', function () { 775 | var stderr = Buffer.concat(chunks) 776 | .toString('utf8') 777 | .replace(/\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+/, '__timestamp__') 778 | callback(null, stderr) 779 | }) 780 | } 781 | --------------------------------------------------------------------------------