Mimic tree-shaking behaviour when importing code from an entry file in development mode.

Overview

vite-plugin-entry-shaking

Mimic tree-shaking behaviour when importing code from an entry file in development mode.

Warning This plugin is experimental, bugs might be expected and some edge cases might not be covered.

Note The main execution logic of this plugin only applies to development mode because it addresses an issue which is specific to development mode.

Install

Open in Codeflow

# Using npm
npm i -D vite vite-plugin-entry-shaking
# Using Yarn
yarn add -D vite vite-plugin-entry-shaking
# Using pnpm
pnpm add -D vite vite-plugin-entry-shaking

Usage

Setup the plugin in your Vite configuration file.

import { resolve } from 'path';
import { defineConfig } from 'vite';
import EntryShakingPlugin from 'vite-plugin-entry-shaking';

export default defineConfig(async () => ({
  plugins: [
    await EntryShakingPlugin({
      targets: [resolve(__dirname, 'src/entry-a')],
    }),
  ],
}));

Plugin options

Option name Type Default Description
targets (required) string[]` N/A You need to list all of the entry points you want this plugin to process.
extensions string[] ['js', 'jsx', 'mjs', 'ts', 'tsx', 'mts'] This specifies which file extensions you want this plugin to process.
ignorePatterns (string | RegExp)[] [/node_modules/] This specifies RegExp/string whose matched paths must be ignored by the plugin.
debug boolean false Turns on debug mode. This will print debug logs if Vite's logLevel is set to any other value than 'silent'

Motivation

The problem this plugin tries to address is well described by this Vite's github issue, so we'll stick to its author's example. Suppose your codebase contains an index file which is basically used as an entry point to dispatch code imported from other files. This is a rather common pattern which may be handy and avoid writing a lot of individual import statements:

// shared/index.ts
export { a } from './a';
export { b } from './b';
export { c } from './c';

Let's pretend you have a module which imports c from that entry point:

// module.ts
import { c } from './shared';

In development mode, when Vite serves the module.ts file, the browser loads the shared/index.ts file which initiates requests for all of the three a, b and c modules it relies on. The thing is, we actually only needed the c module, this results in both a and b requests being unnecessary!

As your projet and entry points grow, you end up paying the price for the ever-increasing amount of unnecessary requests and HMR updates, which consumes time and resources. Well, that escalated quickly. Let's try to work around this…

The idea

The main idea is to rewrite imports from a target entry point and replace them by individual imports of the actual module. Back with the above example:

// module.ts
import { c } from './shared';

// gets rewritten as…
import { c } from './shared/c';

This way, the shared/index.ts file is not loaded by the browser anymore and no additional requests are initiated.

How it works

First of all, the plugin reads all of the target entry files listed in the plugin's targets option. For each entry file :

  • It uses es-module-lexer to get a list of imports and exports.
  • It stores named exports that are re-exports of code imported from another module and the path they resolve to. It also stores whether this re-exported code is the default or a named export of its origin. This lets us correctly rewrite the import using the adequate statement.
  • It also tracks a mutated version of the entry file where these stored named exports are removed. This is required because we might still import code which is actually defined within the entry file, rather than exported from another module. To make these work, we'll still need to serve this mutated version of the entry file so that this kind of code can be reached.

Whenever Vite serves a file which includes an import which resolved to one of the targets, this file is transformed to rewrite the relevant import statements. It extracts the list of entities imported from that entry file, and for each imported entity :

  • it removes the import statement of that entry file.
  • if it has a matching stored named export, it adds a direct import to the relevant module's absolute path, taking into consideration whether it imports a named export or a default export.
  • If it has no matching stored named export, it is some code which is actually defined within the entry file. These are batched and eventually add a recomposed import of the target.

When encountering the above latest case, we have the browser still loading the shared/index.ts, which could therefore trigger unnecessary requests, as described earlier. To prevent this kind of scenario, any import of an entry point is caught and is forced to serve its mutated version we stored while parsing the entry point file. This ensures the entry point only imports what it needs to make the code it explicitly defines work.

Limitations

  • See es-module-lexer's own limitations.
  • The following syntaxes are not supported:
    • dynamic imports
    • import json from './json.json' assert { type: 'json' }
    • import * as xxx from '…'
    • export * from '…'
Comments
  • Change replaceAll to replace

    Change replaceAll to replace

    Thank you for your great plugin.

    I faced this issue because my project is using node v14.x while replaceAll is only available since node v15.0.0 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll#browser_compatibility image

    So I think it would be great if your plugin could support node v14 or have warning about minimum Node version

    opened by nntai 2
  • Bump version

    Bump version

    This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

    Releases

    [email protected]

    Minor Changes

    • 8fee575: - Replaced the "include" option with a simpler "exclude" option. This should help avoid performance issues that could be caused by globby matching huge amounts of paths.
      • Removed the root option which is not used anymore after the above change

    [email protected]

    Minor Changes

    • 8fee575: - Replaced the "include" option with a simpler "exclude" option. This should help avoid performance issues that could be caused by globby matching huge amounts of paths.
      • Removed the root option which is not used anymore after the above change

    [email protected]

    Minor Changes

    • 8fee575: - Replaced the "include" option with a simpler "exclude" option. This should help avoid performance issues that could be caused by globby matching huge amounts of paths.
      • Removed the root option which is not used anymore after the above change
    opened by github-actions[bot] 0
  • Bump version

    Bump version

    This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

    Releases

    [email protected]

    Patch Changes

    • c0641df: - Fixed the plugin not correctly rewriting imports of aliases
      • Fixed the plugin being used in build mode (#4)
      • Added Node minimal requirements

    [email protected]

    Patch Changes

    • c0641df: - Fixed the plugin not correctly rewriting imports of aliases
      • Fixed the plugin being used in build mode (#4)
      • Added Node minimal requirements

    [email protected]

    Patch Changes

    • c0641df: - Fixed the plugin not correctly rewriting imports of aliases
      • Fixed the plugin being used in build mode (#4)
      • Added Node minimal requirements
    opened by github-actions[bot] 0
  • Bump version

    Bump version

    This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

    Releases

    [email protected]

    Patch Changes

    • a7e8fe2: Prepare release (v0.0.2)

    [email protected]

    Patch Changes

    • a7e8fe2: Prepare release (v0.0.2)

    [email protected]

    Patch Changes

    • a7e8fe2: Prepare release (v0.0.2)
    opened by github-actions[bot] 0
  • Bump version

    Bump version

    This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

    Releases

    [email protected]

    Patch Changes

    • 527b746: Prepare release (v0.0.1)

    [email protected]

    Patch Changes

    • 527b746: Prepare release (v0.0.1)

    [email protected]

    Patch Changes

    • 527b746: Prepare release (v0.0.1)
    opened by github-actions[bot] 0
  • Support for absolute import paths

    Support for absolute import paths

    Hello! Our team recently ran into similar degraded performance issues other vite users have mentioned which led us to experiment with this plugin. We are using React + Typescript + Vite.

    We configured our tsconfig and viteconfig to resolve and build paths so we can import modules from a directory's shared file. This allows us to import named modules through absolute paths by referencing a directory living directly under the baseUrl path configured in typescript so as long as that module is exported through that directory's shared file.

    // src/components/index.ts
    import a from './a'
    import b from './b'
    import c from './c'
    
    export { a, b, c }
    
    // src/directory/module.tsx
    import { a, b } from 'components'
    

    We noticed this plugin doesn't transform named imports from a shared file given an absolute path which leads to runtime errors because the module no longer exists in the shared file. Is this a misuse or a limit of the plugin on our end?

    our tsconfig.json

    {
      "compilerOptions": {
        "baseUrl": "./src",
        ...
    

    our vite.config.ts uses vite-tsconfig-paths to resolve path mapping

    export default defineConfig( async () => ({
      plugins: [
        tsconfigPaths(),
        await EntryShakingPlugin({
          targets: [
            resolve(__dirname, 'src/elements'),
            resolve(__dirname, 'src/utils'),
            resolve(__dirname, 'src/hooks'),
          ],
          debug: true,
        }),
      ],
    }))
    
    opened by RRamirez1414 2
  • Exporting a `type` in TS causes the export to be renamed `type`

    Exporting a `type` in TS causes the export to be renamed `type`

    It seems like if I have something like:

    export { Component, type Props as ComponentProps } from './Component'
    

    It gets rewritten as

    export { type } from "/src/.../Component/index.tsx";
    

    This causes an error in the browser, due to the file not actually exporting type.

    EDIT: after testing, it seems like it might be that a export type Props = {} in the Component file might be the issue? I'm not quite sure.

    opened by PaulBGD 1
  • Doesn't work in build mode

    Doesn't work in build mode

    When I run this in build mode, it still removes the exports from the index files.

    This of course causes rollup error complaining about missing exports.

    The plugin should only apply itself during dev like this: https://vitejs.dev/guide/using-plugins.html#conditional-application

    opened by shayded-exe 1
Owner
n028
n028
PDF.js Read Only is an additional readonly mode for PDF.js

PDF.js Read Only PDF.js Read Only is an additional readonly mode for PDF.js, a Portable Document Format (PDF) viewer that is built with HTML5 which is

Aprillio Latuminggi 18 Nov 2, 2022
Obsidian plugin to add keyboard shortcuts commonly found in code editors such as Visual Studio Code or Sublime Text

Code Editor Shortcuts This Obsidian plugin adds keyboard shortcuts (hotkeys) commonly found in code editors such as Visual Studio Code or Sublime Text

Tim Hor 124 Nov 26, 2022
🛠️ Standard Tooling for Vue.js Development

Vue CLI Vue CLI is the Standard Tooling for Vue.js Development. Documentation Docs are available at https://cli.vuejs.org/ - we are still working on r

vuejs 29.5k Nov 24, 2022
Mobile app development framework and SDK using HTML5 and JavaScript. Create beautiful and performant cross-platform mobile apps. Based on Web Components, and provides bindings for Angular 1, 2, React and Vue.js.

Onsen UI - Cross-Platform Hybrid App and PWA Framework Onsen UI is an open source framework that makes it easy to create native-feeling Progressive We

null 8.7k Nov 30, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 11 Sep 13, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 5 May 1, 2022
Modern Vue Stack 2022. Joyful development experience 🎉

Modern Vue Modern Vue stack 2022 with Micro front end & Monorepo ?? . Joyful development experience ?? . The main branch will keep clean for quickly c

han 259 Nov 22, 2022
Catamyst Frontend Stack - Discussion and demo for Frontend Web Development with Modern Tech Stack

Catamyst Frontend Stack Discussion and demo for Frontend Web Development with Modern Tech Stack. Made by M Haidar Hanif from Catamyst. Haidar is the F

Catamyst Community 47 Oct 18, 2022
A Vite plugin for projecting your application onto a remote production page during development.

vite-plugin-proxy-page A Vite plugin for developing an SPA in the context of a deployed production page. What's the Problem? It's an increasingly comm

Alex MacArthur 13 Nov 13, 2022
🌊 A flexible and fun JavaScript file upload library

A JavaScript library that can upload anything you throw at it, optimizes images for faster uploads, and offers a great, accessible, silky smooth user

pqina 13k Nov 21, 2022
A API documentation generator for Vue3 single file component.

doc-vue A API documentation generator for Vue3 single file component. Table of Contents Installation Write API Description Command Line Usage Programm

annnhan 36 Oct 20, 2022
Restream is a module that allows you to create a stream of an audio/video file from the Firebase storage, protected from direct download through the client-side.

nuxt-restream Restream is a module that allows you to create a stream of an audio/video file from the Firebase storage, protected from direct download

Restorm 4 Nov 19, 2022
Auto generate the corresponding `.env` file.

unplugin-vue-dotenv Auto generate the corresponding .env file. Install npm i unplugin-vue-dotenv Vite // vite.config.ts import Dotenv from 'unplugin-v

Chris 6 Oct 23, 2022
A swiss army knife with lots of tools, extensions and (scriptable) enhancements for Visual Studio Code.

vscode-powertools A swiss army knife with lots of tools, extensions and (scriptable) enhancements for Visual Studio Code. ⚠️ ⚠️ ⚠️ NOTICE: If you have

e.GO Mobile 43 Nov 15, 2022
Prefetch and sync state to client with one line of code, out-of-the-box

vue3-SSR-starter Prefetch and sync state to client with one line of code, out-of-the-box Features vue3 SSR vue-router we don't need vuex anymore one l

周子贤 36 Aug 28, 2022
⚡️ Minimal GraphQL Client + Code Generation for Nuxt

nuxt-graphql-client ⚡️ Minimal GraphQL Client + Code Generation for Nuxt ⚡️ Minimal GraphQL Client + Code Generation for Nuxt Features Zero Configurat

Dizzy 238 Nov 25, 2022
Next version of massCode [WIP]. A free and open source code snippets manager for developers

massCode next Built with Electron, Vue 3 & Ace Editor. Inspired by applications like SnippetsLab and Quiver. ☝️ massCode is currently in development,

null 4.1k Nov 22, 2022
jump to local IDE source code while click the element of browser automatically.

?? Introduction A vite plugin which provides the ability that to jump to the local IDE when you click the element of browser automatically. It support

webfansplz 315 Nov 28, 2022
⚡️ Minimal GraphQL Client + Code Generation for Nuxt3

nuxt-graphql-client ⚡️ Minimal GraphQL Client + Code Generation for Nuxt ⚡️ Minimal GraphQL Client + Code Generation for Nuxt Documentation Features Z

Conrawl Rogers 238 Nov 25, 2022