Website: collect dependencies with package-dependencies-tree (#18591)
This commit is contained in:
1 parent
98ed9261a7
commit
32114b760c
4 files changed
+139
-157
No files matched your search
@@ -50,6 +50,7 @@
|
|||||||
"@vitejs/plugin-vue": "6.0.3",
|
"@vitejs/plugin-vue": "6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "5.1.3",
|
"@vitejs/plugin-vue-jsx": "5.1.3",
|
||||||
"concurrently": "9.2.1",
|
"concurrently": "9.2.1",
|
||||||
|
"package-dependencies-tree": "0.0.1",
|
||||||
"rollup-plugin-visualizer": "6.0.5",
|
"rollup-plugin-visualizer": "6.0.5",
|
||||||
"vite": "7.3.0",
|
"vite": "7.3.0",
|
||||||
"yaml": "2.8.2"
|
"yaml": "2.8.2"
|
||||||
|
|||||||
@@ -1,106 +1,102 @@
|
|||||||
import fs from "node:fs/promises";
|
import assert from "node:assert/strict";
|
||||||
import { findPackageJSON } from "node:module";
|
import getDependencies from "package-dependencies-tree";
|
||||||
|
|
||||||
async function _getPackageJson(packageJsonUrl) {
|
|
||||||
const packageJson = JSON.parse(await fs.readFile(packageJsonUrl, "utf8"));
|
|
||||||
const dependencies = new Set();
|
|
||||||
|
|
||||||
for (const { dependencyType, optional } of [
|
|
||||||
{ dependencyType: "dependencies", optional: false },
|
|
||||||
{ dependencyType: "peerDependencies", optional: true },
|
|
||||||
]) {
|
|
||||||
if (!packageJson[dependencyType]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const name of Object.keys(packageJson[dependencyType])) {
|
|
||||||
let dependencyPackageJsonUrl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
dependencyPackageJsonUrl = findPackageJSON(name, packageJsonUrl);
|
|
||||||
} catch (error) {
|
|
||||||
if (optional && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies.add({ name, packageJsonUrl: dependencyPackageJsonUrl });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: packageJson.name,
|
|
||||||
version: packageJson.version,
|
|
||||||
packageJsonUrl,
|
|
||||||
dependencies: [...dependencies],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const packageJsonCache = new Map();
|
|
||||||
function getPackageJson(packageJsonUrl) {
|
|
||||||
if (!packageJsonCache.has(packageJsonUrl)) {
|
|
||||||
packageJsonCache.set(packageJsonUrl, _getPackageJson(packageJsonUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
return packageJsonCache.get(packageJsonUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@param {{
|
@import {PackageJson} from "package-dependencies-tree"
|
||||||
name: string,
|
|
||||||
base: string | URL,
|
|
||||||
overrideDependencies?: Map<string, string[]>,
|
|
||||||
ignoreDependencies?: (name: string) => boolean,
|
|
||||||
}} param0
|
|
||||||
@returns
|
|
||||||
*/
|
*/
|
||||||
async function getPackageDependencies({
|
|
||||||
name,
|
/**
|
||||||
base,
|
@param {PackageJson} packageJson
|
||||||
|
@param {{
|
||||||
|
overrideDependencies: Map<string, string[]>
|
||||||
|
ignoreDependencies = (name) => boolean,
|
||||||
|
seen: Set<PackageJson>
|
||||||
|
}} seen
|
||||||
|
@returns {Generator<PackageJson>}
|
||||||
|
*/
|
||||||
|
function* collectDependencies(packageJson, options) {
|
||||||
|
if (!options.seen) {
|
||||||
|
options = { ...options, seen: new Set() };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { seen, overrideDependencies, ignoreDependencies } = options;
|
||||||
|
|
||||||
|
if (overrideDependencies.has(packageJson.name)) {
|
||||||
|
yield* overrideDependencies.get(packageJson.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { type, optional } of [
|
||||||
|
{ type: "dependencies", optional: false },
|
||||||
|
{ type: "peerDependencies", optional: true },
|
||||||
|
]) {
|
||||||
|
for (const dependency of packageJson[type].values()) {
|
||||||
|
if (ignoreDependencies(dependency.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resolved } = dependency;
|
||||||
|
if (!optional) {
|
||||||
|
assert.ok(
|
||||||
|
resolved,
|
||||||
|
`Unable to resolve ${dependency.type} '${dependency.name}@${dependency.version}' from '${dependency.base}'.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolved && !seen.has(resolved)) {
|
||||||
|
seen.add(resolved);
|
||||||
|
yield resolved;
|
||||||
|
yield* collectDependencies(resolved, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertDependenciesUnique(packageJson, dependencies) {
|
||||||
|
const versions = new Map();
|
||||||
|
|
||||||
|
for (const dependency of dependencies) {
|
||||||
|
assert.ok(
|
||||||
|
!versions.has(dependency.name),
|
||||||
|
`Multiple version of '${dependency.name}' in dependency tree of '${packageJson.name}', '${versions.get(dependency.name)?.version}' and '${dependency.version}'.`,
|
||||||
|
);
|
||||||
|
versions.set(dependency.name, dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPackageDependencies({
|
||||||
|
file,
|
||||||
|
exclude,
|
||||||
overrideDependencies = new Map(),
|
overrideDependencies = new Map(),
|
||||||
ignoreDependencies = () => false,
|
ignoreDependencies = () => false,
|
||||||
}) {
|
}) {
|
||||||
const packageJsonUrl = findPackageJSON(name, base);
|
return Array.from(
|
||||||
const packageJsonUrls = new Set();
|
getDependencies(file).dependencies.entries(),
|
||||||
|
([name, dependency]) => {
|
||||||
|
if (exclude.has(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const packages = [{ name, packageJsonUrl }];
|
const packageJson = dependency.resolved;
|
||||||
while (packages.length > 0) {
|
|
||||||
const { name, packageJsonUrl } = packages.pop();
|
|
||||||
if (ignoreDependencies(name) || packageJsonUrls.has(packageJsonUrl)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
packageJsonUrls.add(packageJsonUrl);
|
const dependencies = [
|
||||||
|
...collectDependencies(packageJson, {
|
||||||
|
overrideDependencies,
|
||||||
|
ignoreDependencies,
|
||||||
|
}),
|
||||||
|
].toSorted(({ name: nameA }, { name: nameB }) =>
|
||||||
|
nameA.localeCompare(nameB),
|
||||||
|
);
|
||||||
|
|
||||||
let dependencies;
|
assertDependenciesUnique(packageJson, dependencies);
|
||||||
|
|
||||||
if (overrideDependencies.has(name)) {
|
return {
|
||||||
dependencies = overrideDependencies
|
name,
|
||||||
.get(name)
|
packageJson,
|
||||||
.map((name) => ({ name, packageJsonUrl: findPackageJSON(name, base) }));
|
dependencies,
|
||||||
} else {
|
};
|
||||||
({ dependencies } = await getPackageJson(packageJsonUrl));
|
},
|
||||||
}
|
).filter(Boolean);
|
||||||
|
|
||||||
packages.push(...dependencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete self
|
|
||||||
packageJsonUrls.delete(packageJsonUrl);
|
|
||||||
|
|
||||||
const [packageJsonData, ...dependencies] = await Promise.all(
|
|
||||||
[packageJsonUrl, ...packageJsonUrls].map((packageJsonUrl) =>
|
|
||||||
getPackageJson(packageJsonUrl),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...packageJsonData,
|
|
||||||
dependencies: dependencies.toSorted(({ name: nameA }, { name: nameB }) =>
|
|
||||||
nameA.localeCompare(nameB),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getPackageDependencies };
|
export { getPackageDependencies };
|
||||||
+41
-64
@@ -1,15 +1,14 @@
|
|||||||
import assert from "node:assert/strict";
|
|
||||||
import vitePluginVue from "@vitejs/plugin-vue";
|
import vitePluginVue from "@vitejs/plugin-vue";
|
||||||
import vitePluginVueJsx from "@vitejs/plugin-vue-jsx";
|
import vitePluginVueJsx from "@vitejs/plugin-vue-jsx";
|
||||||
import { visualizer as rollupPluginVisualizer } from "rollup-plugin-visualizer";
|
import { visualizer as rollupPluginVisualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import packageJson from "./package.json" with { type: "json" };
|
|
||||||
import { getPackageDependencies } from "./scripts/collect-dependencies.mjs";
|
import { getPackageDependencies } from "./scripts/collect-dependencies.mjs";
|
||||||
|
|
||||||
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
||||||
const IS_CI = Boolean(process.env.CI);
|
const IS_CI = Boolean(process.env.CI);
|
||||||
const OUT_DIRECTORY = "./static/playground/";
|
const OUT_DIRECTORY = "./static/playground/";
|
||||||
const cdnDependencies = {
|
const cdnDependencies = {
|
||||||
|
file: new URL("./package.json", import.meta.url),
|
||||||
exclude: new Set(["@docusaurus/preset-classic"]),
|
exclude: new Set(["@docusaurus/preset-classic"]),
|
||||||
overrideDependencies: new Map([
|
overrideDependencies: new Map([
|
||||||
// We don't need deps from `vue`
|
// We don't need deps from `vue`
|
||||||
@@ -23,76 +22,54 @@ if (!IS_PRODUCTION) {
|
|||||||
cdnDependencies.exclude.add("vue");
|
cdnDependencies.exclude.add("vue");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineConfig(async () => {
|
const alias = buildCdnAlias();
|
||||||
const alias = await buildCdnAlias();
|
|
||||||
|
|
||||||
return {
|
export default defineConfig({
|
||||||
base: IS_PRODUCTION ? "/playground/" : undefined,
|
base: IS_PRODUCTION ? "/playground/" : undefined,
|
||||||
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
||||||
plugins: [
|
plugins: [
|
||||||
vitePluginVue(),
|
vitePluginVue(),
|
||||||
vitePluginVueJsx(),
|
vitePluginVueJsx(),
|
||||||
IS_CI || !IS_PRODUCTION
|
IS_CI || !IS_PRODUCTION
|
||||||
? undefined
|
? undefined
|
||||||
: rollupPluginVisualizer({
|
: rollupPluginVisualizer({
|
||||||
filename: `${OUT_DIRECTORY}bundle-report.html`,
|
filename: `${OUT_DIRECTORY}bundle-report.html`,
|
||||||
}),
|
}),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
resolve: {
|
resolve: {
|
||||||
alias,
|
alias,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: OUT_DIRECTORY,
|
outDir: OUT_DIRECTORY,
|
||||||
minify: IS_CI,
|
minify: IS_CI,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function buildCdnAlias() {
|
function buildCdnAlias() {
|
||||||
if (!IS_PRODUCTION) {
|
if (!IS_PRODUCTION) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
await Promise.all(
|
getPackageDependencies(cdnDependencies).map(
|
||||||
Object.keys(packageJson.dependencies)
|
({ name, packageJson, dependencies }) => {
|
||||||
.filter((name) => !cdnDependencies.exclude.has(name))
|
const url = new URL(
|
||||||
.map(async (name) => [name, await getPackageCdnUrl(name)]),
|
`https://esm.sh/${packageJson.name}@${packageJson.version}`,
|
||||||
|
);
|
||||||
|
const { searchParams } = url;
|
||||||
|
// searchParams.set("conditions", ["browser"]);
|
||||||
|
|
||||||
|
if (dependencies.length > 0) {
|
||||||
|
searchParams.set(
|
||||||
|
"deps",
|
||||||
|
dependencies
|
||||||
|
.map(({ name, version }) => `${name}@${version}`)
|
||||||
|
.join(","),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [name, url.href];
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPackageCdnUrl(dependencyName) {
|
|
||||||
const { name, version, dependencies } = await getPackageDependencies({
|
|
||||||
name: dependencyName,
|
|
||||||
base: import.meta.url,
|
|
||||||
overrideDependencies: cdnDependencies.overrideDependencies,
|
|
||||||
ignoreDependencies: cdnDependencies.ignoreDependencies,
|
|
||||||
});
|
|
||||||
|
|
||||||
assertDependenciesUnique(name, dependencies);
|
|
||||||
const url = new URL(`https://esm.sh/${name}@${version}`);
|
|
||||||
const { searchParams } = url;
|
|
||||||
// searchParams.set("conditions", ["browser"]);
|
|
||||||
|
|
||||||
if (dependencies.length > 0) {
|
|
||||||
searchParams.set(
|
|
||||||
"deps",
|
|
||||||
dependencies.map(({ name, version }) => `${name}@${version}`).join(","),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return url.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertDependenciesUnique(name, dependencies) {
|
|
||||||
const versions = new Map();
|
|
||||||
|
|
||||||
for (const dependency of dependencies) {
|
|
||||||
assert.ok(
|
|
||||||
!versions.has(dependency.name),
|
|
||||||
`Multiple version of '${dependency.name}' in dependency tree of '${name}', '${versions.get(dependency.name)?.version}' and '${dependency.version}'.`,
|
|
||||||
);
|
|
||||||
versions.set(dependency.name, dependency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11350,6 +11350,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"package-dependencies-tree@npm:0.0.1":
|
||||||
|
version: 0.0.1
|
||||||
|
resolution: "package-dependencies-tree@npm:0.0.1"
|
||||||
|
checksum: 10/018fa5be276997bc618eccb8aac827135fb7eb788dc8f1bfbc1da55ee59b3bcd8037ae39dc023de655d30b125c1358e4523afa07cc4dca6aec3cd9a8c89e2530
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"package-json@npm:^8.1.0":
|
"package-json@npm:^8.1.0":
|
||||||
version: 8.1.1
|
version: 8.1.1
|
||||||
resolution: "package-json@npm:8.1.1"
|
resolution: "package-json@npm:8.1.1"
|
||||||
@@ -13292,6 +13299,7 @@ __metadata:
|
|||||||
github-buttons: "npm:2.29.1"
|
github-buttons: "npm:2.29.1"
|
||||||
graphql: "npm:16.12.0"
|
graphql: "npm:16.12.0"
|
||||||
lz-string: "npm:1.5.0"
|
lz-string: "npm:1.5.0"
|
||||||
|
package-dependencies-tree: "npm:0.0.1"
|
||||||
prism-react-renderer: "npm:2.4.1"
|
prism-react-renderer: "npm:2.4.1"
|
||||||
react: "npm:19.2.1"
|
react: "npm:19.2.1"
|
||||||
react-dom: "npm:19.2.1"
|
react-dom: "npm:19.2.1"
|
||||||
|
|||||||
Reference in new issue
Block a user