Comparing version 0.35.1 to 0.41.0
32
bin.js
@@ -12,17 +12,19 @@ #!/usr/bin/env node | ||
if (!fs.existsSync(exePath)) { | ||
require("./install_api").runInstall().then(() => { | ||
// I'm not sure why (I think due zip extraction), but the executable | ||
// doesn't seem fully ready unless waiting for the next tick | ||
setTimeout(() => { | ||
runDprintExe(); | ||
}, 0); | ||
}).catch(err => { | ||
console.error(err); | ||
try { | ||
const resolvedExePath = require("./install_api").runInstall(); | ||
runDprintExe(resolvedExePath); | ||
} catch (err) { | ||
if (err !== undefined && typeof err.message === "string") { | ||
console.error(err.message); | ||
} else { | ||
console.error(err); | ||
} | ||
process.exit(1); | ||
}); | ||
} | ||
} else { | ||
runDprintExe(); | ||
runDprintExe(exePath); | ||
} | ||
function runDprintExe() { | ||
/** @param exePath {string} */ | ||
function runDprintExe(exePath) { | ||
const result = child_process.spawnSync( | ||
@@ -40,8 +42,8 @@ exePath, | ||
process.exitCode = result.status; | ||
} | ||
function throwIfNoExePath() { | ||
if (!fs.existsSync(exePath)) { | ||
throw new Error("Could not find exe at path '" + exePath + "'. Maybe try running dprint again."); | ||
function throwIfNoExePath() { | ||
if (!fs.existsSync(exePath)) { | ||
throw new Error("Could not find exe at path '" + exePath + "'. Maybe try running dprint again."); | ||
} | ||
} | ||
} |
// @ts-check | ||
"use strict"; | ||
const crypto = require("crypto"); | ||
const fs = require("fs"); | ||
const https = require("https"); | ||
const os = require("os"); | ||
const path = require("path"); | ||
const url = require("url"); | ||
const HttpsProxyAgent = require("https-proxy-agent"); | ||
const yauzl = require("yauzl"); | ||
/** @type {string | undefined} */ | ||
let cachedIsMusl = undefined; | ||
function install() { | ||
const executableFilePath = path.join( | ||
__dirname, | ||
os.platform() === "win32" ? "dprint.exe" : "dprint", | ||
); | ||
module.exports = { | ||
runInstall() { | ||
const dprintFileName = os.platform() === "win32" ? "dprint.exe" : "dprint"; | ||
const targetExecutablePath = path.join( | ||
__dirname, | ||
dprintFileName, | ||
); | ||
if (fs.existsSync(executableFilePath)) { | ||
return Promise.resolve(); | ||
} | ||
const info = JSON.parse(fs.readFileSync(path.join(__dirname, "info.json"), "utf8")); | ||
const zipFilePath = path.join(__dirname, "dprint.zip"); | ||
const target = getTarget(); | ||
const downloadUrl = "https://github.com/dprint/dprint/releases/download/" | ||
+ info.version | ||
+ "/dprint-" + target + ".zip"; | ||
// remove the old zip file if it exists | ||
try { | ||
fs.unlinkSync(zipFilePath); | ||
} catch (err) { | ||
// ignore | ||
} | ||
// now try to download it | ||
return downloadZipFileWithRetries(downloadUrl).then(() => { | ||
verifyZipChecksum(); | ||
return extractZipFile().then(() => { | ||
// todo: how to just +x? does it matter? | ||
fs.chmodSync(executableFilePath, 0o755); | ||
// delete the zip file | ||
try { | ||
fs.unlinkSync(zipFilePath); | ||
} catch (err) { | ||
// ignore | ||
} | ||
}).catch(err => { | ||
throw new Error("Error extracting dprint zip file.\n\n" + err); | ||
}); | ||
}).catch(err => { | ||
throw new Error("Error downloading dprint zip file.\n\n" + err); | ||
}); | ||
function getTarget() { | ||
if (os.platform() === "win32") { | ||
return "x86_64-pc-windows-msvc"; | ||
} else if (os.platform() === "darwin") { | ||
return `${getArch()}-apple-darwin`; | ||
} else { | ||
return `${getArch()}-unknown-linux-${getLinuxFamily()}`; | ||
if (fs.existsSync(targetExecutablePath)) { | ||
return targetExecutablePath; | ||
} | ||
} | ||
function downloadZipFileWithRetries(url) { | ||
/** @param remaining {number} */ | ||
function download(remaining) { | ||
return downloadZipFile(url) | ||
.catch(err => { | ||
if (remaining === 0) { | ||
return Promise.reject(err); | ||
} else { | ||
console.error("Error downloading dprint zip file.", err); | ||
console.error("Retrying download (remaining: " + remaining + ")"); | ||
return download(remaining - 1); | ||
} | ||
}); | ||
} | ||
const target = getTarget(); | ||
const sourcePackagePath = path.dirname(require.resolve("@dprint/" + target + "/package.json")); | ||
const sourceExecutablePath = path.join(sourcePackagePath, dprintFileName); | ||
return download(3); | ||
} | ||
function downloadZipFile(url) { | ||
return new Promise((resolve, reject) => { | ||
const options = {}; | ||
const proxyUrl = getProxyUrl(url); | ||
if (proxyUrl != null) { | ||
options.agent = new HttpsProxyAgent(proxyUrl); | ||
} | ||
https.get(url, options, function(response) { | ||
if (response.statusCode != null && response.statusCode >= 200 && response.statusCode <= 299) { | ||
downloadResponse(response).then(resolve).catch(reject); | ||
} else if (response.headers.location) { | ||
downloadZipFile(response.headers.location).then(resolve).catch(reject); | ||
} else { | ||
reject(new Error("Unknown status code " + response.statusCode + " : " + response.statusMessage)); | ||
} | ||
}).on("error", function(err) { | ||
try { | ||
fs.unlinkSync(zipFilePath); | ||
} catch (err) { | ||
// ignore | ||
} | ||
reject(err); | ||
}); | ||
}); | ||
/** @param response {import("http").IncomingMessage} */ | ||
function downloadResponse(response) { | ||
return new Promise((resolve, reject) => { | ||
const file = fs.createWriteStream(zipFilePath); | ||
response.pipe(file); | ||
file.on("finish", function() { | ||
file.close((err) => { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(undefined); | ||
} | ||
}); | ||
}); | ||
}); | ||
if (!fs.existsSync(sourceExecutablePath)) { | ||
throw new Error("Could not find executable for @dprint/" + target + " at " + sourceExecutablePath); | ||
} | ||
} | ||
function getProxyUrl(requestUrl) { | ||
try { | ||
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; | ||
if (typeof proxyUrl !== "string" || proxyUrl.length === 0) { | ||
return undefined; | ||
if (process.env.DPRINT_SIMULATED_READONLY_FILE_SYSTEM === "1") { | ||
console.warn("Simulating readonly file system for testing."); | ||
throw new Error("Throwing for testing purposes."); | ||
} | ||
if (typeof process.env.NO_PROXY === "string") { | ||
const noProxyAddresses = process.env.NO_PROXY.split(","); | ||
const host = url.parse(requestUrl).host; | ||
if (host == null || noProxyAddresses.indexOf(host) >= 0) { | ||
return undefined; | ||
} | ||
// in order to make things faster the next time we run and to allow the | ||
// dprint vscode extension to easily pick this up, copy the executable | ||
// into the dprint package folder | ||
atomicCopyFileSync(sourceExecutablePath, targetExecutablePath); | ||
if (os.platform() !== "win32") { | ||
// chomd +x | ||
chmodX(targetExecutablePath); | ||
} | ||
return proxyUrl; | ||
return targetExecutablePath; | ||
} catch (err) { | ||
console.error("[dprint]: Error getting proxy url.", err); | ||
return undefined; | ||
} | ||
} | ||
function verifyZipChecksum() { | ||
const fileData = fs.readFileSync(zipFilePath); | ||
const actualZipChecksum = crypto.createHash("sha256").update(fileData).digest("hex").toLowerCase(); | ||
const expectedZipChecksum = getExpectedZipChecksum().toLowerCase(); | ||
if (actualZipChecksum !== expectedZipChecksum) { | ||
throw new Error( | ||
"Downloaded dprint zip checksum did not match the expected checksum (Actual: " | ||
+ actualZipChecksum | ||
+ ", Expected: " | ||
+ expectedZipChecksum | ||
+ ").", | ||
); | ||
} | ||
function getExpectedZipChecksum() { | ||
const checksum = info.checksums[getTarget()]; | ||
if (checksum == null) { | ||
throw new Error("Could not find checksum for target: " + checksum); | ||
// this may fail on readonly file systems... in this case, fall | ||
// back to using the resolved package path | ||
if (process.env.DPRINT_DEBUG === "1") { | ||
console.warn( | ||
"Failed to copy executable from " | ||
+ sourceExecutablePath + " to " + targetExecutablePath | ||
+ ". Using resolved package path instead.", | ||
err, | ||
); | ||
} | ||
return checksum; | ||
// use the path found in the specific package | ||
try { | ||
chmodX(sourceExecutablePath); | ||
} catch (_err) { | ||
// ignore | ||
} | ||
return sourceExecutablePath; | ||
} | ||
} | ||
}, | ||
}; | ||
function extractZipFile() { | ||
return new Promise((resolve, reject) => { | ||
// code adapted from: https://github.com/thejoshwolfe/yauzl#usage | ||
yauzl.open(zipFilePath, { autoClose: true }, (err, zipFile) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
/** @filePath {string} */ | ||
function chmodX(filePath) { | ||
const perms = fs.statSync(filePath).mode; | ||
fs.chmodSync(filePath, perms | 0o111); | ||
} | ||
const pendingWrites = []; | ||
zipFile.on("entry", (entry) => { | ||
if (!/\/$/.test(entry.fileName)) { | ||
// file entry | ||
// note: reject at the top level, but resolve this promise when finished | ||
pendingWrites.push( | ||
new Promise((resolve) => { | ||
zipFile.openReadStream(entry, (err, readStream) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
const destination = path.join(__dirname, entry.fileName); | ||
const writeStream = fs.createWriteStream(destination); | ||
readStream.pipe(writeStream); | ||
writeStream.on("error", (err) => { | ||
reject(err); | ||
}); | ||
writeStream.on("finish", () => { | ||
resolve(undefined); | ||
}); | ||
}); | ||
}), | ||
); | ||
} | ||
}); | ||
zipFile.once("close", function() { | ||
Promise.all(pendingWrites).then(resolve).catch(reject); | ||
}); | ||
}); | ||
}); | ||
function getTarget() { | ||
const platform = os.platform(); | ||
if (platform === "linux") { | ||
return platform + "-" + getArch() + "-" + getLinuxFamily(); | ||
} else { | ||
return platform + "-" + getArch(); | ||
} | ||
} | ||
function getArch() { | ||
if (os.arch() === "arm64") { | ||
return "aarch64"; | ||
} else if (os.arch() === "x64") { | ||
return "x86_64"; | ||
} else { | ||
throw new Error("Unsupported architecture " + os.arch() + ". Only x64 and aarch64 binaries are available."); | ||
} | ||
function getArch() { | ||
const arch = os.arch(); | ||
if (arch !== "arm64" && arch !== "x64") { | ||
throw new Error("Unsupported architecture " + os.arch() + ". Only x64 and aarch64 binaries are available."); | ||
} | ||
return arch; | ||
} | ||
function getLinuxFamily() { | ||
return getIsMusl() ? "musl" : "gnu"; | ||
function getLinuxFamily() { | ||
return getIsMusl() ? "musl" : "glibc"; | ||
function getIsMusl() { | ||
// code adapted from https://github.com/lovell/detect-libc | ||
// Copyright Apache 2.0 license, the detect-libc maintainers | ||
if (cachedIsMusl == null) { | ||
cachedIsMusl = innerGet(); | ||
} | ||
return cachedIsMusl; | ||
function getIsMusl() { | ||
// code adapted from https://github.com/lovell/detect-libc | ||
// Copyright Apache 2.0 license, the detect-libc maintainers | ||
if (cachedIsMusl == null) { | ||
cachedIsMusl = innerGet(); | ||
} | ||
return cachedIsMusl; | ||
function innerGet() { | ||
try { | ||
if (os.platform() !== "linux") { | ||
return false; | ||
} | ||
return isProcessReportMusl() || isConfMusl(); | ||
} catch (err) { | ||
// just in case | ||
console.warn("Error checking if musl.", err); | ||
function innerGet() { | ||
try { | ||
if (os.platform() !== "linux") { | ||
return false; | ||
} | ||
return isProcessReportMusl() || isConfMusl(); | ||
} catch (err) { | ||
// just in case | ||
console.warn("Error checking if musl.", err); | ||
return false; | ||
} | ||
} | ||
function isProcessReportMusl() { | ||
if (!process.report) { | ||
return false; | ||
} | ||
const report = process.report.getReport(); | ||
if (!report || !(report.sharedObjects instanceof Array)) { | ||
return false; | ||
} | ||
return report.sharedObjects.some(o => o.includes("libc.musl-") || o.includes("ld-musl-")); | ||
function isProcessReportMusl() { | ||
if (!process.report) { | ||
return false; | ||
} | ||
function isConfMusl() { | ||
const output = getCommandOutput(); | ||
const [_, ldd1] = output.split(/[\r\n]+/); | ||
return ldd1 && ldd1.includes("musl"); | ||
const rawReport = process.report.getReport(); | ||
const report = typeof rawReport === "string" ? JSON.parse(rawReport) : rawReport; | ||
if (!report || !(report.sharedObjects instanceof Array)) { | ||
return false; | ||
} | ||
return report.sharedObjects.some(o => o.includes("libc.musl-") || o.includes("ld-musl-")); | ||
} | ||
function getCommandOutput() { | ||
try { | ||
const command = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true"; | ||
return require("child_process").execSync(command, { encoding: "utf8" }); | ||
} catch (_err) { | ||
return ""; | ||
} | ||
function isConfMusl() { | ||
const output = getCommandOutput(); | ||
const [_, ldd1] = output.split(/[\r\n]+/); | ||
return ldd1 && ldd1.includes("musl"); | ||
} | ||
function getCommandOutput() { | ||
try { | ||
const command = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true"; | ||
return require("child_process").execSync(command, { encoding: "utf8" }); | ||
} catch (_err) { | ||
return ""; | ||
} | ||
@@ -285,13 +143,23 @@ } | ||
module.exports = { | ||
runInstall() { | ||
return install().catch(err => { | ||
if (err !== undefined && typeof err.message === "string") { | ||
console.error(err.message); | ||
} else { | ||
console.error(err); | ||
} | ||
process.exit(1); | ||
}); | ||
}, | ||
}; | ||
/** | ||
* @param sourcePath {string} | ||
* @param destinationPath {string} | ||
*/ | ||
function atomicCopyFileSync(sourcePath, destinationPath) { | ||
const crypto = require("crypto"); | ||
const rand = crypto.randomBytes(4).toString("hex"); | ||
const tempFilePath = destinationPath + "." + rand; | ||
fs.copyFileSync(sourcePath, tempFilePath); | ||
try { | ||
fs.renameSync(tempFilePath, destinationPath); | ||
} catch (err) { | ||
// will maybe throw when another process had already done this | ||
// so just ignore and delete the created temporary file | ||
try { | ||
fs.unlikSync(tempFilePath); | ||
} catch (_err2) { | ||
// ignore | ||
} | ||
throw err; | ||
} | ||
} |
{ | ||
"name": "dprint", | ||
"version": "0.35.1", | ||
"version": "0.41.0", | ||
"description": "Pluggable and configurable code formatting platform written in Rust.", | ||
"bin": "bin.js", | ||
"scripts": { | ||
"postinstall": "node ./install.js" | ||
}, | ||
"repository": { | ||
@@ -23,6 +20,14 @@ "type": "git", | ||
"homepage": "https://github.com/dprint/dprint#readme", | ||
"dependencies": { | ||
"https-proxy-agent": "=5.0.1", | ||
"yauzl": "=2.10.0" | ||
"preferUnplugged": true, | ||
"scripts": { | ||
"postinstall": "node ./install.js" | ||
}, | ||
"optionalDependencies": { | ||
"@dprint/win32-x64": "0.41.0", | ||
"@dprint/darwin-x64": "0.41.0", | ||
"@dprint/darwin-arm64": "0.41.0", | ||
"@dprint/linux-x64-glibc": "0.41.0", | ||
"@dprint/linux-x64-musl": "0.41.0", | ||
"@dprint/linux-arm64-glibc": "0.41.0" | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
5
2
7987
6
6
190
+ Added@dprint/darwin-arm64@0.41.0(transitive)
+ Added@dprint/darwin-x64@0.41.0(transitive)
+ Added@dprint/linux-arm64-glibc@0.41.0(transitive)
+ Added@dprint/linux-x64-glibc@0.41.0(transitive)
+ Added@dprint/linux-x64-musl@0.41.0(transitive)
+ Added@dprint/win32-x64@0.41.0(transitive)
- Removedhttps-proxy-agent@=5.0.1
- Removedyauzl@=2.10.0