vite-plugin-entry-shaking
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -17,2 +17,2 @@ import { PluginOption } from 'vite'; | ||
export { createEntryShakingPlugin as default }; | ||
export { createEntryShakingPlugin, createEntryShakingPlugin as default }; |
{ | ||
"name": "vite-plugin-entry-shaking", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Mimic tree-shaking behaviour when importing code from an entry file in development mode.", | ||
@@ -47,8 +47,9 @@ "author": "Charles Gruenais", | ||
"@vitest/coverage-istanbul": "^0.23.4", | ||
"@yungezeit/eslint-config-typescript": "^0.0.8", | ||
"eslint": "^8.23.1", | ||
"@yungezeit/eslint-config-typescript": "^0.0.12", | ||
"eslint": "^8.46.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"ts-dedent": "^2.2.0", | ||
"typescript": "^4.8.3", | ||
"unbuild": "^0.8.11", | ||
"vite": "^4.0.1", | ||
"vite": "^4.0.5", | ||
"vitest": "^0.23.2" | ||
@@ -58,6 +59,8 @@ }, | ||
"dev": "pnpm unbuild --stub", | ||
"postinstall": "pnpm build", | ||
"build": "pnpm unbuild", | ||
"lint": "eslint .", | ||
"lint:fix": "eslint . --fix", | ||
"test": "vitest", | ||
"test": "vitest run", | ||
"test:watch": "vitest", | ||
"test:coverage": "vitest run --coverage" | ||
@@ -64,0 +67,0 @@ }, |
@@ -7,7 +7,7 @@ <h1 align="center">vite-plugin-entry-shaking</h1> | ||
> **Warning** | ||
> This plugin is experimental, bugs might be expected and some edge cases might not be covered. | ||
> **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. | ||
> **Note** The main execution logic of this plugin only applies to development mode because it | ||
> addresses an issue which is specific to development mode. | ||
@@ -18,3 +18,2 @@ ## Install | ||
```bash | ||
@@ -38,9 +37,9 @@ # Using npm | ||
export default defineConfig(async () => ({ | ||
export default defineConfig({ | ||
plugins: [ | ||
await EntryShakingPlugin({ | ||
EntryShakingPlugin({ | ||
targets: [resolve(__dirname, 'src/entry-a')], | ||
}), | ||
], | ||
})); | ||
}); | ||
``` | ||
@@ -62,3 +61,3 @@ | ||
<td>targets <em>(required)</em></td> | ||
<td><code>string[]`</code></td> | ||
<td><code>string[]</code></td> | ||
<td>N/A</td> | ||
@@ -90,7 +89,15 @@ <td>You need to list all of the entry points you want this plugin to process.</td> | ||
Examples illustrating usage and benefits can be found [here](https://github.com/Dschungelabenteuer/vite-plugin-entry-shaking/tree/main/examples). Feel free to fork and play around. For instance, you can toggle the plugin on and off their respective vite config file while serving in development mode and see how it affects the amount of requests made by your browser. | ||
Examples illustrating usage and benefits can be found | ||
[here](https://github.com/Dschungelabenteuer/vite-plugin-entry-shaking/tree/main/examples). Feel | ||
free to fork and play around. For instance, you can toggle the plugin on and off their respective | ||
vite config file while serving in development mode and see how it affects the amount of requests | ||
made by your browser. | ||
## Motivation | ||
The problem this plugin tries to address is well described by this [Vite's github issue](https://github.com/vitejs/vite/issues/8237), 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: | ||
The problem this plugin tries to address is well described by this | ||
[Vite's github issue](https://github.com/vitejs/vite/issues/8237), 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: | ||
@@ -111,9 +118,15 @@ ```ts | ||
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! | ||
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… | ||
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: | ||
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: | ||
@@ -128,24 +141,47 @@ ```ts | ||
This way, the `shared/index.ts` file is not loaded by the browser anymore and no additional requests are initiated. | ||
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`](https://github.com/guybedford/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. | ||
First of all, the plugin reads all of the target entry files listed in the plugin's `targets` | ||
option. For each entry file : | ||
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. | ||
- It uses [`es-module-lexer`](https://github.com/guybedford/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. | ||
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. | ||
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](https://github.com/guybedford/es-module-lexer#limitations). | ||
* The following syntaxes are not supported: | ||
* dynamic imports | ||
* `import json from './json.json' assert { type: 'json' }` | ||
* `import * as xxx from '…'` | ||
* `export * from '…'` | ||
- See `es-module-lexer`'s | ||
[own limitations](https://github.com/guybedford/es-module-lexer#limitations). | ||
- The following syntaxes are not handled: | ||
- dynamic imports | ||
- `import json from './json.json' assert { type: 'json' }` | ||
- `import * as xxx from '…'` | ||
- `export * from '…'` | ||
> **Note** This does not mean you should expect errors using these. Instead, it just means the | ||
> content they intend to import won't be tree-shaken by the plugin. |
@@ -8,3 +8,3 @@ import type { PluginOption, Logger, ResolveFn } from 'vite'; | ||
export default async function createEntryShakingPlugin( | ||
export async function createEntryShakingPlugin( | ||
userOptions: PluginOptions, | ||
@@ -37,10 +37,3 @@ ): Promise<PluginOption> { | ||
async transform(code, id) { | ||
return await transformIfNeeded( | ||
id, | ||
code, | ||
entries, | ||
options, | ||
resolver, | ||
logger, | ||
); | ||
return await transformIfNeeded(id, code, entries, options, resolver, logger); | ||
}, | ||
@@ -56,1 +49,3 @@ | ||
} | ||
export default createEntryShakingPlugin; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37186
181
10
756
1