Skip to content

Commit

Permalink
feat(compat): remove generatorBundle hook order. (#20)
Browse files Browse the repository at this point in the history
* feat: remove excute order

* feat: should dynamic

* test: update unit test case

* feat: capture error and ingored

* chore: supplementary note

* chore: remove peerDependencies
  • Loading branch information
nonzzz committed Feb 5, 2023
1 parent db64bc1 commit 6ea16e4
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 206 deletions.
10 changes: 10 additions & 0 deletions __tests__/plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import test from 'ava'
import path from 'path'
import zlib, { BrotliOptions } from 'zlib'
import fs from 'fs'
import fsp from 'fs/promises'
import { build } from 'vite'
import { compression } from '../src'
Expand Down Expand Up @@ -174,3 +175,12 @@ test('dynamic', async (t) => {
const compressed = len(r.filter((s) => s.endsWith('.gz')))
t.is(compressed, 4)
})

test('dynamic diff', async (t) => {
const ids = await Promise.all([mockBuild({ deleteOriginalAssets: true }, 'dynamic'), mockBuild({}, 'dynamic')])
await sleep(3000)
const [diff1, diff2] = await Promise.all(ids.map((id) => readAll(path.join(dist, id))))
const diff1Js = diff1.filter((v) => v.endsWith('.js.gz')).map((v) => zlib.unzipSync(fs.readFileSync(v)))
const diff2Js = diff2.filter((v) => v.endsWith('.js')).map((v) => fs.readFileSync(v))
t.deepEqual(diff1Js, diff2Js)
})
1 change: 1 addition & 0 deletions example/client/src/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<DynamicComponent />
<fe-spacer :x="2" :y="2" />
<fe-button type="success" auto size="mini">Action</fe-button>
<text />
</div>
</template>

Expand Down
3 changes: 3 additions & 0 deletions example/client/src/dynamic.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
p {
font-size: 30px;
}
2 changes: 2 additions & 0 deletions example/client/src/dynamic.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import('./dynamic.css')

export const d = 'dynamic'
4 changes: 4 additions & 0 deletions example/client/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import App from './app.vue'
import '@fect-ui/themes'
import '@fect-ui/vue/dist/cjs/main.css'

import { seq } from './seq'

import('./dynamic').then((re) => console.log(re.d))

createApp(App).mount('#app')

console.log(seq)
1 change: 1 addition & 0 deletions example/client/src/seq.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const seq = 1
10 changes: 5 additions & 5 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "example",
"scripts": {
"dev":"vite",
"build":"vite build",
"preview":"node ./server/app.js"
"dev": "vite",
"build": "vite build",
"preview": "node ./server/app.js"
},
"devDependencies": {
"vite": "^3.2.3",
"vite": "3.0.9",
"@vitejs/plugin-vue": "^3.1.2",
"vite-plugin-compression2":"file:../"
"vite-plugin-compression2": "file:../"
},
"dependencies": {
"@fect-ui/vue": "^1.6.1",
Expand Down
9 changes: 4 additions & 5 deletions example/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { compression } from 'vite-plugin-compression2'
export default defineConfig({
plugins: [
vue(),
process.env.NODE_ENV === 'production' &&
compression({
include: [/\.(js)$/, /\.(css)$/],
deleteOriginalAssets: true
})
compression({
include: [/\.(js)$/, /\.(css)$/],
deleteOriginalAssets: true
})
]
})
299 changes: 151 additions & 148 deletions example/yarn.lock

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@
"dependencies": {
"@rollup/pluginutils": "^5.0.2"
},
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0"
},
"ava": {
"files": [
"__tests__/*.spec.ts"
Expand Down
105 changes: 60 additions & 45 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path'
import { createFilter } from '@rollup/pluginutils'
import { len, replaceFileName, slash } from './utils'
import { defaultCompressionOptions, ensureAlgorithm, transfer } from './compress'
import type { Plugin } from 'vite'
import type { Plugin, ChunkMetadata } from 'vite'
import type {
Algorithm,
AlgorithmFunction,
Expand All @@ -15,6 +15,15 @@ import type {
UserCompressionOptions
} from './interface'

const VITE_INTERNAL_CHUNK_META = 'viteMetadata'

type HandleCompressInvork = ([file, meta]: [string, CompressMetaInfo]) => Promise<void>

async function handleCompress(tasks: Map<string, CompressMetaInfo>, invork: HandleCompressInvork) {
if (!tasks.size) return
await Promise.all(Array.from(tasks.entries()).map(invork))
}

function compression(): Plugin
function compression<A extends Algorithm>(opts: ViteCompressionPluginConfigAlgorithm<A>): Plugin
function compression<T = UserCompressionOptions>(opts: ViteCompressionPluginConfigFunction<T>): Plugin
Expand Down Expand Up @@ -55,60 +64,66 @@ function compression<T, A extends Algorithm>(opts: ViteCompressionPluginConfig<T
configResolved(config) {
zlib.dest = config.build.outDir
},
// We need specify the order of the hook
// Becasue in vite's intenral logic it will
// trigger _preload logic. So that if we run the hook
// before vite's importAnalysisBuild function it will
// cause loose all of link map.
// Please see: https://github.com/vitejs/vite/blob/HEAD/packages/vite/src/node/plugins/index.ts#L94-L98
generateBundle: {
order: 'post',
async handler(_, bundles) {
for (const fileName in bundles) {
if (!filter(fileName)) continue
const bundle = bundles[fileName]
const result = bundle.type == 'asset' ? bundle.source : bundle.code
const size = len(result)
if (size < threshold) continue
const effect = bundle.type === 'chunk' && !!len(bundle.dynamicImports)
const meta: CompressMetaInfo = Object.create(null)
meta.effect = effect
if (meta.effect) {
meta.file = slash(path.join(zlib.dest, fileName))
}
schedule.set(fileName, meta)
}
try {
if (!schedule.size) return
await Promise.all(
Array.from(schedule.entries()).map(async ([file, meta]) => {
if (meta.effect) return
const bundle = bundles[file]
const source = bundle.type === 'asset' ? bundle.source : bundle.code
const compressed = await transfer(Buffer.from(source), zlib.algorithm, zlib.options)
const fileName = replaceFileName(file, zlib.filename)
this.emitFile({ type: 'asset', source: compressed, fileName })
if (deleteOriginalAssets) Reflect.deleteProperty(bundles, file)
schedule.delete(file)
// Unfortunately. Vite support using object as hooks to change execution order need at least 3.1.0
// So we should record that with side Effect bundle file. (Because file with dynamic import will trigger vite's internal importAnalysisBuild logic and it will generator vite's placeholder.)
// Vite importAnalysisBuild source code: https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/importAnalysisBuild.ts
// Vite's plugin order see: https://github.com/vitejs/vite/blob/HEAD/packages/vite/src/node/plugins/index.ts#L94-L98
async generateBundle(_, bundles) {
for (const fileName in bundles) {
if (!filter(fileName)) continue
const bundle = bundles[fileName]
const result = bundle.type == 'asset' ? bundle.source : bundle.code
const size = len(result)
if (size < threshold) continue
const effect = bundle.type === 'chunk' && !!len(bundle.dynamicImports)
const meta: CompressMetaInfo = Object.create(null)
meta.effect = effect
if (meta.effect) {
meta.file = slash(path.join(zlib.dest, fileName))
if (VITE_INTERNAL_CHUNK_META in bundle) {
// This is a hack logic. We get all bundle file reference relation. And record them with effect.
// Some case like virtual module we should ignored them.
const { importedAssets, importedCss } = bundle[VITE_INTERNAL_CHUNK_META] as ChunkMetadata
// @ts-ignored
const imports = [...importedAssets, ...importedCss, ...bundle.dynamicImports]
imports.forEach((importer) => {
importer in bundles &&
schedule.set(importer, { effect: true, file: slash(path.join(zlib.dest, importer)) })
})
)
} catch (error) {
this.error(error)
}
}
if (!schedule.has(fileName) && bundle) schedule.set(fileName, meta)
}
try {
await handleCompress(schedule, async ([file, meta]) => {
if (meta.effect) return
const bundle = bundles[file]
const source = bundle.type === 'asset' ? bundle.source : bundle.code
const compressed = await transfer(Buffer.from(source), zlib.algorithm, zlib.options)
const fileName = replaceFileName(file, zlib.filename)
this.emitFile({ type: 'asset', source: compressed, fileName })
if (deleteOriginalAssets) Reflect.deleteProperty(bundles, file)
schedule.delete(file)
})
} catch (error) {
this.error(error)
}
},
async closeBundle() {
if (!schedule.size) return
await Promise.all(
Array.from(schedule.entries()).map(async ([file, meta]) => {
if (!meta.effect) return
await handleCompress(schedule, async ([file, meta]) => {
if (!meta.effect) return
try {
const buf = await fsp.readFile(meta.file)
const compressed = await transfer(buf, zlib.algorithm, zlib.options)
const fileName = replaceFileName(file, zlib.filename)
await fsp.writeFile(path.join(zlib.dest, fileName), compressed)
if (deleteOriginalAssets) await fsp.rm(meta.file, { recursive: true, force: true })
})
)
} catch {
// issue #18
// In somecase. Like vuepress it will called vite build with `Promise.all`. But it's concurrency. when we record the
// file fd. It had been changed. So that we should catch the error
}
})
schedule.clear()
}
}
Expand Down

0 comments on commit 6ea16e4

Please sign in to comment.