├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── add-to-project.yml │ ├── docs.yml │ ├── release.yml │ ├── semantic.yml │ └── test.yml ├── .gitignore ├── .mocharc.jsonc ├── .nvmrc ├── .releaserc.json ├── LICENSE ├── README.md ├── contributing.md ├── package.json ├── src ├── arch.ts ├── cache.ts ├── clang-fetcher.ts ├── cli.ts ├── constants.ts ├── electron-locator.ts ├── fetcher.ts ├── main.ts ├── module-rebuilder.ts ├── module-type │ ├── index.ts │ ├── node-gyp │ │ ├── node-gyp.ts │ │ └── worker.ts │ ├── node-pre-gyp.ts │ ├── prebuild-install.ts │ └── prebuildify.ts ├── module-walker.ts ├── node-api.ts ├── prebuild-shim.ts ├── promisifiedGracefulFs.ts ├── read-package-json.ts ├── rebuild.ts ├── search-module.ts ├── sysroot-fetcher.ts └── types.ts ├── test ├── arch.ts ├── electron-locator.ts ├── fixture │ ├── electron-locator │ │ ├── not-installed │ │ │ └── package.json │ │ ├── prebuilt-compile │ │ │ └── node_modules │ │ │ │ └── electron-prebuilt-compile │ │ │ │ └── package.json │ │ ├── single │ │ │ └── node_modules │ │ │ │ └── electron │ │ │ │ └── package.json │ │ └── workspace │ │ │ ├── node_modules │ │ │ └── electron │ │ │ │ └── package.json │ │ │ └── packages │ │ │ └── descendant │ │ │ └── package.json │ ├── empty-project │ │ ├── node_modules │ │ │ └── extra │ │ │ │ └── package.json │ │ └── package.json │ ├── forked-module-test │ │ └── package.json │ ├── multi-level-workspace │ │ └── packages │ │ │ ├── bar │ │ │ └── package.json │ │ │ └── foo │ │ │ └── package.json │ ├── napi-build-version │ │ └── package.json │ ├── native-app1 │ │ ├── package.json │ │ └── yarn.lock │ ├── prebuildify │ │ ├── abi │ │ │ └── prebuilds │ │ │ │ └── linux-x64 │ │ │ │ └── electron.abi89.node │ │ ├── has-prebuildify-devdep │ │ │ └── package.json │ │ ├── napi │ │ │ └── prebuilds │ │ │ │ ├── linux-arm │ │ │ │ └── node.napi.armv7.node │ │ │ │ ├── linux-arm64 │ │ │ │ └── electron.napi.armv8.node │ │ │ │ └── linux-x64 │ │ │ │ └── node.napi.node │ │ ├── no-prebuildify-devdep │ │ │ └── package.json │ │ └── not-found │ │ │ └── prebuilds │ │ │ └── electron.abi0.node │ └── workspace-test │ │ ├── child-workspace │ │ └── package.json │ │ └── package.json ├── helpers │ ├── electron-version.ts │ ├── module-setup.ts │ └── rebuild.ts ├── module-type-node-gyp.ts ├── module-type-node-pre-gyp.ts ├── module-type-prebuild-install.ts ├── module-type-prebuildify.ts ├── read-package-json.ts ├── rebuild-napibuildversion.ts ├── rebuild-yarnworkspace.ts ├── rebuild.ts └── search-module.ts ├── tsconfig.json ├── typings └── ambient.d.ts └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | *.d.ts 2 | /node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | ecmaVersion: 2025, 5 | sourceType: 'module', 6 | }, 7 | plugins: ['mocha', '@typescript-eslint'], 8 | env: { 9 | es6: true, 10 | mocha: true, 11 | node: true, 12 | }, 13 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 14 | rules: { 15 | 'mocha/no-exclusive-tests': 'error', 16 | 'no-unused-vars': 'off', 17 | '@typescript-eslint/no-unused-vars': [ 18 | 'error', 19 | { 20 | args: 'after-used', 21 | argsIgnorePattern: '^_', 22 | ignoreRestSiblings: true, 23 | vars: 'all', 24 | }, 25 | ], 26 | semi: 'off', 27 | '@typescript-eslint/semi': 'error', 28 | 'no-unexpected-multiline': 'error', 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @electron/wg-ecosystem 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/add-to-project.yml: -------------------------------------------------------------------------------- 1 | name: Add to Ecosystem WG Project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | pull_request_target: 8 | types: 9 | - opened 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | add-to-project: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Generate GitHub App token 18 | uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1 19 | id: generate-token 20 | with: 21 | creds: ${{ secrets.ECOSYSTEM_ISSUE_TRIAGE_GH_APP_CREDS }} 22 | org: electron 23 | - name: Add to Project 24 | uses: dsanders11/project-actions/add-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0 25 | with: 26 | field: Opened 27 | field-value: ${{ github.event.pull_request.created_at || github.event.issue.created_at }} 28 | project-number: 89 29 | token: ${{ steps.generate-token.outputs.token }} 30 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish documentation 2 | 3 | on: 4 | push: 5 | tags: 6 | - v[0-9]+.[0-9]+.[0-9]+* 7 | 8 | permissions: 9 | id-token: write 10 | contents: read 11 | 12 | jobs: 13 | docs: 14 | runs-on: ubuntu-latest 15 | environment: docs-publish 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag: v4.2.2 18 | - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # tag: v4.4.0 19 | with: 20 | node-version: 22.12.x 21 | - name: Install dependencies 22 | run: yarn --frozen-lockfile 23 | - name: Build API documentation 24 | run: yarn build:docs 25 | - name: Azure login 26 | uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 27 | with: 28 | client-id: ${{ secrets.AZURE_OIDC_CLIENT_ID }} 29 | tenant-id: ${{ secrets.AZURE_OIDC_TENANT_ID }} 30 | subscription-id: ${{ secrets.AZURE_OIDC_SUBSCRIPTION_ID }} 31 | - name: Upload to Azure Blob Storage 32 | uses: azure/cli@089eac9d8cc39f5d003e94f8b65efc51076c9cbd # tag: v2.1.0 33 | with: 34 | inlineScript: | 35 | az storage blob upload-batch --account-name ${{ secrets.AZURE_ECOSYSTEM_PACKAGES_STORAGE_ACCOUNT_NAME }} -d '$web/rebuild/${{ github.ref_name }}' -s ./docs --overwrite --auth-mode login 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | uses: ./.github/workflows/test.yml 11 | 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | needs: test 16 | environment: npm 17 | permissions: 18 | id-token: write # for CFA and npm provenance 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false 24 | - name: Setup Node.js 25 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 26 | with: 27 | node-version: 22.12.x 28 | cache: 'yarn' 29 | - name: Install 30 | run: yarn install --frozen-lockfile 31 | - uses: continuousauth/action@4e8a2573eeb706f6d7300d6a9f3ca6322740b72d # v1.0.5 32 | timeout-minutes: 60 33 | with: 34 | project-id: ${{ secrets.CFA_PROJECT_ID }} 35 | secret: ${{ secrets.CFA_SECRET }} 36 | npm-token: ${{ secrets.NPM_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/semantic.yml: -------------------------------------------------------------------------------- 1 | name: "Check Semantic Commit" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | main: 15 | permissions: 16 | pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs 17 | statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR 18 | name: Validate PR Title 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: semantic-pull-request 22 | uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | validateSingleCommit: false 27 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | schedule: 8 | - cron: '0 22 * * 3' 9 | workflow_call: 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | strategy: 18 | matrix: 19 | node-version: 20 | - '22.12.x' 21 | os: 22 | - macos-latest 23 | - ubuntu-latest 24 | - windows-latest 25 | runs-on: "${{ matrix.os }}" 26 | env: 27 | GYP_MSVS_VERSION: '2022' 28 | steps: 29 | - run: git config --global core.autocrlf input 30 | - name: Enable Long Paths (Windows) 31 | if : ${{ matrix.os == 'windows-latest' }} 32 | run: | 33 | New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force 34 | - name: Setup distutils (Windows) 35 | if : ${{ matrix.os == 'windows-latest' }} 36 | run: pip3 install setuptools 37 | - name: Setup distutils (macOS) 38 | if : ${{ matrix.os == 'macos-latest' }} 39 | run: pip3 install --break-system-packages --user setuptools 40 | - name: Checkout 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | - name: Setup Node.js 43 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 44 | with: 45 | node-version: "${{ matrix.node-version }}" 46 | cache: 'yarn' 47 | - name: Install dependencies 48 | run: yarn install --frozen-lockfile 49 | - name: Lint 50 | run: yarn run lint 51 | - name: Test & Report Coverage 52 | env: 53 | DEBUG: electron-rebuild 54 | run: | 55 | yarn run coverage 56 | yarn run coverage:report 57 | - name: Upload coverage to Codecov 58 | uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 59 | with: 60 | files: ./coverage.lcov 61 | token: ${{ secrets.CODECOV_TOKEN }} 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | .nyc_output 16 | coverage.lcov 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 29 | node_modules 30 | 31 | lib 32 | test-dist 33 | 34 | docs -------------------------------------------------------------------------------- /.mocharc.jsonc: -------------------------------------------------------------------------------- 1 | // See also: https://github.com/mochajs/mocha/blob/b720ec1b3ca630a90f80311da391b2a0cdfead4e/example/config/.mocharc.js 2 | { 3 | "$schema": "https://json.schemastore.org/mocharc", 4 | "color": false, 5 | "extensions": [ 6 | "ts" 7 | ], 8 | "import": "tsx", 9 | "spec": ["test/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.12 2 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | "@continuous-auth/semantic-release-npm", 6 | "@semantic-release/github" 7 | ], 8 | "branches": [ "main" ] 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Contributors to the Electron project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Electron Rebuild 2 | 3 | [![Test](https://github.com/electron/rebuild/actions/workflows/test.yml/badge.svg)](https://github.com/electron/rebuild/actions/workflows/test.yml) 4 | [![NPM](https://img.shields.io/npm/v/@electron/rebuild.svg?style=flat)](https://npm.im/@electron/rebuild) 5 | [![Coverage Status](https://codecov.io/gh/electron/rebuild/branch/main/graph/badge.svg)](https://codecov.io/gh/electron/rebuild) 6 | 7 | This executable rebuilds native Node.js modules against the version of Node.js 8 | that your Electron project is using. This allows you to use native Node.js 9 | modules in Electron apps without your system version of Node.js matching exactly 10 | (which is often not the case, and sometimes not even possible). 11 | 12 | ### How does it work? 13 | 14 | Install the package with `--save-dev`: 15 | 16 | ```sh 17 | npm install --save-dev @electron/rebuild 18 | ``` 19 | 20 | Then, whenever you install a new npm package, rerun electron-rebuild: 21 | 22 | ```sh 23 | $(npm bin)/electron-rebuild 24 | ``` 25 | 26 | Or if you're on Windows: 27 | 28 | ```sh 29 | .\node_modules\.bin\electron-rebuild.cmd 30 | ``` 31 | If you have a good node-gyp config but you see an error about a missing element on Windows like `Could not load the Visual C++ component "VCBuild.exe"`, try to launch electron-rebuild in an npm script: 32 | 33 | ```json 34 | "scripts": { 35 | "rebuild": "electron-rebuild -f -w yourmodule" 36 | } 37 | ``` 38 | 39 | and then 40 | 41 | ```sh 42 | npm run rebuild 43 | ``` 44 | 45 | ### What are the requirements? 46 | 47 | Node v22.12.0 or higher is required. Building native modules from source uses 48 | [`node-gyp`](https://github.com/nodejs/node-gyp#installation), refer to the link for its 49 | installation/runtime requirements. 50 | 51 | ### CLI Arguments 52 | 53 | ``` 54 | Usage: electron-rebuild --version [version] --module-dir [path] 55 | 56 | Options: 57 | -v, --version The version of Electron to build against [string] 58 | -f, --force Force rebuilding modules, even if we would skip 59 | it otherwise [boolean] 60 | -a, --arch Override the target architecture to something 61 | other than your system's [string] 62 | -m, --module-dir The path to the node_modules directory to rebuild 63 | [string] 64 | -w, --which-module A specific module to build, or comma separated 65 | list of modules. Modules will only be rebuilt if 66 | they also match the types of dependencies being 67 | rebuilt (see --types). [string] 68 | -o, --only Only build specified module, or comma separated 69 | list of modules. All others are ignored. [string] 70 | -e, --electron-prebuilt-dir The path to the prebuilt electron module [string] 71 | -d, --dist-url Custom header tarball URL [string] 72 | -t, --types The types of dependencies to rebuild. Comma 73 | separated list of "prod", "dev" and "optional". 74 | Default is "prod,optional" [string] 75 | -p, --parallel Rebuild in parallel, this is enabled by default 76 | on macOS and Linux [boolean] 77 | -s, --sequential Rebuild modules sequentially, this is enabled by 78 | default on Windows [boolean] 79 | -b, --debug Build debug version of modules [boolean] 80 | --prebuild-tag-prefix GitHub tag prefix passed to prebuild-install. 81 | Default is "v" [string] 82 | --force-abi Override the ABI version for the version of 83 | Electron you are targeting. Only use when 84 | targeting Nightly releases. [number] 85 | --use-electron-clang Use the clang executable that Electron used when 86 | building its binary. This will guarantee compiler 87 | compatibility [boolean] 88 | --disable-pre-gyp-copy Disables the pre-gyp copy step [boolean] 89 | --build-from-source Skips prebuild download and rebuilds module from 90 | source. [boolean] 91 | -h, --help Show help [boolean] 92 | ``` 93 | 94 | ### How can I use this with [Electron Forge](https://github.com/electron/forge)? 95 | 96 | This package is automatically used with Electron Forge when packaging an Electron app. 97 | 98 | ### How can I integrate this into [Electron Packager](https://github.com/electron/packager)? 99 | 100 | electron-rebuild provides a function compatible with the [`afterCopy` hook](https://electron.github.io/packager/main/interfaces/Options.html#afterCopy) 101 | for Electron Packager. For example: 102 | 103 | ```javascript 104 | import packager from '@electron/packager'; 105 | import { rebuild } from '@electron/rebuild'; 106 | 107 | packager({ 108 | // … other options 109 | afterCopy: [(buildPath, electronVersion, platform, arch, callback) => { 110 | rebuild({ buildPath, electronVersion, arch }) 111 | .then(() => callback()) 112 | .catch((error) => callback(error)); 113 | }], 114 | // … other options 115 | }); 116 | ``` 117 | 118 | ### How can I integrate this with [prebuild](https://github.com/prebuild/prebuild)? 119 | 120 | If your module uses [prebuild](https://github.com/prebuild/prebuild) for creating prebuilt binaries, 121 | it also uses [prebuild-install](https://github.com/prebuild/prebuild-install) to download them. If 122 | this is the case, then `electron-rebuild` will run `prebuild-install` to download the correct 123 | binaries from the project's GitHub Releases instead of rebuilding them. 124 | 125 | ### How can I integrate this into Grunt / Gulp / Whatever? 126 | 127 | electron-rebuild is also a library that you can require into your app or 128 | build process. It has a very simple API: 129 | 130 | ```javascript 131 | import { rebuild } from '@electron/rebuild'; 132 | 133 | // Public: Rebuilds a node_modules directory with the given Electron version. 134 | // 135 | // options: Object with the following properties 136 | // buildPath - An absolute path to your app's directory. (The directory that contains your node_modules) 137 | // electronVersion - The version of Electron to rebuild for 138 | // arch (optional) - Default: process.arch - The arch to rebuild for 139 | // extraModules (optional) - Default: [] - An array of modules to rebuild as well as the detected modules 140 | // onlyModules (optional) - Default: null - An array of modules to rebuild, ONLY these module names will be rebuilt. 141 | // The "types" property will be ignored if this option is set. 142 | // force (optional) - Default: false - Force a rebuild of modules regardless of their current build state 143 | // headerURL (optional) - Default: https://www.electronjs.org/headers - The URL to download Electron header files from 144 | // types (optional) - Default: ['prod', 'optional'] - The types of modules to rebuild 145 | // mode (optional) - The rebuild mode, either 'sequential' or 'parallel' - Default varies per platform (probably shouldn't mess with this one) 146 | // useElectronClang (optional) - Whether to use the clang executable that Electron used when building its binary. This will guarantee compiler compatibility 147 | 148 | // Returns a Promise indicating whether the operation succeeded or not 149 | ``` 150 | 151 | A full build process might look something like: 152 | 153 | ```javascript 154 | // ESM 155 | import { rebuild } from '@electron/rebuild' 156 | 157 | rebuild({ 158 | buildPath: import.meta.dirname, 159 | electronVersion: '35.1.5' 160 | }) 161 | .then(() => console.info('Rebuild Successful')) 162 | .catch((e) => { 163 | console.error('Building modules didn\'t work!') 164 | console.error(e) 165 | }) 166 | 167 | // CommonJS 168 | const { rebuild } = require('@electron/rebuild') 169 | 170 | rebuild({ 171 | buildPath: __dirname, 172 | electronVersion: '35.1.5' 173 | }) 174 | .then(() => console.info('Rebuild Successful')) 175 | .catch((e) => { 176 | console.error('Building modules didn\'t work!') 177 | console.error(e) 178 | }) 179 | ``` 180 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to @electron/rebuild 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | This project adheres to the Contributor Covenant 6 | [code of conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md). 7 | By participating, you are expected to uphold this code. 8 | 9 | The following is a set of guidelines for contributing to @electron/rebuild. 10 | These are just guidelines, not rules, use your best judgment and feel free to 11 | propose changes to this document in a pull request. 12 | 13 | ## Submitting Issues 14 | 15 | * You can create an issue [here](https://github.com/electron/rebuild/issues/new), 16 | but before doing that please read the notes below and include as many details as 17 | possible with your report. If you can, please include: 18 | * The version of electron and @electron/rebuild you are using 19 | * The operating system you are using 20 | * If applicable, what you were doing when the issue arose and what you 21 | expected to happen 22 | * Other things that will help resolve your issue: 23 | * Screenshots and animated GIFs 24 | * Error output that appears in your terminal, dev tools or as an alert 25 | * Perform a [cursory search](https://github.com/electron/rebuild/issues?utf8=✓&q=is%3Aissue+) 26 | to see if a similar issue has already been submitted 27 | 28 | ## Submitting Pull Requests 29 | 30 | * Include screenshots and animated GIFs in your pull request whenever possible. 31 | * Write documentation in [Markdown](https://daringfireball.net/projects/markdown). 32 | * Use short, present tense commit messages. See [Commit Message Styleguide](#git-commit-messages). 33 | 34 | ## Git Commit Messages 35 | 36 | * Use the present tense ("Add feature" not "Added feature") 37 | * Use the imperative mood ("Move cursor to..." not "Moves cursor to...") 38 | * Limit the first line to 72 characters or less 39 | * Reference issues and pull requests liberally 40 | * When only changing documentation, include `[ci skip]` in the commit description 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@electron/rebuild", 3 | "version": "0.0.0-development", 4 | "description": "Electron supporting package to rebuild native node modules against the currently installed electron", 5 | "type": "module", 6 | "exports": "./lib/main.js", 7 | "typings": "lib/main.d.ts", 8 | "scripts": { 9 | "build": "tsc", 10 | "build:docs": "typedoc src/main.ts", 11 | "coverage": "npm run prewarm-headers && nyc npm run spec", 12 | "coverage:report": "nyc report --reporter=text-lcov > coverage.lcov", 13 | "watch": "tsc -w", 14 | "prepare": "npm run build", 15 | "lint": "eslint --ext .ts .", 16 | "spec": "tsc && mocha", 17 | "test": "npm run prewarm-headers && npm run lint && npm run spec", 18 | "prewarm-headers": "node-gyp install --ensure" 19 | }, 20 | "bin": { 21 | "electron-rebuild": "lib/cli.js" 22 | }, 23 | "files": [ 24 | "lib" 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/electron/rebuild.git" 29 | }, 30 | "keywords": [ 31 | "electron" 32 | ], 33 | "authors": [ 34 | "Ani Betts ", 35 | "Electron Community" 36 | ], 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/electron/rebuild/issues" 40 | }, 41 | "homepage": "https://github.com/electron/rebuild", 42 | "engines": { 43 | "node": ">=22.12.0" 44 | }, 45 | "publishConfig": { 46 | "provenance": true 47 | }, 48 | "dependencies": { 49 | "@malept/cross-spawn-promise": "^2.0.0", 50 | "chalk": "^4.0.0", 51 | "debug": "^4.1.1", 52 | "detect-libc": "^2.0.1", 53 | "got": "^11.7.0", 54 | "graceful-fs": "^4.2.11", 55 | "node-abi": "^4.2.0", 56 | "node-api-version": "^0.2.1", 57 | "node-gyp": "^11.2.0", 58 | "ora": "^5.1.0", 59 | "read-binary-file-arch": "^1.0.6", 60 | "semver": "^7.3.5", 61 | "tar": "^6.0.5", 62 | "yargs": "^17.0.1" 63 | }, 64 | "devDependencies": { 65 | "@istanbuljs/nyc-config-typescript": "^1.0.1", 66 | "@tsconfig/node22": "^22.0.0", 67 | "@types/chai": "^4.2.12", 68 | "@types/chai-as-promised": "^7.1.3", 69 | "@types/debug": "^4.1.5", 70 | "@types/graceful-fs": "^4.1.9", 71 | "@types/mocha": "^10.0.10", 72 | "@types/node": "~22.10.7", 73 | "@types/node-abi": "^3.0.0", 74 | "@types/semver": "^7.3.9", 75 | "@types/tar": "^6.1.0", 76 | "@types/yargs": "^17.0.2", 77 | "@typescript-eslint/eslint-plugin": "^6.21.0", 78 | "@typescript-eslint/parser": "^6.21.0", 79 | "chai": "^4.2.0", 80 | "chai-as-promised": "^7.1.1", 81 | "electron": "^22.0.0", 82 | "eslint": "^7.7.0", 83 | "eslint-plugin-mocha": "^9.0.0", 84 | "mocha": "^11.1.0", 85 | "nyc": "^15.1.0", 86 | "tsx": "^4.19.3", 87 | "typedoc": "~0.25.13", 88 | "typescript": "~5.4.5" 89 | }, 90 | "nyc": { 91 | "extends": "@istanbuljs/nyc-config-typescript" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/arch.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process'; 2 | 3 | /** 4 | * Runs the `uname` command and returns the trimmed output. 5 | * 6 | * Copied from `@electron/get`. 7 | */ 8 | export function uname(): string { 9 | return execSync('uname -m') 10 | .toString() 11 | .trim(); 12 | } 13 | 14 | export type ConfigVariables = { 15 | arm_version?: string; 16 | }; 17 | 18 | /** 19 | * Generates an architecture name that would be used in an Electron or Node.js 20 | * download file name. 21 | * 22 | * Copied from `@electron/get`. 23 | */ 24 | export function getNodeArch(arch: string, configVariables: ConfigVariables): string { 25 | if (arch === 'arm') { 26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 27 | switch (configVariables.arm_version) { 28 | case '6': 29 | return uname(); 30 | case '7': 31 | default: 32 | return 'armv7l'; 33 | } 34 | } 35 | 36 | return arch; 37 | } 38 | -------------------------------------------------------------------------------- /src/cache.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'node:crypto'; 2 | import debug from 'debug'; 3 | import fs from 'graceful-fs'; 4 | import path from 'node:path'; 5 | import zlib from 'node:zlib'; 6 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 7 | 8 | const d = debug('electron-rebuild'); 9 | 10 | // Update this number if you change the caching logic to ensure no bad cache hits 11 | const ELECTRON_REBUILD_CACHE_ID = 1; 12 | 13 | class Snap { 14 | constructor(public hash: string, public data: Buffer) {} 15 | } 16 | 17 | interface Snapshot { 18 | [key: string]: Snap | Snapshot; 19 | } 20 | 21 | type HashTree = { [path: string]: string | HashTree }; 22 | 23 | type CacheOptions = { 24 | ABI: string; 25 | arch: string; 26 | platform: string; 27 | debug: boolean; 28 | electronVersion: string; 29 | headerURL: string; 30 | modulePath: string; 31 | }; 32 | 33 | const takeSnapshot = async (dir: string, relativeTo = dir): Promise => { 34 | const snap: Snapshot = {}; 35 | await Promise.all((await promisifiedGracefulFs.readdir(dir)).map(async (child) => { 36 | if (child === 'node_modules') return; 37 | const childPath = path.resolve(dir, child); 38 | const relative = path.relative(relativeTo, childPath); 39 | if ((await fs.promises.stat(childPath)).isDirectory()) { 40 | snap[relative] = await takeSnapshot(childPath, relativeTo); 41 | } else { 42 | const data = await promisifiedGracefulFs.readFile(childPath); 43 | snap[relative] = new Snap( 44 | crypto.createHash('SHA256').update(data).digest('hex'), 45 | data, 46 | ); 47 | } 48 | })); 49 | return snap; 50 | }; 51 | 52 | const writeSnapshot = async (diff: Snapshot, dir: string): Promise => { 53 | for (const key in diff) { 54 | if (diff[key] instanceof Snap) { 55 | await fs.promises.mkdir(path.dirname(path.resolve(dir, key)), { recursive: true }); 56 | await promisifiedGracefulFs.writeFile(path.resolve(dir, key), (diff[key] as Snap).data); 57 | } else { 58 | await fs.promises.mkdir(path.resolve(dir, key), { recursive: true }); 59 | await writeSnapshot(diff[key] as Snapshot, dir); 60 | } 61 | } 62 | }; 63 | 64 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 65 | const serialize = (snap: Snapshot): any => { 66 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 67 | const jsonReady: any = {}; 68 | for (const key in snap) { 69 | if (snap[key] instanceof Snap) { 70 | const s = snap[key] as Snap; 71 | jsonReady[key] = { 72 | __isSnap: true, 73 | hash: s.hash, 74 | data: s.data.toString('base64') 75 | }; 76 | } else { 77 | jsonReady[key] = serialize(snap[key] as Snapshot); 78 | } 79 | } 80 | return jsonReady; 81 | }; 82 | 83 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 84 | const unserialize = (jsonReady: any): Snapshot => { 85 | const snap: Snapshot = {}; 86 | for (const key in jsonReady) { 87 | if (jsonReady[key].__isSnap) { 88 | snap[key] = new Snap( 89 | jsonReady[key].hash, 90 | Buffer.from(jsonReady[key].data, 'base64') 91 | ); 92 | } else { 93 | snap[key] = unserialize(jsonReady[key]); 94 | } 95 | } 96 | return snap; 97 | }; 98 | 99 | export const cacheModuleState = async (dir: string, cachePath: string, key: string): Promise => { 100 | const snap = await takeSnapshot(dir); 101 | 102 | const moduleBuffer = Buffer.from(JSON.stringify(serialize(snap))); 103 | const zipped = await new Promise(resolve => zlib.gzip(moduleBuffer, (_, result) => resolve(result))); 104 | await fs.promises.mkdir(cachePath, { recursive: true }); 105 | await promisifiedGracefulFs.writeFile(path.resolve(cachePath, key), zipped); 106 | }; 107 | 108 | type ApplyDiffFunction = (dir: string) => Promise; 109 | 110 | export const lookupModuleState = async (cachePath: string, key: string): Promise => { 111 | if (fs.existsSync(path.resolve(cachePath, key))) { 112 | return async function applyDiff(dir: string): Promise { 113 | const zipped = await promisifiedGracefulFs.readFile(path.resolve(cachePath, key)); 114 | const unzipped: Buffer = await new Promise(resolve => { zlib.gunzip(zipped, (_, result) => resolve(result)); }); 115 | const diff = unserialize(JSON.parse(unzipped.toString())); 116 | await writeSnapshot(diff, dir); 117 | }; 118 | } 119 | return false; 120 | }; 121 | 122 | function dHashTree(tree: HashTree, hash: crypto.Hash): void { 123 | for (const key of Object.keys(tree).sort()) { 124 | hash.update(key); 125 | if (typeof tree[key] === 'string') { 126 | hash.update(tree[key] as string); 127 | } else { 128 | dHashTree(tree[key] as HashTree, hash); 129 | } 130 | } 131 | } 132 | 133 | async function hashDirectory(dir: string, relativeTo?: string): Promise { 134 | relativeTo ??= dir; 135 | d('hashing dir', dir); 136 | const dirTree: HashTree = {}; 137 | await Promise.all((await promisifiedGracefulFs.readdir(dir)).map(async (child) => { 138 | d('found child', child, 'in dir', dir); 139 | // Ignore output directories 140 | if (dir === relativeTo && (child === 'build' || child === 'bin')) return; 141 | // Don't hash nested node_modules 142 | if (child === 'node_modules') return; 143 | 144 | const childPath = path.resolve(dir, child); 145 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 146 | const relative = path.relative(relativeTo!, childPath); 147 | if ((await fs.promises.stat(childPath)).isDirectory()) { 148 | dirTree[relative] = await hashDirectory(childPath, relativeTo); 149 | } else { 150 | dirTree[relative] = crypto.createHash('SHA256').update(await promisifiedGracefulFs.readFile(childPath)).digest('hex'); 151 | } 152 | })); 153 | 154 | return dirTree; 155 | } 156 | 157 | export async function generateCacheKey(opts: CacheOptions): Promise { 158 | const tree = await hashDirectory(opts.modulePath); 159 | const hasher = crypto.createHash('SHA256') 160 | .update(`${ELECTRON_REBUILD_CACHE_ID}`) 161 | .update(path.basename(opts.modulePath)) 162 | .update(opts.ABI) 163 | .update(opts.arch) 164 | .update(opts.platform) 165 | .update(opts.debug ? 'debug' : 'not debug') 166 | .update(opts.headerURL) 167 | .update(opts.electronVersion); 168 | dHashTree(tree, hasher); 169 | const hash = hasher.digest('hex'); 170 | d('calculated hash of', opts.modulePath, 'to be', hash); 171 | return hash; 172 | } 173 | -------------------------------------------------------------------------------- /src/clang-fetcher.ts: -------------------------------------------------------------------------------- 1 | import cp from 'node:child_process'; 2 | import debug from 'debug'; 3 | import fs from 'graceful-fs'; 4 | import path from 'node:path'; 5 | import tar from 'tar'; 6 | import zlib from 'node:zlib'; 7 | import { ELECTRON_GYP_DIR } from './constants.js'; 8 | import { fetch } from './fetcher.js'; 9 | import { downloadLinuxSysroot } from './sysroot-fetcher.js'; 10 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 11 | 12 | const d = debug('electron-rebuild'); 13 | 14 | const CDS_URL = 'https://commondatastorage.googleapis.com/chromium-browser-clang'; 15 | 16 | function getPlatformUrlPrefix(hostOS: string, hostArch: string) { 17 | const prefixMap: Record = { 18 | 'linux': 'Linux_x64', 19 | 'darwin': 'Mac', 20 | 'win32': 'Win', 21 | }; 22 | let prefix = prefixMap[hostOS]; 23 | if (prefix === 'Mac' && hostArch === 'arm64') { 24 | prefix = 'Mac_arm64'; 25 | } 26 | return CDS_URL + '/' + prefix + '/'; 27 | } 28 | 29 | function getClangDownloadURL(packageFile: string, packageVersion: string, hostOS: string, hostArch: string) { 30 | const cdsFile = `${packageFile}-${packageVersion}.tgz`; 31 | return getPlatformUrlPrefix(hostOS, hostArch) + cdsFile; 32 | } 33 | 34 | function getSDKRoot(): string { 35 | if (process.env.SDKROOT) return process.env.SDKROOT; 36 | const output = cp.execFileSync('xcrun', ['--sdk', 'macosx', '--show-sdk-path']); 37 | return output.toString().trim(); 38 | } 39 | 40 | export async function getClangEnvironmentVars(electronVersion: string, targetArch: string): Promise<{ env: Record; args: string[] }> { 41 | const clangDownloadDir = await downloadClangVersion(electronVersion); 42 | 43 | const clangDir = path.resolve(clangDownloadDir, 'bin'); 44 | const clangArgs: string[] = []; 45 | if (process.platform === 'darwin') { 46 | clangArgs.push('-isysroot', getSDKRoot()); 47 | } 48 | 49 | const gypArgs = []; 50 | if (process.platform === 'win32') { 51 | console.log(await promisifiedGracefulFs.readdir(clangDir)); 52 | gypArgs.push(`/p:CLToolExe=clang-cl.exe`, `/p:CLToolPath=${clangDir}`); 53 | } 54 | 55 | if (process.platform === 'linux') { 56 | const sysrootPath = await downloadLinuxSysroot(electronVersion, targetArch); 57 | clangArgs.push('--sysroot', sysrootPath); 58 | } 59 | 60 | return { 61 | env: { 62 | CC: `"${path.resolve(clangDir, 'clang')}" ${clangArgs.join(' ')}`, 63 | CXX: `"${path.resolve(clangDir, 'clang++')}" ${clangArgs.join(' ')}`, 64 | }, 65 | args: gypArgs, 66 | }; 67 | } 68 | 69 | function clangVersionFromRevision(update: string): string | null { 70 | const regex = /CLANG_REVISION = '([^']+)'\nCLANG_SUB_REVISION = (\d+)\n/g; 71 | const clangVersionMatch = regex.exec(update); 72 | if (!clangVersionMatch) return null; 73 | const [,clangVersion, clangSubRevision] = clangVersionMatch; 74 | return `${clangVersion}-${clangSubRevision}`; 75 | } 76 | 77 | function clangVersionFromSVN(update: string): string | null { 78 | const regex = /CLANG_REVISION = '([^']+)'\nCLANG_SVN_REVISION = '([^']+)'\nCLANG_SUB_REVISION = (\d+)\n/g; 79 | const clangVersionMatch = regex.exec(update); 80 | if (!clangVersionMatch) return null; 81 | const [,clangVersion, clangSvn, clangSubRevision] = clangVersionMatch; 82 | return `${clangSvn}-${clangVersion.substr(0, 8)}-${clangSubRevision}`; 83 | } 84 | 85 | async function downloadClangVersion(electronVersion: string) { 86 | d('fetching clang for Electron:', electronVersion); 87 | const clangDirPath = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-clang`); 88 | if (fs.existsSync(path.resolve(clangDirPath, 'bin', 'clang'))) return clangDirPath; 89 | await fs.promises.mkdir(ELECTRON_GYP_DIR, { recursive: true }); 90 | 91 | const electronDeps = await fetch(`https://raw.githubusercontent.com/electron/electron/v${electronVersion}/DEPS`, 'text'); 92 | const chromiumRevisionExtractor = /'chromium_version':\n\s+'([^']+)/g; 93 | const chromiumRevisionMatch = chromiumRevisionExtractor.exec(electronDeps); 94 | if (!chromiumRevisionMatch) throw new Error('Failed to determine Chromium revision for given Electron version'); 95 | const chromiumRevision = chromiumRevisionMatch[1]; 96 | d('fetching clang for Chromium:', chromiumRevision); 97 | 98 | const base64ClangUpdate = await fetch(`https://chromium.googlesource.com/chromium/src.git/+/${chromiumRevision}/tools/clang/scripts/update.py?format=TEXT`, 'text'); 99 | const clangUpdate = Buffer.from(base64ClangUpdate, 'base64').toString('utf8'); 100 | 101 | const clangVersionString = clangVersionFromRevision(clangUpdate) || clangVersionFromSVN(clangUpdate); 102 | if (!clangVersionString) throw new Error('Failed to determine Clang revision from Electron version'); 103 | d('fetching clang:', clangVersionString); 104 | 105 | const clangDownloadURL = getClangDownloadURL('clang', clangVersionString, process.platform, process.arch); 106 | 107 | const contents = await fetch(clangDownloadURL, 'buffer'); 108 | d('deflating clang'); 109 | zlib.deflateSync(contents); 110 | const tarPath = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-clang.tar`); 111 | if (fs.existsSync(tarPath)) await fs.promises.rm(tarPath, { recursive: true, force: true }); 112 | await promisifiedGracefulFs.writeFile(tarPath, Buffer.from(contents)); 113 | await fs.promises.mkdir(clangDirPath, { recursive: true }); 114 | d('tar running on clang'); 115 | await tar.x({ 116 | file: tarPath, 117 | cwd: clangDirPath, 118 | }); 119 | await fs.promises.rm(tarPath, { recursive: true, force: true }); 120 | d('cleaning up clang tar file'); 121 | return clangDirPath; 122 | } 123 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import chalk from 'chalk'; 4 | import fs from 'graceful-fs'; 5 | import path from 'node:path'; 6 | import ora from 'ora'; 7 | import yargs from 'yargs/yargs'; 8 | 9 | import { getProjectRootPath } from './search-module.js'; 10 | import { locateElectronModule } from './electron-locator.js'; 11 | import { ModuleType } from './module-walker.js'; 12 | import { rebuild } from './rebuild.js'; 13 | import { pathToFileURL } from 'node:url'; 14 | 15 | const argv = yargs(process.argv.slice(2)).version(false).options({ 16 | version: { alias: 'v', type: 'string', description: 'The version of Electron to build against' }, 17 | force: { alias: 'f', type: 'boolean', description: 'Force rebuilding modules, even if we would skip it otherwise' }, 18 | arch: { alias: 'a', type: 'string', description: "Override the target architecture to something other than your system's" }, 19 | 'module-dir': { alias: 'm', type: 'string', description: 'The path to the node_modules directory to rebuild' }, 20 | // TODO: should be type: array 21 | 'which-module': { alias: 'w', type: 'string', description: 'A specific module to build, or comma separated list of modules. Modules will only be rebuilt if they also match the types of dependencies being rebuilt (see --types).' }, 22 | // TODO: should be type: array 23 | only: { alias: 'o', type: 'string', description: 'Only build specified module, or comma separated list of modules. All others are ignored.' }, 24 | 'electron-prebuilt-dir': { alias: 'e', type: 'string', description: 'The path to the prebuilt electron module' }, 25 | 'dist-url': { alias: 'd', type: 'string', description: 'Custom header tarball URL' }, 26 | // TODO: should be type: array 27 | types: { alias: 't', type: 'string', description: 'The types of dependencies to rebuild. Comma separated list of "prod", "dev" and "optional". Default is "prod,optional"' }, 28 | parallel: { alias: 'p', type: 'boolean', description: 'Rebuild in parallel, this is enabled by default on macOS and Linux' }, 29 | sequential: { alias: 's', type: 'boolean', description: 'Rebuild modules sequentially, this is enabled by default on Windows' }, 30 | debug: { alias: 'b', type: 'boolean', description: 'Build debug version of modules' }, 31 | 'prebuild-tag-prefix': { type: 'string', description: 'GitHub tag prefix passed to prebuild-install. Default is "v"' }, 32 | 'force-abi': { type: 'number', description: 'Override the ABI version for the version of Electron you are targeting. Only use when targeting Nightly releases.' }, 33 | 'use-electron-clang': { type: 'boolean', description: 'Use the clang executable that Electron used when building its binary. This will guarantee compiler compatibility' }, 34 | 'disable-pre-gyp-copy': { type: 'boolean', description: 'Disables the pre-gyp copy step' }, 35 | 'build-from-source': { type: 'boolean', description: 'Skips prebuild download and rebuilds module from source.'}, 36 | }).usage('Usage: $0 --version [version] --module-dir [path]') 37 | .help() 38 | .alias('help', 'h') 39 | .epilog('Copyright 2016-2021') 40 | .parseSync(); 41 | 42 | if (process.argv.length === 3 && process.argv[2] === '--version') { 43 | try { 44 | console.log( 45 | 'Electron Rebuild Version:', 46 | ( 47 | await import( 48 | pathToFileURL(path.resolve(import.meta.dirname, '../../package.json')).toString(), 49 | { with: { type: 'json' } } 50 | ) 51 | ).default.version, 52 | ); 53 | } catch (err) { 54 | console.log( 55 | 'Electron Rebuild Version:', 56 | ( 57 | await import( 58 | pathToFileURL(path.resolve(import.meta.dirname, '../package.json')).toString(), 59 | { with: { type: 'json' } } 60 | ) 61 | ).default.version, 62 | ); 63 | } 64 | 65 | process.exit(0); 66 | } 67 | 68 | const handler = (err: Error): void => { 69 | console.error(chalk.red('\nAn unhandled error occurred inside electron-rebuild')); 70 | console.error(chalk.red(`${err.message}\n\n${err.stack}`)); 71 | process.exit(-1); 72 | }; 73 | 74 | process.on('uncaughtException', handler); 75 | process.on('unhandledRejection', handler); 76 | 77 | 78 | (async (): Promise => { 79 | const projectRootPath = await getProjectRootPath(process.cwd()); 80 | const electronModulePath = argv.e ? path.resolve(process.cwd(), (argv.e as string)) : await locateElectronModule(projectRootPath); 81 | let electronModuleVersion = argv.v as string; 82 | 83 | if (!electronModuleVersion) { 84 | try { 85 | if (!electronModulePath) throw new Error('Prebuilt electron module not found'); 86 | const pkgJson = await import(pathToFileURL(path.join(electronModulePath, 'package.json')).toString(), { with: { type: 'json' }}); 87 | 88 | electronModuleVersion = pkgJson.default.version; 89 | } catch (e) { 90 | throw new Error(`Unable to find electron's version number, either install it or specify an explicit version`); 91 | } 92 | } 93 | 94 | let rootDirectory = argv.m as string; 95 | 96 | if (!rootDirectory) { 97 | // NB: We assume here that we're going to rebuild the immediate parent's 98 | // node modules, which might not always be the case but it's at least a 99 | // good guess 100 | rootDirectory = path.resolve(import.meta.dirname, '../../..'); 101 | if (!fs.existsSync(rootDirectory) || !fs.existsSync(path.resolve(rootDirectory, 'package.json'))) { 102 | // Then we try the CWD 103 | rootDirectory = process.cwd(); 104 | if (!fs.existsSync(rootDirectory) || !fs.existsSync(path.resolve(rootDirectory, 'package.json'))) { 105 | throw new Error('Unable to find parent node_modules directory, specify it via --module-dir, E.g. "--module-dir ." for the current directory'); 106 | } 107 | } 108 | } else { 109 | rootDirectory = path.resolve(process.cwd(), rootDirectory); 110 | } 111 | 112 | if (argv.forceAbi && typeof argv.forceAbi !== 'number') { 113 | throw new Error('force-abi must be a number'); 114 | } 115 | 116 | let modulesDone = 0; 117 | let moduleTotal = 0; 118 | const rebuildSpinner = ora('Searching dependency tree').start(); 119 | let lastModuleName: string; 120 | 121 | const redraw = (moduleName?: string): void => { 122 | if (moduleName) lastModuleName = moduleName; 123 | 124 | if (argv.p) { 125 | rebuildSpinner.text = `Building modules: ${modulesDone}/${moduleTotal}`; 126 | } else { 127 | rebuildSpinner.text = `Building module: ${lastModuleName}, Completed: ${modulesDone}`; 128 | } 129 | }; 130 | 131 | const rebuilder = rebuild({ 132 | buildPath: rootDirectory, 133 | electronVersion: electronModuleVersion, 134 | arch: (argv.a as string) || process.arch, 135 | extraModules: argv.w ? (argv.w as string).split(',') : [], 136 | onlyModules: argv.o ? (argv.o as string).split(',') : null, 137 | force: argv.f as boolean, 138 | headerURL: argv.d as string, 139 | types: argv.t ? (argv.t as string).split(',') as ModuleType[] : ['prod', 'optional'], 140 | mode: argv.p ? 'parallel' : (argv.s ? 'sequential' : undefined), 141 | debug: argv.debug, 142 | prebuildTagPrefix: (argv.prebuildTagPrefix as string) || 'v', 143 | forceABI: argv.forceAbi as number, 144 | useElectronClang: !!argv.useElectronClang, 145 | disablePreGypCopy: !!argv.disablePreGypCopy, 146 | projectRootPath, 147 | buildFromSource: !!argv.buildFromSource, 148 | }); 149 | 150 | const lifecycle = rebuilder.lifecycle; 151 | 152 | lifecycle.on('module-found', (moduleName: string) => { 153 | moduleTotal += 1; 154 | redraw(moduleName); 155 | }); 156 | 157 | lifecycle.on('module-done', () => { 158 | modulesDone += 1; 159 | redraw(); 160 | }); 161 | 162 | try { 163 | await rebuilder; 164 | } catch (err) { 165 | rebuildSpinner.text = 'Rebuild Failed'; 166 | rebuildSpinner.fail(); 167 | throw err; 168 | } 169 | 170 | rebuildSpinner.text = 'Rebuild Complete'; 171 | rebuildSpinner.succeed(); 172 | })(); 173 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import os from 'node:os'; 2 | import path from 'node:path'; 3 | 4 | export const ELECTRON_GYP_DIR = path.resolve(os.homedir(), '.electron-gyp'); 5 | -------------------------------------------------------------------------------- /src/electron-locator.ts: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import path from 'node:path'; 3 | import { searchForModule } from './search-module.js'; 4 | import { fileURLToPath } from 'node:url'; 5 | 6 | const electronModuleNames = ['electron', 'electron-prebuilt-compile']; 7 | 8 | async function locateModuleByImport(): Promise { 9 | for (const moduleName of electronModuleNames) { 10 | try { 11 | const modulePath = path.resolve(fileURLToPath(import.meta.resolve(path.join(moduleName, 'package.json'))), '..'); 12 | if (fs.existsSync(path.join(modulePath, 'package.json'))) { 13 | return modulePath; 14 | } 15 | } catch { // eslint-disable-line no-empty 16 | } 17 | } 18 | 19 | return null; 20 | } 21 | 22 | export async function locateElectronModule( 23 | projectRootPath: string | undefined = undefined, 24 | startDir: string | undefined = undefined, 25 | ): Promise { 26 | startDir ??= process.cwd(); 27 | 28 | for (const moduleName of electronModuleNames) { 29 | const electronPaths = await searchForModule(startDir, moduleName, projectRootPath); 30 | const electronPath = electronPaths.find((ePath: string) => fs.existsSync(path.join(ePath, 'package.json'))); 31 | 32 | if (electronPath) { 33 | return electronPath; 34 | } 35 | } 36 | 37 | return locateModuleByImport(); 38 | } 39 | -------------------------------------------------------------------------------- /src/fetcher.ts: -------------------------------------------------------------------------------- 1 | import { setTimeout as sleep } from 'node:timers/promises'; 2 | 3 | import debug from 'debug'; 4 | import got from 'got'; 5 | 6 | const d = debug('electron-rebuild'); 7 | 8 | export async function fetch(url: string, responseType: T, retries = 3): Promise { 9 | if (retries === 0) throw new Error('Failed to fetch a clang resource, run with DEBUG=electron-rebuild for more information'); 10 | d('downloading:', url); 11 | try { 12 | const response = await got.default.get(url, { 13 | responseType, 14 | }); 15 | if (response.statusCode !== 200) { 16 | d('got bad status code:', response.statusCode); 17 | await sleep(2000); 18 | return fetch(url, responseType, retries - 1); 19 | } 20 | d('response came back OK'); 21 | return response.body as RT; 22 | } catch (err) { 23 | d('request failed for some reason', err); 24 | await sleep(2000); 25 | return fetch(url, responseType, retries - 1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { rebuild, RebuildOptions } from './rebuild.js'; 2 | 3 | export { rebuild, RebuildOptions }; 4 | -------------------------------------------------------------------------------- /src/module-rebuilder.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | 5 | import { cacheModuleState } from './cache.js'; 6 | import { NodeGyp } from './module-type/node-gyp/node-gyp.js'; 7 | import { Prebuildify } from './module-type/prebuildify.js'; 8 | import { PrebuildInstall } from './module-type/prebuild-install.js'; 9 | import { NodePreGyp } from './module-type/node-pre-gyp.js'; 10 | import { IRebuilder } from './types.js'; 11 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 12 | 13 | const d = debug('electron-rebuild'); 14 | 15 | export class ModuleRebuilder { 16 | private modulePath: string; 17 | private nodeGyp: NodeGyp; 18 | private rebuilder: IRebuilder; 19 | private prebuildify: Prebuildify; 20 | private prebuildInstall: PrebuildInstall; 21 | private nodePreGyp: NodePreGyp; 22 | 23 | constructor(rebuilder: IRebuilder, modulePath: string) { 24 | this.modulePath = modulePath; 25 | this.rebuilder = rebuilder; 26 | 27 | this.nodeGyp = new NodeGyp(rebuilder, modulePath); 28 | this.prebuildify = new Prebuildify(rebuilder, modulePath); 29 | this.prebuildInstall = new PrebuildInstall(rebuilder, modulePath); 30 | this.nodePreGyp = new NodePreGyp(rebuilder, modulePath); 31 | } 32 | 33 | get metaPath(): string { 34 | return path.resolve(this.modulePath, 'build', this.rebuilder.buildType, '.forge-meta'); 35 | } 36 | 37 | get metaData(): string { 38 | return `${this.rebuilder.arch}--${this.rebuilder.ABI}`; 39 | } 40 | 41 | async alreadyBuiltByRebuild(): Promise { 42 | if (fs.existsSync(this.metaPath)) { 43 | const meta = await promisifiedGracefulFs.readFile(this.metaPath, 'utf8'); 44 | return meta === this.metaData; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | async cacheModuleState(cacheKey: string): Promise { 51 | if (this.rebuilder.useCache) { 52 | await cacheModuleState(this.modulePath, this.rebuilder.cachePath, cacheKey); 53 | } 54 | } 55 | 56 | /** 57 | * Whether a prebuild-install-generated native module exists. 58 | */ 59 | async prebuildInstallNativeModuleExists(): Promise { 60 | return this.prebuildInstall.prebuiltModuleExists(); 61 | } 62 | 63 | /** 64 | * If the native module uses prebuildify, check to see if it comes with a prebuilt module for 65 | * the given platform and arch. 66 | */ 67 | async findPrebuildifyModule(cacheKey: string): Promise { 68 | if (await this.prebuildify.usesTool()) { 69 | d(`assuming is prebuildify powered: ${this.prebuildify.moduleName}`); 70 | 71 | if (await this.prebuildify.findPrebuiltModule()) { 72 | await this.writeMetadata(); 73 | await this.cacheModuleState(cacheKey); 74 | return true; 75 | } 76 | } 77 | 78 | return false; 79 | } 80 | 81 | async findPrebuildInstallModule(cacheKey: string): Promise { 82 | if (await this.prebuildInstall.usesTool()) { 83 | d(`assuming is prebuild-install powered: ${this.prebuildInstall.moduleName}`); 84 | 85 | if (await this.prebuildInstall.findPrebuiltModule()) { 86 | d('installed prebuilt module:', this.prebuildInstall.moduleName); 87 | await this.writeMetadata(); 88 | await this.cacheModuleState(cacheKey); 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | 96 | async findNodePreGypInstallModule(cacheKey: string): Promise { 97 | if (await this.nodePreGyp.usesTool()) { 98 | d(`assuming is node-pre-gyp powered: ${this.nodePreGyp.moduleName}`); 99 | 100 | if (await this.nodePreGyp.findPrebuiltModule()) { 101 | d('installed prebuilt module:', this.nodePreGyp.moduleName); 102 | await this.writeMetadata(); 103 | await this.cacheModuleState(cacheKey); 104 | return true; 105 | } 106 | } 107 | 108 | return false; 109 | } 110 | 111 | async rebuildNodeGypModule(cacheKey: string): Promise { 112 | await this.nodeGyp.rebuildModule(); 113 | d('built via node-gyp:', this.nodeGyp.moduleName); 114 | await this.writeMetadata(); 115 | await this.replaceExistingNativeModule(); 116 | await this.cacheModuleState(cacheKey); 117 | return true; 118 | } 119 | 120 | async replaceExistingNativeModule(): Promise { 121 | const buildLocation = path.resolve(this.modulePath, 'build', this.rebuilder.buildType); 122 | 123 | d('searching for .node file', buildLocation); 124 | const buildLocationFiles = await promisifiedGracefulFs.readdir(buildLocation); 125 | d('testing files', buildLocationFiles); 126 | 127 | const nodeFile = buildLocationFiles.find((file) => file !== '.node' && file.endsWith('.node')); 128 | const nodePath = nodeFile ? path.resolve(buildLocation, nodeFile) : undefined; 129 | 130 | if (nodePath && fs.existsSync(nodePath)) { 131 | d('found .node file', nodePath); 132 | if (!this.rebuilder.disablePreGypCopy) { 133 | const abiPath = path.resolve(this.modulePath, `bin/${this.rebuilder.platform}-${this.rebuilder.arch}-${this.rebuilder.ABI}`); 134 | d('copying to prebuilt place:', abiPath); 135 | await fs.promises.mkdir(abiPath, { recursive: true }); 136 | await promisifiedGracefulFs.copyFile(nodePath, path.join(abiPath, `${this.nodeGyp.moduleName}.node`)); 137 | } 138 | } 139 | } 140 | 141 | async writeMetadata(): Promise { 142 | await fs.promises.mkdir(path.dirname(this.metaPath), { recursive: true }); 143 | await promisifiedGracefulFs.writeFile(this.metaPath, this.metaData); 144 | } 145 | 146 | async rebuild(cacheKey: string): Promise { 147 | if ( 148 | !this.rebuilder.buildFromSource && ( 149 | (await this.findPrebuildifyModule(cacheKey)) || 150 | (await this.findPrebuildInstallModule(cacheKey)) || 151 | (await this.findNodePreGypInstallModule(cacheKey))) 152 | ) { 153 | return true; 154 | } 155 | 156 | return await this.rebuildNodeGypModule(cacheKey); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/module-type/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import path from 'node:path'; 3 | 4 | import { NodeAPI } from '../node-api.js'; 5 | import { readPackageJson } from '../read-package-json.js'; 6 | import { IRebuilder } from '../types.js'; 7 | 8 | type PackageJSONValue = string | Record; 9 | 10 | export class NativeModule { 11 | protected rebuilder: IRebuilder; 12 | private _moduleName: string | undefined; 13 | protected modulePath: string; 14 | public nodeAPI: NodeAPI; 15 | private packageJSON!: Record; 16 | 17 | constructor(rebuilder: IRebuilder, modulePath: string) { 18 | this.rebuilder = rebuilder; 19 | this.modulePath = modulePath; 20 | this.nodeAPI = new NodeAPI(this.moduleName, this.rebuilder.electronVersion); 21 | } 22 | 23 | get moduleName(): string { 24 | if (!this._moduleName) { 25 | const basename = path.basename(this.modulePath); 26 | const parentDir = path.basename(path.dirname(this.modulePath)); 27 | if (parentDir.startsWith('@')) { 28 | this._moduleName = `${parentDir}/${basename}`; 29 | } 30 | 31 | this._moduleName = basename; 32 | } 33 | 34 | return this._moduleName; 35 | } 36 | 37 | async packageJSONFieldWithDefault(key: string, defaultValue: PackageJSONValue): Promise { 38 | const result = await this.packageJSONField(key); 39 | return result === undefined ? defaultValue : result; 40 | } 41 | 42 | async packageJSONField(key: string): Promise { 43 | this.packageJSON ||= await readPackageJson(this.modulePath); 44 | 45 | return this.packageJSON[key]; 46 | } 47 | 48 | async getSupportedNapiVersions(): Promise { 49 | const binary = (await this.packageJSONFieldWithDefault( 50 | 'binary', 51 | {} 52 | )) as Record; 53 | 54 | return binary?.napi_versions; 55 | } 56 | 57 | /** 58 | * Search dependencies for package using either `packageName` or 59 | * `@namespace/packageName` in the case of forks. 60 | */ 61 | async findPackageInDependencies(packageName: string, packageProperty = 'dependencies'): Promise { 62 | const dependencies = await this.packageJSONFieldWithDefault(packageProperty, {}); 63 | if (typeof dependencies !== 'object') return null; 64 | 65 | // Look for direct dependency match 66 | // eslint-disable-next-line no-prototype-builtins 67 | if (dependencies.hasOwnProperty(packageName)) return packageName; 68 | 69 | const forkedPackage = Object.keys(dependencies).find(dependency => 70 | dependency.startsWith('@') && dependency.endsWith(`/${packageName}`)); 71 | 72 | return forkedPackage || null; 73 | } 74 | } 75 | 76 | export async function locateBinary(basePath: string, suffix: string): Promise { 77 | let parentPath = basePath; 78 | let testPath: string | undefined; 79 | 80 | while (testPath !== parentPath) { 81 | testPath = parentPath; 82 | const checkPath = path.resolve(testPath, suffix); 83 | if (fs.existsSync(checkPath)) { 84 | return checkPath; 85 | } 86 | parentPath = path.resolve(testPath, '..'); 87 | } 88 | 89 | return null; 90 | } 91 | -------------------------------------------------------------------------------- /src/module-type/node-gyp/node-gyp.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import detectLibc from 'detect-libc'; 3 | import path from 'node:path'; 4 | import semver from 'semver'; 5 | 6 | import { ELECTRON_GYP_DIR } from '../../constants.js'; 7 | import { getClangEnvironmentVars } from '../../clang-fetcher.js'; 8 | import { NativeModule } from '../index.js'; 9 | import { fork } from 'node:child_process'; 10 | 11 | const d = debug('electron-rebuild'); 12 | 13 | export class NodeGyp extends NativeModule { 14 | async buildArgs(prefixedArgs: string[]): Promise { 15 | const args = [ 16 | 'node', 17 | 'node-gyp', 18 | 'rebuild', 19 | ...prefixedArgs, 20 | `--runtime=electron`, 21 | `--target=${this.rebuilder.electronVersion}`, 22 | `--arch=${this.rebuilder.arch}`, 23 | `--dist-url=${this.rebuilder.headerURL}`, 24 | '--build-from-source' 25 | ]; 26 | 27 | args.push(d.enabled ? '--verbose' : '--silent'); 28 | 29 | if (this.rebuilder.debug) { 30 | args.push('--debug'); 31 | } 32 | 33 | args.push(...(await this.buildArgsFromBinaryField())); 34 | 35 | if (this.rebuilder.msvsVersion) { 36 | args.push(`--msvs_version=${this.rebuilder.msvsVersion}`); 37 | } 38 | 39 | // Headers of old Electron versions do not have a valid config.gypi file 40 | // and --force-process-config must be passed to node-gyp >= 8.4.0 to 41 | // correctly build modules for them. 42 | // See also https://github.com/nodejs/node-gyp/pull/2497 43 | if (!semver.satisfies(this.rebuilder.electronVersion, '^14.2.0 || ^15.3.0') && semver.major(this.rebuilder.electronVersion) < 16) { 44 | args.push('--force-process-config'); 45 | } 46 | 47 | return args; 48 | } 49 | 50 | async buildArgsFromBinaryField(): Promise { 51 | const binary = await this.packageJSONFieldWithDefault('binary', {}) as Record; 52 | let napiBuildVersion: number | undefined = undefined; 53 | if (Array.isArray(binary.napi_versions)) { 54 | napiBuildVersion = this.nodeAPI.getNapiVersion(binary.napi_versions.map(str => Number(str))); 55 | } 56 | const flags = await Promise.all(Object.entries(binary).map(async ([binaryKey, binaryValue]) => { 57 | if (binaryKey === 'napi_versions') { 58 | return; 59 | } 60 | 61 | let value = binaryValue; 62 | 63 | if (binaryKey === 'module_path') { 64 | value = path.resolve(this.modulePath, value); 65 | } 66 | 67 | value = value.replace('{configuration}', this.rebuilder.buildType) 68 | .replace('{node_abi}', `electron-v${this.rebuilder.electronVersion.split('.').slice(0, 2).join('.')}`) 69 | .replace('{platform}', this.rebuilder.platform) 70 | .replace('{arch}', this.rebuilder.arch) 71 | .replace('{version}', await this.packageJSONField('version') as string) 72 | .replace('{libc}', await detectLibc.family() || 'unknown'); 73 | if (napiBuildVersion !== undefined) { 74 | value = value.replace('{napi_build_version}', napiBuildVersion.toString()); 75 | } 76 | for (const [replaceKey, replaceValue] of Object.entries(binary)) { 77 | value = value.replace(`{${replaceKey}}`, replaceValue); 78 | } 79 | 80 | return `--${binaryKey}=${value}`; 81 | })); 82 | 83 | return flags.filter(value => value) as string[]; 84 | } 85 | 86 | async rebuildModule(): Promise { 87 | if (this.rebuilder.platform !== process.platform) { 88 | throw new Error("node-gyp does not support cross-compiling native modules from source."); 89 | } 90 | 91 | if (this.modulePath.includes(' ')) { 92 | console.error('Attempting to build a module with a space in the path'); 93 | console.error('See https://github.com/nodejs/node-gyp/issues/65#issuecomment-368820565 for reasons why this may not work'); 94 | // FIXME: Re-enable the throw when more research has been done 95 | // throw new Error(`node-gyp does not support building modules with spaces in their path, tried to build: ${modulePath}`); 96 | } 97 | 98 | const env = { 99 | ...process.env, 100 | }; 101 | const extraNodeGypArgs: string[] = []; 102 | 103 | if (this.rebuilder.useElectronClang) { 104 | const { env: clangEnv, args: clangArgs } = await getClangEnvironmentVars(this.rebuilder.electronVersion, this.rebuilder.arch); 105 | Object.assign(env, clangEnv); 106 | extraNodeGypArgs.push(...clangArgs); 107 | } 108 | 109 | const nodeGypArgs = await this.buildArgs(extraNodeGypArgs); 110 | d('rebuilding', this.moduleName, 'with args', nodeGypArgs); 111 | 112 | const forkedChild = fork(path.resolve(import.meta.dirname, 'worker.js'), { 113 | env, 114 | cwd: this.modulePath, 115 | stdio: 'pipe', 116 | }); 117 | const outputBuffers: Buffer[] = []; 118 | forkedChild.stdout?.on('data', (chunk) => { 119 | outputBuffers.push(chunk); 120 | }); 121 | forkedChild.stderr?.on('data', (chunk) => { 122 | outputBuffers.push(chunk); 123 | }); 124 | forkedChild.send({ 125 | moduleName: this.moduleName, 126 | nodeGypArgs, 127 | extraNodeGypArgs, 128 | devDir: this.rebuilder.mode === 'sequential' ? ELECTRON_GYP_DIR : path.resolve(ELECTRON_GYP_DIR, '_p', this.moduleName), 129 | }); 130 | 131 | await new Promise((resolve, reject) => { 132 | forkedChild.on('exit', (code) => { 133 | if (code === 0) return resolve(); 134 | console.error(Buffer.concat(outputBuffers).toString()); 135 | reject(new Error(`node-gyp failed to rebuild '${this.modulePath}'`)); 136 | }); 137 | }); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/module-type/node-gyp/worker.ts: -------------------------------------------------------------------------------- 1 | import NodeGypRunner from 'node-gyp'; 2 | 3 | process.on('message', async ({ 4 | nodeGypArgs, 5 | devDir, 6 | extraNodeGypArgs, 7 | }) => { 8 | const nodeGyp = NodeGypRunner(); 9 | nodeGyp.parseArgv(nodeGypArgs); 10 | nodeGyp.devDir = devDir; 11 | let command = nodeGyp.todo.shift(); 12 | try { 13 | while (command) { 14 | if (command.name === 'configure') { 15 | command.args = command.args.filter((arg: string) => !extraNodeGypArgs.includes(arg)); 16 | } else if (command.name === 'build' && process.platform === 'win32') { 17 | // This is disgusting but it prevents node-gyp from destroying our MSBuild arguments 18 | command.args.map = (fn: (arg: string) => string) => { 19 | return Array.prototype.map.call(command.args, (arg: string) => { 20 | if (arg.startsWith('/p:')) return arg; 21 | return fn(arg); 22 | }); 23 | }; 24 | } 25 | await nodeGyp.commands[command.name](command.args); 26 | command = nodeGyp.todo.shift(); 27 | } 28 | process.exit(0); 29 | } catch (err) { 30 | console.error(err); 31 | process.exit(1); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /src/module-type/node-pre-gyp.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { spawn } from '@malept/cross-spawn-promise'; 3 | import { readBinaryFileArch } from 'read-binary-file-arch'; 4 | 5 | import { locateBinary, NativeModule } from './index.js'; 6 | const d = debug('electron-rebuild'); 7 | 8 | export class NodePreGyp extends NativeModule { 9 | async usesTool(): Promise { 10 | const packageName = await this.findPackageInDependencies('node-pre-gyp'); 11 | return !!packageName; 12 | } 13 | 14 | async locateBinary(): Promise { 15 | const packageName = await this.findPackageInDependencies('node-pre-gyp'); 16 | if (!packageName) return null; 17 | return locateBinary( 18 | this.modulePath, 19 | `node_modules/${packageName}/bin/node-pre-gyp` 20 | ); 21 | } 22 | 23 | async run(nodePreGypPath: string): Promise { 24 | const redownloadBinary = await this.shouldUpdateBinary(nodePreGypPath); 25 | 26 | await spawn( 27 | process.execPath, 28 | [ 29 | nodePreGypPath, 30 | 'reinstall', 31 | '--fallback-to-build', 32 | ...(redownloadBinary ? ['--update-binary'] : []), 33 | `--arch=${this.rebuilder.arch}`, // fallback build arch 34 | `--target_arch=${this.rebuilder.arch}`, // prebuild arch 35 | `--target_platform=${this.rebuilder.platform}`, 36 | ...await this.getNodePreGypRuntimeArgs(), 37 | ], 38 | { 39 | cwd: this.modulePath, 40 | } 41 | ); 42 | } 43 | 44 | async findPrebuiltModule(): Promise { 45 | const nodePreGypPath = await this.locateBinary(); 46 | if (nodePreGypPath) { 47 | d(`triggering prebuild download step: ${this.moduleName}`); 48 | try { 49 | await this.run(nodePreGypPath); 50 | return true; 51 | } catch (err) { 52 | d('failed to use node-pre-gyp:', err); 53 | 54 | if ((err as Error)?.message?.includes('requires Node-API but Electron')) { 55 | throw err; 56 | } 57 | } 58 | } else { 59 | d(`could not find node-pre-gyp relative to: ${this.modulePath}`); 60 | } 61 | 62 | return false; 63 | } 64 | 65 | async getNodePreGypRuntimeArgs(): Promise { 66 | const moduleNapiVersions = await this.getSupportedNapiVersions(); 67 | if (moduleNapiVersions) { 68 | return []; 69 | } else { 70 | return [ 71 | '--runtime=electron', 72 | `--target=${this.rebuilder.electronVersion}`, 73 | `--dist-url=${this.rebuilder.headerURL}`, 74 | ]; 75 | } 76 | } 77 | 78 | private async shouldUpdateBinary(nodePreGypPath: string) { 79 | let shouldUpdate = false; 80 | 81 | // Redownload binary only if the existing module arch differs from the 82 | // target arch. 83 | try { 84 | const modulePaths = await this.getModulePaths(nodePreGypPath); 85 | d('module paths:', modulePaths); 86 | for (const modulePath of modulePaths) { 87 | let moduleArch; 88 | try { 89 | moduleArch = await readBinaryFileArch(modulePath); 90 | d('module arch:', moduleArch); 91 | } catch (error) { 92 | d('failed to read module arch:', (error as Error).message); 93 | continue; 94 | } 95 | 96 | if (moduleArch && moduleArch !== this.rebuilder.arch) { 97 | shouldUpdate = true; 98 | d('module architecture differs:', `${moduleArch} !== ${this.rebuilder.arch}`); 99 | break; 100 | } 101 | } 102 | } catch (error) { 103 | d('failed to get existing binary arch:', (error as Error).message); 104 | 105 | // Assume architecture differs 106 | shouldUpdate = true; 107 | } 108 | 109 | return shouldUpdate; 110 | } 111 | 112 | private async getModulePaths(nodePreGypPath: string): Promise { 113 | const results = await spawn(process.execPath, [ 114 | nodePreGypPath, 115 | 'reveal', 116 | 'module', // pick property with module path 117 | `--target_arch=${this.rebuilder.arch}`, 118 | `--target_platform=${this.rebuilder.platform}`, 119 | ], { 120 | cwd: this.modulePath, 121 | }); 122 | 123 | // Packages with multiple binaries will output one per line 124 | return results.split('\n').filter(Boolean); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/module-type/prebuild-install.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | import { spawn } from '@malept/cross-spawn-promise'; 5 | 6 | import { locateBinary, NativeModule } from './index.js'; 7 | const d = debug('electron-rebuild'); 8 | 9 | export class PrebuildInstall extends NativeModule { 10 | async usesTool(): Promise { 11 | const packageName = await this.findPackageInDependencies('prebuild-install'); 12 | return !!packageName; 13 | } 14 | 15 | async locateBinary(): Promise { 16 | const packageName = await this.findPackageInDependencies('prebuild-install'); 17 | if (!packageName) return null; 18 | return locateBinary( 19 | this.modulePath, 20 | `node_modules/${packageName}/bin.js` 21 | ); 22 | } 23 | 24 | async run(prebuildInstallPath: string): Promise { 25 | await spawn( 26 | process.execPath, 27 | [ 28 | path.resolve(import.meta.dirname, '..', `prebuild-shim.js`), 29 | prebuildInstallPath, 30 | `--arch=${this.rebuilder.arch}`, 31 | `--platform=${this.rebuilder.platform}`, 32 | `--tag-prefix=${this.rebuilder.prebuildTagPrefix}`, 33 | ...await this.getPrebuildInstallRuntimeArgs(), 34 | ], 35 | { 36 | cwd: this.modulePath, 37 | } 38 | ); 39 | } 40 | 41 | async findPrebuiltModule(): Promise { 42 | const prebuildInstallPath = await this.locateBinary(); 43 | if (prebuildInstallPath) { 44 | d(`triggering prebuild download step: ${this.moduleName}`); 45 | try { 46 | await this.run(prebuildInstallPath); 47 | return true; 48 | } catch (err) { 49 | d('failed to use prebuild-install:', err); 50 | 51 | if ((err as Error)?.message?.includes('requires Node-API but Electron')) { 52 | throw err; 53 | } 54 | } 55 | } else { 56 | d(`could not find prebuild-install relative to: ${this.modulePath}`); 57 | } 58 | 59 | return false; 60 | } 61 | 62 | /** 63 | * Whether a prebuild-install-based native module exists. 64 | */ 65 | async prebuiltModuleExists(): Promise { 66 | return fs.existsSync(path.resolve(this.modulePath, 'prebuilds', `${this.rebuilder.platform}-${this.rebuilder.arch}`, `electron-${this.rebuilder.ABI}.node`)); 67 | } 68 | 69 | async getPrebuildInstallRuntimeArgs(): Promise { 70 | const moduleNapiVersions = await this.getSupportedNapiVersions(); 71 | if (moduleNapiVersions) { 72 | const napiVersion = this.nodeAPI.getNapiVersion(moduleNapiVersions); 73 | return [ 74 | '--runtime=napi', 75 | `--target=${napiVersion}`, 76 | ]; 77 | } else { 78 | return [ 79 | '--runtime=electron', 80 | `--target=${this.rebuilder.electronVersion}`, 81 | ]; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/module-type/prebuildify.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | 5 | import { ConfigVariables, getNodeArch } from '../arch.js'; 6 | import { NativeModule } from './index.js'; 7 | 8 | const d = debug('electron-rebuild'); 9 | 10 | export function determineNativePrebuildArch(arch: string): string { 11 | if (arch === 'armv7l') { 12 | return 'arm'; 13 | } 14 | 15 | return arch; 16 | } 17 | 18 | /** 19 | * The extension of `prebuildify`-generated native modules, after the last `.`. This value differs 20 | * based on whether the target arch is ARM-based. 21 | */ 22 | export function determineNativePrebuildExtension(arch: string): string { 23 | switch (arch) { 24 | case 'arm64': 25 | return 'armv8.node'; 26 | case 'armv7l': 27 | return 'armv7.node'; 28 | } 29 | 30 | return 'node'; 31 | } 32 | 33 | export class Prebuildify extends NativeModule { 34 | async usesTool(): Promise { 35 | const packageName = await this.findPackageInDependencies('prebuildify', 'devDependencies'); 36 | return !!packageName; 37 | } 38 | 39 | async findPrebuiltModule(): Promise { 40 | d(`Checking for prebuilds for "${this.moduleName}"`); 41 | 42 | const prebuildsDir = path.join(this.modulePath, 'prebuilds'); 43 | if (!(fs.existsSync(prebuildsDir))) { 44 | d(`Could not find the prebuilds directory at "${prebuildsDir}"`); 45 | return false; 46 | } 47 | 48 | const nodeArch = getNodeArch(this.rebuilder.arch, process.config.variables as ConfigVariables); 49 | const prebuiltModuleDir = path.join(prebuildsDir, `${this.rebuilder.platform}-${determineNativePrebuildArch(nodeArch)}`); 50 | const nativeExt = determineNativePrebuildExtension(nodeArch); 51 | const electronNapiModuleFilename = path.join(prebuiltModuleDir, `electron.napi.${nativeExt}`); 52 | const nodejsNapiModuleFilename = path.join(prebuiltModuleDir, `node.napi.${nativeExt}`); 53 | const abiModuleFilename = path.join(prebuiltModuleDir, `electron.abi${this.rebuilder.ABI}.${nativeExt}`); 54 | 55 | if (fs.existsSync(electronNapiModuleFilename) || fs.existsSync(nodejsNapiModuleFilename)) { 56 | this.nodeAPI.ensureElectronSupport(); 57 | d(`Found prebuilt Node-API module in ${prebuiltModuleDir}"`); 58 | } else if (fs.existsSync(abiModuleFilename)) { 59 | d(`Found prebuilt module: "${abiModuleFilename}"`); 60 | } else { 61 | d(`Could not locate "${electronNapiModuleFilename}", "${nodejsNapiModuleFilename}", or "${abiModuleFilename}"`); 62 | return false; 63 | } 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/module-walker.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | 5 | import { readPackageJson } from './read-package-json.js'; 6 | import { searchForModule, searchForNodeModules } from './search-module.js'; 7 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 8 | 9 | const d = debug('electron-rebuild'); 10 | 11 | export type ModuleType = 'prod' | 'dev' | 'optional'; 12 | 13 | export class ModuleWalker { 14 | buildPath: string; 15 | modulesToRebuild: string[]; 16 | onlyModules: string[] | null; 17 | prodDeps: Set; 18 | projectRootPath?: string; 19 | realModulePaths: Set; 20 | realNodeModulesPaths: Set; 21 | types: ModuleType[]; 22 | 23 | constructor(buildPath: string, projectRootPath: string | undefined, types: ModuleType[], prodDeps: Set, onlyModules: string[] | null) { 24 | this.buildPath = buildPath; 25 | this.modulesToRebuild = []; 26 | this.projectRootPath = projectRootPath; 27 | this.types = types; 28 | this.prodDeps = prodDeps; 29 | this.onlyModules = onlyModules; 30 | this.realModulePaths = new Set(); 31 | this.realNodeModulesPaths = new Set(); 32 | } 33 | 34 | get nodeModulesPaths(): Promise { 35 | return searchForNodeModules( 36 | this.buildPath, 37 | this.projectRootPath 38 | ); 39 | } 40 | 41 | async walkModules(): Promise { 42 | const rootPackageJson = await readPackageJson(this.buildPath); 43 | const markWaiters: Promise[] = []; 44 | const depKeys = []; 45 | 46 | if (this.types.includes('prod') || this.onlyModules) { 47 | depKeys.push(...Object.keys(rootPackageJson.dependencies || {})); 48 | } 49 | if (this.types.includes('optional') || this.onlyModules) { 50 | depKeys.push(...Object.keys(rootPackageJson.optionalDependencies || {})); 51 | } 52 | if (this.types.includes('dev') || this.onlyModules) { 53 | depKeys.push(...Object.keys(rootPackageJson.devDependencies || {})); 54 | } 55 | 56 | for (const key of depKeys) { 57 | this.prodDeps.add(key); 58 | const modulePaths: string[] = await searchForModule( 59 | this.buildPath, 60 | key, 61 | this.projectRootPath 62 | ); 63 | for (const modulePath of modulePaths) { 64 | markWaiters.push(this.markChildrenAsProdDeps(modulePath)); 65 | } 66 | } 67 | 68 | await Promise.all(markWaiters); 69 | 70 | d('identified prod deps:', this.prodDeps); 71 | } 72 | 73 | async findModule(moduleName: string, fromDir: string, foundFn: ((p: string) => Promise)): Promise { 74 | 75 | const testPaths = await searchForModule( 76 | fromDir, 77 | moduleName, 78 | this.projectRootPath 79 | ); 80 | const foundFns = testPaths.map(testPath => foundFn(testPath)); 81 | 82 | return Promise.all(foundFns); 83 | } 84 | 85 | async markChildrenAsProdDeps(modulePath: string): Promise { 86 | if (!fs.existsSync(modulePath)) { 87 | return; 88 | } 89 | 90 | d('exploring', modulePath); 91 | let childPackageJson; 92 | try { 93 | childPackageJson = await readPackageJson(modulePath, true); 94 | } catch (err) { 95 | return; 96 | } 97 | const moduleWait: Promise[] = []; 98 | 99 | const callback = this.markChildrenAsProdDeps.bind(this); 100 | for (const key of Object.keys(childPackageJson.dependencies || {}).concat(Object.keys(childPackageJson.optionalDependencies || {}))) { 101 | if (this.prodDeps.has(key)) { 102 | continue; 103 | } 104 | 105 | this.prodDeps.add(key); 106 | 107 | moduleWait.push(this.findModule(key, modulePath, callback)); 108 | } 109 | 110 | await Promise.all(moduleWait); 111 | } 112 | 113 | async findAllModulesIn(nodeModulesPath: string, prefix = ''): Promise { 114 | // Some package managers use symbolic links when installing node modules 115 | // we need to be sure we've never tested the a package before by resolving 116 | // all symlinks in the path and testing against a set 117 | const realNodeModulesPath = await fs.promises.realpath(nodeModulesPath); 118 | if (this.realNodeModulesPaths.has(realNodeModulesPath)) { 119 | return; 120 | } 121 | this.realNodeModulesPaths.add(realNodeModulesPath); 122 | 123 | d('scanning:', realNodeModulesPath); 124 | 125 | for (const modulePath of await promisifiedGracefulFs.readdir(realNodeModulesPath)) { 126 | // Ignore the magical .bin directory 127 | if (modulePath === '.bin') continue; 128 | 129 | const subPath = path.resolve(nodeModulesPath, modulePath); 130 | 131 | // Ensure that we don't mark modules as needing to be rebuilt more than once 132 | // by ignoring / resolving symlinks 133 | let realPath: string; 134 | try { 135 | realPath = await fs.promises.realpath(subPath); 136 | } catch (error) { 137 | // pnpm leaves dangling symlinks when modules are removed 138 | if ((error as NodeJS.ErrnoException).code === 'ENOENT') { 139 | const stat = await fs.promises.lstat(subPath); 140 | if (stat.isSymbolicLink()) { 141 | continue; 142 | } 143 | } 144 | throw error; 145 | } 146 | 147 | if (this.realModulePaths.has(realPath)) { 148 | continue; 149 | } 150 | this.realModulePaths.add(realPath); 151 | 152 | if (this.prodDeps.has(`${prefix}${modulePath}`) && (!this.onlyModules || this.onlyModules.includes(modulePath))) { 153 | this.modulesToRebuild.push(realPath); 154 | } 155 | 156 | if (modulePath.startsWith('@')) { 157 | await this.findAllModulesIn(realPath, `${modulePath}/`); 158 | } 159 | 160 | if (fs.existsSync(path.resolve(nodeModulesPath, modulePath, 'node_modules'))) { 161 | await this.findAllModulesIn(path.resolve(realPath, 'node_modules')); 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/node-api.ts: -------------------------------------------------------------------------------- 1 | import { fromElectronVersion as napiVersionFromElectronVersion } from 'node-api-version'; 2 | 3 | export class NodeAPI { 4 | private moduleName: string; 5 | private electronVersion: string; 6 | 7 | constructor(moduleName: string, electronVersion: string) { 8 | this.moduleName = moduleName; 9 | this.electronVersion = electronVersion; 10 | } 11 | 12 | ensureElectronSupport(): void { 13 | this.getVersionForElectron(); 14 | } 15 | 16 | getVersionForElectron(): number { 17 | const electronNapiVersion = napiVersionFromElectronVersion(this.electronVersion); 18 | 19 | if (!electronNapiVersion) { 20 | throw new Error(`Native module '${this.moduleName}' requires Node-API but Electron v${this.electronVersion} does not support Node-API`); 21 | } 22 | 23 | return electronNapiVersion; 24 | } 25 | 26 | getNapiVersion(moduleNapiVersions: number[]): number { 27 | const electronNapiVersion = this.getVersionForElectron(); 28 | 29 | // Filter out Node-API versions that are too high 30 | const filteredVersions = moduleNapiVersions.filter((v) => (v <= electronNapiVersion)); 31 | 32 | if (filteredVersions.length === 0) { 33 | throw new Error(`Native module '${this.moduleName}' supports Node-API versions ${moduleNapiVersions} but Electron v${this.electronVersion} only supports Node-API v${electronNapiVersion}`); 34 | } 35 | 36 | return Math.max(...filteredVersions); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/prebuild-shim.ts: -------------------------------------------------------------------------------- 1 | import { pathToFileURL } from 'node:url'; 2 | 3 | process.argv.splice(1, 1); 4 | 5 | // This tricks prebuild-install into not validating on the 6 | // 1.8.x and 8.x ABI collision 7 | Object.defineProperty(process.versions, 'modules', { value: '-1', writable: false }); 8 | 9 | import(pathToFileURL(process.argv[1]).toString()); 10 | -------------------------------------------------------------------------------- /src/promisifiedGracefulFs.ts: -------------------------------------------------------------------------------- 1 | import gracefulFS from 'graceful-fs'; 2 | import { promisify } from 'node:util'; 3 | 4 | export const promisifiedGracefulFs = { 5 | copyFile: promisify(gracefulFS.copyFile), 6 | readFile: promisify(gracefulFS.readFile), 7 | readdir: promisify(gracefulFS.readdir), 8 | writeFile: promisify(gracefulFS.writeFile), 9 | } as Pick; 10 | -------------------------------------------------------------------------------- /src/read-package-json.ts: -------------------------------------------------------------------------------- 1 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 2 | import path from 'node:path'; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | export async function readPackageJson(dir: string, safe = false): Promise { 6 | try { 7 | return JSON.parse(await promisifiedGracefulFs.readFile(path.resolve(dir, 'package.json'), { 8 | encoding: 'utf-8', 9 | })); 10 | } catch (err) { 11 | if (safe) { 12 | return {}; 13 | } else { 14 | throw err; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/rebuild.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { EventEmitter } from 'node:events'; 3 | import fs from 'graceful-fs'; 4 | import { getAbi } from 'node-abi'; 5 | import os from 'node:os'; 6 | import path from 'node:path'; 7 | 8 | import { generateCacheKey, lookupModuleState } from './cache.js'; 9 | import { BuildType, IRebuilder, RebuildMode } from './types.js'; 10 | import { ModuleRebuilder } from './module-rebuilder.js'; 11 | import { ModuleType, ModuleWalker } from './module-walker.js'; 12 | 13 | export interface RebuildOptions { 14 | /** 15 | * The path to the `node_modules` directory to rebuild. 16 | */ 17 | buildPath: string; 18 | /** 19 | * The version of Electron to build against. 20 | */ 21 | electronVersion: string; 22 | /** 23 | * Override the target platform to something other than the host system platform. 24 | * Note: This only applies to downloading prebuilt binaries. **It is not possible to cross-compile native modules.** 25 | * 26 | * @defaultValue The system {@link https://nodejs.org/api/process.html#processplatform | `process.platform`} value 27 | */ 28 | platform?: NodeJS.Platform; 29 | /** 30 | * Override the target rebuild architecture to something other than the host system architecture. 31 | * 32 | * @defaultValue The system {@link https://nodejs.org/api/process.html#processarch | `process.arch`} value 33 | */ 34 | arch?: string; 35 | /** 36 | * An array of module names to rebuild in addition to detected modules 37 | * @default [] 38 | */ 39 | extraModules?: string[]; 40 | /** 41 | * An array of module names to rebuild. **Only** these modules will be rebuilt. 42 | */ 43 | onlyModules?: string[] | null; 44 | /** 45 | * Force a rebuild of modules regardless of their current build state. 46 | */ 47 | force?: boolean; 48 | /** 49 | * URL to download Electron header files from. 50 | * @defaultValue `https://www.electronjs.org/headers` 51 | */ 52 | headerURL?: string; 53 | /** 54 | * Array of types of dependencies to rebuild. Possible values are `prod`, `dev`, and `optional`. 55 | * 56 | * @defaultValue `['prod', 'optional']` 57 | */ 58 | types?: ModuleType[]; 59 | /** 60 | * Whether to rebuild modules sequentially or in parallel. 61 | * 62 | * @defaultValue `sequential` 63 | */ 64 | mode?: RebuildMode; 65 | /** 66 | * Rebuilds a Debug build of target modules. If this is `false`, a Release build will be generated instead. 67 | * 68 | * @defaultValue false 69 | */ 70 | debug?: boolean; 71 | /** 72 | * Enables hash-based caching to speed up local rebuilds. 73 | * 74 | * @experimental 75 | * @defaultValue false 76 | */ 77 | useCache?: boolean; 78 | /** 79 | * Whether to use the `clang` executable that Electron uses when building. 80 | * This will guarantee compiler compatibility. 81 | * 82 | * @defaultValue false 83 | */ 84 | useElectronClang?: boolean; 85 | /** 86 | * Sets a custom cache path for the {@link useCache} option. 87 | * @experimental 88 | * @defaultValue a `.electron-rebuild-cache` folder in the `os.homedir()` directory 89 | */ 90 | cachePath?: string; 91 | /** 92 | * GitHub tag prefix passed to {@link https://www.npmjs.com/package/prebuild-install | `prebuild-install`}. 93 | * @defaultValue `v` 94 | */ 95 | prebuildTagPrefix?: string; 96 | /** 97 | * Path to the root of the project if using npm or yarn workspaces. 98 | */ 99 | projectRootPath?: string; 100 | /** 101 | * Override the Application Binary Interface (ABI) version for the version of Electron you are targeting. 102 | * Only use when targeting nightly releases. 103 | * 104 | * @see the {@link https://github.com/electron/node-abi | electron/node-abi} repository for a list of Electron and Node.js ABIs 105 | */ 106 | forceABI?: number; 107 | /** 108 | * Disables the copying of `.node` files if not needed. 109 | * @defaultValue false 110 | */ 111 | disablePreGypCopy?: boolean; 112 | /** 113 | * Skip prebuild download and rebuild module from source. 114 | * 115 | * @defaultValue false 116 | */ 117 | buildFromSource?: boolean; 118 | /** 119 | * Array of module names to ignore during the rebuild process. 120 | */ 121 | ignoreModules?: string[]; 122 | } 123 | 124 | export interface RebuilderOptions extends RebuildOptions { 125 | lifecycle: EventEmitter; 126 | } 127 | 128 | const d = debug('electron-rebuild'); 129 | 130 | const defaultMode: RebuildMode = 'sequential'; 131 | const defaultTypes: ModuleType[] = ['prod', 'optional']; 132 | 133 | export class Rebuilder implements IRebuilder { 134 | private ABIVersion: string | undefined; 135 | private moduleWalker: ModuleWalker; 136 | rebuilds: (() => Promise)[]; 137 | 138 | public lifecycle: EventEmitter; 139 | public buildPath: string; 140 | public electronVersion: string; 141 | public platform: NodeJS.Platform; 142 | public arch: string; 143 | public force: boolean; 144 | public headerURL: string; 145 | public mode: RebuildMode; 146 | public debug: boolean; 147 | public useCache: boolean; 148 | public cachePath: string; 149 | public prebuildTagPrefix: string; 150 | public msvsVersion?: string; 151 | public useElectronClang: boolean; 152 | public disablePreGypCopy: boolean; 153 | public buildFromSource: boolean; 154 | public ignoreModules: string[]; 155 | 156 | constructor(options: RebuilderOptions) { 157 | this.lifecycle = options.lifecycle; 158 | this.buildPath = options.buildPath; 159 | this.electronVersion = options.electronVersion; 160 | this.platform = options.platform || process.platform; 161 | this.arch = options.arch || process.arch; 162 | this.force = options.force || false; 163 | this.headerURL = options.headerURL || 'https://www.electronjs.org/headers'; 164 | this.mode = options.mode || defaultMode; 165 | this.debug = options.debug || false; 166 | this.useCache = options.useCache || false; 167 | this.useElectronClang = options.useElectronClang || false; 168 | this.cachePath = options.cachePath || path.resolve(os.homedir(), '.electron-rebuild-cache'); 169 | this.prebuildTagPrefix = options.prebuildTagPrefix || 'v'; 170 | this.msvsVersion = process.env.GYP_MSVS_VERSION; 171 | this.disablePreGypCopy = options.disablePreGypCopy || false; 172 | this.buildFromSource = options.buildFromSource || false; 173 | this.ignoreModules = options.ignoreModules || []; 174 | d('ignoreModules', this.ignoreModules); 175 | 176 | if (this.useCache && this.force) { 177 | console.warn('[WARNING]: Electron Rebuild has force enabled and cache enabled, force take precedence and the cache will not be used.'); 178 | this.useCache = false; 179 | } 180 | 181 | if (typeof this.electronVersion === 'number') { 182 | if (`${this.electronVersion}`.split('.').length === 1) { 183 | this.electronVersion = `${this.electronVersion}.0.0`; 184 | } else { 185 | this.electronVersion = `${this.electronVersion}.0`; 186 | } 187 | } 188 | if (typeof this.electronVersion !== 'string') { 189 | throw new Error(`Expected a string version for electron version, got a "${typeof this.electronVersion}"`); 190 | } 191 | 192 | this.ABIVersion = options.forceABI?.toString(); 193 | const onlyModules = options.onlyModules || null; 194 | const extraModules = new Set(options.extraModules); 195 | const types = options.types || defaultTypes; 196 | this.moduleWalker = new ModuleWalker( 197 | this.buildPath, 198 | options.projectRootPath, 199 | types, 200 | extraModules, 201 | onlyModules, 202 | ); 203 | this.rebuilds = []; 204 | 205 | d( 206 | 'rebuilding with args:', 207 | this.buildPath, 208 | this.electronVersion, 209 | this.platform, 210 | this.arch, 211 | extraModules, 212 | this.force, 213 | this.headerURL, 214 | types, 215 | this.debug 216 | ); 217 | } 218 | 219 | get ABI(): string { 220 | if (this.ABIVersion === undefined) { 221 | this.ABIVersion = getAbi(this.electronVersion, 'electron'); 222 | } 223 | 224 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 225 | return this.ABIVersion!; 226 | } 227 | 228 | get buildType(): BuildType { 229 | return this.debug ? BuildType.Debug : BuildType.Release; 230 | } 231 | 232 | async rebuild(): Promise { 233 | if (!path.isAbsolute(this.buildPath)) { 234 | throw new Error('Expected buildPath to be an absolute path'); 235 | } 236 | 237 | this.lifecycle.emit('start'); 238 | 239 | for (const modulePath of await this.modulesToRebuild()) { 240 | this.rebuilds.push(() => this.rebuildModuleAt(modulePath)); 241 | } 242 | 243 | this.rebuilds.push(() => this.rebuildModuleAt(this.buildPath)); 244 | 245 | if (this.mode !== 'sequential') { 246 | await Promise.all(this.rebuilds.map(fn => fn())); 247 | } else { 248 | for (const rebuildFn of this.rebuilds) { 249 | await rebuildFn(); 250 | } 251 | } 252 | } 253 | 254 | async modulesToRebuild(): Promise { 255 | await this.moduleWalker.walkModules(); 256 | 257 | for (const nodeModulesPath of await this.moduleWalker.nodeModulesPaths) { 258 | await this.moduleWalker.findAllModulesIn(nodeModulesPath); 259 | } 260 | 261 | return this.moduleWalker.modulesToRebuild; 262 | } 263 | 264 | async rebuildModuleAt(modulePath: string): Promise { 265 | if (!(fs.existsSync(path.resolve(modulePath, 'binding.gyp')))) { 266 | return; 267 | } 268 | 269 | const moduleRebuilder = new ModuleRebuilder(this, modulePath); 270 | 271 | let moduleName = path.basename(modulePath); 272 | const parentName = path.basename(path.dirname(modulePath)); 273 | if (parentName !== 'node_modules') { 274 | moduleName = `${parentName}/${moduleName}`; 275 | } 276 | 277 | this.lifecycle.emit('module-found', moduleName); 278 | 279 | if (!this.force && await moduleRebuilder.alreadyBuiltByRebuild()) { 280 | d(`skipping: ${moduleName} as it is already built`); 281 | this.lifecycle.emit('module-done', moduleName); 282 | this.lifecycle.emit('module-skip', moduleName); 283 | return; 284 | } 285 | 286 | d('checking', moduleName, 'against', this.ignoreModules); 287 | if (this.ignoreModules.includes(moduleName)) { 288 | d(`skipping: ${moduleName} as it is in the ignoreModules array`); 289 | this.lifecycle.emit('module-done', moduleName); 290 | this.lifecycle.emit('module-skip', moduleName); 291 | return; 292 | } 293 | 294 | if (await moduleRebuilder.prebuildInstallNativeModuleExists()) { 295 | d(`skipping: ${moduleName} as it was prebuilt`); 296 | return; 297 | } 298 | 299 | let cacheKey!: string; 300 | if (this.useCache) { 301 | cacheKey = await generateCacheKey({ 302 | ABI: this.ABI, 303 | arch: this.arch, 304 | platform: this.platform, 305 | debug: this.debug, 306 | electronVersion: this.electronVersion, 307 | headerURL: this.headerURL, 308 | modulePath, 309 | }); 310 | 311 | const applyDiffFn = await lookupModuleState(this.cachePath, cacheKey); 312 | if (typeof applyDiffFn === 'function') { 313 | await applyDiffFn(modulePath); 314 | this.lifecycle.emit('module-done', moduleName); 315 | return; 316 | } 317 | } 318 | 319 | if (await moduleRebuilder.rebuild(cacheKey)) { 320 | this.lifecycle.emit('module-done', moduleName); 321 | } 322 | } 323 | } 324 | 325 | export type RebuildResult = Promise & { lifecycle: EventEmitter }; 326 | 327 | export function rebuild(options: RebuildOptions): RebuildResult { 328 | // eslint-disable-next-line prefer-rest-params 329 | d('rebuilding with args:', arguments); 330 | const lifecycle = new EventEmitter(); 331 | const rebuilderOptions: RebuilderOptions = { ...options, lifecycle }; 332 | const rebuilder = new Rebuilder(rebuilderOptions); 333 | 334 | const ret = rebuilder.rebuild() as RebuildResult; 335 | ret.lifecycle = lifecycle; 336 | 337 | return ret; 338 | } 339 | -------------------------------------------------------------------------------- /src/search-module.ts: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import path from 'node:path'; 3 | 4 | async function shouldContinueSearch(traversedPath: string, rootPath?: string, stopAtPackageJSON?: boolean): Promise { 5 | if (rootPath) { 6 | return Promise.resolve(traversedPath !== path.dirname(rootPath)); 7 | } else if (stopAtPackageJSON) { 8 | return fs.existsSync(path.join(traversedPath, 'package.json')); 9 | } else { 10 | return true; 11 | } 12 | } 13 | 14 | type PathGeneratorFunction = (traversedPath: string) => string; 15 | 16 | async function traverseAncestorDirectories( 17 | cwd: string, 18 | pathGenerator: PathGeneratorFunction, 19 | rootPath?: string, 20 | maxItems?: number, 21 | stopAtPackageJSON?: boolean 22 | ): Promise { 23 | const paths: string[] = []; 24 | let traversedPath = path.resolve(cwd); 25 | 26 | while (await shouldContinueSearch(traversedPath, rootPath, stopAtPackageJSON)) { 27 | const generatedPath = pathGenerator(traversedPath); 28 | if (fs.existsSync(generatedPath)) { 29 | paths.push(generatedPath); 30 | } 31 | 32 | const parentPath = path.dirname(traversedPath); 33 | if (parentPath === traversedPath || (maxItems && paths.length >= maxItems)) { 34 | break; 35 | } 36 | traversedPath = parentPath; 37 | } 38 | 39 | return paths; 40 | } 41 | 42 | /** 43 | * Find all instances of a given module in node_modules subdirectories while traversing up 44 | * ancestor directories. 45 | * 46 | * @param cwd the initial directory to traverse 47 | * @param moduleName the Node module name (should work for scoped modules as well) 48 | * @param rootPath the project's root path. If provided, the traversal will stop at this path. 49 | */ 50 | export async function searchForModule( 51 | cwd: string, 52 | moduleName: string, 53 | rootPath?: string 54 | ): Promise { 55 | const pathGenerator: PathGeneratorFunction = (traversedPath) => path.join(traversedPath, 'node_modules', moduleName); 56 | return traverseAncestorDirectories(cwd, pathGenerator, rootPath, undefined, true); 57 | } 58 | 59 | /** 60 | * Find all instances of node_modules subdirectories while traversing up ancestor directories. 61 | * 62 | * @param cwd the initial directory to traverse 63 | * @param rootPath the project's root path. If provided, the traversal will stop at this path. 64 | */ 65 | export async function searchForNodeModules(cwd: string, rootPath?: string): Promise { 66 | const pathGenerator: PathGeneratorFunction = (traversedPath) => path.join(traversedPath, 'node_modules'); 67 | return traverseAncestorDirectories(cwd, pathGenerator, rootPath, undefined, true); 68 | } 69 | 70 | /** 71 | * Determine the root directory of a given project, by looking for a directory with an 72 | * NPM or yarn lockfile or pnpm lockfile. 73 | * 74 | * @param cwd the initial directory to traverse 75 | */ 76 | export async function getProjectRootPath(cwd: string): Promise { 77 | for (const lockFilename of ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']) { 78 | const pathGenerator: PathGeneratorFunction = (traversedPath) => path.join(traversedPath, lockFilename); 79 | const lockPaths = await traverseAncestorDirectories(cwd, pathGenerator, undefined, 1); 80 | if (lockPaths.length > 0) { 81 | return path.dirname(lockPaths[0]); 82 | } 83 | } 84 | 85 | return cwd; 86 | } 87 | -------------------------------------------------------------------------------- /src/sysroot-fetcher.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from '@malept/cross-spawn-promise'; 2 | import crypto from 'node:crypto'; 3 | import debug from 'debug'; 4 | import fs from 'graceful-fs'; 5 | import path from 'node:path'; 6 | 7 | import { ELECTRON_GYP_DIR } from './constants.js'; 8 | import { fetch } from './fetcher.js'; 9 | import { promisifiedGracefulFs } from './promisifiedGracefulFs.js'; 10 | 11 | const d = debug('electron-rebuild'); 12 | 13 | const sysrootArchAliases: Record = { 14 | x64: 'amd64', 15 | ia32: 'i386', 16 | }; 17 | 18 | const SYSROOT_BASE_URL = 'https://dev-cdn.electronjs.org/linux-sysroots'; 19 | 20 | export async function downloadLinuxSysroot(electronVersion: string, targetArch: string): Promise { 21 | d('fetching sysroot for Electron:', electronVersion); 22 | const sysrootDir = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-sysroot`); 23 | if (fs.existsSync(path.resolve(sysrootDir, 'lib'))) return sysrootDir; 24 | await fs.promises.mkdir(sysrootDir, { recursive: true }); 25 | 26 | const linuxArch = sysrootArchAliases[targetArch] || targetArch; 27 | const electronSysroots = JSON.parse(await fetch(`https://raw.githubusercontent.com/electron/electron/v${electronVersion}/script/sysroots.json`, 'text')); 28 | 29 | const { Sha1Sum: sha, Tarball: fileName } = electronSysroots[`sid_${linuxArch}`] || electronSysroots[`bullseye_${linuxArch}`]; 30 | const sysrootURL = `${SYSROOT_BASE_URL}/${sha}/${fileName}`; 31 | const sysrootBuffer = await fetch(sysrootURL, 'buffer'); 32 | 33 | const actualSha = crypto.createHash('SHA1').update(sysrootBuffer).digest('hex'); 34 | d('expected sha:', sha); 35 | d('actual sha:', actualSha); 36 | if (sha !== actualSha) throw new Error(`Attempted to download the linux sysroot for ${electronVersion} but the SHA checksum did not match`); 37 | 38 | d('writing sysroot to disk'); 39 | const tmpTarFile = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-${fileName}`); 40 | if (fs.existsSync(tmpTarFile)) await fs.promises.rm(tmpTarFile, { recursive: true, force: true }); 41 | await promisifiedGracefulFs.writeFile(tmpTarFile, sysrootBuffer); 42 | 43 | d('decompressing sysroot'); 44 | await spawn('tar', ['-xf', tmpTarFile, '-C', sysrootDir], { stdio: 'ignore' }); 45 | 46 | return sysrootDir; 47 | } 48 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'node:events'; 2 | 3 | export enum BuildType { 4 | Debug = 'Debug', 5 | Release = 'Release', 6 | } 7 | 8 | export type RebuildMode = 'sequential' | 'parallel'; 9 | 10 | export interface IRebuilder { 11 | ABI: string; 12 | arch: string; 13 | buildPath: string; 14 | buildType: BuildType; 15 | cachePath: string; 16 | debug: boolean; 17 | disablePreGypCopy: boolean; 18 | electronVersion: string; 19 | force: boolean; 20 | headerURL: string; 21 | lifecycle: EventEmitter; 22 | mode: RebuildMode; 23 | msvsVersion?: string; 24 | platform: NodeJS.Platform; 25 | prebuildTagPrefix: string; 26 | buildFromSource: boolean; 27 | useCache: boolean; 28 | useElectronClang: boolean; 29 | } 30 | -------------------------------------------------------------------------------- /test/arch.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { getNodeArch, uname } from '../lib/arch.js'; 4 | 5 | // Copied from @electron/get 6 | describe('uname()', () => { 7 | if (process.platform !== 'win32') { 8 | it('should return the correct arch for your system', () => { 9 | // assumes that the tests will always be run on an x64 system 😬 10 | expect(uname()).to.equal(process.arch === 'arm64' ? 'arm64' : 'x86_64'); 11 | }); 12 | } 13 | }); 14 | 15 | // Based on getHostArch tests from @electron/get 16 | describe('getNodeArch()', () => { 17 | it('should return process.arch on x64', () => { 18 | expect(getNodeArch('x64', {})).to.equal('x64'); 19 | }); 20 | 21 | it('should return process.arch on ia32', () => { 22 | expect(getNodeArch('ia32', {})).to.equal('ia32'); 23 | }); 24 | 25 | it('should return process.arch on arm64', () => { 26 | expect(getNodeArch('arm64', {})).to.equal('arm64'); 27 | }); 28 | 29 | it('should return process.arch on unknown arm', () => { 30 | expect(getNodeArch('arm', {})).to.equal('armv7l'); 31 | }); 32 | 33 | if (process.platform !== 'win32') { 34 | it('should return uname on arm 6', () => { 35 | expect(getNodeArch('arm', { arm_version: '6' })).to.equal(uname()); 36 | }); 37 | } 38 | 39 | it('should return armv7l on arm 7', () => { 40 | expect(getNodeArch('arm', { arm_version: '7' })).to.equal('armv7l'); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/electron-locator.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | 5 | import { locateElectronModule } from '../lib/electron-locator.js'; 6 | 7 | const baseFixtureDir = path.resolve(import.meta.dirname, 'fixture', 'electron-locator'); 8 | 9 | async function expectElectronCanBeFound(projectRootPath: string, startDir: string): Promise { 10 | it('should return a valid path', async () => { 11 | const electronPath = await locateElectronModule(projectRootPath, startDir); 12 | expect(electronPath).to.be.a('string'); 13 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 14 | expect(fs.existsSync(electronPath!)).to.be.equal(true); 15 | }); 16 | } 17 | 18 | describe('locateElectronModule', () => { 19 | describe('when electron is not installed', () => { 20 | const electronDir = path.resolve(import.meta.dirname, '..', 'node_modules', 'electron'); 21 | 22 | before(async () => { 23 | await fs.promises.rename(electronDir, `${electronDir}-moved`); 24 | }); 25 | 26 | it('should return null when electron is not installed', async () => { 27 | const fixtureDir = path.join(baseFixtureDir, 'not-installed'); 28 | expect(await locateElectronModule(fixtureDir, fixtureDir)).to.be.equal(null); 29 | }); 30 | 31 | after(async () => { 32 | await fs.promises.rename(`${electronDir}-moved`, electronDir); 33 | }); 34 | }); 35 | 36 | describe('using import.meta.resolve() in the current project to search', () => { 37 | const fixtureDir = path.join(baseFixtureDir, 'not-installed'); 38 | expectElectronCanBeFound(fixtureDir, fixtureDir); 39 | }); 40 | 41 | describe('with electron-prebuilt-compile installed', () => { 42 | const fixtureDir = path.join(baseFixtureDir, 'prebuilt-compile'); 43 | expectElectronCanBeFound(fixtureDir, fixtureDir); 44 | }); 45 | 46 | describe('with electron installed', () => { 47 | const fixtureDir = path.join(baseFixtureDir, 'single'); 48 | expectElectronCanBeFound(fixtureDir, fixtureDir); 49 | 50 | describe('in a workspace', () => { 51 | const fixtureDir = path.join(baseFixtureDir, 'workspace'); 52 | expectElectronCanBeFound(fixtureDir, path.join(fixtureDir, 'packages', 'descendant')); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/fixture/electron-locator/not-installed/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/electron-locator/prebuilt-compile/node_modules/electron-prebuilt-compile/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/electron-locator/single/node_modules/electron/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/electron-locator/workspace/node_modules/electron/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/electron-locator/workspace/packages/descendant/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/empty-project/node_modules/extra/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/empty-project/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/forked-module-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forked-module-test", 3 | "productName": "Native App", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "src/index.js", 7 | "keywords": [], 8 | "author": "", 9 | "license": "MIT", 10 | "dependencies": { 11 | "@electron/node-pre-gyp": "npm:@mapbox/node-pre-gyp@1.0.11", 12 | "@electron/prebuild-install": "npm:prebuild-install@7.1.2" 13 | }, 14 | "devDependencies": { 15 | "@electron/prebuildify": "npm:prebuildify@6.0.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/fixture/multi-level-workspace/packages/bar/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/multi-level-workspace/packages/foo/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/napi-build-version/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace-app", 3 | "productName": "Workspace App", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "src/index.js", 7 | "keywords": [], 8 | "author": "", 9 | "license": "MIT", 10 | "dependencies": { 11 | "sqlite3": "5.1.6" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/native-app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "native-app", 3 | "productName": "Native App", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "start": "electron-forge start" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "config": { 14 | "forge": "./forge.config.js" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^12.0.10", 18 | "windows-active-process": "1.1.1" 19 | }, 20 | "dependencies": { 21 | "@newrelic/native-metrics": "5.3.0", 22 | "farmhash": "3.3.1", 23 | "level": "6.0.0", 24 | "native-hello-world": "2.0.0", 25 | "ref-napi": "1.4.2", 26 | "sqlite3": "5.1.6" 27 | }, 28 | "optionalDependencies": { 29 | "bcrypt": "5.0.0" 30 | }, 31 | "resolutions": { 32 | "bcrypt/nan": "2.17.0", 33 | "node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/fixture/native-app1/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@gar/promisify@^1.1.3": 6 | version "1.1.3" 7 | resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" 8 | integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== 9 | 10 | "@mapbox/node-pre-gyp@^1.0.0": 11 | version "1.0.11" 12 | resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" 13 | integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== 14 | dependencies: 15 | detect-libc "^2.0.0" 16 | https-proxy-agent "^5.0.0" 17 | make-dir "^3.1.0" 18 | node-fetch "^2.6.7" 19 | nopt "^5.0.0" 20 | npmlog "^5.0.1" 21 | rimraf "^3.0.2" 22 | semver "^7.3.5" 23 | tar "^6.1.11" 24 | 25 | "@newrelic/native-metrics@5.3.0": 26 | version "5.3.0" 27 | resolved "https://registry.yarnpkg.com/@newrelic/native-metrics/-/native-metrics-5.3.0.tgz#762ffcee0e682b1d4182ad307b6c2d32dda508ec" 28 | integrity sha512-GF3AIUz6vGzTLeXtQPlwA54LHlQbmKjIxtwY+SKaiFebyL/C7eD1mwW+9sL07B93DIUcs+pEc/OnHei314mNWg== 29 | dependencies: 30 | nan "^2.14.1" 31 | semver "^5.5.1" 32 | 33 | "@npmcli/fs@^2.1.0": 34 | version "2.1.2" 35 | resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" 36 | integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== 37 | dependencies: 38 | "@gar/promisify" "^1.1.3" 39 | semver "^7.3.5" 40 | 41 | "@npmcli/move-file@^2.0.0": 42 | version "2.0.1" 43 | resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" 44 | integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== 45 | dependencies: 46 | mkdirp "^1.0.4" 47 | rimraf "^3.0.2" 48 | 49 | "@tootallnate/once@2": 50 | version "2.0.0" 51 | resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" 52 | integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== 53 | 54 | "@types/node@^12.0.10": 55 | version "12.20.55" 56 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" 57 | integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== 58 | 59 | abbrev@1, abbrev@^1.0.0: 60 | version "1.1.1" 61 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 62 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 63 | 64 | abstract-leveldown@^6.2.1: 65 | version "6.3.0" 66 | resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" 67 | integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== 68 | dependencies: 69 | buffer "^5.5.0" 70 | immediate "^3.2.3" 71 | level-concat-iterator "~2.0.0" 72 | level-supports "~1.0.0" 73 | xtend "~4.0.0" 74 | 75 | abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: 76 | version "6.2.3" 77 | resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" 78 | integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== 79 | dependencies: 80 | buffer "^5.5.0" 81 | immediate "^3.2.3" 82 | level-concat-iterator "~2.0.0" 83 | level-supports "~1.0.0" 84 | xtend "~4.0.0" 85 | 86 | agent-base@6, agent-base@^6.0.2: 87 | version "6.0.2" 88 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" 89 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== 90 | dependencies: 91 | debug "4" 92 | 93 | agentkeepalive@^4.2.1: 94 | version "4.6.0" 95 | resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" 96 | integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== 97 | dependencies: 98 | humanize-ms "^1.2.1" 99 | 100 | aggregate-error@^3.0.0: 101 | version "3.1.0" 102 | resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" 103 | integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== 104 | dependencies: 105 | clean-stack "^2.0.0" 106 | indent-string "^4.0.0" 107 | 108 | ansi-regex@^2.0.0: 109 | version "2.1.1" 110 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 111 | integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== 112 | 113 | ansi-regex@^5.0.1: 114 | version "5.0.1" 115 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 116 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 117 | 118 | aproba@^1.0.3: 119 | version "1.2.0" 120 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" 121 | integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== 122 | 123 | "aproba@^1.0.3 || ^2.0.0": 124 | version "2.0.0" 125 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" 126 | integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== 127 | 128 | are-we-there-yet@^2.0.0: 129 | version "2.0.0" 130 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" 131 | integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== 132 | dependencies: 133 | delegates "^1.0.0" 134 | readable-stream "^3.6.0" 135 | 136 | are-we-there-yet@~1.1.2: 137 | version "1.1.7" 138 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" 139 | integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== 140 | dependencies: 141 | delegates "^1.0.0" 142 | readable-stream "^2.0.6" 143 | 144 | balanced-match@^1.0.0: 145 | version "1.0.2" 146 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 147 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 148 | 149 | base64-js@^1.3.1: 150 | version "1.5.1" 151 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 152 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 153 | 154 | bcrypt@5.0.0: 155 | version "5.0.0" 156 | resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.0.tgz#051407c7cd5ffbfb773d541ca3760ea0754e37e2" 157 | integrity sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg== 158 | dependencies: 159 | node-addon-api "^3.0.0" 160 | node-pre-gyp "0.15.0" 161 | 162 | bindings@^1.2.1, bindings@^1.3.0: 163 | version "1.5.0" 164 | resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" 165 | integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== 166 | dependencies: 167 | file-uri-to-path "1.0.0" 168 | 169 | bl@^4.0.3: 170 | version "4.1.0" 171 | resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" 172 | integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== 173 | dependencies: 174 | buffer "^5.5.0" 175 | inherits "^2.0.4" 176 | readable-stream "^3.4.0" 177 | 178 | brace-expansion@^1.1.7: 179 | version "1.1.11" 180 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 181 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 182 | dependencies: 183 | balanced-match "^1.0.0" 184 | concat-map "0.0.1" 185 | 186 | brace-expansion@^2.0.1: 187 | version "2.0.1" 188 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 189 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 190 | dependencies: 191 | balanced-match "^1.0.0" 192 | 193 | buffer@^5.5.0, buffer@^5.6.0: 194 | version "5.7.1" 195 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 196 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 197 | dependencies: 198 | base64-js "^1.3.1" 199 | ieee754 "^1.1.13" 200 | 201 | cacache@^16.1.0: 202 | version "16.1.3" 203 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" 204 | integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== 205 | dependencies: 206 | "@npmcli/fs" "^2.1.0" 207 | "@npmcli/move-file" "^2.0.0" 208 | chownr "^2.0.0" 209 | fs-minipass "^2.1.0" 210 | glob "^8.0.1" 211 | infer-owner "^1.0.4" 212 | lru-cache "^7.7.1" 213 | minipass "^3.1.6" 214 | minipass-collect "^1.0.2" 215 | minipass-flush "^1.0.5" 216 | minipass-pipeline "^1.2.4" 217 | mkdirp "^1.0.4" 218 | p-map "^4.0.0" 219 | promise-inflight "^1.0.1" 220 | rimraf "^3.0.2" 221 | ssri "^9.0.0" 222 | tar "^6.1.11" 223 | unique-filename "^2.0.0" 224 | 225 | chownr@^1.1.1, chownr@^1.1.4: 226 | version "1.1.4" 227 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" 228 | integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== 229 | 230 | chownr@^2.0.0: 231 | version "2.0.0" 232 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" 233 | integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== 234 | 235 | clean-stack@^2.0.0: 236 | version "2.2.0" 237 | resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" 238 | integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== 239 | 240 | code-point-at@^1.0.0: 241 | version "1.1.0" 242 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 243 | integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== 244 | 245 | color-support@^1.1.2: 246 | version "1.1.3" 247 | resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" 248 | integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== 249 | 250 | concat-map@0.0.1: 251 | version "0.0.1" 252 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 253 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 254 | 255 | console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: 256 | version "1.1.0" 257 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 258 | integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== 259 | 260 | core-util-is@~1.0.0: 261 | version "1.0.3" 262 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" 263 | integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== 264 | 265 | debug@4, debug@^4.3.3: 266 | version "4.3.5" 267 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" 268 | integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== 269 | dependencies: 270 | ms "2.1.2" 271 | 272 | debug@^3.1.0, debug@^3.2.6: 273 | version "3.2.7" 274 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" 275 | integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== 276 | dependencies: 277 | ms "^2.1.1" 278 | 279 | decompress-response@^6.0.0: 280 | version "6.0.0" 281 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" 282 | integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== 283 | dependencies: 284 | mimic-response "^3.1.0" 285 | 286 | deep-extend@^0.6.0: 287 | version "0.6.0" 288 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 289 | integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== 290 | 291 | deferred-leveldown@~5.3.0: 292 | version "5.3.0" 293 | resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" 294 | integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== 295 | dependencies: 296 | abstract-leveldown "~6.2.1" 297 | inherits "^2.0.3" 298 | 299 | delegates@^1.0.0: 300 | version "1.0.0" 301 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 302 | integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== 303 | 304 | detect-libc@^1.0.2: 305 | version "1.0.3" 306 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" 307 | integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== 308 | 309 | detect-libc@^2.0.0: 310 | version "2.0.3" 311 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" 312 | integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== 313 | 314 | emoji-regex@^8.0.0: 315 | version "8.0.0" 316 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 317 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 318 | 319 | encoding-down@^6.3.0: 320 | version "6.3.0" 321 | resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" 322 | integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== 323 | dependencies: 324 | abstract-leveldown "^6.2.1" 325 | inherits "^2.0.3" 326 | level-codec "^9.0.0" 327 | level-errors "^2.0.0" 328 | 329 | encoding@^0.1.13: 330 | version "0.1.13" 331 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" 332 | integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== 333 | dependencies: 334 | iconv-lite "^0.6.2" 335 | 336 | end-of-stream@^1.1.0, end-of-stream@^1.4.1: 337 | version "1.4.4" 338 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 339 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 340 | dependencies: 341 | once "^1.4.0" 342 | 343 | env-paths@^2.2.0: 344 | version "2.2.1" 345 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" 346 | integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== 347 | 348 | err-code@^2.0.2: 349 | version "2.0.3" 350 | resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" 351 | integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== 352 | 353 | errno@~0.1.1: 354 | version "0.1.8" 355 | resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" 356 | integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== 357 | dependencies: 358 | prr "~1.0.1" 359 | 360 | expand-template@^2.0.3: 361 | version "2.0.3" 362 | resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" 363 | integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== 364 | 365 | exponential-backoff@^3.1.1: 366 | version "3.1.1" 367 | resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" 368 | integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== 369 | 370 | farmhash@3.3.1: 371 | version "3.3.1" 372 | resolved "https://registry.yarnpkg.com/farmhash/-/farmhash-3.3.1.tgz#f497dab369d4deb87ae6df2ab062c3bc160e1df4" 373 | integrity sha512-XUizHanzlr/v7suBr/o85HSakOoWh6HKXZjFYl5C2+Gj0f0rkw+XTUZzrd9odDsgI9G5tRUcF4wSbKaX04T0DQ== 374 | dependencies: 375 | node-addon-api "^5.1.0" 376 | prebuild-install "^7.1.2" 377 | 378 | file-uri-to-path@1.0.0: 379 | version "1.0.0" 380 | resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" 381 | integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== 382 | 383 | fs-constants@^1.0.0: 384 | version "1.0.0" 385 | resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 386 | integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== 387 | 388 | fs-minipass@^1.2.7: 389 | version "1.2.7" 390 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" 391 | integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== 392 | dependencies: 393 | minipass "^2.6.0" 394 | 395 | fs-minipass@^2.0.0, fs-minipass@^2.1.0: 396 | version "2.1.0" 397 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" 398 | integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== 399 | dependencies: 400 | minipass "^3.0.0" 401 | 402 | fs.realpath@^1.0.0: 403 | version "1.0.0" 404 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 405 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 406 | 407 | gauge@^3.0.0: 408 | version "3.0.2" 409 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" 410 | integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== 411 | dependencies: 412 | aproba "^1.0.3 || ^2.0.0" 413 | color-support "^1.1.2" 414 | console-control-strings "^1.0.0" 415 | has-unicode "^2.0.1" 416 | object-assign "^4.1.1" 417 | signal-exit "^3.0.0" 418 | string-width "^4.2.3" 419 | strip-ansi "^6.0.1" 420 | wide-align "^1.1.2" 421 | 422 | gauge@~2.7.3: 423 | version "2.7.4" 424 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" 425 | integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg== 426 | dependencies: 427 | aproba "^1.0.3" 428 | console-control-strings "^1.0.0" 429 | has-unicode "^2.0.0" 430 | object-assign "^4.1.0" 431 | signal-exit "^3.0.0" 432 | string-width "^1.0.1" 433 | strip-ansi "^3.0.1" 434 | wide-align "^1.1.0" 435 | 436 | github-from-package@0.0.0: 437 | version "0.0.0" 438 | resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" 439 | integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== 440 | 441 | glob@^7.1.3: 442 | version "7.2.3" 443 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 444 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 445 | dependencies: 446 | fs.realpath "^1.0.0" 447 | inflight "^1.0.4" 448 | inherits "2" 449 | minimatch "^3.1.1" 450 | once "^1.3.0" 451 | path-is-absolute "^1.0.0" 452 | 453 | glob@^8.0.1, glob@^8.1.0: 454 | version "8.1.0" 455 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" 456 | integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== 457 | dependencies: 458 | fs.realpath "^1.0.0" 459 | inflight "^1.0.4" 460 | inherits "2" 461 | minimatch "^5.0.1" 462 | once "^1.3.0" 463 | 464 | graceful-fs@^4.2.6: 465 | version "4.2.11" 466 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" 467 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 468 | 469 | has-unicode@^2.0.0, has-unicode@^2.0.1: 470 | version "2.0.1" 471 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 472 | integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== 473 | 474 | http-cache-semantics@^4.1.0: 475 | version "4.1.1" 476 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" 477 | integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== 478 | 479 | http-proxy-agent@^5.0.0: 480 | version "5.0.0" 481 | resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" 482 | integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== 483 | dependencies: 484 | "@tootallnate/once" "2" 485 | agent-base "6" 486 | debug "4" 487 | 488 | https-proxy-agent@^5.0.0: 489 | version "5.0.1" 490 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" 491 | integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== 492 | dependencies: 493 | agent-base "6" 494 | debug "4" 495 | 496 | humanize-ms@^1.2.1: 497 | version "1.2.1" 498 | resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" 499 | integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== 500 | dependencies: 501 | ms "^2.0.0" 502 | 503 | iconv-lite@^0.4.4: 504 | version "0.4.24" 505 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 506 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 507 | dependencies: 508 | safer-buffer ">= 2.1.2 < 3" 509 | 510 | iconv-lite@^0.6.2: 511 | version "0.6.3" 512 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" 513 | integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== 514 | dependencies: 515 | safer-buffer ">= 2.1.2 < 3.0.0" 516 | 517 | ieee754@^1.1.13: 518 | version "1.2.1" 519 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 520 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 521 | 522 | ignore-walk@^3.0.1: 523 | version "3.0.4" 524 | resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" 525 | integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== 526 | dependencies: 527 | minimatch "^3.0.4" 528 | 529 | immediate@^3.2.3: 530 | version "3.3.0" 531 | resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" 532 | integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== 533 | 534 | imurmurhash@^0.1.4: 535 | version "0.1.4" 536 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 537 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 538 | 539 | indent-string@^4.0.0: 540 | version "4.0.0" 541 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" 542 | integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== 543 | 544 | infer-owner@^1.0.4: 545 | version "1.0.4" 546 | resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" 547 | integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== 548 | 549 | inflight@^1.0.4: 550 | version "1.0.6" 551 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 552 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 553 | dependencies: 554 | once "^1.3.0" 555 | wrappy "1" 556 | 557 | inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: 558 | version "2.0.4" 559 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 560 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 561 | 562 | ini@~1.3.0: 563 | version "1.3.8" 564 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 565 | integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 566 | 567 | ip-address@^9.0.5: 568 | version "9.0.5" 569 | resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" 570 | integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== 571 | dependencies: 572 | jsbn "1.1.0" 573 | sprintf-js "^1.1.3" 574 | 575 | is-fullwidth-code-point@^1.0.0: 576 | version "1.0.0" 577 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 578 | integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== 579 | dependencies: 580 | number-is-nan "^1.0.0" 581 | 582 | is-fullwidth-code-point@^3.0.0: 583 | version "3.0.0" 584 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 585 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 586 | 587 | is-lambda@^1.0.1: 588 | version "1.0.1" 589 | resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" 590 | integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== 591 | 592 | isarray@~1.0.0: 593 | version "1.0.0" 594 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 595 | integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== 596 | 597 | isexe@^2.0.0: 598 | version "2.0.0" 599 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 600 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 601 | 602 | jsbn@1.1.0: 603 | version "1.1.0" 604 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" 605 | integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== 606 | 607 | level-codec@^9.0.0: 608 | version "9.0.2" 609 | resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" 610 | integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== 611 | dependencies: 612 | buffer "^5.6.0" 613 | 614 | level-concat-iterator@~2.0.0: 615 | version "2.0.1" 616 | resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" 617 | integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== 618 | 619 | level-errors@^2.0.0, level-errors@~2.0.0: 620 | version "2.0.1" 621 | resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" 622 | integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== 623 | dependencies: 624 | errno "~0.1.1" 625 | 626 | level-iterator-stream@~4.0.0: 627 | version "4.0.2" 628 | resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" 629 | integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== 630 | dependencies: 631 | inherits "^2.0.4" 632 | readable-stream "^3.4.0" 633 | xtend "^4.0.2" 634 | 635 | level-js@^5.0.0: 636 | version "5.0.2" 637 | resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.2.tgz#5e280b8f93abd9ef3a305b13faf0b5397c969b55" 638 | integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg== 639 | dependencies: 640 | abstract-leveldown "~6.2.3" 641 | buffer "^5.5.0" 642 | inherits "^2.0.3" 643 | ltgt "^2.1.2" 644 | 645 | level-packager@^5.1.0: 646 | version "5.1.1" 647 | resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" 648 | integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== 649 | dependencies: 650 | encoding-down "^6.3.0" 651 | levelup "^4.3.2" 652 | 653 | level-supports@~1.0.0: 654 | version "1.0.1" 655 | resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" 656 | integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== 657 | dependencies: 658 | xtend "^4.0.2" 659 | 660 | level@6.0.0: 661 | version "6.0.0" 662 | resolved "https://registry.yarnpkg.com/level/-/level-6.0.0.tgz#d216fb9b9c3940bcec15c5880d8da775ca086c5c" 663 | integrity sha512-3oAi7gXLLNr7pHj8c4vbI6lHkXf35m8qb7zWMrNTrOax6CXBVggQAwL1xnC/1CszyYrW3BsLXsY5TMgTxtKfFA== 664 | dependencies: 665 | level-js "^5.0.0" 666 | level-packager "^5.1.0" 667 | leveldown "^5.4.0" 668 | opencollective-postinstall "^2.0.0" 669 | 670 | leveldown@^5.4.0: 671 | version "5.6.0" 672 | resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" 673 | integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== 674 | dependencies: 675 | abstract-leveldown "~6.2.1" 676 | napi-macros "~2.0.0" 677 | node-gyp-build "~4.1.0" 678 | 679 | levelup@^4.3.2: 680 | version "4.4.0" 681 | resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" 682 | integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== 683 | dependencies: 684 | deferred-leveldown "~5.3.0" 685 | level-errors "~2.0.0" 686 | level-iterator-stream "~4.0.0" 687 | level-supports "~1.0.0" 688 | xtend "~4.0.0" 689 | 690 | lru-cache@^7.7.1: 691 | version "7.18.3" 692 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" 693 | integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== 694 | 695 | ltgt@^2.1.2: 696 | version "2.2.1" 697 | resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" 698 | integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== 699 | 700 | make-dir@^3.1.0: 701 | version "3.1.0" 702 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" 703 | integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== 704 | dependencies: 705 | semver "^6.0.0" 706 | 707 | make-fetch-happen@^10.2.1: 708 | version "10.2.1" 709 | resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" 710 | integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== 711 | dependencies: 712 | agentkeepalive "^4.2.1" 713 | cacache "^16.1.0" 714 | http-cache-semantics "^4.1.0" 715 | http-proxy-agent "^5.0.0" 716 | https-proxy-agent "^5.0.0" 717 | is-lambda "^1.0.1" 718 | lru-cache "^7.7.1" 719 | minipass "^3.1.6" 720 | minipass-collect "^1.0.2" 721 | minipass-fetch "^2.0.3" 722 | minipass-flush "^1.0.5" 723 | minipass-pipeline "^1.2.4" 724 | negotiator "^0.6.3" 725 | promise-retry "^2.0.1" 726 | socks-proxy-agent "^7.0.0" 727 | ssri "^9.0.0" 728 | 729 | mimic-response@^3.1.0: 730 | version "3.1.0" 731 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" 732 | integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== 733 | 734 | minimatch@^3.0.4, minimatch@^3.1.1: 735 | version "3.1.2" 736 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 737 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 738 | dependencies: 739 | brace-expansion "^1.1.7" 740 | 741 | minimatch@^5.0.1: 742 | version "5.1.6" 743 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" 744 | integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== 745 | dependencies: 746 | brace-expansion "^2.0.1" 747 | 748 | minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: 749 | version "1.2.8" 750 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" 751 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 752 | 753 | minipass-collect@^1.0.2: 754 | version "1.0.2" 755 | resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" 756 | integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== 757 | dependencies: 758 | minipass "^3.0.0" 759 | 760 | minipass-fetch@^2.0.3: 761 | version "2.1.2" 762 | resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" 763 | integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== 764 | dependencies: 765 | minipass "^3.1.6" 766 | minipass-sized "^1.0.3" 767 | minizlib "^2.1.2" 768 | optionalDependencies: 769 | encoding "^0.1.13" 770 | 771 | minipass-flush@^1.0.5: 772 | version "1.0.5" 773 | resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" 774 | integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== 775 | dependencies: 776 | minipass "^3.0.0" 777 | 778 | minipass-pipeline@^1.2.4: 779 | version "1.2.4" 780 | resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" 781 | integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== 782 | dependencies: 783 | minipass "^3.0.0" 784 | 785 | minipass-sized@^1.0.3: 786 | version "1.0.3" 787 | resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" 788 | integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== 789 | dependencies: 790 | minipass "^3.0.0" 791 | 792 | minipass@^2.6.0, minipass@^2.9.0: 793 | version "2.9.0" 794 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" 795 | integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== 796 | dependencies: 797 | safe-buffer "^5.1.2" 798 | yallist "^3.0.0" 799 | 800 | minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: 801 | version "3.3.6" 802 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" 803 | integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== 804 | dependencies: 805 | yallist "^4.0.0" 806 | 807 | minipass@^5.0.0: 808 | version "5.0.0" 809 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" 810 | integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== 811 | 812 | minizlib@^1.3.3: 813 | version "1.3.3" 814 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" 815 | integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== 816 | dependencies: 817 | minipass "^2.9.0" 818 | 819 | minizlib@^2.1.1, minizlib@^2.1.2: 820 | version "2.1.2" 821 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" 822 | integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== 823 | dependencies: 824 | minipass "^3.0.0" 825 | yallist "^4.0.0" 826 | 827 | mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: 828 | version "0.5.3" 829 | resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" 830 | integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== 831 | 832 | mkdirp@^0.5.3, mkdirp@^0.5.5: 833 | version "0.5.6" 834 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" 835 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== 836 | dependencies: 837 | minimist "^1.2.6" 838 | 839 | mkdirp@^1.0.3, mkdirp@^1.0.4: 840 | version "1.0.4" 841 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 842 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 843 | 844 | ms@2.1.2: 845 | version "2.1.2" 846 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 847 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 848 | 849 | ms@^2.0.0, ms@^2.1.1: 850 | version "2.1.3" 851 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 852 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 853 | 854 | nan@2.17.0: 855 | version "2.17.0" 856 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" 857 | integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== 858 | 859 | nan@^2.14.1: 860 | version "2.20.0" 861 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" 862 | integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== 863 | 864 | napi-build-utils@^1.0.1: 865 | version "1.0.2" 866 | resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" 867 | integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== 868 | 869 | napi-macros@~2.0.0: 870 | version "2.0.0" 871 | resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" 872 | integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== 873 | 874 | native-hello-world@2.0.0: 875 | version "2.0.0" 876 | resolved "https://registry.yarnpkg.com/native-hello-world/-/native-hello-world-2.0.0.tgz#b3930ee0e2b691d4a27c7f103b847a26d7a85578" 877 | integrity sha512-XHio2BW/hM9WEpoiBGWRzG8f4ht0CP2RrR2ejCM5RbWBZHL56Sh82qseFMt6lOnFgoAyD0LGW4HuNVF33NSwag== 878 | dependencies: 879 | bindings "^1.2.1" 880 | 881 | needle@^2.5.0: 882 | version "2.9.1" 883 | resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" 884 | integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== 885 | dependencies: 886 | debug "^3.2.6" 887 | iconv-lite "^0.4.4" 888 | sax "^1.2.4" 889 | 890 | negotiator@^0.6.3: 891 | version "0.6.4" 892 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" 893 | integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== 894 | 895 | node-abi@^3.3.0: 896 | version "3.71.0" 897 | resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038" 898 | integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw== 899 | dependencies: 900 | semver "^7.3.5" 901 | 902 | node-addon-api@^1.6.2: 903 | version "1.7.2" 904 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" 905 | integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== 906 | 907 | node-addon-api@^3.0.0: 908 | version "3.2.1" 909 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" 910 | integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== 911 | 912 | node-addon-api@^4.2.0: 913 | version "4.3.0" 914 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" 915 | integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== 916 | 917 | node-addon-api@^5.1.0: 918 | version "5.1.0" 919 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" 920 | integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== 921 | 922 | node-fetch@^2.6.7: 923 | version "2.7.0" 924 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" 925 | integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== 926 | dependencies: 927 | whatwg-url "^5.0.0" 928 | 929 | node-gyp-build@~4.1.0: 930 | version "4.1.1" 931 | resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" 932 | integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== 933 | 934 | node-gyp@8.x, "node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2": 935 | version "10.2.0-electron.1" 936 | resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2" 937 | dependencies: 938 | env-paths "^2.2.0" 939 | exponential-backoff "^3.1.1" 940 | glob "^8.1.0" 941 | graceful-fs "^4.2.6" 942 | make-fetch-happen "^10.2.1" 943 | nopt "^6.0.0" 944 | proc-log "^2.0.1" 945 | semver "^7.3.5" 946 | tar "^6.2.1" 947 | which "^2.0.2" 948 | 949 | node-pre-gyp@0.15.0: 950 | version "0.15.0" 951 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz#c2fc383276b74c7ffa842925241553e8b40f1087" 952 | integrity sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA== 953 | dependencies: 954 | detect-libc "^1.0.2" 955 | mkdirp "^0.5.3" 956 | needle "^2.5.0" 957 | nopt "^4.0.1" 958 | npm-packlist "^1.1.6" 959 | npmlog "^4.0.2" 960 | rc "^1.2.7" 961 | rimraf "^2.6.1" 962 | semver "^5.3.0" 963 | tar "^4.4.2" 964 | 965 | nopt@^4.0.1: 966 | version "4.0.3" 967 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" 968 | integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== 969 | dependencies: 970 | abbrev "1" 971 | osenv "^0.1.4" 972 | 973 | nopt@^5.0.0: 974 | version "5.0.0" 975 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" 976 | integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== 977 | dependencies: 978 | abbrev "1" 979 | 980 | nopt@^6.0.0: 981 | version "6.0.0" 982 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" 983 | integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== 984 | dependencies: 985 | abbrev "^1.0.0" 986 | 987 | npm-bundled@^1.0.1: 988 | version "1.1.2" 989 | resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" 990 | integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== 991 | dependencies: 992 | npm-normalize-package-bin "^1.0.1" 993 | 994 | npm-normalize-package-bin@^1.0.1: 995 | version "1.0.1" 996 | resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" 997 | integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== 998 | 999 | npm-packlist@^1.1.6: 1000 | version "1.4.8" 1001 | resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" 1002 | integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== 1003 | dependencies: 1004 | ignore-walk "^3.0.1" 1005 | npm-bundled "^1.0.1" 1006 | npm-normalize-package-bin "^1.0.1" 1007 | 1008 | npmlog@^4.0.2: 1009 | version "4.1.2" 1010 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" 1011 | integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== 1012 | dependencies: 1013 | are-we-there-yet "~1.1.2" 1014 | console-control-strings "~1.1.0" 1015 | gauge "~2.7.3" 1016 | set-blocking "~2.0.0" 1017 | 1018 | npmlog@^5.0.1: 1019 | version "5.0.1" 1020 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" 1021 | integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== 1022 | dependencies: 1023 | are-we-there-yet "^2.0.0" 1024 | console-control-strings "^1.1.0" 1025 | gauge "^3.0.0" 1026 | set-blocking "^2.0.0" 1027 | 1028 | number-is-nan@^1.0.0: 1029 | version "1.0.1" 1030 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1031 | integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== 1032 | 1033 | object-assign@^4.1.0, object-assign@^4.1.1: 1034 | version "4.1.1" 1035 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1036 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 1037 | 1038 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 1039 | version "1.4.0" 1040 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1041 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1042 | dependencies: 1043 | wrappy "1" 1044 | 1045 | opencollective-postinstall@^2.0.0: 1046 | version "2.0.3" 1047 | resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" 1048 | integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== 1049 | 1050 | os-homedir@^1.0.0: 1051 | version "1.0.2" 1052 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1053 | integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== 1054 | 1055 | os-tmpdir@^1.0.0: 1056 | version "1.0.2" 1057 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1058 | integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== 1059 | 1060 | osenv@^0.1.4: 1061 | version "0.1.5" 1062 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" 1063 | integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== 1064 | dependencies: 1065 | os-homedir "^1.0.0" 1066 | os-tmpdir "^1.0.0" 1067 | 1068 | p-map@^4.0.0: 1069 | version "4.0.0" 1070 | resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" 1071 | integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== 1072 | dependencies: 1073 | aggregate-error "^3.0.0" 1074 | 1075 | path-is-absolute@^1.0.0: 1076 | version "1.0.1" 1077 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1078 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1079 | 1080 | prebuild-install@^7.1.2: 1081 | version "7.1.2" 1082 | resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" 1083 | integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== 1084 | dependencies: 1085 | detect-libc "^2.0.0" 1086 | expand-template "^2.0.3" 1087 | github-from-package "0.0.0" 1088 | minimist "^1.2.3" 1089 | mkdirp-classic "^0.5.3" 1090 | napi-build-utils "^1.0.1" 1091 | node-abi "^3.3.0" 1092 | pump "^3.0.0" 1093 | rc "^1.2.7" 1094 | simple-get "^4.0.0" 1095 | tar-fs "^2.0.0" 1096 | tunnel-agent "^0.6.0" 1097 | 1098 | proc-log@^2.0.1: 1099 | version "2.0.1" 1100 | resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" 1101 | integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== 1102 | 1103 | process-nextick-args@~2.0.0: 1104 | version "2.0.1" 1105 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 1106 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 1107 | 1108 | promise-inflight@^1.0.1: 1109 | version "1.0.1" 1110 | resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" 1111 | integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== 1112 | 1113 | promise-retry@^2.0.1: 1114 | version "2.0.1" 1115 | resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" 1116 | integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== 1117 | dependencies: 1118 | err-code "^2.0.2" 1119 | retry "^0.12.0" 1120 | 1121 | prr@~1.0.1: 1122 | version "1.0.1" 1123 | resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" 1124 | integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== 1125 | 1126 | pump@^3.0.0: 1127 | version "3.0.0" 1128 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 1129 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 1130 | dependencies: 1131 | end-of-stream "^1.1.0" 1132 | once "^1.3.1" 1133 | 1134 | rc@^1.2.7: 1135 | version "1.2.8" 1136 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 1137 | integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== 1138 | dependencies: 1139 | deep-extend "^0.6.0" 1140 | ini "~1.3.0" 1141 | minimist "^1.2.0" 1142 | strip-json-comments "~2.0.1" 1143 | 1144 | readable-stream@^2.0.6: 1145 | version "2.3.8" 1146 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" 1147 | integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== 1148 | dependencies: 1149 | core-util-is "~1.0.0" 1150 | inherits "~2.0.3" 1151 | isarray "~1.0.0" 1152 | process-nextick-args "~2.0.0" 1153 | safe-buffer "~5.1.1" 1154 | string_decoder "~1.1.1" 1155 | util-deprecate "~1.0.1" 1156 | 1157 | readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: 1158 | version "3.6.2" 1159 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" 1160 | integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== 1161 | dependencies: 1162 | inherits "^2.0.3" 1163 | string_decoder "^1.1.1" 1164 | util-deprecate "^1.0.1" 1165 | 1166 | ref-napi@1.4.2: 1167 | version "1.4.2" 1168 | resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-1.4.2.tgz#28ee242de131cd4fbbdd7d935086996d3cb9abc8" 1169 | integrity sha512-6AkdfqTLmP9oHQ6/aTnuIoPlVble6LHZ2wWqC1Sh/LWhnXHoT2L3CvyF72rJQ9w76XR5v9rIX6UQUwsry1vfBg== 1170 | dependencies: 1171 | bindings "^1.3.0" 1172 | debug "^3.1.0" 1173 | node-addon-api "^1.6.2" 1174 | 1175 | retry@^0.12.0: 1176 | version "0.12.0" 1177 | resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" 1178 | integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== 1179 | 1180 | rimraf@^2.6.1: 1181 | version "2.7.1" 1182 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 1183 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 1184 | dependencies: 1185 | glob "^7.1.3" 1186 | 1187 | rimraf@^3.0.2: 1188 | version "3.0.2" 1189 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 1190 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1191 | dependencies: 1192 | glob "^7.1.3" 1193 | 1194 | safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: 1195 | version "5.2.1" 1196 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1197 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1198 | 1199 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1200 | version "5.1.2" 1201 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1202 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1203 | 1204 | "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": 1205 | version "2.1.2" 1206 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1207 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1208 | 1209 | sax@^1.2.4: 1210 | version "1.4.1" 1211 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" 1212 | integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== 1213 | 1214 | semver@^5.3.0, semver@^5.5.1: 1215 | version "5.7.2" 1216 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" 1217 | integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== 1218 | 1219 | semver@^6.0.0: 1220 | version "6.3.1" 1221 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" 1222 | integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== 1223 | 1224 | semver@^7.3.5: 1225 | version "7.6.2" 1226 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" 1227 | integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== 1228 | 1229 | set-blocking@^2.0.0, set-blocking@~2.0.0: 1230 | version "2.0.0" 1231 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 1232 | integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== 1233 | 1234 | signal-exit@^3.0.0: 1235 | version "3.0.7" 1236 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" 1237 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== 1238 | 1239 | simple-concat@^1.0.0: 1240 | version "1.0.1" 1241 | resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" 1242 | integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== 1243 | 1244 | simple-get@^4.0.0: 1245 | version "4.0.1" 1246 | resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" 1247 | integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== 1248 | dependencies: 1249 | decompress-response "^6.0.0" 1250 | once "^1.3.1" 1251 | simple-concat "^1.0.0" 1252 | 1253 | smart-buffer@^4.2.0: 1254 | version "4.2.0" 1255 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" 1256 | integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== 1257 | 1258 | socks-proxy-agent@^7.0.0: 1259 | version "7.0.0" 1260 | resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" 1261 | integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== 1262 | dependencies: 1263 | agent-base "^6.0.2" 1264 | debug "^4.3.3" 1265 | socks "^2.6.2" 1266 | 1267 | socks@^2.6.2: 1268 | version "2.8.3" 1269 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" 1270 | integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== 1271 | dependencies: 1272 | ip-address "^9.0.5" 1273 | smart-buffer "^4.2.0" 1274 | 1275 | sprintf-js@^1.1.3: 1276 | version "1.1.3" 1277 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" 1278 | integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== 1279 | 1280 | sqlite3@5.1.6: 1281 | version "5.1.6" 1282 | resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.6.tgz#1d4fbc90fe4fbd51e952e0a90fd8f6c2b9098e97" 1283 | integrity sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw== 1284 | dependencies: 1285 | "@mapbox/node-pre-gyp" "^1.0.0" 1286 | node-addon-api "^4.2.0" 1287 | tar "^6.1.11" 1288 | optionalDependencies: 1289 | node-gyp "8.x" 1290 | 1291 | ssri@^9.0.0: 1292 | version "9.0.1" 1293 | resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" 1294 | integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== 1295 | dependencies: 1296 | minipass "^3.1.1" 1297 | 1298 | string-width@^1.0.1: 1299 | version "1.0.2" 1300 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1301 | integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== 1302 | dependencies: 1303 | code-point-at "^1.0.0" 1304 | is-fullwidth-code-point "^1.0.0" 1305 | strip-ansi "^3.0.0" 1306 | 1307 | "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: 1308 | version "4.2.3" 1309 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1310 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1311 | dependencies: 1312 | emoji-regex "^8.0.0" 1313 | is-fullwidth-code-point "^3.0.0" 1314 | strip-ansi "^6.0.1" 1315 | 1316 | string_decoder@^1.1.1: 1317 | version "1.3.0" 1318 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 1319 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 1320 | dependencies: 1321 | safe-buffer "~5.2.0" 1322 | 1323 | string_decoder@~1.1.1: 1324 | version "1.1.1" 1325 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1326 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 1327 | dependencies: 1328 | safe-buffer "~5.1.0" 1329 | 1330 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 1331 | version "3.0.1" 1332 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1333 | integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== 1334 | dependencies: 1335 | ansi-regex "^2.0.0" 1336 | 1337 | strip-ansi@^6.0.1: 1338 | version "6.0.1" 1339 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1340 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1341 | dependencies: 1342 | ansi-regex "^5.0.1" 1343 | 1344 | strip-json-comments@~2.0.1: 1345 | version "2.0.1" 1346 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1347 | integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== 1348 | 1349 | tar-fs@^2.0.0: 1350 | version "2.1.3" 1351 | resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.3.tgz#fb3b8843a26b6f13a08e606f7922875eb1fbbf92" 1352 | integrity sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg== 1353 | dependencies: 1354 | chownr "^1.1.1" 1355 | mkdirp-classic "^0.5.2" 1356 | pump "^3.0.0" 1357 | tar-stream "^2.1.4" 1358 | 1359 | tar-stream@^2.1.4: 1360 | version "2.2.0" 1361 | resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" 1362 | integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== 1363 | dependencies: 1364 | bl "^4.0.3" 1365 | end-of-stream "^1.4.1" 1366 | fs-constants "^1.0.0" 1367 | inherits "^2.0.3" 1368 | readable-stream "^3.1.1" 1369 | 1370 | tar@^4.4.2: 1371 | version "4.4.19" 1372 | resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" 1373 | integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== 1374 | dependencies: 1375 | chownr "^1.1.4" 1376 | fs-minipass "^1.2.7" 1377 | minipass "^2.9.0" 1378 | minizlib "^1.3.3" 1379 | mkdirp "^0.5.5" 1380 | safe-buffer "^5.2.1" 1381 | yallist "^3.1.1" 1382 | 1383 | tar@^6.1.11, tar@^6.2.1: 1384 | version "6.2.1" 1385 | resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" 1386 | integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== 1387 | dependencies: 1388 | chownr "^2.0.0" 1389 | fs-minipass "^2.0.0" 1390 | minipass "^5.0.0" 1391 | minizlib "^2.1.1" 1392 | mkdirp "^1.0.3" 1393 | yallist "^4.0.0" 1394 | 1395 | tr46@~0.0.3: 1396 | version "0.0.3" 1397 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" 1398 | integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== 1399 | 1400 | tunnel-agent@^0.6.0: 1401 | version "0.6.0" 1402 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" 1403 | integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== 1404 | dependencies: 1405 | safe-buffer "^5.0.1" 1406 | 1407 | unique-filename@^2.0.0: 1408 | version "2.0.1" 1409 | resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" 1410 | integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== 1411 | dependencies: 1412 | unique-slug "^3.0.0" 1413 | 1414 | unique-slug@^3.0.0: 1415 | version "3.0.0" 1416 | resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" 1417 | integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== 1418 | dependencies: 1419 | imurmurhash "^0.1.4" 1420 | 1421 | util-deprecate@^1.0.1, util-deprecate@~1.0.1: 1422 | version "1.0.2" 1423 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1424 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== 1425 | 1426 | webidl-conversions@^3.0.0: 1427 | version "3.0.1" 1428 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" 1429 | integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== 1430 | 1431 | whatwg-url@^5.0.0: 1432 | version "5.0.0" 1433 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" 1434 | integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== 1435 | dependencies: 1436 | tr46 "~0.0.3" 1437 | webidl-conversions "^3.0.0" 1438 | 1439 | which@^2.0.2: 1440 | version "2.0.2" 1441 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1442 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1443 | dependencies: 1444 | isexe "^2.0.0" 1445 | 1446 | wide-align@^1.1.0, wide-align@^1.1.2: 1447 | version "1.1.5" 1448 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" 1449 | integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== 1450 | dependencies: 1451 | string-width "^1.0.2 || 2 || 3 || 4" 1452 | 1453 | windows-active-process@1.1.1: 1454 | version "1.1.1" 1455 | resolved "https://registry.yarnpkg.com/windows-active-process/-/windows-active-process-1.1.1.tgz#e64605d8a3f6d8811ea290fdbcec3f885e41f11c" 1456 | integrity sha512-L/ZF2JxmqQZxp8gPWTu4BOLsVms/ykZVdkkfQj4xtXvZiPOqRAiuI5gvjQS6WfXcRpSm6j8HrgB1KyG9/I2Mcg== 1457 | dependencies: 1458 | bindings "^1.2.1" 1459 | 1460 | wrappy@1: 1461 | version "1.0.2" 1462 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1463 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1464 | 1465 | xtend@^4.0.2, xtend@~4.0.0: 1466 | version "4.0.2" 1467 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1468 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1469 | 1470 | yallist@^3.0.0, yallist@^3.1.1: 1471 | version "3.1.1" 1472 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 1473 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== 1474 | 1475 | yallist@^4.0.0: 1476 | version "4.0.0" 1477 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1478 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1479 | -------------------------------------------------------------------------------- /test/fixture/prebuildify/abi/prebuilds/linux-x64/electron.abi89.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/electron/rebuild/ec9abc8af81e8d732bd28844c5d8486ab2f208bf/test/fixture/prebuildify/abi/prebuilds/linux-x64/electron.abi89.node -------------------------------------------------------------------------------- /test/fixture/prebuildify/has-prebuildify-devdep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "prebuildify": "^1.0.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/prebuildify/napi/prebuilds/linux-arm/node.napi.armv7.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/electron/rebuild/ec9abc8af81e8d732bd28844c5d8486ab2f208bf/test/fixture/prebuildify/napi/prebuilds/linux-arm/node.napi.armv7.node -------------------------------------------------------------------------------- /test/fixture/prebuildify/napi/prebuilds/linux-arm64/electron.napi.armv8.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/electron/rebuild/ec9abc8af81e8d732bd28844c5d8486ab2f208bf/test/fixture/prebuildify/napi/prebuilds/linux-arm64/electron.napi.armv8.node -------------------------------------------------------------------------------- /test/fixture/prebuildify/napi/prebuilds/linux-x64/node.napi.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/electron/rebuild/ec9abc8af81e8d732bd28844c5d8486ab2f208bf/test/fixture/prebuildify/napi/prebuilds/linux-x64/node.napi.node -------------------------------------------------------------------------------- /test/fixture/prebuildify/no-prebuildify-devdep/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixture/prebuildify/not-found/prebuilds/electron.abi0.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/electron/rebuild/ec9abc8af81e8d732bd28844c5d8486ab2f208bf/test/fixture/prebuildify/not-found/prebuilds/electron.abi0.node -------------------------------------------------------------------------------- /test/fixture/workspace-test/child-workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace-app", 3 | "productName": "Workspace App", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "src/index.js", 7 | "keywords": [], 8 | "author": "", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "sleep": "6.3.0" 12 | }, 13 | "dependencies": { 14 | "snappy": "6.3.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixture/workspace-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "child-workspace" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/helpers/electron-version.ts: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import path from 'node:path'; 3 | import electron from 'electron'; 4 | 5 | function electronVersionPath() { 6 | const electronPath = electron as unknown as string; 7 | if (process.platform === 'darwin') { 8 | return path.resolve(path.dirname(electronPath), '..', '..', '..', 'version'); 9 | } else { 10 | return path.join(path.dirname(electronPath), 'version'); 11 | } 12 | } 13 | 14 | export function getExactElectronVersionSync(): string { 15 | return fs.readFileSync(electronVersionPath()).toString().trim(); 16 | } 17 | -------------------------------------------------------------------------------- /test/helpers/module-setup.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import crypto from 'node:crypto'; 3 | import fs from 'graceful-fs'; 4 | import os from 'node:os'; 5 | import path from 'node:path'; 6 | import { spawn } from '@malept/cross-spawn-promise'; 7 | 8 | const d = debug('electron-rebuild'); 9 | 10 | const originalGypMSVSVersion: string | undefined = process.env.GYP_MSVS_VERSION; 11 | const TIMEOUT_IN_MINUTES = process.platform === 'win32' ? 5 : 2; 12 | 13 | export const MINUTES_IN_MILLISECONDS = 60 * 1000; 14 | export const TIMEOUT_IN_MILLISECONDS = TIMEOUT_IN_MINUTES * MINUTES_IN_MILLISECONDS; 15 | 16 | export const TEST_MODULE_PATH = path.resolve(os.tmpdir(), 'electron-rebuild-test'); 17 | 18 | export function resetMSVSVersion(): void { 19 | if (originalGypMSVSVersion) { 20 | process.env.GYP_MSVS_VERSION = originalGypMSVSVersion; 21 | } 22 | } 23 | 24 | const testModuleTmpPath = fs.mkdtempSync(path.resolve(os.tmpdir(), 'e-r-test-module-')); 25 | 26 | export async function resetTestModule(testModulePath: string, installModules = true, fixtureName = 'native-app1'): Promise { 27 | /** 28 | * Remove the `--import` CLI flag added by Mocha so Node doesn't try to load 29 | * `tsx` when we spawn the `node-gyp` worker, which would fail because the 30 | * test fixtures don't include that dependency. 31 | */ 32 | process.execArgv = process.execArgv.filter(item => item !== '--import=tsx'); 33 | 34 | const oneTimeModulePath = path.resolve(testModuleTmpPath, `${crypto.createHash('SHA1').update(testModulePath).digest('hex')}-${fixtureName}-${installModules}`); 35 | if (!fs.existsSync(oneTimeModulePath)) { 36 | d(`creating test module '%s' in %s`, fixtureName, oneTimeModulePath); 37 | await fs.promises.mkdir(oneTimeModulePath, { recursive: true }); 38 | await fs.promises.cp(path.resolve(import.meta.dirname, `../../test/fixture/${ fixtureName }`), oneTimeModulePath, { recursive: true, force: true }); 39 | if (installModules) { 40 | await spawn('yarn', ['install'], { cwd: oneTimeModulePath }); 41 | } 42 | } 43 | await fs.promises.rm(testModulePath, { recursive: true, force: true }); 44 | await fs.promises.cp(oneTimeModulePath, testModulePath, { recursive: true, force: true }); 45 | resetMSVSVersion(); 46 | } 47 | 48 | export async function cleanupTestModule(testModulePath: string): Promise { 49 | await fs.promises.rm(testModulePath, { recursive: true, force: true, maxRetries: 10 }); 50 | resetMSVSVersion(); 51 | } 52 | 53 | process.on('exit', () => { 54 | fs.rmSync(testModuleTmpPath, { recursive: true, force: true }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/helpers/rebuild.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import fs from 'graceful-fs'; 3 | import path from 'node:path'; 4 | 5 | type ExpectRebuildOptions = { 6 | buildType?: string; 7 | metaShouldExist?: boolean; 8 | }; 9 | 10 | export async function expectNativeModuleToBeRebuilt( 11 | basePath: string, 12 | modulePath: string, 13 | options: ExpectRebuildOptions = {}, 14 | ): Promise { 15 | const metaShouldExist = Object.prototype.hasOwnProperty.call(options, 'metaShouldExist') ? options.metaShouldExist : true; 16 | const message = `${path.basename(modulePath)} build meta should ${metaShouldExist ? '' : 'not '}exist`; 17 | const buildType = options.buildType || 'Release'; 18 | const metaPath = path.resolve(basePath, 'node_modules', modulePath, 'build', buildType, '.forge-meta'); 19 | expect(fs.existsSync(metaPath), message).to.equal(metaShouldExist); 20 | } 21 | 22 | export async function expectNativeModuleToNotBeRebuilt( 23 | basePath: string, 24 | modulePath: string, 25 | options: ExpectRebuildOptions = {}, 26 | ): Promise { 27 | await expectNativeModuleToBeRebuilt(basePath, modulePath, { ...options, metaShouldExist: false }); 28 | } 29 | -------------------------------------------------------------------------------- /test/module-type-node-gyp.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'node:events'; 2 | import chai, { expect } from 'chai'; 3 | import chaiAsPromised from 'chai-as-promised'; 4 | 5 | import { cleanupTestModule, resetTestModule, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup.js'; 6 | import { NodeGyp } from '../lib/module-type/node-gyp/node-gyp.js'; 7 | import { Rebuilder } from '../lib/rebuild.js'; 8 | 9 | chai.use(chaiAsPromised); 10 | 11 | describe('node-gyp', () => { 12 | describe('buildArgs', () => { 13 | 14 | before(async () => await resetTestModule(testModulePath, false)); 15 | after(async () => await cleanupTestModule(testModulePath)); 16 | 17 | function nodeGypArgsForElectronVersion(electronVersion: string): Promise { 18 | const rebuilder = new Rebuilder({ 19 | buildPath: testModulePath, 20 | electronVersion: electronVersion, 21 | lifecycle: new EventEmitter() 22 | }); 23 | const nodeGyp = new NodeGyp(rebuilder, testModulePath); 24 | return nodeGyp.buildArgs([]); 25 | } 26 | 27 | context('sufficiently old Electron versions which lack a bundled config.gypi', () => { 28 | it('adds --force-process-config for < 14', async () => { 29 | const args = await nodeGypArgsForElectronVersion('12.0.0'); 30 | expect(args).to.include('--force-process-config'); 31 | }); 32 | 33 | it('adds --force-process-config for between 14.0.0 and < 14.2.0', async () => { 34 | const args = await nodeGypArgsForElectronVersion('14.1.0'); 35 | expect(args).to.include('--force-process-config'); 36 | }); 37 | 38 | it('adds --force-process-config for versions between 15.0.0 and < 15.3.0', async () => { 39 | const args = await nodeGypArgsForElectronVersion('15.2.0'); 40 | expect(args).to.include('--force-process-config'); 41 | }); 42 | }); 43 | 44 | context('for sufficiently new Electron versions', () => { 45 | it('does not add --force-process-config for ^14.2.0', async () => { 46 | const args = await nodeGypArgsForElectronVersion('14.2.0'); 47 | expect(args).to.not.include('--force-process-config'); 48 | }); 49 | 50 | it('does not add --force-process-config for ^15.3.0', async () => { 51 | const args = await nodeGypArgsForElectronVersion('15.3.0'); 52 | expect(args).to.not.include('--force-process-config'); 53 | }); 54 | 55 | it('does not add --force-process-config for >= 16.0.0', async () => { 56 | const args = await nodeGypArgsForElectronVersion('16.0.0-alpha.1'); 57 | expect(args).to.not.include('--force-process-config'); 58 | }); 59 | }); 60 | 61 | context('cross-compilation', async () => { 62 | it('throws error early if platform mismatch', async function () { 63 | let platform: NodeJS.Platform = 'darwin'; 64 | 65 | // we're verifying platform mismatch error throwing, not `rebuildModule` rebuilding. 66 | if (process.platform === platform) { 67 | platform = 'win32'; 68 | } 69 | 70 | const rebuilder = new Rebuilder({ 71 | buildPath: testModulePath, 72 | electronVersion: '15.3.0', 73 | lifecycle: new EventEmitter(), 74 | platform 75 | }); 76 | const nodeGyp = new NodeGyp(rebuilder, testModulePath); 77 | 78 | const errorMessage = "node-gyp does not support cross-compiling native modules from source."; 79 | expect(nodeGyp.rebuildModule()).to.eventually.be.rejectedWith(new Error(errorMessage)); 80 | }); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/module-type-node-pre-gyp.ts: -------------------------------------------------------------------------------- 1 | import chai, { expect } from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import { EventEmitter } from 'node:events'; 4 | import path from 'node:path'; 5 | 6 | import { cleanupTestModule, resetTestModule, TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup.js'; 7 | import { NodePreGyp } from '../lib/module-type/node-pre-gyp.js'; 8 | import { Rebuilder, RebuilderOptions } from '../lib/rebuild.js'; 9 | 10 | chai.use(chaiAsPromised); 11 | 12 | describe('node-pre-gyp', function () { 13 | this.timeout(TIMEOUT_IN_MILLISECONDS); 14 | 15 | const modulePath = path.join(testModulePath, 'node_modules', 'sqlite3'); 16 | const rebuilderArgs: RebuilderOptions = { 17 | buildPath: testModulePath, 18 | electronVersion: '8.0.0', 19 | arch: process.arch, 20 | lifecycle: new EventEmitter() 21 | }; 22 | 23 | before(async () => await resetTestModule(testModulePath)); 24 | after(async () => await cleanupTestModule(testModulePath)); 25 | 26 | describe('Node-API support', function() { 27 | it('should find correct napi version and select napi args', async () => { 28 | const rebuilder = new Rebuilder(rebuilderArgs); 29 | const nodePreGyp = new NodePreGyp(rebuilder, modulePath); 30 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 31 | expect(nodePreGyp.nodeAPI.getNapiVersion((await nodePreGyp.getSupportedNapiVersions())!)).to.equal(3); 32 | expect(await nodePreGyp.getNodePreGypRuntimeArgs()).to.deep.equal([]); 33 | }); 34 | 35 | it('should not fail running node-pre-gyp', async () => { 36 | const rebuilder = new Rebuilder(rebuilderArgs); 37 | const nodePreGyp = new NodePreGyp(rebuilder, modulePath); 38 | expect(await nodePreGyp.findPrebuiltModule()).to.equal(true); 39 | }); 40 | 41 | it('should throw error with unsupported Electron version', async () => { 42 | const rebuilder = new Rebuilder({ 43 | ...rebuilderArgs, 44 | electronVersion: '2.0.0', 45 | }); 46 | const nodePreGyp = new NodePreGyp(rebuilder, modulePath); 47 | expect(nodePreGyp.findPrebuiltModule()).to.eventually.be.rejectedWith("Native module 'sqlite3' requires Node-API but Electron v2.0.0 does not support Node-API"); 48 | }); 49 | }); 50 | 51 | it('should redownload the module if the architecture changes', async () => { 52 | let rebuilder = new Rebuilder(rebuilderArgs); 53 | let nodePreGyp = new NodePreGyp(rebuilder, modulePath); 54 | expect(await nodePreGyp.findPrebuiltModule()).to.equal(true); 55 | 56 | let alternativeArch: string; 57 | if (process.platform === 'win32') { 58 | alternativeArch = rebuilderArgs.arch === 'x64' ? 'ia32' : 'x64'; 59 | } else { 60 | alternativeArch = rebuilderArgs.arch === 'arm64' ? 'x64' : 'arm64'; 61 | } 62 | 63 | rebuilder = new Rebuilder({ ...rebuilderArgs, arch: alternativeArch }); 64 | nodePreGyp = new NodePreGyp(rebuilder, modulePath); 65 | expect(await nodePreGyp.findPrebuiltModule()).to.equal(true); 66 | }); 67 | 68 | it('should download for target platform', async () => { 69 | let rebuilder = new Rebuilder(rebuilderArgs); 70 | let nodePreGyp = new NodePreGyp(rebuilder, modulePath); 71 | expect(await nodePreGyp.findPrebuiltModule()).to.equal(true); 72 | 73 | let alternativePlatform: NodeJS.Platform; 74 | if (process.platform === 'win32') { 75 | alternativePlatform = 'darwin'; 76 | } else { 77 | alternativePlatform = 'win32'; 78 | } 79 | 80 | rebuilder = new Rebuilder({ ...rebuilderArgs, platform: alternativePlatform }); 81 | nodePreGyp = new NodePreGyp(rebuilder, modulePath); 82 | expect(await nodePreGyp.findPrebuiltModule()).to.equal(true); 83 | }); 84 | 85 | it('should find module fork', async () => { 86 | const rebuilder = new Rebuilder(rebuilderArgs); 87 | const nodePreGyp = new NodePreGyp(rebuilder, path.join(import.meta.dirname, 'fixture', 'forked-module-test')); 88 | expect(await nodePreGyp.usesTool()).to.equal(true); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/module-type-prebuild-install.ts: -------------------------------------------------------------------------------- 1 | import chai, { expect } from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import { EventEmitter } from 'node:events'; 4 | import path from 'node:path'; 5 | 6 | import { cleanupTestModule, resetTestModule, TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup.js'; 7 | import { PrebuildInstall } from '../lib/module-type/prebuild-install.js'; 8 | import { Rebuilder, RebuilderOptions } from '../lib/rebuild.js'; 9 | 10 | chai.use(chaiAsPromised); 11 | 12 | describe('prebuild-install', () => { 13 | const modulePath = path.join(testModulePath, 'node_modules', 'farmhash'); 14 | const rebuilderArgs: RebuilderOptions = { 15 | buildPath: testModulePath, 16 | electronVersion: '8.0.0', 17 | arch: process.arch, 18 | lifecycle: new EventEmitter() 19 | }; 20 | 21 | describe('Node-API support', function() { 22 | this.timeout(TIMEOUT_IN_MILLISECONDS); 23 | 24 | before(async () => await resetTestModule(testModulePath)); 25 | after(async () => await cleanupTestModule(testModulePath)); 26 | 27 | it('should find correct napi version and select napi args', async () => { 28 | const rebuilder = new Rebuilder(rebuilderArgs); 29 | const prebuildInstall = new PrebuildInstall(rebuilder, modulePath); 30 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 31 | expect(prebuildInstall.nodeAPI.getNapiVersion((await prebuildInstall.getSupportedNapiVersions())!)).to.equal(3); 32 | expect(await prebuildInstall.getPrebuildInstallRuntimeArgs()).to.deep.equal([ 33 | '--runtime=napi', 34 | `--target=3`, 35 | ]); 36 | }); 37 | 38 | it('should not fail running prebuild-install', async function () { 39 | const rebuilder = new Rebuilder(rebuilderArgs); 40 | const prebuildInstall = new PrebuildInstall(rebuilder, modulePath); 41 | expect(await prebuildInstall.findPrebuiltModule()).to.equal(true); 42 | }); 43 | 44 | it('should throw error with unsupported Electron version', async () => { 45 | const rebuilder = new Rebuilder({ 46 | ...rebuilderArgs, 47 | electronVersion: '2.0.0', 48 | }); 49 | const prebuildInstall = new PrebuildInstall(rebuilder, modulePath); 50 | expect(prebuildInstall.findPrebuiltModule()).to.eventually.be.rejectedWith("Native module 'farmhash' requires Node-API but Electron v2.0.0 does not support Node-API"); 51 | }); 52 | 53 | it('should download for target platform', async function () { 54 | let rebuilder = new Rebuilder(rebuilderArgs); 55 | let prebuild = new PrebuildInstall(rebuilder, modulePath); 56 | expect(await prebuild.findPrebuiltModule()).to.equal(true); 57 | 58 | let alternativePlatform: NodeJS.Platform; 59 | let arch = process.arch; 60 | 61 | if (process.platform === 'win32') { 62 | alternativePlatform = 'darwin'; 63 | } else { 64 | alternativePlatform = 'win32'; 65 | 66 | // farmhash has no prebuilt binaries for ARM64 Windows 67 | arch = 'x64'; 68 | } 69 | 70 | rebuilder = new Rebuilder({ ...rebuilderArgs, platform: alternativePlatform, arch }); 71 | prebuild = new PrebuildInstall(rebuilder, modulePath); 72 | expect(await prebuild.findPrebuiltModule()).to.equal(true); 73 | }); 74 | }); 75 | 76 | it('should find module fork', async () => { 77 | const rebuilder = new Rebuilder(rebuilderArgs); 78 | const prebuildInstall = new PrebuildInstall(rebuilder, path.join(import.meta.dirname, 'fixture', 'forked-module-test')); 79 | expect(await prebuildInstall.usesTool()).to.equal(true); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/module-type-prebuildify.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'node:events'; 2 | import { expect } from 'chai'; 3 | import path from 'node:path'; 4 | 5 | import { 6 | determineNativePrebuildArch, 7 | determineNativePrebuildExtension, 8 | Prebuildify 9 | } from '../lib/module-type/prebuildify.js'; 10 | import { Rebuilder, RebuilderOptions } from '../lib/rebuild.js'; 11 | 12 | describe('determineNativePrebuildArch', () => { 13 | it('returns arm if passed in armv7l', () => { 14 | expect(determineNativePrebuildArch('armv7l')).to.equal('arm'); 15 | }); 16 | 17 | it('returns the input arch if the input is not armv7l', () => { 18 | expect(determineNativePrebuildArch('x64')).to.equal('x64'); 19 | }); 20 | }); 21 | 22 | describe('determineNativePrebuildExtension', () => { 23 | it('returns armv8 suffix for an arm64 arch', () => { 24 | expect(determineNativePrebuildExtension('arm64')).to.equal('armv8.node'); 25 | }); 26 | 27 | it('returns armv7 suffix for an armv7l arch', () => { 28 | expect(determineNativePrebuildExtension('armv7l')).to.equal('armv7.node'); 29 | }); 30 | 31 | it('returns no suffix for non-ARM arches', () => { 32 | expect(determineNativePrebuildExtension('x64')).to.equal('node'); 33 | }); 34 | }); 35 | 36 | describe('prebuildify', () => { 37 | const fixtureBaseDir = path.join(import.meta.dirname, 'fixture', 'prebuildify'); 38 | const rebuilderArgs = { 39 | buildPath: 'nonexistent-path', 40 | electronVersion: '13.0.0', 41 | arch: 'x64', 42 | lifecycle: new EventEmitter() 43 | }; 44 | 45 | const createRebuilder = (args: Partial = {}): Rebuilder => { 46 | const rebuilder = new Rebuilder({ ...rebuilderArgs, ...args}); 47 | rebuilder.platform = 'linux'; 48 | return rebuilder; 49 | }; 50 | 51 | describe('usesTool', () => { 52 | it('succeeds if prebuildify exists in devDependencies', async () => { 53 | const rebuilder = createRebuilder(); 54 | const prebuildify = new Prebuildify(rebuilder, path.join(fixtureBaseDir, 'has-prebuildify-devdep')); 55 | expect(await prebuildify.usesTool()).to.equal(true); 56 | }); 57 | 58 | it('fails if prebuildify does not exist in devDependencies', async () => { 59 | const rebuilder = createRebuilder(); 60 | const prebuildify = new Prebuildify(rebuilder, path.join(fixtureBaseDir, 'no-prebuildify-devdep')); 61 | expect(await prebuildify.usesTool()).to.equal(false); 62 | }); 63 | }); 64 | 65 | describe('findPrebuiltModule', () => { 66 | describe('with no prebuilds directory', () => { 67 | it('should not find a prebuilt native module', async () => { 68 | const noPrebuildsDir = import.meta.dirname; 69 | const rebuilder = createRebuilder(); 70 | const prebuildify = new Prebuildify(rebuilder, noPrebuildsDir); 71 | expect(await prebuildify.findPrebuiltModule()).to.equal(false); 72 | }); 73 | }); 74 | 75 | describe('with prebuilt module for the given ABI', async () => { 76 | it('should find a prebuilt native module for x64/electron', async () => { 77 | const fixtureDir = path.join(fixtureBaseDir, 'abi'); 78 | const rebuilder = createRebuilder(); 79 | const prebuildify = new Prebuildify(rebuilder, fixtureDir); 80 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 81 | }); 82 | }); 83 | 84 | describe('with prebuilt Node-API module', async () => { 85 | it('should find a prebuilt native module for x64/node', async () => { 86 | const fixtureDir = path.join(fixtureBaseDir, 'napi'); 87 | const rebuilder = createRebuilder(); 88 | const prebuildify = new Prebuildify(rebuilder, fixtureDir); 89 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 90 | }); 91 | 92 | it('should find a prebuilt native module for armv7l/node', async () => { 93 | const fixtureDir = path.join(fixtureBaseDir, 'napi'); 94 | const rebuilder = createRebuilder({ arch: 'armv7l' }); 95 | const prebuildify = new Prebuildify(rebuilder, fixtureDir); 96 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 97 | }); 98 | 99 | it('should find a prebuilt native module for arm64/electron', async () => { 100 | const fixtureDir = path.join(fixtureBaseDir, 'napi'); 101 | const rebuilder = createRebuilder({ arch: 'arm64' }); 102 | const prebuildify = new Prebuildify(rebuilder, fixtureDir); 103 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 104 | }); 105 | }); 106 | 107 | describe('when it cannot find a prebuilt module', async () => { 108 | it('should not find a prebuilt native module', async () => { 109 | const fixtureDir = path.join(fixtureBaseDir, 'not-found'); 110 | const rebuilder = createRebuilder(); 111 | const prebuildify = new Prebuildify(rebuilder, fixtureDir); 112 | expect(await prebuildify.findPrebuiltModule()).to.equal(false); 113 | }); 114 | }); 115 | }); 116 | 117 | describe('cross-platform downloads', async () => { 118 | it('should download for target platform', async () => { 119 | const fixtureDir = path.join(fixtureBaseDir, 'napi'); 120 | let rebuilder = createRebuilder(); 121 | let prebuildify = new Prebuildify(rebuilder, fixtureDir); 122 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 123 | 124 | let alternativePlatform: NodeJS.Platform; 125 | if (process.platform === 'win32') { 126 | alternativePlatform = 'darwin'; 127 | } else { 128 | alternativePlatform = 'win32'; 129 | } 130 | 131 | rebuilder = createRebuilder({ platform: alternativePlatform }); 132 | prebuildify = new Prebuildify(rebuilder, fixtureDir); 133 | expect(await prebuildify.findPrebuiltModule()).to.equal(true); 134 | }); 135 | }); 136 | 137 | it('should find module fork', async () => { 138 | const rebuilder = createRebuilder(); 139 | const prebuildify = new Prebuildify(rebuilder, path.join(import.meta.dirname, 'fixture', 'forked-module-test')); 140 | expect(await prebuildify.usesTool()).to.equal(true); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/read-package-json.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { expect } from 'chai'; 3 | 4 | import { readPackageJson } from '../lib/read-package-json.js'; 5 | import { pathToFileURL } from 'node:url'; 6 | 7 | describe('read-package-json', () => { 8 | it('should find a package.json file from the given directory', async () => { 9 | expect(await readPackageJson(path.resolve(import.meta.dirname, '..'))).to.deep.equal( 10 | ( 11 | await import(pathToFileURL(path.join(import.meta.dirname, '../package.json')).toString(), { 12 | with: { type: 'json' }, 13 | }) 14 | ).default, 15 | ); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/rebuild-napibuildversion.ts: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import path from 'node:path'; 3 | 4 | import { expect } from 'chai'; 5 | import { rebuild } from '../lib/rebuild.js'; 6 | import { getExactElectronVersionSync } from './helpers/electron-version.js'; 7 | import { TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath, cleanupTestModule, resetTestModule } from './helpers/module-setup.js'; 8 | import { expectNativeModuleToBeRebuilt } from './helpers/rebuild.js'; 9 | import detectLibc from 'detect-libc'; 10 | 11 | const testElectronVersion = getExactElectronVersionSync(); 12 | 13 | describe('rebuild with napi_build_versions in binary config', async function () { 14 | this.timeout(TIMEOUT_IN_MILLISECONDS); 15 | 16 | const napiBuildVersion = 6; 17 | const napiBuildVersionSpecificPath = (arch: string, libc: string) => path.resolve(testModulePath, `node_modules/sqlite3/lib/binding/napi-v${ napiBuildVersion }-${ process.platform }-${ libc }-${ arch }/node_sqlite3.node`); 18 | 19 | before(async () => { 20 | await resetTestModule(testModulePath, true, 'napi-build-version'); 21 | // Forcing `msvs_version` needed in order for `arm64` `win32` binary to be built 22 | process.env.GYP_MSVS_VERSION = process.env.GYP_MSVS_VERSION ?? "2019"; 23 | }); 24 | after(() => cleanupTestModule(testModulePath)); 25 | 26 | // https://github.com/electron/rebuild/issues/554 27 | const archs = ['x64', 'arm64']; 28 | for (const arch of archs) { 29 | it(`${ arch } arch should have rebuilt binary with 'napi_build_versions' array and 'libc' provided`, async () => { 30 | const libc = await detectLibc.family() || 'unknown'; 31 | const binaryPath = napiBuildVersionSpecificPath(arch, libc); 32 | 33 | await fs.promises.rm(binaryPath, { recursive: true, force: true }); 34 | expect(fs.existsSync(binaryPath)).to.be.false; 35 | 36 | await rebuild({ 37 | buildPath: testModulePath, 38 | electronVersion: testElectronVersion, 39 | arch, 40 | buildFromSource: true, // need to skip node-pre-gyp prebuilt binary 41 | }); 42 | 43 | await expectNativeModuleToBeRebuilt(testModulePath, 'sqlite3'); 44 | expect(fs.existsSync(binaryPath)).to.be.true; 45 | }); 46 | } 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /test/rebuild-yarnworkspace.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | 3 | import { expectNativeModuleToBeRebuilt, expectNativeModuleToNotBeRebuilt } from './helpers/rebuild.js'; 4 | import { getExactElectronVersionSync } from './helpers/electron-version.js'; 5 | import { getProjectRootPath } from '../lib/search-module.js'; 6 | import { rebuild } from '../lib/rebuild.js'; 7 | import { TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath, cleanupTestModule, resetTestModule } from './helpers/module-setup.js'; 8 | 9 | const testElectronVersion = getExactElectronVersionSync(); 10 | 11 | describe('rebuild for yarn workspace', function() { 12 | this.timeout(TIMEOUT_IN_MILLISECONDS); 13 | 14 | describe('core behavior', () => { 15 | before(async () => { 16 | await resetTestModule(testModulePath, true, 'workspace-test'); 17 | const projectRootPath = await getProjectRootPath(path.join(testModulePath, 'workspace-test', 'child-workspace')); 18 | 19 | await rebuild({ 20 | buildPath: path.resolve(testModulePath, 'child-workspace'), 21 | electronVersion: testElectronVersion, 22 | arch: process.arch, 23 | projectRootPath 24 | }); 25 | }); 26 | after(() => cleanupTestModule(testModulePath)); 27 | 28 | it('should have rebuilt top level prod dependencies', async () => { 29 | await expectNativeModuleToBeRebuilt(testModulePath, 'snappy'); 30 | }); 31 | 32 | it('should not have rebuilt top level devDependencies', async () => { 33 | await expectNativeModuleToNotBeRebuilt(testModulePath, 'sleep'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/rebuild.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'node:events'; 2 | import { expect } from 'chai'; 3 | import fs from 'graceful-fs'; 4 | import path from 'node:path'; 5 | 6 | import { cleanupTestModule, MINUTES_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath, resetMSVSVersion, resetTestModule, TIMEOUT_IN_MILLISECONDS } from './helpers/module-setup.js'; 7 | import { expectNativeModuleToBeRebuilt, expectNativeModuleToNotBeRebuilt } from './helpers/rebuild.js'; 8 | import { getExactElectronVersionSync } from './helpers/electron-version.js'; 9 | import { Rebuilder, rebuild } from '../lib/rebuild.js'; 10 | import { promisifiedGracefulFs } from '../lib/promisifiedGracefulFs.js'; 11 | 12 | const testElectronVersion = getExactElectronVersionSync(); 13 | 14 | describe('rebuilder', () => { 15 | 16 | describe('core behavior', function() { 17 | this.timeout(TIMEOUT_IN_MILLISECONDS); 18 | 19 | before(async () => { 20 | await resetTestModule(testModulePath); 21 | 22 | await rebuild({ 23 | buildPath: testModulePath, 24 | electronVersion: testElectronVersion, 25 | arch: process.arch 26 | }); 27 | }); 28 | 29 | it('should have rebuilt top level prod dependencies', async () => { 30 | await expectNativeModuleToBeRebuilt(testModulePath, 'ref-napi'); 31 | }); 32 | 33 | it('should have rebuilt top level prod dependencies that are using prebuild', async () => { 34 | await expectNativeModuleToBeRebuilt(testModulePath, 'farmhash'); 35 | }); 36 | 37 | it('should have rebuilt children of top level prod dependencies', async () => { 38 | await expectNativeModuleToBeRebuilt(testModulePath, 'leveldown'); 39 | }); 40 | 41 | it('should have rebuilt children of scoped top level prod dependencies', async () => { 42 | await expectNativeModuleToBeRebuilt(testModulePath, '@newrelic/native-metrics'); 43 | }); 44 | 45 | it('should have rebuilt top level optional dependencies', async () => { 46 | await expectNativeModuleToBeRebuilt(testModulePath, 'bcrypt'); 47 | }); 48 | 49 | it('should not have rebuilt top level devDependencies', async () => { 50 | await expectNativeModuleToNotBeRebuilt(testModulePath, 'windows-active-process'); 51 | }); 52 | 53 | it('should not download files in the module directory', async () => { 54 | const modulePath = path.resolve(testModulePath, 'node_modules/ref-napi'); 55 | const fileNames = await promisifiedGracefulFs.readdir(modulePath); 56 | 57 | expect(fileNames).to.not.contain(testElectronVersion); 58 | }); 59 | 60 | after(async () => { 61 | await cleanupTestModule(testModulePath); 62 | }); 63 | }); 64 | 65 | describe('force rebuild', function() { 66 | this.timeout(TIMEOUT_IN_MILLISECONDS); 67 | 68 | before(async () => await resetTestModule(testModulePath)); 69 | after(async () => await cleanupTestModule(testModulePath)); 70 | afterEach(resetMSVSVersion); 71 | 72 | const buildPath = testModulePath; 73 | const electronVersion = testElectronVersion; 74 | const arch = process.arch; 75 | const extraModules: string[] = []; 76 | 77 | it('should skip the rebuild step when disabled', async () => { 78 | await rebuild({ buildPath, electronVersion, arch }); 79 | resetMSVSVersion(); 80 | const rebuilder = rebuild({ buildPath, electronVersion, arch, extraModules, force: false }); 81 | let skipped = 0; 82 | rebuilder.lifecycle.on('module-skip', () => { 83 | skipped++; 84 | }); 85 | await rebuilder; 86 | expect(skipped).to.equal(7); 87 | }); 88 | 89 | it('should rebuild all modules again when disabled but the electron ABI changed', async () => { 90 | await rebuild({ buildPath, electronVersion, arch }); 91 | resetMSVSVersion(); 92 | const rebuilder = rebuild({ buildPath, electronVersion: '3.0.0', arch, extraModules, force: false }); 93 | let skipped = 0; 94 | rebuilder.lifecycle.on('module-skip', () => { 95 | skipped++; 96 | }); 97 | await rebuilder; 98 | expect(skipped).to.equal(0); 99 | }); 100 | 101 | it('should rebuild all modules again when enabled', async function() { 102 | if (process.platform === 'darwin') { 103 | this.timeout(5 * MINUTES_IN_MILLISECONDS); 104 | } 105 | await rebuild({ buildPath, electronVersion, arch }); 106 | resetMSVSVersion(); 107 | const rebuilder = rebuild({ buildPath, electronVersion, arch, extraModules, force: true }); 108 | let skipped = 0; 109 | rebuilder.lifecycle.on('module-skip', () => { 110 | skipped++; 111 | }); 112 | await rebuilder; 113 | expect(skipped).to.equal(0); 114 | }); 115 | }); 116 | 117 | describe('ignore rebuild', function() { 118 | this.timeout(2 * MINUTES_IN_MILLISECONDS); 119 | 120 | before(async () => await resetTestModule(testModulePath)); 121 | after(async () => await cleanupTestModule(testModulePath)); 122 | afterEach(resetMSVSVersion); 123 | 124 | const buildPath = testModulePath; 125 | const electronVersion = testElectronVersion; 126 | const arch = process.arch; 127 | 128 | it('should rebuild all modules again when enabled', async function() { 129 | if (process.platform === 'win32') { 130 | this.timeout(5 * MINUTES_IN_MILLISECONDS); 131 | } 132 | await rebuild({ buildPath, electronVersion, arch }); 133 | resetMSVSVersion(); 134 | const rebuilder = rebuild({ buildPath, electronVersion, arch, ignoreModules: ['native-hello-world'], force: true }); 135 | let skipped = 0; 136 | rebuilder.lifecycle.on('module-skip', () => { 137 | skipped++; 138 | }); 139 | await rebuilder; 140 | expect(skipped).to.equal(1); 141 | }); 142 | }); 143 | 144 | describe('only rebuild', function() { 145 | this.timeout(2 * MINUTES_IN_MILLISECONDS); 146 | 147 | beforeEach(async () => await resetTestModule(testModulePath)); 148 | afterEach(async() => await cleanupTestModule(testModulePath)); 149 | 150 | it('should rebuild only specified modules', async () => { 151 | const nativeModuleBinary = path.join(testModulePath, 'node_modules', 'native-hello-world', 'build', 'Release', 'hello_world.node'); 152 | expect(fs.existsSync(nativeModuleBinary)).to.be.true; 153 | await fs.promises.rm(nativeModuleBinary, { recursive: true, force: true }); 154 | expect(fs.existsSync(nativeModuleBinary)).to.be.false; 155 | const rebuilder = rebuild({ 156 | buildPath: testModulePath, 157 | electronVersion: testElectronVersion, 158 | arch: process.arch, 159 | onlyModules: ['native-hello-world'], 160 | force: true 161 | }); 162 | let built = 0; 163 | rebuilder.lifecycle.on('module-done', () => built++); 164 | await rebuilder; 165 | expect(built).to.equal(1); 166 | expect(fs.existsSync(nativeModuleBinary)).to.be.true; 167 | }); 168 | 169 | it('should rebuild multiple specified modules via --only option', async () => { 170 | const rebuilder = rebuild({ 171 | buildPath: testModulePath, 172 | electronVersion: testElectronVersion, 173 | arch: process.arch, 174 | onlyModules: ['windows-active-process', 'ref-napi'], // TODO: check to see if there's a bug with scoped modules 175 | force: true 176 | }); 177 | let built = 0; 178 | rebuilder.lifecycle.on('module-done', () => built++); 179 | await rebuilder; 180 | expect(built).to.equal(2); 181 | }); 182 | }); 183 | 184 | describe('with extraModules', () => { 185 | it('should rebuild existing modules in extraModules despite them not being found during the module walk', async () => { 186 | const rebuilder = new Rebuilder({ 187 | buildPath: path.join(import.meta.dirname, 'fixture', 'empty-project'), 188 | electronVersion: testElectronVersion, 189 | lifecycle: new EventEmitter(), 190 | extraModules: ['extra'] 191 | }); 192 | const modulesToRebuild = await rebuilder.modulesToRebuild(); 193 | expect(modulesToRebuild).to.have.length(1); 194 | expect(modulesToRebuild[0].endsWith('extra')).to.be.true; 195 | }); 196 | }); 197 | 198 | describe('debug rebuild', function() { 199 | this.timeout(10 * MINUTES_IN_MILLISECONDS); 200 | 201 | before(async () => await resetTestModule(testModulePath)); 202 | after(async() => await cleanupTestModule(testModulePath)); 203 | 204 | it('should have rebuilt farmhash module in Debug mode', async () => { 205 | await rebuild({ 206 | buildPath: testModulePath, 207 | electronVersion: testElectronVersion, 208 | arch: process.arch, 209 | onlyModules: ['farmhash'], 210 | force: true, 211 | debug: true 212 | }); 213 | await expectNativeModuleToBeRebuilt(testModulePath, 'farmhash', { buildType: 'Debug' }); 214 | await expectNativeModuleToNotBeRebuilt(testModulePath, 'farmhash'); 215 | }); 216 | }); 217 | 218 | describe('useElectronClang rebuild', function() { 219 | this.timeout(10 * MINUTES_IN_MILLISECONDS); 220 | 221 | before(async () => await resetTestModule(testModulePath)); 222 | after(async() => await cleanupTestModule(testModulePath)); 223 | 224 | it('should have rebuilt farmhash module using clang mode', async () => { 225 | await rebuild({ 226 | buildPath: testModulePath, 227 | electronVersion: testElectronVersion, 228 | arch: process.arch, 229 | onlyModules: ['farmhash'], 230 | force: true, 231 | useElectronClang: true 232 | }); 233 | await expectNativeModuleToBeRebuilt(testModulePath, 'farmhash'); 234 | }); 235 | }); 236 | }); 237 | -------------------------------------------------------------------------------- /test/search-module.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import fs from 'graceful-fs'; 3 | import os from 'node:os'; 4 | import path from 'node:path'; 5 | 6 | import { getProjectRootPath } from '../lib/search-module.js'; 7 | import { promisifiedGracefulFs } from '../lib/promisifiedGracefulFs.js'; 8 | 9 | let baseDir: string; 10 | 11 | async function createTempDir(): Promise { 12 | baseDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'electron-rebuild-test-')); 13 | } 14 | 15 | async function removeTempDir(): Promise { 16 | await fs.promises.rm(baseDir, { recursive: true, force: true }); 17 | } 18 | 19 | describe('search-module', () => { 20 | describe('getProjectRootPath', () => { 21 | describe('multi-level workspace', () => { 22 | for (const lockFile of ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']) { 23 | describe(lockFile, () => { 24 | before(async () => { 25 | await createTempDir(); 26 | await fs.promises.cp(path.resolve(import.meta.dirname, 'fixture', 'multi-level-workspace'), baseDir, { recursive: true, force: true }); 27 | 28 | const lockfilePath = path.join(baseDir, lockFile); 29 | 30 | if(!fs.existsSync(lockfilePath)) { 31 | await fs.promises.mkdir(baseDir, { recursive: true }); 32 | await promisifiedGracefulFs.writeFile(lockfilePath, Buffer.from([]), {}); 33 | } 34 | }); 35 | 36 | it('finds the folder with the lockfile', async () => { 37 | const packageDir = path.join(baseDir, 'packages', 'bar'); 38 | expect(await getProjectRootPath(packageDir)).to.equal(baseDir); 39 | }); 40 | 41 | after(removeTempDir); 42 | }); 43 | } 44 | }); 45 | 46 | describe('no workspace', () => { 47 | before(createTempDir); 48 | 49 | it('returns the input directory if a lockfile cannot be found', async () => { 50 | expect(await getProjectRootPath(baseDir)).to.equal(baseDir); 51 | }); 52 | 53 | after(removeTempDir); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node22/tsconfig.json", 3 | "compilerOptions": { 4 | "removeComments": false, 5 | "preserveConstEnums": true, 6 | "sourceMap": true, 7 | "declaration": true, 8 | "noImplicitAny": true, 9 | "noImplicitReturns": true, 10 | "noUnusedLocals": true, 11 | "noImplicitThis": true, 12 | "noUnusedParameters": true, 13 | "pretty": true, 14 | "outDir": "lib", 15 | "types": ["node", "mocha"] 16 | }, 17 | "formatCodeOptions": { 18 | "indentSize": 2, 19 | "tabSize": 2 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "lib", 24 | "test" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /typings/ambient.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'node-gyp'; 2 | --------------------------------------------------------------------------------