(pkg: string, subpath: string, binPath: string)
| 103 | } |
| 104 | |
| 105 | function installUsingNPM(pkg: string, subpath: string, binPath: string): void { |
| 106 | // Erase "npm_config_global" so that "npm install --global esbuild" works. |
| 107 | // Otherwise this nested "npm install" will also be global, and the install |
| 108 | // will deadlock waiting for the global installation lock. |
| 109 | const env = { ...process.env, npm_config_global: undefined } |
| 110 | |
| 111 | // Create a temporary directory inside the "esbuild" package with an empty |
| 112 | // "package.json" file. We'll use this to run "npm install" in. |
| 113 | const esbuildLibDir = path.dirname(require.resolve('esbuild')) |
| 114 | const installDir = path.join(esbuildLibDir, 'npm-install') |
| 115 | fs.mkdirSync(installDir) |
| 116 | try { |
| 117 | fs.writeFileSync(path.join(installDir, 'package.json'), '{}') |
| 118 | |
| 119 | // Run "npm install" in the temporary directory which should download the |
| 120 | // desired package. Try to avoid unnecessary log output. This uses the "npm" |
| 121 | // command instead of a HTTP request so that it hopefully works in situations |
| 122 | // where HTTP requests are blocked but the "npm" command still works due to, |
| 123 | // for example, a custom configured npm registry and special firewall rules. |
| 124 | child_process.execSync(`npm install --loglevel=error --prefer-offline --no-audit --progress=false ${pkg}@${packageJSON.version}`, |
| 125 | { cwd: installDir, stdio: 'pipe', env }) |
| 126 | |
| 127 | // Move the downloaded binary executable into place. The destination path |
| 128 | // is the same one that the JavaScript API code uses so it will be able to |
| 129 | // find the binary executable here later. |
| 130 | const installedBinPath = path.join(installDir, 'node_modules', pkg, subpath) |
| 131 | binaryIntegrityCheck(pkg, subpath, fs.readFileSync(installedBinPath)) |
| 132 | fs.renameSync(installedBinPath, binPath) |
| 133 | } finally { |
| 134 | // Try to clean up afterward so we don't unnecessarily waste file system |
| 135 | // space. Leaving nested "node_modules" directories can also be problematic |
| 136 | // for certain tools that scan over the file tree and expect it to have a |
| 137 | // certain structure. |
| 138 | try { |
| 139 | removeRecursive(installDir) |
| 140 | } catch { |
| 141 | // Removing a file or directory can randomly break on Windows, returning |
| 142 | // EBUSY for an arbitrary length of time. I think this happens when some |
| 143 | // other program has that file or directory open (e.g. an anti-virus |
| 144 | // program). This is fine on Unix because the OS just unlinks the entry |
| 145 | // but keeps the reference around until it's unused. There's nothing we |
| 146 | // can do in this case so we just leave the directory there. |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | function removeRecursive(dir: string): void { |
| 152 | for (const entry of fs.readdirSync(dir)) { |
no test coverage detected
searching dependent graphs…