├── .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 └── to-batch-syntax.js ├── package.json ├── release-please-config.json ├── tap-snapshots ├── test-basic.js-TAP.test.js └── test │ └── basic.js.test.cjs └── test ├── 00-setup.js ├── basic.js ├── to-batch-syntax-tests.js └── zz-cleanup.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 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | audit: 16 | name: Audit Dependencies 17 | if: github.repository_owner == 'npm' 18 | runs-on: ubuntu-latest 19 | defaults: 20 | run: 21 | shell: bash 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | - name: Setup Git User 26 | run: | 27 | git config --global user.email "npm-cli+bot@github.com" 28 | git config --global user.name "npm CLI robot" 29 | - name: Setup Node 30 | uses: actions/setup-node@v4 31 | id: node 32 | with: 33 | node-version: 22.x 34 | check-latest: contains('22.x', '.x') 35 | - name: Install Latest npm 36 | uses: ./.github/actions/install-latest-npm 37 | with: 38 | node: ${{ steps.node.outputs.node-version }} 39 | - name: Install Dependencies 40 | run: npm i --ignore-scripts --no-audit --no-fund --package-lock 41 | - name: Run Production Audit 42 | run: npm audit --omit=dev 43 | - name: Run Full Audit 44 | run: npm audit --audit-level=none 45 | -------------------------------------------------------------------------------- /.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 | permissions: 22 | contents: read 23 | checks: write 24 | 25 | jobs: 26 | lint-all: 27 | name: Lint All 28 | if: github.repository_owner == 'npm' 29 | runs-on: ubuntu-latest 30 | defaults: 31 | run: 32 | shell: bash 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | with: 37 | ref: ${{ inputs.ref }} 38 | - name: Setup Git User 39 | run: | 40 | git config --global user.email "npm-cli+bot@github.com" 41 | git config --global user.name "npm CLI robot" 42 | - name: Create Check 43 | id: create-check 44 | if: ${{ inputs.check-sha }} 45 | uses: ./.github/actions/create-check 46 | with: 47 | name: "Lint All" 48 | token: ${{ secrets.GITHUB_TOKEN }} 49 | sha: ${{ inputs.check-sha }} 50 | - name: Setup Node 51 | uses: actions/setup-node@v4 52 | id: node 53 | with: 54 | node-version: 22.x 55 | check-latest: contains('22.x', '.x') 56 | - name: Install Latest npm 57 | uses: ./.github/actions/install-latest-npm 58 | with: 59 | node: ${{ steps.node.outputs.node-version }} 60 | - name: Install Dependencies 61 | run: npm i --ignore-scripts --no-audit --no-fund 62 | - name: Lint 63 | run: npm run lint --ignore-scripts 64 | - name: Post Lint 65 | run: npm run postlint --ignore-scripts 66 | - name: Conclude Check 67 | uses: LouisBrunner/checks-action@v1.6.0 68 | if: steps.create-check.outputs.check-id && always() 69 | with: 70 | token: ${{ secrets.GITHUB_TOKEN }} 71 | conclusion: ${{ job.status }} 72 | check_id: ${{ steps.create-check.outputs.check-id }} 73 | 74 | test-all: 75 | name: Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }} 76 | if: github.repository_owner == 'npm' 77 | strategy: 78 | fail-fast: false 79 | matrix: 80 | platform: 81 | - name: Linux 82 | os: ubuntu-latest 83 | shell: bash 84 | - name: macOS 85 | os: macos-latest 86 | shell: bash 87 | - name: macOS 88 | os: macos-13 89 | shell: bash 90 | - name: Windows 91 | os: windows-latest 92 | shell: cmd 93 | node-version: 94 | - 18.17.0 95 | - 18.x 96 | - 20.5.0 97 | - 20.x 98 | - 22.x 99 | exclude: 100 | - platform: { name: macOS, os: macos-13, shell: bash } 101 | node-version: 18.17.0 102 | - platform: { name: macOS, os: macos-13, shell: bash } 103 | node-version: 18.x 104 | - platform: { name: macOS, os: macos-13, shell: bash } 105 | node-version: 20.5.0 106 | - platform: { name: macOS, os: macos-13, shell: bash } 107 | node-version: 20.x 108 | - platform: { name: macOS, os: macos-13, shell: bash } 109 | node-version: 22.x 110 | runs-on: ${{ matrix.platform.os }} 111 | defaults: 112 | run: 113 | shell: ${{ matrix.platform.shell }} 114 | steps: 115 | - name: Checkout 116 | uses: actions/checkout@v4 117 | with: 118 | ref: ${{ inputs.ref }} 119 | - name: Setup Git User 120 | run: | 121 | git config --global user.email "npm-cli+bot@github.com" 122 | git config --global user.name "npm CLI robot" 123 | - name: Create Check 124 | id: create-check 125 | if: ${{ inputs.check-sha }} 126 | uses: ./.github/actions/create-check 127 | with: 128 | name: "Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }}" 129 | token: ${{ secrets.GITHUB_TOKEN }} 130 | sha: ${{ inputs.check-sha }} 131 | - name: Setup Node 132 | uses: actions/setup-node@v4 133 | id: node 134 | with: 135 | node-version: ${{ matrix.node-version }} 136 | check-latest: contains(matrix.node-version, '.x') 137 | - name: Install Latest npm 138 | uses: ./.github/actions/install-latest-npm 139 | with: 140 | node: ${{ steps.node.outputs.node-version }} 141 | - name: Install Dependencies 142 | run: npm i --ignore-scripts --no-audit --no-fund 143 | - name: Add Problem Matcher 144 | run: echo "::add-matcher::.github/matchers/tap.json" 145 | - name: Test 146 | run: npm test --ignore-scripts 147 | - name: Conclude Check 148 | uses: LouisBrunner/checks-action@v1.6.0 149 | if: steps.create-check.outputs.check-id && always() 150 | with: 151 | token: ${{ secrets.GITHUB_TOKEN }} 152 | conclusion: ${{ job.status }} 153 | check_id: ${{ steps.create-check.outputs.check-id }} 154 | -------------------------------------------------------------------------------- /.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 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | lint: 20 | name: Lint 21 | if: github.repository_owner == 'npm' 22 | runs-on: ubuntu-latest 23 | defaults: 24 | run: 25 | shell: bash 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - name: Setup Git User 30 | run: | 31 | git config --global user.email "npm-cli+bot@github.com" 32 | git config --global user.name "npm CLI robot" 33 | - name: Setup Node 34 | uses: actions/setup-node@v4 35 | id: node 36 | with: 37 | node-version: 22.x 38 | check-latest: contains('22.x', '.x') 39 | - name: Install Latest npm 40 | uses: ./.github/actions/install-latest-npm 41 | with: 42 | node: ${{ steps.node.outputs.node-version }} 43 | - name: Install Dependencies 44 | run: npm i --ignore-scripts --no-audit --no-fund 45 | - name: Lint 46 | run: npm run lint --ignore-scripts 47 | - name: Post Lint 48 | run: npm run postlint --ignore-scripts 49 | 50 | test: 51 | name: Test - ${{ matrix.platform.name }} - ${{ matrix.node-version }} 52 | if: github.repository_owner == 'npm' 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | platform: 57 | - name: Linux 58 | os: ubuntu-latest 59 | shell: bash 60 | - name: macOS 61 | os: macos-latest 62 | shell: bash 63 | - name: macOS 64 | os: macos-13 65 | shell: bash 66 | - name: Windows 67 | os: windows-latest 68 | shell: cmd 69 | node-version: 70 | - 18.17.0 71 | - 18.x 72 | - 20.5.0 73 | - 20.x 74 | - 22.x 75 | exclude: 76 | - platform: { name: macOS, os: macos-13, shell: bash } 77 | node-version: 18.17.0 78 | - platform: { name: macOS, os: macos-13, shell: bash } 79 | node-version: 18.x 80 | - platform: { name: macOS, os: macos-13, shell: bash } 81 | node-version: 20.5.0 82 | - platform: { name: macOS, os: macos-13, shell: bash } 83 | node-version: 20.x 84 | - platform: { name: macOS, os: macos-13, shell: bash } 85 | node-version: 22.x 86 | runs-on: ${{ matrix.platform.os }} 87 | defaults: 88 | run: 89 | shell: ${{ matrix.platform.shell }} 90 | steps: 91 | - name: Checkout 92 | uses: actions/checkout@v4 93 | - name: Setup Git User 94 | run: | 95 | git config --global user.email "npm-cli+bot@github.com" 96 | git config --global user.name "npm CLI robot" 97 | - name: Setup Node 98 | uses: actions/setup-node@v4 99 | id: node 100 | with: 101 | node-version: ${{ matrix.node-version }} 102 | check-latest: contains(matrix.node-version, '.x') 103 | - name: Install Latest npm 104 | uses: ./.github/actions/install-latest-npm 105 | with: 106 | node: ${{ steps.node.outputs.node-version }} 107 | - name: Install Dependencies 108 | run: npm i --ignore-scripts --no-audit --no-fund 109 | - name: Add Problem Matcher 110 | run: echo "::add-matcher::.github/matchers/tap.json" 111 | - name: Test 112 | run: npm test --ignore-scripts 113 | -------------------------------------------------------------------------------- /.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 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | analyze: 21 | name: Analyze 22 | runs-on: ubuntu-latest 23 | permissions: 24 | actions: read 25 | contents: read 26 | security-events: write 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Setup Git User 31 | run: | 32 | git config --global user.email "npm-cli+bot@github.com" 33 | git config --global user.name "npm CLI robot" 34 | - name: Initialize CodeQL 35 | uses: github/codeql-action/init@v3 36 | with: 37 | languages: javascript 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | -------------------------------------------------------------------------------- /.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 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | commitlint: 18 | name: Lint Commits 19 | if: github.repository_owner == 'npm' 20 | runs-on: ubuntu-latest 21 | defaults: 22 | run: 23 | shell: bash 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | - name: Setup Git User 30 | run: | 31 | git config --global user.email "npm-cli+bot@github.com" 32 | git config --global user.name "npm CLI robot" 33 | - name: Setup Node 34 | uses: actions/setup-node@v4 35 | id: node 36 | with: 37 | node-version: 22.x 38 | check-latest: contains('22.x', '.x') 39 | - name: Install Latest npm 40 | uses: ./.github/actions/install-latest-npm 41 | with: 42 | node: ${{ steps.node.outputs.node-version }} 43 | - name: Install Dependencies 44 | run: npm i --ignore-scripts --no-audit --no-fund 45 | - name: Run Commitlint on Commits 46 | id: commit 47 | continue-on-error: true 48 | run: npx --offline commitlint -V --from 'origin/${{ github.base_ref }}' --to ${{ github.event.pull_request.head.sha }} 49 | - name: Run Commitlint on PR Title 50 | if: steps.commit.outcome == 'failure' 51 | env: 52 | PR_TITLE: ${{ github.event.pull_request.title }} 53 | run: echo "$PR_TITLE" | npx --offline commitlint -V 54 | -------------------------------------------------------------------------------- /.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 | permissions: 23 | contents: read 24 | id-token: write 25 | 26 | jobs: 27 | publish: 28 | name: Publish 29 | runs-on: ubuntu-latest 30 | defaults: 31 | run: 32 | shell: bash 33 | permissions: 34 | id-token: write 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v4 38 | with: 39 | ref: ${{ fromJSON(inputs.releases)[0].tagName }} 40 | - name: Setup Git User 41 | run: | 42 | git config --global user.email "npm-cli+bot@github.com" 43 | git config --global user.name "npm CLI robot" 44 | - name: Setup Node 45 | uses: actions/setup-node@v4 46 | id: node 47 | with: 48 | node-version: 22.x 49 | check-latest: contains('22.x', '.x') 50 | - name: Install Latest npm 51 | uses: ./.github/actions/install-latest-npm 52 | with: 53 | node: ${{ steps.node.outputs.node-version }} 54 | - name: Install Dependencies 55 | run: npm i --ignore-scripts --no-audit --no-fund 56 | - name: Set npm authToken 57 | run: npm config set '//registry.npmjs.org/:_authToken'=\${PUBLISH_TOKEN} 58 | - name: Publish 59 | env: 60 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} 61 | RELEASES: ${{ inputs.releases }} 62 | run: | 63 | EXIT_CODE=0 64 | 65 | for release in $(echo $RELEASES | jq -r '.[] | @base64'); do 66 | PUBLISH_TAG=$(echo "$release" | base64 --decode | jq -r .publishTag) 67 | npm publish --provenance --tag="$PUBLISH_TAG" 68 | STATUS=$? 69 | if [[ "$STATUS" -eq 1 ]]; then 70 | EXIT_CODE=$STATUS 71 | fi 72 | done 73 | 74 | exit $EXIT_CODE 75 | -------------------------------------------------------------------------------- /.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 | contents: read 248 | id-token: write 249 | secrets: 250 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} 251 | with: 252 | releases: ${{ needs.release.outputs.releases }} 253 | 254 | post-release-integration: 255 | needs: [ release, release-integration, post-release ] 256 | name: Post Release Integration - Release 257 | if: github.repository_owner == 'npm' && needs.release.outputs.releases && always() 258 | runs-on: ubuntu-latest 259 | defaults: 260 | run: 261 | shell: bash 262 | steps: 263 | - name: Get Post Release Conclusion 264 | id: conclusion 265 | run: | 266 | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then 267 | result="x" 268 | elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then 269 | result="heavy_multiplication_x" 270 | else 271 | result="white_check_mark" 272 | fi 273 | echo "result=$result" >> $GITHUB_OUTPUT 274 | - name: Find Release PR Comment 275 | uses: peter-evans/find-comment@v2 276 | id: found-comment 277 | with: 278 | issue-number: ${{ fromJSON(needs.release.outputs.releases)[0].prNumber }} 279 | comment-author: 'github-actions[bot]' 280 | body-includes: '## Release Workflow' 281 | - name: Create Release PR Comment Text 282 | id: comment-text 283 | if: steps.found-comment.outputs.comment-id 284 | uses: actions/github-script@v7 285 | env: 286 | RESULT: ${{ steps.conclusion.outputs.result }} 287 | BODY: ${{ steps.found-comment.outputs.comment-body }} 288 | with: 289 | result-encoding: string 290 | script: | 291 | const { RESULT, BODY } = process.env 292 | const body = [BODY.replace(/(Workflow run: :)[a-z_]+(:)/, `$1${RESULT}$2`)] 293 | if (RESULT !== 'white_check_mark') { 294 | body.push(':rotating_light::rotating_light::rotating_light:') 295 | body.push([ 296 | '@npm/cli-team: The post-release workflow failed for this release.', 297 | 'Manual steps may need to be taken after examining the workflow output.' 298 | ].join(' ')) 299 | body.push(':rotating_light::rotating_light::rotating_light:') 300 | } 301 | return body.join('\n\n').trim() 302 | - name: Update Release PR Comment 303 | if: steps.comment-text.outputs.result 304 | uses: peter-evans/create-or-update-comment@v3 305 | with: 306 | comment-id: ${{ steps.found-comment.outputs.comment-id }} 307 | body: ${{ steps.comment-text.outputs.result }} 308 | edit-mode: 'replace' 309 | -------------------------------------------------------------------------------- /.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 | ".": "7.0.0" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [7.0.0](https://github.com/npm/cmd-shim/compare/v6.0.3...v7.0.0) (2024-08-26) 4 | 5 | ### ⚠️ BREAKING CHANGES 6 | 7 | * `cmd-shim` now supports node `^18.17.0 || >=20.5.0` 8 | 9 | ### Bug Fixes 10 | 11 | * [`aaf6016`](https://github.com/npm/cmd-shim/commit/aaf601687a131bec7eb94ff06d8fb417f95eed6e) [#154](https://github.com/npm/cmd-shim/pull/154) align to npm 10 node engine range (@hashtagchris) 12 | 13 | ### Chores 14 | 15 | * [`926af07`](https://github.com/npm/cmd-shim/commit/926af0785a59e512d3ea9316b58f98bbe9fb2a88) [#154](https://github.com/npm/cmd-shim/pull/154) run template-oss-apply (@hashtagchris) 16 | * [`7c154f3`](https://github.com/npm/cmd-shim/commit/7c154f30af2c4e52d34320d044e1201017cbcea2) [#151](https://github.com/npm/cmd-shim/pull/151) postinstall for dependabot template-oss PR (@hashtagchris) 17 | * [`21178ef`](https://github.com/npm/cmd-shim/commit/21178ef7fc0d087c3071451547e5c635cc19271e) [#151](https://github.com/npm/cmd-shim/pull/151) bump @npmcli/template-oss from 4.22.0 to 4.23.1 (@dependabot[bot]) 18 | 19 | ## [6.0.3](https://github.com/npm/cmd-shim/compare/v6.0.2...v6.0.3) (2024-05-04) 20 | 21 | ### Bug Fixes 22 | 23 | * [`dbe91b4`](https://github.com/npm/cmd-shim/commit/dbe91b4433990e0566903b29a2a17d81ded5890b) [#140](https://github.com/npm/cmd-shim/pull/140) linting: no-unused-vars (@lukekarrys) 24 | 25 | ### Documentation 26 | 27 | * [`5598b4b`](https://github.com/npm/cmd-shim/commit/5598b4b5d04d42201543dc67b459f0a7db78c211) [#137](https://github.com/npm/cmd-shim/pull/137) readme: fix broken badge URLs (#137) (@10xLaCroixDrinker) 28 | 29 | ### Chores 30 | 31 | * [`3140bb1`](https://github.com/npm/cmd-shim/commit/3140bb131f84ac8fc284f1120c5a963621cd001f) [#140](https://github.com/npm/cmd-shim/pull/140) bump @npmcli/template-oss to 4.22.0 (@lukekarrys) 32 | * [`c944f6b`](https://github.com/npm/cmd-shim/commit/c944f6bb60f10b0c2aa803427233a7c76169a8c1) [#140](https://github.com/npm/cmd-shim/pull/140) postinstall for dependabot template-oss PR (@lukekarrys) 33 | * [`fa3f56b`](https://github.com/npm/cmd-shim/commit/fa3f56b3ab022523f59cae4081912a8b535ac234) [#139](https://github.com/npm/cmd-shim/pull/139) bump @npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot]) 34 | 35 | ## [6.0.2](https://github.com/npm/cmd-shim/compare/v6.0.1...v6.0.2) (2023-10-18) 36 | 37 | ### Bug Fixes 38 | 39 | * [`3bdb518`](https://github.com/npm/cmd-shim/commit/3bdb518db21ec6ae64cda74405c7025ee76ccd76) [#117](https://github.com/npm/cmd-shim/pull/117) don't assume cygpath is available (#117) (@davhdavh) 40 | 41 | ## [6.0.1](https://github.com/npm/cmd-shim/compare/v6.0.0...v6.0.1) (2022-12-13) 42 | 43 | ### Bug Fixes 44 | 45 | * [`a32b3a0`](https://github.com/npm/cmd-shim/commit/a32b3a06615ed60afaa0441015fb1a456b6be488) [#96](https://github.com/npm/cmd-shim/pull/96) drop the -S flag from env (#96) (@nlf, @yeldiRium) 46 | 47 | ## [6.0.0](https://github.com/npm/cmd-shim/compare/v5.0.0...v6.0.0) (2022-10-11) 48 | 49 | ### ⚠️ BREAKING CHANGES 50 | 51 | * this module will no longer attempt to alter file ownership 52 | * `cmd-shim` is now compatible with the following semver range for node: `^14.17.0 || ^16.13.0 || >=18.0.0` 53 | 54 | ### Features 55 | 56 | * [`ef99e1c`](https://github.com/npm/cmd-shim/commit/ef99e1cb605c2c8c35d40d8aa771f8060540ac91) [#88](https://github.com/npm/cmd-shim/pull/88) remove ownership changing features (#88) (@nlf) 57 | * [`d5881e8`](https://github.com/npm/cmd-shim/commit/d5881e893bd5a2405ecd6cc53bcb862b43665860) [#82](https://github.com/npm/cmd-shim/pull/82) postinstall for dependabot template-oss PR (@lukekarrys) 58 | 59 | ## [5.0.0](https://github.com/npm/cmd-shim/compare/v4.1.0...v5.0.0) (2022-04-05) 60 | 61 | 62 | ### ⚠ BREAKING CHANGES 63 | 64 | * this drops support for node 10 and non-LTS versions of node 12 and node 14 65 | 66 | ### Bug Fixes 67 | 68 | * replace deprecated String.prototype.substr() ([#59](https://github.com/npm/cmd-shim/issues/59)) ([383d7e9](https://github.com/npm/cmd-shim/commit/383d7e954b72b76d88fad74510204c8ed12a37c1)) 69 | * safer regex ([470678a](https://github.com/npm/cmd-shim/commit/470678a5bfbdacabda8327a3e181bbf0cbcaba12)) 70 | 71 | 72 | ### Dependencies 73 | 74 | * @npmcli/template-oss@3.2.2 ([dfb091d](https://github.com/npm/cmd-shim/commit/dfb091de0d61fa83ee1a32ceb7810565bf7ed31b)) 75 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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) npm, Inc. 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 | # cmd-shim 2 | 3 | The cmd-shim used in npm to create executable scripts on Windows, 4 | since symlinks are not suitable for this purpose there. 5 | 6 | On Unix systems, you should use a symbolic link instead. 7 | 8 | [![Build Status](https://img.shields.io/github/actions/workflow/status/npm/cmd-shim/ci.yml?branch=main)](https://github.com/npm/cmd-shim) 9 | [![Dependency Status](https://img.shields.io/librariesio/github/npm/cmd-shim)](https://libraries.io/npm/cmd-shim) 10 | [![npm version](https://img.shields.io/npm/v/cmd-shim.svg)](https://www.npmjs.com/package/cmd-shim) 11 | 12 | ## Installation 13 | 14 | ``` 15 | npm install cmd-shim 16 | ``` 17 | 18 | ## API 19 | 20 | ### cmdShim(from, to) -> Promise 21 | 22 | Create a cmd shim at `to` for the command line program at `from`. 23 | e.g. 24 | 25 | ```javascript 26 | var cmdShim = require('cmd-shim'); 27 | cmdShim(__dirname + '/cli.js', '/usr/bin/command-name').then(() => { 28 | // shims are created! 29 | }) 30 | ``` 31 | 32 | ### cmdShim.ifExists(from, to) -> Promise 33 | 34 | The same as above, but will just continue if the file does not exist. 35 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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 | // On windows, create a .cmd file. 2 | // Read the #! in the file to see what it uses. The vast majority 3 | // of the time, this will be either: 4 | // "#!/usr/bin/env <prog> <args...>" 5 | // or: 6 | // "#!<prog> <args...>" 7 | // 8 | // Write a binroot/pkg.bin + ".cmd" file that has this line in it: 9 | // @<prog> <args...> %dp0%<target> %* 10 | 11 | const { 12 | chmod, 13 | mkdir, 14 | readFile, 15 | stat, 16 | unlink, 17 | writeFile, 18 | } = require('fs/promises') 19 | 20 | const { dirname, relative } = require('path') 21 | const toBatchSyntax = require('./to-batch-syntax') 22 | // linting disabled because this regex is really long 23 | // eslint-disable-next-line max-len 24 | const shebangExpr = /^#!\s*(?:\/usr\/bin\/env\s+(?:-S\s+)?((?:[^ \t=]+=[^ \t=]+\s+)*))?([^ \t]+)(.*)$/ 25 | 26 | const cmdShimIfExists = (from, to) => 27 | stat(from).then(() => cmdShim(from, to), () => {}) 28 | 29 | // Try to unlink, but ignore errors. 30 | // Any problems will surface later. 31 | const rm = path => unlink(path).catch(() => {}) 32 | 33 | const cmdShim = (from, to) => 34 | stat(from).then(() => cmdShim_(from, to)) 35 | 36 | const cmdShim_ = (from, to) => Promise.all([ 37 | rm(to), 38 | rm(to + '.cmd'), 39 | rm(to + '.ps1'), 40 | ]).then(() => writeShim(from, to)) 41 | 42 | const writeShim = (from, to) => 43 | // make a cmd file and a sh script 44 | // First, check if the bin is a #! of some sort. 45 | // If not, then assume it's something that'll be compiled, or some other 46 | // sort of script, and just call it directly. 47 | mkdir(dirname(to), { recursive: true }) 48 | .then(() => readFile(from, 'utf8')) 49 | .then(data => { 50 | const firstLine = data.trim().split(/\r*\n/)[0] 51 | const shebang = firstLine.match(shebangExpr) 52 | if (!shebang) { 53 | return writeShim_(from, to) 54 | } 55 | const vars = shebang[1] || '' 56 | const prog = shebang[2] 57 | const args = shebang[3] || '' 58 | return writeShim_(from, to, prog, args, vars) 59 | }, () => writeShim_(from, to)) 60 | 61 | const writeShim_ = (from, to, prog, args, variables) => { 62 | let shTarget = relative(dirname(to), from) 63 | let target = shTarget.split('/').join('\\') 64 | let longProg 65 | let shProg = prog && prog.split('\\').join('/') 66 | let shLongProg 67 | let pwshProg = shProg && `"${shProg}$exe"` 68 | let pwshLongProg 69 | shTarget = shTarget.split('\\').join('/') 70 | args = args || '' 71 | variables = variables || '' 72 | if (!prog) { 73 | prog = `"%dp0%\\${target}"` 74 | shProg = `"$basedir/${shTarget}"` 75 | pwshProg = shProg 76 | args = '' 77 | target = '' 78 | shTarget = '' 79 | } else { 80 | longProg = `"%dp0%\\${prog}.exe"` 81 | shLongProg = `"$basedir/${prog}"` 82 | pwshLongProg = `"$basedir/${prog}$exe"` 83 | target = `"%dp0%\\${target}"` 84 | shTarget = `"$basedir/${shTarget}"` 85 | } 86 | 87 | // Subroutine trick to fix https://github.com/npm/cmd-shim/issues/10 88 | // and https://github.com/npm/cli/issues/969 89 | const head = '@ECHO off\r\n' + 90 | 'GOTO start\r\n' + 91 | ':find_dp0\r\n' + 92 | 'SET dp0=%~dp0\r\n' + 93 | 'EXIT /b\r\n' + 94 | ':start\r\n' + 95 | 'SETLOCAL\r\n' + 96 | 'CALL :find_dp0\r\n' 97 | 98 | let cmd 99 | if (longProg) { 100 | shLongProg = shLongProg.trim() 101 | args = args.trim() 102 | const variablesBatch = toBatchSyntax.convertToSetCommands(variables) 103 | cmd = head 104 | + variablesBatch 105 | + '\r\n' 106 | + `IF EXIST ${longProg} (\r\n` 107 | + ` SET "_prog=${longProg.replace(/(^")|("$)/g, '')}"\r\n` 108 | + ') ELSE (\r\n' 109 | + ` SET "_prog=${prog.replace(/(^")|("$)/g, '')}"\r\n` 110 | + ' SET PATHEXT=%PATHEXT:;.JS;=;%\r\n' 111 | + ')\r\n' 112 | + '\r\n' 113 | // prevent "Terminate Batch Job? (Y/n)" message 114 | // https://github.com/npm/cli/issues/969#issuecomment-737496588 115 | + 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & ' 116 | + `"%_prog%" ${args} ${target} %*\r\n` 117 | } else { 118 | cmd = `${head}${prog} ${args} ${target} %*\r\n` 119 | } 120 | 121 | // #!/bin/sh 122 | // basedir=`dirname "$0"` 123 | // 124 | // case `uname` in 125 | // *CYGWIN*|*MINGW*|*MSYS*) 126 | // if command -v cygpath > /dev/null 2>&1; then 127 | // basedir=`cygpath -w "$basedir"` 128 | // fi 129 | // ;; 130 | // esac 131 | // 132 | // if [ -x "$basedir/node.exe" ]; then 133 | // exec "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@" 134 | // else 135 | // exec node "$basedir/node_modules/npm/bin/npm-cli.js" "$@" 136 | // fi 137 | 138 | let sh = '#!/bin/sh\n' 139 | 140 | sh = sh 141 | + `basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")\n` 142 | + '\n' 143 | + 'case `uname` in\n' 144 | + ' *CYGWIN*|*MINGW*|*MSYS*)\n' 145 | + ' if command -v cygpath > /dev/null 2>&1; then\n' 146 | + ' basedir=`cygpath -w "$basedir"`\n' 147 | + ' fi\n' 148 | + ' ;;\n' 149 | + 'esac\n' 150 | + '\n' 151 | 152 | if (shLongProg) { 153 | sh = sh 154 | + `if [ -x ${shLongProg} ]; then\n` 155 | + ` exec ${variables}${shLongProg} ${args} ${shTarget} "$@"\n` 156 | + 'else \n' 157 | + ` exec ${variables}${shProg} ${args} ${shTarget} "$@"\n` 158 | + 'fi\n' 159 | } else { 160 | sh = sh 161 | + `exec ${shProg} ${args} ${shTarget} "$@"\n` 162 | } 163 | 164 | // #!/usr/bin/env pwsh 165 | // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 166 | // 167 | // $ret=0 168 | // $exe = "" 169 | // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 170 | // # Fix case when both the Windows and Linux builds of Node 171 | // # are installed in the same directory 172 | // $exe = ".exe" 173 | // } 174 | // if (Test-Path "$basedir/node") { 175 | // # Suport pipeline input 176 | // if ($MyInvocation.ExpectingInput) { 177 | // input | & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 178 | // } else { 179 | // & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 180 | // } 181 | // $ret=$LASTEXITCODE 182 | // } else { 183 | // # Support pipeline input 184 | // if ($MyInvocation.ExpectingInput) { 185 | // $input | & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 186 | // } else { 187 | // & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 188 | // } 189 | // $ret=$LASTEXITCODE 190 | // } 191 | // exit $ret 192 | let pwsh = '#!/usr/bin/env pwsh\n' 193 | + '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n' 194 | + '\n' 195 | + '$exe=""\n' 196 | + 'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {\n' 197 | + ' # Fix case when both the Windows and Linux builds of Node\n' 198 | + ' # are installed in the same directory\n' 199 | + ' $exe=".exe"\n' 200 | + '}\n' 201 | if (shLongProg) { 202 | pwsh = pwsh 203 | + '$ret=0\n' 204 | + `if (Test-Path ${pwshLongProg}) {\n` 205 | + ' # Support pipeline input\n' 206 | + ' if ($MyInvocation.ExpectingInput) {\n' 207 | + ` $input | & ${pwshLongProg} ${args} ${shTarget} $args\n` 208 | + ' } else {\n' 209 | + ` & ${pwshLongProg} ${args} ${shTarget} $args\n` 210 | + ' }\n' 211 | + ' $ret=$LASTEXITCODE\n' 212 | + '} else {\n' 213 | + ' # Support pipeline input\n' 214 | + ' if ($MyInvocation.ExpectingInput) {\n' 215 | + ` $input | & ${pwshProg} ${args} ${shTarget} $args\n` 216 | + ' } else {\n' 217 | + ` & ${pwshProg} ${args} ${shTarget} $args\n` 218 | + ' }\n' 219 | + ' $ret=$LASTEXITCODE\n' 220 | + '}\n' 221 | + 'exit $ret\n' 222 | } else { 223 | pwsh = pwsh 224 | + '# Support pipeline input\n' 225 | + 'if ($MyInvocation.ExpectingInput) {\n' 226 | + ` $input | & ${pwshProg} ${args} ${shTarget} $args\n` 227 | + '} else {\n' 228 | + ` & ${pwshProg} ${args} ${shTarget} $args\n` 229 | + '}\n' 230 | + 'exit $LASTEXITCODE\n' 231 | } 232 | 233 | return Promise.all([ 234 | writeFile(to + '.ps1', pwsh, 'utf8'), 235 | writeFile(to + '.cmd', cmd, 'utf8'), 236 | writeFile(to, sh, 'utf8'), 237 | ]).then(() => chmodShim(to)) 238 | } 239 | 240 | const chmodShim = to => Promise.all([ 241 | chmod(to, 0o755), 242 | chmod(to + '.cmd', 0o755), 243 | chmod(to + '.ps1', 0o755), 244 | ]) 245 | 246 | module.exports = cmdShim 247 | cmdShim.ifExists = cmdShimIfExists 248 | -------------------------------------------------------------------------------- /lib/to-batch-syntax.js: -------------------------------------------------------------------------------- 1 | exports.replaceDollarWithPercentPair = replaceDollarWithPercentPair 2 | exports.convertToSetCommand = convertToSetCommand 3 | exports.convertToSetCommands = convertToSetCommands 4 | 5 | function convertToSetCommand (key, value) { 6 | var line = '' 7 | key = key || '' 8 | key = key.trim() 9 | value = value || '' 10 | value = value.trim() 11 | if (key && value && value.length > 0) { 12 | line = '@SET ' + key + '=' + replaceDollarWithPercentPair(value) + '\r\n' 13 | } 14 | return line 15 | } 16 | 17 | function extractVariableValuePairs (declarations) { 18 | var pairs = {} 19 | declarations.map(function (declaration) { 20 | var split = declaration.split('=') 21 | pairs[split[0]] = split[1] 22 | }) 23 | return pairs 24 | } 25 | 26 | function convertToSetCommands (variableString) { 27 | var variableValuePairs = extractVariableValuePairs(variableString.split(' ')) 28 | var variableDeclarationsAsBatch = '' 29 | Object.keys(variableValuePairs).forEach(function (key) { 30 | variableDeclarationsAsBatch += convertToSetCommand(key, variableValuePairs[key]) 31 | }) 32 | return variableDeclarationsAsBatch 33 | } 34 | 35 | function replaceDollarWithPercentPair (value) { 36 | var dollarExpressions = /\$\{?([^$@#?\- \t{}:]+)\}?/g 37 | var result = '' 38 | var startIndex = 0 39 | do { 40 | var match = dollarExpressions.exec(value) 41 | if (match) { 42 | var betweenMatches = value.substring(startIndex, match.index) || '' 43 | result += betweenMatches + '%' + match[1] + '%' 44 | startIndex = dollarExpressions.lastIndex 45 | } 46 | } while (dollarExpressions.lastIndex > 0) 47 | result += value.slice(startIndex) 48 | return result 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cmd-shim", 3 | "version": "7.0.0", 4 | "description": "Used in npm for command line application support", 5 | "scripts": { 6 | "test": "tap", 7 | "snap": "tap", 8 | "lint": "npm run eslint", 9 | "postlint": "template-oss-check", 10 | "template-oss-apply": "template-oss-apply --force", 11 | "lintfix": "npm run eslint -- --fix", 12 | "posttest": "npm run lint", 13 | "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/npm/cmd-shim.git" 18 | }, 19 | "license": "ISC", 20 | "devDependencies": { 21 | "@npmcli/eslint-config": "^5.0.0", 22 | "@npmcli/template-oss": "4.24.3", 23 | "tap": "^16.0.1" 24 | }, 25 | "files": [ 26 | "bin/", 27 | "lib/" 28 | ], 29 | "main": "lib/index.js", 30 | "tap": { 31 | "before": "test/00-setup.js", 32 | "after": "test/zz-cleanup.js", 33 | "check-coverage": true, 34 | "nyc-arg": [ 35 | "--exclude", 36 | "tap-snapshots/**" 37 | ] 38 | }, 39 | "engines": { 40 | "node": "^18.17.0 || >=20.5.0" 41 | }, 42 | "author": "GitHub Inc.", 43 | "templateOSS": { 44 | "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", 45 | "version": "4.24.3", 46 | "publish": true 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 | } 35 | }, 36 | "prerelease-type": "pre.0" 37 | } 38 | -------------------------------------------------------------------------------- /tap-snapshots/test-basic.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/basic.js TAP env shebang > cmd 1`] = ` 9 | @ECHO off\\r 10 | GOTO start\\r 11 | :find_dp0\\r 12 | SET dp0=%~dp0\\r 13 | EXIT /b\\r 14 | :start\\r 15 | SETLOCAL\\r 16 | CALL :find_dp0\\r 17 | \\r 18 | IF EXIST "%dp0%\\node.exe" (\\r 19 | SET "_prog=%dp0%\\node.exe"\\r 20 | ) ELSE (\\r 21 | SET "_prog=node"\\r 22 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 23 | )\\r 24 | \\r 25 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.env" %*\\r 26 | 27 | ` 28 | 29 | exports[`test/basic.js TAP env shebang > ps1 1`] = ` 30 | #!/usr/bin/env pwsh 31 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 32 | 33 | $exe="" 34 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 35 | # Fix case when both the Windows and Linux builds of Node 36 | # are installed in the same directory 37 | $exe=".exe" 38 | } 39 | $ret=0 40 | if (Test-Path "$basedir/node$exe") { 41 | # Support pipeline input 42 | if ($MyInvocation.ExpectingInput) { 43 | $input | & "$basedir/node$exe" "$basedir/from.env" $args 44 | } else { 45 | & "$basedir/node$exe" "$basedir/from.env" $args 46 | } 47 | $ret=$LASTEXITCODE 48 | } else { 49 | # Support pipeline input 50 | if ($MyInvocation.ExpectingInput) { 51 | $input | & "node$exe" "$basedir/from.env" $args 52 | } else { 53 | & "node$exe" "$basedir/from.env" $args 54 | } 55 | $ret=$LASTEXITCODE 56 | } 57 | exit $ret 58 | 59 | ` 60 | 61 | exports[`test/basic.js TAP env shebang > shell 1`] = ` 62 | #!/bin/sh 63 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 64 | 65 | case \`uname\` in 66 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 67 | esac 68 | 69 | if [ -x "$basedir/node" ]; then 70 | exec "$basedir/node" "$basedir/from.env" "$@" 71 | else 72 | exec node "$basedir/from.env" "$@" 73 | fi 74 | 75 | ` 76 | 77 | exports[`test/basic.js TAP env shebang with args > cmd 1`] = ` 78 | @ECHO off\\r 79 | GOTO start\\r 80 | :find_dp0\\r 81 | SET dp0=%~dp0\\r 82 | EXIT /b\\r 83 | :start\\r 84 | SETLOCAL\\r 85 | CALL :find_dp0\\r 86 | \\r 87 | IF EXIST "%dp0%\\node.exe" (\\r 88 | SET "_prog=%dp0%\\node.exe"\\r 89 | ) ELSE (\\r 90 | SET "_prog=node"\\r 91 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 92 | )\\r 93 | \\r 94 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" --expose_gc "%dp0%\\from.env.args" %*\\r 95 | 96 | ` 97 | 98 | exports[`test/basic.js TAP env shebang with args > ps1 1`] = ` 99 | #!/usr/bin/env pwsh 100 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 101 | 102 | $exe="" 103 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 104 | # Fix case when both the Windows and Linux builds of Node 105 | # are installed in the same directory 106 | $exe=".exe" 107 | } 108 | $ret=0 109 | if (Test-Path "$basedir/node$exe") { 110 | # Support pipeline input 111 | if ($MyInvocation.ExpectingInput) { 112 | $input | & "$basedir/node$exe" --expose_gc "$basedir/from.env.args" $args 113 | } else { 114 | & "$basedir/node$exe" --expose_gc "$basedir/from.env.args" $args 115 | } 116 | $ret=$LASTEXITCODE 117 | } else { 118 | # Support pipeline input 119 | if ($MyInvocation.ExpectingInput) { 120 | $input | & "node$exe" --expose_gc "$basedir/from.env.args" $args 121 | } else { 122 | & "node$exe" --expose_gc "$basedir/from.env.args" $args 123 | } 124 | $ret=$LASTEXITCODE 125 | } 126 | exit $ret 127 | 128 | ` 129 | 130 | exports[`test/basic.js TAP env shebang with args > shell 1`] = ` 131 | #!/bin/sh 132 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 133 | 134 | case \`uname\` in 135 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 136 | esac 137 | 138 | if [ -x "$basedir/node" ]; then 139 | exec "$basedir/node" --expose_gc "$basedir/from.env.args" "$@" 140 | else 141 | exec node --expose_gc "$basedir/from.env.args" "$@" 142 | fi 143 | 144 | ` 145 | 146 | exports[`test/basic.js TAP env shebang with variables > cmd 1`] = ` 147 | @ECHO off\\r 148 | GOTO start\\r 149 | :find_dp0\\r 150 | SET dp0=%~dp0\\r 151 | EXIT /b\\r 152 | :start\\r 153 | SETLOCAL\\r 154 | CALL :find_dp0\\r 155 | @SET NODE_PATH=./lib:%NODE_PATH%\\r 156 | \\r 157 | IF EXIST "%dp0%\\node.exe" (\\r 158 | SET "_prog=%dp0%\\node.exe"\\r 159 | ) ELSE (\\r 160 | SET "_prog=node"\\r 161 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 162 | )\\r 163 | \\r 164 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.env.variables" %*\\r 165 | 166 | ` 167 | 168 | exports[`test/basic.js TAP env shebang with variables > ps1 1`] = ` 169 | #!/usr/bin/env pwsh 170 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 171 | 172 | $exe="" 173 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 174 | # Fix case when both the Windows and Linux builds of Node 175 | # are installed in the same directory 176 | $exe=".exe" 177 | } 178 | $ret=0 179 | if (Test-Path "$basedir/node$exe") { 180 | # Support pipeline input 181 | if ($MyInvocation.ExpectingInput) { 182 | $input | & "$basedir/node$exe" "$basedir/from.env.variables" $args 183 | } else { 184 | & "$basedir/node$exe" "$basedir/from.env.variables" $args 185 | } 186 | $ret=$LASTEXITCODE 187 | } else { 188 | # Support pipeline input 189 | if ($MyInvocation.ExpectingInput) { 190 | $input | & "node$exe" "$basedir/from.env.variables" $args 191 | } else { 192 | & "node$exe" "$basedir/from.env.variables" $args 193 | } 194 | $ret=$LASTEXITCODE 195 | } 196 | exit $ret 197 | 198 | ` 199 | 200 | exports[`test/basic.js TAP env shebang with variables > shell 1`] = ` 201 | #!/bin/sh 202 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 203 | 204 | case \`uname\` in 205 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 206 | esac 207 | 208 | if [ -x "$basedir/node" ]; then 209 | exec NODE_PATH=./lib:$NODE_PATH "$basedir/node" "$basedir/from.env.variables" "$@" 210 | else 211 | exec NODE_PATH=./lib:$NODE_PATH node "$basedir/from.env.variables" "$@" 212 | fi 213 | 214 | ` 215 | 216 | exports[`test/basic.js TAP explicit shebang > cmd 1`] = ` 217 | @ECHO off\\r 218 | GOTO start\\r 219 | :find_dp0\\r 220 | SET dp0=%~dp0\\r 221 | EXIT /b\\r 222 | :start\\r 223 | SETLOCAL\\r 224 | CALL :find_dp0\\r 225 | \\r 226 | IF EXIST "%dp0%\\/usr/bin/sh.exe" (\\r 227 | SET "_prog=%dp0%\\/usr/bin/sh.exe"\\r 228 | ) ELSE (\\r 229 | SET "_prog=/usr/bin/sh"\\r 230 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 231 | )\\r 232 | \\r 233 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.sh" %*\\r 234 | 235 | ` 236 | 237 | exports[`test/basic.js TAP explicit shebang > ps1 1`] = ` 238 | #!/usr/bin/env pwsh 239 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 240 | 241 | $exe="" 242 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 243 | # Fix case when both the Windows and Linux builds of Node 244 | # are installed in the same directory 245 | $exe=".exe" 246 | } 247 | $ret=0 248 | if (Test-Path "$basedir//usr/bin/sh$exe") { 249 | # Support pipeline input 250 | if ($MyInvocation.ExpectingInput) { 251 | $input | & "$basedir//usr/bin/sh$exe" "$basedir/from.sh" $args 252 | } else { 253 | & "$basedir//usr/bin/sh$exe" "$basedir/from.sh" $args 254 | } 255 | $ret=$LASTEXITCODE 256 | } else { 257 | # Support pipeline input 258 | if ($MyInvocation.ExpectingInput) { 259 | $input | & "/usr/bin/sh$exe" "$basedir/from.sh" $args 260 | } else { 261 | & "/usr/bin/sh$exe" "$basedir/from.sh" $args 262 | } 263 | $ret=$LASTEXITCODE 264 | } 265 | exit $ret 266 | 267 | ` 268 | 269 | exports[`test/basic.js TAP explicit shebang > shell 1`] = ` 270 | #!/bin/sh 271 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 272 | 273 | case \`uname\` in 274 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 275 | esac 276 | 277 | if [ -x "$basedir//usr/bin/sh" ]; then 278 | exec "$basedir//usr/bin/sh" "$basedir/from.sh" "$@" 279 | else 280 | exec /usr/bin/sh "$basedir/from.sh" "$@" 281 | fi 282 | 283 | ` 284 | 285 | exports[`test/basic.js TAP explicit shebang with args > cmd 1`] = ` 286 | @ECHO off\\r 287 | GOTO start\\r 288 | :find_dp0\\r 289 | SET dp0=%~dp0\\r 290 | EXIT /b\\r 291 | :start\\r 292 | SETLOCAL\\r 293 | CALL :find_dp0\\r 294 | \\r 295 | IF EXIST "%dp0%\\/usr/bin/sh.exe" (\\r 296 | SET "_prog=%dp0%\\/usr/bin/sh.exe"\\r 297 | ) ELSE (\\r 298 | SET "_prog=/usr/bin/sh"\\r 299 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 300 | )\\r 301 | \\r 302 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" -x "%dp0%\\from.sh.args" %*\\r 303 | 304 | ` 305 | 306 | exports[`test/basic.js TAP explicit shebang with args > ps1 1`] = ` 307 | #!/usr/bin/env pwsh 308 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 309 | 310 | $exe="" 311 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 312 | # Fix case when both the Windows and Linux builds of Node 313 | # are installed in the same directory 314 | $exe=".exe" 315 | } 316 | $ret=0 317 | if (Test-Path "$basedir//usr/bin/sh$exe") { 318 | # Support pipeline input 319 | if ($MyInvocation.ExpectingInput) { 320 | $input | & "$basedir//usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 321 | } else { 322 | & "$basedir//usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 323 | } 324 | $ret=$LASTEXITCODE 325 | } else { 326 | # Support pipeline input 327 | if ($MyInvocation.ExpectingInput) { 328 | $input | & "/usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 329 | } else { 330 | & "/usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 331 | } 332 | $ret=$LASTEXITCODE 333 | } 334 | exit $ret 335 | 336 | ` 337 | 338 | exports[`test/basic.js TAP explicit shebang with args > shell 1`] = ` 339 | #!/bin/sh 340 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 341 | 342 | case \`uname\` in 343 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 344 | esac 345 | 346 | if [ -x "$basedir//usr/bin/sh" ]; then 347 | exec "$basedir//usr/bin/sh" -x "$basedir/from.sh.args" "$@" 348 | else 349 | exec /usr/bin/sh -x "$basedir/from.sh.args" "$@" 350 | fi 351 | 352 | ` 353 | 354 | exports[`test/basic.js TAP if exists (it does exist) > cmd 1`] = ` 355 | @ECHO off\\r 356 | GOTO start\\r 357 | :find_dp0\\r 358 | SET dp0=%~dp0\\r 359 | EXIT /b\\r 360 | :start\\r 361 | SETLOCAL\\r 362 | CALL :find_dp0\\r 363 | "%dp0%\\from.exe" %*\\r 364 | 365 | ` 366 | 367 | exports[`test/basic.js TAP if exists (it does exist) > ps1 1`] = ` 368 | #!/usr/bin/env pwsh 369 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 370 | 371 | $exe="" 372 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 373 | # Fix case when both the Windows and Linux builds of Node 374 | # are installed in the same directory 375 | $exe=".exe" 376 | } 377 | # Support pipeline input 378 | if ($MyInvocation.ExpectingInput) { 379 | $input | & "$basedir/from.exe" $args 380 | } else { 381 | & "$basedir/from.exe" $args 382 | } 383 | exit $LASTEXITCODE 384 | 385 | ` 386 | 387 | exports[`test/basic.js TAP if exists (it does exist) > shell 1`] = ` 388 | #!/bin/sh 389 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 390 | 391 | case \`uname\` in 392 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 393 | esac 394 | 395 | exec "$basedir/from.exe" "$@" 396 | 397 | ` 398 | 399 | exports[`test/basic.js TAP just proceed if reading fails > cmd 1`] = ` 400 | @ECHO off\\r 401 | GOTO start\\r 402 | :find_dp0\\r 403 | SET dp0=%~dp0\\r 404 | EXIT /b\\r 405 | :start\\r 406 | SETLOCAL\\r 407 | CALL :find_dp0\\r 408 | "%dp0%\\" %*\\r 409 | 410 | ` 411 | 412 | exports[`test/basic.js TAP just proceed if reading fails > ps1 1`] = ` 413 | #!/usr/bin/env pwsh 414 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 415 | 416 | $exe="" 417 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 418 | # Fix case when both the Windows and Linux builds of Node 419 | # are installed in the same directory 420 | $exe=".exe" 421 | } 422 | # Support pipeline input 423 | if ($MyInvocation.ExpectingInput) { 424 | $input | & "$basedir/" $args 425 | } else { 426 | & "$basedir/" $args 427 | } 428 | exit $LASTEXITCODE 429 | 430 | ` 431 | 432 | exports[`test/basic.js TAP just proceed if reading fails > shell 1`] = ` 433 | #!/bin/sh 434 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 435 | 436 | case \`uname\` in 437 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 438 | esac 439 | 440 | exec "$basedir/" "$@" 441 | 442 | ` 443 | 444 | exports[`test/basic.js TAP no shebang > cmd 1`] = ` 445 | @ECHO off\\r 446 | GOTO start\\r 447 | :find_dp0\\r 448 | SET dp0=%~dp0\\r 449 | EXIT /b\\r 450 | :start\\r 451 | SETLOCAL\\r 452 | CALL :find_dp0\\r 453 | "%dp0%\\from.exe" %*\\r 454 | 455 | ` 456 | 457 | exports[`test/basic.js TAP no shebang > ps1 1`] = ` 458 | #!/usr/bin/env pwsh 459 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 460 | 461 | $exe="" 462 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 463 | # Fix case when both the Windows and Linux builds of Node 464 | # are installed in the same directory 465 | $exe=".exe" 466 | } 467 | # Support pipeline input 468 | if ($MyInvocation.ExpectingInput) { 469 | $input | & "$basedir/from.exe" $args 470 | } else { 471 | & "$basedir/from.exe" $args 472 | } 473 | exit $LASTEXITCODE 474 | 475 | ` 476 | 477 | exports[`test/basic.js TAP no shebang > shell 1`] = ` 478 | #!/bin/sh 479 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 480 | 481 | case \`uname\` in 482 | *CYGWIN*|*MINGW*|*MSYS*) basedir=\`cygpath -w "$basedir"\`;; 483 | esac 484 | 485 | exec "$basedir/from.exe" "$@" 486 | 487 | ` 488 | -------------------------------------------------------------------------------- /tap-snapshots/test/basic.js.test.cjs: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/basic.js TAP env shebang > cmd 1`] = ` 9 | @ECHO off\\r 10 | GOTO start\\r 11 | :find_dp0\\r 12 | SET dp0=%~dp0\\r 13 | EXIT /b\\r 14 | :start\\r 15 | SETLOCAL\\r 16 | CALL :find_dp0\\r 17 | \\r 18 | IF EXIST "%dp0%\\node.exe" (\\r 19 | SET "_prog=%dp0%\\node.exe"\\r 20 | ) ELSE (\\r 21 | SET "_prog=node"\\r 22 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 23 | )\\r 24 | \\r 25 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.env" %*\\r 26 | 27 | ` 28 | 29 | exports[`test/basic.js TAP env shebang > ps1 1`] = ` 30 | #!/usr/bin/env pwsh 31 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 32 | 33 | $exe="" 34 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 35 | # Fix case when both the Windows and Linux builds of Node 36 | # are installed in the same directory 37 | $exe=".exe" 38 | } 39 | $ret=0 40 | if (Test-Path "$basedir/node$exe") { 41 | # Support pipeline input 42 | if ($MyInvocation.ExpectingInput) { 43 | $input | & "$basedir/node$exe" "$basedir/from.env" $args 44 | } else { 45 | & "$basedir/node$exe" "$basedir/from.env" $args 46 | } 47 | $ret=$LASTEXITCODE 48 | } else { 49 | # Support pipeline input 50 | if ($MyInvocation.ExpectingInput) { 51 | $input | & "node$exe" "$basedir/from.env" $args 52 | } else { 53 | & "node$exe" "$basedir/from.env" $args 54 | } 55 | $ret=$LASTEXITCODE 56 | } 57 | exit $ret 58 | 59 | ` 60 | 61 | exports[`test/basic.js TAP env shebang > shell 1`] = ` 62 | #!/bin/sh 63 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 64 | 65 | case \`uname\` in 66 | *CYGWIN*|*MINGW*|*MSYS*) 67 | if command -v cygpath > /dev/null 2>&1; then 68 | basedir=\`cygpath -w "$basedir"\` 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -x "$basedir/node" ]; then 74 | exec "$basedir/node" "$basedir/from.env" "$@" 75 | else 76 | exec node "$basedir/from.env" "$@" 77 | fi 78 | 79 | ` 80 | 81 | exports[`test/basic.js TAP env shebang with args > cmd 1`] = ` 82 | @ECHO off\\r 83 | GOTO start\\r 84 | :find_dp0\\r 85 | SET dp0=%~dp0\\r 86 | EXIT /b\\r 87 | :start\\r 88 | SETLOCAL\\r 89 | CALL :find_dp0\\r 90 | \\r 91 | IF EXIST "%dp0%\\node.exe" (\\r 92 | SET "_prog=%dp0%\\node.exe"\\r 93 | ) ELSE (\\r 94 | SET "_prog=node"\\r 95 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 96 | )\\r 97 | \\r 98 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" --expose_gc "%dp0%\\from.env.args" %*\\r 99 | 100 | ` 101 | 102 | exports[`test/basic.js TAP env shebang with args > ps1 1`] = ` 103 | #!/usr/bin/env pwsh 104 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 105 | 106 | $exe="" 107 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 108 | # Fix case when both the Windows and Linux builds of Node 109 | # are installed in the same directory 110 | $exe=".exe" 111 | } 112 | $ret=0 113 | if (Test-Path "$basedir/node$exe") { 114 | # Support pipeline input 115 | if ($MyInvocation.ExpectingInput) { 116 | $input | & "$basedir/node$exe" --expose_gc "$basedir/from.env.args" $args 117 | } else { 118 | & "$basedir/node$exe" --expose_gc "$basedir/from.env.args" $args 119 | } 120 | $ret=$LASTEXITCODE 121 | } else { 122 | # Support pipeline input 123 | if ($MyInvocation.ExpectingInput) { 124 | $input | & "node$exe" --expose_gc "$basedir/from.env.args" $args 125 | } else { 126 | & "node$exe" --expose_gc "$basedir/from.env.args" $args 127 | } 128 | $ret=$LASTEXITCODE 129 | } 130 | exit $ret 131 | 132 | ` 133 | 134 | exports[`test/basic.js TAP env shebang with args > shell 1`] = ` 135 | #!/bin/sh 136 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 137 | 138 | case \`uname\` in 139 | *CYGWIN*|*MINGW*|*MSYS*) 140 | if command -v cygpath > /dev/null 2>&1; then 141 | basedir=\`cygpath -w "$basedir"\` 142 | fi 143 | ;; 144 | esac 145 | 146 | if [ -x "$basedir/node" ]; then 147 | exec "$basedir/node" --expose_gc "$basedir/from.env.args" "$@" 148 | else 149 | exec node --expose_gc "$basedir/from.env.args" "$@" 150 | fi 151 | 152 | ` 153 | 154 | exports[`test/basic.js TAP env shebang with variables > cmd 1`] = ` 155 | @ECHO off\\r 156 | GOTO start\\r 157 | :find_dp0\\r 158 | SET dp0=%~dp0\\r 159 | EXIT /b\\r 160 | :start\\r 161 | SETLOCAL\\r 162 | CALL :find_dp0\\r 163 | @SET NODE_PATH=./lib:%NODE_PATH%\\r 164 | \\r 165 | IF EXIST "%dp0%\\node.exe" (\\r 166 | SET "_prog=%dp0%\\node.exe"\\r 167 | ) ELSE (\\r 168 | SET "_prog=node"\\r 169 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 170 | )\\r 171 | \\r 172 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.env.variables" %*\\r 173 | 174 | ` 175 | 176 | exports[`test/basic.js TAP env shebang with variables > ps1 1`] = ` 177 | #!/usr/bin/env pwsh 178 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 179 | 180 | $exe="" 181 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 182 | # Fix case when both the Windows and Linux builds of Node 183 | # are installed in the same directory 184 | $exe=".exe" 185 | } 186 | $ret=0 187 | if (Test-Path "$basedir/node$exe") { 188 | # Support pipeline input 189 | if ($MyInvocation.ExpectingInput) { 190 | $input | & "$basedir/node$exe" "$basedir/from.env.variables" $args 191 | } else { 192 | & "$basedir/node$exe" "$basedir/from.env.variables" $args 193 | } 194 | $ret=$LASTEXITCODE 195 | } else { 196 | # Support pipeline input 197 | if ($MyInvocation.ExpectingInput) { 198 | $input | & "node$exe" "$basedir/from.env.variables" $args 199 | } else { 200 | & "node$exe" "$basedir/from.env.variables" $args 201 | } 202 | $ret=$LASTEXITCODE 203 | } 204 | exit $ret 205 | 206 | ` 207 | 208 | exports[`test/basic.js TAP env shebang with variables > shell 1`] = ` 209 | #!/bin/sh 210 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 211 | 212 | case \`uname\` in 213 | *CYGWIN*|*MINGW*|*MSYS*) 214 | if command -v cygpath > /dev/null 2>&1; then 215 | basedir=\`cygpath -w "$basedir"\` 216 | fi 217 | ;; 218 | esac 219 | 220 | if [ -x "$basedir/node" ]; then 221 | exec NODE_PATH=./lib:$NODE_PATH "$basedir/node" "$basedir/from.env.variables" "$@" 222 | else 223 | exec NODE_PATH=./lib:$NODE_PATH node "$basedir/from.env.variables" "$@" 224 | fi 225 | 226 | ` 227 | 228 | exports[`test/basic.js TAP explicit shebang > cmd 1`] = ` 229 | @ECHO off\\r 230 | GOTO start\\r 231 | :find_dp0\\r 232 | SET dp0=%~dp0\\r 233 | EXIT /b\\r 234 | :start\\r 235 | SETLOCAL\\r 236 | CALL :find_dp0\\r 237 | \\r 238 | IF EXIST "%dp0%\\/usr/bin/sh.exe" (\\r 239 | SET "_prog=%dp0%\\/usr/bin/sh.exe"\\r 240 | ) ELSE (\\r 241 | SET "_prog=/usr/bin/sh"\\r 242 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 243 | )\\r 244 | \\r 245 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\from.sh" %*\\r 246 | 247 | ` 248 | 249 | exports[`test/basic.js TAP explicit shebang > ps1 1`] = ` 250 | #!/usr/bin/env pwsh 251 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 252 | 253 | $exe="" 254 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 255 | # Fix case when both the Windows and Linux builds of Node 256 | # are installed in the same directory 257 | $exe=".exe" 258 | } 259 | $ret=0 260 | if (Test-Path "$basedir//usr/bin/sh$exe") { 261 | # Support pipeline input 262 | if ($MyInvocation.ExpectingInput) { 263 | $input | & "$basedir//usr/bin/sh$exe" "$basedir/from.sh" $args 264 | } else { 265 | & "$basedir//usr/bin/sh$exe" "$basedir/from.sh" $args 266 | } 267 | $ret=$LASTEXITCODE 268 | } else { 269 | # Support pipeline input 270 | if ($MyInvocation.ExpectingInput) { 271 | $input | & "/usr/bin/sh$exe" "$basedir/from.sh" $args 272 | } else { 273 | & "/usr/bin/sh$exe" "$basedir/from.sh" $args 274 | } 275 | $ret=$LASTEXITCODE 276 | } 277 | exit $ret 278 | 279 | ` 280 | 281 | exports[`test/basic.js TAP explicit shebang > shell 1`] = ` 282 | #!/bin/sh 283 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 284 | 285 | case \`uname\` in 286 | *CYGWIN*|*MINGW*|*MSYS*) 287 | if command -v cygpath > /dev/null 2>&1; then 288 | basedir=\`cygpath -w "$basedir"\` 289 | fi 290 | ;; 291 | esac 292 | 293 | if [ -x "$basedir//usr/bin/sh" ]; then 294 | exec "$basedir//usr/bin/sh" "$basedir/from.sh" "$@" 295 | else 296 | exec /usr/bin/sh "$basedir/from.sh" "$@" 297 | fi 298 | 299 | ` 300 | 301 | exports[`test/basic.js TAP explicit shebang with args > cmd 1`] = ` 302 | @ECHO off\\r 303 | GOTO start\\r 304 | :find_dp0\\r 305 | SET dp0=%~dp0\\r 306 | EXIT /b\\r 307 | :start\\r 308 | SETLOCAL\\r 309 | CALL :find_dp0\\r 310 | \\r 311 | IF EXIST "%dp0%\\/usr/bin/sh.exe" (\\r 312 | SET "_prog=%dp0%\\/usr/bin/sh.exe"\\r 313 | ) ELSE (\\r 314 | SET "_prog=/usr/bin/sh"\\r 315 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 316 | )\\r 317 | \\r 318 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" -x "%dp0%\\from.sh.args" %*\\r 319 | 320 | ` 321 | 322 | exports[`test/basic.js TAP explicit shebang with args > ps1 1`] = ` 323 | #!/usr/bin/env pwsh 324 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 325 | 326 | $exe="" 327 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 328 | # Fix case when both the Windows and Linux builds of Node 329 | # are installed in the same directory 330 | $exe=".exe" 331 | } 332 | $ret=0 333 | if (Test-Path "$basedir//usr/bin/sh$exe") { 334 | # Support pipeline input 335 | if ($MyInvocation.ExpectingInput) { 336 | $input | & "$basedir//usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 337 | } else { 338 | & "$basedir//usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 339 | } 340 | $ret=$LASTEXITCODE 341 | } else { 342 | # Support pipeline input 343 | if ($MyInvocation.ExpectingInput) { 344 | $input | & "/usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 345 | } else { 346 | & "/usr/bin/sh$exe" -x "$basedir/from.sh.args" $args 347 | } 348 | $ret=$LASTEXITCODE 349 | } 350 | exit $ret 351 | 352 | ` 353 | 354 | exports[`test/basic.js TAP explicit shebang with args > shell 1`] = ` 355 | #!/bin/sh 356 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 357 | 358 | case \`uname\` in 359 | *CYGWIN*|*MINGW*|*MSYS*) 360 | if command -v cygpath > /dev/null 2>&1; then 361 | basedir=\`cygpath -w "$basedir"\` 362 | fi 363 | ;; 364 | esac 365 | 366 | if [ -x "$basedir//usr/bin/sh" ]; then 367 | exec "$basedir//usr/bin/sh" -x "$basedir/from.sh.args" "$@" 368 | else 369 | exec /usr/bin/sh -x "$basedir/from.sh.args" "$@" 370 | fi 371 | 372 | ` 373 | 374 | exports[`test/basic.js TAP if exists (it does exist) > cmd 1`] = ` 375 | @ECHO off\\r 376 | GOTO start\\r 377 | :find_dp0\\r 378 | SET dp0=%~dp0\\r 379 | EXIT /b\\r 380 | :start\\r 381 | SETLOCAL\\r 382 | CALL :find_dp0\\r 383 | "%dp0%\\from.exe" %*\\r 384 | 385 | ` 386 | 387 | exports[`test/basic.js TAP if exists (it does exist) > ps1 1`] = ` 388 | #!/usr/bin/env pwsh 389 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 390 | 391 | $exe="" 392 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 393 | # Fix case when both the Windows and Linux builds of Node 394 | # are installed in the same directory 395 | $exe=".exe" 396 | } 397 | # Support pipeline input 398 | if ($MyInvocation.ExpectingInput) { 399 | $input | & "$basedir/from.exe" $args 400 | } else { 401 | & "$basedir/from.exe" $args 402 | } 403 | exit $LASTEXITCODE 404 | 405 | ` 406 | 407 | exports[`test/basic.js TAP if exists (it does exist) > shell 1`] = ` 408 | #!/bin/sh 409 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 410 | 411 | case \`uname\` in 412 | *CYGWIN*|*MINGW*|*MSYS*) 413 | if command -v cygpath > /dev/null 2>&1; then 414 | basedir=\`cygpath -w "$basedir"\` 415 | fi 416 | ;; 417 | esac 418 | 419 | exec "$basedir/from.exe" "$@" 420 | 421 | ` 422 | 423 | exports[`test/basic.js TAP just proceed if reading fails > cmd 1`] = ` 424 | @ECHO off\\r 425 | GOTO start\\r 426 | :find_dp0\\r 427 | SET dp0=%~dp0\\r 428 | EXIT /b\\r 429 | :start\\r 430 | SETLOCAL\\r 431 | CALL :find_dp0\\r 432 | "%dp0%\\" %*\\r 433 | 434 | ` 435 | 436 | exports[`test/basic.js TAP just proceed if reading fails > ps1 1`] = ` 437 | #!/usr/bin/env pwsh 438 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 439 | 440 | $exe="" 441 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 442 | # Fix case when both the Windows and Linux builds of Node 443 | # are installed in the same directory 444 | $exe=".exe" 445 | } 446 | # Support pipeline input 447 | if ($MyInvocation.ExpectingInput) { 448 | $input | & "$basedir/" $args 449 | } else { 450 | & "$basedir/" $args 451 | } 452 | exit $LASTEXITCODE 453 | 454 | ` 455 | 456 | exports[`test/basic.js TAP just proceed if reading fails > shell 1`] = ` 457 | #!/bin/sh 458 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 459 | 460 | case \`uname\` in 461 | *CYGWIN*|*MINGW*|*MSYS*) 462 | if command -v cygpath > /dev/null 2>&1; then 463 | basedir=\`cygpath -w "$basedir"\` 464 | fi 465 | ;; 466 | esac 467 | 468 | exec "$basedir/" "$@" 469 | 470 | ` 471 | 472 | exports[`test/basic.js TAP multiple variables > cmd 1`] = ` 473 | @ECHO off\\r 474 | GOTO start\\r 475 | :find_dp0\\r 476 | SET dp0=%~dp0\\r 477 | EXIT /b\\r 478 | :start\\r 479 | SETLOCAL\\r 480 | CALL :find_dp0\\r 481 | @SET key=value\\r 482 | @SET key2=value2\\r 483 | \\r 484 | IF EXIST "%dp0%\\node.exe" (\\r 485 | SET "_prog=%dp0%\\node.exe"\\r 486 | ) ELSE (\\r 487 | SET "_prog=node"\\r 488 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 489 | )\\r 490 | \\r 491 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" --flag-one --flag-two "%dp0%\\from.env.multiple.variables" %*\\r 492 | 493 | ` 494 | 495 | exports[`test/basic.js TAP multiple variables > ps1 1`] = ` 496 | #!/usr/bin/env pwsh 497 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 498 | 499 | $exe="" 500 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 501 | # Fix case when both the Windows and Linux builds of Node 502 | # are installed in the same directory 503 | $exe=".exe" 504 | } 505 | $ret=0 506 | if (Test-Path "$basedir/node$exe") { 507 | # Support pipeline input 508 | if ($MyInvocation.ExpectingInput) { 509 | $input | & "$basedir/node$exe" --flag-one --flag-two "$basedir/from.env.multiple.variables" $args 510 | } else { 511 | & "$basedir/node$exe" --flag-one --flag-two "$basedir/from.env.multiple.variables" $args 512 | } 513 | $ret=$LASTEXITCODE 514 | } else { 515 | # Support pipeline input 516 | if ($MyInvocation.ExpectingInput) { 517 | $input | & "node$exe" --flag-one --flag-two "$basedir/from.env.multiple.variables" $args 518 | } else { 519 | & "node$exe" --flag-one --flag-two "$basedir/from.env.multiple.variables" $args 520 | } 521 | $ret=$LASTEXITCODE 522 | } 523 | exit $ret 524 | 525 | ` 526 | 527 | exports[`test/basic.js TAP multiple variables > shell 1`] = ` 528 | #!/bin/sh 529 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 530 | 531 | case \`uname\` in 532 | *CYGWIN*|*MINGW*|*MSYS*) 533 | if command -v cygpath > /dev/null 2>&1; then 534 | basedir=\`cygpath -w "$basedir"\` 535 | fi 536 | ;; 537 | esac 538 | 539 | if [ -x "$basedir/node" ]; then 540 | exec key=value key2=value2 "$basedir/node" --flag-one --flag-two "$basedir/from.env.multiple.variables" "$@" 541 | else 542 | exec key=value key2=value2 node --flag-one --flag-two "$basedir/from.env.multiple.variables" "$@" 543 | fi 544 | 545 | ` 546 | 547 | exports[`test/basic.js TAP no shebang > cmd 1`] = ` 548 | @ECHO off\\r 549 | GOTO start\\r 550 | :find_dp0\\r 551 | SET dp0=%~dp0\\r 552 | EXIT /b\\r 553 | :start\\r 554 | SETLOCAL\\r 555 | CALL :find_dp0\\r 556 | "%dp0%\\from.exe" %*\\r 557 | 558 | ` 559 | 560 | exports[`test/basic.js TAP no shebang > ps1 1`] = ` 561 | #!/usr/bin/env pwsh 562 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 563 | 564 | $exe="" 565 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 566 | # Fix case when both the Windows and Linux builds of Node 567 | # are installed in the same directory 568 | $exe=".exe" 569 | } 570 | # Support pipeline input 571 | if ($MyInvocation.ExpectingInput) { 572 | $input | & "$basedir/from.exe" $args 573 | } else { 574 | & "$basedir/from.exe" $args 575 | } 576 | exit $LASTEXITCODE 577 | 578 | ` 579 | 580 | exports[`test/basic.js TAP no shebang > shell 1`] = ` 581 | #!/bin/sh 582 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 583 | 584 | case \`uname\` in 585 | *CYGWIN*|*MINGW*|*MSYS*) 586 | if command -v cygpath > /dev/null 2>&1; then 587 | basedir=\`cygpath -w "$basedir"\` 588 | fi 589 | ;; 590 | esac 591 | 592 | exec "$basedir/from.exe" "$@" 593 | 594 | ` 595 | 596 | exports[`test/basic.js TAP shebang with env -S > cmd 1`] = ` 597 | @ECHO off\\r 598 | GOTO start\\r 599 | :find_dp0\\r 600 | SET dp0=%~dp0\\r 601 | EXIT /b\\r 602 | :start\\r 603 | SETLOCAL\\r 604 | CALL :find_dp0\\r 605 | \\r 606 | IF EXIST "%dp0%\\node.exe" (\\r 607 | SET "_prog=%dp0%\\node.exe"\\r 608 | ) ELSE (\\r 609 | SET "_prog=node"\\r 610 | SET PATHEXT=%PATHEXT:;.JS;=;%\\r 611 | )\\r 612 | \\r 613 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" --expose_gc "%dp0%\\from.env.S" %*\\r 614 | 615 | ` 616 | 617 | exports[`test/basic.js TAP shebang with env -S > cmd 2`] = ` 618 | #!/usr/bin/env pwsh 619 | $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 620 | 621 | $exe="" 622 | if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 623 | # Fix case when both the Windows and Linux builds of Node 624 | # are installed in the same directory 625 | $exe=".exe" 626 | } 627 | $ret=0 628 | if (Test-Path "$basedir/node$exe") { 629 | # Support pipeline input 630 | if ($MyInvocation.ExpectingInput) { 631 | $input | & "$basedir/node$exe" --expose_gc "$basedir/from.env.S" $args 632 | } else { 633 | & "$basedir/node$exe" --expose_gc "$basedir/from.env.S" $args 634 | } 635 | $ret=$LASTEXITCODE 636 | } else { 637 | # Support pipeline input 638 | if ($MyInvocation.ExpectingInput) { 639 | $input | & "node$exe" --expose_gc "$basedir/from.env.S" $args 640 | } else { 641 | & "node$exe" --expose_gc "$basedir/from.env.S" $args 642 | } 643 | $ret=$LASTEXITCODE 644 | } 645 | exit $ret 646 | 647 | ` 648 | 649 | exports[`test/basic.js TAP shebang with env -S > shell 1`] = ` 650 | #!/bin/sh 651 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") 652 | 653 | case \`uname\` in 654 | *CYGWIN*|*MINGW*|*MSYS*) 655 | if command -v cygpath > /dev/null 2>&1; then 656 | basedir=\`cygpath -w "$basedir"\` 657 | fi 658 | ;; 659 | esac 660 | 661 | if [ -x "$basedir/node" ]; then 662 | exec "$basedir/node" --expose_gc "$basedir/from.env.S" "$@" 663 | else 664 | exec node --expose_gc "$basedir/from.env.S" "$@" 665 | fi 666 | 667 | ` 668 | -------------------------------------------------------------------------------- /test/00-setup.js: -------------------------------------------------------------------------------- 1 | const { 2 | mkdirSync, 3 | writeFileSync, 4 | } = require('fs') 5 | 6 | const fixtures = require('path').join(__dirname, '/fixtures') 7 | 8 | const froms = { 9 | 'from.exe': 'exe', 10 | 'from.env': '#!/usr/bin/env node\nconsole.log(/hi/)\n', 11 | 'from.env.args': '#!/usr/bin/env node --expose_gc\ngc()\n', 12 | 'from.env.variables': '#!/usr/bin/env NODE_PATH=./lib:$NODE_PATH node', 13 | 'from.sh': '#!/usr/bin/sh\necho hi\n', 14 | 'from.sh.args': '#!/usr/bin/sh -x\necho hi\n', 15 | 'from.env.multiple.variables': '#!/usr/bin/env key=value key2=value2 node --flag-one --flag-two', 16 | 'from.env.S': '#!/usr/bin/env -S node --expose_gc\ngc()\n', 17 | 'from.env.nospace': '#!/usr/bin/envnode\nconsole.log(/hi/)\n', 18 | } 19 | 20 | mkdirSync(fixtures, { recursive: true }) 21 | for (const [f, content] of Object.entries(froms)) { 22 | writeFileSync(`${fixtures}/${f}`, content) 23 | } 24 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tap').test 4 | var fs = require('fs') 5 | var path = require('path') 6 | var fixtures = path.resolve(__dirname, 'fixtures') 7 | 8 | const matchSnapshot = (t, found, name) => 9 | t.matchSnapshot(found.replace(/\r/g, '\\r'), name) 10 | 11 | var cmdShim = require('..') 12 | 13 | test('no shebang', async t => { 14 | var from = path.resolve(fixtures, 'from.exe') 15 | var to = path.resolve(fixtures, 'exe.shim') 16 | await cmdShim(from, to) 17 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 18 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 19 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 20 | }) 21 | 22 | test('if exists (it does exist)', async t => { 23 | var from = path.resolve(fixtures, 'from.exe') 24 | var to = path.resolve(fixtures, 'exe.shim') 25 | await cmdShim.ifExists(from, to) 26 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 27 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 28 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 29 | }) 30 | 31 | test('if exists (it does not exist)', async t => { 32 | var from = path.resolve(fixtures, 'argle bargle we like to sparkle') 33 | var to = path.resolve(fixtures, 'argle-bargle-shim') 34 | await cmdShim.ifExists(from, to) 35 | t.throws(() => fs.statSync(to)) 36 | t.throws(() => fs.statSync(to + '.cmd')) 37 | t.throws(() => fs.statSync(to + '.ps1')) 38 | }) 39 | 40 | test('fails if from doesnt exist', async t => { 41 | var from = path.resolve(fixtures, 'argle bargle we like to sparkle') 42 | var to = path.resolve(fixtures, 'argle-bargle-shim') 43 | await t.rejects(cmdShim(from, to), { code: 'ENOENT' }) 44 | }) 45 | 46 | test('fails if mkdir fails', async t => { 47 | var from = path.resolve(fixtures, 'from.env') 48 | var to = path.resolve(fixtures, 'from.env/a/b/c') 49 | await t.rejects(cmdShim(from, to), { code: /^(ENOTDIR|EEXIST|ENOENT)$/ }) 50 | }) 51 | 52 | test('fails if to is a dir', async t => { 53 | var from = path.resolve(fixtures, 'from.env') 54 | var to = path.resolve(fixtures) 55 | t.teardown(() => { 56 | fs.rmSync(to + '.cmd', { recursive: true, force: true }) 57 | }) 58 | await t.rejects(cmdShim(from, to), { code: 'EISDIR' }) 59 | }) 60 | 61 | test('just proceed if reading fails', async t => { 62 | var from = fixtures 63 | var to = path.resolve(fixtures, 'env.shim') 64 | await cmdShim(from, to) 65 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 66 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 67 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 68 | }) 69 | 70 | test('env shebang', async t => { 71 | var from = path.resolve(fixtures, 'from.env') 72 | var to = path.resolve(fixtures, 'env.shim') 73 | await cmdShim(from, to) 74 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 75 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 76 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 77 | }) 78 | 79 | test('env shebang with args', async t => { 80 | var from = path.resolve(fixtures, 'from.env.args') 81 | var to = path.resolve(fixtures, 'env.args.shim') 82 | await cmdShim(from, to) 83 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 84 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 85 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 86 | }) 87 | 88 | test('env shebang with variables', async t => { 89 | var from = path.resolve(fixtures, 'from.env.variables') 90 | var to = path.resolve(fixtures, 'env.variables.shim') 91 | await cmdShim(from, to) 92 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 93 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 94 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 95 | }) 96 | 97 | test('explicit shebang', async t => { 98 | var from = path.resolve(fixtures, 'from.sh') 99 | var to = path.resolve(fixtures, 'sh.shim') 100 | await cmdShim(from, to) 101 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 102 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 103 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 104 | }) 105 | 106 | test('explicit shebang with args', async t => { 107 | var from = path.resolve(fixtures, 'from.sh.args') 108 | var to = path.resolve(fixtures, 'sh.args.shim') 109 | await cmdShim(from, to) 110 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 111 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 112 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 113 | }) 114 | 115 | test('multiple variables', async t => { 116 | var from = path.resolve(fixtures, 'from.env.multiple.variables') 117 | var to = path.resolve(fixtures, 'sh.multiple.shim') 118 | await cmdShim(from, to) 119 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 120 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 121 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') 122 | }) 123 | 124 | test('shebang with env -S', async t => { 125 | var from = path.resolve(fixtures, 'from.env.S') 126 | var to = path.resolve(fixtures, 'sh.env.S.shim') 127 | await cmdShim(from, to) 128 | matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') 129 | matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') 130 | matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'cmd') 131 | }) 132 | -------------------------------------------------------------------------------- /test/to-batch-syntax-tests.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test 2 | var toBatchSyntax = require('../lib/to-batch-syntax') 3 | 4 | test('replace $ expressions with % pair', function (t) { 5 | var assertReplacement = function (string, expected) { 6 | t.equal(toBatchSyntax.replaceDollarWithPercentPair(string), expected) 7 | } 8 | assertReplacement('$A', '%A%') 9 | assertReplacement('$A:$B', '%A%:%B%') 10 | assertReplacement('$A bla', '%A% bla') 11 | assertReplacement('${A}bla', '%A%bla') 12 | assertReplacement('$A $bla bla', '%A% %bla% bla') 13 | assertReplacement('${A}bla ${bla}bla', '%A%bla %bla%bla') 14 | assertReplacement('./lib:$NODE_PATH', './lib:%NODE_PATH%') 15 | t.end() 16 | }) 17 | 18 | test('convert variable declaration to set command', function (t) { 19 | t.equal(toBatchSyntax.convertToSetCommand('A', '.lib:$A '), '@SET A=.lib:%A%\r\n') 20 | t.equal(toBatchSyntax.convertToSetCommand('', ''), '') 21 | t.equal(toBatchSyntax.convertToSetCommand(' ', ''), '') 22 | t.equal(toBatchSyntax.convertToSetCommand(' ', ' '), '') 23 | t.equal(toBatchSyntax.convertToSetCommand(' ou', ' ou '), '@SET ou=ou\r\n') 24 | t.end() 25 | }) 26 | -------------------------------------------------------------------------------- /test/zz-cleanup.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path') 2 | const { rmSync } = require('fs') 3 | 4 | rmSync(join(__dirname, 'fixtures'), { recursive: true, force: true }) 5 | rmSync(join(__dirname, 'fixtures.ps1')) 6 | --------------------------------------------------------------------------------