Skip to content

Commit

Permalink
fix: use more robust export default replacement for SFC scripts
Browse files Browse the repository at this point in the history
fix #271
  • Loading branch information
yyx990803 committed May 26, 2020
1 parent e67b698 commit 2e81e64
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 18 deletions.
17 changes: 2 additions & 15 deletions src/node/server/serverPluginHmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { vueCache, srcImportMap } from './serverPluginVue'
import { resolveImport } from './serverPluginModuleRewrite'
import { FSWatcher } from 'chokidar'
import MagicString from 'magic-string'
import { parse } from '@babel/parser'
import { parse } from '../utils/babelParse'
import { Node, StringLiteral, Statement, Expression } from '@babel/types'
import { InternalResolver } from '../resolver'
import LRUCache from 'lru-cache'
Expand Down Expand Up @@ -406,20 +406,7 @@ export function rewriteFileWithHMR(
}
}

const ast = parse(source, {
sourceType: 'module',
plugins: [
// required for import.meta.hot
'importMeta',
// by default we enable proposals slated for ES2020.
// full list at https://babeljs.io/docs/en/next/babel-parser#plugins
// this should be kept in async with @vue/compiler-core's support range
'bigInt',
'optionalChaining',
'nullishCoalescingOperator'
]
}).program.body

const ast = parse(source)
ast.forEach((s) => checkStatements(s, true, false))

// inject import.meta.hot
Expand Down
31 changes: 28 additions & 3 deletions src/node/server/serverPluginVue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { transform } from '../esbuildService'
import { InternalResolver } from '../resolver'
import { seenUrls } from './serverPluginServeStatic'
import { codegenCss, compileCss, rewriteCssUrls } from '../utils/cssUtils'
import { parse } from '../utils/babelParse'
import MagicString from 'magic-string'

const debug = require('debug')('vite:sfc')
const getEtag = require('etag')
Expand Down Expand Up @@ -340,6 +342,8 @@ export async function parseSFC(
return descriptor
}

const defaultExportRE = /((?:\n|;)\s*)export default/

async function compileSFCMain(
descriptor: SFCDescriptor,
filePath: string,
Expand All @@ -351,21 +355,30 @@ async function compileSFCMain(
}

const id = hash_sum(publicPath)
let code = `\nimport { updateStyle } from "${hmrClientPublicPath}"\n`
let code = ``
if (descriptor.script) {
let content = descriptor.script.content
if (descriptor.script.lang === 'ts') {
// TODO merge lang=ts source map
content = (await transform(content, publicPath, { loader: 'ts' })).code
}

code += content.replace(`export default`, 'const __script =')
// rewrite export default.
// fast path: simple regex replacement to avoid full-blown babel parse.
let replaced = content.replace(defaultExportRE, '$1const __script =')
// if the script somehow still contains `default export`, it probably has
// multi-line comments or template strings. fallback to a full parse.
if (defaultExportRE.test(replaced)) {
replaced = rewriteDefaultExport(content)
}
code += replaced
} else {
code += `const __script = {}`
}

let hasScoped = false
let hasCSSModules = false
if (descriptor.styles) {
code += `\nimport { updateStyle } from "${hmrClientPublicPath}"\n`
descriptor.styles.forEach((s, i) => {
const styleRequest = publicPath + `?type=style&index=${i}`
if (s.scoped) hasScoped = true
Expand Down Expand Up @@ -545,3 +558,15 @@ async function compileSFCStyle(
debug(`${publicPath} style compiled in ${Date.now() - start}ms`)
return result
}

function rewriteDefaultExport(code: string): string {
const s = new MagicString(code)
const ast = parse(code)
ast.forEach((node) => {
if (node.type === 'ExportDefaultDeclaration') {
s.overwrite(node.start!, node.declaration.start!, `const __script = `)
}
})
const ret = s.toString()
return ret
}
18 changes: 18 additions & 0 deletions src/node/utils/babelParse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { parse as _parse } from '@babel/parser'
import { Statement } from '@babel/types'

export function parse(source: string): Statement[] {
return _parse(source, {
sourceType: 'module',
plugins: [
// required for import.meta.hot
'importMeta',
// by default we enable proposals slated for ES2020.
// full list at https://babeljs.io/docs/en/next/babel-parser#plugins
// this should be kept in async with @vue/compiler-core's support range
'bigInt',
'optionalChaining',
'nullishCoalescingOperator'
]
}).program.body
}

0 comments on commit 2e81e64

Please sign in to comment.