├── .commitlintrc.js
├── .eslintrc.js
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ └── config.yml
├── actions
│ ├── create-check
│ │ └── action.yml
│ └── install-latest-npm
│ │ └── action.yml
├── dependabot.yml
├── matchers
│ └── tap.json
├── settings.yml
└── workflows
│ ├── audit.yml
│ ├── ci-release.yml
│ ├── ci.yml
│ ├── codeql-analysis.yml
│ ├── post-dependabot.yml
│ ├── pull-request.yml
│ ├── release-integration.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── .release-please-manifest.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── lib
└── index.js
├── package.json
├── release-please-config.json
└── test
├── bin.js
├── bundle-missing-dep.js
├── bundled-cycle.js
├── bundled-file-in-workspace.js
├── bundled-files.js
├── bundled-missing-optional.js
├── bundled-scoped-symlink.js
├── bundled-scoped.js
├── bundled-symlink.js
├── bundled-workspace.js
├── bundled.js
├── callbacks.js
├── can-exclude-shrinkwrap.js
├── cannot-exclude-package-json.js
├── cannot-exclude-readme.js
├── cannot-include-non-file-or-directory.js
├── cannot-include-package-lock.js
├── constructor.js
├── empty-npmignore.js
├── ignore-file-included-by-globstar.js
├── ignores.js
├── include-gitignore.js
├── nested-lock-and-core.js
├── package-json-bin-multiple.js
├── package-json-bin-single.js
├── package-json-dir-with-slashes.js
├── package-json-directory-glob.js
├── package-json-empty.js
├── package-json-filed-dir-nested-npmignore.js
├── package-json-files-and-containing-dir.js
├── package-json-files-dir-and-nested-ignore.js
├── package-json-files-including-npmignore.js
├── package-json-files-nested-dir-and-nested-ignore.js
├── package-json-files-no-dir-nested-npmignore.js
├── package-json-files-with-slashes.js
├── package-json-main.js
├── package-json-negate-non-root.js
├── package-json-negated-files.js
├── package-json-nested-readme-include-npmignore.js
├── package-json-nested-readme.js
├── package-json-nested.js
├── package-json-roots-and-nests.js
├── package-json.js
├── package-not-json.js
├── scoped.js
├── sorting.js
├── star-names.js
├── strip-slash-from-package-files-list-entries.js
├── symlink.js
├── utils
└── bin.js
└── workspace.js
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | /* This file is automatically added by @npmcli/template-oss. Do not edit. */
2 |
3 | module.exports = {
4 | extends: ['@commitlint/config-conventional'],
5 | rules: {
6 | 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'deps', 'chore']],
7 | 'header-max-length': [2, 'always', 80],
8 | 'subject-case': [0],
9 | 'body-max-line-length': [0],
10 | 'footer-max-line-length': [0],
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* This file is automatically added by @npmcli/template-oss. Do not edit. */
2 |
3 | 'use strict'
4 |
5 | const { readdirSync: readdir } = require('fs')
6 |
7 | const localConfigs = readdir(__dirname)
8 | .filter((file) => file.startsWith('.eslintrc.local.'))
9 | .map((file) => `./${file}`)
10 |
11 | module.exports = {
12 | root: true,
13 | ignorePatterns: [
14 | 'tap-testdir*/',
15 | ],
16 | extends: [
17 | '@npmcli',
18 | ...localConfigs,
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | * @npm/cli-team
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Bug
4 | description: File a bug/issue
5 | title: "[BUG]
"
6 | labels: [ Bug, Needs Triage ]
7 |
8 | body:
9 | - type: checkboxes
10 | attributes:
11 | label: Is there an existing issue for this?
12 | description: Please [search here](./issues) to see if an issue already exists for your problem.
13 | options:
14 | - label: I have searched the existing issues
15 | required: true
16 | - type: textarea
17 | attributes:
18 | label: Current Behavior
19 | description: A clear & concise description of what you're experiencing.
20 | validations:
21 | required: false
22 | - type: textarea
23 | attributes:
24 | label: Expected Behavior
25 | description: A clear & concise description of what you expected to happen.
26 | validations:
27 | required: false
28 | - type: textarea
29 | attributes:
30 | label: Steps To Reproduce
31 | description: Steps to reproduce the behavior.
32 | value: |
33 | 1. In this environment...
34 | 2. With this config...
35 | 3. Run '...'
36 | 4. See error...
37 | validations:
38 | required: false
39 | - type: textarea
40 | attributes:
41 | label: Environment
42 | description: |
43 | examples:
44 | - **npm**: 7.6.3
45 | - **Node**: 13.14.0
46 | - **OS**: Ubuntu 20.04
47 | - **platform**: Macbook Pro
48 | value: |
49 | - npm:
50 | - Node:
51 | - OS:
52 | - platform:
53 | validations:
54 | required: false
55 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | blank_issues_enabled: true
4 |
--------------------------------------------------------------------------------
/.github/actions/create-check/action.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: 'Create Check'
4 | inputs:
5 | name:
6 | required: true
7 | token:
8 | required: true
9 | sha:
10 | required: true
11 | check-name:
12 | default: ''
13 | outputs:
14 | check-id:
15 | value: ${{ steps.create-check.outputs.check_id }}
16 | runs:
17 | using: "composite"
18 | steps:
19 | - name: Get Workflow Job
20 | uses: actions/github-script@v7
21 | id: workflow
22 | env:
23 | JOB_NAME: "${{ inputs.name }}"
24 | SHA: "${{ inputs.sha }}"
25 | with:
26 | result-encoding: string
27 | script: |
28 | const { repo: { owner, repo}, runId, serverUrl } = context
29 | const { JOB_NAME, SHA } = process.env
30 |
31 | const job = await github.rest.actions.listJobsForWorkflowRun({
32 | owner,
33 | repo,
34 | run_id: runId,
35 | per_page: 100
36 | }).then(r => r.data.jobs.find(j => j.name.endsWith(JOB_NAME)))
37 |
38 | return [
39 | `This check is assosciated with ${serverUrl}/${owner}/${repo}/commit/${SHA}.`,
40 | 'Run logs:',
41 | job?.html_url || `could not be found for a job ending with: "${JOB_NAME}"`,
42 | ].join(' ')
43 | - name: Create Check
44 | uses: LouisBrunner/checks-action@v1.6.0
45 | id: create-check
46 | with:
47 | token: ${{ inputs.token }}
48 | sha: ${{ inputs.sha }}
49 | status: in_progress
50 | name: ${{ inputs.check-name || inputs.name }}
51 | output: |
52 | {"summary":"${{ steps.workflow.outputs.result }}"}
53 |
--------------------------------------------------------------------------------
/.github/actions/install-latest-npm/action.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: 'Install Latest npm'
4 | description: 'Install the latest version of npm compatible with the Node version'
5 | inputs:
6 | node:
7 | description: 'Current Node version'
8 | required: true
9 | runs:
10 | using: "composite"
11 | steps:
12 | # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows
13 | - name: Update Windows npm
14 | if: |
15 | runner.os == 'Windows' && (
16 | startsWith(inputs.node, 'v10.') ||
17 | startsWith(inputs.node, 'v12.') ||
18 | startsWith(inputs.node, 'v14.')
19 | )
20 | shell: cmd
21 | run: |
22 | curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz
23 | tar xf npm-7.5.4.tgz
24 | cd package
25 | node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz
26 | cd ..
27 | rmdir /s /q package
28 | - name: Install Latest npm
29 | shell: bash
30 | env:
31 | NODE_VERSION: ${{ inputs.node }}
32 | working-directory: ${{ runner.temp }}
33 | run: |
34 | MATCH=""
35 | SPECS=("latest" "next-10" "next-9" "next-8" "next-7" "next-6")
36 |
37 | echo "node@$NODE_VERSION"
38 |
39 | for SPEC in ${SPECS[@]}; do
40 | ENGINES=$(npm view npm@$SPEC --json | jq -r '.engines.node')
41 | echo "Checking if node@$NODE_VERSION satisfies npm@$SPEC ($ENGINES)"
42 |
43 | if npx semver -r "$ENGINES" "$NODE_VERSION" > /dev/null; then
44 | MATCH=$SPEC
45 | echo "Found compatible version: npm@$MATCH"
46 | break
47 | fi
48 | done
49 |
50 | if [ -z $MATCH ]; then
51 | echo "Could not find a compatible version of npm for node@$NODE_VERSION"
52 | exit 1
53 | fi
54 |
55 | npm i --prefer-online --no-fund --no-audit -g npm@$MATCH
56 | - name: npm Version
57 | shell: bash
58 | run: npm -v
59 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | version: 2
4 |
5 | updates:
6 | - package-ecosystem: npm
7 | directory: /
8 | schedule:
9 | interval: daily
10 | target-branch: "main"
11 | allow:
12 | - dependency-type: direct
13 | versioning-strategy: increase-if-necessary
14 | commit-message:
15 | prefix: deps
16 | prefix-development: chore
17 | labels:
18 | - "Dependencies"
19 | open-pull-requests-limit: 10
20 |
--------------------------------------------------------------------------------
/.github/matchers/tap.json:
--------------------------------------------------------------------------------
1 | {
2 | "//@npmcli/template-oss": "This file is automatically added by @npmcli/template-oss. Do not edit.",
3 | "problemMatcher": [
4 | {
5 | "owner": "tap",
6 | "pattern": [
7 | {
8 | "regexp": "^\\s*not ok \\d+ - (.*)",
9 | "message": 1
10 | },
11 | {
12 | "regexp": "^\\s*---"
13 | },
14 | {
15 | "regexp": "^\\s*at:"
16 | },
17 | {
18 | "regexp": "^\\s*line:\\s*(\\d+)",
19 | "line": 1
20 | },
21 | {
22 | "regexp": "^\\s*column:\\s*(\\d+)",
23 | "column": 1
24 | },
25 | {
26 | "regexp": "^\\s*file:\\s*(.*)",
27 | "file": 1
28 | }
29 | ]
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/.github/settings.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | repository:
4 | allow_merge_commit: false
5 | allow_rebase_merge: true
6 | allow_squash_merge: true
7 | squash_merge_commit_title: PR_TITLE
8 | squash_merge_commit_message: PR_BODY
9 | delete_branch_on_merge: true
10 | enable_automated_security_fixes: true
11 | enable_vulnerability_alerts: true
12 |
13 | branches:
14 | - name: main
15 | protection:
16 | required_status_checks: null
17 | enforce_admins: true
18 | block_creations: true
19 | required_pull_request_reviews:
20 | required_approving_review_count: 1
21 | require_code_owner_reviews: true
22 | require_last_push_approval: true
23 | dismiss_stale_reviews: true
24 | restrictions:
25 | apps: []
26 | users: []
27 | teams: [ "cli-team" ]
28 |
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Audit
4 |
5 | on:
6 | workflow_dispatch:
7 | schedule:
8 | # "At 08:00 UTC (01:00 PT) on Monday" https://crontab.guru/#0_8_*_*_1
9 | - cron: "0 8 * * 1"
10 |
11 | jobs:
12 | audit:
13 | name: Audit Dependencies
14 | if: github.repository_owner == 'npm'
15 | runs-on: ubuntu-latest
16 | defaults:
17 | run:
18 | shell: bash
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 | - name: Setup Git User
23 | run: |
24 | git config --global user.email "npm-cli+bot@github.com"
25 | git config --global user.name "npm CLI robot"
26 | - name: Setup Node
27 | uses: actions/setup-node@v4
28 | id: node
29 | with:
30 | node-version: 22.x
31 | check-latest: contains('22.x', '.x')
32 | - name: Install Latest npm
33 | uses: ./.github/actions/install-latest-npm
34 | with:
35 | node: ${{ steps.node.outputs.node-version }}
36 | - name: Install Dependencies
37 | run: npm i --ignore-scripts --no-audit --no-fund --package-lock
38 | - name: Run Production Audit
39 | run: npm audit --omit=dev
40 | - name: Run Full Audit
41 | run: npm audit --audit-level=none
42 |
--------------------------------------------------------------------------------
/.github/workflows/ci-release.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CI - Release
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | ref:
9 | required: true
10 | type: string
11 | default: main
12 | workflow_call:
13 | inputs:
14 | ref:
15 | required: true
16 | type: string
17 | check-sha:
18 | required: true
19 | type: string
20 |
21 | jobs:
22 | lint-all:
23 | name: Lint All
24 | if: github.repository_owner == 'npm'
25 | runs-on: ubuntu-latest
26 | defaults:
27 | run:
28 | shell: bash
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v4
32 | with:
33 | ref: ${{ inputs.ref }}
34 | - name: Setup Git User
35 | run: |
36 | git config --global user.email "npm-cli+bot@github.com"
37 | git config --global user.name "npm CLI robot"
38 | - name: Create Check
39 | id: create-check
40 | if: ${{ inputs.check-sha }}
41 | uses: ./.github/actions/create-check
42 | with:
43 | name: "Lint All"
44 | token: ${{ secrets.GITHUB_TOKEN }}
45 | sha: ${{ inputs.check-sha }}
46 | - name: Setup Node
47 | uses: actions/setup-node@v4
48 | id: node
49 | with:
50 | node-version: 22.x
51 | check-latest: contains('22.x', '.x')
52 | - name: Install Latest npm
53 | uses: ./.github/actions/install-latest-npm
54 | with:
55 | node: ${{ steps.node.outputs.node-version }}
56 | - name: Install Dependencies
57 | run: npm i --ignore-scripts --no-audit --no-fund
58 | - name: Lint
59 | run: npm run lint --ignore-scripts
60 | - name: Post Lint
61 | run: npm run postlint --ignore-scripts
62 | - name: Conclude Check
63 | uses: LouisBrunner/checks-action@v1.6.0
64 | if: steps.create-check.outputs.check-id && always()
65 | with:
66 | token: ${{ secrets.GITHUB_TOKEN }}
67 | conclusion: ${{ job.status }}
68 | check_id: ${{ steps.create-check.outputs.check-id }}
69 |
70 | test-all:
71 | name: Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }}
72 | if: github.repository_owner == 'npm'
73 | strategy:
74 | fail-fast: false
75 | matrix:
76 | platform:
77 | - name: Linux
78 | os: ubuntu-latest
79 | shell: bash
80 | - name: macOS
81 | os: macos-latest
82 | shell: bash
83 | - name: macOS
84 | os: macos-13
85 | shell: bash
86 | - name: Windows
87 | os: windows-latest
88 | shell: cmd
89 | node-version:
90 | - 20.17.0
91 | - 20.x
92 | - 22.9.0
93 | - 22.x
94 | exclude:
95 | - platform: { name: macOS, os: macos-13, shell: bash }
96 | node-version: 20.17.0
97 | - platform: { name: macOS, os: macos-13, shell: bash }
98 | node-version: 20.x
99 | - platform: { name: macOS, os: macos-13, shell: bash }
100 | node-version: 22.9.0
101 | - platform: { name: macOS, os: macos-13, shell: bash }
102 | node-version: 22.x
103 | runs-on: ${{ matrix.platform.os }}
104 | defaults:
105 | run:
106 | shell: ${{ matrix.platform.shell }}
107 | steps:
108 | - name: Checkout
109 | uses: actions/checkout@v4
110 | with:
111 | ref: ${{ inputs.ref }}
112 | - name: Setup Git User
113 | run: |
114 | git config --global user.email "npm-cli+bot@github.com"
115 | git config --global user.name "npm CLI robot"
116 | - name: Create Check
117 | id: create-check
118 | if: ${{ inputs.check-sha }}
119 | uses: ./.github/actions/create-check
120 | with:
121 | name: "Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }}"
122 | token: ${{ secrets.GITHUB_TOKEN }}
123 | sha: ${{ inputs.check-sha }}
124 | - name: Setup Node
125 | uses: actions/setup-node@v4
126 | id: node
127 | with:
128 | node-version: ${{ matrix.node-version }}
129 | check-latest: contains(matrix.node-version, '.x')
130 | - name: Install Latest npm
131 | uses: ./.github/actions/install-latest-npm
132 | with:
133 | node: ${{ steps.node.outputs.node-version }}
134 | - name: Install Dependencies
135 | run: npm i --ignore-scripts --no-audit --no-fund
136 | - name: Add Problem Matcher
137 | run: echo "::add-matcher::.github/matchers/tap.json"
138 | - name: Test
139 | run: npm test --ignore-scripts
140 | - name: Conclude Check
141 | uses: LouisBrunner/checks-action@v1.6.0
142 | if: steps.create-check.outputs.check-id && always()
143 | with:
144 | token: ${{ secrets.GITHUB_TOKEN }}
145 | conclusion: ${{ job.status }}
146 | check_id: ${{ steps.create-check.outputs.check-id }}
147 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CI
4 |
5 | on:
6 | workflow_dispatch:
7 | pull_request:
8 | push:
9 | branches:
10 | - main
11 | schedule:
12 | # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1
13 | - cron: "0 9 * * 1"
14 |
15 | jobs:
16 | lint:
17 | name: Lint
18 | if: github.repository_owner == 'npm'
19 | runs-on: ubuntu-latest
20 | defaults:
21 | run:
22 | shell: bash
23 | steps:
24 | - name: Checkout
25 | uses: actions/checkout@v4
26 | - name: Setup Git User
27 | run: |
28 | git config --global user.email "npm-cli+bot@github.com"
29 | git config --global user.name "npm CLI robot"
30 | - name: Setup Node
31 | uses: actions/setup-node@v4
32 | id: node
33 | with:
34 | node-version: 22.x
35 | check-latest: contains('22.x', '.x')
36 | - name: Install Latest npm
37 | uses: ./.github/actions/install-latest-npm
38 | with:
39 | node: ${{ steps.node.outputs.node-version }}
40 | - name: Install Dependencies
41 | run: npm i --ignore-scripts --no-audit --no-fund
42 | - name: Lint
43 | run: npm run lint --ignore-scripts
44 | - name: Post Lint
45 | run: npm run postlint --ignore-scripts
46 |
47 | test:
48 | name: Test - ${{ matrix.platform.name }} - ${{ matrix.node-version }}
49 | if: github.repository_owner == 'npm'
50 | strategy:
51 | fail-fast: false
52 | matrix:
53 | platform:
54 | - name: Linux
55 | os: ubuntu-latest
56 | shell: bash
57 | - name: macOS
58 | os: macos-latest
59 | shell: bash
60 | - name: macOS
61 | os: macos-13
62 | shell: bash
63 | - name: Windows
64 | os: windows-latest
65 | shell: cmd
66 | node-version:
67 | - 20.17.0
68 | - 20.x
69 | - 22.9.0
70 | - 22.x
71 | exclude:
72 | - platform: { name: macOS, os: macos-13, shell: bash }
73 | node-version: 20.17.0
74 | - platform: { name: macOS, os: macos-13, shell: bash }
75 | node-version: 20.x
76 | - platform: { name: macOS, os: macos-13, shell: bash }
77 | node-version: 22.9.0
78 | - platform: { name: macOS, os: macos-13, shell: bash }
79 | node-version: 22.x
80 | runs-on: ${{ matrix.platform.os }}
81 | defaults:
82 | run:
83 | shell: ${{ matrix.platform.shell }}
84 | steps:
85 | - name: Checkout
86 | uses: actions/checkout@v4
87 | - name: Setup Git User
88 | run: |
89 | git config --global user.email "npm-cli+bot@github.com"
90 | git config --global user.name "npm CLI robot"
91 | - name: Setup Node
92 | uses: actions/setup-node@v4
93 | id: node
94 | with:
95 | node-version: ${{ matrix.node-version }}
96 | check-latest: contains(matrix.node-version, '.x')
97 | - name: Install Latest npm
98 | uses: ./.github/actions/install-latest-npm
99 | with:
100 | node: ${{ steps.node.outputs.node-version }}
101 | - name: Install Dependencies
102 | run: npm i --ignore-scripts --no-audit --no-fund
103 | - name: Add Problem Matcher
104 | run: echo "::add-matcher::.github/matchers/tap.json"
105 | - name: Test
106 | run: npm test --ignore-scripts
107 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CodeQL
4 |
5 | on:
6 | push:
7 | branches:
8 | - main
9 | pull_request:
10 | branches:
11 | - main
12 | schedule:
13 | # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1
14 | - cron: "0 10 * * 1"
15 |
16 | jobs:
17 | analyze:
18 | name: Analyze
19 | runs-on: ubuntu-latest
20 | permissions:
21 | actions: read
22 | contents: read
23 | security-events: write
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@v4
27 | - name: Setup Git User
28 | run: |
29 | git config --global user.email "npm-cli+bot@github.com"
30 | git config --global user.name "npm CLI robot"
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v3
33 | with:
34 | languages: javascript
35 | - name: Perform CodeQL Analysis
36 | uses: github/codeql-action/analyze@v3
37 |
--------------------------------------------------------------------------------
/.github/workflows/post-dependabot.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Post Dependabot
4 |
5 | on: pull_request
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | template-oss:
12 | name: template-oss
13 | if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]'
14 | runs-on: ubuntu-latest
15 | defaults:
16 | run:
17 | shell: bash
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 | with:
22 | ref: ${{ github.event.pull_request.head.ref }}
23 | - name: Setup Git User
24 | run: |
25 | git config --global user.email "npm-cli+bot@github.com"
26 | git config --global user.name "npm CLI robot"
27 | - name: Setup Node
28 | uses: actions/setup-node@v4
29 | id: node
30 | with:
31 | node-version: 22.x
32 | check-latest: contains('22.x', '.x')
33 | - name: Install Latest npm
34 | uses: ./.github/actions/install-latest-npm
35 | with:
36 | node: ${{ steps.node.outputs.node-version }}
37 | - name: Install Dependencies
38 | run: npm i --ignore-scripts --no-audit --no-fund
39 | - name: Fetch Dependabot Metadata
40 | id: metadata
41 | uses: dependabot/fetch-metadata@v1
42 | with:
43 | github-token: ${{ secrets.GITHUB_TOKEN }}
44 |
45 | # Dependabot can update multiple directories so we output which directory
46 | # it is acting on so we can run the command for the correct root or workspace
47 | - name: Get Dependabot Directory
48 | if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss')
49 | id: flags
50 | run: |
51 | dependabot_dir="${{ steps.metadata.outputs.directory }}"
52 | if [[ "$dependabot_dir" == "/" || "$dependabot_dir" == "/main" ]]; then
53 | echo "workspace=-iwr" >> $GITHUB_OUTPUT
54 | else
55 | # strip leading slash from directory so it works as a
56 | # a path to the workspace flag
57 | echo "workspace=-w ${dependabot_dir#/}" >> $GITHUB_OUTPUT
58 | fi
59 |
60 | - name: Apply Changes
61 | if: steps.flags.outputs.workspace
62 | id: apply
63 | run: |
64 | npm run template-oss-apply ${{ steps.flags.outputs.workspace }}
65 | if [[ `git status --porcelain` ]]; then
66 | echo "changes=true" >> $GITHUB_OUTPUT
67 | fi
68 | # This only sets the conventional commit prefix. This workflow can't reliably determine
69 | # what the breaking change is though. If a BREAKING CHANGE message is required then
70 | # this PR check will fail and the commit will be amended with stafftools
71 | if [[ "${{ steps.metadata.outputs.update-type }}" == "version-update:semver-major" ]]; then
72 | prefix='feat!'
73 | else
74 | prefix='chore'
75 | fi
76 | echo "message=$prefix: postinstall for dependabot template-oss PR" >> $GITHUB_OUTPUT
77 |
78 | # This step will fail if template-oss has made any workflow updates. It is impossible
79 | # for a workflow to update other workflows. In the case it does fail, we continue
80 | # and then try to apply only a portion of the changes in the next step
81 | - name: Push All Changes
82 | if: steps.apply.outputs.changes
83 | id: push
84 | continue-on-error: true
85 | env:
86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87 | run: |
88 | git commit -am "${{ steps.apply.outputs.message }}"
89 | git push
90 |
91 | # If the previous step failed, then reset the commit and remove any workflow changes
92 | # and attempt to commit and push again. This is helpful because we will have a commit
93 | # with the correct prefix that we can then --amend with @npmcli/stafftools later.
94 | - name: Push All Changes Except Workflows
95 | if: steps.apply.outputs.changes && steps.push.outcome == 'failure'
96 | env:
97 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
98 | run: |
99 | git reset HEAD~
100 | git checkout HEAD -- .github/workflows/
101 | git clean -fd .github/workflows/
102 | git commit -am "${{ steps.apply.outputs.message }}"
103 | git push
104 |
105 | # Check if all the necessary template-oss changes were applied. Since we continued
106 | # on errors in one of the previous steps, this check will fail if our follow up
107 | # only applied a portion of the changes and we need to followup manually.
108 | #
109 | # Note that this used to run `lint` and `postlint` but that will fail this action
110 | # if we've also shipped any linting changes separate from template-oss. We do
111 | # linting in another action, so we want to fail this one only if there are
112 | # template-oss changes that could not be applied.
113 | - name: Check Changes
114 | if: steps.apply.outputs.changes
115 | run: |
116 | npm exec --offline ${{ steps.flags.outputs.workspace }} -- template-oss-check
117 |
118 | - name: Fail on Breaking Change
119 | if: steps.apply.outputs.changes && startsWith(steps.apply.outputs.message, 'feat!')
120 | run: |
121 | echo "This PR has a breaking change. Run 'npx -p @npmcli/stafftools gh template-oss-fix'"
122 | echo "for more information on how to fix this with a BREAKING CHANGE footer."
123 | exit 1
124 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Pull Request
4 |
5 | on:
6 | pull_request:
7 | types:
8 | - opened
9 | - reopened
10 | - edited
11 | - synchronize
12 |
13 | jobs:
14 | commitlint:
15 | name: Lint Commits
16 | if: github.repository_owner == 'npm'
17 | runs-on: ubuntu-latest
18 | defaults:
19 | run:
20 | shell: bash
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 | - name: Setup Git User
27 | run: |
28 | git config --global user.email "npm-cli+bot@github.com"
29 | git config --global user.name "npm CLI robot"
30 | - name: Setup Node
31 | uses: actions/setup-node@v4
32 | id: node
33 | with:
34 | node-version: 22.x
35 | check-latest: contains('22.x', '.x')
36 | - name: Install Latest npm
37 | uses: ./.github/actions/install-latest-npm
38 | with:
39 | node: ${{ steps.node.outputs.node-version }}
40 | - name: Install Dependencies
41 | run: npm i --ignore-scripts --no-audit --no-fund
42 | - name: Run Commitlint on Commits
43 | id: commit
44 | continue-on-error: true
45 | run: npx --offline commitlint -V --from 'origin/${{ github.base_ref }}' --to ${{ github.event.pull_request.head.sha }}
46 | - name: Run Commitlint on PR Title
47 | if: steps.commit.outcome == 'failure'
48 | env:
49 | PR_TITLE: ${{ github.event.pull_request.title }}
50 | run: echo "$PR_TITLE" | npx --offline commitlint -V
51 |
--------------------------------------------------------------------------------
/.github/workflows/release-integration.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Release Integration
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | releases:
9 | required: true
10 | type: string
11 | description: 'A json array of releases. Required fields: publish: tagName, publishTag. publish check: pkgName, version'
12 | workflow_call:
13 | inputs:
14 | releases:
15 | required: true
16 | type: string
17 | description: 'A json array of releases. Required fields: publish: tagName, publishTag. publish check: pkgName, version'
18 | secrets:
19 | PUBLISH_TOKEN:
20 | required: true
21 |
22 | jobs:
23 | publish:
24 | name: Publish
25 | runs-on: ubuntu-latest
26 | defaults:
27 | run:
28 | shell: bash
29 | permissions:
30 | id-token: write
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | with:
35 | ref: ${{ fromJSON(inputs.releases)[0].tagName }}
36 | - name: Setup Git User
37 | run: |
38 | git config --global user.email "npm-cli+bot@github.com"
39 | git config --global user.name "npm CLI robot"
40 | - name: Setup Node
41 | uses: actions/setup-node@v4
42 | id: node
43 | with:
44 | node-version: 22.x
45 | check-latest: contains('22.x', '.x')
46 | - name: Install Latest npm
47 | uses: ./.github/actions/install-latest-npm
48 | with:
49 | node: ${{ steps.node.outputs.node-version }}
50 | - name: Install Dependencies
51 | run: npm i --ignore-scripts --no-audit --no-fund
52 | - name: Set npm authToken
53 | run: npm config set '//registry.npmjs.org/:_authToken'=\${PUBLISH_TOKEN}
54 | - name: Publish
55 | env:
56 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
57 | RELEASES: ${{ inputs.releases }}
58 | run: |
59 | EXIT_CODE=0
60 |
61 | for release in $(echo $RELEASES | jq -r '.[] | @base64'); do
62 | PUBLISH_TAG=$(echo "$release" | base64 --decode | jq -r .publishTag)
63 | npm publish --provenance --tag="$PUBLISH_TAG"
64 | STATUS=$?
65 | if [[ "$STATUS" -eq 1 ]]; then
66 | EXIT_CODE=$STATUS
67 | fi
68 | done
69 |
70 | exit $EXIT_CODE
71 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Release
4 |
5 | on:
6 | push:
7 | branches:
8 | - main
9 |
10 | permissions:
11 | contents: write
12 | pull-requests: write
13 | checks: write
14 |
15 | jobs:
16 | release:
17 | outputs:
18 | pr: ${{ steps.release.outputs.pr }}
19 | pr-branch: ${{ steps.release.outputs.pr-branch }}
20 | pr-number: ${{ steps.release.outputs.pr-number }}
21 | pr-sha: ${{ steps.release.outputs.pr-sha }}
22 | releases: ${{ steps.release.outputs.releases }}
23 | comment-id: ${{ steps.create-comment.outputs.comment-id || steps.update-comment.outputs.comment-id }}
24 | check-id: ${{ steps.create-check.outputs.check-id }}
25 | name: Release
26 | if: github.repository_owner == 'npm'
27 | runs-on: ubuntu-latest
28 | defaults:
29 | run:
30 | shell: bash
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | - name: Setup Git User
35 | run: |
36 | git config --global user.email "npm-cli+bot@github.com"
37 | git config --global user.name "npm CLI robot"
38 | - name: Setup Node
39 | uses: actions/setup-node@v4
40 | id: node
41 | with:
42 | node-version: 22.x
43 | check-latest: contains('22.x', '.x')
44 | - name: Install Latest npm
45 | uses: ./.github/actions/install-latest-npm
46 | with:
47 | node: ${{ steps.node.outputs.node-version }}
48 | - name: Install Dependencies
49 | run: npm i --ignore-scripts --no-audit --no-fund
50 | - name: Release Please
51 | id: release
52 | env:
53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 | run: npx --offline template-oss-release-please --branch="${{ github.ref_name }}" --backport="" --defaultTag="latest"
55 | - name: Create Release Manager Comment Text
56 | if: steps.release.outputs.pr-number
57 | uses: actions/github-script@v7
58 | id: comment-text
59 | with:
60 | result-encoding: string
61 | script: |
62 | const { runId, repo: { owner, repo } } = context
63 | const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId })
64 | return['## Release Manager', `Release workflow run: ${workflow.html_url}`].join('\n\n')
65 | - name: Find Release Manager Comment
66 | uses: peter-evans/find-comment@v2
67 | if: steps.release.outputs.pr-number
68 | id: found-comment
69 | with:
70 | issue-number: ${{ steps.release.outputs.pr-number }}
71 | comment-author: 'github-actions[bot]'
72 | body-includes: '## Release Manager'
73 | - name: Create Release Manager Comment
74 | id: create-comment
75 | if: steps.release.outputs.pr-number && !steps.found-comment.outputs.comment-id
76 | uses: peter-evans/create-or-update-comment@v3
77 | with:
78 | issue-number: ${{ steps.release.outputs.pr-number }}
79 | body: ${{ steps.comment-text.outputs.result }}
80 | - name: Update Release Manager Comment
81 | id: update-comment
82 | if: steps.release.outputs.pr-number && steps.found-comment.outputs.comment-id
83 | uses: peter-evans/create-or-update-comment@v3
84 | with:
85 | comment-id: ${{ steps.found-comment.outputs.comment-id }}
86 | body: ${{ steps.comment-text.outputs.result }}
87 | edit-mode: 'replace'
88 | - name: Create Check
89 | id: create-check
90 | uses: ./.github/actions/create-check
91 | if: steps.release.outputs.pr-sha
92 | with:
93 | name: "Release"
94 | token: ${{ secrets.GITHUB_TOKEN }}
95 | sha: ${{ steps.release.outputs.pr-sha }}
96 |
97 | update:
98 | needs: release
99 | outputs:
100 | sha: ${{ steps.commit.outputs.sha }}
101 | check-id: ${{ steps.create-check.outputs.check-id }}
102 | name: Update - Release
103 | if: github.repository_owner == 'npm' && needs.release.outputs.pr
104 | runs-on: ubuntu-latest
105 | defaults:
106 | run:
107 | shell: bash
108 | steps:
109 | - name: Checkout
110 | uses: actions/checkout@v4
111 | with:
112 | fetch-depth: 0
113 | ref: ${{ needs.release.outputs.pr-branch }}
114 | - name: Setup Git User
115 | run: |
116 | git config --global user.email "npm-cli+bot@github.com"
117 | git config --global user.name "npm CLI robot"
118 | - name: Setup Node
119 | uses: actions/setup-node@v4
120 | id: node
121 | with:
122 | node-version: 22.x
123 | check-latest: contains('22.x', '.x')
124 | - name: Install Latest npm
125 | uses: ./.github/actions/install-latest-npm
126 | with:
127 | node: ${{ steps.node.outputs.node-version }}
128 | - name: Install Dependencies
129 | run: npm i --ignore-scripts --no-audit --no-fund
130 | - name: Create Release Manager Checklist Text
131 | id: comment-text
132 | env:
133 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
134 | run: npm exec --offline -- template-oss-release-manager --pr="${{ needs.release.outputs.pr-number }}" --backport="" --defaultTag="latest" --publish
135 | - name: Append Release Manager Comment
136 | uses: peter-evans/create-or-update-comment@v3
137 | with:
138 | comment-id: ${{ needs.release.outputs.comment-id }}
139 | body: ${{ steps.comment-text.outputs.result }}
140 | edit-mode: 'append'
141 | - name: Run Post Pull Request Actions
142 | env:
143 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
144 | run: npm run rp-pull-request --ignore-scripts --if-present -- --pr="${{ needs.release.outputs.pr-number }}" --commentId="${{ needs.release.outputs.comment-id }}"
145 | - name: Commit
146 | id: commit
147 | env:
148 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149 | run: |
150 | git commit --all --amend --no-edit || true
151 | git push --force-with-lease
152 | echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
153 | - name: Create Check
154 | id: create-check
155 | uses: ./.github/actions/create-check
156 | with:
157 | name: "Update - Release"
158 | check-name: "Release"
159 | token: ${{ secrets.GITHUB_TOKEN }}
160 | sha: ${{ steps.commit.outputs.sha }}
161 | - name: Conclude Check
162 | uses: LouisBrunner/checks-action@v1.6.0
163 | with:
164 | token: ${{ secrets.GITHUB_TOKEN }}
165 | conclusion: ${{ job.status }}
166 | check_id: ${{ needs.release.outputs.check-id }}
167 |
168 | ci:
169 | name: CI - Release
170 | needs: [ release, update ]
171 | if: needs.release.outputs.pr
172 | uses: ./.github/workflows/ci-release.yml
173 | with:
174 | ref: ${{ needs.release.outputs.pr-branch }}
175 | check-sha: ${{ needs.update.outputs.sha }}
176 |
177 | post-ci:
178 | needs: [ release, update, ci ]
179 | name: Post CI - Release
180 | if: github.repository_owner == 'npm' && needs.release.outputs.pr && always()
181 | runs-on: ubuntu-latest
182 | defaults:
183 | run:
184 | shell: bash
185 | steps:
186 | - name: Get CI Conclusion
187 | id: conclusion
188 | run: |
189 | result=""
190 | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
191 | result="failure"
192 | elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
193 | result="cancelled"
194 | else
195 | result="success"
196 | fi
197 | echo "result=$result" >> $GITHUB_OUTPUT
198 | - name: Conclude Check
199 | uses: LouisBrunner/checks-action@v1.6.0
200 | with:
201 | token: ${{ secrets.GITHUB_TOKEN }}
202 | conclusion: ${{ steps.conclusion.outputs.result }}
203 | check_id: ${{ needs.update.outputs.check-id }}
204 |
205 | post-release:
206 | needs: release
207 | outputs:
208 | comment-id: ${{ steps.create-comment.outputs.comment-id }}
209 | name: Post Release - Release
210 | if: github.repository_owner == 'npm' && needs.release.outputs.releases
211 | runs-on: ubuntu-latest
212 | defaults:
213 | run:
214 | shell: bash
215 | steps:
216 | - name: Create Release PR Comment Text
217 | id: comment-text
218 | uses: actions/github-script@v7
219 | env:
220 | RELEASES: ${{ needs.release.outputs.releases }}
221 | with:
222 | result-encoding: string
223 | script: |
224 | const releases = JSON.parse(process.env.RELEASES)
225 | const { runId, repo: { owner, repo } } = context
226 | const issue_number = releases[0].prNumber
227 | const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`
228 |
229 | return [
230 | '## Release Workflow\n',
231 | ...releases.map(r => `- \`${r.pkgName}@${r.version}\` ${r.url}`),
232 | `- Workflow run: :arrows_counterclockwise: ${runUrl}`,
233 | ].join('\n')
234 | - name: Create Release PR Comment
235 | id: create-comment
236 | uses: peter-evans/create-or-update-comment@v3
237 | with:
238 | issue-number: ${{ fromJSON(needs.release.outputs.releases)[0].prNumber }}
239 | body: ${{ steps.comment-text.outputs.result }}
240 |
241 | release-integration:
242 | needs: release
243 | name: Release Integration
244 | if: needs.release.outputs.releases
245 | uses: ./.github/workflows/release-integration.yml
246 | permissions:
247 | id-token: write
248 | secrets:
249 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
250 | with:
251 | releases: ${{ needs.release.outputs.releases }}
252 |
253 | post-release-integration:
254 | needs: [ release, release-integration, post-release ]
255 | name: Post Release Integration - Release
256 | if: github.repository_owner == 'npm' && needs.release.outputs.releases && always()
257 | runs-on: ubuntu-latest
258 | defaults:
259 | run:
260 | shell: bash
261 | steps:
262 | - name: Get Post Release Conclusion
263 | id: conclusion
264 | run: |
265 | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
266 | result="x"
267 | elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
268 | result="heavy_multiplication_x"
269 | else
270 | result="white_check_mark"
271 | fi
272 | echo "result=$result" >> $GITHUB_OUTPUT
273 | - name: Find Release PR Comment
274 | uses: peter-evans/find-comment@v2
275 | id: found-comment
276 | with:
277 | issue-number: ${{ fromJSON(needs.release.outputs.releases)[0].prNumber }}
278 | comment-author: 'github-actions[bot]'
279 | body-includes: '## Release Workflow'
280 | - name: Create Release PR Comment Text
281 | id: comment-text
282 | if: steps.found-comment.outputs.comment-id
283 | uses: actions/github-script@v7
284 | env:
285 | RESULT: ${{ steps.conclusion.outputs.result }}
286 | BODY: ${{ steps.found-comment.outputs.comment-body }}
287 | with:
288 | result-encoding: string
289 | script: |
290 | const { RESULT, BODY } = process.env
291 | const body = [BODY.replace(/(Workflow run: :)[a-z_]+(:)/, `$1${RESULT}$2`)]
292 | if (RESULT !== 'white_check_mark') {
293 | body.push(':rotating_light::rotating_light::rotating_light:')
294 | body.push([
295 | '@npm/cli-team: The post-release workflow failed for this release.',
296 | 'Manual steps may need to be taken after examining the workflow output.'
297 | ].join(' '))
298 | body.push(':rotating_light::rotating_light::rotating_light:')
299 | }
300 | return body.join('\n\n').trim()
301 | - name: Update Release PR Comment
302 | if: steps.comment-text.outputs.result
303 | uses: peter-evans/create-or-update-comment@v3
304 | with:
305 | comment-id: ${{ steps.found-comment.outputs.comment-id }}
306 | body: ${{ steps.comment-text.outputs.result }}
307 | edit-mode: 'replace'
308 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | # ignore everything in the root
4 | /*
5 |
6 | !**/.gitignore
7 | !/.commitlintrc.js
8 | !/.eslint.config.js
9 | !/.eslintrc.js
10 | !/.eslintrc.local.*
11 | !/.git-blame-ignore-revs
12 | !/.github/
13 | !/.gitignore
14 | !/.npmrc
15 | !/.prettierignore
16 | !/.prettierrc.js
17 | !/.release-please-manifest.json
18 | !/bin/
19 | !/CHANGELOG*
20 | !/CODE_OF_CONDUCT.md
21 | !/CONTRIBUTING.md
22 | !/docs/
23 | !/lib/
24 | !/LICENSE*
25 | !/map.js
26 | !/package.json
27 | !/README*
28 | !/release-please-config.json
29 | !/scripts/
30 | !/SECURITY.md
31 | !/tap-snapshots/
32 | !/test/
33 | !/tsconfig.json
34 | tap-testdir*/
35 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ; This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | package-lock=false
4 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | ".": "10.0.0"
3 | }
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [10.0.0](https://github.com/npm/npm-packlist/compare/v9.0.0...v10.0.0) (2024-11-25)
4 | ### ⚠️ BREAKING CHANGES
5 | * this module is now compatible with the following node versions: ^20.17.0 || >=22.9.0
6 | * `bun.lockb` is now included in the strict ignorelist
7 | ### Features
8 | * [`9f74fd3`](https://github.com/npm/npm-packlist/commit/9f74fd37c857840a8c361a8ada8a9e9524136658) [#168](https://github.com/npm/npm-packlist/pull/168) add `bun.lockb` to ignorelist (#168) (@antongolub)
9 | ### Bug Fixes
10 | * [`67fef03`](https://github.com/npm/npm-packlist/commit/67fef031feea8990a8a288c71d35a8a96e7d9e8e) update engines to ^20.17.0 || >=22.9.0 (#254) (@wraithgar)
11 | ### Chores
12 | * [`fd425fa`](https://github.com/npm/npm-packlist/commit/fd425fa70c15d14c2356656c67deb660680f714d) bump @npmcli/eslint-config from 4.0.5 to 5.0.1 (#250) (@dependabot[bot])
13 | * [`3b66b5a`](https://github.com/npm/npm-packlist/commit/3b66b5a16cf0b23a39487c17cc5e755b57c89496) [#252](https://github.com/npm/npm-packlist/pull/252) bump @npmcli/arborist from 7.5.4 to 8.0.0 (#252) (@dependabot[bot])
14 | * [`e688d26`](https://github.com/npm/npm-packlist/commit/e688d26302629e0b6eb7b1e8ee3d152d06306913) bump @npmcli/template-oss from 4.23.3 to 4.23.4 (#251) (@dependabot[bot], @npm-cli-bot)
15 |
16 | ## [9.0.0](https://github.com/npm/npm-packlist/compare/v8.0.2...v9.0.0) (2024-09-25)
17 | ### ⚠️ BREAKING CHANGES
18 | * `npm-packlist` now supports node `^18.17.0 || >=20.5.0`
19 | ### Bug Fixes
20 | * [`4679b12`](https://github.com/npm/npm-packlist/commit/4679b12aabbcd524a4b6ccd42601297a51b6bd7e) [#247](https://github.com/npm/npm-packlist/pull/247) align to npm 10 node engine range (@hashtagchris)
21 | ### Dependencies
22 | * [`b0500f4`](https://github.com/npm/npm-packlist/commit/b0500f4cb6a2e2e30d7c9df33dc367d6632f2d0a) [#247](https://github.com/npm/npm-packlist/pull/247) `ignore-walk@7.0.0`
23 | ### Chores
24 | * [`46ed801`](https://github.com/npm/npm-packlist/commit/46ed8013989409ab1f50354bcb6e3c0f989bd245) [#245](https://github.com/npm/npm-packlist/pull/245) bump @npmcli/eslint-config from 4.0.5 to 5.0.0 (#245) (@dependabot[bot])
25 | * [`625ddca`](https://github.com/npm/npm-packlist/commit/625ddca24b20a0934b4a5b9ae14fee2232be979f) [#240](https://github.com/npm/npm-packlist/pull/240) bump @npmcli/arborist from 6.5.1 to 7.5.4 (#240) (@dependabot[bot])
26 | * [`e2eb28d`](https://github.com/npm/npm-packlist/commit/e2eb28dec2f8abf75357564cca683c049006be50) [#247](https://github.com/npm/npm-packlist/pull/247) run template-oss-apply (@hashtagchris)
27 | * [`3c27bb3`](https://github.com/npm/npm-packlist/commit/3c27bb305d769ad0f8fd128a8b99787162444de2) [#230](https://github.com/npm/npm-packlist/pull/230) bump @npmcli/template-oss to 4.22.0 (@lukekarrys)
28 | * [`fa9e80d`](https://github.com/npm/npm-packlist/commit/fa9e80ddfa348daf690b548a22a887f3c7fadeec) [#242](https://github.com/npm/npm-packlist/pull/242) bump @npmcli/template-oss from 4.22.0 to 4.23.3 (#242) (@dependabot[bot])
29 | * [`cb4a823`](https://github.com/npm/npm-packlist/commit/cb4a823cd42d50475a8e1e7582b95b15766f5ca2) [#230](https://github.com/npm/npm-packlist/pull/230) postinstall for dependabot template-oss PR (@lukekarrys)
30 |
31 | ## [8.0.2](https://github.com/npm/npm-packlist/compare/v8.0.1...v8.0.2) (2024-01-03)
32 |
33 | ### Bug Fixes
34 |
35 | * [`728d68a`](https://github.com/npm/npm-packlist/commit/728d68ad3dfeb86de6d319f05435a6ac7319232e) [#214](https://github.com/npm/npm-packlist/pull/214) handling of directory glob (#214) (@mohd-akram)
36 | * [`837af58`](https://github.com/npm/npm-packlist/commit/837af580e4b5623afdc1d034b533290bc0a93ebb) [#210](https://github.com/npm/npm-packlist/pull/210) avoid modifying singleton variable (#210) (@mohd-akram)
37 |
38 | ## [8.0.1](https://github.com/npm/npm-packlist/compare/v8.0.0...v8.0.1) (2023-12-06)
39 |
40 | ### Bug Fixes
41 |
42 | * [`8fc4df1`](https://github.com/npm/npm-packlist/commit/8fc4df1b01ddfa99c476c1108f557ab6e172b28c) [#204](https://github.com/npm/npm-packlist/pull/204) always ignore .npmrc files at every level (@wraithgar)
43 | * [`cd5ddbd`](https://github.com/npm/npm-packlist/commit/cd5ddbd9fc7069d62fd89e0de741523e408c889b) [#205](https://github.com/npm/npm-packlist/pull/205) preserve slashes in specified files (#205) (@mohd-akram)
44 |
45 | ### Dependencies
46 |
47 | * [`6058cc5`](https://github.com/npm/npm-packlist/commit/6058cc5ffd8850c0bc7d0235586d21d5724f1c78) [#204](https://github.com/npm/npm-packlist/pull/204) `ignore-walk@6.0.4`
48 |
49 | ### Chores
50 |
51 | * [`20fe0cd`](https://github.com/npm/npm-packlist/commit/20fe0cdfbf0d4b447745c6149b502510f5a9655a) [#207](https://github.com/npm/npm-packlist/pull/207) bump @npmcli/template-oss from 4.21.1 to 4.21.2 (#207) (@dependabot[bot], @lukekarrys)
52 | * [`f8be7bd`](https://github.com/npm/npm-packlist/commit/f8be7bd0d4954b675c3da9f4954df15fb60dc071) [#202](https://github.com/npm/npm-packlist/pull/202) bump @npmcli/template-oss from 4.19.0 to 4.21.1 (#202) (@dependabot[bot], @lukekarrys)
53 | * [`8e1d900`](https://github.com/npm/npm-packlist/commit/8e1d9008cf8ead74a27530d3cda89200fe40f28c) [#204](https://github.com/npm/npm-packlist/pull/204) tests reflect fixed ignore-walk rules (@wraithgar)
54 | * [`6d7cbe9`](https://github.com/npm/npm-packlist/commit/6d7cbe9719fc16115d622398f64e588445fecfad) [#181](https://github.com/npm/npm-packlist/pull/181) postinstall for dependabot template-oss PR (@lukekarrys)
55 | * [`80ec501`](https://github.com/npm/npm-packlist/commit/80ec50165e98050988c0bbb935faf43163fcb5af) [#181](https://github.com/npm/npm-packlist/pull/181) bump @npmcli/template-oss from 4.18.1 to 4.19.0 (@dependabot[bot])
56 | * [`f327738`](https://github.com/npm/npm-packlist/commit/f327738576e0076592880633df358a4b9f76f3b4) [#179](https://github.com/npm/npm-packlist/pull/179) postinstall for dependabot template-oss PR (@lukekarrys)
57 | * [`a770a96`](https://github.com/npm/npm-packlist/commit/a770a96ba042b2221aa909a172bb3ffb51f8fb22) [#179](https://github.com/npm/npm-packlist/pull/179) bump @npmcli/template-oss from 4.18.0 to 4.18.1 (@dependabot[bot])
58 |
59 | ## [8.0.0](https://github.com/npm/npm-packlist/compare/v7.0.4...v8.0.0) (2023-08-24)
60 |
61 | ### ⚠️ BREAKING CHANGES
62 |
63 | * The files array can now be used to exclude non-root readme, license, licence, and copying files.
64 |
65 | ### Bug Fixes
66 |
67 | * [`24344a2`](https://github.com/npm/npm-packlist/commit/24344a2ce2f5a860d8c6048c642bae8db50b9618) [#173](https://github.com/npm/npm-packlist/pull/173) exclude non-root README.md/LICENSE files (#173) (@AaronHamilton965, @rahulio96)
68 |
69 | ## [7.0.4](https://github.com/npm/npm-packlist/compare/v7.0.3...v7.0.4) (2022-12-07)
70 |
71 | ### Bug Fixes
72 |
73 | * [`e5256de`](https://github.com/npm/npm-packlist/commit/e5256de99776ab243ab40653e960a20a8ab6a2ab) [#149](https://github.com/npm/npm-packlist/pull/149) skip missing optional deps when bundling, closes npm/cli#5924 (#149) (@nlf)
74 |
75 | ## [7.0.3](https://github.com/npm/npm-packlist/compare/v7.0.2...v7.0.3) (2022-12-07)
76 |
77 | ### Bug Fixes
78 |
79 | * [`c6f2b69`](https://github.com/npm/npm-packlist/commit/c6f2b69b025575dc683f26f3d098e23a22c462da) [#147](https://github.com/npm/npm-packlist/pull/147) treat glob the same as globstar (#147) (@lukekarrys)
80 |
81 | ## [7.0.2](https://github.com/npm/npm-packlist/compare/v7.0.1...v7.0.2) (2022-10-26)
82 |
83 | ### Bug Fixes
84 |
85 | * [`d5b653c`](https://github.com/npm/npm-packlist/commit/d5b653ce860f6601fb5007c7fcec903c8d58c1c8) [#140](https://github.com/npm/npm-packlist/pull/140) account for directories and files prefixed with `./` (#140) (@wraithgar)
86 |
87 | ## [7.0.1](https://github.com/npm/npm-packlist/compare/v7.0.0...v7.0.1) (2022-10-17)
88 |
89 | ### Dependencies
90 |
91 | * [`250d589`](https://github.com/npm/npm-packlist/commit/250d5896669e2fc84e2512124c28541e5396aad8) [#136](https://github.com/npm/npm-packlist/pull/136) bump ignore-walk from 5.0.1 to 6.0.0
92 |
93 | ## [7.0.0](https://github.com/npm/npm-packlist/compare/v7.0.0-pre.1...v7.0.0) (2022-10-04)
94 |
95 | ### Features
96 |
97 | * [`b83e0aa`](https://github.com/npm/npm-packlist/commit/b83e0aaa31936496499283fc44b773645d3dd969) [#133](https://github.com/npm/npm-packlist/pull/133) set as release (@fritzy)
98 |
99 | ## [7.0.0-pre.1](https://github.com/npm/npm-packlist/compare/v7.0.0-pre.0...v7.0.0-pre.1) (2022-10-03)
100 |
101 | ### ⚠️ BREAKING CHANGES
102 |
103 | * if npm-shrinkwrap.json is included in your .npmignore, the shrinkwrap will now be excluded from your packlist.
104 |
105 | ### Features
106 |
107 | * [`5e80968`](https://github.com/npm/npm-packlist/commit/5e8096883080d8d1650d4558fb62e34ce61d0dda) [#131](https://github.com/npm/npm-packlist/pull/131) npm-shrinkwrap.json files can now be ignored (#131) (@fritzy)
108 |
109 | ## [7.0.0-pre.0](https://github.com/npm/npm-packlist/compare/v6.0.1...v7.0.0-pre.0) (2022-09-26)
110 |
111 | ### ⚠️ BREAKING CHANGES
112 |
113 | * `tree` is now the first parameter
114 | * the arborist tree must now be provided in the options and will not be generated for you. the npm-packlist bin has also been removed.
115 |
116 | ### Features
117 |
118 | * [`87c778e`](https://github.com/npm/npm-packlist/commit/87c778eb0f89101ff5ef4a488e614fb9e7773480) make the required tree the first parameter (@lukekarrys)
119 | * [`123875a`](https://github.com/npm/npm-packlist/commit/123875a9db0d18c2ba91e9841ab8e15df3057e18) remove dependency on arborist, require tree to be passed in (@nlf)
120 |
121 | ## [6.0.1](https://github.com/npm/npm-packlist/compare/v6.0.0...v6.0.1) (2022-09-23)
122 |
123 | ### Dependencies
124 |
125 | * [`f823be6`](https://github.com/npm/npm-packlist/commit/f823be6e9bc9239acbb8e8b6149f0a8b457a0d0a) [#125](https://github.com/npm/npm-packlist/pull/125) arborist@5||6||6.pre
126 |
127 | ## [6.0.0](https://github.com/npm/npm-packlist/compare/v5.1.3...v6.0.0) (2022-09-21)
128 |
129 | ### ⚠️ BREAKING CHANGES
130 |
131 | * this module now follows a strict order of operations when applying ignore rules. if a `files` array is present in the package.json, then rules in `.gitignore` and `.npmignore` files from the root will be ignored.
132 | * `npm-packlist` is now compatible with the following semver range for node: `^14.17.0 || ^16.13.0 || >=18.0.0`
133 |
134 | ### Features
135 |
136 | * [`c37371b`](https://github.com/npm/npm-packlist/commit/c37371b060ee42ebaebf134f37471c8330fc4d27) [#88](https://github.com/npm/npm-packlist/pull/88) change interactions between `files` array and ignore files to be more consistent (#88) (@nlf)
137 | * [`a2c96ef`](https://github.com/npm/npm-packlist/commit/a2c96effdadd1cee1d6b584b92a45a7d09b260cb) [#123](https://github.com/npm/npm-packlist/pull/123) postinstall for dependabot template-oss PR (@lukekarrys)
138 |
139 | ## [5.1.3](https://github.com/npm/npm-packlist/compare/v5.1.2...v5.1.3) (2022-08-25)
140 |
141 |
142 | ### Dependencies
143 |
144 | * bump npm-bundled from 1.1.2 to 2.0.0 ([#113](https://github.com/npm/npm-packlist/issues/113)) ([de5e96b](https://github.com/npm/npm-packlist/commit/de5e96bbdd9ccf345104715fe3080c4d239b7f3b))
145 |
146 | ## [5.1.2](https://github.com/npm/npm-packlist/compare/v5.1.1...v5.1.2) (2022-08-23)
147 |
148 |
149 | ### Dependencies
150 |
151 | * bump npm-normalize-package-bin from 1.0.1 to 2.0.0 ([#110](https://github.com/npm/npm-packlist/issues/110)) ([c97d1a8](https://github.com/npm/npm-packlist/commit/c97d1a8b0d61ed76422bd7da0adaa4285c2a1cb7))
152 |
153 | ### [5.1.1](https://github.com/npm/npm-packlist/compare/v5.1.0...v5.1.1) (2022-05-31)
154 |
155 |
156 | ### Bug Fixes
157 |
158 | * correctly ignore .gitignore when a .npmignore is present ([#108](https://github.com/npm/npm-packlist/issues/108)) ([da1ba4a](https://github.com/npm/npm-packlist/commit/da1ba4a3051ea822d9625347e00c186677247a45))
159 |
160 | ## [5.1.0](https://github.com/npm/npm-packlist/compare/v5.0.4...v5.1.0) (2022-05-25)
161 |
162 |
163 | ### Features
164 |
165 | * correctly handle workspace roots ([#104](https://github.com/npm/npm-packlist/issues/104)) ([d74bdd1](https://github.com/npm/npm-packlist/commit/d74bdd157ef6a701c9aeef902be9e0e51f388c98))
166 |
167 | ### [5.0.4](https://github.com/npm/npm-packlist/compare/v5.0.3...v5.0.4) (2022-05-19)
168 |
169 |
170 | ### Bug Fixes
171 |
172 | * do not pack workspaces by default ([0f31f71](https://github.com/npm/npm-packlist/commit/0f31f71ab652c1bc9250bcb1603357ee7b4fbf28))
173 | * respect gitignore and npmignore files in workspace roots ([839e6e8](https://github.com/npm/npm-packlist/commit/839e6e8b13dc8c5ec14fab79509649d081c3ef54))
174 |
175 | ### [5.0.3](https://github.com/npm/npm-packlist/compare/v5.0.2...v5.0.3) (2022-05-04)
176 |
177 |
178 | ### Bug Fixes
179 |
180 | * strip leading ./ from files array entries ([#97](https://github.com/npm/npm-packlist/issues/97)) ([9f519b7](https://github.com/npm/npm-packlist/commit/9f519b7d38ee46e08dc77b3b730842a2ca0e7500))
181 |
182 | ### [5.0.2](https://github.com/npm/npm-packlist/compare/v5.0.1...v5.0.2) (2022-04-21)
183 |
184 |
185 | ### Bug Fixes
186 |
187 | * normalize win32 paths before globbing ([16f1343](https://github.com/npm/npm-packlist/commit/16f13436ebe31144ea86e3d2c7f1f16022f82885))
188 |
189 | ### [5.0.1](https://github.com/npm/npm-packlist/compare/v5.0.0...v5.0.1) (2022-04-20)
190 |
191 |
192 | ### Dependencies
193 |
194 | * bump glob from 7.2.0 to 8.0.1 ([#90](https://github.com/npm/npm-packlist/issues/90)) ([dc3d40a](https://github.com/npm/npm-packlist/commit/dc3d40a1b89019e5343f76c184cd2fbb296fdb27))
195 |
196 | ## [5.0.0](https://github.com/npm/npm-packlist/compare/v4.0.0...v5.0.0) (2022-04-06)
197 |
198 |
199 | ### ⚠ BREAKING CHANGES
200 |
201 | * this module no longer supports synchronous usage
202 |
203 | ### Features
204 |
205 | * remove synchronous interface ([#80](https://github.com/npm/npm-packlist/issues/80)) ([eb68f64](https://github.com/npm/npm-packlist/commit/eb68f64fe7d70d4776922246dd9cea5da6f1e21a))
206 |
207 |
208 | ### Bug Fixes
209 |
210 | * remove polynomial regex ([b2e2f7b](https://github.com/npm/npm-packlist/commit/b2e2f7b9122b15c8f8041953aa07b5436232b903))
211 | * replace deprecated String.prototype.substr() ([#85](https://github.com/npm/npm-packlist/issues/85)) ([79d6f7e](https://github.com/npm/npm-packlist/commit/79d6f7ebd5b881b3a3ec393769dd132a9a438778))
212 |
213 |
214 | ### Dependencies
215 |
216 | * bump ignore-walk from 4.0.1 to 5.0.1 ([#87](https://github.com/npm/npm-packlist/issues/87)) ([0e241f5](https://github.com/npm/npm-packlist/commit/0e241f50e57b95274cd988e09763e205020c5b84))
217 |
218 | ## [4.0.0](https://www.github.com/npm/npm-packlist/compare/v3.0.0...v4.0.0) (2022-03-03)
219 |
220 |
221 | ### ⚠ BREAKING CHANGES
222 |
223 | * This drops support for node10 and non-LTS versions of node12 and node14
224 |
225 | ### Bug Fixes
226 |
227 | * always ignore pnpm-lock.yaml ([#72](https://www.github.com/npm/npm-packlist/issues/72)) ([f56a4cf](https://www.github.com/npm/npm-packlist/commit/f56a4cf77fbbb123f3c818777cf00555538e1c1c))
228 |
229 |
230 | * @npmcli/template-oss@2.9.1 ([0401893](https://www.github.com/npm/npm-packlist/commit/04018939fc7ae6ceed1504a2fa4de44cfa049036))
231 |
232 |
233 | ### Dependencies
234 |
235 | * update glob requirement from ^7.1.6 to ^7.2.0 ([#70](https://www.github.com/npm/npm-packlist/issues/70)) ([d6b34ac](https://www.github.com/npm/npm-packlist/commit/d6b34ac471215290f2198c5ad14c8eed8b203179))
236 | * update npm-bundled requirement from ^1.1.1 to ^1.1.2 ([#71](https://www.github.com/npm/npm-packlist/issues/71)) ([2a5e140](https://www.github.com/npm/npm-packlist/commit/2a5e1402fb4617fc3791b2be405aa3bbb3181ff3))
237 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | All interactions in this repo are covered by the [npm Code of
4 | Conduct](https://docs.npmjs.com/policies/conduct)
5 |
6 | The npm cli team may, at its own discretion, moderate, remove, or edit
7 | any interactions such as pull requests, issues, and comments.
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Contributing
4 |
5 | ## Code of Conduct
6 |
7 | All interactions in the **npm** organization on GitHub are considered to be covered by our standard [Code of Conduct](https://docs.npmjs.com/policies/conduct).
8 |
9 | ## Reporting Bugs
10 |
11 | Before submitting a new bug report please search for an existing or similar report.
12 |
13 | Use one of our existing issue templates if you believe you've come across a unique problem.
14 |
15 | Duplicate issues, or issues that don't use one of our templates may get closed without a response.
16 |
17 | ## Pull Request Conventions
18 |
19 | ### Commits
20 |
21 | We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
22 |
23 | When opening a pull request please be sure that either the pull request title, or each commit in the pull request, has one of the following prefixes:
24 |
25 | - `feat`: For when introducing a new feature. The result will be a new semver minor version of the package when it is next published.
26 | - `fix`: For bug fixes. The result will be a new semver patch version of the package when it is next published.
27 | - `docs`: For documentation updates. The result will be a new semver patch version of the package when it is next published.
28 | - `chore`: For changes that do not affect the published module. Often these are changes to tests. The result will be *no* change to the version of the package when it is next published (as the commit does not affect the published version).
29 |
30 | ### Test Coverage
31 |
32 | Pull requests made against this repo will run `npm test` automatically. Please make sure tests pass locally before submitting a PR.
33 |
34 | Every new feature or bug fix should come with a corresponding test or tests that validate the solutions. Testing also reports on code coverage and will fail if code coverage drops.
35 |
36 | ### Linting
37 |
38 | Linting is also done automatically once tests pass. `npm run lintfix` will fix most linting errors automatically.
39 |
40 | Please make sure linting passes before submitting a PR.
41 |
42 | ## What _not_ to contribute?
43 |
44 | ### Dependencies
45 |
46 | It should be noted that our team does not accept third-party dependency updates/PRs. If you submit a PR trying to update our dependencies we will close it with or without a reference to these contribution guidelines.
47 |
48 | ### Tools/Automation
49 |
50 | Our core team is responsible for the maintenance of the tooling/automation in this project and we ask contributors to not make changes to these when contributing (e.g. `.github/*`, `.eslintrc.json`, `.licensee.json`). Most of those files also have a header at the top to remind folks they are automatically generated. Pull requests that alter these will not be accepted.
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The ISC License
2 |
3 | Copyright (c) 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 | # npm-packlist
2 |
3 | Get a list of the files to add from a folder into an npm package.
4 |
5 | These can be handed to [tar](http://npm.im/tar) like so to make an npm
6 | package tarball:
7 |
8 | ```js
9 | const Arborist = require('@npmcli/arborist')
10 | const packlist = require('npm-packlist')
11 | const tar = require('tar')
12 | const packageDir = '/path/to/package'
13 | const packageTarball = '/path/to/package.tgz'
14 |
15 | const arborist = new Arborist({ path: packageDir })
16 | arborist.loadActual().then((tree) => {
17 | packlist(tree)
18 | .then(files => tar.create({
19 | prefix: 'package/',
20 | cwd: packageDir,
21 | file: packageTarball,
22 | gzip: true
23 | }, files))
24 | .then(_ => {
25 | // tarball has been created, continue with your day
26 | })
27 | })
28 | ```
29 |
30 | This uses the following rules:
31 |
32 | 1. If a `package.json` file is found, and it has a `files` list,
33 | then ignore everything that isn't in `files`. Always include the root
34 | readme, license, licence and copying files, if they exist, as well
35 | as the package.json file itself. Non-root readme, license, licence and
36 | copying files are included by default, but can be excluded using the
37 | `files` list e.g. `"!readme"`.
38 | 2. If there's no `package.json` file (or it has no `files` list), and
39 | there is a `.npmignore` file, then ignore all the files in the
40 | `.npmignore` file.
41 | 3. If there's no `package.json` with a `files` list, and there's no
42 | `.npmignore` file, but there is a `.gitignore` file, then ignore
43 | all the files in the `.gitignore` file.
44 | 4. Everything in the root `node_modules` is ignored, unless it's a
45 | bundled dependency. If it IS a bundled dependency, and it's a
46 | symbolic link, then the target of the link is included, not the
47 | symlink itself.
48 | 4. Unless they're explicitly included (by being in a `files` list, or
49 | a `!negated` rule in a relevant `.npmignore` or `.gitignore`),
50 | always ignore certain common cruft files:
51 |
52 | 1. .npmignore and .gitignore files (their effect is in the package
53 | already, there's no need to include them in the package)
54 | 2. editor junk like `.*.swp`, `._*` and `.*.orig` files
55 | 3. `.npmrc` files (these may contain private configs)
56 | 4. The `node_modules/.bin` folder
57 | 5. Waf and gyp cruft like `/build/config.gypi` and `.lock-wscript`
58 | 6. Darwin's `.DS_Store` files because wtf are those even
59 | 7. `npm-debug.log` files at the root of a project
60 |
61 | You can explicitly re-include any of these with a `files` list in
62 | `package.json` or a negated ignore file rule.
63 |
64 | Only the `package.json` file in the very root of the project is ever
65 | inspected for a `files` list. Below the top level of the root package,
66 | `package.json` is treated as just another file, and no package-specific
67 | semantics are applied.
68 |
69 | ### Interaction between `package.json` and `.npmignore` rules
70 |
71 | In previous versions of this library, the `files` list in `package.json`
72 | was used as an initial filter to drive further tree walking. That is no
73 | longer the case as of version 6.0.0.
74 |
75 | If you have a `package.json` file with a `files` array within, any top
76 | level `.npmignore` and `.gitignore` files *will be ignored*.
77 |
78 | If a _directory_ is listed in `files`, then any rules in nested `.npmignore` files within that directory will be honored.
79 |
80 | For example, with this package.json:
81 |
82 | ```json
83 | {
84 | "files": [ "dir" ]
85 | }
86 | ```
87 |
88 | a `.npmignore` file at `dir/.npmignore` (and any subsequent
89 | sub-directories) will be honored. However, a `.npmignore` at the root
90 | level will be skipped.
91 |
92 | Additionally, with this package.json:
93 |
94 | ```
95 | {
96 | "files": ["dir/subdir"]
97 | }
98 | ```
99 |
100 | a `.npmignore` file at `dir/.npmignore` will be honored, as well as `dir/subdir/.npmignore`.
101 |
102 | Any specific file matched by an exact filename in the package.json `files` list will be included, and cannot be excluded, by any `.npmignore` files.
103 |
104 | ## API
105 |
106 | Same API as [ignore-walk](http://npm.im/ignore-walk), except providing a `tree` is required and there are hard-coded file list and rule sets.
107 |
108 | The `Walker` class requires an [arborist](https://github.com/npm/cli/tree/latest/workspaces/arborist) tree, and if any bundled dependencies are found will include them as well as their own dependencies in the resulting file set.
109 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | GitHub takes the security of our software products and services seriously, including the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub).
4 |
5 | If you believe you have found a security vulnerability in this GitHub-owned open source repository, you can report it to us in one of two ways.
6 |
7 | If the vulnerability you have found is *not* [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) or if you do not wish to be considered for a bounty reward, please report the issue to us directly through [opensource-security@github.com](mailto:opensource-security@github.com).
8 |
9 | If the vulnerability you have found is [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) and you would like for your finding to be considered for a bounty reward, please submit the vulnerability to us through [HackerOne](https://hackerone.com/github) in order to be eligible to receive a bounty award.
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**
12 |
13 | Thanks for helping make GitHub safe for everyone.
14 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { Walker: IgnoreWalker } = require('ignore-walk')
4 | const { lstatSync: lstat, readFileSync: readFile } = require('fs')
5 | const { basename, dirname, extname, join, relative, resolve, sep } = require('path')
6 |
7 | // symbols used to represent synthetic rule sets
8 | const defaultRules = Symbol('npm-packlist.rules.default')
9 | const strictRules = Symbol('npm-packlist.rules.strict')
10 |
11 | // There may be others, but :?|<> are handled by node-tar
12 | const nameIsBadForWindows = file => /\*/.test(file)
13 |
14 | // these are the default rules that are applied to everything except for non-link bundled deps
15 | const defaults = [
16 | '.npmignore',
17 | '.gitignore',
18 | '**/.git',
19 | '**/.svn',
20 | '**/.hg',
21 | '**/CVS',
22 | '**/.git/**',
23 | '**/.svn/**',
24 | '**/.hg/**',
25 | '**/CVS/**',
26 | '/.lock-wscript',
27 | '/.wafpickle-*',
28 | '/build/config.gypi',
29 | 'npm-debug.log',
30 | '**/.npmrc',
31 | '.*.swp',
32 | '.DS_Store',
33 | '**/.DS_Store/**',
34 | '._*',
35 | '**/._*/**',
36 | '*.orig',
37 | '/archived-packages/**',
38 | ]
39 |
40 | const strictDefaults = [
41 | // these are forcibly excluded
42 | '/.git',
43 | ]
44 |
45 | const normalizePath = (path) => path.split('\\').join('/')
46 |
47 | const readOutOfTreeIgnoreFiles = (root, rel, result = []) => {
48 | for (const file of ['.npmignore', '.gitignore']) {
49 | try {
50 | const ignoreContent = readFile(join(root, file), { encoding: 'utf8' })
51 | result.push(ignoreContent)
52 | // break the loop immediately after reading, this allows us to prioritize
53 | // the .npmignore and discard the .gitignore if one is present
54 | break
55 | } catch (err) {
56 | // we ignore ENOENT errors completely because we don't care if the file doesn't exist
57 | // but we throw everything else because failing to read a file that does exist is
58 | // something that the user likely wants to know about
59 | // istanbul ignore next -- we do not need to test a thrown error
60 | if (err.code !== 'ENOENT') {
61 | throw err
62 | }
63 | }
64 | }
65 |
66 | if (!rel) {
67 | return result
68 | }
69 |
70 | const firstRel = rel.split(sep, 1)[0]
71 | const newRoot = join(root, firstRel)
72 | const newRel = relative(newRoot, join(root, rel))
73 |
74 | return readOutOfTreeIgnoreFiles(newRoot, newRel, result)
75 | }
76 |
77 | class PackWalker extends IgnoreWalker {
78 | constructor (tree, opts) {
79 | const options = {
80 | ...opts,
81 | includeEmpty: false,
82 | follow: false,
83 | // we path.resolve() here because ignore-walk doesn't do it and we want full paths
84 | path: resolve(opts?.path || tree.path).replace(/\\/g, '/'),
85 | ignoreFiles: opts?.ignoreFiles || [
86 | defaultRules,
87 | 'package.json',
88 | '.npmignore',
89 | '.gitignore',
90 | strictRules,
91 | ],
92 | }
93 |
94 | super(options)
95 | this.isPackage = options.isPackage
96 | this.seen = options.seen || new Set()
97 | this.tree = tree
98 | this.requiredFiles = options.requiredFiles || []
99 |
100 | const additionalDefaults = []
101 | if (options.prefix && options.workspaces) {
102 | const path = normalizePath(options.path)
103 | const prefix = normalizePath(options.prefix)
104 | const workspaces = options.workspaces.map((ws) => normalizePath(ws))
105 |
106 | // istanbul ignore else - this does nothing unless we need it to
107 | if (path !== prefix && workspaces.includes(path)) {
108 | // if path and prefix are not the same directory, and workspaces has path in it
109 | // then we know path is a workspace directory. in order to not drop ignore rules
110 | // from directories between the workspaces root (prefix) and the workspace itself
111 | // (path) we need to find and read those now
112 | const relpath = relative(options.prefix, dirname(options.path))
113 | additionalDefaults.push(...readOutOfTreeIgnoreFiles(options.prefix, relpath))
114 | } else if (path === prefix) {
115 | // on the other hand, if the path and prefix are the same, then we ignore workspaces
116 | // so that we don't pack a workspace as part of the root project. append them as
117 | // normalized relative paths from the root
118 | additionalDefaults.push(...workspaces.map((w) => normalizePath(relative(options.path, w))))
119 | }
120 | }
121 |
122 | // go ahead and inject the default rules now
123 | this.injectRules(defaultRules, [...defaults, ...additionalDefaults])
124 |
125 | if (!this.isPackage) {
126 | // if this instance is not a package, then place some strict default rules, and append
127 | // known required files for this directory
128 | this.injectRules(strictRules, [
129 | ...strictDefaults,
130 | ...this.requiredFiles.map((file) => `!${file}`),
131 | ])
132 | }
133 | }
134 |
135 | // overridden method: we intercept the reading of the package.json file here so that we can
136 | // process it into both the package.json file rules as well as the strictRules synthetic rule set
137 | addIgnoreFile (file, callback) {
138 | // if we're adding anything other than package.json, then let ignore-walk handle it
139 | if (file !== 'package.json' || !this.isPackage) {
140 | return super.addIgnoreFile(file, callback)
141 | }
142 |
143 | return this.processPackage(callback)
144 | }
145 |
146 | // overridden method: if we're done, but we're a package, then we also need to evaluate bundles
147 | // before we actually emit our done event
148 | emit (ev, data) {
149 | if (ev !== 'done' || !this.isPackage) {
150 | return super.emit(ev, data)
151 | }
152 |
153 | // we intentionally delay the done event while keeping the function sync here
154 | // eslint-disable-next-line promise/catch-or-return, promise/always-return
155 | this.gatherBundles().then(() => {
156 | super.emit('done', this.result)
157 | })
158 | return true
159 | }
160 |
161 | // overridden method: before actually filtering, we make sure that we've removed the rules for
162 | // files that should no longer take effect due to our order of precedence
163 | filterEntries () {
164 | if (this.ignoreRules['package.json']) {
165 | // package.json means no .npmignore or .gitignore
166 | this.ignoreRules['.npmignore'] = null
167 | this.ignoreRules['.gitignore'] = null
168 | } else if (this.ignoreRules['.npmignore']) {
169 | // .npmignore means no .gitignore
170 | this.ignoreRules['.gitignore'] = null
171 | }
172 |
173 | return super.filterEntries()
174 | }
175 |
176 | // overridden method: we never want to include anything that isn't a file or directory
177 | onstat (opts, callback) {
178 | if (!opts.st.isFile() && !opts.st.isDirectory()) {
179 | return callback()
180 | }
181 |
182 | return super.onstat(opts, callback)
183 | }
184 |
185 | // overridden method: we want to refuse to pack files that are invalid, node-tar protects us from
186 | // a lot of them but not all
187 | stat (opts, callback) {
188 | if (nameIsBadForWindows(opts.entry)) {
189 | return callback()
190 | }
191 |
192 | return super.stat(opts, callback)
193 | }
194 |
195 | // overridden method: this is called to create options for a child walker when we step
196 | // in to a normal child directory (this will never be a bundle). the default method here
197 | // copies the root's `ignoreFiles` value, but we don't want to respect package.json for
198 | // subdirectories, so we override it with a list that intentionally omits package.json
199 | walkerOpt (entry, opts) {
200 | let ignoreFiles = null
201 |
202 | // however, if we have a tree, and we have workspaces, and the directory we're about
203 | // to step into is a workspace, then we _do_ want to respect its package.json
204 | if (this.tree.workspaces) {
205 | const workspaceDirs = [...this.tree.workspaces.values()]
206 | .map((dir) => dir.replace(/\\/g, '/'))
207 |
208 | const entryPath = join(this.path, entry).replace(/\\/g, '/')
209 | if (workspaceDirs.includes(entryPath)) {
210 | ignoreFiles = [
211 | defaultRules,
212 | 'package.json',
213 | '.npmignore',
214 | '.gitignore',
215 | strictRules,
216 | ]
217 | }
218 | } else {
219 | ignoreFiles = [
220 | defaultRules,
221 | '.npmignore',
222 | '.gitignore',
223 | strictRules,
224 | ]
225 | }
226 |
227 | return {
228 | ...super.walkerOpt(entry, opts),
229 | ignoreFiles,
230 | // we map over our own requiredFiles and pass ones that are within this entry
231 | requiredFiles: this.requiredFiles
232 | .map((file) => {
233 | if (relative(file, entry) === '..') {
234 | return relative(entry, file).replace(/\\/g, '/')
235 | }
236 | return false
237 | })
238 | .filter(Boolean),
239 | }
240 | }
241 |
242 | // overridden method: we want child walkers to be instances of this class, not ignore-walk
243 | walker (entry, opts, callback) {
244 | new PackWalker(this.tree, this.walkerOpt(entry, opts)).on('done', callback).start()
245 | }
246 |
247 | // overridden method: we use a custom sort method to help compressibility
248 | sort (a, b) {
249 | // optimize for compressibility
250 | // extname, then basename, then locale alphabetically
251 | // https://twitter.com/isntitvacant/status/1131094910923231232
252 | const exta = extname(a).toLowerCase()
253 | const extb = extname(b).toLowerCase()
254 | const basea = basename(a).toLowerCase()
255 | const baseb = basename(b).toLowerCase()
256 |
257 | return exta.localeCompare(extb, 'en') ||
258 | basea.localeCompare(baseb, 'en') ||
259 | a.localeCompare(b, 'en')
260 | }
261 |
262 | // convenience method: this joins the given rules with newlines, appends a trailing newline,
263 | // and calls the internal onReadIgnoreFile method
264 | injectRules (filename, rules, callback = () => {}) {
265 | this.onReadIgnoreFile(filename, `${rules.join('\n')}\n`, callback)
266 | }
267 |
268 | // custom method: this is called by addIgnoreFile when we find a package.json, it uses the
269 | // arborist tree to pull both default rules and strict rules for the package
270 | processPackage (callback) {
271 | const {
272 | bin,
273 | browser,
274 | files,
275 | main,
276 | } = this.tree.package
277 |
278 | // rules in these arrays are inverted since they are patterns we want to _not_ ignore
279 | const ignores = []
280 | const strict = [
281 | ...strictDefaults,
282 | '!/package.json',
283 | '!/readme{,.*[^~$]}',
284 | '!/copying{,.*[^~$]}',
285 | '!/license{,.*[^~$]}',
286 | '!/licence{,.*[^~$]}',
287 | '/.git',
288 | '/node_modules',
289 | '.npmrc',
290 | '/package-lock.json',
291 | '/yarn.lock',
292 | '/pnpm-lock.yaml',
293 | '/bun.lockb',
294 | ]
295 |
296 | // if we have a files array in our package, we need to pull rules from it
297 | if (files) {
298 | for (let file of files) {
299 | // invert the rule because these are things we want to include
300 | if (file.startsWith('./')) {
301 | file = file.slice(1)
302 | }
303 | if (file.endsWith('/*')) {
304 | file += '*'
305 | }
306 | const inverse = `!${file}`
307 | try {
308 | // if an entry in the files array is a specific file, then we need to include it as a
309 | // strict requirement for this package. if it's a directory or a pattern, it's a default
310 | // pattern instead. this is ugly, but we have to stat to find out if it's a file
311 | const stat = lstat(join(this.path, file.replace(/^!+/, '')).replace(/\\/g, '/'))
312 | // if we have a file and we know that, it's strictly required
313 | if (stat.isFile()) {
314 | strict.unshift(inverse)
315 | this.requiredFiles.push(file.startsWith('/') ? file.slice(1) : file)
316 | } else if (stat.isDirectory()) {
317 | // otherwise, it's a default ignore, and since we got here we know it's not a pattern
318 | // so we include the directory contents
319 | ignores.push(inverse)
320 | ignores.push(`${inverse}/**`)
321 | }
322 | // if the thing exists, but is neither a file or a directory, we don't want it at all
323 | } catch (err) {
324 | // if lstat throws, then we assume we're looking at a pattern and treat it as a default
325 | ignores.push(inverse)
326 | }
327 | }
328 |
329 | // we prepend a '*' to exclude everything, followed by our inverted file rules
330 | // which now mean to include those
331 | this.injectRules('package.json', ['*', ...ignores])
332 | }
333 |
334 | // browser is required
335 | if (browser) {
336 | strict.push(`!/${browser}`)
337 | }
338 |
339 | // main is required
340 | if (main) {
341 | strict.push(`!/${main}`)
342 | }
343 |
344 | // each bin is required
345 | if (bin) {
346 | for (const key in bin) {
347 | strict.push(`!/${bin[key]}`)
348 | }
349 | }
350 |
351 | // and now we add all of the strict rules to our synthetic file
352 | this.injectRules(strictRules, strict, callback)
353 | }
354 |
355 | // custom method: after we've finished gathering the files for the root package, we call this
356 | // before emitting the 'done' event in order to gather all of the files for bundled deps
357 | async gatherBundles () {
358 | if (this.seen.has(this.tree)) {
359 | return
360 | }
361 |
362 | // add this node to our seen tracker
363 | this.seen.add(this.tree)
364 |
365 | // if we're the project root, then we look at our bundleDependencies, otherwise we got here
366 | // because we're a bundled dependency of the root, which means we need to include all prod
367 | // and optional dependencies in the bundle
368 | let toBundle
369 | if (this.tree.isProjectRoot) {
370 | const { bundleDependencies } = this.tree.package
371 | toBundle = bundleDependencies || []
372 | } else {
373 | const { dependencies, optionalDependencies } = this.tree.package
374 | toBundle = Object.keys(dependencies || {}).concat(Object.keys(optionalDependencies || {}))
375 | }
376 |
377 | for (const dep of toBundle) {
378 | const edge = this.tree.edgesOut.get(dep)
379 | // no edgeOut = missing node, so skip it. we can't pack it if it's not here
380 | // we also refuse to pack peer dependencies and dev dependencies
381 | if (!edge || edge.peer || edge.dev) {
382 | continue
383 | }
384 |
385 | // get a reference to the node we're bundling
386 | const node = this.tree.edgesOut.get(dep).to
387 | // if there's no node, this is most likely an optional dependency that hasn't been
388 | // installed. just skip it.
389 | if (!node) {
390 | continue
391 | }
392 | // we use node.path for the path because we want the location the node was linked to,
393 | // not where it actually lives on disk
394 | const path = node.path
395 | // but link nodes don't have edgesOut, so we need to pass in the target of the node
396 | // in order to make sure we correctly traverse its dependencies
397 | const tree = node.target
398 |
399 | // and start building options to be passed to the walker for this package
400 | const walkerOpts = {
401 | path,
402 | isPackage: true,
403 | ignoreFiles: [],
404 | seen: this.seen, // pass through seen so we can prevent infinite circular loops
405 | }
406 |
407 | // if our node is a link, we apply defaultRules. we don't do this for regular bundled
408 | // deps because their .npmignore and .gitignore files are excluded by default and may
409 | // override defaults
410 | if (node.isLink) {
411 | walkerOpts.ignoreFiles.push(defaultRules)
412 | }
413 |
414 | // _all_ nodes will follow package.json rules from their package root
415 | walkerOpts.ignoreFiles.push('package.json')
416 |
417 | // only link nodes will obey .npmignore or .gitignore
418 | if (node.isLink) {
419 | walkerOpts.ignoreFiles.push('.npmignore')
420 | walkerOpts.ignoreFiles.push('.gitignore')
421 | }
422 |
423 | // _all_ nodes follow strict rules
424 | walkerOpts.ignoreFiles.push(strictRules)
425 |
426 | // create a walker for this dependency and gather its results
427 | const walker = new PackWalker(tree, walkerOpts)
428 | const bundled = await new Promise((pResolve, pReject) => {
429 | walker.on('error', pReject)
430 | walker.on('done', pResolve)
431 | walker.start()
432 | })
433 |
434 | // now we make sure we have our paths correct from the root, and accumulate everything into
435 | // our own result set to deduplicate
436 | const relativeFrom = relative(this.root, walker.path)
437 | for (const file of bundled) {
438 | this.result.add(join(relativeFrom, file).replace(/\\/g, '/'))
439 | }
440 | }
441 | }
442 | }
443 |
444 | const walk = (tree, options, callback) => {
445 | if (typeof options === 'function') {
446 | callback = options
447 | options = {}
448 | }
449 | const p = new Promise((pResolve, pReject) => {
450 | new PackWalker(tree, { ...options, isPackage: true })
451 | .on('done', pResolve).on('error', pReject).start()
452 | })
453 | return callback ? p.then(res => callback(null, res), callback) : p
454 | }
455 |
456 | module.exports = walk
457 | walk.Walker = PackWalker
458 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "npm-packlist",
3 | "version": "10.0.0",
4 | "description": "Get a list of the files to add from a folder into an npm package",
5 | "directories": {
6 | "test": "test"
7 | },
8 | "main": "lib/index.js",
9 | "dependencies": {
10 | "ignore-walk": "^7.0.0"
11 | },
12 | "author": "GitHub Inc.",
13 | "license": "ISC",
14 | "files": [
15 | "bin/",
16 | "lib/"
17 | ],
18 | "devDependencies": {
19 | "@npmcli/arborist": "^9.0.0",
20 | "@npmcli/eslint-config": "^5.0.1",
21 | "@npmcli/template-oss": "4.24.3",
22 | "mutate-fs": "^2.1.1",
23 | "tap": "^16.0.1"
24 | },
25 | "scripts": {
26 | "test": "tap",
27 | "posttest": "npm run lint",
28 | "snap": "tap",
29 | "postsnap": "npm run lintfix --",
30 | "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
31 | "lint": "npm run eslint",
32 | "lintfix": "npm run eslint -- --fix",
33 | "npmclilint": "npmcli-lint",
34 | "postlint": "template-oss-check",
35 | "template-oss-apply": "template-oss-apply --force"
36 | },
37 | "repository": {
38 | "type": "git",
39 | "url": "git+https://github.com/npm/npm-packlist.git"
40 | },
41 | "tap": {
42 | "test-env": [
43 | "LC_ALL=sk"
44 | ],
45 | "nyc-arg": [
46 | "--exclude",
47 | "tap-snapshots/**"
48 | ],
49 | "files": [
50 | "test/*.js"
51 | ]
52 | },
53 | "engines": {
54 | "node": "^20.17.0 || >=22.9.0"
55 | },
56 | "templateOSS": {
57 | "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
58 | "version": "4.24.3",
59 | "publish": true
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "group-pull-request-title-pattern": "chore: release ${version}",
3 | "pull-request-title-pattern": "chore: release${component} ${version}",
4 | "changelog-sections": [
5 | {
6 | "type": "feat",
7 | "section": "Features",
8 | "hidden": false
9 | },
10 | {
11 | "type": "fix",
12 | "section": "Bug Fixes",
13 | "hidden": false
14 | },
15 | {
16 | "type": "docs",
17 | "section": "Documentation",
18 | "hidden": false
19 | },
20 | {
21 | "type": "deps",
22 | "section": "Dependencies",
23 | "hidden": false
24 | },
25 | {
26 | "type": "chore",
27 | "section": "Chores",
28 | "hidden": true
29 | }
30 | ],
31 | "packages": {
32 | ".": {
33 | "package-name": "",
34 | "prerelease": false
35 | }
36 | },
37 | "prerelease-type": "pre.0"
38 | }
39 |
--------------------------------------------------------------------------------
/test/bin.js:
--------------------------------------------------------------------------------
1 | // disabling the no-sparse-arrays rule because we use them for assertions
2 | /* eslint-disable no-sparse-arrays */
3 | 'use strict'
4 |
5 | const t = require('tap')
6 | const { spawnSync } = require('child_process')
7 |
8 | const nodePath = process.execPath
9 | const binPath = require.resolve('./utils/bin.js')
10 |
11 | const cwd = t.testdir({
12 | 'package.json': JSON.stringify({
13 | files: ['index.js', 'lib'],
14 | }),
15 | 'README.md': 'hello',
16 | 'LICENSE.txt': 'you can use it but you gotta pay me',
17 | 'index.js': 'console.log(/xss/)',
18 | lib: {
19 | 'index.js': 'console.log(/xss/)',
20 | 'cat.js': 'console.log("meow")',
21 | 'dog.js': 'console.log("woof")',
22 | 'chai.js': 'console.log("blub")',
23 | },
24 | 'ignore.js': 'throw new Error("dont look at me!")',
25 | })
26 |
27 | t.test('no args', async (t) => {
28 | const result = spawnSync(nodePath, [binPath], { cwd, encoding: 'utf8' })
29 | t.equal(result.status, 0, 'completed successfully')
30 | t.same(result.stdout, [
31 | 'lib/cat.js',
32 | 'lib/chai.js',
33 | 'lib/dog.js',
34 | 'index.js',
35 | 'lib/index.js',
36 | 'package.json',
37 | 'README.md',
38 | 'LICENSE.txt',, // empty element at the end so we get a trailing \n
39 | ].join('\n'))
40 | })
41 |
42 | t.test('--sort', async (t) => {
43 | const result = spawnSync(nodePath, [binPath, '--sort'], { cwd, encoding: 'utf8' })
44 | t.equal(result.status, 0, 'completed successfully')
45 | t.same(result.stdout, [
46 | 'index.js',
47 | 'lib/cat.js',
48 | 'lib/chai.js',
49 | 'lib/dog.js',
50 | 'lib/index.js',
51 | 'LICENSE.txt',
52 | 'package.json',
53 | 'README.md',, // empty element at the end so we get a trailing \n
54 | ].join('\n'))
55 | })
56 |
57 | t.test('-s', async (t) => {
58 | const result = spawnSync(nodePath, [binPath, '-s'], { cwd, encoding: 'utf8' })
59 | t.equal(result.status, 0, 'completed successfully')
60 | t.same(result.stdout, [
61 | 'index.js',
62 | 'lib/cat.js',
63 | 'lib/chai.js',
64 | 'lib/dog.js',
65 | 'lib/index.js',
66 | 'LICENSE.txt',
67 | 'package.json',
68 | 'README.md',, // empty element at the end so we get a trailing \n
69 | ].join('\n'))
70 | })
71 |
72 | t.test('dir argument', async (t) => {
73 | const result = spawnSync(nodePath, [binPath, '.'], { cwd, encoding: 'utf8' })
74 | t.equal(result.status, 0, 'completed successfully')
75 | t.same(result.stdout, [
76 | '> .', // the directory name prefixed with "> "
77 | ' lib/cat.js', // the files will all be indented
78 | ' lib/chai.js',
79 | ' lib/dog.js',
80 | ' index.js',
81 | ' lib/index.js',
82 | ' package.json',
83 | ' README.md',
84 | ' LICENSE.txt',, // empty element at the end so we get a trailing \n
85 | ].join('\n'))
86 | })
87 |
88 | t.test('-h', async (t) => {
89 | const result = spawnSync(nodePath, [binPath, '-h'], { cwd, encoding: 'utf8' })
90 | t.equal(result.status, 0, 'completed successfully')
91 | t.match(result.stdout, /^usage: npm-packlist/, 'printed help')
92 | })
93 |
--------------------------------------------------------------------------------
/test/bundle-missing-dep.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('skips bundling deps with missing edges', async (t) => {
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | name: 'test',
11 | version: '1.0.0',
12 | main: 'index.js',
13 | // named in bundleDependencies, but not actually a dependency
14 | bundleDependencies: ['history'],
15 | }),
16 | 'index.js': '',
17 | node_modules: {
18 | history: {
19 | 'package.json': JSON.stringify({
20 | name: 'history',
21 | version: '1.0.0',
22 | main: 'index.js',
23 | }),
24 | 'index.js': '',
25 | },
26 | },
27 | })
28 | const arborist = new Arborist({ path: pkg })
29 | const tree = await arborist.loadActual()
30 |
31 | const files = await packlist(tree)
32 | t.same(files, [
33 | 'index.js',
34 | 'package.json',
35 | ])
36 | })
37 |
--------------------------------------------------------------------------------
/test/bundled-cycle.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('correctly bundles cyclic deps', async (t) => {
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | name: 'root',
11 | version: '1.0.0',
12 | main: 'index.js',
13 | dependencies: {
14 | a: '1.0.0',
15 | },
16 | bundleDependencies: ['a'],
17 | }),
18 | 'index.js': '',
19 | node_modules: {
20 | a: {
21 | 'package.json': JSON.stringify({
22 | name: 'a',
23 | version: '1.0.0',
24 | main: 'index.js',
25 | dependencies: {
26 | b: '1.0.0',
27 | },
28 | }),
29 | 'index.js': '',
30 | },
31 | b: {
32 | 'package.json': JSON.stringify({
33 | name: 'b',
34 | version: '1.0.0',
35 | main: 'index.js',
36 | dependencies: {
37 | a: '1.0.0',
38 | },
39 | }),
40 | 'index.js': '',
41 | },
42 | },
43 | })
44 |
45 | const arborist = new Arborist({ path: pkg })
46 | const tree = await arborist.loadActual()
47 | const files = await packlist(tree)
48 | t.same(files, [
49 | 'index.js',
50 | 'node_modules/a/index.js',
51 | 'node_modules/b/index.js',
52 | 'node_modules/a/package.json',
53 | 'node_modules/b/package.json',
54 | 'package.json',
55 | ])
56 | })
57 |
--------------------------------------------------------------------------------
/test/bundled-file-in-workspace.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('correctly filters files from workspace subdirectory', async (t) => {
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | name: 'root',
11 | version: '1.0.0',
12 | files: ['docs/*.txt'],
13 | main: 'index.js',
14 | workspaces: ['./docs'],
15 | }),
16 | 'index.js': '',
17 | docs: {
18 | 'package.json': JSON.stringify({
19 | name: 'docs',
20 | version: '1.0.0',
21 | main: 'index.js',
22 | files: ['*.txt'],
23 | }),
24 | 'bar.txt': '',
25 | 'foo.txt': '',
26 | 'readme.md': '',
27 | test: {
28 | 'index.js': '',
29 | },
30 | },
31 | })
32 |
33 | const arborist = new Arborist({ path: pkg })
34 | const tree = await arborist.loadActual()
35 | const files = await packlist(tree)
36 | t.same(files, [
37 | 'index.js',
38 | 'package.json',
39 | 'docs/bar.txt',
40 | 'docs/foo.txt',
41 | ])
42 | })
43 |
44 | t.test('does not filter based on package.json if subdirectory is not a workspace', async (t) => {
45 | const pkg = t.testdir({
46 | 'package.json': JSON.stringify({
47 | name: 'root',
48 | version: '1.0.0',
49 | files: ['docs/*.txt'],
50 | main: 'index.js',
51 | // this test needs a workspace to exist, but that workspace cannot be the one we include
52 | // files from
53 | workspaces: ['./unrelated'],
54 | }),
55 | 'index.js': '',
56 | docs: {
57 | 'package.json': JSON.stringify({
58 | name: 'docs',
59 | version: '1.0.0',
60 | main: 'index.js',
61 | files: ['bar.txt', 'foo.txt'],
62 | }),
63 | 'bar.txt': '',
64 | 'baz.txt': '',
65 | 'foo.txt': '',
66 | 'readme.md': '',
67 | test: {
68 | 'index.js': '',
69 | },
70 | },
71 | unrelated: {
72 | 'package.json': JSON.stringify({
73 | name: 'unrelated',
74 | version: '1.0.0',
75 | main: 'index.js',
76 | }),
77 | 'index.js': '',
78 | },
79 | })
80 |
81 | const arborist = new Arborist({ path: pkg })
82 | const tree = await arborist.loadActual()
83 | const files = await packlist(tree)
84 | t.same(files, [
85 | 'index.js',
86 | 'package.json',
87 | 'docs/bar.txt',
88 | 'docs/baz.txt', // was _not_ filtered
89 | 'docs/foo.txt',
90 | ])
91 | })
92 |
--------------------------------------------------------------------------------
/test/bundled-files.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | t.test('includes bundled dependency using bundleDependencies', async (t) => {
13 | const pkg = t.testdir({
14 | 'package.json': JSON.stringify({
15 | name: 'test-package',
16 | version: '3.1.4',
17 | main: 'elf.js',
18 | dependencies: {
19 | history: '1.0.0',
20 | },
21 | bundleDependencies: [
22 | 'history',
23 | ],
24 | files: ['elf.js'],
25 | }),
26 | 'elf.js': elfJS,
27 | '.npmrc': 'packaged=false',
28 | node_modules: {
29 | history: {
30 | 'package.json': JSON.stringify({
31 | name: 'history',
32 | version: '1.0.0',
33 | main: 'index.js',
34 | }),
35 | 'index.js': elfJS,
36 | },
37 | },
38 | })
39 |
40 | const arborist = new Arborist({ path: pkg })
41 | const tree = await arborist.loadActual()
42 | const files = await packlist(tree)
43 | t.same(files, [
44 | 'elf.js',
45 | 'node_modules/history/index.js',
46 | 'node_modules/history/package.json',
47 | 'package.json',
48 | ])
49 | })
50 |
51 | t.test('includes bundled dependency using bundledDependencies', async (t) => {
52 | const pkg = t.testdir({
53 | 'package.json': JSON.stringify({
54 | name: 'test-package',
55 | version: '3.1.4',
56 | main: 'elf.js',
57 | dependencies: {
58 | history: '1.0.0',
59 | },
60 | bundledDependencies: [
61 | 'history',
62 | ],
63 | files: ['elf.js'],
64 | }),
65 | 'elf.js': elfJS,
66 | '.npmrc': 'packaged=false',
67 | node_modules: {
68 | history: {
69 | 'package.json': JSON.stringify({
70 | name: 'history',
71 | version: '1.0.0',
72 | main: 'index.js',
73 | }),
74 | 'index.js': elfJS,
75 | },
76 | },
77 | })
78 |
79 | const arborist = new Arborist({ path: pkg })
80 | const tree = await arborist.loadActual()
81 | const files = await packlist(tree)
82 | t.same(files, [
83 | 'elf.js',
84 | 'node_modules/history/index.js',
85 | 'node_modules/history/package.json',
86 | 'package.json',
87 | ])
88 | })
89 |
--------------------------------------------------------------------------------
/test/bundled-missing-optional.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | t.test('includes bundled dependency using bundleDependencies', async (t) => {
13 | const pkg = t.testdir({
14 | 'package.json': JSON.stringify({
15 | name: 'test-package',
16 | version: '3.1.4',
17 | main: 'elf.js',
18 | dependencies: {
19 | history: '1.0.0',
20 | },
21 | bundleDependencies: [
22 | 'history',
23 | ],
24 | }),
25 | 'elf.js': elfJS,
26 | '.npmrc': 'packaged=false',
27 | node_modules: {
28 | history: {
29 | 'package.json': JSON.stringify({
30 | name: 'history',
31 | version: '1.0.0',
32 | main: 'index.js',
33 | optionalDependencies: {
34 | // defined here, but not installed
35 | optionalDep: '^1.0.0',
36 | },
37 | }),
38 | 'index.js': elfJS,
39 | },
40 | },
41 | })
42 |
43 | const arborist = new Arborist({ path: pkg })
44 | const tree = await arborist.loadActual()
45 | const files = await packlist(tree)
46 | t.same(files, [
47 | 'elf.js',
48 | 'node_modules/history/index.js',
49 | 'node_modules/history/package.json',
50 | 'package.json',
51 | ])
52 | })
53 |
--------------------------------------------------------------------------------
/test/bundled-scoped-symlink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('..')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | pkg: {
14 | 'package.json': JSON.stringify({
15 | name: 'test-package',
16 | version: '3.1.4',
17 | main: 'elf.js',
18 | dependencies: {
19 | '@npmwombat/history': '1.0.0',
20 | },
21 | bundleDependencies: [
22 | '@npmwombat/history',
23 | ],
24 | }),
25 | 'elf.js': elfJS,
26 | '.npmrc': 'packaged=false',
27 | node_modules: {
28 | '@npmwombat': {
29 | history: t.fixture('symlink', '../../../history'),
30 | },
31 | },
32 | },
33 | history: {
34 | 'package.json': JSON.stringify({
35 | name: '@npmwombat/history',
36 | version: '1.0.0',
37 | main: 'index.js',
38 | files: [
39 | 'index.js',
40 | 'lib/',
41 | ],
42 | }),
43 | 'index.js': elfJS,
44 | tests: {
45 | 'test.js': 'please do not include me',
46 | },
47 | // this should not be followed, even though the bundled dep is
48 | lib: {
49 | linky: t.fixture('symlink', '../tests'),
50 | },
51 | },
52 | }) + '/pkg'
53 |
54 | t.test('includes bundled dependency', async (t) => {
55 | const arborist = new Arborist({ path: pkg })
56 | const tree = await arborist.loadActual()
57 | const files = await packlist(tree)
58 | t.same(files, [
59 | 'elf.js',
60 | 'node_modules/@npmwombat/history/index.js',
61 | 'node_modules/@npmwombat/history/package.json',
62 | 'package.json',
63 | ])
64 | })
65 |
--------------------------------------------------------------------------------
/test/bundled-scoped.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | dependencies: {
18 | '@npmwombat/history': '1.0.0',
19 | },
20 | bundleDependencies: [
21 | '@npmwombat/history',
22 | ],
23 | }),
24 | 'elf.js': elfJS,
25 | '.npmrc': 'packaged=false',
26 | node_modules: {
27 | '@npmwombat': {
28 | history: {
29 | 'package.json': JSON.stringify({
30 | name: '@npmwombat/history',
31 | version: '1.0.0',
32 | main: 'index.js',
33 | }),
34 | 'index.js': elfJS,
35 | },
36 | },
37 | },
38 | })
39 |
40 | t.test('includes bundled dependency', async (t) => {
41 | const arborist = new Arborist({ path: pkg })
42 | const tree = await arborist.loadActual()
43 | const files = await packlist(tree)
44 | t.same(files, [
45 | 'elf.js',
46 | 'node_modules/@npmwombat/history/index.js',
47 | 'node_modules/@npmwombat/history/package.json',
48 | 'package.json',
49 | ])
50 | })
51 |
--------------------------------------------------------------------------------
/test/bundled-symlink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | pkg: {
14 | 'package.json': JSON.stringify({
15 | name: 'test-package',
16 | version: '3.1.4',
17 | main: 'elf.js',
18 | dependencies: {
19 | history: '1.0.0',
20 | },
21 | bundleDependencies: [
22 | 'history',
23 | ],
24 | }),
25 | 'elf.js': elfJS,
26 | '.npmrc': 'packaged=false',
27 | node_modules: {
28 | history: t.fixture('symlink', '../../history'),
29 | },
30 | },
31 | history: {
32 | 'package.json': JSON.stringify({
33 | name: 'history',
34 | version: '1.0.0',
35 | main: 'index.js',
36 | files: [
37 | 'index.js',
38 | 'lib/',
39 | ],
40 | }),
41 | 'index.js': elfJS,
42 | tests: {
43 | 'test.js': 'please do not include me',
44 | },
45 | // this should not be followed, even though the bundled dep is
46 | lib: {
47 | linky: t.fixture('symlink', '../tests'),
48 | },
49 | },
50 | }) + '/pkg'
51 |
52 | t.test('includes bundled dependency', async (t) => {
53 | const arborist = new Arborist({ path: pkg })
54 | const tree = await arborist.loadActual()
55 | const files = await packlist(tree)
56 | t.same(files, [
57 | 'elf.js',
58 | 'node_modules/history/index.js',
59 | 'node_modules/history/package.json',
60 | 'package.json',
61 | ])
62 | })
63 |
--------------------------------------------------------------------------------
/test/bundled-workspace.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('packs workspace dependencies correctly', async (t) => {
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | name: 'root',
11 | version: '1.2.3',
12 | main: 'index.js',
13 | files: ['index.js'],
14 | dependencies: {
15 | foo: '1.0.0',
16 | bar: '1.0.0',
17 | },
18 | bundleDependencies: ['foo', 'bar'],
19 | workspaces: ['./workspaces/*'],
20 | }),
21 | 'index.js': '',
22 | workspaces: {
23 | foo: {
24 | 'package.json': JSON.stringify({
25 | name: 'foo',
26 | version: '1.0.0',
27 | main: 'index.js',
28 | }),
29 | 'index.js': '',
30 | },
31 | bar: {
32 | 'package.json': JSON.stringify({
33 | name: 'bar',
34 | version: '1.0.0',
35 | main: 'index.js',
36 | }),
37 | 'index.js': '',
38 | },
39 | },
40 | node_modules: {
41 | foo: t.fixture('symlink', '../workspaces/foo'),
42 | bar: t.fixture('symlink', '../workspaces/bar'),
43 | },
44 | })
45 |
46 | const arborist = new Arborist({ path: pkg })
47 | const tree = await arborist.loadActual()
48 | const files = await packlist(tree)
49 | t.same(files, [
50 | 'index.js',
51 | 'node_modules/bar/index.js',
52 | 'node_modules/foo/index.js',
53 | 'node_modules/bar/package.json',
54 | 'node_modules/foo/package.json',
55 | 'package.json',
56 | ])
57 | })
58 |
--------------------------------------------------------------------------------
/test/bundled.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | t.test('includes bundled dependency using bundleDependencies', async (t) => {
13 | const pkg = t.testdir({
14 | 'package.json': JSON.stringify({
15 | name: 'test-package',
16 | version: '3.1.4',
17 | main: 'elf.js',
18 | dependencies: {
19 | history: '1.0.0',
20 | },
21 | bundleDependencies: [
22 | 'history',
23 | ],
24 | }),
25 | 'elf.js': elfJS,
26 | '.npmrc': 'packaged=false',
27 | node_modules: {
28 | history: {
29 | 'package.json': JSON.stringify({
30 | name: 'history',
31 | version: '1.0.0',
32 | main: 'index.js',
33 | }),
34 | 'index.js': elfJS,
35 | },
36 | },
37 | })
38 |
39 | const arborist = new Arborist({ path: pkg })
40 | const tree = await arborist.loadActual()
41 | const files = await packlist(tree)
42 | t.same(files, [
43 | 'elf.js',
44 | 'node_modules/history/index.js',
45 | 'node_modules/history/package.json',
46 | 'package.json',
47 | ])
48 | })
49 |
50 | t.test('includes bundled dependency using bundledDependencies', async (t) => {
51 | const pkg = t.testdir({
52 | 'package.json': JSON.stringify({
53 | name: 'test-package',
54 | version: '3.1.4',
55 | main: 'elf.js',
56 | dependencies: {
57 | history: '1.0.0',
58 | },
59 | bundledDependencies: [
60 | 'history',
61 | ],
62 | }),
63 | 'elf.js': elfJS,
64 | '.npmrc': 'packaged=false',
65 | node_modules: {
66 | history: {
67 | 'package.json': JSON.stringify({
68 | name: 'history',
69 | version: '1.0.0',
70 | main: 'index.js',
71 | }),
72 | 'index.js': elfJS,
73 | },
74 | },
75 | })
76 |
77 | const arborist = new Arborist({ path: pkg })
78 | const tree = await arborist.loadActual()
79 | const files = await packlist(tree)
80 | t.same(files, [
81 | 'elf.js',
82 | 'node_modules/history/index.js',
83 | 'node_modules/history/package.json',
84 | 'package.json',
85 | ])
86 | })
87 |
--------------------------------------------------------------------------------
/test/callbacks.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | name: 'test',
10 | }),
11 | })
12 |
13 | t.test('export supports callbacks', async (t) => {
14 | const arborist = new Arborist({ path: pkg })
15 | const tree = await arborist.loadActual()
16 |
17 | return new Promise((resolve, reject) => {
18 | packlist(tree, (err, files) => {
19 | if (err) {
20 | reject(err)
21 | }
22 |
23 | t.same(files, [
24 | 'package.json',
25 | ])
26 | resolve(files)
27 | })
28 | })
29 | })
30 |
--------------------------------------------------------------------------------
/test/can-exclude-shrinkwrap.js:
--------------------------------------------------------------------------------
1 | // can exclude npm-shrinkwrap.json in the root
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: ['.npmignore', '!npm-shrinkwrap.json'],
11 | }),
12 | '.npmignore': 'npm-shrinkwrap.json',
13 | 'npm-shrinkwrap.json': '{}',
14 | })
15 |
16 | t.test('package with negated files', async (t) => {
17 | const arborist = new Arborist({ path: pkg })
18 | const tree = await arborist.loadActual()
19 | const files = await packlist(tree)
20 | t.same(files, [
21 | '.npmignore',
22 | 'package.json',
23 | ])
24 | })
25 |
--------------------------------------------------------------------------------
/test/cannot-exclude-package-json.js:
--------------------------------------------------------------------------------
1 | // cannot exclude package.json in the root
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: ['.npmignore', '!package.json'],
11 | }),
12 | '.npmignore': 'package.json',
13 | })
14 |
15 | t.test('try to exclude package.json but cannot', async (t) => {
16 | const arborist = new Arborist({ path: pkg })
17 | const tree = await arborist.loadActual()
18 | const files = await packlist(tree)
19 | t.same(files, [
20 | '.npmignore',
21 | 'package.json',
22 | ])
23 | })
24 |
--------------------------------------------------------------------------------
/test/cannot-exclude-readme.js:
--------------------------------------------------------------------------------
1 | // cannot exclude readme.md in the root
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: ['.npmignore', '!readme.md'],
11 | }),
12 | '.npmignore': 'readme.md\nlicense.md\nhistory.md\n*.xyz',
13 | 'readme.md': 'hello',
14 | 'license.md': 'hello',
15 | 'history.md': 'hello',
16 | 'changes.md': 'hello',
17 | 'changelog.xyz': 'hello',
18 | 'notice.md': 'hello',
19 | })
20 |
21 | t.test('try to exclude package.json but cannot', async (t) => {
22 | const arborist = new Arborist({ path: pkg })
23 | const tree = await arborist.loadActual()
24 | const files = await packlist(tree)
25 | t.same(files, [
26 | '.npmignore',
27 | 'package.json',
28 | 'license.md',
29 | 'readme.md',
30 | ])
31 | })
32 |
--------------------------------------------------------------------------------
/test/cannot-include-non-file-or-directory.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const fs = require('fs')
5 | const t = require('tap')
6 | const { join } = require('path')
7 |
8 | t.test('cannot include something that exists but is neither a file nor a directory', async (t) => {
9 | const pkg = t.testdir({
10 | 'package.json': JSON.stringify({
11 | name: 'root',
12 | version: '1.0.0',
13 | main: 'index.js',
14 | files: ['lib', 'device'],
15 | }),
16 | 'index.js': '',
17 | lib: {
18 | socket: '',
19 | },
20 | device: '',
21 | })
22 |
23 | // mock fs.lstat for ignore-walk so it tells us that lib/socket is not a file, dir, or symlink
24 | const ignoreWalk = t.mock('ignore-walk', {
25 | fs: {
26 | ...fs,
27 | lstat: (path, options, callback) => {
28 | if (typeof options === 'function') {
29 | callback = options
30 | options = undefined
31 | }
32 | if (path === join(pkg, 'lib', 'socket').replace(/\\/g, '/')) {
33 | return callback(null, {
34 | isFile: () => false,
35 | isDirectory: () => false,
36 | isSymbolicLink: () => false,
37 | })
38 | }
39 | return fs.lstat(path, options, callback)
40 | },
41 | },
42 | })
43 |
44 | const packlist = t.mock('../', {
45 | 'ignore-walk': ignoreWalk,
46 | fs: {
47 | ...fs,
48 | lstatSync: (path) => {
49 | if (path === join(pkg, 'device').replace(/\\/g, '/')) {
50 | return { isFile: () => false, isDirectory: () => false }
51 | }
52 |
53 | return fs.lstatSync(path)
54 | },
55 | },
56 | })
57 |
58 | const arborist = new Arborist({ path: pkg })
59 | const tree = await arborist.loadActual()
60 | const files = await packlist(tree)
61 | t.same(files, [
62 | 'index.js',
63 | 'package.json',
64 | ])
65 | })
66 |
--------------------------------------------------------------------------------
/test/cannot-include-package-lock.js:
--------------------------------------------------------------------------------
1 | // cannot include package-lock.json in the root
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: ['.npmignore', 'package-lock.json'],
11 | }),
12 | '.npmignore': `
13 | !package-lock.json
14 | `,
15 | 'package-lock.json': '{}',
16 | })
17 |
18 | t.test('try to include package-lock.json but cannot', async (t) => {
19 | const arborist = new Arborist({ path: pkg })
20 | const tree = await arborist.loadActual()
21 | const files = await packlist(tree)
22 | t.same(files, [
23 | '.npmignore',
24 | 'package.json',
25 | ])
26 | })
27 |
--------------------------------------------------------------------------------
/test/constructor.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | name: 'test',
10 | }),
11 | })
12 |
13 | t.test('Walker constructor allows ommitting options entirely', async (t) => {
14 | const arborist = new Arborist({ path: pkg })
15 | const tree = await arborist.loadActual()
16 | const walker = new packlist.Walker(tree)
17 | const files = await new Promise((resolve, reject) => {
18 | walker
19 | .on('done', resolve)
20 | .on('error', reject)
21 | .start()
22 | })
23 | t.same(files, [
24 | 'package.json',
25 | ])
26 | })
27 |
--------------------------------------------------------------------------------
/test/empty-npmignore.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | }),
18 | 'elf.js': elfJS,
19 | '.gitignore': '*',
20 | '.npmignore': '',
21 | lib: {
22 | node_modules: {
23 | foo: {
24 | 'package.json': '{"name":"just a test","version":"bugbear"}',
25 | },
26 | },
27 | },
28 | '.npmrc': 'packaged=false',
29 | build: {
30 | 'config.gypi': "i_wont_be_included='with any luck'",
31 | 'npm-debug.log': '0 lol\n',
32 | },
33 | deps: {
34 | foo: {
35 | config: {
36 | 'config.gypi': "i_will_be_included='with any luck'",
37 | },
38 | },
39 | },
40 | '.git': {
41 | gitstub: "won't fool git, also won't be included",
42 | },
43 | node_modules: {
44 | history: {
45 | 'README.md': "please don't include me",
46 | },
47 | },
48 | })
49 |
50 | t.test('follows npm package ignoring rules', async (t) => {
51 | const arborist = new Arborist({ path: pkg })
52 | const tree = await arborist.loadActual()
53 | const files = await packlist(tree)
54 | t.same(files, [
55 | 'deps/foo/config/config.gypi',
56 | 'elf.js',
57 | 'lib/node_modules/foo/package.json',
58 | 'package.json',
59 | ])
60 | })
61 |
--------------------------------------------------------------------------------
/test/ignore-file-included-by-globstar.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('exclude certain files always', async t => {
8 | const path = t.testdir({
9 | '.npmrc': 'secrets=true',
10 | '.git': {
11 | HEAD: 'empty',
12 | },
13 | node_modules: {
14 | foo: {
15 | 'index.js': '',
16 | },
17 | },
18 | subdir: {
19 | 'other.js': '',
20 | '.npmrc': 'sneaky=true',
21 | },
22 | 'index.js': '',
23 | 'glorp.txt': '',
24 | 'package.json': JSON.stringify({
25 | name: '@npmcli/globstar-test',
26 | version: '1.0.0',
27 | files: ['*'],
28 | }),
29 | 'package-lock.json': '{}',
30 | 'yarn.lock': '{}',
31 | 'pnpm-lock.yaml': '{}',
32 | })
33 | const arborist = new Arborist({ path })
34 | const tree = await arborist.loadActual()
35 | const files = await packlist(tree)
36 | t.same(files, [
37 | 'index.js',
38 | 'subdir/other.js',
39 | 'package.json',
40 | 'glorp.txt',
41 | ])
42 | })
43 |
44 | t.test('include a globstar, then exclude one of them', async (t) => {
45 | const path = t.testdir({
46 | 'bar.js': '',
47 | bar: {
48 | 'bar.js': '',
49 | },
50 | 'glorp.txt': '',
51 | 'package.json': JSON.stringify({
52 | name: 'cli-issue-2009',
53 | version: '1.0.0',
54 | files: [
55 | '**/*.js',
56 | '!foo.js',
57 | ],
58 | }),
59 | })
60 |
61 | const arborist = new Arborist({ path })
62 | const tree = await arborist.loadActual()
63 | const files = await packlist(tree)
64 | t.same(files, [
65 | 'bar.js',
66 | 'bar/bar.js',
67 | 'package.json',
68 | ])
69 | })
70 |
--------------------------------------------------------------------------------
/test/ignores.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | }),
18 | 'archived-packages': {
19 | ignoreme: 'this should be ignored',
20 | },
21 | 'elf.js': elfJS,
22 | '.npmrc': 'packaged=false',
23 | // the '!**/non.existent' rule is important as it tests if the default rules
24 | // block .git contents even if it's accidentally 'unlocked'.
25 | // see https://npm.community/t/1805
26 | '.npmignore': `
27 | .npmignore
28 | dummy
29 | package.json
30 | !**/non.existent
31 | readme.md
32 | *~
33 | `,
34 | dummy: 'foo',
35 | core: 'foo',
36 | '.DS_Store': {
37 | foo: 'foo',
38 | },
39 | 'readme.md': 'Elf package readme included even if ignored',
40 | 'readme.md~': 'Editor backup file should not be auto-included',
41 | this: {
42 | dir: {
43 | is: {
44 | empty: {
45 | and: {
46 | ignored: {},
47 | },
48 | },
49 | },
50 | },
51 | },
52 | build: {
53 | 'config.gypi': "i_wont_be_included='with any luck'",
54 | 'npm-debug.log': '0 lol\n',
55 | },
56 | deps: {
57 | foo: {
58 | config: {
59 | 'config.gypi': "i_will_be_included='with any luck'",
60 | },
61 | },
62 | '.git': 'do not include me',
63 | },
64 | '.git': {
65 | gitstub: 'wont fool git, also wont be included',
66 | logs: {
67 | refs: {
68 | remotes: {
69 | name: {
70 | readme: 'please donot include git dirs (or even walk them)',
71 | },
72 | },
73 | },
74 | },
75 | },
76 | node_modules: {
77 | history: {
78 | 'README.md': "please don't include me",
79 | },
80 | },
81 | })
82 |
83 | t.test('follows npm package ignoring rules', async (t) => {
84 | const arborist = new Arborist({ path: pkg })
85 | const tree = await arborist.loadActual()
86 | const files = await packlist(tree)
87 | t.same(files, [
88 | 'core',
89 | 'deps/foo/config/config.gypi',
90 | 'elf.js',
91 | 'package.json',
92 | 'readme.md',
93 | ])
94 | })
95 |
--------------------------------------------------------------------------------
/test/include-gitignore.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | }),
18 | z: {
19 | '.gitignore': '!.gitignore',
20 | },
21 | '.DS_Store': 'do not include me',
22 | 'elf.js': elfJS,
23 | '.npmrc': 'packaged=false',
24 | '.npmignore': '.npmignore\ndummy\npackage.json',
25 | dummy: 'foo',
26 | build: {
27 | 'config.gypi': "i_wont_be_included='with any luck'",
28 | 'npm-debug.log': '0 lol\n',
29 | },
30 | deps: {
31 | foo: {
32 | config: {
33 | 'config.gypi': "i_wont_be_included='with any luck'",
34 | },
35 | },
36 | },
37 | '.git': {
38 | gitstub: "won't fool git, also won't be included",
39 | },
40 | node_modules: {
41 | history: {
42 | 'README.md': "please don't include me",
43 | },
44 | },
45 | })
46 |
47 | t.test('follows npm package ignoring rules', async (t) => {
48 | const arborist = new Arborist({ path: pkg })
49 | const tree = await arborist.loadActual()
50 | const files = await packlist(tree)
51 | t.same(files, [
52 | 'z/.gitignore',
53 | 'deps/foo/config/config.gypi',
54 | 'elf.js',
55 | 'package.json',
56 | ])
57 | })
58 |
--------------------------------------------------------------------------------
/test/nested-lock-and-core.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | name: 'test-package',
10 | version: '1.2.3',
11 | }),
12 | 'package-lock.json': JSON.stringify({
13 | lock: true,
14 | include: false,
15 | }),
16 | 'yarn.lock': JSON.stringify({
17 | lock: 'file',
18 | include: false,
19 | }),
20 | 'bun.lockb': JSON.stringify({
21 | lock: 'file',
22 | include: false,
23 | }),
24 | lib: {
25 | core: 'no longer excluded dump file',
26 | 'package-lock.json': JSON.stringify({
27 | lock: 'file',
28 | include: true,
29 | }),
30 | 'yarn.lock': JSON.stringify({
31 | lock: 'file',
32 | include: true,
33 | }),
34 | 'bun.lockb': JSON.stringify({
35 | lock: 'file',
36 | include: true,
37 | }),
38 | },
39 | core: {
40 | 'include-me.txt': 'please include me',
41 | },
42 | })
43 |
44 | t.test('follows npm package ignoring rules', async (t) => {
45 | const arborist = new Arborist({ path: pkg })
46 | const tree = await arborist.loadActual()
47 | const files = await packlist(tree)
48 | t.same(files, [
49 | 'lib/core',
50 | 'lib/package-lock.json',
51 | 'package.json',
52 | 'lib/yarn.lock',
53 | 'lib/bun.lockb',
54 | 'core/include-me.txt',
55 | ])
56 | })
57 |
--------------------------------------------------------------------------------
/test/package-json-bin-multiple.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const bin = `
8 | #!/usr/bin/env node
9 | require("../lib/elf")()
10 | `
11 |
12 | const elfJS = `
13 | module.exports = elf =>
14 | console.log("i'm angry about elves")
15 | `
16 |
17 | const pkg = t.testdir({
18 | 'package.json': JSON.stringify({
19 | name: 'test-package',
20 | version: '1.6.2',
21 | bin: {
22 | bar: '__bin_bar',
23 | foo: '__bin_foo',
24 | },
25 | files: [
26 | 'lib',
27 | ],
28 | }),
29 | __bin_foo: bin,
30 | __bin_bar: bin,
31 | lib: {
32 | 'elf.js': elfJS,
33 | },
34 | dummy: 'ignore this',
35 | })
36 |
37 | t.test('follows npm package ignoring rules', async (t) => {
38 | const arborist = new Arborist({ path: pkg })
39 | const tree = await arborist.loadActual()
40 | const files = await packlist(tree)
41 | t.same(files, [
42 | '__bin_bar',
43 | '__bin_foo',
44 | 'lib/elf.js',
45 | 'package.json',
46 | ])
47 | })
48 |
--------------------------------------------------------------------------------
/test/package-json-bin-single.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const bin = `
8 | #!/usr/bin/env node
9 | require("../lib/elf")()
10 | `
11 |
12 | const elfJS = `
13 | module.exports = elf =>
14 | console.log("i'm angry about elves")
15 | `
16 |
17 | const pkg = t.testdir({
18 | 'package.json': JSON.stringify({
19 | name: 'test-package',
20 | version: '1.6.2',
21 | bin: '__bin',
22 | files: [
23 | 'lib',
24 | ],
25 | }),
26 | __bin: bin,
27 | lib: {
28 | 'elf.js': elfJS,
29 | },
30 | dummy: 'ignore',
31 | })
32 |
33 | t.test('follows npm package ignoring rules', async (t) => {
34 | const arborist = new Arborist({ path: pkg })
35 | const tree = await arborist.loadActual()
36 | const files = await packlist(tree)
37 | t.same(files, [
38 | '__bin',
39 | 'lib/elf.js',
40 | 'package.json',
41 | ])
42 | })
43 |
--------------------------------------------------------------------------------
/test/package-json-dir-with-slashes.js:
--------------------------------------------------------------------------------
1 | // In v1, this would exclude the 'lib/two.js' file, because
2 | // the .npmignore is deeper in the tree and thus had higher
3 | // precedence. In v2, because /lib/two.js is in the files
4 | // list as a file path, it will be included no matter what.
5 | 'use strict'
6 |
7 | const Arborist = require('@npmcli/arborist')
8 | const t = require('tap')
9 | const packlist = require('../')
10 |
11 | const pkg = t.testdir({
12 | 'package.json': JSON.stringify({
13 | files: [
14 | '/lib',
15 | './lib2',
16 | './lib3/*',
17 | ],
18 | }),
19 | lib: {
20 | 'one.js': 'one',
21 | 'two.js': 'two',
22 | 'tre.js': 'tre',
23 | 'for.js': 'for',
24 | '.npmignore': 'two.js',
25 | },
26 | lib2: {
27 | 'fiv.js': 'fiv',
28 | '.DS_Store': 'a store of ds',
29 | },
30 | lib3: 'not a dir',
31 | })
32 |
33 | t.test('package with slash directories', async (t) => {
34 | const arborist = new Arborist({ path: pkg })
35 | const tree = await arborist.loadActual()
36 | const files = await packlist(tree)
37 | t.same(files, [
38 | 'lib2/fiv.js',
39 | 'lib/for.js',
40 | 'lib/one.js',
41 | 'lib/tre.js',
42 | 'package.json',
43 | ])
44 | })
45 |
--------------------------------------------------------------------------------
/test/package-json-directory-glob.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('..')
6 |
7 | const createTestdir = (...files) => t.testdir({
8 | 'package.json': JSON.stringify({
9 | files,
10 | }),
11 | folder: {
12 | one: { file: 'one' },
13 | two: { file: 'two' },
14 | },
15 | folder1: {
16 | one: { file: 'one' },
17 | two: { file: 'two' },
18 | },
19 | })
20 |
21 | t.test('package json directory glob', async (t) => {
22 | const pkgFiles = [
23 | 'folder',
24 | 'folder/',
25 | 'folder/*',
26 | 'folder/**',
27 | 'folder/**/*',
28 | './folder/*',
29 | ]
30 |
31 | for (const files of pkgFiles) {
32 | await t.test(files, async t => {
33 | const pkg = createTestdir(files)
34 | const arborist = new Arborist({ path: pkg })
35 | const tree = await arborist.loadActual()
36 | const res = await packlist(tree)
37 | t.same(res, [
38 | 'folder/one/file',
39 | 'folder/two/file',
40 | 'package.json',
41 | ])
42 | })
43 | }
44 | })
45 |
--------------------------------------------------------------------------------
/test/package-json-empty.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | files: [],
18 | }),
19 | 'index.js': elfJS,
20 | 'elf.js': elfJS,
21 | '.npmrc': 'packaged=false',
22 |
23 | // don't bother even reading this file, because we have files list
24 | '.npmignore': '!.npmignore\n!dummy\npackage.json\n!index.js\n',
25 | dummy: 'foo',
26 | build: {
27 | 'config.gypi': "i_wont_be_included='with any luck'",
28 | 'npm-debug.log': '0 lol\n',
29 | },
30 | deps: {
31 | foo: {
32 | config: {
33 | 'config.gypi': "i_wont_be_included='with any luck'",
34 | },
35 | },
36 | },
37 | '.git': {
38 | gitstub: "won't fool git, also won't be included",
39 | },
40 | node_modules: {
41 | history: {
42 | 'README.md': "please don't include me",
43 | },
44 | },
45 | })
46 |
47 | t.test('follows npm package ignoring rules', async (t) => {
48 | const arborist = new Arborist({ path: pkg })
49 | const tree = await arborist.loadActual()
50 | const files = await packlist(tree)
51 | t.same(files, [
52 | 'elf.js',
53 | 'package.json',
54 | ])
55 | })
56 |
--------------------------------------------------------------------------------
/test/package-json-filed-dir-nested-npmignore.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | files: [
10 | 'lib',
11 | ],
12 | }),
13 | lib: {
14 | 'one.js': 'one',
15 | 'two.js': 'two',
16 | 'tre.js': 'tre',
17 | 'for.js': 'for',
18 | '.npmignore': 'two.js',
19 | '.DS_Store': 'a store of ds',
20 | },
21 | })
22 |
23 | t.test('package with negated files', async (t) => {
24 | const arborist = new Arborist({ path: pkg })
25 | const tree = await arborist.loadActual()
26 | const files = await packlist(tree)
27 | t.same(files, [
28 | 'lib/for.js',
29 | 'lib/one.js',
30 | 'lib/tre.js',
31 | 'package.json',
32 | ])
33 | })
34 |
--------------------------------------------------------------------------------
/test/package-json-files-and-containing-dir.js:
--------------------------------------------------------------------------------
1 | // ensure we don't get files more than one time, even if specified
2 | // in ways that will have them included from multiple directions
3 | 'use strict'
4 |
5 | const Arborist = require('@npmcli/arborist')
6 | const t = require('tap')
7 | const packlist = require('../')
8 |
9 | const pkg = t.testdir({
10 | 'package.json': JSON.stringify({
11 | files: [
12 | 'lib',
13 | '/lib/*.js',
14 | 'lib/*.js',
15 | '/lib/one.js',
16 | 'lib/one.js',
17 | 'lib/one.*',
18 | ],
19 | }),
20 | lib: {
21 | 'one.js': 'one',
22 | 'two.js': 'two',
23 | 'tre.js': 'tre',
24 | 'for.js': 'for',
25 | '.npmignore': 'two.js',
26 | '.DS_Store': 'a store of ds',
27 | },
28 | })
29 |
30 | t.test('package with negated files', async (t) => {
31 | const arborist = new Arborist({ path: pkg })
32 | const tree = await arborist.loadActual()
33 | const files = await packlist(tree)
34 | t.same(files, [
35 | 'lib/for.js',
36 | 'lib/one.js',
37 | 'lib/tre.js',
38 | 'package.json',
39 | ])
40 | })
41 |
--------------------------------------------------------------------------------
/test/package-json-files-dir-and-nested-ignore.js:
--------------------------------------------------------------------------------
1 | // Show that a nested .npmignore will be honored if the
2 | // 'files' array matches a dir, but not any specific files.
3 | 'use strict'
4 |
5 | const Arborist = require('@npmcli/arborist')
6 | const t = require('tap')
7 | const packlist = require('../')
8 |
9 | const pkg = t.testdir({
10 | 'package.json': JSON.stringify({
11 | files: [
12 | 'lib',
13 | ],
14 | }),
15 | lib: {
16 | 'one.js': 'one',
17 | 'two.js': 'two',
18 | 'tre.js': 'tre',
19 | 'for.js': 'for',
20 | '.npmignore': 'two.js',
21 | '.DS_Store': 'a store of ds',
22 | },
23 | })
24 |
25 | t.test('package with negated files', async (t) => {
26 | const arborist = new Arborist({ path: pkg })
27 | const tree = await arborist.loadActual()
28 | const files = await packlist(tree)
29 | t.same(files, [
30 | 'lib/for.js',
31 | 'lib/one.js',
32 | 'lib/tre.js',
33 | 'package.json',
34 | ])
35 | })
36 |
--------------------------------------------------------------------------------
/test/package-json-files-including-npmignore.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | files: [
10 | 'lib/sub/*.js',
11 | 'lib/.npmignore',
12 | ],
13 | }),
14 | lib: {
15 | '.npmignore': 'two.js',
16 | sub: {
17 | 'one.js': 'one',
18 | 'two.js': 'two',
19 | 'tre.js': 'tre',
20 | 'for.js': 'for',
21 | },
22 | '.DS_Store': 'a store of ds',
23 | },
24 | })
25 |
26 | t.test('package with negated files', async (t) => {
27 | const arborist = new Arborist({ path: pkg })
28 | const tree = await arborist.loadActual()
29 | const files = await packlist(tree)
30 | t.same(files, [
31 | 'lib/.npmignore',
32 | 'lib/sub/for.js',
33 | 'lib/sub/one.js',
34 | 'lib/sub/tre.js',
35 | 'package.json',
36 | ])
37 | })
38 |
--------------------------------------------------------------------------------
/test/package-json-files-nested-dir-and-nested-ignore.js:
--------------------------------------------------------------------------------
1 | // Show that a nested .npmignore will be skipped over if the
2 | // 'files' array matches a dir that passes by it.
3 | 'use strict'
4 |
5 | const Arborist = require('@npmcli/arborist')
6 | const t = require('tap')
7 | const packlist = require('../')
8 |
9 | const pkg = t.testdir({
10 | 'package.json': JSON.stringify({
11 | files: [
12 | 'lib/dir',
13 | ],
14 | }),
15 | lib: {
16 | dir: {
17 | 'one.js': 'one',
18 | 'two.js': 'two',
19 | 'tre.js': 'tre',
20 | 'for.js': 'for',
21 | },
22 | '.npmignore': 'dir/two.js',
23 | '.DS_Store': 'a store of ds',
24 | },
25 | })
26 |
27 | t.test('package with negated files', async (t) => {
28 | const arborist = new Arborist({ path: pkg })
29 | const tree = await arborist.loadActual()
30 | const files = await packlist(tree)
31 | t.same(files, [
32 | 'lib/dir/for.js',
33 | 'lib/dir/one.js',
34 | 'lib/dir/tre.js',
35 | 'package.json',
36 | ])
37 | })
38 |
--------------------------------------------------------------------------------
/test/package-json-files-no-dir-nested-npmignore.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | files: [
10 | 'lib/*.js',
11 | ],
12 | }),
13 | lib: {
14 | 'one.js': 'one',
15 | 'two.js': 'two',
16 | 'tre.js': 'tre',
17 | 'for.js': 'for',
18 | '.npmignore': 'two.js',
19 | '.DS_Store': 'a store of ds',
20 | },
21 | })
22 |
23 | t.test('package with negated files', async (t) => {
24 | const arborist = new Arborist({ path: pkg })
25 | const tree = await arborist.loadActual()
26 | const files = await packlist(tree)
27 | t.same(files, [
28 | 'lib/for.js',
29 | 'lib/one.js',
30 | 'lib/tre.js',
31 | 'package.json',
32 | ])
33 | })
34 |
--------------------------------------------------------------------------------
/test/package-json-files-with-slashes.js:
--------------------------------------------------------------------------------
1 | // In v1, this would exclude the 'lib/two.js' file, because
2 | // the .npmignore is deeper in the tree and thus had higher
3 | // precedence. In v2, because /lib/two.js is in the files
4 | // list as a file path, it will be included no matter what.
5 | 'use strict'
6 |
7 | const Arborist = require('@npmcli/arborist')
8 | const t = require('tap')
9 | const packlist = require('../')
10 |
11 | const pkg = t.testdir({
12 | 'package.json': JSON.stringify({
13 | files: [
14 | './fiv.js',
15 | '/lib/one.js',
16 | '/lib/two.js',
17 | '/lib/tre.js',
18 | './lib/for.js',
19 | ],
20 | }),
21 | 'fiv.js': 'fiv',
22 | lib: {
23 | 'one.js': 'one',
24 | 'two.js': 'two',
25 | 'tre.js': 'tre',
26 | 'for.js': 'for',
27 | 'fiv.js': 'fiv',
28 | '.npmignore': 'two.js',
29 | '.DS_Store': 'a store of ds',
30 | },
31 | })
32 |
33 | t.test('package with slash files', async (t) => {
34 | const arborist = new Arborist({ path: pkg })
35 | const tree = await arborist.loadActual()
36 | const files = await packlist(tree)
37 | t.same(files, [
38 | 'fiv.js',
39 | 'lib/for.js',
40 | 'lib/one.js',
41 | 'lib/tre.js',
42 | 'lib/two.js',
43 | 'package.json',
44 | ])
45 | })
46 |
--------------------------------------------------------------------------------
/test/package-json-main.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: '__main.js',
17 | browser: 'browser.js',
18 | files: [
19 | 'elf.js',
20 | 'deps/foo/config/config.gypi',
21 | ],
22 | }),
23 | 'elf.js': elfJS,
24 | '__main.js': elfJS,
25 | 'browser.js': elfJS,
26 | '.npmrc': 'packaged=false',
27 |
28 | // don't bother even reading this file, because we have files list
29 | '.npmignore': '!.npmignore\n!dummy\npackage.json',
30 | dummy: 'foo',
31 | build: {
32 | 'config.gypi': "i_wont_be_included='with any luck'",
33 | 'npm-debug.log': '0 lol\n',
34 | },
35 | deps: {
36 | foo: {
37 | config: {
38 | 'config.gypi': "i_will_be_included='with any luck'",
39 | },
40 | },
41 | },
42 | '.git': {
43 | gitstub: "won't fool git, also won't be included",
44 | },
45 | node_modules: {
46 | history: {
47 | 'README.md': "please don't include me",
48 | },
49 | },
50 | })
51 |
52 | t.test('follows npm package ignoring rules', async (t) => {
53 | const arborist = new Arborist({ path: pkg })
54 | const tree = await arborist.loadActual()
55 | const files = await packlist(tree)
56 | t.same(files, [
57 | 'deps/foo/config/config.gypi',
58 | '__main.js',
59 | 'browser.js',
60 | 'elf.js',
61 | 'package.json',
62 | ])
63 | })
64 |
--------------------------------------------------------------------------------
/test/package-json-negate-non-root.js:
--------------------------------------------------------------------------------
1 | // exclude readme, license, and licence files if package.json
2 | // files array includes !readme, !license, or !licence
3 | 'use strict'
4 |
5 | const Arborist = require('@npmcli/arborist')
6 | const t = require('tap')
7 | const packlist = require('../')
8 |
9 | const pkg = t.testdir({
10 | 'package.json': JSON.stringify({
11 | files: [
12 | 'lib',
13 | '!readme.md',
14 | '!licence',
15 | '!license',
16 | '!copying',
17 | ],
18 |
19 | }),
20 | 'readme.md': 'one',
21 | licence: 'two',
22 | license: 'tre',
23 | copying: 'for',
24 | lib: {
25 | 'readme.md': 'one',
26 | licence: 'two',
27 | license: 'tre',
28 | copying: 'for',
29 | a: {
30 | 'readme.md': 'one',
31 | licence: 'two',
32 | license: 'tre',
33 | copying: 'for',
34 | b: {
35 | 'readme.md': 'one',
36 | licence: 'two',
37 | license: 'tre',
38 | copying: 'for',
39 | c: {
40 | 'readme.md': 'one',
41 | licence: 'two',
42 | license: 'tre',
43 | copying: 'for',
44 | 'file.txt': 'one',
45 | 'c.js': 'two',
46 | },
47 | 'file.txt': 'one',
48 | 'b.js': 'two',
49 | },
50 | 'file.txt': 'one',
51 | 'a.js': 'two',
52 | },
53 | } })
54 |
55 | t.test('package with negated readme, licence and license files', async (t) => {
56 | const arborist = new Arborist({ path: pkg })
57 | const tree = await arborist.loadActual()
58 | const files = await packlist(tree)
59 | t.same(files, [
60 | 'copying',
61 | 'licence',
62 | 'license',
63 | 'lib/a/a.js',
64 | 'lib/a/b/b.js',
65 | 'lib/a/b/c/c.js',
66 | 'package.json',
67 | 'readme.md',
68 | 'lib/a/b/c/file.txt',
69 | 'lib/a/b/file.txt',
70 | 'lib/a/file.txt',
71 | ])
72 | })
73 |
--------------------------------------------------------------------------------
/test/package-json-negated-files.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | files: [
10 | 'lib',
11 | '!lib/one',
12 | ],
13 | }),
14 | lib: {
15 | one: 'one',
16 | two: 'two',
17 | tre: 'tre',
18 | for: 'for',
19 | '.npmignore': 'two',
20 | '.DS_Store': 'a store of ds',
21 | },
22 | })
23 |
24 | t.test('package with negated files', async (t) => {
25 | const arborist = new Arborist({ path: pkg })
26 | const tree = await arborist.loadActual()
27 | const files = await packlist(tree)
28 | t.same(files, [
29 | 'lib/for',
30 | 'lib/tre',
31 | 'package.json',
32 | ])
33 | })
34 |
--------------------------------------------------------------------------------
/test/package-json-nested-readme-include-npmignore.js:
--------------------------------------------------------------------------------
1 | // readme rules can be overridden by files array
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: ['.npmignore', 'lib'],
11 | }),
12 | lib: {
13 | '.npmignore': `
14 | *
15 | !*.js
16 | !**/*.js
17 | test
18 | `,
19 | a: {
20 | b: {
21 | c: {
22 | 'readme.md': 'one',
23 | 'file.txt': 'one',
24 | 'c.js': 'one',
25 | },
26 | 'readme.md': 'one',
27 | 'file.txt': 'one',
28 | 'b.js': 'one',
29 | },
30 | 'readme.md': 'one',
31 | 'file.txt': 'one',
32 | 'a.js': 'one',
33 | },
34 | },
35 | test: {
36 | a: {
37 | b: {
38 | c: {
39 | 'readme.md': 'one',
40 | 'file.txt': 'one',
41 | 'c.js': 'one',
42 | },
43 | 'readme.md': 'one',
44 | 'file.txt': 'one',
45 | 'b.js': 'one',
46 | },
47 | 'readme.md': 'one',
48 | 'file.txt': 'one',
49 | 'a.js': 'one',
50 | },
51 | },
52 | })
53 |
54 | t.test('package with negated files', async (t) => {
55 | const arborist = new Arborist({ path: pkg })
56 | const tree = await arborist.loadActual()
57 | const files = await packlist(tree)
58 | t.same(files, [
59 | 'lib/a/a.js',
60 | 'lib/a/b/b.js',
61 | 'lib/a/b/c/c.js',
62 | 'package.json',
63 | ])
64 | })
65 |
--------------------------------------------------------------------------------
/test/package-json-nested-readme.js:
--------------------------------------------------------------------------------
1 | // include readme.* files anywhere in a package
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const t = require('tap')
6 | const packlist = require('../')
7 |
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({}),
10 | lib: {
11 | a: {
12 | b: {
13 | c: {
14 | 'readme.md': 'one',
15 | 'file.txt': 'one',
16 | 'c.js': 'one',
17 | },
18 | 'readme.md': 'one',
19 | 'file.txt': 'one',
20 | 'b.js': 'one',
21 | },
22 | 'readme.md': 'one',
23 | 'file.txt': 'one',
24 | 'a.js': 'one',
25 | },
26 | },
27 | test: {
28 | a: {
29 | b: {
30 | c: {
31 | 'readme.md': 'one',
32 | 'file.txt': 'one',
33 | 'c.js': 'one',
34 | },
35 | 'readme.md': 'one',
36 | 'file.txt': 'one',
37 | 'b.js': 'one',
38 | },
39 | 'readme.md': 'one',
40 | 'file.txt': 'one',
41 | 'a.js': 'one',
42 | },
43 | },
44 | '.npmignore': `
45 | !*.js
46 | !**/*.js
47 | test
48 | `,
49 | })
50 |
51 | t.test('package with negated files', async (t) => {
52 | const arborist = new Arborist({ path: pkg })
53 | const tree = await arborist.loadActual()
54 | const files = await packlist(tree)
55 | t.same(files, [
56 | 'lib/a/a.js',
57 | 'lib/a/b/b.js',
58 | 'lib/a/b/c/c.js',
59 | 'package.json',
60 | 'lib/a/b/c/readme.md',
61 | 'lib/a/b/readme.md',
62 | 'lib/a/readme.md',
63 | 'lib/a/b/c/file.txt',
64 | 'lib/a/b/file.txt',
65 | 'lib/a/file.txt',
66 | ])
67 | })
68 |
--------------------------------------------------------------------------------
/test/package-json-nested.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | name: 'test-package',
10 | version: '1.2.3',
11 | }),
12 | nest: {
13 | 'package.json': JSON.stringify({
14 | name: 'nested-package',
15 | version: '1.2.3',
16 | files: ['index.js'],
17 | }),
18 | 'index.js': 'console.log("hi")',
19 | 'foo.js': 'console.log("no")',
20 | },
21 | })
22 |
23 | t.test('includes nested package.json file', async (t) => {
24 | const arborist = new Arborist({ path: pkg })
25 | const tree = await arborist.loadActual()
26 | const files = await packlist(tree)
27 | t.same(files, [
28 | 'nest/foo.js',
29 | 'nest/index.js',
30 | 'nest/package.json',
31 | 'package.json',
32 | ])
33 | })
34 |
--------------------------------------------------------------------------------
/test/package-json-roots-and-nests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const pkg = t.testdir({
8 | 'package.json': JSON.stringify({
9 | bin: 'bin.js',
10 | main: 'main.js',
11 | browser: 'browser.js',
12 | dependencies: {
13 | foo: '1.0.0',
14 | '@foo/bar': '1.0.0',
15 | },
16 | bundleDependencies: [
17 | 'foo',
18 | '@foo/bar',
19 | ],
20 | }),
21 | node_modules: {
22 | // bundle dep files are ALWAYS included
23 | // even questionable things
24 | foo: {
25 | 'package-lock.json': 'include',
26 | },
27 | '@foo': {
28 | bar: {
29 | '.DS_Store': 'not this tho',
30 | },
31 | },
32 | },
33 | lib: {
34 | // these are not included
35 | 'package-lock.json': 'sw',
36 | 'package.json.js': '{}',
37 | 'bin.js': 'bin',
38 | 'main.js': 'main',
39 | 'browser.js': 'browser',
40 | 'npm-shrinkwrap.json': 'sw',
41 | },
42 |
43 | // these get included
44 | 'bin.js': 'bin',
45 | 'main.js': 'main',
46 | 'browser.js': 'browser',
47 | 'npm-shrinkwrap.json': 'sw',
48 | inc: {
49 | 'package.json': JSON.stringify({ files: [] }),
50 | 'package-lock.json': 'include me plz',
51 | foo: 'include me plz',
52 | },
53 |
54 | // these do not
55 | '.npmignore': 'lib/*',
56 | 'package-lock.json': 'sw',
57 | })
58 |
59 | t.test('package with negated files', async (t) => {
60 | const arborist = new Arborist({ path: pkg })
61 | const tree = await arborist.loadActual()
62 | const files = await packlist(tree)
63 | t.same(files, [
64 | 'node_modules/@foo/bar/.DS_Store',
65 | 'inc/foo',
66 | 'bin.js',
67 | 'browser.js',
68 | 'main.js',
69 | 'npm-shrinkwrap.json',
70 | 'inc/package-lock.json',
71 | 'node_modules/foo/package-lock.json',
72 | 'inc/package.json',
73 | 'package.json',
74 | ])
75 | })
76 |
--------------------------------------------------------------------------------
/test/package-json.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | files: [
17 | 'elf.js',
18 | 'deps/foo/config/config.gypi',
19 | ],
20 | }),
21 | 'npm-shrinkwrap.json': JSON.stringify({ shrink: 'wrap' }),
22 | 'elf.js': elfJS,
23 | '.npmrc': 'packaged=false',
24 | // don't bother even reading this file, because we have files list
25 | '.npmignore': '!.npmignore\n!dummy\npackage.json',
26 | dummy: 'foo',
27 | build: {
28 | 'config.gypi': "i_wont_be_included='with any luck'",
29 | 'npm-debug.log': '0 lol\n',
30 | },
31 | deps: {
32 | foo: {
33 | config: {
34 | 'config.gypi': "i_will_be_included='with any luck'",
35 | },
36 | },
37 | },
38 | '.git': {
39 | gitstub: "won't fool git, also won't be included",
40 | },
41 | node_modules: {
42 | history: {
43 | 'README.md': "please don't include me",
44 | },
45 | },
46 | })
47 |
48 | t.test('follows npm package ignoring rules', async (t) => {
49 | const arborist = new Arborist({ path: pkg })
50 | const tree = await arborist.loadActual()
51 | const files = await packlist(tree)
52 | t.same(files, [
53 | 'deps/foo/config/config.gypi',
54 | 'elf.js',
55 | 'package.json',
56 | ])
57 | })
58 |
--------------------------------------------------------------------------------
/test/package-not-json.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': "c'est ne pas une j'son",
14 | 'elf.js': elfJS,
15 | '.npmrc': 'packaged=false',
16 | '.npmignore': '.npmignore\ndummy\npackage.json\n',
17 | dummy: 'foo',
18 | build: {
19 | 'config.gypi': "i_wont_be_included='with any luck'",
20 | 'npm-debug.log': '0 lol\n',
21 | },
22 | deps: {
23 | foo: {
24 | config: {
25 | 'config.gypi': "i_will_be_included='with any luck'",
26 | },
27 | },
28 | },
29 | '.git': {
30 | gitstub: "won't fool git, also won't be included",
31 | },
32 | node_modules: {
33 | history: {
34 | 'README.md': "please don't include me",
35 | },
36 | },
37 | })
38 |
39 | t.test('follows npm package ignoring rules', async (t) => {
40 | const arborist = new Arborist({ path: pkg })
41 | const tree = await arborist.loadActual()
42 | const files = await packlist(tree)
43 | t.same(files, [
44 | 'deps/foo/config/config.gypi',
45 | 'elf.js',
46 | 'package.json',
47 | ])
48 | })
49 |
--------------------------------------------------------------------------------
/test/scoped.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package-scoped',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | dependencies: {
18 | '@npmwombat/scoped': '1.0.0',
19 | },
20 | bundledDependencies: [
21 | '@npmwombat/scoped',
22 | ],
23 | }),
24 | 'elf.js': elfJS,
25 | node_modules: {
26 | '@npmwombat': {
27 | scoped: {
28 | 'index.js': "console.log('hello wombat')",
29 | },
30 | no: {
31 | 'wombat.js': "console.log('no bundle please')",
32 | },
33 | },
34 | '@ignore': {
35 | scoped: {
36 | 'index.js': "console.log('i do not want to be bundled')",
37 | },
38 | },
39 | },
40 | })
41 |
42 | t.test('includes bundledDependencies', async (t) => {
43 | const arborist = new Arborist({ path: pkg })
44 | const tree = await arborist.loadActual()
45 | const files = await packlist(tree, { bundled: ['@npmwombat/scoped'] })
46 | t.same(files, [
47 | 'elf.js',
48 | 'node_modules/@npmwombat/scoped/index.js',
49 | 'package.json',
50 | ])
51 | })
52 |
--------------------------------------------------------------------------------
/test/sorting.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const t = require('tap')
4 | const Walker = require('../').Walker
5 | const sort = Walker.prototype.sort
6 |
7 | t.same([
8 | 'a/b/1',
9 | 'cat',
10 | 'dog',
11 | 'chai',
12 | 'package.json',
13 | 'node_modules/b/c',
14 | 'asdf.js',
15 | 'node_modules/a/b/c',
16 | 'a/b/c/d/e/f/g',
17 | 'a/b/x/y',
18 | ].sort(sort), [
19 | 'a/b/1',
20 | 'node_modules/a/b/c',
21 | 'node_modules/b/c',
22 | 'cat',
23 | 'chai',
24 | 'dog',
25 | 'a/b/c/d/e/f/g',
26 | 'a/b/x/y',
27 | 'asdf.js',
28 | 'package.json',
29 | ])
30 |
--------------------------------------------------------------------------------
/test/star-names.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const fs = require('fs')
6 |
7 | // the fs.readdir call takes place in ignore-walk, not npm-packlist, so we t.mock
8 | // ignore-walk first to clear its require cache and pick up the mocked fs
9 | const ignoreWalk = t.mock('ignore-walk', {
10 | fs: {
11 | ...fs,
12 | readdir: (path, callback) => {
13 | fs.readdir(path, (err, entries) => {
14 | if (err) {
15 | return callback(err)
16 | }
17 |
18 | return callback(null, entries.concat('made*of*stars'))
19 | })
20 | },
21 | },
22 | })
23 |
24 | // then we t.mock npm-packlist itself so that we can pick up the mocked ignore-walk
25 | const packlist = t.mock('../', {
26 | 'ignore-walk': ignoreWalk,
27 | })
28 |
29 | const elfJS = `
30 | module.exports = elf =>
31 | console.log("i'm a elf")
32 | `
33 |
34 | const pkg = t.testdir({
35 | 'package.json': JSON.stringify({
36 | name: 'test-package',
37 | version: '3.1.4',
38 | main: 'elf.js',
39 | }),
40 | 'elf.js': elfJS,
41 | '.npmrc': 'packaged=false',
42 | '.npmignore': '.npmignore\ndummy\npackage.json',
43 | dummy: 'foo',
44 |
45 | // empty dir should be ignored
46 | this: { dir: { is: { empty: { and: { ignored: {} } } } } },
47 | build: {
48 | 'config.gypi': "i_wont_be_included='with any luck'",
49 | 'npm-debug.log': '0 lol\n',
50 | },
51 | deps: {
52 | foo: {
53 | config: {
54 | 'config.gypi': "i_will_be_included='with any luck'",
55 | },
56 | },
57 | },
58 | '.git': {
59 | gitstub: "won't fool git, also won't be included",
60 | },
61 | node_modules: {
62 | history: {
63 | 'README.md': "please don't include me",
64 | },
65 | },
66 | })
67 |
68 | t.test('follows npm package ignoring rules', async (t) => {
69 | const arborist = new Arborist({ path: pkg })
70 | const tree = await arborist.loadActual()
71 | const files = await packlist(tree)
72 | t.same(files, [
73 | 'deps/foo/config/config.gypi',
74 | 'elf.js',
75 | 'package.json',
76 | ])
77 | })
78 |
--------------------------------------------------------------------------------
/test/strip-slash-from-package-files-list-entries.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | t.test('should strip / from package.json files array entry results', async (t) => {
8 | const pkg = t.testdir({
9 | 'package.json': JSON.stringify({
10 | files: [
11 | // include without slash, then exclude with it
12 | 'somedir',
13 | '!somedir/',
14 |
15 | // other way around
16 | 'otherdir/',
17 | '!otherdir',
18 |
19 | // now including it that way
20 | '!incldir/',
21 | 'incldir',
22 |
23 | // exclude without slash, then include with it
24 | '!dist',
25 | 'dist/',
26 | '!dist/foo/*.src',
27 | ],
28 | }),
29 | otherdir: {
30 | donotinclude: '',
31 | },
32 | somedir: {
33 | donotinclude: '',
34 | },
35 | incldir: {
36 | yesinclude: '',
37 | },
38 | foo: '',
39 | dist: {
40 | foo: {
41 | 'foo.src': '',
42 | 'foo.result': '',
43 | },
44 | bar: '',
45 | baz: {
46 | boo: '',
47 | 'boo.src': '',
48 | },
49 | },
50 | })
51 |
52 | const arborist = new Arborist({ path: pkg })
53 | const tree = await arborist.loadActual()
54 | const files = await packlist(tree)
55 | t.same(files, [
56 | 'dist/bar',
57 | 'dist/baz/boo',
58 | 'incldir/yesinclude',
59 | 'package.json',
60 | 'dist/foo/foo.result',
61 | 'dist/baz/boo.src',
62 | ])
63 | })
64 |
--------------------------------------------------------------------------------
/test/symlink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const t = require('tap')
5 | const packlist = require('../')
6 |
7 | const elfJS = `
8 | module.exports = elf =>
9 | console.log("i'm a elf")
10 | `
11 |
12 | const pkg = t.testdir({
13 | 'package.json': JSON.stringify({
14 | name: 'test-package',
15 | version: '3.1.4',
16 | main: 'elf.js',
17 | }),
18 | 'elf.js': elfJS,
19 | 'link.js': t.fixture('symlink', 'elf.js'),
20 | '.npmrc': 'packaged=false',
21 | '.npmignore': '.npmignore\ndummy\n/package.json\n',
22 | // empty dir should be ignored
23 | this: { dir: { is: { empty: { and: { ignored: {} } } } } },
24 | dummy: 'foo',
25 | build: {
26 | 'config.gypi': "i_wont_be_included='with any luck'",
27 | 'npm-debug.log': '0 lol\n',
28 | },
29 | deps: {
30 | foo: {
31 | config: {
32 | 'config.gypi': "i_will_be_included='with any luck'",
33 | },
34 | },
35 | },
36 | '.git': {
37 | gitstub: "won't fool git, also won't be included",
38 | },
39 | node_modules: {
40 | history: {
41 | 'README.md': "please don't include me",
42 | },
43 | },
44 |
45 | // ljharb's monorepo test goober that blew up
46 | test: { resolver: { multirepo: { packages: {
47 | a: {
48 | README: 'included',
49 | node_modules: {
50 | some_dep: {
51 | 'package.json': JSON.stringify({ version: '1.2.3' }),
52 | },
53 | '@scope': {
54 | b: t.fixture('symlink', '../../../b'),
55 | },
56 | },
57 | },
58 | b: {
59 | 'index.js': 'console.log("woop")',
60 | node_modules: {
61 | a: t.fixture('symlink', '../../a'),
62 | },
63 | },
64 | } } } },
65 | })
66 |
67 | t.test('follows npm package ignoring rules', async (t) => {
68 | const arborist = new Arborist({ path: pkg })
69 | const tree = await arborist.loadActual()
70 | const files = await packlist(tree)
71 | t.same(files, [
72 | 'test/resolver/multirepo/packages/a/README',
73 | 'deps/foo/config/config.gypi',
74 | 'elf.js',
75 | 'test/resolver/multirepo/packages/b/index.js',
76 | 'package.json',
77 | 'test/resolver/multirepo/packages/a/node_modules/some_dep/package.json',
78 | ])
79 | })
80 |
--------------------------------------------------------------------------------
/test/utils/bin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict'
3 |
4 | const Arborist = require('@npmcli/arborist')
5 | const packlist = require('../../')
6 |
7 | const dirs = []
8 | let doSort = false
9 | process.argv.slice(2).forEach(arg => {
10 | if (arg === '-h' || arg === '--help') {
11 | console.log('usage: npm-packlist [-s --sort] [directory, directory, ...]')
12 | process.exit(0)
13 | } else if (arg === '-s' || arg === '--sort') {
14 | doSort = true
15 | } else {
16 | dirs.push(arg)
17 | }
18 | })
19 |
20 | const sort = list => doSort ? list.sort((a, b) => a.localeCompare(b, 'en')) : list
21 |
22 | const main = async () => {
23 | if (!dirs.length) {
24 | const path = process.cwd()
25 | const arborist = new Arborist({ path })
26 | const tree = await arborist.loadActual()
27 | const results = await packlist(tree)
28 | console.log(sort(results).join('\n'))
29 | } else {
30 | for (const dir of dirs) {
31 | const arborist = new Arborist({ path: dir })
32 | const tree = await arborist.loadActual()
33 | console.group(`> ${dir}`)
34 | const results = await packlist(tree)
35 | console.log(sort(results).join('\n'))
36 | console.groupEnd()
37 | }
38 | }
39 | }
40 |
41 | // coverage disabled for catch handler because we don't need to test that
42 | main().catch(/* istanbul ignore next */(err) => {
43 | process.exitCode = 1
44 | console.error(err.stack)
45 | })
46 |
--------------------------------------------------------------------------------
/test/workspace.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Arborist = require('@npmcli/arborist')
4 | const path = require('path')
5 | const t = require('tap')
6 |
7 | const packlist = require('../')
8 |
9 | t.test('respects workspace root ignore files', async (t) => {
10 | const root = t.testdir({
11 | 'package.json': JSON.stringify({
12 | name: 'workspace-root',
13 | version: '1.0.0',
14 | main: 'root.js',
15 | workspaces: ['./workspaces/foo'],
16 | }),
17 | 'root.js': `console.log('hello')`,
18 | '.gitignore': 'ignore-me',
19 | 'ignore-me': 'should be ignored',
20 | workspaces: {
21 | '.gitignore': 'ignore-me-also',
22 | 'ignore-me': 'should be ignored',
23 | 'ignore-me-also': 'should also be ignored',
24 | foo: {
25 | 'package.json': JSON.stringify({
26 | name: 'workspace-child',
27 | version: '1.0.0',
28 | main: 'child.js',
29 | }),
30 | 'child.js': `console.log('hello')`,
31 | 'ignore-me': 'should be ignored',
32 | 'ignore-me-also': 'should also be ignored',
33 | },
34 | },
35 | })
36 |
37 | const workspacePath = path.join(root, 'workspaces', 'foo')
38 | const arborist = new Arborist({ path: workspacePath })
39 | const tree = await arborist.loadActual()
40 | // this simulates what it looks like when a user does i.e. npm pack -w ./workspaces/foo
41 | const files = await packlist(tree, {
42 | prefix: root,
43 | workspaces: [workspacePath],
44 | })
45 | t.same(files, [
46 | 'child.js',
47 | 'package.json',
48 | ])
49 |
50 | // leave off workspaces to prove that when prefix and root differ there is no change
51 | // in behavior without also specifying workspaces
52 | const secondFiles = await packlist(tree, {
53 | prefix: root,
54 | })
55 | t.same(secondFiles, [
56 | 'ignore-me',
57 | 'ignore-me-also',
58 | 'child.js',
59 | 'package.json',
60 | ])
61 | })
62 |
63 | t.test('packing a workspace root does not include children', async (t) => {
64 | const root = t.testdir({
65 | 'package.json': JSON.stringify({
66 | name: 'workspace-root',
67 | version: '1.0.0',
68 | main: 'root.js',
69 | workspaces: ['./workspaces/foo'],
70 | }),
71 | 'root.js': `console.log('hello')`,
72 | '.gitignore': 'ignore-me',
73 | 'ignore-me': 'should be ignored',
74 | workspaces: {
75 | '.gitignore': 'ignore-me-also',
76 | 'ignore-me': 'should be ignored',
77 | 'ignore-me-also': 'should also be ignored',
78 | foo: {
79 | 'package.json': JSON.stringify({
80 | name: 'workspace-child',
81 | version: '1.0.0',
82 | main: 'child.js',
83 | }),
84 | 'child.js': `console.log('hello')`,
85 | 'ignore-me': 'should be ignored',
86 | 'ignore-me-also': 'should also be ignored',
87 | },
88 | },
89 | })
90 |
91 | const workspacePath = path.join(root, 'workspaces', 'foo')
92 | // this simulates what it looks like when a user does `npm pack` from a workspace root
93 | const arborist = new Arborist({ path: root })
94 | const tree = await arborist.loadActual()
95 | const files = await packlist(tree, {
96 | prefix: root,
97 | workspaces: [workspacePath],
98 | })
99 | t.same(files, [
100 | 'root.js',
101 | 'package.json',
102 | ])
103 |
104 | // prove if we leave off workspaces we do not omit them
105 | const secondFiles = await packlist(tree, {
106 | prefix: root,
107 | })
108 | t.same(secondFiles, [
109 | 'workspaces/foo/child.js',
110 | 'root.js',
111 | 'package.json',
112 | 'workspaces/foo/package.json',
113 | ])
114 | })
115 |
116 | t.test('.gitignore is discarded if .npmignore exists outside of tree', async (t) => {
117 | const root = t.testdir({
118 | 'package.json': JSON.stringify({
119 | name: 'workspace-root',
120 | version: '1.0.0',
121 | main: 'root.js',
122 | workspaces: ['./workspaces/foo'],
123 | }),
124 | 'root.js': `console.log('hello')`,
125 | '.gitignore': 'dont-ignore-me',
126 | '.npmignore': 'only-ignore-me',
127 | 'dont-ignore-me': 'should not be ignored',
128 | 'only-ignore-me': 'should be ignored',
129 | workspaces: {
130 | '.gitignore': 'dont-ignore-me-either',
131 | '.npmignore': 'ignore-me-also',
132 | 'dont-ignore-me': 'should not be ignored',
133 | 'dont-ignore-me-either': 'should not be ignored',
134 | 'only-ignore-me': 'should be ignored',
135 | 'ignore-me-also': 'should be ignored',
136 | foo: {
137 | 'package.json': JSON.stringify({
138 | name: 'workspace-child',
139 | version: '1.0.0',
140 | main: 'child.js',
141 | }),
142 | 'child.js': `console.log('hello')`,
143 | 'dont-ignore-me': 'should not be ignored',
144 | 'dont-ignore-me-either': 'should not be ignored',
145 | 'only-ignore-me': 'should be ignored',
146 | 'ignore-me-also': 'should also be ignored',
147 | },
148 | },
149 | })
150 |
151 | const workspacePath = path.join(root, 'workspaces', 'foo')
152 | // this simulates what it looks like when a user does i.e. npm pack -w ./workspaces/foo
153 | const arborist = new Arborist({ path: workspacePath })
154 | const tree = await arborist.loadActual()
155 | const files = await packlist(tree, {
156 | prefix: root,
157 | workspaces: [workspacePath],
158 | })
159 | t.same(files, [
160 | 'dont-ignore-me',
161 | 'dont-ignore-me-either',
162 | 'child.js',
163 | 'package.json',
164 | ])
165 |
166 | // here we leave off workspaces to satisfy coverage
167 | const secondFiles = await packlist(tree, {
168 | prefix: root,
169 | })
170 | t.same(secondFiles, [
171 | 'dont-ignore-me',
172 | 'dont-ignore-me-either',
173 | 'ignore-me-also',
174 | 'only-ignore-me',
175 | 'child.js',
176 | 'package.json',
177 | ])
178 | })
179 |
--------------------------------------------------------------------------------