├── .github └── workflows │ ├── ci.yml │ ├── commit-if-modified.sh │ ├── copyright-year.sh │ ├── isaacs-makework.yml │ └── package-json-repo.js ├── .travis.yml ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── proto-list.js └── test └── basic.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | node-version: [12.x, 14.x, 16.x, 17.x] 10 | platform: 11 | - os: ubuntu-latest 12 | shell: bash 13 | - os: macos-latest 14 | shell: bash 15 | - os: windows-latest 16 | shell: bash 17 | - os: windows-latest 18 | shell: powershell 19 | fail-fast: false 20 | 21 | runs-on: ${{ matrix.platform.os }} 22 | defaults: 23 | run: 24 | shell: ${{ matrix.platform.shell }} 25 | 26 | steps: 27 | - name: Checkout Repository 28 | uses: actions/checkout@v1.1.0 29 | 30 | - name: Use Nodejs ${{ matrix.node-version }} 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | 35 | - name: Install dependencies 36 | run: npm install 37 | 38 | - name: Run Tests 39 | run: npm test -- -c -t0 40 | -------------------------------------------------------------------------------- /.github/workflows/commit-if-modified.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git config --global user.email "$1" 3 | shift 4 | git config --global user.name "$1" 5 | shift 6 | message="$1" 7 | shift 8 | if [ $(git status --porcelain "$@" | egrep '^ M' | wc -l) -gt 0 ]; then 9 | git add "$@" 10 | git commit -m "$message" 11 | git push || git pull --rebase 12 | git push 13 | fi 14 | -------------------------------------------------------------------------------- /.github/workflows/copyright-year.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | dir=${1:-$PWD} 3 | dates=($(git log --date=format:%Y --pretty=format:'%ad' --reverse | sort | uniq)) 4 | if [ "${#dates[@]}" -eq 1 ]; then 5 | datestr="${dates}" 6 | else 7 | datestr="${dates}-${dates[${#dates[@]}-1]}" 8 | fi 9 | 10 | stripDate='s/^((.*)Copyright\b(.*?))((?:,\s*)?(([0-9]{4}\s*-\s*[0-9]{4})|(([0-9]{4},\s*)*[0-9]{4})))(?:,)?\s*(.*)\n$/$1$9\n/g' 11 | addDate='s/^.*Copyright(?:\s*\(c\))? /Copyright \(c\) '$datestr' /g' 12 | for l in $dir/LICENSE*; do 13 | perl -pi -e "$stripDate" $l 14 | perl -pi -e "$addDate" $l 15 | done 16 | -------------------------------------------------------------------------------- /.github/workflows/isaacs-makework.yml: -------------------------------------------------------------------------------- 1 | name: "various tidying up tasks to silence nagging" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | makework: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Use Node.js 17 | uses: actions/setup-node@v2.1.4 18 | with: 19 | node-version: 16.x 20 | - name: put repo in package.json 21 | run: node .github/workflows/package-json-repo.js 22 | - name: check in package.json if modified 23 | run: | 24 | bash -x .github/workflows/commit-if-modified.sh \ 25 | "package-json-repo-bot@example.com" \ 26 | "package.json Repo Bot" \ 27 | "chore: add repo to package.json" \ 28 | package.json package-lock.json 29 | - name: put all dates in license copyright line 30 | run: bash .github/workflows/copyright-year.sh 31 | - name: check in licenses if modified 32 | run: | 33 | bash .github/workflows/commit-if-modified.sh \ 34 | "license-year-bot@example.com" \ 35 | "License Year Bot" \ 36 | "chore: add copyright year to license" \ 37 | LICENSE* 38 | -------------------------------------------------------------------------------- /.github/workflows/package-json-repo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const pf = require.resolve(`${process.cwd()}/package.json`) 4 | const pj = require(pf) 5 | 6 | if (!pj.repository && process.env.GITHUB_REPOSITORY) { 7 | const fs = require('fs') 8 | const server = process.env.GITHUB_SERVER_URL || 'https://github.com' 9 | const repo = `${server}/${process.env.GITHUB_REPOSITORY}` 10 | pj.repository = repo 11 | const json = fs.readFileSync(pf, 'utf8') 12 | const match = json.match(/^\s*\{[\r\n]+([ \t]*)"/) 13 | const indent = match[1] 14 | const output = JSON.stringify(pj, null, indent || 2) + '\n' 15 | fs.writeFileSync(pf, output) 16 | } 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | language: node_js 3 | node_js: 4 | - '0.8' 5 | - '0.10' 6 | - '0.12' 7 | - 'iojs' 8 | before_install: 9 | - npm install -g npm@latest 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A list of objects, bound by their prototype chain. 2 | 3 | Used in npm's config stuff. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proto-list", 3 | "version": "1.2.4", 4 | "description": "A utility for managing a prototype chain", 5 | "main": "./proto-list.js", 6 | "author": "Isaac Z. Schlueter (http://blog.izs.me/)", 7 | "scripts": { 8 | "test": "tap test/*.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/isaacs/proto-list" 13 | }, 14 | "license": "ISC", 15 | "devDependencies": { 16 | "tap": "^15.1.6" 17 | }, 18 | "tap": { 19 | "lines": 60, 20 | "statements": 60, 21 | "branches": 60, 22 | "functions": 60 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /proto-list.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = ProtoList 3 | 4 | function setProto(obj, proto) { 5 | if (typeof Object.setPrototypeOf === "function") 6 | return Object.setPrototypeOf(obj, proto) 7 | else 8 | obj.__proto__ = proto 9 | } 10 | 11 | function ProtoList () { 12 | this.list = [] 13 | var root = null 14 | Object.defineProperty(this, 'root', { 15 | get: function () { return root }, 16 | set: function (r) { 17 | root = r 18 | if (this.list.length) { 19 | setProto(this.list[this.list.length - 1], r) 20 | } 21 | }, 22 | enumerable: true, 23 | configurable: true 24 | }) 25 | } 26 | 27 | ProtoList.prototype = 28 | { get length () { return this.list.length } 29 | , get keys () { 30 | var k = [] 31 | for (var i in this.list[0]) k.push(i) 32 | return k 33 | } 34 | , get snapshot () { 35 | var o = {} 36 | this.keys.forEach(function (k) { o[k] = this.get(k) }, this) 37 | return o 38 | } 39 | , get store () { 40 | return this.list[0] 41 | } 42 | , push : function (obj) { 43 | if (typeof obj !== "object") obj = {valueOf:obj} 44 | if (this.list.length >= 1) { 45 | setProto(this.list[this.list.length - 1], obj) 46 | } 47 | setProto(obj, this.root) 48 | return this.list.push(obj) 49 | } 50 | , pop : function () { 51 | if (this.list.length >= 2) { 52 | setProto(this.list[this.list.length - 2], this.root) 53 | } 54 | return this.list.pop() 55 | } 56 | , unshift : function (obj) { 57 | setProto(obj, this.list[0] || this.root) 58 | return this.list.unshift(obj) 59 | } 60 | , shift : function () { 61 | if (this.list.length === 1) { 62 | setProto(this.list[0], this.root) 63 | } 64 | return this.list.shift() 65 | } 66 | , get : function (key) { 67 | return this.list[0][key] 68 | } 69 | , set : function (key, val, save) { 70 | if (!this.length) this.push({}) 71 | if (save && this.list[0].hasOwnProperty(key)) this.push({}) 72 | return this.list[0][key] = val 73 | } 74 | , forEach : function (fn, thisp) { 75 | for (var key in this.list[0]) fn.call(thisp, key, this.list[0][key]) 76 | } 77 | , slice : function () { 78 | return this.list.slice.apply(this.list, arguments) 79 | } 80 | , splice : function () { 81 | // handle injections 82 | var ret = this.list.splice.apply(this.list, arguments) 83 | for (var i = 0, l = this.list.length; i < l; i++) { 84 | setProto(this.list[i], this.list[i + 1] || this.root) 85 | } 86 | return ret 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | var tap = require("tap") 2 | , test = tap.test 3 | , ProtoList = require("../proto-list.js") 4 | 5 | tap.plan(1) 6 | 7 | tap.test("protoList tests", function (t) { 8 | var p = new ProtoList 9 | p.push({foo:"bar"}) 10 | p.push({}) 11 | p.set("foo", "baz") 12 | t.equal(p.get("foo"), "baz") 13 | 14 | var p = new ProtoList 15 | p.push({foo:"bar"}) 16 | p.set("foo", "baz") 17 | t.equal(p.get("foo"), "baz") 18 | t.equal(p.length, 1) 19 | p.pop() 20 | t.equal(p.length, 0) 21 | p.set("foo", "asdf") 22 | t.equal(p.length, 1) 23 | t.equal(p.get("foo"), "asdf") 24 | p.push({bar:"baz"}) 25 | t.equal(p.length, 2) 26 | t.equal(p.get("foo"), "asdf") 27 | p.shift() 28 | t.equal(p.length, 1) 29 | t.equal(p.get("foo"), undefined) 30 | 31 | 32 | p.unshift({foo:"blo", bar:"rab"}) 33 | p.unshift({foo:"boo"}) 34 | t.equal(p.length, 3) 35 | t.equal(p.get("foo"), "boo") 36 | t.equal(p.get("bar"), "rab") 37 | 38 | var ret = p.splice(1, 1, {bar:"bar"}) 39 | t.same(ret, [{foo:"blo", bar:"rab"}]) 40 | t.equal(p.get("bar"), "bar") 41 | 42 | // should not inherit default object properties 43 | t.equal(p.get('hasOwnProperty'), undefined) 44 | 45 | // unless we give it those. 46 | p.root = {} 47 | t.equal(p.get('hasOwnProperty'), {}.hasOwnProperty) 48 | 49 | p.root = {default:'monkey'} 50 | t.equal(p.get('default'), 'monkey') 51 | 52 | p.push({red:'blue'}) 53 | p.push({red:'blue'}) 54 | p.push({red:'blue'}) 55 | while (p.length) { 56 | t.equal(p.get('default'), 'monkey') 57 | p.shift() 58 | } 59 | 60 | t.end() 61 | }) 62 | --------------------------------------------------------------------------------