diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 0000000..5b0b1a5 --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1,10 @@ +/* This file is automatically added by @npmcli/template-oss. Do not edit. */ + +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'deps', 'chore']], + 'header-max-length': [2, 'always', 80], + 'subject-case': [0, 'always', ['lower-case', 'sentence-case', 'start-case']], + }, +} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..5db9f81 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,17 @@ +/* This file is automatically added by @npmcli/template-oss. Do not edit. */ + +'use strict' + +const { readdirSync: readdir } = require('fs') + +const localConfigs = readdir(__dirname) + .filter((file) => file.startsWith('.eslintrc.local.')) + .map((file) => `./${file}`) + +module.exports = { + root: true, + extends: [ + '@npmcli', + ...localConfigs, + ], +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2c54b0d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +* @npm/cli-team diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..d043192 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,54 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: Bug +description: File a bug/issue +title: "[BUG] " +labels: [ Bug, Needs Triage ] + +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please [search here](./issues) to see if an issue already exists for your problem. + options: + - label: I have searched the existing issues + required: true + - type: textarea + attributes: + label: Current Behavior + description: A clear & concise description of what you're experiencing. + validations: + required: false + - type: textarea + attributes: + label: Expected Behavior + description: A clear & concise description of what you expected to happen. + validations: + required: false + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + value: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: false + - type: textarea + attributes: + label: Environment + description: | + examples: + - **npm**: 7.6.3 + - **Node**: 13.14.0 + - **OS**: Ubuntu 20.04 + - **platform**: Macbook Pro + value: | + - npm: + - Node: + - OS: + - platform: + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d640909 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,3 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +blank_issues_enabled: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8da2a45 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +version: 2 + +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" diff --git a/.github/matchers/tap.json b/.github/matchers/tap.json new file mode 100644 index 0000000..2c81ea9 --- /dev/null +++ b/.github/matchers/tap.json @@ -0,0 +1,32 @@ +{ + "//@npmcli/template-oss": "This file is automatically added by @npmcli/template-oss. Do not edit.", + "problemMatcher": [ + { + "owner": "tap", + "pattern": [ + { + "regexp": "^\\s*not ok \\d+ - (.*)", + "message": 1 + }, + { + "regexp": "^\\s*---" + }, + { + "regexp": "^\\s*at:" + }, + { + "regexp": "^\\s*line:\\s*(\\d+)", + "line": 1 + }, + { + "regexp": "^\\s*column:\\s*(\\d+)", + "column": 1 + }, + { + "regexp": "^\\s*file:\\s*(.*)", + "file": 1 + } + ] + } + ] +} diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..62892f9 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,39 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: Audit + +on: + workflow_dispatch: + schedule: + # "At 08:00 UTC (01:00 PT) on Monday" https://crontab.guru/#0_8_*_*_1 + - cron: "0 8 * * 1" + +jobs: + audit: + name: Audit Dependencies + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund --package-lock + - name: Run Production Audit + run: npm audit --omit=dev + - name: Run Full Audit + run: npm audit --audit-level=none diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml new file mode 100644 index 0000000..6e80aa6 --- /dev/null +++ b/.github/workflows/ci-release.yml @@ -0,0 +1,216 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CI - Release + +on: + workflow_dispatch: + inputs: + ref: + required: true + type: string + default: main + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string + +jobs: + lint-all: + name: Lint All + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Workflow Job + uses: actions/github-script@v6 + if: inputs.check-sha + id: check-output + env: + JOB_NAME: "Lint All" + MATRIX_NAME: "" + with: + script: | + const { owner, repo } = context.repo + + const { data } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: context.runId, + per_page: 100 + }) + + const jobName = process.env.JOB_NAME + process.env.MATRIX_NAME + const job = data.jobs.find(j => j.name.endsWith(jobName)) + const jobUrl = job?.html_url + + const shaUrl = `${context.serverUrl}/${owner}/${repo}/commit/${{ inputs.check-sha }}` + + let summary = `This check is assosciated with ${shaUrl}\n\n` + + if (jobUrl) { + summary += `For run logs, click here: ${jobUrl}` + } else { + summary += `Run logs could not be found for a job with name: "${jobName}"` + } + + return { summary } + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: inputs.check-sha + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Lint All + sha: ${{ inputs.check-sha }} + output: ${{ steps.check-output.outputs.result }} + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: steps.check.outputs.check_id && always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ steps.check.outputs.check_id }} + + test-all: + name: Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x + runs-on: ${{ matrix.platform.os }} + defaults: + run: + shell: ${{ matrix.platform.shell }} + steps: + - name: Get Workflow Job + uses: actions/github-script@v6 + if: inputs.check-sha + id: check-output + env: + JOB_NAME: "Test All" + MATRIX_NAME: " - ${{ matrix.platform.name }} - ${{ matrix.node-version }}" + with: + script: | + const { owner, repo } = context.repo + + const { data } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: context.runId, + per_page: 100 + }) + + const jobName = process.env.JOB_NAME + process.env.MATRIX_NAME + const job = data.jobs.find(j => j.name.endsWith(jobName)) + const jobUrl = job?.html_url + + const shaUrl = `${context.serverUrl}/${owner}/${repo}/commit/${{ inputs.check-sha }}` + + let summary = `This check is assosciated with ${shaUrl}\n\n` + + if (jobUrl) { + summary += `For run logs, click here: ${jobUrl}` + } else { + summary += `Run logs could not be found for a job with name: "${jobName}"` + } + + return { summary } + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: inputs.check-sha + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }} + sha: ${{ inputs.check-sha }} + output: ${{ steps.check-output.outputs.result }} + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: ${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: steps.check.outputs.check_id && always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ steps.check.outputs.check_id }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9cc149d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,107 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CI + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + - latest + schedule: + # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 + - cron: "0 9 * * 1" + +jobs: + lint: + name: Lint + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + + test: + name: Test - ${{ matrix.platform.name }} - ${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x + runs-on: ${{ matrix.platform.os }} + defaults: + run: + shell: ${{ matrix.platform.shell }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: ${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..66b9498 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,38 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CodeQL + +on: + push: + branches: + - main + - latest + pull_request: + branches: + - main + - latest + schedule: + # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1 + - cron: "0 10 * * 1" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: javascript + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/post-dependabot.yml b/.github/workflows/post-dependabot.yml new file mode 100644 index 0000000..19902bd --- /dev/null +++ b/.github/workflows/post-dependabot.yml @@ -0,0 +1,121 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: Post Dependabot + +on: pull_request + +permissions: + contents: write + +jobs: + template-oss: + name: template-oss + if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.ref }} + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Fetch Dependabot Metadata + id: metadata + uses: dependabot/fetch-metadata@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory + if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') + id: flags + run: | + dependabot_dir="${{ steps.metadata.outputs.directory }}" + if [[ "$dependabot_dir" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + # strip leading slash from directory so it works as a + # a path to the workspace flag + echo "::set-output name=workspace::-w ${dependabot_dir#/}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + npm run template-oss-apply ${{ steps.flags.outputs.workspace }} + if [[ `git status --porcelain` ]]; then + echo "::set-output name=changes::true" + fi + # This only sets the conventional commit prefix. This workflow can't reliably determine + # what the breaking change is though. If a BREAKING CHANGE message is required then + # this PR check will fail and the commit will be amended with stafftools + if [[ "${{ steps.metadata.outputs.update-type }}" == "version-update:semver-major" ]]; then + prefix='feat!' + else + prefix='chore' + fi + echo "::set-output name=message::$prefix: postinstall for dependabot template-oss PR" + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git commit -am "${{ steps.apply.outputs.message }}" + git push + + # If the previous step failed, then reset the commit and remove any workflow changes + # and attempt to commit and push again. This is helpful because we will have a commit + # with the correct prefix that we can then --amend with @npmcli/stafftools later. + - name: Push All Changes Except Workflows + if: steps.apply.outputs.changes && steps.push.outcome == 'failure' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ + git commit -am "${{ steps.apply.outputs.message }}" + git push + + # Check if all the necessary template-oss changes were applied. Since we continued + # on errors in one of the previous steps, this check will fail if our follow up + # only applied a portion of the changes and we need to followup manually. + # + # Note that this used to run `lint` and `postlint` but that will fail this action + # if we've also shipped any linting changes separate from template-oss. We do + # linting in another action, so we want to fail this one only if there are + # template-oss changes that could not be applied. + - name: Check Changes + if: steps.apply.outputs.changes + run: | + npm exec --offline ${{ steps.flags.outputs.workspace }} -- template-oss-check + + - name: Fail on Breaking Change + if: steps.apply.outputs.changes && startsWith(steps.apply.outputs.message, 'feat!') + run: | + echo "This PR has a breaking change. Run 'npx -p @npmcli/stafftools gh template-oss-fix'" + echo "for more information on how to fix this with a BREAKING CHANGE footer." + exit 1 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..1a1d1ee --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,48 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: Pull Request + +on: + pull_request: + types: + - opened + - reopened + - edited + - synchronize + +jobs: + commitlint: + name: Lint Commits + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Commitlint on Commits + id: commit + continue-on-error: true + run: | + npx --offline commitlint -V --from origin/${{ github.base_ref }} --to ${{ github.event.pull_request.head.sha }} + - name: Run Commitlint on PR Title + if: steps.commit.outcome == 'failure' + run: | + echo ${{ github.event.pull_request.title }} | npx --offline commitlint -V diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..264cf3d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,299 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: Release + +on: + workflow_dispatch: + push: + branches: + - main + - latest + - release/v* + +permissions: + contents: write + pull-requests: write + checks: write + +jobs: + release: + outputs: + pr: ${{ steps.release.outputs.pr }} + releases: ${{ steps.release.outputs.releases }} + release-flags: ${{ steps.release.outputs.release-flags }} + branch: ${{ steps.release.outputs.pr-branch }} + pr-number: ${{ steps.release.outputs.pr-number }} + comment-id: ${{ steps.pr-comment.outputs.result }} + check-id: ${{ steps.check.outputs.check_id }} + name: Release + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Release Please + id: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + npx --offline template-oss-release-please ${{ github.ref_name }} ${{ github.event_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: ${{ steps.release.outputs.pr-number }} + REF_NAME: ${{ github.ref_name }} + with: + script: | + const { REF_NAME, PR_NUMBER } = process.env + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: PR_NUMBER } + + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager\n\n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += `Release workflow run: ${workflow.html_url}\n\n#### Force CI to Update This Release\n\n` + body += `This PR will be updated and CI will run for every non-\`chore:\` commit that is pushed to \`main\`. ` + body += `To force CI to update this PR, run this command:\n\n` + body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME}\n\`\`\`` + + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + - name: Get Workflow Job + uses: actions/github-script@v6 + if: steps.release.outputs.pr-sha + id: check-output + env: + JOB_NAME: "Release" + MATRIX_NAME: "" + with: + script: | + const { owner, repo } = context.repo + + const { data } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: context.runId, + per_page: 100 + }) + + const jobName = process.env.JOB_NAME + process.env.MATRIX_NAME + const job = data.jobs.find(j => j.name.endsWith(jobName)) + const jobUrl = job?.html_url + + const shaUrl = `${context.serverUrl}/${owner}/${repo}/commit/${{ steps.release.outputs.pr-sha }}` + + let summary = `This check is assosciated with ${shaUrl}\n\n` + + if (jobUrl) { + summary += `For run logs, click here: ${jobUrl}` + } else { + summary += `Run logs could not be found for a job with name: "${jobName}"` + } + + return { summary } + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.release.outputs.pr-sha + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: ${{ steps.release.outputs.pr-sha }} + output: ${{ steps.check-output.outputs.result }} + + update: + needs: release + outputs: + sha: ${{ steps.commit.outputs.sha }} + check-id: ${{ steps.check.outputs.check_id }} + name: Update - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.release.outputs.branch }} + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Pull Request Actions + env: + RELEASE_PR_NUMBER: ${{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: ${{ needs.release.outputs.comment-id }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + npm exec --offline -- template-oss-release-manager --lockfile=false + npm run rp-pull-request --ignore-scripts --if-present + - name: Commit + id: commit + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Get Workflow Job + uses: actions/github-script@v6 + if: steps.commit.outputs.sha + id: check-output + env: + JOB_NAME: "Update - Release" + MATRIX_NAME: "" + with: + script: | + const { owner, repo } = context.repo + + const { data } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: context.runId, + per_page: 100 + }) + + const jobName = process.env.JOB_NAME + process.env.MATRIX_NAME + const job = data.jobs.find(j => j.name.endsWith(jobName)) + const jobUrl = job?.html_url + + const shaUrl = `${context.serverUrl}/${owner}/${repo}/commit/${{ steps.commit.outputs.sha }}` + + let summary = `This check is assosciated with ${shaUrl}\n\n` + + if (jobUrl) { + summary += `For run logs, click here: ${jobUrl}` + } else { + summary += `Run logs could not be found for a job with name: "${jobName}"` + } + + return { summary } + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.commit.outputs.sha + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: ${{ steps.commit.outputs.sha }} + output: ${{ steps.check-output.outputs.result }} + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: needs.release.outputs.check-id && always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ needs.release.outputs.check-id }} + + ci: + name: CI - Release + needs: [ release, update ] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: ${{ needs.release.outputs.branch }} + check-sha: ${{ needs.update.outputs.sha }} + + post-ci: + needs: [ release, update, ci ] + name: Post CI - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: needs.update.outputs.check-id && always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ steps.needs-result.outputs.result }} + check_id: ${{ needs.update.outputs.check-id }} + + post-release: + needs: release + name: Post Release - Release + if: github.repository_owner == 'npm' && needs.release.outputs.releases + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Release Actions + env: + RELEASES: ${{ needs.release.outputs.releases }} + run: | + npm run rp-release --ignore-scripts --if-present ${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} diff --git a/.gitignore b/.gitignore index 902c5dd..0ec3c84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,28 @@ -/coverage -/.nyc_output -/node_modules -package-lock.json +# This file is automatically added by @npmcli/template-oss. Do not edit. + +# ignore everything in the root +/* + +# keep these +!**/.gitignore +!/.commitlintrc.js +!/.eslintrc.js +!/.eslintrc.local.* +!/.github/ +!/.gitignore +!/.npmrc +!/.release-please-manifest.json +!/bin/ +!/CHANGELOG* +!/CODE_OF_CONDUCT.md +!/docs/ +!/lib/ +!/LICENSE* +!/map.js +!/package.json +!/README* +!/release-please-config.json +!/scripts/ +!/SECURITY.md +!/tap-snapshots/ +!/test/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..529f93e --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +; This file is automatically added by @npmcli/template-oss. Do not edit. + +package-lock=false diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..969d3db --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "2.1.0" +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 09263d7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -sudo: false - -node_js: - - node - - 12 - - 10 - - 8 - -os: - - linux - -cache: - directories: - - $HOME/.npm - -notifications: - email: false diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..167043c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,7 @@ +<!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> + +All interactions in this repo are covered by the [npm Code of +Conduct](https://docs.npmjs.com/policies/conduct) + +The npm cli team may, at its own discretion, moderate, remove, or edit +any interactions such as pull requests, issues, and comments. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..4e7c26c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +<!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> + +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). + +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. + +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 using [private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability). + +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. + +**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** + +Thanks for helping make GitHub safe for everyone. + diff --git a/index.js b/lib/index.js similarity index 84% rename from index.js rename to lib/index.js index 9b0779c..f3f2a88 100644 --- a/index.js +++ b/lib/index.js @@ -3,21 +3,7 @@ const MiniPass = require('minipass') const EE = require('events').EventEmitter const fs = require('fs') -let writev = fs.writev -/* istanbul ignore next */ -if (!writev) { - // This entire block can be removed if support for earlier than Node.js - // 12.9.0 is not needed. - const binding = process.binding('fs') - const FSReqWrap = binding.FSReqWrap || binding.FSReqCallback - - writev = (fd, iovec, pos, cb) => { - const done = (er, bw) => cb(er, bw, iovec) - const req = new FSReqWrap() - req.oncomplete = done - binding.writeBuffers(fd, iovec, pos, req) - } -} +const writev = fs.writev const _autoClose = Symbol('_autoClose') const _close = Symbol('_close') @@ -56,27 +42,34 @@ class ReadStream extends MiniPass { this.readable = true this.writable = false - if (typeof path !== 'string') + if (typeof path !== 'string') { throw new TypeError('path must be a string') + } this[_errored] = false this[_fd] = typeof opt.fd === 'number' ? opt.fd : null this[_path] = path - this[_readSize] = opt.readSize || 16*1024*1024 + this[_readSize] = opt.readSize || 16 * 1024 * 1024 this[_reading] = false this[_size] = typeof opt.size === 'number' ? opt.size : Infinity this[_remain] = this[_size] this[_autoClose] = typeof opt.autoClose === 'boolean' ? opt.autoClose : true - if (typeof this[_fd] === 'number') + if (typeof this[_fd] === 'number') { this[_read]() - else + } else { this[_open]() + } } - get fd () { return this[_fd] } - get path () { return this[_path] } + get fd () { + return this[_fd] + } + + get path () { + return this[_path] + } write () { throw new TypeError('this is a readable stream') @@ -91,9 +84,9 @@ class ReadStream extends MiniPass { } [_onopen] (er, fd) { - if (er) + if (er) { this[_onerror](er) - else { + } else { this[_fd] = fd this.emit('open', fd) this[_read]() @@ -109,19 +102,21 @@ class ReadStream extends MiniPass { this[_reading] = true const buf = this[_makeBuf]() /* istanbul ignore if */ - if (buf.length === 0) + if (buf.length === 0) { return process.nextTick(() => this[_onread](null, 0, buf)) - fs.read(this[_fd], buf, 0, buf.length, null, (er, br, buf) => - this[_onread](er, br, buf)) + } + fs.read(this[_fd], buf, 0, buf.length, null, (er, br, b) => + this[_onread](er, br, b)) } } [_onread] (er, br, buf) { this[_reading] = false - if (er) + if (er) { this[_onerror](er) - else if (this[_handleChunk](br, buf)) + } else if (this[_handleChunk](br, buf)) { this[_read]() + } } [_close] () { @@ -142,8 +137,9 @@ class ReadStream extends MiniPass { let ret = false // no effect if infinite this[_remain] -= br - if (br > 0) + if (br > 0) { ret = super.write(br < buf.length ? buf.slice(0, br) : buf) + } if (br === 0 || this[_remain] <= 0) { ret = false @@ -161,13 +157,15 @@ class ReadStream extends MiniPass { break case 'drain': - if (typeof this[_fd] === 'number') + if (typeof this[_fd] === 'number') { this[_read]() + } break case 'error': - if (this[_errored]) + if (this[_errored]) { return + } this[_errored] = true return super.emit(ev, data) @@ -184,8 +182,9 @@ class ReadStreamSync extends ReadStream { this[_onopen](null, fs.openSync(this[_path], 'r')) threw = false } finally { - if (threw) + if (threw) { this[_close]() + } } } @@ -199,15 +198,17 @@ class ReadStreamSync extends ReadStream { /* istanbul ignore next */ const br = buf.length === 0 ? 0 : fs.readSync(this[_fd], buf, 0, buf.length, null) - if (!this[_handleChunk](br, buf)) + if (!this[_handleChunk](br, buf)) { break + } } while (true) this[_reading] = false } threw = false } finally { - if (threw) + if (threw) { this[_close]() + } } } @@ -244,22 +245,28 @@ class WriteStream extends EE { this[_defaultFlag] = opt.flags === undefined this[_flags] = this[_defaultFlag] ? defaultFlag : opt.flags - if (this[_fd] === null) + if (this[_fd] === null) { this[_open]() + } } emit (ev, data) { if (ev === 'error') { - if (this[_errored]) + if (this[_errored]) { return + } this[_errored] = true } return super.emit(ev, data) } + get fd () { + return this[_fd] + } - get fd () { return this[_fd] } - get path () { return this[_path] } + get path () { + return this[_path] + } [_onerror] (er) { this[_close]() @@ -278,9 +285,9 @@ class WriteStream extends EE { er && er.code === 'ENOENT') { this[_flags] = 'w' this[_open]() - } else if (er) + } else if (er) { this[_onerror](er) - else { + } else { this[_fd] = fd this.emit('open', fd) this[_flush]() @@ -288,21 +295,24 @@ class WriteStream extends EE { } end (buf, enc) { - if (buf) + if (buf) { this.write(buf, enc) + } this[_ended] = true // synthetic after-write logic, where drain/finish live if (!this[_writing] && !this[_queue].length && - typeof this[_fd] === 'number') + typeof this[_fd] === 'number') { this[_onwrite](null, 0) + } return this } write (buf, enc) { - if (typeof buf === 'string') + if (typeof buf === 'string') { buf = Buffer.from(buf, enc) + } if (this[_ended]) { this.emit('error', new Error('write() after end()')) @@ -326,14 +336,15 @@ class WriteStream extends EE { } [_onwrite] (er, bw) { - if (er) + if (er) { this[_onerror](er) - else { - if (this[_pos] !== null) + } else { + if (this[_pos] !== null) { this[_pos] += bw - if (this[_queue].length) + } + if (this[_queue].length) { this[_flush]() - else { + } else { this[_writing] = false if (this[_ended] && !this[_finished]) { @@ -350,11 +361,12 @@ class WriteStream extends EE { [_flush] () { if (this[_queue].length === 0) { - if (this[_ended]) + if (this[_ended]) { this[_onwrite](null, 0) - } else if (this[_queue].length === 1) + } + } else if (this[_queue].length === 1) { this[_write](this[_queue].pop()) - else { + } else { const iovec = this[_queue] this[_queue] = [] writev(this[_fd], iovec, this[_pos], @@ -383,11 +395,13 @@ class WriteStreamSync extends WriteStream { if (er.code === 'ENOENT') { this[_flags] = 'w' return this[_open]() - } else + } else { throw er + } } - } else + } else { fd = fs.openSync(this[_path], this[_flags], this[_mode]) + } this[_onopen](null, fd) } @@ -409,8 +423,13 @@ class WriteStreamSync extends WriteStream { fs.writeSync(this[_fd], buf, 0, buf.length, this[_pos])) threw = false } finally { - if (threw) - try { this[_close]() } catch (_) {} + if (threw) { + try { + this[_close]() + } catch { + // ok error + } + } } } } diff --git a/package.json b/package.json index c555ce8..8bf2444 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,22 @@ { "name": "fs-minipass", "version": "2.1.0", - "main": "index.js", + "main": "lib/index.js", "scripts": { "test": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --follow-tags" + "lint": "eslint \"**/*.js\"", + "postlint": "template-oss-check", + "template-oss-apply": "template-oss-apply --force", + "lintfix": "npm run lint -- --fix", + "snap": "tap", + "posttest": "npm run lint" }, "keywords": [], - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", + "author": "GitHub Inc.", "license": "ISC", "repository": { "type": "git", - "url": "git+https://github.com/npm/fs-minipass.git" + "url": "https://github.com/npm/fs-minipass.git" }, "bugs": { "url": "https://github.com/npm/fs-minipass/issues" @@ -24,16 +27,27 @@ "minipass": "^3.0.0" }, "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "^4.11.0", "mutate-fs": "^2.0.1", - "tap": "^15.0.10" + "tap": "^16.3.0" }, "files": [ - "index.js" + "bin/", + "lib/" ], "tap": { - "check-coverage": true + "check-coverage": true, + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.11.0" } } diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..73d1e35 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,36 @@ +{ + "exclude-packages-from-root": true, + "group-pull-request-title-pattern": "chore: release ${version}", + "pull-request-title-pattern": "chore: release${component} ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features", + "hidden": false + }, + { + "type": "fix", + "section": "Bug Fixes", + "hidden": false + }, + { + "type": "docs", + "section": "Documentation", + "hidden": false + }, + { + "type": "deps", + "section": "Dependencies", + "hidden": false + }, + { + "type": "chore", + "hidden": true + } + ], + "packages": { + ".": { + "package-name": "" + } + } +} diff --git a/test/read.js b/test/read.js index f7d5f94..66f8e7c 100644 --- a/test/read.js +++ b/test/read.js @@ -3,11 +3,11 @@ const t = require('tap') const fsm = require('../') const fs = require('fs') -const EE = require('events').EventEmitter +const { resolve } = require('path') const mutateFS = require('mutate-fs') t.test('read the readme', t => { - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const rm = fs.readFileSync(p, 'utf8') const check = (t, res) => { t.equal(rm, res) @@ -27,8 +27,9 @@ t.test('read the readme', t => { t.type(str.fd, 'number') const out = [] let chunk - while (chunk = str.read()) + while (chunk = str.read()) { out.push(chunk) + } check(t, out.join('')) }) @@ -48,7 +49,7 @@ t.test('read the readme', t => { }) t.test('read the readme sized', t => { - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const size = fs.statSync(p).size const rm = fs.readFileSync(p, 'utf8') const check = (t, res) => { @@ -69,8 +70,9 @@ t.test('read the readme sized', t => { t.equal(str.fd, null) const out = [] let chunk - while (chunk = str.read()) + while (chunk = str.read()) { out.push(chunk) + } check(t, out.join('')) }) @@ -98,10 +100,13 @@ t.test('slow sink', t => { setTimeout(_ => this.emit('drain')) return false } - end () { return this.write() } + + end () { + return this.write() + } } - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const rm = fs.readFileSync(p, 'utf8') const check = t => { t.equal(chunks.join(''), rm) @@ -135,10 +140,11 @@ t.test('zeno reading style', t => { chunks.push(chunk) return true } + end () {} } - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const rm = fs.readFileSync(p, 'utf8') const check = t => { t.equal(chunks.join(''), rm) @@ -213,33 +219,38 @@ t.test('fail read', t => { }) fs.open = (path, flags, cb) => { - if (path === __filename) + if (path === __filename) { open(path, flags, (er, fd) => { - if (!er) + if (!er) { badFDs.add(fd) + } return cb(er, fd) }) - else + } else { open(path, flags, cb) + } } fs.openSync = (path, flags) => { const fd = openSync(path, flags) - if (path === __filename) + if (path === __filename) { badFDs.add(fd) + } return fd } fs.read = function (fd, buf, offset, length, pos, cb) { - if (badFDs.has(fd)) + if (badFDs.has(fd)) { process.nextTick(_ => cb(new Error('poop'))) - else + } else { read(fd, buf, offset, length, pos, cb) + } } fs.readSync = function (fd, buf, offset, length, pos) { - if (badFDs.has(fd)) + if (badFDs.has(fd)) { throw new Error('poop sync') + } } t.throws(_ => new fsm.ReadStreamSync(__filename)) @@ -256,7 +267,7 @@ t.test('fail read', t => { }) t.test('fd test', t => { - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const rm = fs.readFileSync(p, 'utf8') const check = (t, res) => { t.equal(rm, res) @@ -280,8 +291,9 @@ t.test('fd test', t => { t.equal(str.path, p) const out = [] let chunk - while (chunk = str.read()) + while (chunk = str.read()) { out.push(chunk) + } check(t, out.join('')) }) @@ -300,7 +312,7 @@ t.test('fd test', t => { }) t.test('fd test, no autoClose', t => { - const p = __dirname + '/../README.md' + const p = resolve(__dirname, '..', 'README.md') const rm = fs.readFileSync(p, 'utf8') const check = (t, res, fd) => { // will throw EBADF if already closed @@ -314,7 +326,7 @@ t.test('fd test, no autoClose', t => { const str = new fsm.ReadStreamSync(p, { encoding: 'utf8', fd: fd, - autoClose: false + autoClose: false, }) t.type(str.fd, 'number') t.equal(str.path, p) @@ -328,14 +340,15 @@ t.test('fd test, no autoClose', t => { const str = new fsm.ReadStreamSync(p, { encoding: 'utf8', fd: fd, - autoClose: false + autoClose: false, }) t.type(str.fd, 'number') t.equal(str.path, p) const out = [] let chunk - while (chunk = str.read()) + while (chunk = str.read()) { out.push(chunk) + } check(t, out.join(''), fd) }) @@ -344,7 +357,7 @@ t.test('fd test, no autoClose', t => { const str = new fsm.ReadStream(p, { encoding: 'utf8', fd: fd, - autoClose: false + autoClose: false, }) t.type(str.fd, 'number') t.equal(str.path, p) diff --git a/test/write.js b/test/write.js index 383d87d..3e2b85d 100644 --- a/test/write.js +++ b/test/write.js @@ -3,11 +3,11 @@ const t = require('tap') const fsm = require('../') const fs = require('fs') -const EE = require('events').EventEmitter +const { join } = require('path') const mutateFS = require('mutate-fs') t.test('basic write', t => { - const p = __dirname + '/basic-write' + const p = join(__dirname, 'basic-write') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'ok') @@ -30,7 +30,7 @@ t.test('basic write', t => { }) t.test('write then end', t => { - const p = __dirname + '/write-then-end' + const p = join(__dirname, '/write-then-end') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'okend') @@ -62,7 +62,7 @@ t.test('write then end', t => { }) t.test('multiple writes', t => { - const p = __dirname + '/multiple-writes' + const p = join(__dirname, '/multiple-writes') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'abcdefghijklmnop') @@ -128,22 +128,22 @@ t.test('multiple writes', t => { t.ok(s.write('c')) t.notOk(s.write('d')) t.notOk(s.write('e')) - s.once('drain', _ => { + s.once('drain', () => { t.ok(s.write('f')) t.notOk(s.write(Buffer.from('676869', 'hex'))) t.notOk(s.write('jklm')) t.notOk(s.write(Buffer.from('nop'))) - s.once('drain', _ => s.end()) + s.once('drain', () => s.end()) }) }) - s.on('finish', _ => check(t)) + s.on('finish', () => check(t)) }) }) t.end() }) t.test('flags', t => { - const p = __dirname + '/flags' + const p = join(__dirname, '/flags') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'ok') @@ -166,7 +166,7 @@ t.test('flags', t => { }) t.test('mode', t => { - const p = __dirname + '/mode' + const p = join(__dirname, '/mode') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'ok') @@ -190,7 +190,7 @@ t.test('mode', t => { }) t.test('write after end', t => { - const p = __dirname + '/write-after-end' + const p = join(__dirname, '/write-after-end') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'ok') @@ -220,7 +220,7 @@ t.test('write after end', t => { }) t.test('fd', t => { - const p = __dirname + '/fd' + const p = join(__dirname, '/fd') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), 'ok') @@ -245,7 +245,7 @@ t.test('fd', t => { }) t.test('empty write', t => { - const p = __dirname + '/empty-write' + const p = join(__dirname, '/empty-write') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), '') @@ -297,7 +297,7 @@ t.test('empty write', t => { }) t.test('fail open', t => { - const p = __dirname + '/fail-open' + const p = join(__dirname, '/fail-open') const poop = new Error('poop') t.teardown(mutateFS.fail('open', poop)) t.throws(_ => new fsm.WriteStreamSync(p), poop) @@ -309,7 +309,7 @@ t.test('fail open', t => { }) t.test('fail open, positioned write', t => { - const p = __dirname + '/fail-open-positioned' + const p = join(__dirname, '/fail-open-positioned') const poop = new Error('poop') t.teardown(mutateFS.fail('open', poop)) t.throws(_ => new fsm.WriteStreamSync(p, { start: 2 }), poop) @@ -321,7 +321,7 @@ t.test('fail open, positioned write', t => { }) t.test('fail close', t => { - const p = __dirname + '/fail-close' + const p = join(__dirname, '/fail-close') const poop = new Error('poop') t.teardown(mutateFS.fail('close', poop)) t.teardown(() => fs.unlinkSync(p)) @@ -338,7 +338,7 @@ t.test('fail write', t => { const closeError = new Error('close error') t.teardown(mutateFS.fail('close', closeError)) - const p = __dirname + '/fail-write' + const p = join(__dirname, '/fail-write') const poop = new Error('poop') t.teardown(mutateFS.fail('write', poop)) @@ -353,7 +353,7 @@ t.test('fail write', t => { }) t.test('positioned write', t => { - const p = __dirname + '/positioned-write' + const p = join(__dirname, '/positioned-write') const write = Buffer.from('this is the data that is written') const data = Buffer.allocUnsafe(256) @@ -389,7 +389,7 @@ t.test('positioned write', t => { }) t.test('positioned then unpositioned', t => { - const p = __dirname + '/positioned-then-unpositioned' + const p = join(__dirname, '/positioned-then-unpositioned') const write = Buffer.from('this is the data that is written') const data = Buffer.allocUnsafe(256) @@ -428,7 +428,7 @@ t.test('positioned then unpositioned', t => { }) t.test('positioned then unpositioned at zero', t => { - const p = __dirname + '/positioned-then-unpositioned' + const p = join(__dirname, '/positioned-then-unpositioned') const write = Buffer.from('this is the data that is written') const data = Buffer.allocUnsafe(256) @@ -467,7 +467,7 @@ t.test('positioned then unpositioned at zero', t => { }) t.test('fd, no autoClose', t => { - const p = __dirname + '/fd-no-autoclose' + const p = join(__dirname, '/fd-no-autoclose') const check = (t, fd) => { fs.closeSync(fd) @@ -493,7 +493,7 @@ t.test('fd, no autoClose', t => { }) t.test('positioned, nonexistent file', t => { - const p = __dirname + '/pos-noent' + const p = join(__dirname, '/pos-noent') const check = t => { t.equal(fs.readFileSync(p, 'utf8'), '\0\0asdf\0\0\0\0asdf') @@ -513,9 +513,9 @@ t.test('positioned, nonexistent file', t => { const w = new fsm.WriteStream(p, { start: 10 }) w.end('asdf') w.on('close', _ => { - const w = new fsm.WriteStream(p, { start: 2 }) - w.end('asdf') - w.on('close', _ => check(t)) + const w2 = new fsm.WriteStream(p, { start: 2 }) + w2.end('asdf') + w2.on('close', () => check(t)) }) })