@@ -14,2 +14,4 @@ import { builtinModules } from "node:module"; | ||
| import { gzipSync } from "node:zlib"; | ||
| import license from "rollup-plugin-license"; | ||
| import { dirname as dirname$1 } from "node:path"; | ||
| import { defu } from "defu"; | ||
@@ -114,2 +116,93 @@ import MagicString from "magic-string"; | ||
| } | ||
| function licensePlugin(opts) { | ||
| const originalPlugin = license({ async thirdParty(dependencies) { | ||
| const deps = sortDependencies([...dependencies]); | ||
| const licenses = sortLicenses(new Set(dependencies.map((dep) => dep.license).filter(Boolean))); | ||
| let dependencyLicenseTexts = ""; | ||
| for (let i = 0; i < deps.length; i++) { | ||
| const licenseText = deps[i].licenseText; | ||
| const sameDeps = [deps[i]]; | ||
| if (licenseText) { | ||
| for (let j = i + 1; j < deps.length; j++) if (licenseText === deps[j].licenseText) { | ||
| sameDeps.push(...deps.splice(j, 1)); | ||
| j--; | ||
| } | ||
| } | ||
| let text = `## ${sameDeps.map((d) => d.name || "unknown").join(", ")}\n\n`; | ||
| const depInfos = sameDeps.map((d) => getDependencyInformation(d)); | ||
| if (depInfos.length > 1 && depInfos.every((info) => info.license === depInfos[0].license && info.names === depInfos[0].names)) { | ||
| const { license, names } = depInfos[0]; | ||
| const repositoryText = depInfos.map((info) => info.repository).filter(Boolean).join(", "); | ||
| if (license) text += `License: ${license}\n`; | ||
| if (names) text += `By: ${names}\n`; | ||
| if (repositoryText) text += `Repositories: ${repositoryText}\n`; | ||
| } else for (let j = 0; j < depInfos.length; j++) { | ||
| const { license, names, repository } = depInfos[j]; | ||
| if (license) text += `License: ${license}\n`; | ||
| if (names) text += `By: ${names}\n`; | ||
| if (repository) text += `Repository: ${repository}\n`; | ||
| if (j !== depInfos.length - 1) text += "\n"; | ||
| } | ||
| if (licenseText) text += "\n" + licenseText.trim().replace(/\r\n|\r/g, "\n").split("\n").map((line) => `> ${line}`).join("\n") + "\n"; | ||
| if (i !== deps.length - 1) text += "\n---------------------------------------\n\n"; | ||
| dependencyLicenseTexts += text; | ||
| } | ||
| if (!dependencyLicenseTexts) return; | ||
| const licenseText = `# Licenses of Bundled Dependencies | ||
| The published artifact additionally contains code with the following licenses: | ||
| ${licenses.join(", ")}\n\n# Bundled Dependencies\n\n` + dependencyLicenseTexts; | ||
| console.log("Writing third-party licenses to", opts.output); | ||
| await mkdir(dirname$1(opts.output), { recursive: true }); | ||
| await writeFile(opts.output, licenseText); | ||
| } }); | ||
| for (const hook of ["renderChunk", "generateBundle"]) { | ||
| const originalHook = originalPlugin[hook]; | ||
| if (!originalHook) continue; | ||
| originalPlugin[hook] = function(...args) { | ||
| if (this.meta.watchMode) return; | ||
| return originalHook.apply(this, args); | ||
| }; | ||
| } | ||
| return originalPlugin; | ||
| } | ||
| function sortDependencies(dependencies) { | ||
| return dependencies.sort(({ name: nameA }, { name: nameB }) => { | ||
| return (nameA || "") > (nameB || "") ? 1 : (nameB || "") > (nameA || "") ? -1 : 0; | ||
| }); | ||
| } | ||
| function sortLicenses(licenses) { | ||
| let withParenthesis = []; | ||
| let noParenthesis = []; | ||
| for (const license of licenses) if (license[0] === "(") withParenthesis.push(license); | ||
| else noParenthesis.push(license); | ||
| withParenthesis = withParenthesis.sort(); | ||
| noParenthesis = noParenthesis.sort(); | ||
| return [...noParenthesis, ...withParenthesis]; | ||
| } | ||
| function getDependencyInformation(dep) { | ||
| const info = {}; | ||
| const { license, author, maintainers, contributors, repository } = dep; | ||
| if (license) info.license = license; | ||
| const names = /* @__PURE__ */ new Set(); | ||
| for (const person of [ | ||
| author, | ||
| ...maintainers, | ||
| ...contributors | ||
| ]) { | ||
| const name = typeof person === "string" ? person : person?.name; | ||
| if (name) names.add(name); | ||
| } | ||
| if (names.size > 0) info.names = [...names].join(", "); | ||
| if (repository) info.repository = normalizeGitUrl(typeof repository === "string" ? repository : repository.url); | ||
| return info; | ||
| } | ||
| function normalizeGitUrl(url) { | ||
| url = url.replace(/^git\+/, "").replace(/\.git$/, "").replace(/(^|\/)[^/]+?@/, "$1").replace(/(\.[^.]+?):(?!\d)/, "$1/").replace(/^git:\/\//, "https://").replace(/^ssh:\/\//, "https://"); | ||
| if (url.startsWith("github:")) return `https://github.com/${url.slice(7)}`; | ||
| else if (url.startsWith("gitlab:")) return `https://gitlab.com/${url.slice(7)}`; | ||
| else if (url.startsWith("bitbucket:")) return `https://bitbucket.org/${url.slice(10)}`; | ||
| else if (!url.includes(":") && url.split("/").length === 2) return `https://github.com/${url}`; | ||
| else return url.includes("://") ? url : `https://${url}`; | ||
| } | ||
| async function rolldownBuild(ctx, entry, hooks) { | ||
@@ -136,3 +229,3 @@ const inputs = normalizeBundleInputs(entry.input, ctx); | ||
| input: inputs, | ||
| plugins: [shebangPlugin()], | ||
| plugins: [shebangPlugin(), licensePlugin({ output: resolve(ctx.pkgDir, entry.outDir || "dist", "THIRD-PARTY-LICENSES.md") })], | ||
| platform: "node", | ||
@@ -139,0 +232,0 @@ onLog(level, log, defaultHandler) { |
+2
-1
| { | ||
| "name": "obuild", | ||
| "version": "0.4.24", | ||
| "version": "0.4.25", | ||
| "description": "Zero-config ESM/TS package builder", | ||
@@ -43,2 +43,3 @@ "repository": "unjs/obuild", | ||
| "rolldown-plugin-dts": "^0.22.1", | ||
| "rollup-plugin-license": "^3.6.0", | ||
| "tinyglobby": "^0.2.15" | ||
@@ -45,0 +46,0 @@ }, |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
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 1 instance in 1 package
30362
17.08%541
20.49%11
10%3
50%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added