@@ -111,19 +111,34 @@ const ASTCallbacks = {
111111 VariableDeclarator ( node , _state , ancestors ) {
112112 if ( ancestors . some ( nodeIsUniform ) ) { return ; }
113113 if ( nodeIsUniform ( node . init ) ) {
114- const uniformNameLiteral = {
115- type : 'Literal' ,
116- value : node . id . name
114+ // Only inject the variable name if the first argument isn't already a string
115+ if ( node . init . arguments . length === 0 ||
116+ node . init . arguments [ 0 ] . type !== 'Literal' ||
117+ typeof node . init . arguments [ 0 ] . value !== 'string' ) {
118+ const uniformNameLiteral = {
119+ type : 'Literal' ,
120+ value : node . id . name
121+ }
122+ node . init . arguments . unshift ( uniformNameLiteral ) ;
117123 }
118- node . init . arguments . unshift ( uniformNameLiteral ) ;
119124 }
120125 if ( nodeIsVarying ( node . init ) ) {
121- const varyingNameLiteral = {
122- type : 'Literal' ,
123- value : node . id . name
126+ // Only inject the variable name if the first argument isn't already a string
127+ if (
128+ node . init . arguments . length === 0 ||
129+ node . init . arguments [ 0 ] . type !== 'Literal' ||
130+ typeof node . init . arguments [ 0 ] . value !== 'string'
131+ ) {
132+ const varyingNameLiteral = {
133+ type : 'Literal' ,
134+ value : node . id . name
135+ }
136+ node . init . arguments . unshift ( varyingNameLiteral ) ;
137+ _state . varyings [ node . id . name ] = varyingNameLiteral ;
138+ } else {
139+ // Still track it as a varying even if name wasn't injected
140+ _state . varyings [ node . id . name ] = node . init . arguments [ 0 ] ;
124141 }
125- node . init . arguments . unshift ( varyingNameLiteral ) ;
126- _state . varyings [ node . id . name ] = varyingNameLiteral ;
127142 }
128143 } ,
129144 Identifier ( node , _state , ancestors ) {
@@ -362,45 +377,75 @@ const ASTCallbacks = {
362377 } ;
363378 // Analyze which outer scope variables are assigned in any branch
364379 const assignedVars = new Set ( ) ;
365- const analyzeBlock = ( body ) => {
366- if ( body . type !== 'BlockStatement' ) return ;
367- // First pass: collect variable declarations within this block
368- const localVars = new Set ( ) ;
369- for ( const stmt of body . body ) {
380+
381+ // Helper function to check if a block contains strands control flow calls as immediate children
382+ const blockContainsStrandsControlFlow = ( node ) => {
383+ if ( node . type !== 'BlockStatement' ) return false ;
384+ return node . body . some ( stmt => {
385+ // Check for variable declarations with strands control flow init
370386 if ( stmt . type === 'VariableDeclaration' ) {
371- for ( const decl of stmt . declarations ) {
372- if ( decl . id . type === 'Identifier' ) {
373- localVars . add ( decl . id . name ) ;
374- }
387+ const match = stmt . declarations . some ( decl =>
388+ decl . init ?. type === 'CallExpression' &&
389+ (
390+ (
391+ decl . init ?. callee ?. type === 'MemberExpression' &&
392+ decl . init ?. callee ?. object ?. type === 'Identifier' &&
393+ decl . init ?. callee ?. object ?. name === '__p5' &&
394+ ( decl . init ?. callee ?. property ?. name === 'strandsFor' ||
395+ decl . init ?. callee ?. property ?. name === 'strandsIf' )
396+ ) ||
397+ (
398+ decl . init ?. callee ?. type === 'Identifier' &&
399+ ( decl . init ?. callee ?. name === '__p5.strandsFor' ||
400+ decl . init ?. callee ?. name === '__p5.strandsIf' )
401+ )
402+ )
403+ ) ;
404+ return match
405+ }
406+ return false ;
407+ } ) ;
408+ } ;
409+
410+ const analyzeBranch = ( functionBody ) => {
411+ // First pass: collect all variable declarations in the branch
412+ const localVars = new Set ( ) ;
413+ ancestor ( functionBody , {
414+ VariableDeclarator ( node , ancestors ) {
415+ // Skip if we're inside a block that contains strands control flow
416+ if ( ancestors . some ( blockContainsStrandsControlFlow ) ) return ;
417+ if ( node . id . type === 'Identifier' ) {
418+ localVars . add ( node . id . name ) ;
375419 }
376420 }
377- }
378- // Second pass: find assignments to non-local variables
379- for ( const stmt of body . body ) {
380- if ( stmt . type === 'ExpressionStatement' &&
381- stmt . expression . type === 'AssignmentExpression' ) {
382- const left = stmt . expression . left ;
421+ } ) ;
422+
423+ // Second pass: find assignments to non-local variables using acorn-walk
424+ ancestor ( functionBody , {
425+ AssignmentExpression ( node , ancestors ) {
426+ // Skip if we're inside a block that contains strands control flow
427+ if ( ancestors . some ( blockContainsStrandsControlFlow ) ) return ;
428+
429+ const left = node . left ;
383430 if ( left . type === 'Identifier' ) {
384431 // Direct variable assignment: x = value
385432 if ( ! localVars . has ( left . name ) ) {
386433 assignedVars . add ( left . name ) ;
387434 }
388435 } else if ( left . type === 'MemberExpression' &&
389436 left . object . type === 'Identifier' ) {
390- // Property assignment: obj.prop = value
437+ // Property assignment: obj.prop = value (includes swizzles)
391438 if ( ! localVars . has ( left . object . name ) ) {
392439 assignedVars . add ( left . object . name ) ;
393440 }
394441 }
395- } else if ( stmt . type === 'BlockStatement' ) {
396- // Recursively analyze nested block statements
397- analyzeBlock ( stmt ) ;
398442 }
399- }
443+ } ) ;
400444 } ;
445+
401446 // Analyze all branches for assignments to outer scope variables
402- analyzeBlock ( thenFunction . body ) ;
403- analyzeBlock ( elseFunction . body ) ;
447+ analyzeBranch ( thenFunction . body ) ;
448+ analyzeBranch ( elseFunction . body ) ;
404449 if ( assignedVars . size > 0 ) {
405450 // Add copying, reference replacement, and return statements to branch functions
406451 const addCopyingAndReturn = ( functionBody , varsToReturn ) => {
0 commit comments