├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ ├── dev.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test └── last-run.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gulp" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: dev 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | env: 9 | CI: true 10 | 11 | jobs: 12 | prettier: 13 | name: Format code 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event_name == 'push' }} 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Prettier 22 | uses: gulpjs/prettier_action@v3.0 23 | with: 24 | commit_message: 'chore: Run prettier' 25 | prettier_options: '--write .' 26 | 27 | test: 28 | name: Tests for Node ${{ matrix.node }} on ${{ matrix.os }} 29 | runs-on: ${{ matrix.os }} 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | node: [10, 12, 14, 16] 35 | os: [ubuntu-latest, windows-latest, macos-latest] 36 | 37 | steps: 38 | - name: Clone repository 39 | uses: actions/checkout@v2 40 | 41 | - name: Set Node.js version 42 | uses: actions/setup-node@v2 43 | with: 44 | node-version: ${{ matrix.node }} 45 | 46 | - run: node --version 47 | - run: npm --version 48 | 49 | - name: Install npm dependencies 50 | run: npm install 51 | 52 | - name: Run lint 53 | run: npm run lint 54 | 55 | - name: Run tests 56 | run: npm test 57 | 58 | - name: Coveralls 59 | uses: coverallsapp/github-action@v1.1.2 60 | with: 61 | github-token: ${{ secrets.GITHUB_TOKEN }} 62 | flag-name: ${{matrix.os}}-node-${{ matrix.node }} 63 | parallel: true 64 | 65 | coveralls: 66 | needs: test 67 | name: Finish up 68 | 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Coveralls Finished 72 | uses: coverallsapp/github-action@v1.1.2 73 | with: 74 | github-token: ${{ secrets.GITHUB_TOKEN }} 75 | parallel-finished: true 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | 8 | jobs: 9 | release-please: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: GoogleCloudPlatform/release-please-action@v2 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | release-type: node 16 | package-name: release-please-action 17 | bump-minor-pre-major: true 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Garbage files 64 | .DS_Store 65 | 66 | # Test results 67 | test.xunit 68 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.0.0](https://www.github.com/gulpjs/last-run/compare/v1.1.1...v2.0.0) (2022-01-10) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Normalize repository, dropping node <10.13 support (#8) 9 | 10 | ### Features 11 | 12 | * Remove default-resolution dependency since platform has consistent resolution ([50a17d8](https://www.github.com/gulpjs/last-run/commit/50a17d874923dafc5c00fbfff23c935424c79df0)) 13 | * Support non-extensible functions by removing WeakMap shim ([50a17d8](https://www.github.com/gulpjs/last-run/commit/50a17d874923dafc5c00fbfff23c935424c79df0)) 14 | 15 | 16 | ### Miscellaneous Chores 17 | 18 | * Normalize repository, dropping node <10.13 support ([#8](https://www.github.com/gulpjs/last-run/issues/8)) ([50a17d8](https://www.github.com/gulpjs/last-run/commit/50a17d874923dafc5c00fbfff23c935424c79df0)) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017, 2021 Blaine Bublitz and Eric Schoffstall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # last-run 8 | 9 | [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url] 10 | 11 | Capture and retrieve the last time a function was run. 12 | 13 | ## Usage 14 | 15 | ```js 16 | var lastRun = require('last-run'); 17 | 18 | function myFunc() {} 19 | 20 | myFunc(); 21 | // capture the run after (or before) calling the function 22 | lastRun.capture(myFunc); 23 | 24 | // retrieve the last run time 25 | lastRun(myFunc); 26 | //-> outputs the Date.now() when capture was called 27 | ``` 28 | 29 | ## API 30 | 31 | ### lastRun(fn, [timeResolution]) => [Timestamp] 32 | 33 | Takes a function (`fn`) and returns a timestamp of the last time the function was captured. 34 | 35 | Returns undefined if the function has not been captured. 36 | 37 | The timestamp is always given in millisecond but the time resolution can be reduced (rounded down). 38 | The use case is to be able to compare a build time to a file time attribute. 39 | On some file systems, `fs.stat` time attributes like `mtime` might have one second precision. 40 | 41 | ### lastRun.capture(fn, [timestamp]) 42 | 43 | Takes a function (`fn`) and captures the current timestamp with `Date.now()`. 44 | If passed the optional timestamp, captures that time instead of `Date.now()`. 45 | The captured timestamp can then be retrieved using the `lastRun` function. 46 | 47 | ### lastRun.release(fn) 48 | 49 | Takes a function (`fn`) and removes the last run timestamp for it. 50 | 51 | ## License 52 | 53 | MIT 54 | 55 | 56 | [downloads-image]: https://img.shields.io/npm/dm/last-run.svg?style=flat-square 57 | [npm-url]: https://www.npmjs.com/package/last-run 58 | [npm-image]: https://img.shields.io/npm/v/last-run.svg?style=flat-square 59 | 60 | [ci-url]: https://github.com/gulpjs/last-run/actions?query=workflow:dev 61 | [ci-image]: https://img.shields.io/github/actions/workflow/status/gulpjs/last-run/dev.yml?branch=master&style=flat-square 62 | 63 | [coveralls-url]: https://coveralls.io/r/gulpjs/last-run 64 | [coveralls-image]: https://img.shields.io/coveralls/gulpjs/last-run/master.svg?style=flat-square 65 | 66 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | 5 | var runtimes = new WeakMap(); 6 | 7 | function isFunction(fn) { 8 | return typeof fn === 'function'; 9 | } 10 | 11 | function lastRun(fn, timeResolution) { 12 | assert(isFunction(fn), 'Only functions can check lastRun'); 13 | 14 | var time = runtimes.get(fn); 15 | 16 | if (time == null) { 17 | return; 18 | } 19 | 20 | var resolution = parseInt(timeResolution, 10) || 1; 21 | 22 | return time - (time % resolution); 23 | } 24 | 25 | function capture(fn, timestamp) { 26 | assert(isFunction(fn), 'Only functions can be captured'); 27 | 28 | timestamp = timestamp || Date.now(); 29 | 30 | runtimes.set(fn, timestamp); 31 | } 32 | 33 | function release(fn) { 34 | assert(isFunction(fn), 'Only functions can be captured'); 35 | 36 | runtimes.delete(fn); 37 | } 38 | 39 | lastRun.capture = capture; 40 | lastRun.release = release; 41 | 42 | module.exports = lastRun; 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "last-run", 3 | "version": "2.0.0", 4 | "description": "Capture and retrieve the last time a function was run", 5 | "author": "Gulp Team (http://gulpjs.com/)", 6 | "contributors": [ 7 | "Blaine Bublitz " 8 | ], 9 | "repository": "gulpjs/last-run", 10 | "license": "MIT", 11 | "engines": { 12 | "node": ">= 10.13.0" 13 | }, 14 | "main": "index.js", 15 | "files": [ 16 | "LICENSE", 17 | "index.js" 18 | ], 19 | "scripts": { 20 | "lint": "eslint .", 21 | "pretest": "npm run lint", 22 | "test": "nyc mocha --async-only" 23 | }, 24 | "devDependencies": { 25 | "eslint": "^7.32.0", 26 | "eslint-config-gulp": "^5.0.1", 27 | "eslint-plugin-node": "^11.1.0", 28 | "expect": "^27.3.1", 29 | "mocha": "^8.4.0", 30 | "nyc": "^15.1.0" 31 | }, 32 | "nyc": { 33 | "reporter": [ 34 | "lcov", 35 | "text-summary" 36 | ] 37 | }, 38 | "prettier": { 39 | "singleQuote": true 40 | }, 41 | "keywords": [ 42 | "execution", 43 | "function", 44 | "last run", 45 | "timing" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/last-run.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('expect'); 4 | 5 | var lastRun = require('../'); 6 | 7 | describe('lastRun', function () { 8 | var since; 9 | 10 | beforeEach(function (done) { 11 | since = Date.now(); 12 | done(); 13 | }); 14 | 15 | it('should record function capture time', function (done) { 16 | function test() {} 17 | 18 | var beforeRun = Date.now(); 19 | 20 | lastRun.capture(test); 21 | 22 | var justRun = lastRun(test); 23 | var afterRun = Date.now(); 24 | expect(justRun).toBeGreaterThanOrEqual(beforeRun); 25 | expect(justRun).toBeLessThanOrEqual(afterRun); 26 | done(); 27 | }); 28 | 29 | it('should accept a timestamp', function (done) { 30 | function test() {} 31 | 32 | lastRun.capture(test, since); 33 | 34 | expect(lastRun(test)).toEqual(since); 35 | done(); 36 | }); 37 | 38 | it('removes last run time with release method', function (done) { 39 | function test() {} 40 | 41 | var beforeRun = Date.now(); 42 | 43 | lastRun.capture(test); 44 | 45 | var justRun = lastRun(test); 46 | var afterRun = Date.now(); 47 | expect(justRun).toBeGreaterThanOrEqual(beforeRun); 48 | expect(justRun).toBeLessThanOrEqual(afterRun); 49 | 50 | lastRun.release(test); 51 | 52 | expect(lastRun(test)).toBeUndefined(); 53 | done(); 54 | }); 55 | 56 | it('does not error on release if not captures', function (done) { 57 | function test() {} 58 | 59 | lastRun.release(test); 60 | 61 | expect(lastRun(test)).toBeUndefined(); 62 | done(); 63 | }); 64 | 65 | it('should return undefined for a function not captured', function (done) { 66 | function test() {} 67 | 68 | expect(lastRun(test)).toBeUndefined(); 69 | done(); 70 | }); 71 | 72 | it('should throw on non-functions', function (done) { 73 | function obj() { 74 | lastRun({}); 75 | } 76 | 77 | function str() { 78 | lastRun('wat'); 79 | } 80 | 81 | function num() { 82 | lastRun(1); 83 | } 84 | 85 | function undef() { 86 | lastRun(undefined); 87 | } 88 | 89 | function nul() { 90 | lastRun(null); 91 | } 92 | 93 | expect(obj).toThrow('Only functions can check lastRun'); 94 | expect(str).toThrow('Only functions can check lastRun'); 95 | expect(num).toThrow('Only functions can check lastRun'); 96 | expect(undef).toThrow('Only functions can check lastRun'); 97 | expect(nul).toThrow('Only functions can check lastRun'); 98 | done(); 99 | }); 100 | 101 | it('works with anonymous functions', function (done) { 102 | var test = function () {}; 103 | 104 | var beforeRun = Date.now(); 105 | 106 | lastRun.capture(test); 107 | 108 | var justRun = lastRun(test); 109 | var afterRun = Date.now(); 110 | expect(justRun).toBeGreaterThanOrEqual(beforeRun); 111 | expect(justRun).toBeLessThanOrEqual(afterRun); 112 | done(); 113 | }); 114 | 115 | it('should give time with 1s resolution', function (done) { 116 | var resolution = 1000; // 1s 117 | since = Date.now(); 118 | since = since - (since % resolution); 119 | 120 | function test() {} 121 | lastRun.capture(test); 122 | 123 | expect(lastRun(test, resolution)).toEqual(since); 124 | done(); 125 | }); 126 | 127 | it('should accept a string for resolution', function (done) { 128 | var resolution = '1000'; // 1s 129 | since = Date.now(); 130 | since = since - (since % 1000); 131 | 132 | function test() {} 133 | lastRun.capture(test); 134 | 135 | expect(lastRun(test, resolution)).toEqual(since); 136 | done(); 137 | }); 138 | 139 | it('should use default resolution when forced to 0ms resolution', function (done) { 140 | var resolution = 0; 141 | 142 | function test() {} 143 | lastRun.capture(test); 144 | 145 | expect(lastRun(test, resolution)).toBeGreaterThanOrEqual(since); 146 | done(); 147 | }); 148 | }); 149 | --------------------------------------------------------------------------------