{"id":20916173,"url":"https://github.com/emosheeep/vite-plugin-lib-inject-css","last_synced_at":"2025-04-05T21:08:15.969Z","repository":{"id":64230389,"uuid":"573665071","full_name":"emosheeep/vite-plugin-lib-inject-css","owner":"emosheeep","description":"Inject css at the top of chunk file in lib mode using import statement, support multiple entries.","archived":false,"fork":false,"pushed_at":"2024-03-23T09:05:07.000Z","size":1194,"stargazers_count":97,"open_issues_count":1,"forks_count":9,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-03-23T11:21:38.445Z","etag":null,"topics":["build","inject","lib","style","vite","vite-plugin"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/~/github.com/emosheeep/vite-plugin-lib-inject-css","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emosheeep.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-03T03:10:54.000Z","updated_at":"2024-05-09T05:32:50.350Z","dependencies_parsed_at":"2024-03-23T11:20:40.774Z","dependency_job_id":"5be89f1b-8658-402c-bf62-ee9a2d86106a","html_url":"https://github.com/emosheeep/vite-plugin-lib-inject-css","commit_stats":{"total_commits":17,"total_committers":2,"mean_commits":8.5,"dds":0.05882352941176472,"last_synced_commit":"44246153a294a773916e73255178c1af30609b4d"},"previous_names":["emosheeep/vite-plugin-lib-inject-css"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emosheeep%2Fvite-plugin-lib-inject-css","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emosheeep%2Fvite-plugin-lib-inject-css/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emosheeep%2Fvite-plugin-lib-inject-css/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emosheeep%2Fvite-plugin-lib-inject-css/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emosheeep","download_url":"https://codeload.github.com/emosheeep/vite-plugin-lib-inject-css/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399877,"owners_count":20932876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build","inject","lib","style","vite","vite-plugin"],"created_at":"2024-11-18T16:20:13.139Z","updated_at":"2025-04-05T21:08:15.933Z","avatar_url":"https://github.com/emosheeep.png","language":"TypeScript","readme":"# vite-plugin-lib-inject-css\n\n[![npm version](https://img.shields.io/npm/v/vite-plugin-lib-inject-css)](https://npmjs.com/package/vite-plugin-lib-inject-css)\n![weekly downloads](https://img.shields.io/npm/dw/vite-plugin-lib-inject-css)\n![license](https://img.shields.io/npm/l/vite-plugin-lib-inject-css)\n![stars](https://img.shields.io/github/stars/emosheeep/vite-plugin-lib-inject-css)\n\nEnglish | [简体中文](https://juejin.cn/post/7214374960192782373)\n\nInject css at the top of each chunk file in library mode using `import` statement, support multi-entries build, especially to help building component libraries.\n\n```js\n// bundled js file, with import css at top (if any)\nimport './style.css';\n// rest of the file\n// ...\n```\n\n\u003e **Can css be styleInjected in library mode**? [vite#1579](https://github.com/vitejs/vite/issues/1579).\n\n# Features\n\nNote that this plugin only works with [library-mode](https://vitejs.dev/guide/build.html#library-mode).\n\n- 💡 Multiple entires support.\n- ⚡️ Sourcemap support.\n- 💻 SSR build support.\n- 🛠 Out-of-box, tiny and pretty.\n\n# Quick Experience\n\nView the [cloud ide](https://stackblitz.com/~/github.com/emosheeep/vite-plugin-lib-inject-css) on stackblitz.com, and setup with the following scripts:\n\n```sh\npnpm install\ncd example\npnpm build\n```\n\n# Usage\n\n```shell\npnpm i vite-plugin-lib-inject-css -D # npm/yarn\n```\n\n```js\n// vite.config.ts\nimport { libInjectCss } from 'vite-plugin-lib-inject-css';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    libInjectCss(),\n  ],\n  build: {\n    lib: {\n      formats: ['es'],\n      entry: {\n        index: 'src/index.ts',\n        button: 'src/components/button/index.ts',\n        select: 'src/components/select/index.ts',\n      },\n    }\n    rollupOptions: {\n      output: {\n        // Put chunk files at \u003coutput\u003e/chunks\n        chunkFileNames: 'chunks/[name].[hash].js',\n        // Put chunk styles at \u003coutput\u003e/assets\n        assetFileNames: 'assets/[name][extname]',\n        entryFileNames: '[name].js',\n      },\n    },\n  }\n})\n```\n\n# Motivation\n\nVite shines in Web project development, but it can also be used for library projects.\n\nBut when it comes to component library development, multiple entries are often involved. Although `Vite 3.2+` later supports multiple entry points, it doesn't provide relevant options for us to associate css with component, which leads to the problem that we can't distinguish between the entry point and css files even though the bundle is successful.\n\nBased on this, we need to write a plugin to try to find the relationship between the two, and inject styles correctly.\n\n# How does It Work\n\nAs a library(mostly component library), we want to import styles automatically when referencing the component.\n\n```js\n/** component-lib/dist/button.js */\nimport './assets/button.css'; // This is what this plugin do;\n...\nexport default Button;\n\n/** component-lib/dist/index.js */\nimport Button from './button.js';\nimport xxx from './xxx.js';\nexport { Button, xxx };\n...\n\n/** In our project's main file */\n// Once we import Button, styles'll be imported together.\nimport { Button } from 'component-lib';\n```\n\nThe simplest way is to add a line `import './style.css';` to the top of the generated file, multiple lines are multiple lines. As a library provider, we should **provide flexibility as much as possible**, and **delegate the task of how to handle these css files to the user's build tool**.\n\nBut most of the Vite plugins on the market that claim to automatically inject CSS are designed in a way use `document.createElement ('style')`, which is not graceful, and **it assumes that the current is in the Browser's DOM environment, which makes the library SSR-incompatible**.\n\nHere's the reply([vite#issuecomment](https://github.com/vitejs/vite/issues/1579#issuecomment-763295757)) of the author of Vite, Evan You.\n\nSo the main problem becomes to **how do we know which style files are involved in one chunk file**?\n\nIn fact, vite adds a property named `viteMetadata` on each chunk file in plugin lifecycle, you can check [css.ts](https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/css.ts) for further information.\n\nWe can get which resources(include CSS files) are associated with current chunk file by using this property. Based on this, the plugin injects styles by using [renderChunk](https://rollupjs.org/plugin-development/#renderchunk) hook, which is the simplest and most effective way.\n\n*Prefer to check plugin source code to get more information and welcome to make contribution*, which is simple enough(100+ lines).\n\n# Recipes of Creating Component Library\n\nHow do we create a component library with vite, which can auto import styles and has out-of-box tree-shaking functionality?\n\nMost of component libraries provide two ways. One is totally import:\n\n```js\nimport Vue from 'vue';\nimport XxxUI from 'component-lib';\nVue.use(XxxUI);\n```\n\nThe other is import on demand, in common uses with a third-part plugin like `babel-plugin-import`:\n\n```js\nimport { Button } from 'component-lib';\n// ↓ ↓ ↓ transformed ↓ ↓ ↓\nimport Button from 'component-lib/dist/button/index.js'\nimport 'component-lib/dist/button/style.css'\n```\n\n**But the best way is that when we use named imports, the style imports and tree-shaking can be applied automatically**.\n\n## How Should We do\n\nFortunately, ES Module naturally has static analysis capabilities, and mainstream tools basically implement ESM-based Tree-shaking functions, such as `webpack/rollup/vite`.\n\nThen we only need the following three steps:\n1. Adjust the output format to ES Module → Out-of-the-box Tree-shaking functionality.\n2. Use this plugin for style injection → auto import styles\n3. The last and most important, **add all of the entry files you’ve exported in main.js to your `rollup.input` configurations**.\n\nIt should be noted that the import of CSS files has side effects, and we also need to **declare the [sideEffects](https://webpack.js.org/guides/tree-shaking) field in the library's package.json file** to prevent the CSS file from being accidentally removed by the user side builds.\n\nHere's an example:\n\n```json\n{\n  \"name\": \"component-lib\",\n  \"version\": \"1.0.0\",\n  \"main\": \"dist/index.mjs\",\n  \"sideEffects\": [\n    \"**/*.css\"\n  ]\n}\n```\n\n# Questions\n\n## Why does Style Code Injection Fail?\n\nHere're some possible reasons:\n\n- When `build.cssCodeSplit` is `false`, all of css code will be collected into a standalone css file named `style.css`, **style injection will be skipped**.\n\n- *Before v2.2.0*, when `rollupOptions.output.preserveModules` is enabled, the style code injection will be skipped for some reasons. This problem has been solved, refers to [#29](https://github.com/emosheeep/vite-plugin-lib-inject-css/issues/29).\n\n```js\n{\n  build: {\n    cssCodeSplit: true, // Required. This is set internally by default.\n  },\n  rollupOptions: {\n    output: {\n      preserveModules: false, // Required before v2.2.0.\n    }\n  }\n}\n```\n\n## About Rollup Option `output.preserveModules`\n\nAnytime when you attempt using this option, there in common may  has more efficient ways to help you. For example, **you can turn every file into an entry point, like suggested in the Rollup [docs](https://rollupjs.org/configuration-options/#input)**:\n\n\u003e If you want to convert a set of files to another format while maintaining the file structure and export signatures, the recommended way—instead of using `output.preserveModules` that may tree-shake exports as well as emit virtual files created by plugins—is to turn every file into an entry point. You can do so dynamically e.g. via the `glob` package:\n\n## Why do Additional Empty Imports Turn Up in Entry Chunks?\n\nWhen using **multiple** chunks, imports of dependencies of entry chunks will be added as empty imports to the entry chunks themselves. This is rollup's default behavior **in order for javascript engine performance optimizations**, you can turn it off via `output.hoistTransitiveImports: false`.\n\nHere's an example:\n\n```js\n// dist/demo.js\nimport { _ as p } from \"../chunks/demo.js\";\nimport \"vue\";\nimport \"axios\";\nimport \"lodash-es\";\nimport \"xxx\"; // ... and so on.\nexport {\n  p as Demo\n};\n```\n\nFurther, see [why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting](https://rollupjs.org/faqs/#why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting).\n\nAs a third-part library, this behavior may cause sideEffects and make tree-shaking fail, so we set `hoistTransitiveImports: false` internally by default, you can still manually overwrite it.\n\n## Output Directory Structure is Ugly when Building with Multi-entries.\n\nWhen we build our library with multi-entries, the output looks as follows in common:\n\n```\ndist/demo.css            0.05 kB │ gzip: 0.07 kB\ndist/demo-451ab1e5.mjs   0.36 kB │ gzip: 0.26 kB\ndist/demo-component.mjs  0.09 kB │ gzip: 0.10 kB\ndist/index.mjs           0.16 kB │ gzip: 0.14 kB\n```\n\nGenerally speaking, **the users who use your library usually don't care about how does your dist directory structure looks like**. Just don’t tangle this, we just need to ensure that the unused files will get tree-shaken when we use named imports from the library.\n\nIndeed, you can customize filenames by using `output.xxxFileNames` options:\n\n```js\n{\n  rollupOptions: {\n    output: {\n      // Put chunk files at \u003coutput\u003e/chunks\n      chunkFileNames: 'chunks/[name].[hash].js',\n      // Put chunk styles at \u003coutput\u003e/assets\n      assetFileNames: 'assets/[name][extname]',\n      entryFileNames: '[name].js',\n    },\n  },\n}\n```\n\nAnd then you'll get:\n\n```\ndist/assets/demo.css          0.05 kB │ gzip: 0.07 kB\ndist/chunks/demo.a6107609.js  0.37 kB │ gzip: 0.26 kB\ndist/demo-component.js        0.09 kB │ gzip: 0.10 kB\ndist/index.js                 0.15 kB │ gzip: 0.14 kB\n```\n\n# License\n\n[MIT](./LICENSE) License © 2023 [秦旭洋](https://github.com/emosheeep)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femosheeep%2Fvite-plugin-lib-inject-css","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femosheeep%2Fvite-plugin-lib-inject-css","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femosheeep%2Fvite-plugin-lib-inject-css/lists"}