diff --git a/package.json b/package.json index 6cbba72d5..e9eb52433 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "node": ">=10.0.0" }, "devDependencies": { + "@ls-lint/ls-lint": "^1.8.0", "@microsoft/api-extractor": "^7.3.9", "@rollup/plugin-commonjs": "^11.0.2", "@rollup/plugin-json": "^4.0.0", @@ -62,6 +63,8 @@ "prettier": "~1.14.0", "puppeteer": "^2.0.0", "rollup": "^2.2.0", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-node-globals": "^1.4.0", "rollup-plugin-terser": "^5.3.0", "rollup-plugin-typescript2": "^0.27.0", "semver": "^6.3.0", @@ -69,7 +72,6 @@ "ts-jest": "^25.2.1", "tsd": "^0.11.0", "typescript": "^3.8.3", - "yorkie": "^2.0.0", - "@ls-lint/ls-lint": "^1.8.0" + "yorkie": "^2.0.0" } } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index e4076bd57..a9836530e 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -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) } diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index 3555e9a3d..e2c5dd1e5 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -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]/ diff --git a/packages/compiler-sfc/README.md b/packages/compiler-sfc/README.md index 4cffa17fe..4fc4ffa10 100644 --- a/packages/compiler-sfc/README.md +++ b/packages/compiler-sfc/README.md @@ -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 diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index 0a7f35a73..882bff8ef 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -8,10 +8,14 @@ "dist" ], "buildOptions": { - "prod": false, + "name": "VueCompilerSFC", "formats": [ - "cjs" - ] + "cjs", + "global", + "esm-browser" + ], + "prod": false, + "enableNonBrowserBranches": true }, "repository": { "type": "git", diff --git a/packages/compiler-sfc/src/compileStyle.ts b/packages/compiler-sfc/src/compileStyle.ts index c0a510c0b..ded5f0bf6 100644 --- a/packages/compiler-sfc/src/compileStyle.ts +++ b/packages/compiler-sfc/src/compileStyle.ts @@ -94,20 +94,24 @@ export function doCompileStyle( } let cssModules: Record | undefined if (modules) { - if (options.isAsync) { - plugins.push( - require('postcss-modules')({ - ...modulesOptions, - getJSON: (cssFileName: string, json: Record) => { - 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) => { + 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, diff --git a/packages/compiler-sfc/src/compileTemplate.ts b/packages/compiler-sfc/src/compileTemplate.ts index 13bbd23f3..2c50c7f3e 100644 --- a/packages/compiler-sfc/src/compileTemplate.ts +++ b/packages/compiler-sfc/src/compileTemplate.ts @@ -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 { diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts index d59e131a8..04310acbf 100644 --- a/packages/compiler-sfc/src/parse.ts +++ b/packages/compiler-sfc/src/parse.ts @@ -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(SFC_CACHE_MAX_SIZE) +const sourceToSFC = + __GLOBAL__ || __ESM_BROWSER__ + ? new Map() + : (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 = diff --git a/packages/template-explorer/index.html b/packages/template-explorer/index.html index b6421521f..81dc39146 100644 --- a/packages/template-explorer/index.html +++ b/packages/template-explorer/index.html @@ -6,15 +6,8 @@
- - -