* Renders an AST node to JavaScript source code, with special handling for variable declarators. * @param node - The ESTree node to render. * @param preDeclared - Whether the variable has been previously declared. * @param options - Configuration options for the emitter.
(
node: ESTree.Node,
preDeclared: boolean,
options: EmitterOptions = {}
)
| 246 | * @param options - Configuration options for the emitter. |
| 247 | */ |
| 248 | private renderNode( |
| 249 | node: ESTree.Node, |
| 250 | preDeclared: boolean, |
| 251 | options: EmitterOptions = {} |
| 252 | ) { |
| 253 | const source = this.analyzer.getSource(); |
| 254 | const declaredVariables = this.analyzer.declaredVariables; |
| 255 | |
| 256 | const sideEffectPolicy = options.disallowSideEffectInitializers; |
| 257 | const sideEffectMode = typeof sideEffectPolicy === 'object' && sideEffectPolicy !== null ? |
| 258 | sideEffectPolicy.mode ?? 'strict' : 'strict'; |
| 259 | const canDisallow = Boolean(sideEffectPolicy); |
| 260 | |
| 261 | const assignmentTarget = |
| 262 | node.type === 'AssignmentExpression' |
| 263 | ? node |
| 264 | : node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' |
| 265 | ? node.expression |
| 266 | : null; |
| 267 | |
| 268 | const init = assignmentTarget && assignmentTarget.operator === '=' ? assignmentTarget.right : node.type === 'VariableDeclarator' ? node.init : null; |
| 269 | const forceRemove = canDisallow && init && !this.isSafeInitializer(init, sideEffectMode); |
| 270 | const initializerFallback = this.getInitializerFallback(init); |
| 271 | |
| 272 | let initSource = initializerFallback; |
| 273 | |
| 274 | if (!forceRemove && init) { |
| 275 | // e.g. `var someVar = document;` |
| 276 | if (!preDeclared && init.type === 'Identifier' && !declaredVariables.has(init.name)) { |
| 277 | initSource = initializerFallback; |
| 278 | } else { |
| 279 | const left = assignmentTarget?.left; |
| 280 | const isPrototypeAlias = init?.type === 'MemberExpression' && !init.computed && init.property.type === 'Identifier' && init.property.name === 'prototype'; |
| 281 | |
| 282 | // Skip things we don't need. |
| 283 | if (!isPrototypeAlias && left?.type === 'MemberExpression' && init) { |
| 284 | if ( |
| 285 | canDisallow && |
| 286 | left.object.type === 'Identifier' && |
| 287 | init.type !== 'FunctionExpression' && |
| 288 | init.type !== 'ArrowFunctionExpression' && |
| 289 | init.type !== 'LogicalExpression' && |
| 290 | init.type !== 'ClassExpression' |
| 291 | ) { |
| 292 | return `${indent}// Skipped ${memberToString(left, source)} assignment.`; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | // e.g. `someVar = someOtherVarFuncOrCall` |
| 297 | initSource = extractNodeSource(init, source) |
| 298 | ?.trim() |
| 299 | .replace(/;\s*$/, '') || 'undefined // [JsExtractor] Failed to extract initializer source.'; |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | // Wrap sequence expressions in parens to avoid syntax issues. |
| 304 | if (!forceRemove && init && init.type === 'SequenceExpression' && !initSource.startsWith('(')) { |
| 305 | initSource = `(${initSource})`; |
no test coverage detected