Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extract css false option in lib build mode #4345

Open
4 tasks done
JackFGreen opened this issue Jul 22, 2021 · 19 comments · May be fixed by #6569 or #13565
Open
4 tasks done

Add extract css false option in lib build mode #4345

JackFGreen opened this issue Jul 22, 2021 · 19 comments · May be fixed by #6569 or #13565

Comments

@JackFGreen
Copy link

Clear and concise description of the problem

When buliding a lib, css will be extracted in a single file style.css, is there a way to bundle css in output js.

Suggested solution

Like rollup-plugin-postcss can set extract

Alternative

No response

Additional context

No response

Validations

@emosheeep
Copy link
Contributor

I have the same problem which I suffer from..

@kooriookami
Copy link

Me too

1 similar comment
@falstack
Copy link
Contributor

falstack commented Sep 7, 2021

Me too

@xinyao27
Copy link

I encountered the same problem, css cannot be loaded correctly when building lib mode.

I created a plugin to temporarily solve this problem.

import fs from 'fs'
import { resolve } from 'path'
import type { ResolvedConfig, PluginOption } from 'vite'

const fileRegex = /\.(css)$/

const injectCode = (code: string) =>
  `function styleInject(css,ref){if(ref===void 0){ref={}}var insertAt=ref.insertAt;if(!css||typeof document==="undefined"){return}var head=document.head||document.getElementsByTagName("head")[0];var style=document.createElement("style");style.type="text/css";if(insertAt==="top"){if(head.firstChild){head.insertBefore(style,head.firstChild)}else{head.appendChild(style)}}else{head.appendChild(style)}if(style.styleSheet){style.styleSheet.cssText=css}else{style.appendChild(document.createTextNode(css))}};styleInject(\`${code}\`)`
const template = `console.warn("__INJECT__")`

let viteConfig: ResolvedConfig
const css: string[] = []

export default function libInjectCss(): PluginOption {
  return {
    name: 'lib-inject-css',

    apply: 'build',

    configResolved(resolvedConfig: ResolvedConfig) {
      viteConfig = resolvedConfig
    },

    transform(code: string, id: string) {
      if (fileRegex.test(id)) {
        css.push(code)
        return {
          code: '',
        }
      }
      if (
        // @ts-ignore
        id.includes(viteConfig.build.lib.entry)
      ) {
        return {
          code: `${code}
          ${template}`,
        }
      }
      return null
    },

    async writeBundle(_: any, bundle: any) {
      for (const file of Object.entries(bundle)) {
        const { root } = viteConfig
        const outDir: string = viteConfig.build.outDir || 'dist'
        const fileName: string = file[0]
        const filePath: string = resolve(root, outDir, fileName)

        try {
          let data: string = fs.readFileSync(filePath, {
            encoding: 'utf8',
          })

          if (data.includes(template)) {
            data = data.replace(template, injectCode(css.join('\n')))
          }

          fs.writeFileSync(filePath, data)
        } catch (e) {
          console.error(e)
        }
      }
    },
  }
}

https://github.com/ohbug-org/ohbug-extension-feedback/blob/main/libInjectCss.ts

@jackchoumine
Copy link

I encountered the same problem, css cannot be loaded correctly when building lib mode.

I created a plugin to temporarily solve this problem.

import fs from 'fs'
import { resolve } from 'path'
import type { ResolvedConfig, PluginOption } from 'vite'

const fileRegex = /\.(css)$/

const injectCode = (code: string) =>
  `function styleInject(css,ref){if(ref===void 0){ref={}}var insertAt=ref.insertAt;if(!css||typeof document==="undefined"){return}var head=document.head||document.getElementsByTagName("head")[0];var style=document.createElement("style");style.type="text/css";if(insertAt==="top"){if(head.firstChild){head.insertBefore(style,head.firstChild)}else{head.appendChild(style)}}else{head.appendChild(style)}if(style.styleSheet){style.styleSheet.cssText=css}else{style.appendChild(document.createTextNode(css))}};styleInject(\`${code}\`)`
const template = `console.warn("__INJECT__")`

let viteConfig: ResolvedConfig
const css: string[] = []

export default function libInjectCss(): PluginOption {
  return {
    name: 'lib-inject-css',

    apply: 'build',

    configResolved(resolvedConfig: ResolvedConfig) {
      viteConfig = resolvedConfig
    },

    transform(code: string, id: string) {
      if (fileRegex.test(id)) {
        css.push(code)
        return {
          code: '',
        }
      }
      if (
        // @ts-ignore
        id.includes(viteConfig.build.lib.entry)
      ) {
        return {
          code: `${code}
          ${template}`,
        }
      }
      return null
    },

    async writeBundle(_: any, bundle: any) {
      for (const file of Object.entries(bundle)) {
        const { root } = viteConfig
        const outDir: string = viteConfig.build.outDir || 'dist'
        const fileName: string = file[0]
        const filePath: string = resolve(root, outDir, fileName)

        try {
          let data: string = fs.readFileSync(filePath, {
            encoding: 'utf8',
          })

          if (data.includes(template)) {
            data = data.replace(template, injectCode(css.join('\n')))
          }

          fs.writeFileSync(filePath, data)
        } catch (e) {
          console.error(e)
        }
      }
    },
  }
}

https://github.com/ohbug-org/ohbug-extension-feedback/blob/main/libInjectCss.ts

How to use it? install a npm?

@xinyao27
Copy link

I encountered the same problem, css cannot be loaded correctly when building lib mode.
I created a plugin to temporarily solve this problem.

import fs from 'fs'
import { resolve } from 'path'
import type { ResolvedConfig, PluginOption } from 'vite'

const fileRegex = /\.(css)$/

const injectCode = (code: string) =>
  `function styleInject(css,ref){if(ref===void 0){ref={}}var insertAt=ref.insertAt;if(!css||typeof document==="undefined"){return}var head=document.head||document.getElementsByTagName("head")[0];var style=document.createElement("style");style.type="text/css";if(insertAt==="top"){if(head.firstChild){head.insertBefore(style,head.firstChild)}else{head.appendChild(style)}}else{head.appendChild(style)}if(style.styleSheet){style.styleSheet.cssText=css}else{style.appendChild(document.createTextNode(css))}};styleInject(\`${code}\`)`
const template = `console.warn("__INJECT__")`

let viteConfig: ResolvedConfig
const css: string[] = []

export default function libInjectCss(): PluginOption {
  return {
    name: 'lib-inject-css',

    apply: 'build',

    configResolved(resolvedConfig: ResolvedConfig) {
      viteConfig = resolvedConfig
    },

    transform(code: string, id: string) {
      if (fileRegex.test(id)) {
        css.push(code)
        return {
          code: '',
        }
      }
      if (
        // @ts-ignore
        id.includes(viteConfig.build.lib.entry)
      ) {
        return {
          code: `${code}
          ${template}`,
        }
      }
      return null
    },

    async writeBundle(_: any, bundle: any) {
      for (const file of Object.entries(bundle)) {
        const { root } = viteConfig
        const outDir: string = viteConfig.build.outDir || 'dist'
        const fileName: string = file[0]
        const filePath: string = resolve(root, outDir, fileName)

        try {
          let data: string = fs.readFileSync(filePath, {
            encoding: 'utf8',
          })

          if (data.includes(template)) {
            data = data.replace(template, injectCode(css.join('\n')))
          }

          fs.writeFileSync(filePath, data)
        } catch (e) {
          console.error(e)
        }
      }
    },
  }
}

https://github.com/ohbug-org/ohbug-extension-feedback/blob/main/libInjectCss.ts

How to use it? install a npm?

// vite.config.ts
import { defineConfig } from 'vite'
import libInjectCss from './libInjectCss'

export default defineConfig({
  plugins: [
    libInjectCss(),
  ],
})

Here is a sample file: https://github.com/ohbug-org/ohbug-extension-feedback/blob/main/vite.config.ts

@m-nathani
Copy link

i cant really find a sustainable way to make the styles inline in lib mode...
my lib is used in more then 5 place.. and expected to grow.. and i don't want to manually import style file everywhere.. so wanted to include as build itself...

is vite going proposed any solution ? 🤔

@koooge
Copy link

koooge commented Jan 14, 2022

Same here. To me as a vue user, vue-cli's extract: false works fine in lib mode. https://cli.vuejs.org/config/#css-extract
I am wondering if vite does not have the option on purpose. But any solution would be needed.

@ruofee
Copy link

ruofee commented Mar 17, 2022

Excuse me, is there a solution to this problem?

@ruofee
Copy link

ruofee commented Mar 21, 2022

Excuse me, is there a solution to this problem?

I wrote a vite plugin:

https://github.com/ruofee/vue-dynamic-form-component/blob/vite/build/ViteSingleCssPlugin.js

@wal0x
Copy link

wal0x commented Mar 2, 2023

Both plugins shared above did not work for me... and I am still looking for a solution...

@emosheeep
Copy link
Contributor

emosheeep commented Mar 25, 2023

Both plugins shared above did not work for me... and I am still looking for a solution...

You can check adjacent 2 comments at #1579 (comment)

@marcelobotega
Copy link

Is this feature still relevant for Vite ?
If yes, I can try to develop it.

@mrswylet
Copy link

Hi. I'm developing a UI kit. At the moment I use vue-cli and I can't switch to vite because of this problem.

marcelobotega added a commit to marcelobotega/vite that referenced this issue Jun 20, 2023
@marcelobotega marcelobotega linked a pull request Jun 20, 2023 that will close this issue
9 tasks
@marcelobotega
Copy link

marcelobotega commented Jun 27, 2023

Hey, I created a PR for this #13565.
Feel free to look into it.

@tibineagu
Copy link

@marcelobotega thank you for building this! It will be very helpful!

Do you know when #13565 might get merged?

@marcelobotega
Copy link

Hey @tibineagu, sadly I do not have the time to update the PR right now (could not find a way to make the legacy work with multiple entries), but we ended using this solution on our library and worked better than the cssExtract option.
I'll leave the vite.config file here in case anyone want to check it out.

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { visualizer } from 'rollup-plugin-visualizer';
import { PluginPure } from 'rollup-plugin-pure';
import { createRequire } from 'module';
import { libInjectCss } from 'vite-plugin-lib-inject-css';
import { extname, relative } from 'path';
import { fileURLToPath } from 'node:url';
import { glob } from 'glob';

const require = createRequire(import.meta.url);
const pkg = require('./package.json');
const external = Object.keys(pkg.dependencies);

export default defineConfig({
  optimizeDeps: {
    exclude: ['vue-demi'],
  },
  plugins: [
    vue(),
    libInjectCss(),
    PluginPure({
      functions: ['defineComponent'],
      sourcemap: true,
    }),
    {
      ...visualizer({ template: 'sunburst', sourcemap: true }),
      apply: 'build',
    },
  ],
  outDir: './dist',
  build: {
    cssCodeSplit: true,
    sourcemap: true,
    lib: {
      entry: ['main.js'],
      formats: ['es'],
    },
    rollupOptions: {
      // https://rollupjs.org/configuration-options/#input
      input: Object.fromEntries(
        glob
          .sync('src/**/*.{js,ts,vue}', {
            ignore: {
              ignored: p => /\.spec.(js|ts)$/.test(p.name),
            },
          })
          .map(file => [
            relative('src', file.slice(0, file.length - extname(file).length)),
            fileURLToPath(new URL(file, import.meta.url)),
          ])
      ),
      output: {
        assetFileNames: '[name][extname]',
        entryFileNames: '[name].mjs',
      },
      external: [...external.filter(dependency => !dependency.includes('vue-demi')), 'vue'],
    },
  },
});

@marcelobotega
Copy link

And shout out to @emosheeep for making the plugin, it works like a charm together with multiple entries option from rollup.

@emosheeep
Copy link
Contributor

And shout out to @emosheeep for making the plugin, it works like a charm together with multiple entries option from rollup.

Thanks so much,I still remember the day in last year when I was developing on my vue component library with multi entries in vite. I was driven into a corner, so I had to dive into the source code and found something useful. It's really a simple plugin that only contains 100 lines, I'm glad to see it helping so many guys🎉🎉🎉.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment