vite-magic-tree-shaking - v1.1.0
    Preparing search index...

    vite-magic-tree-shaking - v1.1.0

    vite-magic-tree-shaking

    npm version npm downloads license CI

    Auto-generate tree-shakeable build.lib.entry for Vite from your source directory.

    npm install -D vite-magic-tree-shaking
    
    // vite.config.ts
    import { defineConfig } from 'vite'
    import { fileURLToPath } from 'node:url'
    import { generateEntries } from 'vite-magic-tree-shaking'

    const __dirname = fileURLToPath(new URL('.', import.meta.url))

    export default defineConfig({
    build: {
    lib: {
    entry: generateEntries(__dirname), // scans src/ by default
    // entry: generateEntries(__dirname, 'lib'), // custom source dir
    formats: ['es', 'cjs'],
    fileName: (format, entryName) =>
    `${entryName}.${format === 'es' ? 'js' : 'cjs'}`,
    },
    rollupOptions: {
    external: ['react', 'react-dom'],
    output: {
    preserveModules: true,
    preserveModulesRoot: 'src',
    },
    },
    },
    })

    Pass warnOnExportsMismatch: true to get a console.warn at build/dev time if the exports field in package.json does not match the entries resolved from your source directory:

    entry: generateEntries(__dirname, 'src', {
    warnOnExportsMismatch: true,
    exports: { formats: ['es', 'cjs'] },
    })
    [vite-magic-tree-shaking] package.json exports are out of sync with src entries.
    Run: npx vite-magic-tree-shaking generate
    

    The package ships a vite-magic binary with two commands.

    Reads your source directory, derives the entry map, and safely merges matching entries into the exports field in package.json. Existing custom exports are preserved unless --prune is passed explicitly.

    # using npx (no install required)
    npx vite-magic-tree-shaking generate

    # custom rootDir and srcDir
    npx vite-magic-tree-shaking generate /path/to/project lib

    # project emits ES modules only
    npx vite-magic-tree-shaking generate --formats es

    # preview without modifying package.json
    npx vite-magic-tree-shaking generate --dry-run

    Example output:

    ✓ package.json exports updated:
      .
      ./Button/Button
      ./Users
      ./Users/domain/user
      ./Users/types/UserDTO
    

    Checks that the exports field in package.json matches the entries derived from the source directory. Exits with code 1 if they are out of sync — useful in CI.

    npx vite-magic-tree-shaking validate

    # also reject custom exports not generated from source
    npx vite-magic-tree-shaking validate --strict

    Example output when in sync:

    ✓ package.json exports are in sync
    

    Example output from strict validation when out of sync:

    ✗ package.json exports are out of sync with src entries
      Missing : ./NewFeature
      Extra   : ./OldFeature
      Changed : ./Users
    
    Run: npx vite-magic-tree-shaking generate
    
    {
    "scripts": {
    "sync-exports": "vite-magic generate",
    "validate-exports": "vite-magic validate",
    "prebuild": "vite-magic validate"
    }
    }

    With prebuild wired up, running npm run build will abort with a clear error if package.json exports are stale, before Vite even starts.

    Option Purpose
    --formats es,cjs Match the formats emitted by Vite
    --out-dir dist Set the JavaScript output directory
    --types-dir dist Set the declaration output directory
    --no-types Omit TypeScript declaration conditions
    --dry-run Preview changes without writing
    --prune Explicitly remove exports not generated from source
    --strict Make validate reject additional custom exports

    Given this structure:

    src/
    ├── index.ts
    ├── Users/
    │   ├── index.ts
    │   ├── domain/
    │   │   └── user.ts
    │   └── types/
    │       └── UserDTO.ts
    └── Button/
        └── Button.tsx
    

    generateEntries(__dirname) produces:

    {
    index: '/abs/src/index.ts',
    Users: '/abs/src/Users/index.ts',
    'Users/domain/user': '/abs/src/Users/domain/user.ts',
    'Users/types/UserDTO': '/abs/src/Users/types/UserDTO.ts',
    'Button/Button': '/abs/src/Button/Button.tsx',
    }

    And vite-magic generate writes the corresponding exports to package.json:

    {
    "exports": {
    ".": {
    "types": "./dist/index.d.ts",
    "import": "./dist/index.js",
    "require": "./dist/index.cjs"
    },
    "./Users": {
    "types": "./dist/Users/index.d.ts",
    "import": "./dist/Users.js",
    "require": "./dist/Users.cjs"
    },
    "./Users/domain/user": {
    "types": "./dist/Users/domain/user.d.ts",
    "import": "./dist/Users/domain/user.js",
    "require": "./dist/Users/domain/user.cjs"
    },
    "./Users/types/UserDTO": {
    "types": "./dist/Users/types/UserDTO.d.ts",
    "import": "./dist/Users/types/UserDTO.js",
    "require": "./dist/Users/types/UserDTO.cjs"
    },
    "./Button/Button": {
    "types": "./dist/Button/Button.d.ts",
    "import": "./dist/Button/Button.js",
    "require": "./dist/Button/Button.cjs"
    }
    }
    }
    Source Entry key
    src/index.ts index
    src/Users/index.ts Users
    src/Button/Button.tsx (no index in dir) Button/Button
    src/Users/domain/user.ts (subdir of indexed dir) Users/domain/user

    Directories with an index file use the directory name as key. Their subdirectories are still scanned recursively.

    Directories without an index expose each file individually using its relative path (without extension).

    Symbolic links are ignored by default. Set followSymlinks: true to follow links whose resolved target remains inside the source directory. Duplicate entry keys and multiple index files fail with a descriptive error instead of silently overwriting an entry.

    The following are never included as entries:

    • *.test.ts / *.spec.ts
    • *.stories.tsx
    • *.d.ts / *.d.mts / *.d.cts
    • Any file with an extension other than .ts, .tsx, .js, .jsx, .mts, .cts
    generateEntries(rootDir: string, srcDir?: string, options?: GenerateEntriesOptions): Record<string, string>
    
    Parameter Type Default Description
    rootDir string Absolute path to the project root
    srcDir string 'src' Source directory name, relative to rootDir
    options GenerateEntriesOptions {} Optional configuration
    Option Type Default Description
    warnOnExportsMismatch boolean false Emit a console.warn if package.json exports do not match the resolved entries
    followSymlinks boolean false Follow links that stay inside srcDir
    include (path: string) => boolean Include matching valid source files
    exclude (path: string) => boolean Exclude matching files or directories
    onCollision 'error' | 'overwrite' 'error' Choose how duplicate keys are handled
    exports ExportsOptions {} Output contract used by mismatch warnings
    entryRecordToExports(entries, {
    sourceRoot: '/project/src',
    formats: ['es'],
    outDir: 'dist',
    typesOutDir: 'dist',
    })

    The generated conditions only include formats that the build emits. Declaration paths retain the source layout, so src/Users/index.ts correctly maps to dist/Users/index.d.ts.

    MIT