]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: generate valid v-model and TS code in script setup inline mode
authorEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 17:42:58 +0000 (12:42 -0500)
committerEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 17:42:58 +0000 (12:42 -0500)
packages/compiler-core/src/options.ts
packages/compiler-core/src/runtimeHelpers.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/vModel.ts
packages/compiler-core/src/transforms/vOn.ts
packages/compiler-sfc/src/compileScript.ts

index 2a6b79c1642b56c66514288d4297ad1ea7fe8cb6..02ac12e62275e3858a92421b356c0e5d1b392d76 100644 (file)
@@ -169,6 +169,10 @@ export interface TransformOptions extends SharedTransformCodegenOptions {
    * needed to render inline CSS variables on component root
    */
   ssrCssVars?: string
+  /**
+   * Indicates that transforms should try to output valid TS code
+   */
+  isTS?: boolean
   onError?: (error: CompilerError) => void
 }
 
index 1cf8c7bfc1209f5fcebb64722f8a534140df5f5e..f40c94c3d89efe7ae312c8d642e4e749a694c47a 100644 (file)
@@ -30,6 +30,7 @@ export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
 export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``)
 export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
 export const UNREF = Symbol(__DEV__ ? `unref` : ``)
+export const IS_REF = Symbol(__DEV__ ? `isRef` : ``)
 
 // Name mapping for runtime helpers that need to be imported from 'vue' in
 // generated code. Make sure these are correctly exported in the runtime!
@@ -64,7 +65,8 @@ export const helperNameMap: any = {
   [POP_SCOPE_ID]: `popScopeId`,
   [WITH_SCOPE_ID]: `withScopeId`,
   [WITH_CTX]: `withCtx`,
-  [UNREF]: `unref`
+  [UNREF]: `unref`,
+  [IS_REF]: `isRef`
 }
 
 export function registerRuntimeHelpers(helpers: any) {
index c3a37c9f7157d8b69a98d4741bdaf47bd03d1a6f..eddd03caa14217492ac47da43acb1b1bf93676c2 100644 (file)
@@ -125,6 +125,7 @@ export function createTransformContext(
     ssrCssVars = ``,
     bindingMetadata = EMPTY_OBJ,
     inline = false,
+    isTS = false,
     onError = defaultOnError
   }: TransformOptions
 ): TransformContext {
@@ -144,6 +145,7 @@ export function createTransformContext(
     ssrCssVars,
     bindingMetadata,
     inline,
+    isTS,
     onError,
 
     // state
index ee1a4bd8bc31e5c9d1102a97c0dee48f4534d163..2acb5af195222c4820debbe015b879ef7adc7505 100644 (file)
@@ -14,6 +14,7 @@ import {
   hasScopeRef,
   isStaticExp
 } from '../utils'
+import { helperNameMap, IS_REF, UNREF } from '../runtimeHelpers'
 
 export const transformModel: DirectiveTransform = (dir, node, context) => {
   const { exp, arg } = dir
@@ -24,10 +25,16 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
     return createTransformProps()
   }
 
+  const rawExp = exp.loc.source
   const expString =
-    exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
+    exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : rawExp
 
-  if (!isMemberExpression(expString)) {
+  // im SFC <script setup> inline mode, the exp may have been transformed into
+  // _unref(exp)
+  const isUnrefExp =
+    !__BROWSER__ && expString.startsWith(`_${helperNameMap[UNREF]}`)
+
+  if (!isMemberExpression(expString) && !isUnrefExp) {
     context.onError(
       createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc)
     )
@@ -53,14 +60,25 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
       : createCompoundExpression(['"onUpdate:" + ', arg])
     : `onUpdate:modelValue`
 
+  const assigmentExp = isUnrefExp
+    ? // v-model used on a potentially ref binding in <script setup> inline mode.
+      // not the most beautiful codegen here but it gets the job done.
+      createSimpleExpression(
+        `$event => { if (${context.helperString(IS_REF)}(${rawExp})) {` +
+          `${rawExp}.value = $event` +
+          ` } else {${context.isTS ? `\n//@ts-ignore\n` : ``}` +
+          `${rawExp} = $event` +
+          ` }}`,
+        false,
+        exp.loc
+      )
+    : createCompoundExpression([`$event => (`, exp, ` = $event)`])
+
   const props = [
     // modelValue: foo
     createObjectProperty(propName, dir.exp!),
     // "onUpdate:modelValue": $event => (foo = $event)
-    createObjectProperty(
-      eventName,
-      createCompoundExpression([`$event => (`, exp, ` = $event)`])
-    )
+    createObjectProperty(eventName, assigmentExp)
   ]
 
   // cache v-model handler if applicable (when it doesn't refer any scope vars)
index 546f669e2aece1ce9c5ece249c3af53f59e687ba..ce506aa3eb1ef8eb842f396d330161b9db743c80 100644 (file)
@@ -122,9 +122,13 @@ export const transformOn: DirectiveTransform = (
     if (isInlineStatement || (shouldCache && isMemberExp)) {
       // wrap inline statement in a function expression
       exp = createCompoundExpression([
-        `${isInlineStatement ? `$event` : `(...args)`} => ${
-          hasMultipleStatements ? `{` : `(`
-        }`,
+        `${
+          isInlineStatement
+            ? `$event`
+            : `${
+                !__BROWSER__ && context.isTS ? `\n//@ts-ignore\n` : ``
+              }(...args)`
+        } => ${hasMultipleStatements ? `{` : `(`}`,
         exp,
         hasMultipleStatements ? `}` : `)`
       ])
index 522277db2f464957b76d49c4d7b1f3794af7dfdd..cce7a1bd11439efba415fe5535884b5e6dd67da7 100644 (file)
@@ -814,11 +814,12 @@ export function compileScript(
         ...options.templateOptions,
         filename,
         source: sfc.template.content,
+        inMap: sfc.template.map,
         compilerOptions: {
           inline: true,
+          isTS,
           bindingMetadata
         }
-        // TODO source map
       })
       if (tips.length) {
         tips.forEach(warnOnce)
@@ -827,6 +828,16 @@ export function compileScript(
       if (typeof err === 'string') {
         throw new Error(err)
       } else if (err) {
+        if (err.loc) {
+          err.message +=
+            `\n` +
+            generateCodeFrame(
+              source,
+              err.loc.start.offset,
+              err.loc.end.offset
+            ) +
+            `\n`
+        }
         throw err
       }
       if (preamble) {