@@ -8,6 +8,7 @@ import type {
88} from 'estree'
99import { walk } from 'estree-walker'
1010import MagicString from 'magic-string'
11+ import { hashString } from '../plugins/utils'
1112import { buildScopeTree , type ScopeTree } from './scope'
1213
1314export function transformHoistInlineDirective (
@@ -21,13 +22,14 @@ export function transformHoistInlineDirective(
2122 runtime : (
2223 value : string ,
2324 name : string ,
24- meta : { directiveMatch : RegExpMatchArray } ,
25+ meta : { directiveMatch : RegExpMatchArray ; hasBoundArgs : boolean } ,
2526 ) => string
2627 directive : string | RegExp
2728 rejectNonAsyncFunction ?: boolean
2829 encode ?: ( value : string ) => string
2930 decode ?: ( value : string ) => string
3031 noExport ?: boolean
32+ stableName ?: boolean
3133 } ,
3234) : {
3335 output : MagicString
@@ -45,6 +47,7 @@ export function transformHoistInlineDirective(
4547
4648 const scopeTree = buildScopeTree ( ast )
4749 const names : string [ ] = [ ]
50+ const signatureCounts = new Map < string , number > ( )
4851
4952 walk ( ast , {
5053 enter ( node , parent ) {
@@ -65,12 +68,60 @@ export function transformHoistInlineDirective(
6568 )
6669 }
6770
71+ const isObjectMethod =
72+ node . type === 'FunctionExpression' &&
73+ parent ?. type === 'Property' &&
74+ ( parent . method || parent . kind !== 'init' )
75+ const isClassMethod =
76+ node . type === 'FunctionExpression' &&
77+ parent ?. type === 'MethodDefinition'
78+ if ( isClassMethod && ! parent . static ) {
79+ throw Object . assign (
80+ new Error (
81+ `It is not allowed to define inline ${ JSON . stringify ( match [ 0 ] ) } annotated class instance methods. Use a function, object method property, or static class method instead.` ,
82+ ) ,
83+ { pos : parent . start } ,
84+ )
85+ }
86+ if ( isClassMethod && parent . key . type === 'PrivateIdentifier' ) {
87+ throw Object . assign (
88+ new Error (
89+ `It is not allowed to define inline ${ JSON . stringify ( match [ 0 ] ) } annotated private class methods.` ,
90+ ) ,
91+ { pos : parent . start } ,
92+ )
93+ }
94+ if (
95+ ( isObjectMethod && parent . kind !== 'init' ) ||
96+ ( isClassMethod && parent . kind !== 'method' )
97+ ) {
98+ throw Object . assign (
99+ new Error (
100+ `It is not allowed to define inline ${ JSON . stringify ( match [ 0 ] ) } annotated getters or setters.` ,
101+ ) ,
102+ { pos : parent . start } ,
103+ )
104+ }
105+
68106 const declName = node . type === 'FunctionDeclaration' && node . id . name
107+ const expressionName =
108+ node . type === 'FunctionExpression' ? node . id ?. name : undefined
109+ const methodName =
110+ ( isObjectMethod || isClassMethod ) &&
111+ ( parent . key . type === 'Identifier' || parent . key . type === 'Literal' )
112+ ? String (
113+ parent . key . type === 'Identifier'
114+ ? parent . key . name
115+ : parent . key . value ,
116+ )
117+ : undefined
69118 const originalName =
70119 declName ||
120+ methodName ||
71121 ( parent ?. type === 'VariableDeclarator' &&
72122 parent . id . type === 'Identifier' &&
73123 parent . id . name ) ||
124+ expressionName ||
74125 'anonymous_server_function'
75126
76127 const bindVars = getBindVars ( node , scopeTree )
@@ -92,8 +143,15 @@ export function transformHoistInlineDirective(
92143 }
93144
94145 // append a new `FunctionDeclaration` at the end
146+ let nameKey = String ( names . length )
147+ if ( options . stableName ) {
148+ const signature = `${ originalName } :${ input . slice ( node . start , node . end ) } `
149+ const signatureCount = signatureCounts . get ( signature ) ?? 0
150+ signatureCounts . set ( signature , signatureCount + 1 )
151+ nameKey = `${ hashString ( signature ) } _${ signatureCount } `
152+ }
95153 const newName =
96- `$$hoist_${ names . length } ` + ( originalName ? `_${ originalName } ` : '' )
154+ `$$hoist_${ nameKey } ` + ( originalName ? `_${ originalName } ` : '' )
97155 names . push ( newName )
98156 output . update (
99157 node . start ,
@@ -113,14 +171,27 @@ export function transformHoistInlineDirective(
113171 // replace original declartion with action register + bind
114172 let newCode = `/* #__PURE__ */ ${ runtime ( newName , newName , {
115173 directiveMatch : match ,
174+ hasBoundArgs : bindVars . length > 0 ,
116175 } ) } `
117176 if ( bindVars . length > 0 ) {
118177 const bindArgs = options . encode
119178 ? options . encode ( '[' + bindVars . map ( ( b ) => b . expr ) . join ( ', ' ) + ']' )
120179 : bindVars . map ( ( b ) => b . expr ) . join ( ', ' )
121180 newCode = `${ newCode } .bind(null, ${ bindArgs } )`
122181 }
123- if ( declName ) {
182+ if ( isObjectMethod ) {
183+ output . update (
184+ parent . start ,
185+ node . start ,
186+ `${ input . slice ( parent . key . start , parent . key . end ) } : ` ,
187+ )
188+ } else if ( isClassMethod ) {
189+ const key = parent . computed
190+ ? `[${ input . slice ( parent . key . start , parent . key . end ) } ]`
191+ : input . slice ( parent . key . start , parent . key . end )
192+ output . update ( parent . start , node . start , `static ${ key } = ` )
193+ newCode += ';'
194+ } else if ( declName ) {
124195 newCode = `const ${ declName } = ${ newCode } ;`
125196 if ( parent ?. type === 'ExportDefaultDeclaration' ) {
126197 output . remove ( parent . start , node . start )
@@ -132,10 +203,7 @@ export function transformHoistInlineDirective(
132203 } ,
133204 } )
134205
135- return {
136- output,
137- names,
138- }
206+ return { output, names }
139207}
140208
141209const exactRegex = ( s : string ) : RegExp =>
@@ -151,7 +219,9 @@ function matchDirective(
151219 stmt . expression . type === 'Literal' &&
152220 typeof stmt . expression . value === 'string'
153221 ) {
222+ directive . lastIndex = 0
154223 const match = stmt . expression . value . match ( directive )
224+ directive . lastIndex = 0
155225 if ( match ) {
156226 return { match, node : stmt . expression }
157227 }
0 commit comments