From: Evan You Date: Thu, 23 Mar 2023 08:30:42 +0000 (+0800) Subject: refactor: remove circular dependencies in compiler X-Git-Tag: v3.3.0-alpha.5~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aac163110facbf7374cf335c3d01cd4c5dd26c8d;p=thirdparty%2Fvuejs%2Fcore.git refactor: remove circular dependencies in compiler --- diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 457a905763..515083c336 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -7,11 +7,14 @@ import { OPEN_BLOCK, FRAGMENT, WITH_DIRECTIVES, - WITH_MEMO + WITH_MEMO, + CREATE_VNODE, + CREATE_ELEMENT_VNODE, + CREATE_BLOCK, + CREATE_ELEMENT_BLOCK } from './runtimeHelpers' import { PropsExpression } from './transforms/transformElement' import { ImportItem, TransformContext } from './transform' -import { getVNodeBlockHelper, getVNodeHelper } from './utils' // Vue template is a platform-agnostic superset of HTML (syntax only). // More namespaces like SVG and MathML are declared by platform specific @@ -810,3 +813,23 @@ export function createReturnStatement( loc: locStub } } + +export function getVNodeHelper(ssr: boolean, isComponent: boolean) { + return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE +} + +export function getVNodeBlockHelper(ssr: boolean, isComponent: boolean) { + return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK +} + +export function convertToBlock( + node: VNodeCall, + { helper, removeHelper, inSSR }: TransformContext +) { + if (!node.isBlock) { + node.isBlock = true + removeHelper(getVNodeHelper(inSSR, node.isComponent)) + helper(OPEN_BLOCK) + helper(getVNodeBlockHelper(inSSR, node.isComponent)) + } +} diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index dce5873773..e8da0a4cb4 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -24,14 +24,14 @@ import { AssignmentExpression, ReturnStatement, VNodeCall, - SequenceExpression + SequenceExpression, + getVNodeBlockHelper, + getVNodeHelper } from './ast' import { SourceMapGenerator, RawSourceMap } from 'source-map' import { advancePositionWithMutation, assert, - getVNodeBlockHelper, - getVNodeHelper, isSimpleIdentifier, toValidAssetId } from './utils' diff --git a/packages/compiler-core/src/compat/transformFilter.ts b/packages/compiler-core/src/compat/transformFilter.ts index 8eaa81eb3f..77331db4d8 100644 --- a/packages/compiler-core/src/compat/transformFilter.ts +++ b/packages/compiler-core/src/compat/transformFilter.ts @@ -1,19 +1,18 @@ import { RESOLVE_FILTER } from '../runtimeHelpers' import { + ExpressionNode, AttributeNode, DirectiveNode, - NodeTransform, NodeTypes, - SimpleExpressionNode, - toValidAssetId, - TransformContext -} from '@vue/compiler-core' + SimpleExpressionNode +} from '../ast' import { CompilerDeprecationTypes, isCompatEnabled, warnDeprecation } from './compatConfig' -import { ExpressionNode } from '../ast' +import { NodeTransform, TransformContext } from '../transform' +import { toValidAssetId } from '../utils' const validDivisionCharRE = /[\w).+\-_$\]]/ diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 3cb3e6bf01..4d9e8c6ed4 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -17,7 +17,8 @@ import { TemplateLiteral, createVNodeCall, ConstantTypes, - ArrayExpression + ArrayExpression, + convertToBlock } from './ast' import { isString, @@ -36,7 +37,7 @@ import { helperNameMap, CREATE_COMMENT } from './runtimeHelpers' -import { isVSlot, makeBlock } from './utils' +import { isVSlot } from './utils' import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic' import { CompilerCompatOptions } from './compat/compatConfig' @@ -348,7 +349,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) { // SimpleExpressionNode const codegenNode = child.codegenNode if (codegenNode.type === NodeTypes.VNODE_CALL) { - makeBlock(codegenNode, context) + convertToBlock(codegenNode, context) } root.codegenNode = codegenNode } else { diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index b2b7f871c0..5526163c6f 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -12,11 +12,13 @@ import { ParentNode, JSChildNode, CallExpression, - createArrayExpression + createArrayExpression, + getVNodeBlockHelper, + getVNodeHelper } from '../ast' import { TransformContext } from '../transform' import { PatchFlags, isString, isSymbol, isArray } from '@vue/shared' -import { getVNodeBlockHelper, getVNodeHelper, isSlotOutlet } from '../utils' +import { isSlotOutlet } from '../utils' import { OPEN_BLOCK, GUARD_REACTIVE_PROPS, diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index e72c64c1a5..a44532c0d5 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -26,7 +26,9 @@ import { ForIteratorExpression, ConstantTypes, createBlockStatement, - createCompoundExpression + createCompoundExpression, + getVNodeBlockHelper, + getVNodeHelper } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { @@ -35,8 +37,6 @@ import { isTemplateNode, isSlotOutlet, injectProp, - getVNodeBlockHelper, - getVNodeHelper, findDir } from '../utils' import { diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index 636d9dff67..4e1673561c 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -23,7 +23,8 @@ import { locStub, CacheExpression, ConstantTypes, - MemoExpression + MemoExpression, + convertToBlock } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { processExpression } from './transformExpression' @@ -34,10 +35,9 @@ import { findDir, findProp, isBuiltInType, - makeBlock + getMemoedVNodeCall } from '../utils' import { PatchFlags, PatchFlagNames } from '@vue/shared' -import { getMemoedVNodeCall } from '..' export const transformIf = createStructuralDirectiveTransform( /^(if|else|else-if)$/, @@ -301,7 +301,7 @@ function createChildrenCodegenNode( const vnodeCall = getMemoedVNodeCall(ret) // Change createVNode to createBlock. if (vnodeCall.type === NodeTypes.VNODE_CALL) { - makeBlock(vnodeCall, context) + convertToBlock(vnodeCall, context) } // inject branch key injectProp(vnodeCall, keyProperty, context) diff --git a/packages/compiler-core/src/transforms/vMemo.ts b/packages/compiler-core/src/transforms/vMemo.ts index 4e150875d9..c14f6ceb86 100644 --- a/packages/compiler-core/src/transforms/vMemo.ts +++ b/packages/compiler-core/src/transforms/vMemo.ts @@ -1,6 +1,7 @@ import { NodeTransform } from '../transform' -import { findDir, makeBlock } from '../utils' +import { findDir } from '../utils' import { + convertToBlock, createCallExpression, createFunctionExpression, ElementTypes, @@ -26,7 +27,7 @@ export const transformMemo: NodeTransform = (node, context) => { if (codegenNode && codegenNode.type === NodeTypes.VNODE_CALL) { // non-component sub tree should be turned into a block if (node.tagType !== ElementTypes.COMPONENT) { - makeBlock(codegenNode, context) + convertToBlock(codegenNode, context) } node.codegenNode = createCallExpression(context.helper(WITH_MEMO), [ dir.exp!, diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index 6a6b03a2ea..bd2882b09e 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -35,12 +35,7 @@ import { TO_HANDLERS, NORMALIZE_PROPS, GUARD_REACTIVE_PROPS, - CREATE_BLOCK, - CREATE_ELEMENT_BLOCK, - CREATE_VNODE, - CREATE_ELEMENT_VNODE, - WITH_MEMO, - OPEN_BLOCK + WITH_MEMO } from './runtimeHelpers' import { isString, isObject, hyphenate, extend, NOOP } from '@vue/shared' import { PropsExpression } from './transforms/transformElement' @@ -331,14 +326,6 @@ export function isSlotOutlet( return node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT } -export function getVNodeHelper(ssr: boolean, isComponent: boolean) { - return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE -} - -export function getVNodeBlockHelper(ssr: boolean, isComponent: boolean) { - return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK -} - const propsHelperSet = new Set([NORMALIZE_PROPS, GUARD_REACTIVE_PROPS]) function getUnnormalizedProps( @@ -532,15 +519,3 @@ export function getMemoedVNodeCall(node: BlockCodegenNode | MemoExpression) { return node } } - -export function makeBlock( - node: VNodeCall, - { helper, removeHelper, inSSR }: TransformContext -) { - if (!node.isBlock) { - node.isBlock = true - removeHelper(getVNodeHelper(inSSR, node.isComponent)) - helper(OPEN_BLOCK) - helper(getVNodeBlockHelper(inSSR, node.isComponent)) - } -} diff --git a/packages/shared/src/general.ts b/packages/shared/src/general.ts new file mode 100644 index 0000000000..6efaf52524 --- /dev/null +++ b/packages/shared/src/general.ts @@ -0,0 +1,183 @@ +import { makeMap } from './makeMap' + +export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__ + ? Object.freeze({}) + : {} +export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : [] + +export const NOOP = () => {} + +/** + * Always return false. + */ +export const NO = () => false + +const onRE = /^on[^a-z]/ +export const isOn = (key: string) => onRE.test(key) + +export const isModelListener = (key: string) => key.startsWith('onUpdate:') + +export const extend = Object.assign + +export const remove = (arr: T[], el: T) => { + const i = arr.indexOf(el) + if (i > -1) { + arr.splice(i, 1) + } +} + +const hasOwnProperty = Object.prototype.hasOwnProperty +export const hasOwn = ( + val: object, + key: string | symbol +): key is keyof typeof val => hasOwnProperty.call(val, key) + +export const isArray = Array.isArray +export const isMap = (val: unknown): val is Map => + toTypeString(val) === '[object Map]' +export const isSet = (val: unknown): val is Set => + toTypeString(val) === '[object Set]' + +export const isDate = (val: unknown): val is Date => + toTypeString(val) === '[object Date]' +export const isRegExp = (val: unknown): val is RegExp => + toTypeString(val) === '[object RegExp]' +export const isFunction = (val: unknown): val is Function => + typeof val === 'function' +export const isString = (val: unknown): val is string => typeof val === 'string' +export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol' +export const isObject = (val: unknown): val is Record => + val !== null && typeof val === 'object' + +export const isPromise = (val: unknown): val is Promise => { + return isObject(val) && isFunction(val.then) && isFunction(val.catch) +} + +export const objectToString = Object.prototype.toString +export const toTypeString = (value: unknown): string => + objectToString.call(value) + +export const toRawType = (value: unknown): string => { + // extract "RawType" from strings like "[object RawType]" + return toTypeString(value).slice(8, -1) +} + +export const isPlainObject = (val: unknown): val is object => + toTypeString(val) === '[object Object]' + +export const isIntegerKey = (key: unknown) => + isString(key) && + key !== 'NaN' && + key[0] !== '-' && + '' + parseInt(key, 10) === key + +export const isReservedProp = /*#__PURE__*/ makeMap( + // the leading comma is intentional so empty string "" is also included + ',key,ref,ref_for,ref_key,' + + 'onVnodeBeforeMount,onVnodeMounted,' + + 'onVnodeBeforeUpdate,onVnodeUpdated,' + + 'onVnodeBeforeUnmount,onVnodeUnmounted' +) + +export const isBuiltInDirective = /*#__PURE__*/ makeMap( + 'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo' +) + +const cacheStringFunction = string>(fn: T): T => { + const cache: Record = Object.create(null) + return ((str: string) => { + const hit = cache[str] + return hit || (cache[str] = fn(str)) + }) as T +} + +const camelizeRE = /-(\w)/g +/** + * @private + */ +export const camelize = cacheStringFunction((str: string): string => { + return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')) +}) + +const hyphenateRE = /\B([A-Z])/g +/** + * @private + */ +export const hyphenate = cacheStringFunction((str: string) => + str.replace(hyphenateRE, '-$1').toLowerCase() +) + +/** + * @private + */ +export const capitalize = cacheStringFunction( + (str: string) => str.charAt(0).toUpperCase() + str.slice(1) +) + +/** + * @private + */ +export const toHandlerKey = cacheStringFunction((str: string) => + str ? `on${capitalize(str)}` : `` +) + +// compare whether a value has changed, accounting for NaN. +export const hasChanged = (value: any, oldValue: any): boolean => + !Object.is(value, oldValue) + +export const invokeArrayFns = (fns: Function[], arg?: any) => { + for (let i = 0; i < fns.length; i++) { + fns[i](arg) + } +} + +export const def = (obj: object, key: string | symbol, value: any) => { + Object.defineProperty(obj, key, { + configurable: true, + enumerable: false, + value + }) +} + +/** + * "123-foo" will be parsed to 123 + * This is used for the .number modifier in v-model + */ +export const looseToNumber = (val: any): any => { + const n = parseFloat(val) + return isNaN(n) ? val : n +} + +/** + * Only conerces number-like strings + * "123-foo" will be returned as-is + */ +export const toNumber = (val: any): any => { + const n = isString(val) ? Number(val) : NaN + return isNaN(n) ? val : n +} + +let _globalThis: any +export const getGlobalThis = (): any => { + return ( + _globalThis || + (_globalThis = + typeof globalThis !== 'undefined' + ? globalThis + : typeof self !== 'undefined' + ? self + : typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}) + ) +} + +const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/ + +export function genPropsAccessExp(name: string) { + return identRE.test(name) + ? `__props.${name}` + : `__props[${JSON.stringify(name)}]` +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index e3fcd86627..2e7292f0ea 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,6 +1,5 @@ -import { makeMap } from './makeMap' - -export { makeMap } +export { makeMap } from './makeMap' +export * from './general' export * from './patchFlags' export * from './shapeFlags' export * from './slotFlags' @@ -13,185 +12,3 @@ export * from './escapeHtml' export * from './looseEqual' export * from './toDisplayString' export * from './typeUtils' - -export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__ - ? Object.freeze({}) - : {} -export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : [] - -export const NOOP = () => {} - -/** - * Always return false. - */ -export const NO = () => false - -const onRE = /^on[^a-z]/ -export const isOn = (key: string) => onRE.test(key) - -export const isModelListener = (key: string) => key.startsWith('onUpdate:') - -export const extend = Object.assign - -export const remove = (arr: T[], el: T) => { - const i = arr.indexOf(el) - if (i > -1) { - arr.splice(i, 1) - } -} - -const hasOwnProperty = Object.prototype.hasOwnProperty -export const hasOwn = ( - val: object, - key: string | symbol -): key is keyof typeof val => hasOwnProperty.call(val, key) - -export const isArray = Array.isArray -export const isMap = (val: unknown): val is Map => - toTypeString(val) === '[object Map]' -export const isSet = (val: unknown): val is Set => - toTypeString(val) === '[object Set]' - -export const isDate = (val: unknown): val is Date => - toTypeString(val) === '[object Date]' -export const isRegExp = (val: unknown): val is RegExp => - toTypeString(val) === '[object RegExp]' -export const isFunction = (val: unknown): val is Function => - typeof val === 'function' -export const isString = (val: unknown): val is string => typeof val === 'string' -export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol' -export const isObject = (val: unknown): val is Record => - val !== null && typeof val === 'object' - -export const isPromise = (val: unknown): val is Promise => { - return isObject(val) && isFunction(val.then) && isFunction(val.catch) -} - -export const objectToString = Object.prototype.toString -export const toTypeString = (value: unknown): string => - objectToString.call(value) - -export const toRawType = (value: unknown): string => { - // extract "RawType" from strings like "[object RawType]" - return toTypeString(value).slice(8, -1) -} - -export const isPlainObject = (val: unknown): val is object => - toTypeString(val) === '[object Object]' - -export const isIntegerKey = (key: unknown) => - isString(key) && - key !== 'NaN' && - key[0] !== '-' && - '' + parseInt(key, 10) === key - -export const isReservedProp = /*#__PURE__*/ makeMap( - // the leading comma is intentional so empty string "" is also included - ',key,ref,ref_for,ref_key,' + - 'onVnodeBeforeMount,onVnodeMounted,' + - 'onVnodeBeforeUpdate,onVnodeUpdated,' + - 'onVnodeBeforeUnmount,onVnodeUnmounted' -) - -export const isBuiltInDirective = /*#__PURE__*/ makeMap( - 'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo' -) - -const cacheStringFunction = string>(fn: T): T => { - const cache: Record = Object.create(null) - return ((str: string) => { - const hit = cache[str] - return hit || (cache[str] = fn(str)) - }) as T -} - -const camelizeRE = /-(\w)/g -/** - * @private - */ -export const camelize = cacheStringFunction((str: string): string => { - return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')) -}) - -const hyphenateRE = /\B([A-Z])/g -/** - * @private - */ -export const hyphenate = cacheStringFunction((str: string) => - str.replace(hyphenateRE, '-$1').toLowerCase() -) - -/** - * @private - */ -export const capitalize = cacheStringFunction( - (str: string) => str.charAt(0).toUpperCase() + str.slice(1) -) - -/** - * @private - */ -export const toHandlerKey = cacheStringFunction((str: string) => - str ? `on${capitalize(str)}` : `` -) - -// compare whether a value has changed, accounting for NaN. -export const hasChanged = (value: any, oldValue: any): boolean => - !Object.is(value, oldValue) - -export const invokeArrayFns = (fns: Function[], arg?: any) => { - for (let i = 0; i < fns.length; i++) { - fns[i](arg) - } -} - -export const def = (obj: object, key: string | symbol, value: any) => { - Object.defineProperty(obj, key, { - configurable: true, - enumerable: false, - value - }) -} - -/** - * "123-foo" will be parsed to 123 - * This is used for the .number modifier in v-model - */ -export const looseToNumber = (val: any): any => { - const n = parseFloat(val) - return isNaN(n) ? val : n -} - -/** - * Only conerces number-like strings - * "123-foo" will be returned as-is - */ -export const toNumber = (val: any): any => { - const n = isString(val) ? Number(val) : NaN - return isNaN(n) ? val : n -} - -let _globalThis: any -export const getGlobalThis = (): any => { - return ( - _globalThis || - (_globalThis = - typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}) - ) -} - -const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/ - -export function genPropsAccessExp(name: string) { - return identRE.test(name) - ? `__props.${name}` - : `__props[${JSON.stringify(name)}]` -} diff --git a/packages/shared/src/looseEqual.ts b/packages/shared/src/looseEqual.ts index 387150535c..db869922fc 100644 --- a/packages/shared/src/looseEqual.ts +++ b/packages/shared/src/looseEqual.ts @@ -1,4 +1,4 @@ -import { isArray, isDate, isObject, isSymbol } from './' +import { isArray, isDate, isObject, isSymbol } from './general' function looseCompareArrays(a: any[], b: any[]) { if (a.length !== b.length) return false diff --git a/packages/shared/src/normalizeProp.ts b/packages/shared/src/normalizeProp.ts index b6f822670f..e6ef62a5c8 100644 --- a/packages/shared/src/normalizeProp.ts +++ b/packages/shared/src/normalizeProp.ts @@ -1,4 +1,4 @@ -import { isArray, isString, isObject, hyphenate } from './' +import { isArray, isString, isObject, hyphenate } from './general' export type NormalizedStyle = Record diff --git a/packages/shared/src/toDisplayString.ts b/packages/shared/src/toDisplayString.ts index efe3b7cc0e..7f5818d949 100644 --- a/packages/shared/src/toDisplayString.ts +++ b/packages/shared/src/toDisplayString.ts @@ -7,7 +7,7 @@ import { isSet, objectToString, isString -} from './index' +} from './general' /** * For converting {{ interpolation }} values to displayed strings.