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

feat: support css @import hmr #281

Merged
merged 3 commits into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
refactor: apply postcss-import in compileCss + remove precprocessor @…
…import tests
  • Loading branch information
yyx990803 committed May 29, 2020
commit e111c601759e74cb13d6900d6da99ad1a6d4a786
6 changes: 3 additions & 3 deletions playground/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<TestPostCss />
<TestScopedCss />
<TestCssModules />
<TestCssImportBoundary/>
<TestCssAtImport/>
<TestPreprocessors />
<TestAssets />
<TestSrcImport />
Expand Down Expand Up @@ -43,7 +43,7 @@ import TestJsx from './TestJsx.vue'
import TestAlias from './TestAlias.vue'
import TestTransform from './TestTransform.vue'
import TestRewriteOptimized from "./rewrite-optimized/TestRewriteOptimized.vue";
import TestCssImportBoundary from './css-@import/testCssImportBoundary.vue'
import TestCssAtImport from './css-@import/TestCssAtImport.vue'

export default {
data: () => ({
Expand All @@ -58,7 +58,7 @@ export default {
TestScopedCss,
TestCssModules,
TestPreprocessors,
TestCssImportBoundary,
TestCssAtImport,
TestSrcImport,
TestAssets,
TestJsonImport,
Expand Down
19 changes: 19 additions & 0 deletions playground/css-@import/TestCssAtImport.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<h2>CSS @import</h2>
<div class="sfc-style-at-import">
&lt;style @import &gt; this should be red
</div>
<div class="script-at-import">
&lt;script @import &gt; this should be green
</div>
</template>

<style scoped>
@import './testCssAtImportFromStyle.css';
</style>

<script>
import './testCssAtImportFromScript.css'

export default {}
</script>
3 changes: 3 additions & 0 deletions playground/css-@import/imported.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.script-at-import {
color: green;
}
1 change: 1 addition & 0 deletions playground/css-@import/testCssAtImportFromScript.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import './imported.css'
3 changes: 3 additions & 0 deletions playground/css-@import/testCssAtImportFromStyle.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.sfc-style-at-import {
color: red;
}
3 changes: 0 additions & 3 deletions playground/css-@import/testCssImportBoundary.css

This file was deleted.

5 changes: 0 additions & 5 deletions playground/css-@import/testCssImportBoundary.module.scss

This file was deleted.

1 change: 0 additions & 1 deletion playground/css-@import/testCssImportBoundary.scss

This file was deleted.

31 changes: 0 additions & 31 deletions playground/css-@import/testCssImportBoundary.vue

This file was deleted.

11 changes: 2 additions & 9 deletions src/node/build/buildPluginCss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
urlRE,
compileCss,
cssPreprocessLangRE,
rewriteCssUrls,
parseCssImport
rewriteCssUrls
} from '../utils/cssUtils'
import { SFCStyleCompileResults } from '@vue/compiler-sfc'

Expand All @@ -32,16 +31,10 @@ export const createBuildCssPlugin = (
name: 'vite:css',
async transform(css: string, id: string) {
if (id.endsWith('.css') || cssPreprocessLangRE.test(id)) {
const res = await parseCssImport(css, id)
if (typeof res === 'string') {
css = res
} else {
css = res.css
}
const result = await compileCss(root, id, {
id: '',
source: css,
filename: path.basename(id),
filename: id,
scoped: false,
modules: id.endsWith('.module.css'),
preprocessLang: id.replace(cssPreprocessLangRE, '$2') as any
Expand Down
16 changes: 2 additions & 14 deletions src/node/server/serverPluginCss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
cssImportMap,
cssPreprocessLangRE,
getCssImportBoundaries,
parseCssImport,
recordCssImportPlain,
rewriteCssUrls
} from '../utils/cssUtils'
import qs from 'querystring'
Expand Down Expand Up @@ -78,7 +76,7 @@ export const cssPlugin: ServerPlugin = ({ root, app, watcher, resolver }) => {
return
}
// handle HMR for module.css
// it cannot process with normal css, the class which in module.css maybe removed
// it cannot be handled as normal css because the js exports may change
if (filePath.endsWith('.module.css')) {
moduleCssUpdate(filePath)
return
Expand Down Expand Up @@ -130,17 +128,7 @@ export const cssPlugin: ServerPlugin = ({ root, app, watcher, resolver }) => {
}

async function processCss(root: string, ctx: Context) {
let css = (await readBody(ctx.body))!
// should parser import before compile
const res = await parseCssImport(css, resolver.requestToFile(ctx.path))

if (typeof res === 'string') {
css = res
} else {
css = res.css
recordCssImportPlain(res.messages)
}

const css = (await readBody(ctx.body))!
const result = await compileCss(root, ctx.path, {
id: '',
source: css,
Expand Down
24 changes: 3 additions & 21 deletions src/node/server/serverPluginVue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@ import { Context } from 'koa'
import { transform } from '../esbuildService'
import { InternalResolver } from '../resolver'
import { seenUrls } from './serverPluginServeStatic'
import {
codegenCss,
compileCss,
parseCssImport,
recordCssImportPlain,
rewriteCssUrls
} from '../utils/cssUtils'
import { codegenCss, compileCss, rewriteCssUrls } from '../utils/cssUtils'
import { parse } from '../utils/babelParse'
import MagicString from 'magic-string'
import { resolveImport } from './serverPluginModuleRewrite'
Expand Down Expand Up @@ -520,21 +514,9 @@ async function compileSFCStyle(
const start = Date.now()

const { generateCodeFrame } = resolveCompiler(root)

let css = style.content
// should parser css import before compile
const res = await parseCssImport(css, `${filePath}?type=style&index=${index}`)

if (typeof res === 'string') {
css = res
} else {
css = res.css
recordCssImportPlain(res.messages)
}

const result = (await compileCss(root, publicPath, {
source: css,
filename: filePath,
source: style.content,
filename: filePath + `?type=style&index=${index}`,
id: ``, // will be computed in compileCss
scoped: style.scoped != null,
modules: style.module != null,
Expand Down
73 changes: 37 additions & 36 deletions src/node/utils/cssUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
SFCStyleCompileResults
} from '@vue/compiler-sfc'
import { hmrClientPublicPath } from '../server/serverPluginHmr'
import postcss, { Result, ResultMessage } from 'postcss'
import cssImport from 'postcss-import'

export const urlRE = /(url\(\s*['"]?)([^"')]+)(["']?\s*\))/
export const cssPreprocessLangRE = /(.+).(less|sass|scss|styl|stylus)$/

Expand Down Expand Up @@ -55,15 +54,24 @@ export async function compileCss(
}: SFCAsyncStyleCompileOptions
): Promise<SFCStyleCompileResults | string> {
const id = hash_sum(publicPath)
const postcssConfig = await loadPostcssConfig(root)
let postcssConfig = await loadPostcssConfig(root)
const { compileStyleAsync } = resolveCompiler(root)

if (publicPath.endsWith('.css') && !modules && !postcssConfig) {
if (
publicPath.endsWith('.css') &&
!modules &&
!postcssConfig &&
!source.includes('@import')
) {
// no need to invoke compile for plain css if no postcss config is present
return source
}

return await compileStyleAsync({
const postcssOptions = postcssConfig && postcssConfig.options
const postcssPlugins = postcssConfig ? postcssConfig.plugins : []
postcssPlugins.push(require('postcss-import')())

const res = await compileStyleAsync({
source,
filename,
id: `data-v-${id}`,
Expand All @@ -72,18 +80,32 @@ export async function compileCss(
modulesOptions: {
generateScopedName: `[local]_${id}`
},

preprocessLang: preprocessLang,
preprocessCustomRequire: (id: string) => require(resolveFrom(root, id)),
...(postcssConfig
? {
postcssOptions: postcssConfig.options,
postcssPlugins: postcssConfig.plugins
}
: {}),
preprocessOptions: {
includePaths: ['node_modules']
}
},

postcssOptions,
postcssPlugins
})

// record css import dependencies
if (res.rawResult) {
res.rawResult.messages.forEach((msg) => {
let { type, file, parent } = msg
if (type === 'dependency') {
if (cssImportMap.has(file)) {
cssImportMap.get(file)!.add(parent)
} else {
cssImportMap.set(file, new Set([parent]))
}
}
})
}

return res
}

export function codegenCss(
Expand Down Expand Up @@ -133,34 +155,13 @@ export const cssImportMap = new Map<
Set<string /*filePath*/>
>()

export async function parseCssImport(
css: string,
filePath: string
): Promise<string | Result> {
if (!css.includes('@import')) {
return css
}
return await postcss().use(cssImport()).process(css, { from: filePath })
}

export function recordCssImportPlain(messages: ResultMessage[]) {
messages.forEach((msg) => {
let { type, file, parent } = msg
if (type === 'dependency') {
if (cssImportMap.has(file)) {
cssImportMap.get(file)!.add(parent)
} else {
cssImportMap.set(file, new Set([parent]))
}
}
})
}

export function getCssImportBoundaries(
filePath: string,
boundaries = new Set<string>()
) {
if (!cssImportMap.has(filePath)) return boundaries
if (!cssImportMap.has(filePath)) {
return boundaries
}
const importers = cssImportMap.get(filePath)!
for (const importer of importers) {
boundaries.add(importer)
Expand Down
56 changes: 23 additions & 33 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,39 +184,6 @@ describe('vite', () => {
})
}

test('SFC normal css w/ @import', async () => {
const el = await page.$('.style-at-import')
expect(await getComputedColor(el)).toBe('rgb(255, 0, 0)')
if (!isBuild) {
await updateFile('css-@import/testCssImportBoundary.css', (content) =>
content.replace('red', 'rgb(0, 0, 0)')
)
await expectByPolling(() => getComputedColor(el), 'rgb(0, 0, 0)')
}
})

test('SFC preprocessor w/ @import', async () => {
const el = await page.$('.style-at-import-scss')
expect(await getComputedColor(el)).toBe('rgb(255, 0, 0)')
if (!isBuild) {
await updateFile('css-@import/testCssImportBoundary.scss', (content) =>
content.replace('red', 'rgb(0, 0, 0)')
)
await expectByPolling(() => getComputedColor(el), 'rgb(0, 0, 0)')
}
})

// test('SFC <style module> w/ @import', async () => {
// const el = await page.$('#css-modules-at-import-sfc')
// expect(await getComputedColor(el)).toBe('rgb(255, 0, 0)')
// if (!isBuild) {
// await updateFile('css-@import/testCssImportBoundary.scss', (content) =>
// content.replace('red', 'rgb(0, 0, 0)')
// )
// await expectByPolling(() => getComputedColor(el), 'rgb(0, 0, 0)')
// }
// })

test('CSS import w/ PostCSS', async () => {
const el = await page.$('.postcss-from-css')
expect(await getComputedColor(el)).toBe('rgb(255, 0, 0)')
Expand Down Expand Up @@ -267,6 +234,29 @@ describe('vite', () => {
}
})

test('CSS @import', async () => {
const el = await page.$('.script-at-import')
expect(await getComputedColor(el)).toBe('rgb(0, 128, 0)')
if (!isBuild) {
await updateFile('css-@import/imported.css', (content) =>
content.replace('green', 'rgb(0, 0, 0)')
)
await expectByPolling(() => getComputedColor(el), 'rgb(0, 0, 0)')
}
})

test('SFC <style> w/ @import', async () => {
const el = await page.$('.sfc-style-at-import')
expect(await getComputedColor(el)).toBe('rgb(255, 0, 0)')
if (!isBuild) {
await updateFile(
'css-@import/testCssAtImportFromStyle.css',
(content) => content.replace('red', 'rgb(0, 0, 0)')
)
await expectByPolling(() => getComputedColor(el), 'rgb(0, 0, 0)')
}
})

test('import *.module.css', async () => {
const el = await page.$('.css-modules-import')
expect(await getComputedColor(el)).toBe('rgb(255, 140, 0)')
Expand Down