Refactor website CDN url replacements (#18553)
This commit is contained in:
1 parent
65e600a539
commit
1c49bedbee
2 files changed
+166
-81
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import fs from "node:fs/promises";
|
||||
import { findPackageJSON } from "node:module";
|
||||
|
||||
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 {{
|
||||
name: string,
|
||||
base: string | URL,
|
||||
overrideDependencies?: Map<string, string[]>,
|
||||
ignoreDependencies?: (name: string) => boolean,
|
||||
}} param0
|
||||
@returns
|
||||
*/
|
||||
async function getPackageDependencies({
|
||||
name,
|
||||
base,
|
||||
overrideDependencies = new Map(),
|
||||
ignoreDependencies = () => false,
|
||||
}) {
|
||||
const packageJsonUrl = findPackageJSON(name, base);
|
||||
const packageJsonUrls = new Set();
|
||||
|
||||
const packages = [{ name, packageJsonUrl }];
|
||||
while (packages.length > 0) {
|
||||
const { name, packageJsonUrl } = packages.pop();
|
||||
if (ignoreDependencies(name) || packageJsonUrls.has(packageJsonUrl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
packageJsonUrls.add(packageJsonUrl);
|
||||
|
||||
let dependencies;
|
||||
|
||||
if (overrideDependencies.has(name)) {
|
||||
dependencies = overrideDependencies
|
||||
.get(name)
|
||||
.map((name) => ({ name, packageJsonUrl: findPackageJSON(name, base) }));
|
||||
} else {
|
||||
({ dependencies } = await getPackageJson(packageJsonUrl));
|
||||
}
|
||||
|
||||
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 };
|
||||
+60
-81
@@ -1,119 +1,98 @@
|
||||
import fs from "node:fs/promises";
|
||||
import module from "node:module";
|
||||
import assert from "node:assert/strict";
|
||||
import vitePluginVue from "@vitejs/plugin-vue";
|
||||
import vitePluginVueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import { visualizer as rollupPluginVisualizer } from "rollup-plugin-visualizer";
|
||||
import { defineConfig } from "vite";
|
||||
import packageJson from "./package.json" with { type: "json" };
|
||||
import { getPackageDependencies } from "./scripts/collect-dependencies.mjs";
|
||||
|
||||
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
||||
const IS_CI = Boolean(process.env.CI);
|
||||
const OUT_DIRECTORY = "./static/playground/";
|
||||
const DEPENDENCIES_EXCLUDE_FROM_CDN = new Set(["@docusaurus/preset-classic"]);
|
||||
const cdnDependencies = {
|
||||
exclude: new Set(["@docusaurus/preset-classic"]),
|
||||
overrideDependencies: new Map([
|
||||
// We don't need deps from `vue`
|
||||
["vue", []],
|
||||
]),
|
||||
ignoreDependencies: (name) => name.startsWith("@types/"),
|
||||
};
|
||||
|
||||
// Local develop requires `vue` for HMR
|
||||
if (!IS_PRODUCTION) {
|
||||
DEPENDENCIES_EXCLUDE_FROM_CDN.add("vue");
|
||||
cdnDependencies.exclude.add("vue");
|
||||
}
|
||||
|
||||
export default defineConfig(async () => ({
|
||||
base: IS_PRODUCTION ? "/playground/" : undefined,
|
||||
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
||||
plugins: [
|
||||
vitePluginVue(),
|
||||
vitePluginVueJsx(),
|
||||
IS_CI || !IS_PRODUCTION
|
||||
? undefined
|
||||
: rollupPluginVisualizer({
|
||||
filename: `${OUT_DIRECTORY}bundle-report.html`,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
alias: await buildCdnAlias(),
|
||||
},
|
||||
build: {
|
||||
outDir: OUT_DIRECTORY,
|
||||
minify: IS_CI,
|
||||
},
|
||||
}));
|
||||
export default defineConfig(async () => {
|
||||
const alias = await buildCdnAlias();
|
||||
|
||||
return {
|
||||
base: IS_PRODUCTION ? "/playground/" : undefined,
|
||||
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
||||
plugins: [
|
||||
vitePluginVue(),
|
||||
vitePluginVueJsx(),
|
||||
IS_CI || !IS_PRODUCTION
|
||||
? undefined
|
||||
: rollupPluginVisualizer({
|
||||
filename: `${OUT_DIRECTORY}bundle-report.html`,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
alias,
|
||||
},
|
||||
build: {
|
||||
outDir: OUT_DIRECTORY,
|
||||
minify: IS_CI,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
async function buildCdnAlias() {
|
||||
// if (!IS_PRODUCTION) {
|
||||
// return;
|
||||
// }
|
||||
if (!IS_PRODUCTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
Object.keys(packageJson.dependencies)
|
||||
.filter((name) => !DEPENDENCIES_EXCLUDE_FROM_CDN.has(name))
|
||||
.filter((name) => !cdnDependencies.exclude.has(name))
|
||||
.map(async (name) => [name, await getPackageCdnUrl(name)]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async function getPackageCdnUrl(name) {
|
||||
const version = packageJson.dependencies[name];
|
||||
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}`);
|
||||
|
||||
let dependencies = await getPackageDependencies(name, import.meta.url);
|
||||
|
||||
dependencies = dependencies
|
||||
// Exclude self
|
||||
.filter((dependency) => dependency.name !== name)
|
||||
// Sort alphabetically
|
||||
.toSorted(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB))
|
||||
// Dedupe
|
||||
.filter(
|
||||
({ name, version }, index, dependencies) =>
|
||||
dependencies.findIndex(
|
||||
(searching) =>
|
||||
searching.name === name && searching.version === version,
|
||||
) === index,
|
||||
);
|
||||
const { searchParams } = url;
|
||||
// searchParams.set("conditions", ["browser"]);
|
||||
|
||||
if (dependencies.length > 0) {
|
||||
url.searchParams.set(
|
||||
searchParams.set(
|
||||
"deps",
|
||||
dependencies.map(({ name, version }) => `${name}@${version}`).join(","),
|
||||
);
|
||||
}
|
||||
|
||||
return url;
|
||||
return url.href;
|
||||
}
|
||||
|
||||
async function getPackageJson(name, base) {
|
||||
const packageJsonUrl = module.findPackageJSON(name, base);
|
||||
function assertDependenciesUnique(name, dependencies) {
|
||||
const versions = new Map();
|
||||
|
||||
return {
|
||||
url: packageJsonUrl,
|
||||
packageJson: JSON.parse(await fs.readFile(packageJsonUrl, "utf8")),
|
||||
};
|
||||
}
|
||||
|
||||
async function getPackageDependenciesWithoutCache(name, base) {
|
||||
const { url, packageJson } = await getPackageJson(name, base);
|
||||
const dependencies = [{ name, version: packageJson.version }];
|
||||
for (const name of Object.keys({
|
||||
...packageJson.dependencies,
|
||||
...packageJson.peerDependencies,
|
||||
})) {
|
||||
dependencies.push(...(await getPackageDependencies(name, url)));
|
||||
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);
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
const cache = new Map();
|
||||
function getPackageDependencies(name, base) {
|
||||
// We don't need deps from `vue`
|
||||
if (name === "vue") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cacheKey = JSON.stringify({ name, base });
|
||||
|
||||
if (!cache.has(cacheKey)) {
|
||||
cache.set(cacheKey, getPackageDependenciesWithoutCache(name, base));
|
||||
}
|
||||
|
||||
return cache.get(cacheKey);
|
||||
}
|
||||
Reference in new issue
Block a user