mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-02 19:39:30 +08:00
241 lines
6.6 KiB
Text
241 lines
6.6 KiB
Text
import { log } from './log';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { createRequire } from 'module';
|
|
|
|
export function findRootSvelteDependencies(root: string, cwdFallback = true): SvelteDependency[] {
|
|
log.debug(`findSvelteDependencies: searching svelte dependencies in ${root}`);
|
|
const pkgFile = path.join(root, 'package.json');
|
|
if (!fs.existsSync(pkgFile)) {
|
|
if (cwdFallback) {
|
|
const cwd = process.cwd();
|
|
if (root !== cwd) {
|
|
log.debug(`no package.json found in vite root ${root}`);
|
|
return findRootSvelteDependencies(cwd, false);
|
|
}
|
|
}
|
|
log.warn(`no package.json found, findRootSvelteDependencies failed`);
|
|
return [];
|
|
}
|
|
|
|
const pkg = parsePkg(root);
|
|
if (!pkg) {
|
|
return [];
|
|
}
|
|
|
|
const deps = [
|
|
...Object.keys(pkg.dependencies || {}),
|
|
...Object.keys(pkg.devDependencies || {})
|
|
].filter((dep) => !is_common_without_svelte_field(dep));
|
|
|
|
return getSvelteDependencies(deps, root);
|
|
}
|
|
|
|
function getSvelteDependencies(
|
|
deps: string[],
|
|
pkgDir: string,
|
|
path: string[] = []
|
|
): SvelteDependency[] {
|
|
const result = [];
|
|
const localRequire = createRequire(`${pkgDir}/package.json`);
|
|
const resolvedDeps = deps
|
|
.map((dep) => resolveDependencyData(dep, localRequire))
|
|
.filter(Boolean) as DependencyData[];
|
|
for (const { pkg, dir } of resolvedDeps) {
|
|
const type = getSvelteDependencyType(pkg);
|
|
if (!type) continue;
|
|
result.push({ name: pkg.name, type, pkg, dir, path });
|
|
// continue crawling for component libraries so we can optimize them, js libraries are fine
|
|
if (type === 'component-library' && pkg.dependencies) {
|
|
let dependencyNames = Object.keys(pkg.dependencies);
|
|
const circular = dependencyNames.filter((name) => path.includes(name));
|
|
if (circular.length > 0) {
|
|
log.warn.enabled &&
|
|
log.warn(
|
|
`skipping circular svelte dependencies in automated vite optimizeDeps handling`,
|
|
circular.map((x) => path.concat(x).join('>'))
|
|
);
|
|
dependencyNames = dependencyNames.filter((name) => !path.includes(name));
|
|
}
|
|
if (path.length === 3) {
|
|
log.debug.once(`encountered deep svelte dependency tree: ${path.join('>')}`);
|
|
}
|
|
result.push(...getSvelteDependencies(dependencyNames, dir, path.concat(pkg.name)));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function resolveDependencyData(
|
|
dep: string,
|
|
localRequire: NodeRequire
|
|
): DependencyData | void {
|
|
try {
|
|
const pkgJson = `${dep}/package.json`;
|
|
const pkg = localRequire(pkgJson);
|
|
const dir = path.dirname(localRequire.resolve(pkgJson));
|
|
return { dir, pkg };
|
|
} catch (e) {
|
|
log.debug.once(`dependency ${dep} does not export package.json`, e);
|
|
// walk up from default export until we find package.json with name=dep
|
|
try {
|
|
let dir = path.dirname(localRequire.resolve(dep));
|
|
while (dir) {
|
|
const pkg = parsePkg(dir, true);
|
|
if (pkg && pkg.name === dep) {
|
|
return { dir, pkg };
|
|
}
|
|
const parent = path.dirname(dir);
|
|
if (parent === dir) {
|
|
break;
|
|
}
|
|
dir = parent;
|
|
}
|
|
} catch (e) {
|
|
log.debug.once(`error while trying to find package.json of ${dep}`, e);
|
|
}
|
|
}
|
|
log.debug.once(`failed to resolve ${dep}`);
|
|
}
|
|
|
|
function parsePkg(dir: string, silent = false): Pkg | void {
|
|
const pkgFile = path.join(dir, 'package.json');
|
|
try {
|
|
return JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
} catch (e) {
|
|
!silent && log.warn.enabled && log.warn(`failed to parse ${pkgFile}`, e);
|
|
}
|
|
}
|
|
|
|
function getSvelteDependencyType(pkg: Pkg): SvelteDependencyType | undefined {
|
|
if (isSvelteComponentLib(pkg)) {
|
|
return 'component-library';
|
|
} else if (isSvelteLib(pkg)) {
|
|
return 'js-library';
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function isSvelteComponentLib(pkg: Pkg) {
|
|
return !!pkg.svelte;
|
|
}
|
|
|
|
function isSvelteLib(pkg: Pkg) {
|
|
return !!pkg.dependencies?.svelte || !!pkg.peerDependencies?.svelte;
|
|
}
|
|
|
|
const COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD = [
|
|
'@lukeed/uuid',
|
|
'@playwright/test',
|
|
'@sveltejs/vite-plugin-svelte',
|
|
'@sveltejs/kit',
|
|
'autoprefixer',
|
|
'cookie',
|
|
'dotenv',
|
|
'esbuild',
|
|
'eslint',
|
|
'jest',
|
|
'mdsvex',
|
|
'playwright',
|
|
'postcss',
|
|
'prettier',
|
|
'svelte',
|
|
'svelte-check',
|
|
'svelte-hmr',
|
|
'svelte-preprocess',
|
|
'tslib',
|
|
'typescript',
|
|
'vite',
|
|
'vitest',
|
|
'__vite-browser-external' // see https://github.com/sveltejs/vite-plugin-svelte/issues/362
|
|
];
|
|
const COMMON_PREFIXES_WITHOUT_SVELTE_FIELD = [
|
|
'@fontsource/',
|
|
'@postcss-plugins/',
|
|
'@rollup/',
|
|
'@sveltejs/adapter-',
|
|
'@types/',
|
|
'@typescript-eslint/',
|
|
'eslint-',
|
|
'jest-',
|
|
'postcss-plugin-',
|
|
'prettier-plugin-',
|
|
'rollup-plugin-',
|
|
'vite-plugin-'
|
|
];
|
|
|
|
/**
|
|
* Test for common dependency names that tell us it is not a package including a svelte field, eg. eslint + plugins.
|
|
*
|
|
* This speeds up the find process as we don't have to try and require the package.json for all of them
|
|
*
|
|
* @param dependency {string}
|
|
* @returns {boolean} true if it is a dependency without a svelte field
|
|
*/
|
|
export function is_common_without_svelte_field(dependency: string): boolean {
|
|
return (
|
|
COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD.includes(dependency) ||
|
|
COMMON_PREFIXES_WITHOUT_SVELTE_FIELD.some(
|
|
(prefix) =>
|
|
prefix.startsWith('@')
|
|
? dependency.startsWith(prefix)
|
|
: dependency.substring(dependency.lastIndexOf('/') + 1).startsWith(prefix) // check prefix omitting @scope/
|
|
)
|
|
);
|
|
}
|
|
|
|
export function needsOptimization(dep: string, localRequire: NodeRequire): boolean {
|
|
const depData = resolveDependencyData(dep, localRequire);
|
|
if (!depData) return false;
|
|
const pkg = depData.pkg;
|
|
// only optimize if is cjs, using the below as heuristic
|
|
// see https://github.com/sveltejs/vite-plugin-svelte/issues/162
|
|
const hasEsmFields = pkg.module || pkg.exports;
|
|
if (hasEsmFields) return false;
|
|
if (pkg.main) {
|
|
// ensure entry is js so vite can prebundle it
|
|
// see https://github.com/sveltejs/vite-plugin-svelte/issues/233
|
|
const entryExt = path.extname(pkg.main);
|
|
return !entryExt || entryExt === '.js' || entryExt === '.cjs';
|
|
} else {
|
|
// check if has implicit index.js entrypoint
|
|
// https://github.com/sveltejs/vite-plugin-svelte/issues/281
|
|
try {
|
|
localRequire.resolve(`${dep}/index.js`);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
interface DependencyData {
|
|
dir: string;
|
|
pkg: Pkg;
|
|
}
|
|
|
|
export interface SvelteDependency {
|
|
name: string;
|
|
type: SvelteDependencyType;
|
|
dir: string;
|
|
pkg: Pkg;
|
|
path: string[];
|
|
}
|
|
|
|
// component-library => exports svelte components
|
|
// js-library => only uses svelte api, no components
|
|
export type SvelteDependencyType = 'component-library' | 'js-library';
|
|
|
|
export interface Pkg {
|
|
name: string;
|
|
svelte?: string;
|
|
dependencies?: DependencyList;
|
|
devDependencies?: DependencyList;
|
|
peerDependencies?: DependencyList;
|
|
[key: string]: any;
|
|
}
|
|
|
|
export interface DependencyList {
|
|
[key: string]: string;
|
|
}
|