Skip to content
This repository has been archived by the owner on Jan 18, 2022. It is now read-only.

Commit

Permalink
refactor: handle script compilation separately
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Nov 20, 2020
1 parent 746acc3 commit facf4c6
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 71 deletions.
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createCustomBlockFilter } from './utils/customBlockFilter'
import { getDescriptor, setDescriptor } from './utils/descriptorCache'
import { parseVuePartRequest } from './utils/query'
import { normalizeSourceMap } from './utils/sourceMap'
import { getResolvedScript } from './script'

const debug = createDebugger('rollup-plugin-vue')

Expand Down Expand Up @@ -113,7 +114,7 @@ export default function PluginVue(userOptions: Partial<Options> = {}): Plugin {
query.type === 'template'
? descriptor.template!
: query.type === 'script'
? descriptor.scriptCompiled || descriptor.script
? getResolvedScript(descriptor, isServer)
: query.type === 'style'
? descriptor.styles[query.index]
: typeof query.index === 'number'
Expand Down
59 changes: 59 additions & 0 deletions src/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { compileScript, SFCDescriptor, SFCScriptBlock } from '@vue/compiler-sfc'
import { Options } from '.'
import { getTemplateCompilerOptions } from './template'

// since we generate different output based on whether the template is inlined
// or not, we need to cache the results separately
const inlinedCache = new WeakMap<SFCDescriptor, SFCScriptBlock | null>()
const normalCache = new WeakMap<SFCDescriptor, SFCScriptBlock | null>()

export function getResolvedScript(
descriptor: SFCDescriptor,
isServer: boolean
): SFCScriptBlock | null | undefined {
const cacheToUse = isServer ? normalCache : inlinedCache
return cacheToUse.get(descriptor)
}

export function resolveScript(
descriptor: SFCDescriptor,
scopeId: string,
isProd: boolean,
isServer: boolean,
options: Options
) {
if (!descriptor.script && !descriptor.scriptSetup) {
return null
}

const cached = getResolvedScript(descriptor, isServer)
if (cached) {
return cached
}

let resolved: SFCScriptBlock | null

if (compileScript) {
resolved = compileScript(descriptor, {
id: scopeId,
isProd,
inlineTemplate: !isServer,
templateOptions: getTemplateCompilerOptions(options, descriptor, scopeId),
})
} else if (descriptor.scriptSetup) {
throw new Error(
`<script setup> is not supported by the installed version of ` +
`@vue/compiler-sfc - please upgrade.`
)
} else {
resolved = descriptor.script
}

if (isServer) {
normalCache.set(descriptor, resolved)
} else {
inlinedCache.set(descriptor, resolved)
}

return resolved
}
87 changes: 28 additions & 59 deletions src/sfc.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import hash from 'hash-sum'
import path from 'path'
import qs from 'querystring'
import {
compileScript,
parse,
SFCBlock,
SFCDescriptor,
SFCTemplateCompileOptions,
} from '@vue/compiler-sfc'
import { parse, SFCBlock, SFCDescriptor } from '@vue/compiler-sfc'
import { Options } from '.'
import { getTemplateCompilerOptions } from './template'
import { setDescriptor } from './utils/descriptorCache'
import { TransformPluginContext } from 'rollup'
import { createRollupError } from './utils/error'
import { resolveScript } from './script'

export function transformSFCEntry(
code: string,
resourcePath: string,
filename: string,
options: Options,
sourceRoot: string,
isProduction: boolean,
Expand All @@ -26,20 +20,20 @@ export function transformSFCEntry(
) {
const { descriptor, errors } = parse(code, {
sourceMap: true,
filename: resourcePath,
filename,
sourceRoot,
})
setDescriptor(resourcePath, descriptor)
setDescriptor(filename, descriptor)

if (errors.length) {
errors.forEach((error) =>
pluginContext.error(createRollupError(resourcePath, error))
pluginContext.error(createRollupError(filename, error))
)
return null
}

const shortFilePath = path
.relative(sourceRoot, resourcePath)
.relative(sourceRoot, filename)
.replace(/^(\.\.[\/\\])+/, '')
.replace(/\\/g, '/')
const scopeId = hash(
Expand All @@ -54,7 +48,7 @@ export function transformSFCEntry(
(isServer || !descriptor.scriptSetup)

const templateImport = hasTemplateImport
? genTemplateCode(descriptor, resourcePath, scopeId, isServer)
? genTemplateCode(descriptor, scopeId, isServer)
: ''

const renderReplace = hasTemplateImport
Expand All @@ -65,23 +59,13 @@ export function transformSFCEntry(

const scriptImport = genScriptCode(
descriptor,
resourcePath,
scopeId,
isProduction,
isServer,
getTemplateCompilerOptions(options, descriptor, scopeId)
)
const stylesCode = genStyleCode(
descriptor,
resourcePath,
scopeId,
options.preprocessStyles
)
const customBlocksCode = getCustomBlock(
descriptor,
resourcePath,
filterCustomBlock
options
)
const stylesCode = genStyleCode(descriptor, scopeId, options.preprocessStyles)
const customBlocksCode = getCustomBlock(descriptor, filterCustomBlock)
const output = [
scriptImport,
templateImport,
Expand Down Expand Up @@ -110,15 +94,14 @@ export function transformSFCEntry(

function genTemplateCode(
descriptor: SFCDescriptor,
resourcePath: string,
id: string,
isServer: boolean
) {
const renderFnName = isServer ? 'ssrRender' : 'render'
let templateImport = `const ${renderFnName} = () => {}`
let templateRequest
if (descriptor.template) {
const src = descriptor.template.src || resourcePath
const src = descriptor.template.src || descriptor.filename
const idQuery = `&id=${id}`
const srcQuery = descriptor.template.src ? `&src` : ``
const attrsQuery = attrsToQuery(descriptor.template.attrs, 'js', true)
Expand All @@ -132,48 +115,35 @@ function genTemplateCode(

function genScriptCode(
descriptor: SFCDescriptor,
resourcePath: string,
id: string,
scopeId: string,
isProd: boolean,
isServer: boolean,
templateOptions?: Partial<SFCTemplateCompileOptions>
options: Options
) {
let scriptImport = `const script = {}`
if (descriptor.script || descriptor.scriptSetup) {
if (compileScript) {
descriptor.scriptCompiled = compileScript(descriptor, {
id,
isProd,
inlineTemplate: !isServer,
templateOptions,
})
}
const script = descriptor.scriptCompiled || descriptor.script
if (script) {
const src = script.src || resourcePath
const attrsQuery = attrsToQuery(script.attrs, 'js')
const srcQuery = script.src ? `&src` : ``
const query = `?vue&type=script${srcQuery}${attrsQuery}`
const scriptRequest = JSON.stringify(src + query)
scriptImport =
`import script from ${scriptRequest}\n` +
`export * from ${scriptRequest}` // support named exports
}
const script = resolveScript(descriptor, scopeId, isProd, isServer, options)
if (script) {
const src = script.src || descriptor.filename
const attrsQuery = attrsToQuery(script.attrs, 'js')
const srcQuery = script.src ? `&src` : ``
const query = `?vue&type=script${srcQuery}${attrsQuery}`
const scriptRequest = JSON.stringify(src + query)
scriptImport =
`import script from ${scriptRequest}\n` + `export * from ${scriptRequest}` // support named exports
}
return scriptImport
}

function genStyleCode(
descriptor: SFCDescriptor,
resourcePath: string,
id: string,
scopeId: string,
preprocessStyles?: boolean
) {
let stylesCode = ``
let hasCSSModules = false
if (descriptor.styles.length) {
descriptor.styles.forEach((style, i) => {
const src = style.src || resourcePath
const src = style.src || descriptor.filename
// do not include module in default query, since we use it to indicate
// that the module needs to export the modules json
const attrsQuery = attrsToQuery(style.attrs, 'css', preprocessStyles)
Expand All @@ -183,7 +153,7 @@ function genStyleCode(
)
// make sure to only pass id when necessary so that we don't inject
// duplicate tags when multiple components import the same css file
const idQuery = `&id=${id}`
const idQuery = `&id=${scopeId}`
const srcQuery = style.src ? `&src` : ``
const query = `?vue&type=style&index=${i}${srcQuery}${idQuery}`
const styleRequest = src + query + attrsQuery
Expand All @@ -194,7 +164,7 @@ function genStyleCode(
hasCSSModules = true
}
stylesCode += genCSSModulesCode(
id,
scopeId,
i,
styleRequest,
styleRequestWithoutModule,
Expand All @@ -211,14 +181,13 @@ function genStyleCode(

function getCustomBlock(
descriptor: SFCDescriptor,
resourcePath: string,
filter: (type: string) => boolean
) {
let code = ''

descriptor.customBlocks.forEach((block, index) => {
if (filter(block.type)) {
const src = block.src || resourcePath
const src = block.src || descriptor.filename
const attrsQuery = attrsToQuery(block.attrs, block.type)
const srcQuery = block.src ? `&src` : ``
const query = `?vue&type=${block.type}&index=${index}${srcQuery}${attrsQuery}`
Expand Down
4 changes: 2 additions & 2 deletions src/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { normalizeSourceMap } from './utils/sourceMap'

export async function transformStyle(
code: string,
resourcePath: string,
request: string,
options: Options,
query: StyleBlockQuery,
isProduction: boolean,
Expand Down Expand Up @@ -79,7 +79,7 @@ export async function transformStyle(
} else {
return {
code: result.code,
map: normalizeSourceMap(result.map!, resourcePath),
map: normalizeSourceMap(result.map!, request),
}
}
}
10 changes: 5 additions & 5 deletions src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
} from '@vue/compiler-sfc'
import { TransformPluginContext } from 'rollup'
import { Options } from '.'
import { getResolvedScript } from './script'
import { getDescriptor } from './utils/descriptorCache'
import { createRollupError } from './utils/error'
import { TemplateBlockQuery } from './utils/query'
import { normalizeSourceMap } from './utils/sourceMap'

export function transformTemplate(
code: string,
resourcePath: string,
request: string,
options: Options,
query: TemplateBlockQuery,
pluginContext: TransformPluginContext
Expand Down Expand Up @@ -47,7 +48,7 @@ export function transformTemplate(

return {
code: result.code,
map: normalizeSourceMap(result.map!, resourcePath),
map: normalizeSourceMap(result.map!, request),
}
}

Expand All @@ -70,6 +71,7 @@ export function getTemplateCompilerOptions(
preprocessLang &&
options.templatePreprocessOptions &&
options.templatePreprocessOptions[preprocessLang]
const resolvedScript = getResolvedScript(descriptor, isServer)
return {
filename: descriptor.filename,
inMap: block.src ? undefined : block.map,
Expand All @@ -81,9 +83,7 @@ export function getTemplateCompilerOptions(
compilerOptions: {
...options.compilerOptions,
scopeId: hasScoped ? `data-v-${scopeId}` : undefined,
bindingMetadata: descriptor.scriptCompiled
? descriptor.scriptCompiled.bindings
: undefined,
bindingMetadata: resolvedScript ? resolvedScript.bindings : undefined,
ssrCssVars: isServer
? generateCssVars(descriptor, scopeId, isProduction)
: undefined,
Expand Down
8 changes: 4 additions & 4 deletions src/utils/sourceMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { SFCTemplateCompileResults } from '@vue/compiler-sfc'

export function normalizeSourceMap(
map: SFCTemplateCompileResults['map'],
id: string
request: string
): any {
if (!map) return null as any

if (!id.includes('type=script')) {
map.file = id
map.sources[0] = id
if (!request.includes('type=script')) {
map.file = request
map.sources[0] = request
}

return {
Expand Down

1 comment on commit facf4c6

@yyx990803
Copy link
Member Author

@yyx990803 yyx990803 commented on facf4c6 Nov 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failing because the code relies on unreleased typing from @vue/compiler-sfc. Tests are passing locally.

Please sign in to comment.