diff --git a/packages/vite/src/modes/global/build.ts b/packages/vite/src/modes/global/build.ts index fec5907bc8..5d022f4598 100644 --- a/packages/vite/src/modes/global/build.ts +++ b/packages/vite/src/modes/global/build.ts @@ -1,5 +1,5 @@ import { resolve } from 'path' -import type { Plugin } from 'vite' +import type { Plugin, ResolvedConfig } from 'vite' import type { GenerateResult, UnocssPluginContext } from '@unocss/core' import { HASH_PLACEHOLDER_RE, LAYER_MARK_ALL, LAYER_PLACEHOLDER_RE, @@ -18,6 +18,7 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, filter, get const vfsLayers = new Set() const layerImporterMap = new Map() let tasks: Promise[] = [] + let viteConfig: ResolvedConfig // use maps to differentiate multiple build. using outDir as key const cssPostPlugins = new Map() @@ -143,14 +144,17 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, filter, get apply(options, { command }) { return command === 'build' && !options.build?.ssr }, + configResolved(config) { + viteConfig = config + }, enforce: 'post', // rewrite the css placeholders async generateBundle(options, bundle) { - const files = Object.entries(bundle) - const cssFiles = files.filter(i => i[0].endsWith('.css')) - const jsFiles = files.filter(i => i[0].endsWith('.js')) + const checkJs = ['umd', 'amd', 'iife'].includes(options.format) + const files = Object.keys(bundle) + .filter(i => i.endsWith('.css') || (checkJs && i.endsWith('.js'))) - if (!cssFiles.length && !jsFiles.length) + if (!files.length) return if (!vfsLayers.size) { @@ -162,23 +166,20 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, filter, get const result = await generateAll() let replaced = false - for (const [, chunk] of cssFiles) { + for (const file of files) { + const chunk = bundle[file] + if (chunk.type === 'asset' && typeof chunk.source === 'string') { const css = chunk.source .replace(HASH_PLACEHOLDER_RE, '') chunk.source = await replaceAsync(css, LAYER_PLACEHOLDER_RE, async (_, __, layer) => { replaced = true - const css = layer === LAYER_MARK_ALL + return await applyCssTransform(layer === LAYER_MARK_ALL ? result.getLayers(undefined, Array.from(vfsLayers)) - : result.getLayer(layer) || '' - return await applyCssTransform(css, `${chunk.fileName}.css`, options.dir) + : result.getLayer(layer) || '', `${chunk.fileName}.css`, options.dir) }) } - } - - // replace the hash in the js files (iife or umd bundle) - for (const [, chunk] of jsFiles) { - if (chunk.type === 'chunk' && typeof chunk.code === 'string') { + else if (chunk.type === 'chunk' && typeof chunk.code === 'string') { const js = chunk.code .replace(HASH_PLACEHOLDER_RE, '') chunk.code = await replaceAsync(js, LAYER_PLACEHOLDER_RE, async (_, __, layer) => { @@ -193,8 +194,14 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, filter, get } } - if (!replaced) - this.error(new Error('[unocss] does not found CSS placeholder in the generated chunks,\nthis is likely an internal bug of unocss vite plugin')) + if (!replaced) { + let msg = '[unocss] does not found CSS placeholder in the generated chunks' + if (viteConfig.build.lib && checkJs) + msg += '\nIt seems you are building in library mode, it\'s recommanded to set `build.cssCodeSplit` to true.\nSee https://github.com/vitejs/vite/issues/1579' + else + msg += '\nThis is likely an internal bug of unocss vite plugin' + this.error(new Error(msg)) + } }, }, ] diff --git a/test/vite.test.ts b/test/fixtures.test.ts similarity index 53% rename from test/vite.test.ts rename to test/fixtures.test.ts index f0bebf819c..546c970975 100644 --- a/test/vite.test.ts +++ b/test/fixtures.test.ts @@ -4,8 +4,8 @@ import { describe, expect, it } from 'vitest' import fs from 'fs-extra' import fg from 'fast-glob' -describe('vite', () => { - it('build', async () => { +describe.concurrent('fixtures', () => { + it('vite client', async () => { const root = resolve(__dirname, 'fixtures/vite') await fs.emptyDir(join(root, 'dist')) await build({ @@ -41,4 +41,40 @@ describe('vite', () => { // transformer-compile-class expect(js).contains('uno-tacwqa') }) + + it('vite lib', async () => { + const root = resolve(__dirname, 'fixtures/vite-lib') + await fs.emptyDir(join(root, 'dist')) + await build({ + root, + logLevel: 'warn', + build: { + sourcemap: true, + }, + }) + + const files = await fg('dist/**/*.{umd,iife}.js', { cwd: root, absolute: true }) + + expect(files).toHaveLength(2) + + for (const path of files) { + const code = await fs.readFile(path, 'utf-8') + // basic + expect(code).contains('.text-red') + // transformer-variant-group + expect(code).contains('.text-sm') + // transformer-compile-class + expect(code).contains('.uno-tacwqa') + // transformer-directives + expect(code).not.contains('@apply') + expect(code).not.contains('--at-apply') + expect(code).contains('gap:.25rem') + expect(code).contains('gap:.5rem') + + // transformer-variant-group + expect(code).contains('text-sm') + // transformer-compile-class + expect(code).contains('uno-tacwqa') + } + }) }) diff --git a/test/fixtures/vite-lib/index.html b/test/fixtures/vite-lib/index.html new file mode 100644 index 0000000000..07f7d3c896 --- /dev/null +++ b/test/fixtures/vite-lib/index.html @@ -0,0 +1,12 @@ + + + + + + + + +
+ + + diff --git a/test/fixtures/vite-lib/lib/main.css b/test/fixtures/vite-lib/lib/main.css new file mode 100644 index 0000000000..a12547aa62 --- /dev/null +++ b/test/fixtures/vite-lib/lib/main.css @@ -0,0 +1,8 @@ +.foo { + @apply flex; + @apply gap-1; +} + +.bar { + --at-apply: gap-2; +} diff --git a/test/fixtures/vite-lib/lib/main.ts b/test/fixtures/vite-lib/lib/main.ts new file mode 100644 index 0000000000..6f89528653 --- /dev/null +++ b/test/fixtures/vite-lib/lib/main.ts @@ -0,0 +1,19 @@ +import '@unocss/reset/tailwind.css' +import './main.css' +import 'uno.css' + +// @unocss-include +const html = String.raw +document.querySelector('#app')!.innerHTML = html` +
+

+ Red +

+

+ Green Large +

+

+ Blue Small +

+
+` diff --git a/test/fixtures/vite-lib/package.json b/test/fixtures/vite-lib/package.json new file mode 100644 index 0000000000..48e43bb1c3 --- /dev/null +++ b/test/fixtures/vite-lib/package.json @@ -0,0 +1,8 @@ +{ + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build" + } +} diff --git a/test/fixtures/vite-lib/unocss.config.ts b/test/fixtures/vite-lib/unocss.config.ts new file mode 100644 index 0000000000..efc9d1f153 --- /dev/null +++ b/test/fixtures/vite-lib/unocss.config.ts @@ -0,0 +1,22 @@ +import { + defineConfig, + presetAttributify, + presetIcons, + presetUno, + transformerCompileClass, + transformerDirectives, + transformerVariantGroup, +} from 'unocss' + +export default defineConfig({ + presets: [ + presetAttributify(), + presetUno(), + presetIcons(), + ], + transformers: [ + transformerCompileClass(), + transformerVariantGroup(), + transformerDirectives(), + ], +}) diff --git a/test/fixtures/vite-lib/vite.config.ts b/test/fixtures/vite-lib/vite.config.ts new file mode 100644 index 0000000000..96b05fc5b2 --- /dev/null +++ b/test/fixtures/vite-lib/vite.config.ts @@ -0,0 +1,23 @@ +import { resolve } from 'path' +import { defineConfig } from 'vite' +import UnoCSS from '@unocss/vite' + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, 'lib/main.ts'), + name: 'MyLib', + fileName: 'my-lib', + formats: [ + 'cjs', + 'es', + 'iife', + 'umd', + ], + }, + cssCodeSplit: true, + }, + plugins: [ + UnoCSS(), + ], +})