Skip to content

Commit

Permalink
fix(plugin-vue): make cssm code tree shakeable (#6353)
Browse files Browse the repository at this point in the history
  • Loading branch information
fnlctrl authored Jan 21, 2022
1 parent 6e98ff2 commit 3fb4118
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 12 deletions.
7 changes: 7 additions & 0 deletions packages/playground/vue-lib/__tests__/serve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @ts-check
// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
// the default e2e test serve behavior

exports.serve = async function serve() {
// do nothing, skip default behavior
}
24 changes: 24 additions & 0 deletions packages/playground/vue-lib/__tests__/vue-lib.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { build } from 'vite'
import path from 'path'
import type { OutputChunk, RollupOutput } from 'rollup'

describe('vue component library', () => {
test('should output tree shakeable css module code', async () => {
// Build lib
await build({
logLevel: 'silent',
configFile: path.resolve(__dirname, '../vite.config.lib.ts')
})
// Build app
const { output } = (await build({
logLevel: 'silent',
configFile: path.resolve(__dirname, '../vite.config.consumer.ts')
})) as RollupOutput
const { code } = output.find(
(e) => e.type === 'chunk' && e.isEntry
) as OutputChunk
// Unused css module should be treeshaked
expect(code).toContain('styleA') // styleA is used by CompA
expect(code).not.toContain('styleB') // styleB is not used
})
})
6 changes: 6 additions & 0 deletions packages/playground/vue-lib/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<html>
<body>
<div id="app"></div>
<script type="module" src="./src-consumer/index.ts"></script>
</body>
</html>
16 changes: 16 additions & 0 deletions packages/playground/vue-lib/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "test-vue-lib",
"private": true,
"version": "0.0.0",
"scripts": {
"dev-consumer": "vite --config ./vite.config.consumer.ts",
"build-lib": "vite build --config ./vite.config.lib.ts",
"build-consumer": "vite build --config ./vite.config.consumer.ts"
},
"dependencies": {
"vue": "^3.2.25"
},
"devDependencies": {
"@vitejs/plugin-vue": "workspace:*"
}
}
8 changes: 8 additions & 0 deletions packages/playground/vue-lib/src-consumer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @ts-ignore
/* eslint-disable node/no-missing-import */
import { CompA } from '../dist/lib/my-vue-lib.es'
import '../dist/lib/style.css'
import { createApp } from 'vue'

const app = createApp(CompA)
app.mount('#app')
8 changes: 8 additions & 0 deletions packages/playground/vue-lib/src-lib/CompA.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<div :class="$style.styleA">CompA</div>
</template>
<style module>
.styleA {
color: red;
}
</style>
8 changes: 8 additions & 0 deletions packages/playground/vue-lib/src-lib/CompB.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<div :class="$style.styleB">CompB</div>
</template>
<style module>
.styleB {
color: blue;
}
</style>
2 changes: 2 additions & 0 deletions packages/playground/vue-lib/src-lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as CompA } from './CompA.vue'
export { default as CompB } from './CompB.vue'
10 changes: 10 additions & 0 deletions packages/playground/vue-lib/vite.config.consumer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
root: __dirname,
build: {
outDir: 'dist/consumer'
},
plugins: [vue()]
})
23 changes: 23 additions & 0 deletions packages/playground/vue-lib/vite.config.lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import path from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
root: __dirname,
build: {
outDir: 'dist/lib',
lib: {
entry: path.resolve(__dirname, 'src-lib/index.ts'),
name: 'MyVueLib',
formats: ['es'],
fileName: (format) => `my-vue-lib.${format}.js`
},
rollupOptions: {
external: ['vue'],
output: {
globals: { vue: 'Vue' }
}
}
},
plugins: [vue()]
})
34 changes: 22 additions & 12 deletions packages/plugin-vue/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ async function genStyleCode(
attachedProps: [string, string][]
) {
let stylesCode = ``
let hasCSSModules = false
let cssModulesMap: Record<string, string> | undefined
if (descriptor.styles.length) {
for (let i = 0; i < descriptor.styles.length; i++) {
const style = descriptor.styles[i]
Expand All @@ -320,12 +320,13 @@ async function genStyleCode(
`<style module> is not supported in custom elements mode.`
)
}
if (!hasCSSModules) {
stylesCode += `\nconst cssModules = {}`
attachedProps.push([`__cssModules`, `cssModules`])
hasCSSModules = true
}
stylesCode += genCSSModulesCode(i, styleRequest, style.module)
const [importCode, nameMap] = genCSSModulesCode(
i,
styleRequest,
style.module
)
stylesCode += importCode
Object.assign((cssModulesMap ||= {}), nameMap)
} else {
if (asCustomElement) {
stylesCode += `\nimport _style_${i} from ${JSON.stringify(
Expand All @@ -344,22 +345,31 @@ async function genStyleCode(
])
}
}
if (cssModulesMap) {
const mappingCode =
Object.entries(cssModulesMap).reduce(
(code, [key, value]) => code + `"${key}":${value},\n`,
'{\n'
) + '}'
stylesCode += `\nconst cssModules = ${mappingCode}`
attachedProps.push([`__cssModules`, `cssModules`])
}
return stylesCode
}

function genCSSModulesCode(
index: number,
request: string,
moduleName: string | boolean
): string {
): [importCode: string, nameMap: Record<string, string>] {
const styleVar = `style${index}`
const exposedName = typeof moduleName === 'string' ? moduleName : '$style'
// inject `.module` before extension so vite handles it as css module
const moduleRequest = request.replace(/\.(\w+)$/, '.module.$1')
return (
`\nimport ${styleVar} from ${JSON.stringify(moduleRequest)}` +
`\ncssModules["${exposedName}"] = ${styleVar}`
)
return [
`\nimport ${styleVar} from ${JSON.stringify(moduleRequest)}`,
{ [exposedName]: styleVar }
]
}

async function genCustomBlockCode(
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3fb4118

Please sign in to comment.