Skip to content

Commit

Permalink
wip: deep search for svelte dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikg committed Aug 18, 2021
1 parent c2b9e74 commit 63ea6a3
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 3 deletions.
3 changes: 0 additions & 3 deletions packages/playground/optimizedeps-include/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ const SVELTE_IMPORTS = [
export default defineConfig(({ command, mode }) => {
const isProduction = mode === 'production';
return {
optimizeDeps: {
include: [...SVELTE_IMPORTS]
},
plugins: [svelte()],
build: {
minify: isProduction
Expand Down
124 changes: 124 additions & 0 deletions packages/vite-plugin-svelte/src/utils/dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { log } from './log';
import path from 'path';
import fs from 'fs';
import { createRequire } from 'module';

export function findSvelteDependencies(
root: string,
cwdFallback = true
): Record<string, 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}`);

This comment has been minimized.

Copy link
@benmccann

benmccann Aug 18, 2021

Member

fyi, extra space here "in vite"

return findSvelteDependencies(cwd, false);
}
}
log.debug(`no package.json found, search failed`);
return {};
}

const stack = [{ dir: root, depPath: [] as string[] }];
// name->SvelteDependency[]
const result: Record<string, SvelteDependency[]> = {};
const doNotScan = new Set<string>([
'svelte',
'vite',
'@sveltejs/kit',
'@sveltejs/vite-plugin-svelte'
]);

while (stack.length > 0) {
const { dir, depPath } = stack.shift()!;
const pkg = parsePkg(dir);
if (!pkg) {
continue;
}
if (dir !== root) {
if (!isSvelte(pkg)) {
doNotScan.add(pkg.name);
continue;
}
if (!result[pkg.name]) {
result[pkg.name] = [];
}
result[pkg.name].push({ name: pkg.name, pkg, dir, paths: [depPath] });
}

const pkgRequire = createRequire(dir);
const deps = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.devDependencies || {})

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

I don't think we'll be able to resolve dev deps? since they're not installed when installing an npm package.

];
const resolvedDeps = deps.map((dep) => ({ name: dep, dir: getDependencyDir(pkgRequire, dep) }));
const nestedDepPath = [...depPath, pkg.name];
for (const dep of resolvedDeps) {
if (doNotScan.has(dep.name)) {
continue;
}
const existingResult = result[dep.name]?.find((x) => x.dir === dep.dir);

This comment has been minimized.

Copy link
@benmccann

benmccann Aug 18, 2021

Member

?. isn't supported on Node 12, which SvelteKit currently supports. People are constantly sending PRs with it, so I can't wait to drop Node 12

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

Should be safe to use since tsup (uses esbuild) will transpile the syntax to not use ?.

if (existingResult) {
// we already have this, just add an additional path to it
existingResult.paths.push(nestedDepPath);
} else {
stack.push({ dir: dep.dir, depPath: nestedDepPath });
}
}
}
return result;
}

// TODO better implementation
function getDependencyDir(pkgRequire: NodeRequire, dep: string) {
try {
return path.dirname(pkgRequire.resolve(path.join(dep, 'package.json')));
} catch (e) {
// does not export package.json, walk up parent directories of default export until we find the one named like the package
let dir = path.dirname(pkgRequire.resolve(path.join(dep)));

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

Is there a scenario where a package doesn't have package.json? I believe all packages should have package.json to be a valid package?

This comment has been minimized.

Copy link
@dominikg

dominikg Aug 18, 2021

Author Member

they have it, but it may not be resolvable because they don't specify it in an exports map.

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

Ah alright that makes sense. Though I'm not sure how we can make it more robust though, a package could have a different directory name than the one in package.json, e.g. local linked packages and probably pnpm nested packages?

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

Maybe resolve might be the way to go after all 😄

while (dir && path.basename(dir) !== dep) {
const parent = path.dirname(dir);
if (parent !== dir) {
dir = parent;
}
}
return dir;
}
}

function parsePkg(dir: string) {
const pkgFile = path.join(dir, 'package.json');
try {
return JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
} catch (e) {
log.warn(`failed to parse ${pkgFile}`, e);
return null;
}
}

function isSvelte(pkg: Pkg) {
return pkg.svelte || pkg.peerDependencies?.svelte;

This comment has been minimized.

Copy link
@bluwy

bluwy Aug 18, 2021

Member

I think checking pkg.svelte would be enough, we only need to auto-exclude svelte libraries that exports svelte components. Those that don't (like using svelte/store only) won't have issues if svelte and others are added to optimizeDeps.include.

}

export interface SvelteDependency {
name: string;
dir: string;
pkg: Pkg;
paths: string[][];
}

export interface Pkg {
name: string;
svelte?: string;
dependencies?: DependencyList;
peerDependencies?: DependencyList;
devDependencies?: DependencyList;
[key: string]: any;
}

export interface DependencyList {
[key: string]: string;
}
9 changes: 9 additions & 0 deletions packages/vite-plugin-svelte/src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
// eslint-disable-next-line node/no-missing-import
} from 'svelte/types/compiler/preprocess';
import path from 'path';
import { findSvelteDependencies } from './dependencies';

const knownOptions = new Set([
'configFile',
Expand Down Expand Up @@ -192,6 +193,14 @@ export function buildExtraViteConfig(
} else {
log.debug('"svelte" is excluded in optimizeDeps.exclude, skipped adding it to include.');
}

const svelteDeps = findSvelteDependencies(options.root);
console.log('svelteDeps', svelteDeps);
const svelteDepsToExclude = Object.keys(svelteDeps).filter(
(dep) => !config?.optimizeDeps?.include?.includes(dep)
);
log.debug(`automatically excluding found svelte dependencies: ${svelteDepsToExclude.join(', ')}`);
exclude.push(...svelteDepsToExclude);
const extraViteConfig: Partial<UserConfig> = {
optimizeDeps: {
include,
Expand Down

0 comments on commit 63ea6a3

Please sign in to comment.