]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: use enum to replace const enum (#9261)
authorZHAO Jin-Xiang <xiaoxiangmoe@gmail.com>
Wed, 29 Nov 2023 04:24:50 +0000 (12:24 +0800)
committerGitHub <noreply@github.com>
Wed, 29 Nov 2023 04:24:50 +0000 (12:24 +0800)
close #1228

41 files changed:
.eslintrc.cjs
package.json
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/compat/compatConfig.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/options.ts
packages/compiler-core/src/tokenizer.ts
packages/compiler-core/src/utils.ts
packages/compiler-dom/src/errors.ts
packages/compiler-dom/src/transforms/stringifyStatic.ts
packages/compiler-sfc/src/style/cssVars.ts
packages/compiler-ssr/src/errors.ts
packages/reactivity/src/constants.ts
packages/reactivity/src/index.ts
packages/reactivity/src/reactive.ts
packages/runtime-core/src/compat/compatConfig.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/componentPublicInstance.ts
packages/runtime-core/src/components/Teleport.ts
packages/runtime-core/src/devtools.ts
packages/runtime-core/src/enums.ts
packages/runtime-core/src/errorHandling.ts
packages/runtime-core/src/hydration.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/renderer.ts
packages/runtime-test/src/nodeOps.ts
packages/shared/src/patchFlags.ts
packages/shared/src/shapeFlags.ts
packages/shared/src/slotFlags.ts
packages/vue/macros.d.ts
pnpm-lock.yaml
rollup.config.js
rollup.dts.config.js
scripts/build.js
scripts/const-enum.js [deleted file]
scripts/dev.js
scripts/inline-enums.js [new file with mode: 0644]
tsconfig.build.json
tsconfig.json

index 70135b1036ed9db2983c1c4a2c452aeb49c42c37..92b5b534293855854fb3c8a9c16611b10b0566dc 100644 (file)
@@ -3,6 +3,15 @@
 const DOMGlobals = ['window', 'document']
 const NodeGlobals = ['module', 'require']
 
+const banConstEnum = {
+  selector: 'TSEnumDeclaration[const=true]',
+  message:
+    'Please use non-const enums. This project automatically inlines enums.'
+}
+
+/**
+ * @type {import('eslint-define-config').ESLintConfig}
+ */
 module.exports = {
   parser: '@typescript-eslint/parser',
   parserOptions: {
@@ -16,6 +25,7 @@ module.exports = {
 
     'no-restricted-syntax': [
       'error',
+      banConstEnum,
       // since we target ES2015 for baseline support, we need to forbid object
       // rest spread usage in destructure as it compiles into a verbose helper.
       'ObjectPattern > RestElement',
@@ -55,7 +65,7 @@ module.exports = {
       files: ['packages/{compiler-sfc,compiler-ssr,server-renderer}/**'],
       rules: {
         'no-restricted-globals': ['error', ...DOMGlobals],
-        'no-restricted-syntax': 'off'
+        'no-restricted-syntax': ['error', banConstEnum]
       }
     },
     // Private package, browser only + no syntax restrictions
@@ -63,7 +73,7 @@ module.exports = {
       files: ['packages/template-explorer/**', 'packages/sfc-playground/**'],
       rules: {
         'no-restricted-globals': ['error', ...NodeGlobals],
-        'no-restricted-syntax': 'off'
+        'no-restricted-syntax': ['error', banConstEnum]
       }
     },
     // JavaScript files
@@ -79,7 +89,7 @@ module.exports = {
       files: ['scripts/**', '*.{js,ts}', 'packages/**/index.js'],
       rules: {
         'no-restricted-globals': 'off',
-        'no-restricted-syntax': 'off'
+        'no-restricted-syntax': ['error', banConstEnum]
       }
     }
   ]
index f7d420ab88f8ef1b0475e908ec5b08ec49c85562..d4e6e7270544234844835b35c0e2faae9707e046 100644 (file)
@@ -66,7 +66,9 @@
     "@rollup/plugin-replace": "^5.0.4",
     "@rollup/plugin-terser": "^0.4.4",
     "@types/hash-sum": "^1.0.2",
+    "@types/minimist": "^1.2.5",
     "@types/node": "^20.10.0",
+    "@types/semver": "^7.5.5",
     "@typescript-eslint/parser": "^6.13.0",
     "@vitest/coverage-istanbul": "^0.34.6",
     "@vue/consolidate": "0.17.3",
@@ -75,6 +77,7 @@
     "esbuild": "^0.19.5",
     "esbuild-plugin-polyfill-node": "^0.3.0",
     "eslint": "^8.54.0",
+    "eslint-define-config": "^1.24.1",
     "eslint-plugin-jest": "^27.6.0",
     "estree-walker": "^2.0.2",
     "execa": "^8.0.1",
index 05c187feec0db82b7f23ec92ceee71e724601c7f..2bc85bf53d8775187b8be5d38843451d20dece9c 100644 (file)
@@ -19,13 +19,13 @@ import { ImportItem, TransformContext } from './transform'
 // More namespaces can be declared by platform specific compilers.
 export type Namespace = number
 
-export const enum Namespaces {
+export enum Namespaces {
   HTML,
   SVG,
   MATH_ML
 }
 
-export const enum NodeTypes {
+export enum NodeTypes {
   ROOT,
   ELEMENT,
   TEXT,
@@ -59,7 +59,7 @@ export const enum NodeTypes {
   JS_RETURN_STATEMENT
 }
 
-export const enum ElementTypes {
+export enum ElementTypes {
   ELEMENT,
   COMPONENT,
   SLOT,
@@ -214,7 +214,7 @@ export interface DirectiveNode extends Node {
  * Higher levels implies lower levels. e.g. a node that can be stringified
  * can always be hoisted and skipped for patch.
  */
-export const enum ConstantTypes {
+export enum ConstantTypes {
   NOT_CONSTANT = 0,
   CAN_SKIP_PATCH,
   CAN_HOIST,
index 9fe2bb249734b71d8fd9bfb314cb7167cf93d9d0..890ef9bfda4211a8ecc58c163d0e1ae40f239a71 100644 (file)
@@ -69,7 +69,7 @@ export interface CodegenResult {
   map?: RawSourceMap
 }
 
-const enum NewlineType {
+enum NewlineType {
   Start = 0,
   End = -1,
   None = -2,
index 1ca59b5b31df8503c2aa4563e5f43387dd51c984..eec0499876d87e8acc5cea31e5188f28026d6392 100644 (file)
@@ -13,7 +13,7 @@ export interface CompilerCompatOptions {
   compatConfig?: CompilerCompatConfig
 }
 
-export const enum CompilerDeprecationTypes {
+export enum CompilerDeprecationTypes {
   COMPILER_IS_ON_ELEMENT = 'COMPILER_IS_ON_ELEMENT',
   COMPILER_V_BIND_SYNC = 'COMPILER_V_BIND_SYNC',
   COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
index dac779dab542eb90362bd51c072f5870cd18dcd6..ac11b7e3d55377efbe6c0a2858c8803a6606ed2c 100644 (file)
@@ -37,7 +37,7 @@ export function createCompilerError<T extends number>(
   return error
 }
 
-export const enum ErrorCodes {
+export enum ErrorCodes {
   // parse errors
   ABRUPT_CLOSING_OF_EMPTY_COMMENT,
   CDATA_IN_HTML_CONTENT,
index f7f9f2c1118c2917bb199a6645fc160f4d3dc372..b0e3835e6a5280d2d7f3c0d55904d126d846b473 100644 (file)
@@ -94,7 +94,7 @@ export type HoistTransform = (
   parent: ParentNode
 ) => void
 
-export const enum BindingTypes {
+export enum BindingTypes {
   /**
    * returned from data()
    */
index ea6d0592e0a804341579491d70e1620fab30e160..861107ef8ba1fa9f431f5d8f72c327a6def601f6 100644 (file)
@@ -38,13 +38,13 @@ import {
   fromCodePoint
 } from 'entities/lib/decode.js'
 
-export const enum ParseMode {
+export enum ParseMode {
   BASE,
   HTML,
   SFC
 }
 
-export const enum CharCodes {
+export enum CharCodes {
   Tab = 0x9, // "\t"
   NewLine = 0xa, // "\n"
   FormFeed = 0xc, // "\f"
@@ -72,7 +72,6 @@ export const enum CharCodes {
   UpperZ = 0x5a, // "Z"
   LowerZ = 0x7a, // "z"
   LowerX = 0x78, // "x"
-  OpeningSquareBracket = 0x5b, // "["
   LowerV = 0x76, // "v"
   Dot = 0x2e, // "."
   Colon = 0x3a, // ":"
@@ -85,7 +84,7 @@ const defaultDelimitersOpen = new Uint8Array([123, 123]) // "{{"
 const defaultDelimitersClose = new Uint8Array([125, 125]) // "}}"
 
 /** All the states the tokenizer can be in. */
-export const enum State {
+export enum State {
   Text = 1,
 
   // interpolation
@@ -820,7 +819,7 @@ export default class Tokenizer {
     }
   }
   private stateBeforeDeclaration(c: number): void {
-    if (c === CharCodes.OpeningSquareBracket) {
+    if (c === CharCodes.LeftSqaure) {
       this.state = State.CDATASequence
       this.sequenceIndex = 0
     } else {
index 0d6e0f5fe149aa2477d7e9a60feb4d98445e3a79..a159d2eedc729a75239c3691f36bf1048c708ecf 100644 (file)
@@ -65,7 +65,7 @@ const nonIdentifierRE = /^\d|[^\$\w]/
 export const isSimpleIdentifier = (name: string): boolean =>
   !nonIdentifierRE.test(name)
 
-const enum MemberExpLexState {
+enum MemberExpLexState {
   inMemberExp,
   inBrackets,
   inParens,
index b519dbdb762221041aadd5351103b0f27e05d91e..f8582c0b6ac11a6646d6c2f09b14ad4c09f77de4 100644 (file)
@@ -20,7 +20,7 @@ export function createDOMCompilerError(
   ) as DOMCompilerError
 }
 
-export const enum DOMErrorCodes {
+export enum DOMErrorCodes {
   X_V_HTML_NO_EXPRESSION = 53 /* ErrorCodes.__EXTEND_POINT__ */,
   X_V_HTML_WITH_CHILDREN,
   X_V_TEXT_NO_EXPRESSION,
@@ -36,7 +36,7 @@ export const enum DOMErrorCodes {
 }
 
 if (__TEST__) {
-  // esbuild cannot infer const enum increments if first value is from another
+  // esbuild cannot infer enum increments if first value is from another
   // file, so we have to manually keep them in sync. this check ensures it
   // errors out if there are collisions.
   if (DOMErrorCodes.X_V_HTML_NO_EXPRESSION < ErrorCodes.__EXTEND_POINT__) {
index 02a7cb031bffe94cebe86bf8a398e1d0cc60c65a..32e5b3c2289ea4f6e5d12fcf48c36eef7d09635e 100644 (file)
@@ -33,7 +33,7 @@ import {
   isBooleanAttr
 } from '@vue/shared'
 
-export const enum StringifyThresholds {
+export enum StringifyThresholds {
   ELEMENT_WITH_BINDING_COUNT = 5,
   NODE_COUNT = 20
 }
index 9fe727bc5dcbed537316c2922d8744491c3a4045..fc14969dea853173dc786d2f1d5ea73bbb788874 100644 (file)
@@ -70,7 +70,7 @@ export function parseCssVars(sfc: SFCDescriptor): string[] {
   return vars
 }
 
-const enum LexerState {
+enum LexerState {
   inParens,
   inSingleQuoteString,
   inDoubleQuoteString
index e8e5a3a541dcdf8f5de0a8f73d0b9598887fc03e..c75a87e2a005f41969784376f5b9b43244009468 100644 (file)
@@ -16,14 +16,14 @@ export function createSSRCompilerError(
   return createCompilerError(code, loc, SSRErrorMessages) as SSRCompilerError
 }
 
-export const enum SSRErrorCodes {
+export enum SSRErrorCodes {
   X_SSR_UNSAFE_ATTR_NAME = 65 /* DOMErrorCodes.__EXTEND_POINT__ */,
   X_SSR_NO_TELEPORT_TARGET,
   X_SSR_INVALID_AST_NODE
 }
 
 if (__TEST__) {
-  // esbuild cannot infer const enum increments if first value is from another
+  // esbuild cannot infer enum increments if first value is from another
   // file, so we have to manually keep them in sync. this check ensures it
   // errors out if there are collisions.
   if (SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME < DOMErrorCodes.__EXTEND_POINT__) {
index 4ad2ec3c7da6472ca2003d224a34c74f4084ef03..f87c62604a89e43fdd86ba236a8dd4bdafd93113 100644 (file)
@@ -1,20 +1,20 @@
 // using literal strings instead of numbers so that it's easier to inspect
 // debugger events
 
-export const enum TrackOpTypes {
+export enum TrackOpTypes {
   GET = 'get',
   HAS = 'has',
   ITERATE = 'iterate'
 }
 
-export const enum TriggerOpTypes {
+export enum TriggerOpTypes {
   SET = 'set',
   ADD = 'add',
   DELETE = 'delete',
   CLEAR = 'clear'
 }
 
-export const enum ReactiveFlags {
+export enum ReactiveFlags {
   SKIP = '__v_skip',
   IS_REACTIVE = '__v_isReactive',
   IS_READONLY = '__v_isReadonly',
@@ -22,7 +22,7 @@ export const enum ReactiveFlags {
   RAW = '__v_raw'
 }
 
-export const enum DirtyLevels {
+export enum DirtyLevels {
   NotDirty = 0,
   ComputedValueMaybeDirty = 1,
   ComputedValueDirty = 2,
index 9497527e81e3aaafe1fc9c98a90795829b005e0b..2a9615b14b7df7b37ba724b9096666d23476b60b 100644 (file)
@@ -68,8 +68,4 @@ export {
   getCurrentScope,
   onScopeDispose
 } from './effectScope'
-export {
-  TrackOpTypes /* @remove */,
-  TriggerOpTypes /* @remove */,
-  ReactiveFlags /* @remove */
-} from './constants'
+export { TrackOpTypes, TriggerOpTypes, ReactiveFlags } from './constants'
index c4888f6fba5f5fc51971f1d2d7873bdf6487cd05..4bb1c198896adf3fa7ec2aead929cd2dd96b4207 100644 (file)
@@ -27,7 +27,7 @@ export const shallowReactiveMap = new WeakMap<Target, any>()
 export const readonlyMap = new WeakMap<Target, any>()
 export const shallowReadonlyMap = new WeakMap<Target, any>()
 
-const enum TargetType {
+enum TargetType {
   INVALID = 0,
   COMMON = 1,
   COLLECTION = 2
index 88b58fdfab0b97b2a149cfdb13dc417b3a6ed045..9ae34c07a5c71b359d0ec4730d4acbf65dfb4fb8 100644 (file)
@@ -10,7 +10,7 @@ import {
 } from '../component'
 import { warn } from '../warning'
 
-export const enum DeprecationTypes {
+export enum DeprecationTypes {
   GLOBAL_MOUNT = 'GLOBAL_MOUNT',
   GLOBAL_MOUNT_CONTAINER = 'GLOBAL_MOUNT_CONTAINER',
   GLOBAL_EXTEND = 'GLOBAL_EXTEND',
index c7128ecfa89c4feb277dc5b311b2e24c5003e67c..9633cbfe907199278a381058c1533666ada8b105 100644 (file)
@@ -586,7 +586,7 @@ export type OptionTypesType<
   Defaults: Defaults
 }
 
-const enum OptionTypes {
+enum OptionTypes {
   PROPS = 'Props',
   DATA = 'Data',
   COMPUTED = 'Computed',
index b8e9bf66db526d6ec697466f3342e7f70064ba59..bbe88bf7b82672948b4e4817ea8aa40288f2748b 100644 (file)
@@ -164,7 +164,7 @@ export type ExtractPublicPropTypes<O> = {
   [K in keyof Pick<O, PublicOptionalKeys<O>>]?: InferPropType<O[K]>
 }
 
-const enum BooleanFlags {
+enum BooleanFlags {
   shouldCast,
   shouldCastTrue
 }
index 2f934b14089f52f6f4defa22dfe17939ff72605a..06e9965094bca20bacfdae83f751a8379769dacd 100644 (file)
@@ -281,7 +281,7 @@ if (__COMPAT__) {
   installCompatInstanceProperties(publicPropertiesMap)
 }
 
-const enum AccessTypes {
+enum AccessTypes {
   OTHER,
   SETUP,
   DATA,
index b9413598e54f6fbe01853d90c1848357f8d8750d..d1327db8ee806554b51e1266ea5086e703a104b0 100644 (file)
@@ -269,7 +269,7 @@ export const TeleportImpl = {
   hydrate: hydrateTeleport
 }
 
-export const enum TeleportMoveTypes {
+export enum TeleportMoveTypes {
   TARGET_CHANGE,
   TOGGLE, // enable / disable
   REORDER // moved in the main view
index bf67093f5c3e619837b12255641ab0879dbdd1b7..240a4aa04a781ec5d02a2ac988ac364dcee29618 100644 (file)
@@ -10,7 +10,7 @@ interface AppRecord {
   types: Record<string, string | Symbol>
 }
 
-const enum DevtoolsHooks {
+enum DevtoolsHooks {
   APP_INIT = 'app:init',
   APP_UNMOUNT = 'app:unmount',
   COMPONENT_UPDATED = 'component:updated',
index 63d829d7a87b31531c7b13a5963e1b71431c8315..829e2555d32736cdc164fb9d1ecbd746680e3084 100644 (file)
@@ -1,4 +1,4 @@
-export const enum LifecycleHooks {
+export enum LifecycleHooks {
   BEFORE_CREATE = 'bc',
   CREATED = 'c',
   BEFORE_MOUNT = 'bm',
index f3c03cc9e4f80b9c32106ecdd6106c4f8af38f89..aff4f5567fab08ee6738267c740a846793d907de 100644 (file)
@@ -6,7 +6,7 @@ import { LifecycleHooks } from './enums'
 
 // contexts where user provided function may be executed, in addition to
 // lifecycle hooks.
-export const enum ErrorCodes {
+export enum ErrorCodes {
   SETUP_FUNCTION,
   RENDER_FUNCTION,
   WATCH_GETTER,
index 94d6e8f627700f25596b08b0c781e284fa40c070..d79c09d3d363f8dc5d0b7450974b038f36dbb84b 100644 (file)
@@ -30,7 +30,7 @@ export type RootHydrateFunction = (
   container: (Element | ShadowRoot) & { _vnode?: VNode }
 ) => void
 
-const enum DOMNodeTypes {
+enum DOMNodeTypes {
   ELEMENT = 1,
   TEXT = 3,
   COMMENT = 8
index 85bd92e75b000998212db82931c865162fbc9690..fcc460c43d0a46e130f19bd91820d5dcb9f0e02a 100644 (file)
@@ -361,7 +361,7 @@ export const ssrUtils = (__SSR__ ? _ssrUtils : null) as typeof _ssrUtils
 
 // 2.x COMPAT ------------------------------------------------------------------
 
-export { DeprecationTypes } from './compat/compatConfig'
+import { DeprecationTypes as _DeprecationTypes } from './compat/compatConfig'
 export type { CompatVue } from './compat/global'
 export type { LegacyConfig } from './compat/globalConfig'
 
@@ -393,3 +393,7 @@ const _compatUtils = {
 export const compatUtils = (
   __COMPAT__ ? _compatUtils : null
 ) as typeof _compatUtils
+
+export const DeprecationTypes = (
+  __COMPAT__ ? _DeprecationTypes : null
+) as typeof _DeprecationTypes
index 3483f95d309d1e4729dea17738e38ac543daf8e4..fc762af3d96553937d5866415735efca758c3ff9 100644 (file)
@@ -265,7 +265,7 @@ export type SetupRenderEffectFn = (
   optimized: boolean
 ) => void
 
-export const enum MoveType {
+export enum MoveType {
   ENTER,
   LEAVE,
   REORDER
index a3a8012f28043b1d359f5f901650f22999062b3f..e45dda1a06f49ad9cd27d0452985cc03ceb2bae1 100644 (file)
@@ -1,12 +1,12 @@
 import { markRaw } from '@vue/reactivity'
 
-export const enum TestNodeTypes {
+export enum TestNodeTypes {
   TEXT = 'text',
   ELEMENT = 'element',
   COMMENT = 'comment'
 }
 
-export const enum NodeOpTypes {
+export enum NodeOpTypes {
   CREATE = 'create',
   INSERT = 'insert',
   REMOVE = 'remove',
index af5ee7b49e2e698d33c6967d5e3c8cee313bce40..49ee70dc21c9a0660343369cd5f293d9451d21c2 100644 (file)
@@ -16,7 +16,7 @@
  * Check the `patchElement` function in '../../runtime-core/src/renderer.ts' to see how the
  * flags are handled during diff.
  */
-export const enum PatchFlags {
+export enum PatchFlags {
   /**
    * Indicates an element with dynamic textContent (children fast path)
    */
index 8defb8a3bf3ae5536f5d78a840ce40294beb8332..84825c7f198f0ff66348bdf1dac196e3167dc588 100644 (file)
@@ -1,4 +1,4 @@
-export const enum ShapeFlags {
+export enum ShapeFlags {
   ELEMENT = 1,
   FUNCTIONAL_COMPONENT = 1 << 1,
   STATEFUL_COMPONENT = 1 << 2,
index 53860ff4dd11e69ef66137f663d798470406dff7..805c7d7c69380b9633864f11101ba20cb62ac967 100644 (file)
@@ -1,4 +1,4 @@
-export const enum SlotFlags {
+export enum SlotFlags {
   /**
    * Stable slots that only reference slot props or context state. The slot
    * can fully capture its own dependencies so when passed down the parent won't
index 54fa154af7d685572153ad0361d4d4a1b7604d9d..7feeb42ff581c6ad8c7f87d6dd3d50d54f7314a8 100644 (file)
@@ -10,7 +10,7 @@ import {
 
 export declare const RefType: unique symbol
 
-export declare const enum RefTypes {
+export declare enum RefTypes {
   Ref = 1,
   ComputedRef = 2,
   WritableComputedRef = 3
index b2c545f6b704f975e3d033e1d3d1251424c619ac..2435b03508534dce679423f209dc45931bc2b27a 100644 (file)
@@ -35,9 +35,15 @@ importers:
       '@types/hash-sum':
         specifier: ^1.0.2
         version: 1.0.2
+      '@types/minimist':
+        specifier: ^1.2.5
+        version: 1.2.5
       '@types/node':
         specifier: ^20.10.0
         version: 20.10.0
+      '@types/semver':
+        specifier: ^7.5.5
+        version: 7.5.5
       '@typescript-eslint/parser':
         specifier: ^6.13.0
         version: 6.13.0(eslint@8.54.0)(typescript@5.2.2)
@@ -62,6 +68,9 @@ importers:
       eslint:
         specifier: ^8.54.0
         version: 8.54.0
+      eslint-define-config:
+        specifier: ^1.24.1
+        version: 1.24.1
       eslint-plugin-jest:
         specifier: ^27.6.0
         version: 27.6.0(eslint@8.54.0)(typescript@5.2.2)
@@ -1514,6 +1523,10 @@ packages:
     resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==}
     dev: true
 
+  /@types/minimist@1.2.5:
+    resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
+    dev: true
+
   /@types/node@20.10.0:
     resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==}
     dependencies:
@@ -1528,8 +1541,8 @@ packages:
     resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
     dev: true
 
-  /@types/semver@7.5.4:
-    resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==}
+  /@types/semver@7.5.5:
+    resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
     dev: true
 
   /@types/yauzl@2.10.2:
@@ -1637,7 +1650,7 @@ packages:
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
       '@types/json-schema': 7.0.14
-      '@types/semver': 7.5.4
+      '@types/semver': 7.5.5
       '@typescript-eslint/scope-manager': 5.62.0
       '@typescript-eslint/types': 5.62.0
       '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
@@ -2842,6 +2855,11 @@ packages:
       source-map: 0.6.1
     dev: true
 
+  /eslint-define-config@1.24.1:
+    resolution: {integrity: sha512-o36vBhPSWyIQlHoMqGhhcGmOOm2A2ccBVIdLTG/AWdm9YmjpsLpf+5ntf9LlHR6dduLREgxtGwvwPwSt7vnXJg==}
+    engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'}
+    dev: true
+
   /eslint-plugin-jest@27.6.0(eslint@8.54.0)(typescript@5.2.2):
     resolution: {integrity: sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
index 44d01861da20cbf5d79ac88cde9dcae9ee80e4e3..421fa9c37819bb0b9612cfea45b18caf3bb562d4 100644 (file)
@@ -12,7 +12,7 @@ import terser from '@rollup/plugin-terser'
 import esbuild from 'rollup-plugin-esbuild'
 import alias from '@rollup/plugin-alias'
 import { entries } from './scripts/aliases.js'
-import { constEnum } from './scripts/const-enum.js'
+import { inlineEnums } from './scripts/inline-enums.js'
 
 if (!process.env.TARGET) {
   throw new Error('TARGET package must be specified via --environment flag.')
@@ -32,7 +32,7 @@ const pkg = require(resolve(`package.json`))
 const packageOptions = pkg.buildOptions || {}
 const name = packageOptions.filename || path.basename(packageDir)
 
-const [enumPlugin, enumDefines] = constEnum()
+const [enumPlugin, enumDefines] = inlineEnums()
 
 const outputConfigs = {
   'esm-bundler': {
index 39d2331e5f769b6ee2b718f26488f555de3ed68e..3e45167bc40f9a36ed0fe4484148ee8ae3962a19 100644 (file)
@@ -17,26 +17,29 @@ const targetPackages = targets
   ? packages.filter(pkg => targets.includes(pkg))
   : packages
 
-export default targetPackages.map(pkg => {
-  return {
-    input: `./temp/packages/${pkg}/src/index.d.ts`,
-    output: {
-      file: `packages/${pkg}/dist/${pkg}.d.ts`,
-      format: 'es'
-    },
-    plugins: [dts(), patchTypes(pkg), ...(pkg === 'vue' ? [copyMts()] : [])],
-    onwarn(warning, warn) {
-      // during dts rollup, everything is externalized by default
-      if (
-        warning.code === 'UNRESOLVED_IMPORT' &&
-        !warning.exporter.startsWith('.')
-      ) {
-        return
+export default targetPackages.map(
+  /** @returns {import('rollup').RollupOptions} */
+  pkg => {
+    return {
+      input: `./temp/packages/${pkg}/src/index.d.ts`,
+      output: {
+        file: `packages/${pkg}/dist/${pkg}.d.ts`,
+        format: 'es'
+      },
+      plugins: [dts(), patchTypes(pkg), ...(pkg === 'vue' ? [copyMts()] : [])],
+      onwarn(warning, warn) {
+        // during dts rollup, everything is externalized by default
+        if (
+          warning.code === 'UNRESOLVED_IMPORT' &&
+          !warning.exporter?.startsWith('.')
+        ) {
+          return
+        }
+        warn(warning)
       }
-      warn(warning)
     }
   }
-})
+)
 
 /**
  * Patch the dts generated by rollup-plugin-dts
@@ -45,6 +48,8 @@ export default targetPackages.map(pkg => {
  *    otherwise it gets weird in vitepress `defineComponent` call with
  *    "the inferred type cannot be named without a reference"
  * 2. Append custom augmentations (jsx, macros)
+ *
+ * @param {string} pkg
  * @returns {import('rollup').Plugin}
  */
 function patchTypes(pkg) {
index 162380900e7e6e0da6a1a41d3d18a6948310a36d..b96e576caa92d469dbbd92cd4386d1246d85c8f3 100644 (file)
@@ -26,7 +26,7 @@ import { execa, execaSync } from 'execa'
 import { cpus } from 'node:os'
 import { createRequire } from 'node:module'
 import { targets as allTargets, fuzzyMatchTarget } from './utils.js'
-import { scanEnums } from './const-enum.js'
+import { scanEnums } from './inline-enums.js'
 import prettyBytes from 'pretty-bytes'
 
 const require = createRequire(import.meta.url)
diff --git a/scripts/const-enum.js b/scripts/const-enum.js
deleted file mode 100644 (file)
index e9f25bc..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-// @ts-check
-
-/**
- * We use rollup-plugin-esbuild for faster builds, but esbuild in isolation
- * mode compiles const enums into runtime enums, bloating bundle size.
- *
- * Here we pre-process all the const enums in the project and turn them into
- * global replacements, and remove the original declarations and re-exports.
- *
- * This erases the const enums before the esbuild transform so that we can
- * leverage esbuild's speed while retaining the DX and bundle size benefits
- * of const enums.
- *
- * This file is expected to be executed with project root as cwd.
- */
-
-import { execaSync } from 'execa'
-import {
-  existsSync,
-  mkdirSync,
-  readFileSync,
-  rmSync,
-  writeFileSync
-} from 'node:fs'
-import { parse } from '@babel/parser'
-import path from 'node:path'
-import MagicString from 'magic-string'
-
-const ENUM_CACHE_PATH = 'temp/enum.json'
-
-function evaluate(exp) {
-  return new Function(`return ${exp}`)()
-}
-
-// this is called in the build script entry once
-// so the data can be shared across concurrent Rollup processes
-export function scanEnums() {
-  /**
-   * @type {{ ranges: Record<string, [number, number][]>, defines: Record<string, string>, ids: string[] }}
-   */
-  const enumData = {
-    ranges: {},
-    defines: {},
-    ids: []
-  }
-
-  // 1. grep for files with exported const enum
-  const { stdout } = execaSync('git', ['grep', `export const enum`])
-  const files = [...new Set(stdout.split('\n').map(line => line.split(':')[0]))]
-
-  // 2. parse matched files to collect enum info
-  for (const relativeFile of files) {
-    const file = path.resolve(process.cwd(), relativeFile)
-    const content = readFileSync(file, 'utf-8')
-    const ast = parse(content, {
-      plugins: ['typescript'],
-      sourceType: 'module'
-    })
-
-    for (const node of ast.program.body) {
-      if (
-        node.type === 'ExportNamedDeclaration' &&
-        node.declaration &&
-        node.declaration.type === 'TSEnumDeclaration'
-      ) {
-        if (file in enumData.ranges) {
-          // @ts-ignore
-          enumData.ranges[file].push([node.start, node.end])
-        } else {
-          // @ts-ignore
-          enumData.ranges[file] = [[node.start, node.end]]
-        }
-
-        const decl = node.declaration
-        let lastInitialized
-        for (let i = 0; i < decl.members.length; i++) {
-          const e = decl.members[i]
-          const id = decl.id.name
-          if (!enumData.ids.includes(id)) {
-            enumData.ids.push(id)
-          }
-          const key = e.id.type === 'Identifier' ? e.id.name : e.id.value
-          const fullKey = `${id}.${key}`
-          const saveValue = value => {
-            if (fullKey in enumData.defines) {
-              throw new Error(`name conflict for enum ${id} in ${file}`)
-            }
-            enumData.defines[fullKey] = JSON.stringify(value)
-          }
-          const init = e.initializer
-          if (init) {
-            let value
-            if (
-              init.type === 'StringLiteral' ||
-              init.type === 'NumericLiteral'
-            ) {
-              value = init.value
-            }
-
-            // e.g. 1 << 2
-            if (init.type === 'BinaryExpression') {
-              const resolveValue = node => {
-                if (
-                  node.type === 'NumericLiteral' ||
-                  node.type === 'StringLiteral'
-                ) {
-                  return node.value
-                } else if (node.type === 'MemberExpression') {
-                  const exp = content.slice(node.start, node.end)
-                  if (!(exp in enumData.defines)) {
-                    throw new Error(
-                      `unhandled enum initialization expression ${exp} in ${file}`
-                    )
-                  }
-                  return enumData.defines[exp]
-                } else {
-                  throw new Error(
-                    `unhandled BinaryExpression operand type ${node.type} in ${file}`
-                  )
-                }
-              }
-              const exp = `${resolveValue(init.left)}${
-                init.operator
-              }${resolveValue(init.right)}`
-              value = evaluate(exp)
-            }
-
-            if (init.type === 'UnaryExpression') {
-              if (
-                init.argument.type === 'StringLiteral' ||
-                init.argument.type === 'NumericLiteral'
-              ) {
-                const exp = `${init.operator}${init.argument.value}`
-                value = evaluate(exp)
-              } else {
-                throw new Error(
-                  `unhandled UnaryExpression argument type ${init.argument.type} in ${file}`
-                )
-              }
-            }
-
-            if (value === undefined) {
-              throw new Error(
-                `unhandled initializer type ${init.type} for ${fullKey} in ${file}`
-              )
-            }
-            saveValue(value)
-            lastInitialized = value
-          } else {
-            if (lastInitialized === undefined) {
-              // first initialized
-              saveValue((lastInitialized = 0))
-            } else if (typeof lastInitialized === 'number') {
-              saveValue(++lastInitialized)
-            } else {
-              // should not happen
-              throw new Error(`wrong enum initialization sequence in ${file}`)
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // 3. save cache
-  if (!existsSync('temp')) mkdirSync('temp')
-  writeFileSync(ENUM_CACHE_PATH, JSON.stringify(enumData))
-
-  return () => {
-    rmSync(ENUM_CACHE_PATH, { force: true })
-  }
-}
-
-/**
- * @returns {[import('rollup').Plugin, Record<string, string>]}
- */
-export function constEnum() {
-  if (!existsSync(ENUM_CACHE_PATH)) {
-    throw new Error('enum cache needs to be initialized before creating plugin')
-  }
-  /**
-   * @type {{ ranges: Record<string, [number, number][]>, defines: Record<string, string>, ids: string[] }}
-   */
-  const enumData = JSON.parse(readFileSync(ENUM_CACHE_PATH, 'utf-8'))
-
-  // construct a regex for matching re-exports of known const enums
-  const reExportsRE = new RegExp(
-    `export {[^}]*?\\b(${enumData.ids.join('|')})\\b[^]*?}`
-  )
-
-  // 3. during transform:
-  //    3.1 files w/ const enum declaration: remove declaration
-  //    3.2 files using const enum: inject into esbuild define
-  /**
-   * @type {import('rollup').Plugin}
-   */
-  const plugin = {
-    name: 'remove-const-enum',
-    transform(code, id) {
-      let s
-
-      if (id in enumData.ranges) {
-        s = s || new MagicString(code)
-        for (const [start, end] of enumData.ranges[id]) {
-          s.remove(start, end)
-        }
-      }
-
-      // check for const enum re-exports that must be removed
-      if (reExportsRE.test(code)) {
-        s = s || new MagicString(code)
-        const ast = parse(code, {
-          plugins: ['typescript'],
-          sourceType: 'module'
-        })
-        for (const node of ast.program.body) {
-          if (
-            node.type === 'ExportNamedDeclaration' &&
-            node.exportKind !== 'type' &&
-            node.source
-          ) {
-            for (let i = 0; i < node.specifiers.length; i++) {
-              const spec = node.specifiers[i]
-              if (
-                spec.type === 'ExportSpecifier' &&
-                spec.exportKind !== 'type' &&
-                enumData.ids.includes(spec.local.name)
-              ) {
-                const next = node.specifiers[i + 1]
-                if (next) {
-                  // @ts-ignore
-                  s.remove(spec.start, next.start)
-                } else {
-                  // last one
-                  const prev = node.specifiers[i - 1]
-                  // @ts-ignore
-                  s.remove(prev ? prev.end : spec.start, spec.end)
-                }
-              }
-            }
-          }
-        }
-      }
-
-      if (s) {
-        return {
-          code: s.toString(),
-          map: s.generateMap()
-        }
-      }
-    }
-  }
-
-  return [plugin, enumData.defines]
-}
index ec5f9c674cd44743bc9d361bddb78984b5bbe0fc..e31216ca4ef40b324333b771a07e4e5e99d675de 100644 (file)
@@ -41,6 +41,7 @@ const relativeOutfile = relative(process.cwd(), outfile)
 
 // resolve externals
 // TODO this logic is largely duplicated from rollup.config.js
+/** @type {string[]} */
 let external = []
 if (!inlineDeps) {
   // cjs & esm-bundler: external all deps
@@ -80,7 +81,7 @@ if (!inlineDeps) {
     ]
   }
 }
-
+/** @type {Array<import('esbuild').Plugin>} */
 const plugins = [
   {
     name: 'log-rebuild',
diff --git a/scripts/inline-enums.js b/scripts/inline-enums.js
new file mode 100644 (file)
index 0000000..0835dd8
--- /dev/null
@@ -0,0 +1,279 @@
+// @ts-check
+
+/**
+ * We used const enums before, but it caused some issues: #1228, so we
+ * switched to regular enums. But we still want to keep the zero-cost benefit
+ * of const enums, and minimize the impact on bundle size as much as possible.
+ *
+ * Here we pre-process all the enums in the project and turn them into
+ * global replacements, and rewrite the original declarations as object literals.
+ *
+ * This file is expected to be executed with project root as cwd.
+ */
+
+import * as assert from 'node:assert'
+import {
+  existsSync,
+  mkdirSync,
+  readFileSync,
+  rmSync,
+  writeFileSync
+} from 'node:fs'
+import * as path from 'node:path'
+import { parse } from '@babel/parser'
+import { execaSync } from 'execa'
+import MagicString from 'magic-string'
+
+/**
+ * @typedef {{ readonly name: string, readonly value: string | number }} EnumMember
+ * @typedef {{ readonly id: string, readonly range: readonly [start: number, end: number], readonly members: ReadonlyArray<EnumMember>}} EnumDeclaration
+ * @typedef {{ readonly declarations: { readonly [file: string] : ReadonlyArray<EnumDeclaration>}, readonly defines: { readonly [ id_key: `${string}.${string}`]: string } }} EnumData
+ */
+
+const ENUM_CACHE_PATH = 'temp/enum.json'
+
+/**
+ * @param {string} exp
+ * @returns {string | number}
+ */
+function evaluate(exp) {
+  return new Function(`return ${exp}`)()
+}
+
+// this is called in the build script entry once
+// so the data can be shared across concurrent Rollup processes
+export function scanEnums() {
+  /** @type {{ [file: string]: EnumDeclaration[] }} */
+  const declarations = Object.create(null)
+  /** @type {{ [id_key: `${string}.${string}`]: string; }} */
+  const defines = Object.create(null)
+
+  // 1. grep for files with exported enum
+  const { stdout } = execaSync('git', ['grep', `export enum`])
+  const files = [...new Set(stdout.split('\n').map(line => line.split(':')[0]))]
+
+  // 2. parse matched files to collect enum info
+  for (const relativeFile of files) {
+    const file = path.resolve(process.cwd(), relativeFile)
+    const content = readFileSync(file, 'utf-8')
+    const ast = parse(content, {
+      plugins: ['typescript'],
+      sourceType: 'module'
+    })
+
+    /** @type {Set<string>} */
+    const enumIds = new Set()
+    for (const node of ast.program.body) {
+      if (
+        node.type === 'ExportNamedDeclaration' &&
+        node.declaration &&
+        node.declaration.type === 'TSEnumDeclaration'
+      ) {
+        const decl = node.declaration
+        const id = decl.id.name
+        if (enumIds.has(id)) {
+          throw new Error(
+            `not support declaration merging for enum ${id} in ${file}`
+          )
+        }
+        enumIds.add(id)
+        /** @type {string | number | undefined} */
+        let lastInitialized
+        /** @type {Array<EnumMember>} */
+        const members = []
+
+        for (let i = 0; i < decl.members.length; i++) {
+          const e = decl.members[i]
+          const key = e.id.type === 'Identifier' ? e.id.name : e.id.value
+          const fullKey = /** @type {const} */ (`${id}.${key}`)
+          const saveValue = (/** @type {string | number} */ value) => {
+            // We need allow same name enum in different file.
+            // For example: enum ErrorCodes exist in both @vue/compiler-core and @vue/runtime-core
+            // But not allow `ErrorCodes.__EXTEND_POINT__` appear in two same name enum
+            if (fullKey in defines) {
+              throw new Error(`name conflict for enum ${id} in ${file}`)
+            }
+            members.push({
+              name: key,
+              value
+            })
+            defines[fullKey] = JSON.stringify(value)
+          }
+          const init = e.initializer
+          if (init) {
+            /** @type {string | number} */
+            let value
+            if (
+              init.type === 'StringLiteral' ||
+              init.type === 'NumericLiteral'
+            ) {
+              value = init.value
+            }
+            // e.g. 1 << 2
+            else if (init.type === 'BinaryExpression') {
+              const resolveValue = (
+                /** @type {import('@babel/types').Expression | import('@babel/types').PrivateName} */ node
+              ) => {
+                assert.ok(typeof node.start === 'number')
+                assert.ok(typeof node.end === 'number')
+                if (
+                  node.type === 'NumericLiteral' ||
+                  node.type === 'StringLiteral'
+                ) {
+                  return node.value
+                } else if (node.type === 'MemberExpression') {
+                  const exp = /** @type {`${string}.${string}`} */ (
+                    content.slice(node.start, node.end)
+                  )
+                  if (!(exp in defines)) {
+                    throw new Error(
+                      `unhandled enum initialization expression ${exp} in ${file}`
+                    )
+                  }
+                  return defines[exp]
+                } else {
+                  throw new Error(
+                    `unhandled BinaryExpression operand type ${node.type} in ${file}`
+                  )
+                }
+              }
+              const exp = `${resolveValue(init.left)}${
+                init.operator
+              }${resolveValue(init.right)}`
+              value = evaluate(exp)
+            } else if (init.type === 'UnaryExpression') {
+              if (
+                init.argument.type === 'StringLiteral' ||
+                init.argument.type === 'NumericLiteral'
+              ) {
+                const exp = `${init.operator}${init.argument.value}`
+                value = evaluate(exp)
+              } else {
+                throw new Error(
+                  `unhandled UnaryExpression argument type ${init.argument.type} in ${file}`
+                )
+              }
+            } else {
+              throw new Error(
+                `unhandled initializer type ${init.type} for ${fullKey} in ${file}`
+              )
+            }
+            lastInitialized = value
+            saveValue(lastInitialized)
+          } else {
+            if (lastInitialized === undefined) {
+              // first initialized
+              lastInitialized = 0
+              saveValue(lastInitialized)
+            } else if (typeof lastInitialized === 'number') {
+              lastInitialized++
+              saveValue(lastInitialized)
+            } else {
+              // should not happen
+              throw new Error(`wrong enum initialization sequence in ${file}`)
+            }
+          }
+        }
+
+        if (!(file in declarations)) {
+          declarations[file] = []
+        }
+        assert.ok(typeof node.start === 'number')
+        assert.ok(typeof node.end === 'number')
+        declarations[file].push({
+          id,
+          range: [node.start, node.end],
+          members
+        })
+      }
+    }
+  }
+
+  // 3. save cache
+  if (!existsSync('temp')) mkdirSync('temp')
+
+  /** @type {EnumData} */
+  const enumData = {
+    declarations,
+    defines
+  }
+
+  writeFileSync(ENUM_CACHE_PATH, JSON.stringify(enumData))
+
+  return () => {
+    rmSync(ENUM_CACHE_PATH, { force: true })
+  }
+}
+
+/**
+ * @returns {[import('rollup').Plugin, Record<string, string>]}
+ */
+export function inlineEnums() {
+  if (!existsSync(ENUM_CACHE_PATH)) {
+    throw new Error('enum cache needs to be initialized before creating plugin')
+  }
+  /**
+   * @type {EnumData}
+   */
+  const enumData = JSON.parse(readFileSync(ENUM_CACHE_PATH, 'utf-8'))
+
+  // 3. during transform:
+  //    3.1 files w/ enum declaration: rewrite declaration as object literal
+  //    3.2 files using enum: inject into esbuild define
+  /**
+   * @type {import('rollup').Plugin}
+   */
+  const plugin = {
+    name: 'inline-enum',
+    transform(code, id) {
+      /**
+       * @type {MagicString | undefined}
+       */
+      let s
+
+      if (id in enumData.declarations) {
+        s = s || new MagicString(code)
+        for (const declaration of enumData.declarations[id]) {
+          const {
+            range: [start, end],
+            id,
+            members
+          } = declaration
+          s.update(
+            start,
+            end,
+            `export const ${id} = {${members
+              .flatMap(({ name, value }) => {
+                const forwardMapping =
+                  JSON.stringify(name) + ': ' + JSON.stringify(value)
+                const reverseMapping =
+                  JSON.stringify(value.toString()) + ': ' + JSON.stringify(name)
+
+                // see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
+                return typeof value === 'string'
+                  ? [
+                      forwardMapping
+                      // string enum members do not get a reverse mapping generated at all
+                    ]
+                  : [
+                      forwardMapping,
+                      // other enum members should support enum reverse mapping
+                      reverseMapping
+                    ]
+              })
+              .join(',\n')}}`
+          )
+        }
+      }
+
+      if (s) {
+        return {
+          code: s.toString(),
+          map: s.generateMap()
+        }
+      }
+    }
+  }
+
+  return [plugin, enumData.defines]
+}
index 954103c0f2f66d05033a949956d1bb0e4006df68..28036b1bed91f7550a4ba7baf1f9ce0e337483cc 100644 (file)
@@ -10,6 +10,8 @@
     "packages/runtime-test",
     "packages/template-explorer",
     "packages/sfc-playground",
-    "packages/dts-test"
+    "packages/dts-test",
+    "rollup.config.js",
+    "scripts/*"
   ]
 }
index 5d7789b082ca708e0a5f1807bbe23d151c962aba..0aad03ad6ce20593e341b9c70f6f767f8a7f6f79 100644 (file)
@@ -8,7 +8,7 @@
     "useDefineForClassFields": false,
     "module": "esnext",
     "moduleResolution": "bundler",
-    "allowJs": false,
+    "allowJs": true,
     "strict": true,
     "noUnusedLocals": true,
     "experimentalDecorators": true,
@@ -34,6 +34,8 @@
     "packages/*/__tests__",
     "packages/dts-test",
     "packages/vue/jsx-runtime",
-    "scripts/setupVitest.ts"
-  ]
+    "scripts/*",
+    "rollup.*.js"
+  ],
+  "exclude": ["rollup.config.js", "scripts/*"]
 }