Chapter 1 ยท CORE

Framework Dependency Crawler

๐Ÿ“„ 01_framework_dependency_crawler.md ๐Ÿท Core

Chapter 1: Framework Dependency Crawler

Welcome to the Vitefu documentation! If you are building tools for Vite, you might have realized that handling dependencies isn't always straightforward. Some packages are standard JavaScript, while others contain "raw" framework code (like .svelte, .vue, or specific JSX patterns) that Vite needs to handle differently.

The Motivation: The Building Inspector

Imagine you are a building inspector tasked with checking a massive skyscraper (your project).

  1. Standard Rooms: Most rooms contain standard furniture (standard JavaScript libraries like lodash). The construction crew (Vite) can handle these quickly and automatically.
  2. Hazardous Rooms: Some rooms contain raw hazardous materials (Framework UI libraries with uncompiled components).

If the construction crew rushes into a "Hazardous Room" treating it like a normal one, accidents happen (Build Errors!).

The Framework Dependency Crawler is that inspector. It walks through every room (dependency), marks the hazardous ones, and gives Vite a precise list of instructions on which rooms are safe to speed through and which ones need special safety gear.

How to Use It

The core function of this library is crawlFrameworkPkgs. It scans your project and generates configuration options for Vite.

Here is the most basic way to use it:

import { crawlFrameworkPkgs } from 'vitefu';

const config = await crawlFrameworkPkgs({
  root: process.cwd(),
  isBuild: false, // Are we in 'vite build' or 'vite dev'?
  isFrameworkPkgByName: (pkgName) => {
    return pkgName.startsWith('my-framework-');
  }
});

Understanding the Input

To perform the inspection, the crawler needs to know what to look for. In the example above, we told it: "If a package name starts with my-framework-, treat it as hazardous/special."

Understanding the Output

The crawler returns an object with two main sections: optimizeDeps and ssr.

console.log(config);
/* Output:
{
  optimizeDeps: {
    include: [...], // Deep dependencies that need bundling
    exclude: ['my-framework-ui'] // Don't pre-bundle this! It has raw code.
  },
  ssr: {
    noExternal: ['my-framework-ui'], // Process this with Vite plugins!
    external: [...]
  }
}
*/

Internal Implementation: How it Works

Under the hood, the crawler performs a recursive search. It starts at your project's root package.json and looks at your dependencies.

High-Level Flow

sequenceDiagram participant U as User participant C as Crawler participant FS as File System U->>C: crawlFrameworkPkgs(root) C->>FS: Read root package.json loop For Every Dependency C->>C: Is this a Framework Package? alt Yes (Hazardous) C->>C: Mark as exclude/noExternal C->>C: RECURSE (Crawl inside this dep) else No (Standard) C->>C: Stop (Usually) end end C->>U: Return Config

Code Walkthrough

Let's look at the simplified logic inside src/index.js. The engine relies on a recursive function called crawl.

1. Identifying the Dependencies

First, the crawler looks at the current package.json. If we are at the root, we check devDependencies too.

// src/index.js (simplified)
async function crawl(pkgJsonPath, pkgJson, parentDepNames = []) {
  const isRoot = parentDepNames.length === 0;
  
  // If root, check devDeps. Otherwise, only regular deps.
  let deps = [
    ...Object.keys(pkgJson.dependencies || {}),
    ...(isRoot ? Object.keys(pkgJson.devDependencies || {}) : [])
  ];
  // ... filtering logic follows
}

2. The Classification Logic

This is the brain of the inspector. It decides if a dependency is "special".

// src/index.js (simplified)
deps = deps.filter((dep) => {
  const isFramework = options.isFrameworkPkgByName?.(dep);

  if (isFramework) {
    // It's raw code! Exclude from optimization.
    optimizeDepsExclude.push(dep);
    // Ensure SSR processes it.
    ssrNoExternal.push(dep);
    return true; // Keep crawling deeper!
  }
  return false; // Stop crawling this branch
});

Note: Determining exactly how to identify a framework package is complex. We cover custom logic for this in Package Classification Strategies.

3. Handling Nested Standard Libraries

Sometimes, a Framework Library (Hazardous) imports a Standard Library (Safe).

Since we excluded my-framework-ui from optimization, Vite might miss lodash. The crawler fixes this by explicitly adding deep dependencies to include.

// src/index.js (simplified)
if (!isRoot && !isFrameworkPkg) {
  // If we are deep inside a framework package, 
  // but found a standard JS package (CJS):
  if (await pkgNeedsOptimization(depPkgJson, path)) {
    // Tell Vite to bundle this specific nested path
    optimizeDepsInclude.push(parentDepNames.concat(dep).join(' > '));
  }
}

This ensures that even though the parent is raw code, the standard children are still optimized for performance.

Determining Optimization Needs

How does the crawler know a standard package needs optimization? It checks if the package is CommonJS (CJS) or has specific entry points.

// src/index.js
export async function pkgNeedsOptimization(pkgJson, pkgJsonPath) {
  // If it's modern ESM, usually no need to force optimize
  if (pkgJson.module || pkgJson.exports) return false;

  // If it has a main file pointing to .js or .cjs
  if (pkgJson.main) {
    const ext = path.extname(pkgJson.main);
    return !ext || ext === '.js' || ext === '.cjs';
  }
  return false; 
}

This heuristic is discussed further in Optimization Heuristics.

Summary

In this chapter, we learned:

  1. The Goal: Separate "Framework" (raw) packages from "Standard" packages to prevent Vite build errors.
  2. The Mechanism: A recursive crawler that inspects package.json files.
  3. The Output: It populates optimizeDeps.exclude (for raw code) and optimizeDeps.include (for standard libs nested inside raw code).

But how exactly do we tell the crawler which packages are "Framework" packages? By name? By a field in package.json?

Next Chapter: Package Classification Strategies


Generated by Code IQ