@@ -37,7 +37,11 @@ export function shouldTransform(src: string): boolean {
3737 return transformCheckRE . test ( src )
3838}
3939
40- type Scope = Record < string , boolean | 'prop' >
40+ interface Binding {
41+ isConst ?: boolean
42+ isProp ?: boolean
43+ }
44+ type Scope = Record < string , Binding | false >
4145
4246export interface RefTransformOptions {
4347 filename ?: string
@@ -118,6 +122,7 @@ export function transformAST(
118122 {
119123 local : string // local identifier, may be different
120124 default ?: any
125+ isConst ?: boolean
121126 }
122127 >
123128) : {
@@ -168,17 +173,20 @@ export function transformAST(
168173 let escapeScope : CallExpression | undefined // inside $$()
169174 const excludedIds = new WeakSet < Identifier > ( )
170175 const parentStack : Node [ ] = [ ]
171- const propsLocalToPublicMap = Object . create ( null )
176+ const propsLocalToPublicMap : Record < string , string > = Object . create ( null )
172177
173178 if ( knownRefs ) {
174179 for ( const key of knownRefs ) {
175- rootScope [ key ] = true
180+ rootScope [ key ] = { }
176181 }
177182 }
178183 if ( knownProps ) {
179184 for ( const key in knownProps ) {
180- const { local } = knownProps [ key ]
181- rootScope [ local ] = 'prop'
185+ const { local, isConst } = knownProps [ key ]
186+ rootScope [ local ] = {
187+ isProp : true ,
188+ isConst : ! ! isConst
189+ }
182190 propsLocalToPublicMap [ local ] = key
183191 }
184192 }
@@ -218,7 +226,7 @@ export function transformAST(
218226 return false
219227 }
220228
221- function error ( msg : string , node : Node ) {
229+ function error ( msg : string , node : Node ) : never {
222230 const e = new Error ( msg )
223231 ; ( e as any ) . node = node
224232 throw e
@@ -229,10 +237,10 @@ export function transformAST(
229237 return `_${ msg } `
230238 }
231239
232- function registerBinding ( id : Identifier , isRef = false ) {
240+ function registerBinding ( id : Identifier , binding ?: Binding ) {
233241 excludedIds . add ( id )
234242 if ( currentScope ) {
235- currentScope [ id . name ] = isRef
243+ currentScope [ id . name ] = binding ? binding : false
236244 } else {
237245 error (
238246 'registerBinding called without active scope, something is wrong.' ,
@@ -241,7 +249,8 @@ export function transformAST(
241249 }
242250 }
243251
244- const registerRefBinding = ( id : Identifier ) => registerBinding ( id , true )
252+ const registerRefBinding = ( id : Identifier , isConst = false ) =>
253+ registerBinding ( id , { isConst } )
245254
246255 let tempVarCount = 0
247256 function genTempVar ( ) {
@@ -296,7 +305,12 @@ export function transformAST(
296305 isCall &&
297306 ( refCall = isRefCreationCall ( ( decl as any ) . init . callee . name ) )
298307 ) {
299- processRefDeclaration ( refCall , decl . id , decl . init as CallExpression )
308+ processRefDeclaration (
309+ refCall ,
310+ decl . id ,
311+ decl . init as CallExpression ,
312+ stmt . kind === 'const'
313+ )
300314 } else {
301315 const isProps =
302316 isRoot && isCall && ( decl as any ) . init . callee . name === 'defineProps'
@@ -316,7 +330,8 @@ export function transformAST(
316330 function processRefDeclaration (
317331 method : string ,
318332 id : VariableDeclarator [ 'id' ] ,
319- call : CallExpression
333+ call : CallExpression ,
334+ isConst : boolean
320335 ) {
321336 excludedIds . add ( call . callee as Identifier )
322337 if ( method === convertSymbol ) {
@@ -325,16 +340,16 @@ export function transformAST(
325340 s . remove ( call . callee . start ! + offset , call . callee . end ! + offset )
326341 if ( id . type === 'Identifier' ) {
327342 // single variable
328- registerRefBinding ( id )
343+ registerRefBinding ( id , isConst )
329344 } else if ( id . type === 'ObjectPattern' ) {
330- processRefObjectPattern ( id , call )
345+ processRefObjectPattern ( id , call , isConst )
331346 } else if ( id . type === 'ArrayPattern' ) {
332- processRefArrayPattern ( id , call )
347+ processRefArrayPattern ( id , call , isConst )
333348 }
334349 } else {
335350 // shorthands
336351 if ( id . type === 'Identifier' ) {
337- registerRefBinding ( id )
352+ registerRefBinding ( id , isConst )
338353 // replace call
339354 s . overwrite (
340355 call . start ! + offset ,
@@ -350,6 +365,7 @@ export function transformAST(
350365 function processRefObjectPattern (
351366 pattern : ObjectPattern ,
352367 call : CallExpression ,
368+ isConst : boolean ,
353369 tempVar ?: string ,
354370 path : PathSegment [ ] = [ ]
355371 ) {
@@ -384,21 +400,27 @@ export function transformAST(
384400 // { foo: bar }
385401 nameId = p . value
386402 } else if ( p . value . type === 'ObjectPattern' ) {
387- processRefObjectPattern ( p . value , call , tempVar , [ ...path , key ] )
403+ processRefObjectPattern ( p . value , call , isConst , tempVar , [
404+ ...path ,
405+ key
406+ ] )
388407 } else if ( p . value . type === 'ArrayPattern' ) {
389- processRefArrayPattern ( p . value , call , tempVar , [ ...path , key ] )
408+ processRefArrayPattern ( p . value , call , isConst , tempVar , [
409+ ...path ,
410+ key
411+ ] )
390412 } else if ( p . value . type === 'AssignmentPattern' ) {
391413 if ( p . value . left . type === 'Identifier' ) {
392414 // { foo: bar = 1 }
393415 nameId = p . value . left
394416 defaultValue = p . value . right
395417 } else if ( p . value . left . type === 'ObjectPattern' ) {
396- processRefObjectPattern ( p . value . left , call , tempVar , [
418+ processRefObjectPattern ( p . value . left , call , isConst , tempVar , [
397419 ...path ,
398420 [ key , p . value . right ]
399421 ] )
400422 } else if ( p . value . left . type === 'ArrayPattern' ) {
401- processRefArrayPattern ( p . value . left , call , tempVar , [
423+ processRefArrayPattern ( p . value . left , call , isConst , tempVar , [
402424 ...path ,
403425 [ key , p . value . right ]
404426 ] )
@@ -412,7 +434,7 @@ export function transformAST(
412434 error ( `reactivity destructure does not support rest elements.` , p )
413435 }
414436 if ( nameId ) {
415- registerRefBinding ( nameId )
437+ registerRefBinding ( nameId , isConst )
416438 // inject toRef() after original replaced pattern
417439 const source = pathToString ( tempVar , path )
418440 const keyStr = isString ( key )
@@ -437,6 +459,7 @@ export function transformAST(
437459 function processRefArrayPattern (
438460 pattern : ArrayPattern ,
439461 call : CallExpression ,
462+ isConst : boolean ,
440463 tempVar ?: string ,
441464 path : PathSegment [ ] = [ ]
442465 ) {
@@ -462,12 +485,12 @@ export function transformAST(
462485 // [...a]
463486 error ( `reactivity destructure does not support rest elements.` , e )
464487 } else if ( e . type === 'ObjectPattern' ) {
465- processRefObjectPattern ( e , call , tempVar , [ ...path , i ] )
488+ processRefObjectPattern ( e , call , isConst , tempVar , [ ...path , i ] )
466489 } else if ( e . type === 'ArrayPattern' ) {
467- processRefArrayPattern ( e , call , tempVar , [ ...path , i ] )
490+ processRefArrayPattern ( e , call , isConst , tempVar , [ ...path , i ] )
468491 }
469492 if ( nameId ) {
470- registerRefBinding ( nameId )
493+ registerRefBinding ( nameId , isConst )
471494 // inject toRef() after original replaced pattern
472495 const source = pathToString ( tempVar , path )
473496 const defaultStr = defaultValue ? `, ${ snip ( defaultValue ) } ` : ``
@@ -520,9 +543,18 @@ export function transformAST(
520543 parentStack : Node [ ]
521544 ) : boolean {
522545 if ( hasOwn ( scope , id . name ) ) {
523- const bindingType = scope [ id . name ]
524- if ( bindingType ) {
525- const isProp = bindingType === 'prop'
546+ const binding = scope [ id . name ]
547+
548+ if ( binding ) {
549+ if (
550+ binding . isConst &&
551+ ( ( parent . type === 'AssignmentExpression' && id === parent . left ) ||
552+ parent . type === 'UpdateExpression' )
553+ ) {
554+ error ( `Assignment to constant variable.` , id )
555+ }
556+
557+ const { isProp } = binding
526558 if ( isStaticProperty ( parent ) && parent . shorthand ) {
527559 // let binding used in a property shorthand
528560 // skip for destructure patterns
@@ -638,18 +670,20 @@ export function transformAST(
638670 return this . skip ( )
639671 }
640672
641- if (
642- node . type === 'Identifier' &&
643- // if inside $$(), skip unless this is a destructured prop binding
644- ! ( escapeScope && rootScope [ node . name ] !== 'prop' ) &&
645- isReferencedIdentifier ( node , parent ! , parentStack ) &&
646- ! excludedIds . has ( node )
647- ) {
648- // walk up the scope chain to check if id should be appended .value
649- let i = scopeStack . length
650- while ( i -- ) {
651- if ( rewriteId ( scopeStack [ i ] , node , parent ! , parentStack ) ) {
652- return
673+ if ( node . type === 'Identifier' ) {
674+ const binding = rootScope [ node . name ]
675+ if (
676+ // if inside $$(), skip unless this is a destructured prop binding
677+ ! ( escapeScope && ( ! binding || ! binding . isProp ) ) &&
678+ isReferencedIdentifier ( node , parent ! , parentStack ) &&
679+ ! excludedIds . has ( node )
680+ ) {
681+ // walk up the scope chain to check if id should be appended .value
682+ let i = scopeStack . length
683+ while ( i -- ) {
684+ if ( rewriteId ( scopeStack [ i ] , node , parent ! , parentStack ) ) {
685+ return
686+ }
653687 }
654688 }
655689 }
@@ -729,7 +763,10 @@ export function transformAST(
729763 } )
730764
731765 return {
732- rootRefs : Object . keys ( rootScope ) . filter ( key => rootScope [ key ] === true ) ,
766+ rootRefs : Object . keys ( rootScope ) . filter ( key => {
767+ const binding = rootScope [ key ]
768+ return binding && ! binding . isProp
769+ } ) ,
733770 importedHelpers : [ ...importedHelpers ]
734771 }
735772}
0 commit comments