]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: initial compat build setup
authorEvan You <yyx990803@gmail.com>
Sat, 3 Apr 2021 15:55:44 +0000 (11:55 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 7 Apr 2021 20:19:24 +0000 (16:19 -0400)
14 files changed:
.eslintrc.js
packages/global.d.ts
packages/runtime-dom/src/index.ts
packages/shared/src/deprecations.ts [new file with mode: 0644]
packages/shared/src/index.ts
packages/vue-compat/README.md [new file with mode: 0644]
packages/vue-compat/api-extractor.json [new file with mode: 0644]
packages/vue-compat/index.js [new file with mode: 0644]
packages/vue-compat/package.json [new file with mode: 0644]
packages/vue-compat/src/apiGlobal.ts [new file with mode: 0644]
packages/vue-compat/src/dev.ts [new file with mode: 0644]
packages/vue-compat/src/index.ts [new file with mode: 0644]
packages/vue-compat/src/runtime.ts [new file with mode: 0644]
rollup.config.js

index 0732923e84baad4b0ea6f97707e824de7083a856..fc169abef813d8fba7b165542037ad57652a66a4 100644 (file)
@@ -41,7 +41,7 @@ module.exports = {
     },
     // Packages targeting DOM
     {
-      files: ['packages/{vue,runtime-dom}/**'],
+      files: ['packages/{vue,vue-compat,runtime-dom}/**'],
       rules: {
         'no-restricted-globals': ['error', ...NodeGlobals]
       }
index 8c6c57d8b7a65f2292d0e845b51e3667678466c0..72ab2fe93b2972f9d172dfc957592e4b7f6941e8 100644 (file)
@@ -8,6 +8,7 @@ declare var __ESM_BROWSER__: boolean
 declare var __NODE_JS__: boolean
 declare var __COMMIT__: string
 declare var __VERSION__: string
+declare var __COMPAT__: boolean
 
 // Feature flags
 declare var __FEATURE_OPTIONS_API__: boolean
index 773470621fe5fd621158138377d9cfc0a2cb962c..b2a87a60adad0f1b5b0514b3d5049cdb9c42ce9d 100644 (file)
@@ -13,7 +13,15 @@ import {
 import { nodeOps } from './nodeOps'
 import { patchProp, forcePatchProp } from './patchProp'
 // Importing from the compiler, will be tree-shaken in prod
-import { isFunction, isString, isHTMLTag, isSVGTag, extend } from '@vue/shared'
+import {
+  isFunction,
+  isString,
+  isHTMLTag,
+  isSVGTag,
+  extend,
+  warnDeprecation,
+  DeprecationTypes
+} from '@vue/shared'
 
 declare module '@vue/reactivity' {
   export interface RefUnwrapBailTypes {
@@ -63,8 +71,24 @@ export const createApp = ((...args) => {
   app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
     const container = normalizeContainer(containerOrSelector)
     if (!container) return
+
+    // 2.x compat check
+    if (__COMPAT__ && __DEV__) {
+      for (let i = 0; i < container.attributes.length; i++) {
+        const attr = container.attributes[i]
+        if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
+          warnDeprecation(DeprecationTypes.DOM_TEMPLATE_MOUNT)
+          break
+        }
+      }
+    }
+
     const component = app._component
     if (!isFunction(component) && !component.render && !component.template) {
+      // __UNSAFE__
+      // Reason: potential execution of JS expressions in in-DOM template.
+      // The user must make sure the in-DOM template is trusted. If it's
+      // rendered by the server, the template should not contain any user data.
       component.template = container.innerHTML
     }
     // clear content before mounting
diff --git a/packages/shared/src/deprecations.ts b/packages/shared/src/deprecations.ts
new file mode 100644 (file)
index 0000000..f3d2375
--- /dev/null
@@ -0,0 +1,25 @@
+export const enum DeprecationTypes {
+  DOM_TEMPLATE_MOUNT
+}
+
+type DeprecationData = {
+  message: string
+  link?: string
+}
+
+const deprecations: Record<DeprecationTypes, DeprecationData> = {
+  [DeprecationTypes.DOM_TEMPLATE_MOUNT]: {
+    message:
+      `Vue detected directives on the mount container. ` +
+      `In Vue 3, the container is no longer considered part of the template ` +
+      `and will not be processed/replaced.`,
+    link: `https://v3.vuejs.org/guide/migration/mount-changes.html`
+  }
+}
+
+export function warnDeprecation(key: DeprecationTypes) {
+  const { message, link } = deprecations[key]
+  console.warn(
+    `[Deprecation]: ${message}${link ? `\nFor more details, see ${link}` : ``}`
+  )
+}
index 84b324beda3511e50f9e764a460e1b75fff0e30c..8a16c7c4840ab239fd7d0a8b2ed4793dc61d6803 100644 (file)
@@ -12,6 +12,7 @@ export * from './domAttrConfig'
 export * from './escapeHtml'
 export * from './looseEqual'
 export * from './toDisplayString'
+export * from './deprecations'
 
 /**
  * List of @babel/parser plugins that are used for template expression
diff --git a/packages/vue-compat/README.md b/packages/vue-compat/README.md
new file mode 100644 (file)
index 0000000..62c5aa5
--- /dev/null
@@ -0,0 +1 @@
+# @vue/compat
\ No newline at end of file
diff --git a/packages/vue-compat/api-extractor.json b/packages/vue-compat/api-extractor.json
new file mode 100644 (file)
index 0000000..a8982eb
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "extends": "../../api-extractor.json",
+  "mainEntryPointFilePath": "./dist/packages/<unscopedPackageName>/src/index.d.ts",
+  "dtsRollup": {
+    "publicTrimmedFilePath": "./dist/<unscopedPackageName>.d.ts"
+  }
+}
\ No newline at end of file
diff --git a/packages/vue-compat/index.js b/packages/vue-compat/index.js
new file mode 100644 (file)
index 0000000..aadbf10
--- /dev/null
@@ -0,0 +1,7 @@
+'use strict'
+
+if (process.env.NODE_ENV === 'production') {
+  module.exports = require('./dist/compat.cjs.prod.js')
+} else {
+  module.exports = require('./dist/compat.cjs.js')
+}
diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json
new file mode 100644 (file)
index 0000000..6064756
--- /dev/null
@@ -0,0 +1,44 @@
+{
+  "name": "@vue/compat",
+  "version": "3.0.11",
+  "description": "@vue/compat",
+  "main": "index.js",
+  "module": "dist/vue.esm-bundler.js",
+  "types": "dist/vue.d.ts",
+  "unpkg": "dist/vue.global.js",
+  "jsdelivr": "dist/vue.global.js",
+  "files": [
+    "index.js",
+    "dist"
+  ],
+  "buildOptions": {
+    "name": "Vue",
+    "filename": "vue",
+    "compat": true,
+    "formats": [
+      "esm-bundler",
+      "esm-bundler-runtime",
+      "cjs",
+      "global",
+      "global-runtime",
+      "esm-browser",
+      "esm-browser-runtime"
+    ]
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vuejs/vue.git"
+  },
+  "keywords": [
+    "vue"
+  ],
+  "author": "Evan You",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/vuejs/vue/issues"
+  },
+  "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-compat#readme",
+  "peerDependencies": {
+    "vue": "3.0.11"
+  }
+}
diff --git a/packages/vue-compat/src/apiGlobal.ts b/packages/vue-compat/src/apiGlobal.ts
new file mode 100644 (file)
index 0000000..10ad71e
--- /dev/null
@@ -0,0 +1,157 @@
+import { reactive } from '@vue/reactivity'
+import {
+  createApp,
+  defineComponent,
+  nextTick,
+  App,
+  AppConfig,
+  Plugin,
+  Component,
+  ComponentOptions,
+  ComponentPublicInstance,
+  Directive,
+  RenderFunction,
+  isRuntimeOnly
+} from '@vue/runtime-dom'
+
+// TODO make these getter/setters and trigger deprecation warnings
+export type LegacyConfig = AppConfig & {
+  /**
+   * @deprecated `config.silent` option has been removed
+   */
+  silent?: boolean
+  /**
+   * @deprecated use __VUE_PROD_DEVTOOLS__ compile-time feature flag instead
+   * https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
+   */
+  devtools?: boolean
+  /**
+   * @deprecated use `config.isCustomElement` instead
+   * https://v3.vuejs.org/guide/migration/global-api.html#config-ignoredelements-is-now-config-iscustomelement
+   */
+  ignoredElements?: (string | RegExp)[]
+  /**
+   * @deprecated
+   * https://v3.vuejs.org/guide/migration/keycode-modifiers.html
+   */
+  keyCodes?: Record<string, number | number[]>
+  /**
+   * @deprecated
+   * https://v3.vuejs.org/guide/migration/global-api.html#config-productiontip-removed
+   */
+  productionTip?: boolean
+}
+
+/**
+ * @deprecated the default `Vue` export has been removed in Vue 3. The type for
+ * the default export is provided only for migration purposes. Please use
+ * named imports instead - e.g. `import { createApp } from 'vue'`.
+ */
+export type GlobalVue = Pick<App, 'version' | 'component' | 'directive'> & {
+  // no inference here since these types are not meant for actual use - they
+  // are merely here to provide type checks for internal implementation and
+  // information for migration.
+  new (options?: ComponentOptions): ComponentPublicInstance
+
+  version: string
+  config: LegacyConfig
+
+  extend: typeof defineComponent
+  nextTick: typeof nextTick
+
+  use(plugin: Plugin, ...options: any[]): GlobalVue
+  mixin(mixin: ComponentOptions): GlobalVue
+
+  component(name: string): Component | undefined
+  component(name: string, component: Component): GlobalVue
+  directive(name: string): Directive | undefined
+  directive(name: string, directive: Directive): GlobalVue
+
+  compile(template: string): RenderFunction
+
+  /**
+   * @deprecated Vue 3 no longer needs set() for adding new properties.
+   */
+  set(target: any, key: string | number | symbol, value: any): void
+  /**
+   * @deprecated Vue 3 no longer needs delete() for property deletions.
+   */
+  delete(target: any, key: string | number | symbol): void
+  /**
+   * @deprecated use `reactive` instead.
+   */
+  observable: typeof reactive
+  /**
+   * @deprecated filters have been removed from Vue 3.
+   */
+  filter(name: string, arg: any): null
+}
+
+export const Vue: GlobalVue = function Vue(options: ComponentOptions = {}) {
+  const app = createApp(options)
+  // copy over global config mutations
+  for (const key in singletonApp.config) {
+    if (
+      key !== 'isNativeTag' &&
+      !(key === 'isCustomElement' && isRuntimeOnly())
+    ) {
+      // @ts-ignore
+      app.config[key] = singletonApp.config[key]
+    }
+  }
+  if (options.el) {
+    return app.mount(options.el)
+  }
+} as any
+
+const singletonApp = createApp({})
+
+Vue.version = __VERSION__
+Vue.config = singletonApp.config
+
+Vue.extend = defineComponent
+Vue.nextTick = nextTick
+
+Vue.set = (target, key, value) => {
+  // TODO deprecation warnings
+  target[key] = value
+}
+Vue.delete = (target, key) => {
+  // TODO deprecation warnings
+  delete target[key]
+}
+// TODO wrap with deprecation warning
+Vue.observable = reactive
+
+Vue.use = (p, ...options) => {
+  singletonApp.use(p, ...options)
+  return Vue
+}
+
+Vue.mixin = m => {
+  singletonApp.mixin(m)
+  return Vue
+}
+
+Vue.component = ((name: string, comp: any) => {
+  if (comp) {
+    singletonApp.component(name, comp)
+    return Vue
+  } else {
+    return singletonApp.component(name)
+  }
+}) as any
+
+Vue.directive = ((name: string, dir: any) => {
+  if (dir) {
+    singletonApp.directive(name, dir)
+    return Vue
+  } else {
+    return singletonApp.directive(name)
+  }
+}) as any
+
+Vue.filter = ((name: string, filter: any) => {
+  // TODO deprecation warning
+  // TODO compiler warning for filters (maybe behavior compat?)
+}) as any
diff --git a/packages/vue-compat/src/dev.ts b/packages/vue-compat/src/dev.ts
new file mode 100644 (file)
index 0000000..99ba49a
--- /dev/null
@@ -0,0 +1,14 @@
+import { initCustomFormatter } from '@vue/runtime-dom'
+
+export function initDev() {
+  if (__BROWSER__) {
+    if (!__ESM_BUNDLER__) {
+      console.info(
+        `You are running a development build of Vue.\n` +
+          `Make sure to use the production build (*.prod.js) when deploying for production.`
+      )
+    }
+
+    initCustomFormatter()
+  }
+}
diff --git a/packages/vue-compat/src/index.ts b/packages/vue-compat/src/index.ts
new file mode 100644 (file)
index 0000000..481f3b5
--- /dev/null
@@ -0,0 +1,93 @@
+// This entry is the "full-build" that includes both the runtime
+// and the compiler, and supports on-the-fly compilation of the template option.
+import { initDev } from './dev'
+import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
+import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
+import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared'
+import { InternalRenderFunction } from 'packages/runtime-core/src/component'
+import * as runtimeDom from '@vue/runtime-dom'
+import { Vue } from './apiGlobal'
+
+if (__DEV__) {
+  initDev()
+}
+
+const compileCache: Record<string, RenderFunction> = Object.create(null)
+
+function compileToFunction(
+  template: string | HTMLElement,
+  options?: CompilerOptions
+): RenderFunction {
+  if (!isString(template)) {
+    if (template.nodeType) {
+      template = template.innerHTML
+    } else {
+      __DEV__ && warn(`invalid template option: `, template)
+      return NOOP
+    }
+  }
+
+  const key = template
+  const cached = compileCache[key]
+  if (cached) {
+    return cached
+  }
+
+  if (template[0] === '#') {
+    const el = document.querySelector(template)
+    if (__DEV__ && !el) {
+      warn(`Template element not found or is empty: ${template}`)
+    }
+    // __UNSAFE__
+    // Reason: potential execution of JS expressions in in-DOM template.
+    // The user must make sure the in-DOM template is trusted. If it's rendered
+    // by the server, the template should not contain any user data.
+    template = el ? el.innerHTML : ``
+  }
+
+  const { code } = compile(
+    template,
+    extend(
+      {
+        hoistStatic: true,
+        onError(err: CompilerError) {
+          if (__DEV__) {
+            const message = `Template compilation error: ${err.message}`
+            const codeFrame =
+              err.loc &&
+              generateCodeFrame(
+                template as string,
+                err.loc.start.offset,
+                err.loc.end.offset
+              )
+            warn(codeFrame ? `${message}\n${codeFrame}` : message)
+          } else {
+            /* istanbul ignore next */
+            throw err
+          }
+        }
+      },
+      options
+    )
+  )
+
+  // The wildcard import results in a huge object with every export
+  // with keys that cannot be mangled, and can be quite heavy size-wise.
+  // In the global build we know `Vue` is available globally so we can avoid
+  // the wildcard object.
+  const render = (__GLOBAL__
+    ? new Function(code)()
+    : new Function('Vue', code)(runtimeDom)) as RenderFunction
+
+  // mark the function as runtime compiled
+  ;(render as InternalRenderFunction)._rc = true
+
+  return (compileCache[key] = render)
+}
+
+registerRuntimeCompiler(compileToFunction)
+
+Vue.compile = compileToFunction
+extend(Vue, runtimeDom)
+
+export default Vue
diff --git a/packages/vue-compat/src/runtime.ts b/packages/vue-compat/src/runtime.ts
new file mode 100644 (file)
index 0000000..04b60b3
--- /dev/null
@@ -0,0 +1,25 @@
+// This entry exports the runtime only, and is built as
+// `dist/vue.esm-bundler.js` which is used by default for bundlers.
+import { initDev } from './dev'
+import { warn } from '@vue/runtime-dom'
+
+if (__DEV__) {
+  initDev()
+}
+
+export * from '@vue/runtime-dom'
+
+export const compile = () => {
+  if (__DEV__) {
+    warn(
+      `Runtime compilation is not supported in this build of Vue.` +
+        (__ESM_BUNDLER__
+          ? ` Configure your bundler to alias "vue" to "@vue/compat/dist/vue.esm-bundler.js".`
+          : __ESM_BROWSER__
+            ? ` Use "vue.esm-browser.js" instead.`
+            : __GLOBAL__
+              ? ` Use "vue.global.js" instead.`
+              : ``) /* should not happen */
+    )
+  }
+}
index e8fe8d8860176fbe01c75b8dfafc342c32e6941c..eced8ac8fd9e3557e2b3d0b02bb8bdcf575f2641 100644 (file)
@@ -1,3 +1,4 @@
+// @ts-check
 import path from 'path'
 import ts from 'rollup-plugin-typescript2'
 import replace from '@rollup/plugin-replace'
@@ -10,10 +11,10 @@ if (!process.env.TARGET) {
 const masterVersion = require('./package.json').version
 const packagesDir = path.resolve(__dirname, 'packages')
 const packageDir = path.resolve(packagesDir, process.env.TARGET)
-const name = path.basename(packageDir)
 const resolve = p => path.resolve(packageDir, p)
 const pkg = require(resolve(`package.json`))
 const packageOptions = pkg.buildOptions || {}
+const name = packageOptions.filename || path.basename(packageDir)
 
 // ensure TS checks only once for each build
 let hasTSChecked = false
@@ -89,6 +90,7 @@ function createConfig(format, output, plugins = []) {
   const isBrowserESMBuild = /esm-browser/.test(format)
   const isNodeBuild = format === 'cjs'
   const isGlobalBuild = /global/.test(format)
+  const isCompatBuild = !!packageOptions.compat
 
   if (isGlobalBuild) {
     output.name = packageOptions.name
@@ -116,19 +118,23 @@ function createConfig(format, output, plugins = []) {
 
   const entryFile = /runtime$/.test(format) ? `src/runtime.ts` : `src/index.ts`
 
-  const external =
-    isGlobalBuild || isBrowserESMBuild
-      ? packageOptions.enableNonBrowserBranches
-        ? []
-        : // normal browser builds - non-browser only imports are tree-shaken,
-          // they are only listed here to suppress warnings.
-          ['source-map', '@babel/parser', 'estree-walker']
-      : // Node / esm-bundler builds. Externalize everything.
-        [
-          ...Object.keys(pkg.dependencies || {}),
-          ...Object.keys(pkg.peerDependencies || {}),
-          ...['path', 'url', 'stream'] // for @vue/compiler-sfc / server-renderer
-        ]
+  let external = []
+
+  if (isGlobalBuild || isBrowserESMBuild) {
+    if (!packageOptions.enableNonBrowserBranches) {
+      // normal browser builds - non-browser only imports are tree-shaken,
+      // they are only listed here to suppress warnings.
+      external = ['source-map', '@babel/parser', 'estree-walker']
+    }
+  } else if (!isCompatBuild) {
+    // Node / esm-bundler builds.
+    // externalize all deps unless it's the compat build.
+    external = [
+      ...Object.keys(pkg.dependencies || {}),
+      ...Object.keys(pkg.peerDependencies || {}),
+      ...['path', 'url', 'stream'] // for @vue/compiler-sfc / server-renderer
+    ]
+  }
 
   // the browser builds of @vue/compiler-sfc requires postcss to be available
   // as a global (e.g. http://wzrd.in/standalone/postcss)
@@ -139,9 +145,11 @@ function createConfig(format, output, plugins = []) {
   const nodePlugins =
     packageOptions.enableNonBrowserBranches && format !== 'cjs'
       ? [
+          // @ts-ignore
           require('@rollup/plugin-commonjs')({
             sourceMap: false
           }),
+          // @ts-ignore
           require('rollup-plugin-polyfill-node')(),
           require('@rollup/plugin-node-resolve').nodeResolve()
         ]
@@ -165,7 +173,8 @@ function createConfig(format, output, plugins = []) {
         (isGlobalBuild || isBrowserESMBuild || isBundlerESMBuild) &&
           !packageOptions.enableNonBrowserBranches,
         isGlobalBuild,
-        isNodeBuild
+        isNodeBuild,
+        isCompatBuild
       ),
       ...nodePlugins,
       ...plugins
@@ -188,7 +197,8 @@ function createReplacePlugin(
   isBrowserESMBuild,
   isBrowserBuild,
   isGlobalBuild,
-  isNodeBuild
+  isNodeBuild,
+  isCompatBuild
 ) {
   const replacements = {
     __COMMIT__: `"${process.env.COMMIT}"`,
@@ -208,6 +218,9 @@ function createReplacePlugin(
     // is targeting Node (SSR)?
     __NODE_JS__: isNodeBuild,
 
+    // 2.x compat build
+    __COMPAT__: isCompatBuild,
+
     // feature flags
     __FEATURE_SUSPENSE__: true,
     __FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : true,
@@ -231,6 +244,7 @@ function createReplacePlugin(
     }
   })
   return replace({
+    // @ts-ignore
     values: replacements,
     preventAssignment: true
   })