Multiple entry points/output in library mode? #1736
-
Hi. Vite supports generating multiple page apps by passing an object to |
Beta Was this translation helpful? Give feedback.
Replies: 16 comments 46 replies
-
library mode really is just a preset of rollup output options. You can check out https://github.com/vitejs/vite/blob/main/packages/vite/src/node/build.ts#L345-L372 to tweak it to fit your needs. |
Beta Was this translation helpful? Give feedback.
-
import fs from 'fs'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import { build } from 'vite'
import { babel } from '@rollup/plugin-babel'
import glob from 'fast-glob'
const imports = []
const getImports = async () => {
const files = await glob([path.resolve('../../components/**/package.json'), '!**/node_modules/**/*'])
files.forEach(file => {
const content = fs.readFileSync(file, 'utf-8')
const pkg = JSON.parse(content)
if (pkg.imports) {
imports.push({
name: pkg.name,
lib: path.resolve(file, '../', pkg.imports.lib),
style: path.resolve(file, '../', pkg.imports.style)
})
}
})
}
await getImports()
imports.forEach(async item => {
await build({
configFile: false,
build: {
lib: {
entry: item.lib,
name: item.name
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
},
assetFileNames: `${item.name}/[name].[ext]`,
entryFileNames: () => '[name]/[name].[format].js'
}
}
},
plugins: [vue(), babel({ babelHelpers: 'bundled' })]
})
}) |
Beta Was this translation helpful? Give feedback.
-
This question has been marked as "answered" but there are 4-5 different answers, lol. I understand the spirit of Evan's answer but the point is that Rollup natively supports the return of multiple entries in a canonical way, and I don't believe we've received an agreed upon answer on what the canonical way of achieving this is in Vite? @falstack seems to be closest to the mark but can we formally reach a consensus on how this is intended to be achieved? 😅 i.e. this seems like something that should be more explicitly documented, especially for cases where, for example, there is no top-level entry point e.g. with an SFC-based shared component library (my particular use-case). To put it another way, why is the accepted answer a reference to Vite source-code? lol 😅 I'd be happy to collaborate with others on this thread to better formalize the recommended canonical approach, I just do not want to open a duplicate issue. |
Beta Was this translation helpful? Give feedback.
-
There is some light on the horizon in the shape of these two pull requests: #7047 #4530 |
Beta Was this translation helpful? Give feedback.
-
set up a
and then use
🔥 Finally |
Beta Was this translation helpful? Give feedback.
-
I did the same as @osama996 but instead having to need a # scripts/buid.sh
LIB=core pnpm exec vite build && \
LIB=scripts pnpm exec vite build Then I've created a map config and selected it via the // vite.config.ts
import path from 'path';
import { defineConfig } from 'vite';
const isExternal = (id: string) => !id.startsWith('.') && !path.isAbsolute(id);
type LibTypes = 'core' | 'scripts';
const LIB: LibTypes = (process.env.LIB as LibTypes) ?? 'core';
const libConfig: Record<LibTypes, { entry: string; fileName: string }> = {
core: {
entry: path.resolve(__dirname, 'src/index.ts'),
fileName: 'core',
},
scripts: {
entry: path.resolve(__dirname, 'src/scripts/index.ts'),
fileName: 'scripts',
},
};
const currentConfig = libConfig[LIB];
export default defineConfig(() => ({
build: {
outDir: 'dist',
sourcemap: true,
emptyOutDir: false, // important to add otherwise vite will remove the dist folder before run the second build
lib: {
fileName: currentConfig.fileName,
entry: currentConfig.entry,
formats: ['es', 'cjs'],
},
rollupOptions: {
external: isExternal,
},
},
})); Now running my It seems an ok solution until it gets supported natively. |
Beta Was this translation helpful? Give feedback.
-
I imported Vite build module and loop it through the libraries. For example, create a file
The generated files are
|
Beta Was this translation helpful? Give feedback.
-
For those who are struggling with that, I've written a post based on some solutions I encountered here and create a repository with some examples: https://www.raulmelo.dev/blog/build-javascript-library-with-multiple-entry-points-using-vite-3 |
Beta Was this translation helpful? Give feedback.
-
Vite@3.1.3+ has solved the problem #10116 that But I think the main reason that Vite doesn't support lib mode with multiple entries is that rollupOptions.output.inlineDynamicImports doesn't support multiple entry points while Vite's lib mode usually make all chunks inline and set it true if there is only one resolved entry, which is actually a flaw of rollup. I don't know whether you noticed that you will get some common chunks as follows, and the more complicated the dependencies are, the more common chunks you will get. And we can get only one As a common UI components library, we usually need to provide an out-of-box way to import on demand. But for now, we have to manually write scripts using JavaScript API provided by Vite if you want to make outputs inline and stored into corresponding directory while using multiple entries. |
Beta Was this translation helpful? Give feedback.
-
I temporarily gave up this solution, and now refer to the element-plus solution |
Beta Was this translation helpful? Give feedback.
-
Hi all, I had to create a vue/js library for multiple projects and was able to get this structure:
by using this snippet (inspired by @emosheeep solution): // vite.config.js
import glob from 'fast-glob';
...
const files = glob.sync(['./src/{components,composables}/**/*.{vue,js}'])
.map(file => {
const key = file.match(/(?<=\.\/src\/).*(?=\.js|\.vue)/);
return [key[0], file];
});
const entries = Object.fromEntries(files);
export default defineConfig({
...
build: {
sourcemap: true,
lib: {
entry: entries
}
}
}) // package.json
{
"exports": {
".": {
"import": "./dist/*.js"
},
"./components/*": {
"import": "./dist/components/*.js"
},
"./composables/*": {
"import": "./dist/composables/*.js"
}
},
...
} It's currently working on vite |
Beta Was this translation helpful? Give feedback.
-
vite: 4.1.1 import { defineConfig } from "vite";
import { resolve } from "path";
export default defineConfig({
build: {
outDir: "./dist",
lib: {
entry: [
resolve(__dirname, "./src/content.ts"),
resolve(__dirname, "./src/popup.ts"),
],
fileName: (_, entryName) => {
return `js/${entryName}.js`;
},
},
},
}); It will create file under the dist
├── css
│ └── popup.css
├── images
│ ├── icon-128.png
│ ├── icon-16.png
│ ├── icon-32.png
│ └── icon-48.png
├── js
│ ├── content.js
│ └── popup.js
├── manifest.json
└── popup.html
4 directories, 9 files |
Beta Was this translation helpful? Give feedback.
-
🙏 The official documentation should be more explicit with this. 🙏In my case I solved it in a relatively simple way.
Below an example: import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'
import fg from 'fast-glob'
// Defines an array of entry points to be used to search for files.
const entryPoints = [
'src/common/**/*.ts',
'src/utils.ts',
'src/index.ts'
]
// Searches for files that match the patterns defined in the array of input points.
// Returns an array of absolute file paths.
const files = fg.sync(entryPoints, { absolute: true })
// Maps the file paths in the "files" array to an array of key-value pair.
const entities = files.map((file) => {
// Extract the part of the file path after the "src" folder and before the file extension.
const [key] = file.match(/(?<=src\/).*$/) || []
// Remove the file extension from the key.
const keyWithoutExt = key.replace(/\.[^.]*$/, '')
return [keyWithoutExt, file]
})
// Convert the array of key-value pairs to an object using the Object.fromEntries() method.
// Returns an object where each key is the file name without the extension and the value is the absolute file path.
const entries = Object.fromEntries(entities)
export default defineConfig({
plugins: [dts()],
build: {
outDir: 'lib',
lib: {
entry: entries,
formats: ['es']
}
}
}) With this setup, the output of our files will have a structure similar to the way we have structured our project. |
Beta Was this translation helpful? Give feedback.
-
It would be great if we could have different formats for different entries. I am building a worker which needs to be an iife, as a part of a library. I can't use vite's worker loader support as the url from which it's loaded is user-configurable, and can't be I then need to do something like: // this will get stripped out of the bundle, but the worker will get emitted
if (false) {
new Worker(new URL("./worker.ts", import.meta.url));
}
export function loadWorker(url: string) {
const worker = new Worker(new URL("./worker.ts", url));
} And I can't use it as an entry inside the lib array, as I can't configure it to be emitted as an |
Beta Was this translation helpful? Give feedback.
-
Here is a way to bundle multi-entry component library #1579 (comment). |
Beta Was this translation helpful? Give feedback.
-
This is a SPA syndrome problem, where the Js community is oblivious to the fact that everything is NOT a SPA with a single HTML entry. The Monolith and multi-page-applications is coming back in a big way for VERY good reasons, but now we're stuck with build tools designed around a SPA mentality, and we need to apply String Theory to get simple concepts like multiple entry/output to work properly and easily. You have a Django, Laravel or Slim app (or native PHP), you have 50 modules i.e. Auth, Users, Products, Orders, Pages... etc., each callable module has its own set of images, Js and css, and we need to build those to public dist/build (or CDN) in the same directory structure so that we can easily include those assets into our SSR view files ONLY when that specific module is used, we DONT need all 50 module's assets bundled together (regardless chunks). Better decoupling, better maintenance and smaller files. |
Beta Was this translation helpful? Give feedback.
library mode really is just a preset of rollup output options. You can check out https://github.com/vitejs/vite/blob/main/packages/vite/src/node/build.ts#L345-L372 to tweak it to fit your needs.