build: add browser builds for @vue/compiler-sfc

This commit is contained in:
Evan You
2020-04-26 01:24:25 -04:00
parent 0bb1f67d12
commit bd0f7da2c6
13 changed files with 746 additions and 87 deletions

View File

@@ -31,7 +31,6 @@ import {
advancePositionWithMutation,
assert,
isSimpleIdentifier,
loadDep,
toValidAssetId
} from './utils'
import { isString, isArray, isSymbol } from '@vue/shared'
@@ -167,7 +166,7 @@ function createCodegenContext(
if (!__BROWSER__ && sourceMap) {
// lazy require source-map implementation, only in non-browser builds
context.map = new (loadDep('source-map')).SourceMapGenerator()
context.map = new SourceMapGenerator()
context.map!.setSourceContent(filename, context.source)
}

View File

@@ -30,8 +30,9 @@ import {
KEEP_ALIVE,
BASE_TRANSITION
} from './runtimeHelpers'
import { isString, isFunction, isObject, hyphenate } from '@vue/shared'
import { isString, isObject, hyphenate } from '@vue/shared'
import { parse } from '@babel/parser'
import { walk } from 'estree-walker'
import { Node } from '@babel/types'
export const isBuiltInType = (tag: string, expected: string): boolean =>
@@ -49,31 +50,16 @@ export function isCoreComponent(tag: string): symbol | void {
}
}
// cache node requires
// lazy require dependencies so that they don't end up in rollup's dep graph
// and thus can be tree-shaken in browser builds.
let _parse: typeof parse
let _walk: any
export function loadDep(name: string) {
if (!__BROWSER__ && typeof process !== 'undefined' && isFunction(require)) {
return require(name)
} else {
// This is only used when we are building a dev-only build of the compiler
// which runs in the browser but also uses Node deps.
return (window as any)._deps[name]
}
}
export const parseJS: typeof parse = (code, options) => {
assert(
!__BROWSER__,
`Expression AST analysis can only be performed in non-browser builds.`
)
if (!_parse) {
_parse = loadDep('@babel/parser').parse
if (__BROWSER__) {
assert(
!__BROWSER__,
`Expression AST analysis can only be performed in non-browser builds.`
)
return null as any
} else {
return parse(code, options)
}
return _parse(code, options)
}
interface Walker {
@@ -82,12 +68,15 @@ interface Walker {
}
export const walkJS = (ast: Node, walker: Walker) => {
assert(
!__BROWSER__,
`Expression AST analysis can only be performed in non-browser builds.`
)
const walk = _walk || (_walk = loadDep('estree-walker').walk)
return walk(ast, walker)
if (__BROWSER__) {
assert(
!__BROWSER__,
`Expression AST analysis can only be performed in non-browser builds.`
)
return null as any
} else {
return (walk as any)(ast, walker)
}
}
const nonIdentifierRE = /^\d|[^\$\w]/

View File

@@ -6,6 +6,10 @@ This package contains lower level utilities that you can use if you are writing
The API surface is intentionally minimal - the goal is to reuse as much as possible while being as flexible as possible.
## Browser Build Usage
This package relies on `postcss`, `postcss-selector-parser` and `postcss-modules`
## API
TODO

View File

@@ -8,10 +8,14 @@
"dist"
],
"buildOptions": {
"prod": false,
"name": "VueCompilerSFC",
"formats": [
"cjs"
]
"cjs",
"global",
"esm-browser"
],
"prod": false,
"enableNonBrowserBranches": true
},
"repository": {
"type": "git",

View File

@@ -94,20 +94,24 @@ export function doCompileStyle(
}
let cssModules: Record<string, string> | undefined
if (modules) {
if (options.isAsync) {
plugins.push(
require('postcss-modules')({
...modulesOptions,
getJSON: (cssFileName: string, json: Record<string, string>) => {
cssModules = json
}
})
)
} else {
if (__GLOBAL__ || __ESM_BROWSER__) {
throw new Error(
'`modules` option can only be used with compileStyleAsync().'
'[@vue/compiler-sfc] `modules` option is not supported in the browser build.'
)
}
if (!options.isAsync) {
throw new Error(
'[@vue/compiler-sfc] `modules` option can only be used with compileStyleAsync().'
)
}
plugins.push(
require('postcss-modules')({
...modulesOptions,
getJSON: (_cssFileName: string, json: Record<string, string>) => {
cssModules = json
}
})
)
}
const postCSSOptions: ProcessOptions = {
@@ -172,6 +176,14 @@ function preprocess(
options: SFCStyleCompileOptions,
preprocessor: StylePreprocessor
): StylePreprocessorResults {
if ((__ESM_BROWSER__ || __GLOBAL__) && !options.preprocessCustomRequire) {
throw new Error(
`[@vue/compiler-sfc] Style preprocessing in the browser build must ` +
`provide the \`preprocessCustomRequire\` option to return the in-browser ` +
`version of the preprocessor.`
)
}
return preprocessor.render(
options.source,
options.map,

View File

@@ -14,6 +14,8 @@ import {
} from './templateTransformAssetUrl'
import { transformSrcset } from './templateTransformSrcset'
import { isObject } from '@vue/shared'
import * as CompilerDOM from '@vue/compiler-dom'
import * as CompilerSSR from '@vue/compiler-ssr'
import consolidate from 'consolidate'
export interface TemplateCompiler {
@@ -38,6 +40,7 @@ export interface SFCTemplateCompileOptions {
compilerOptions?: CompilerOptions
preprocessLang?: string
preprocessOptions?: any
preprocessCustomRequire?: (id: string) => any
transformAssetUrls?: AssetURLOptions | boolean
}
@@ -66,9 +69,25 @@ function preprocess(
export function compileTemplate(
options: SFCTemplateCompileOptions
): SFCTemplateCompileResults {
const { preprocessLang } = options
const preprocessor =
preprocessLang && consolidate[preprocessLang as keyof typeof consolidate]
const { preprocessLang, preprocessCustomRequire } = options
if (
(__ESM_BROWSER__ || __GLOBAL__) &&
preprocessLang &&
!preprocessCustomRequire
) {
throw new Error(
`[@vue/compiler-sfc] Template preprocessing in the browser build must ` +
`provide the \`preprocessCustomRequire\` option to return the in-browser ` +
`version of the preprocessor in the shape of { render(): string }.`
)
}
const preprocessor = preprocessLang
? preprocessCustomRequire
? preprocessCustomRequire(preprocessLang)
: require('consolidate')[preprocessLang as keyof typeof consolidate]
: false
if (preprocessor) {
try {
return doCompileTemplate({
@@ -108,7 +127,7 @@ function doCompileTemplate({
inMap,
source,
ssr = false,
compiler = ssr ? require('@vue/compiler-ssr') : require('@vue/compiler-dom'),
compiler = ssr ? (CompilerSSR as TemplateCompiler) : CompilerDOM,
compilerOptions = {},
transformAssetUrls
}: SFCTemplateCompileOptions): SFCTemplateCompileResults {

View File

@@ -6,9 +6,9 @@ import {
TextModes
} from '@vue/compiler-core'
import { RawSourceMap, SourceMapGenerator } from 'source-map'
import LRUCache from 'lru-cache'
import { generateCodeFrame } from '@vue/shared'
import { TemplateCompiler } from './compileTemplate'
import * as CompilerDOM from '@vue/compiler-dom'
export interface SFCParseOptions {
filename?: string
@@ -57,7 +57,13 @@ export interface SFCParseResult {
}
const SFC_CACHE_MAX_SIZE = 500
const sourceToSFC = new LRUCache<string, SFCParseResult>(SFC_CACHE_MAX_SIZE)
const sourceToSFC =
__GLOBAL__ || __ESM_BROWSER__
? new Map<string, SFCParseResult>()
: (new (require('lru-cache'))(SFC_CACHE_MAX_SIZE) as Map<
string,
SFCParseResult
>)
export function parse(
source: string,
@@ -66,7 +72,7 @@ export function parse(
filename = 'component.vue',
sourceRoot = '',
pad = false,
compiler = require('@vue/compiler-dom')
compiler = CompilerDOM
}: SFCParseOptions = {}
): SFCParseResult {
const sourceKey =

View File

@@ -6,15 +6,8 @@
<div id="source" class="editor"></div>
<div id="output" class="editor"></div>
<script src="https://unpkg.com/estree-walker@0.8.1/dist/estree-walker.umd.js"></script>
<script src="https://unpkg.com/source-map@0.6.1/dist/source-map.js"></script>
<script src="https://unpkg.com/monaco-editor@0.20.0/min/vs/loader.js"></script>
<script>
window._deps = {
'estree-walker': estreeWalker,
'source-map': sourceMap
}
require.config({
paths: {
'vs': 'https://unpkg.com/monaco-editor@0.20.0/min/vs'

View File

@@ -6,16 +6,8 @@
<div id="source" class="editor"></div>
<div id="output" class="editor"></div>
<script src="../../node_modules/estree-walker/dist/estree-walker.umd.js"></script>
<script src="../../node_modules/source-map/dist/source-map.js"></script>
<script src="../../node_modules/monaco-editor/min/vs/loader.js"></script>
<script>
window._deps = {
// @babel/parser is injected by the bundle
'estree-walker': estreeWalker,
'source-map': sourceMap
}
require.config({
paths: {
'vs': '../../node_modules/monaco-editor/min/vs'

View File

@@ -4,9 +4,6 @@ import { compile as ssrCompile } from '@vue/compiler-ssr'
import { compilerOptions, initOptions, ssrMode } from './options'
import { watchEffect } from '@vue/runtime-dom'
import { SourceMapConsumer } from 'source-map'
import { parse } from '@babel/parser'
window._deps['@babel/parser'] = { parse }
declare global {
interface Window {
@@ -57,7 +54,7 @@ window.init = () => {
)
console.log(`AST: `, ast)
lastSuccessfulCode = code + `\n\n// Check the console for the AST`
lastSuccessfulMap = new window._deps['source-map'].SourceMapConsumer(map)
lastSuccessfulMap = new SourceMapConsumer(map!)
lastSuccessfulMap!.computeColumnSpans()
} catch (e) {
lastSuccessfulCode = `/* ERROR: ${