1- /** @import { Node, BinaryExpression, LogicalExpression, UnaryExpression, Expression, SequenceExpression, TemplateLiteral, ConditionalExpression } from 'estree' */
1+ /** @import { Node, BinaryExpression, LogicalExpression, UnaryExpression, Expression, SequenceExpression, TemplateLiteral, ConditionalExpression, CallExpression } from 'estree' */
22/** @import { ComponentClientTransformState } from '../client/types' */
33/** @import { ComponentServerTransformState } from '../server/types' */
44export const DYNAMIC = Symbol ( 'DYNAMIC' ) ;
5-
5+ if ( ! ( 'difference' in Set . prototype ) ) {
6+ /**
7+ * Quick and dirty polfill for `Set.prototype.difference`
8+ * @template T
9+ * @this {Set<T>}
10+ * @param {Set<T> } other
11+ * @returns {Set<T> }
12+ */
13+ //@ts -ignore
14+ Set . prototype . difference = function difference ( other ) {
15+ /** @type {Set<T> } */
16+ let res = new Set ( ) ;
17+ for ( let item of this ) {
18+ if ( ! other . has ( item ) ) {
19+ res . add ( item ) ;
20+ }
21+ }
22+ return res ;
23+ } ;
24+ }
625/**
726 * @template {boolean} S
827 * @param {Node } node
@@ -135,6 +154,7 @@ export function evaluate_static_expression(node, state, server) {
135154 function handle_ident ( name ) {
136155 if ( server ) return DYNAMIC ;
137156 const scope = state . scope . get ( name ) ;
157+ // TODO tweak this when implicit top-level reactivity is removed
138158 if ( scope ?. kind === 'normal' && scope ?. declaration_kind !== 'import' ) {
139159 if ( scope . initial && ! scope . mutated && ! scope . reassigned && ! scope . updated ) {
140160 //@ts -ignore
@@ -186,6 +206,91 @@ export function evaluate_static_expression(node, state, server) {
186206 }
187207 return DYNAMIC ;
188208 }
209+ /**
210+ * @param {CallExpression } node
211+ */
212+ function handle_call ( node ) {
213+ /**
214+ * There isn't much we can really do here (without having an unreasonable amount of code),
215+ * so we don't optimize for much
216+ * We only optimize for these:
217+ * ```
218+ * (() => identifier_or_evaluable_value)();
219+ * (() => {
220+ * return evaluable;
221+ * })();
222+ * (() => {
223+ * let variable = ident_or_evaluable_value;
224+ * return variable;
225+ * });
226+ * // it's fine if we don't want to use this for side effect reasons, its just one line (251)
227+ * (() => {
228+ * anything_but_a_return;
229+ * })()
230+ * ```
231+ * I would like to possibly optimize this:
232+ * ```
233+ * (() => {
234+ * let variable = ident_or_evaluable_value;
235+ * return variable_combined_with_evaluable;
236+ * })();
237+ * ```
238+ * But I don't know how to do that with the `Scope` class.
239+ */
240+ let { callee } = node ;
241+ if (
242+ callee . type !== 'ArrowFunctionExpression' ||
243+ node . arguments . length ||
244+ callee . params . length
245+ ) {
246+ return DYNAMIC ;
247+ }
248+ let { body } = callee ;
249+ if ( body . type === 'BlockStatement' ) {
250+ let children = body . body ;
251+ if ( ! children . find ( ( { type } ) => type === 'ReturnStatement' ) ) return undefined ;
252+ if ( children . length === 1 && children [ 0 ] . type === 'ReturnStatement' ) {
253+ return children [ 0 ] . argument == null
254+ ? undefined
255+ : internal ( children [ 0 ] . argument , state , server ) ;
256+ }
257+ let valid_body_children = new Set ( [
258+ 'VariableDeclaration' ,
259+ 'EmptyStatement' ,
260+ 'ReturnStatement'
261+ ] ) ;
262+ let types = new Set ( children . map ( ( { type } ) => type ) ) ;
263+ if ( types . difference ( valid_body_children ) . size ) {
264+ return DYNAMIC ;
265+ }
266+ if ( types . has ( 'EmptyStatement' ) ) {
267+ children = children . filter ( ( { type } ) => type !== 'EmptyStatement' ) ;
268+ }
269+ if ( children . length > 2 ) return DYNAMIC ;
270+ if ( children [ 0 ] . type !== 'VariableDeclaration' || children [ 1 ] . type !== 'ReturnStatement' )
271+ return DYNAMIC ;
272+ let [ declaration , return_statement ] = children ;
273+ if ( declaration . declarations . length > 1 ) return DYNAMIC ;
274+ let [ declarator ] = declaration . declarations ;
275+ if ( declarator . id . type !== 'Identifier' ) return DYNAMIC ;
276+ let variable_value ;
277+ if ( declarator . init != null ) {
278+ variable_value = internal ( declarator . init , state , server ) ;
279+ if ( variable_value === DYNAMIC ) {
280+ //might be unpure
281+ return DYNAMIC ;
282+ }
283+ }
284+ let { argument } = return_statement ;
285+ if ( argument == null ) return undefined ;
286+ if ( argument . type === 'Identifier' && argument . name === declarator . id . name ) {
287+ return variable_value ;
288+ }
289+ return internal ( argument , state , server ) ;
290+ } else {
291+ return internal ( body , state , server ) ;
292+ }
293+ }
189294 switch ( node . type ) {
190295 case 'Literal' :
191296 return node . value ;
@@ -203,6 +308,8 @@ export function evaluate_static_expression(node, state, server) {
203308 return handle_template ( node ) ;
204309 case 'ConditionalExpression' :
205310 return handle_ternary ( node ) ;
311+ case 'CallExpression' :
312+ return handle_call ( node ) ;
206313 default :
207314 return DYNAMIC ;
208315 }
0 commit comments