visitor.js (28352B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import * as t from "@babel/types"; 6 7 import getFunctionName from "../utils/getFunctionName"; 8 import { getAst } from "../utils/ast"; 9 10 /** 11 * "implicit" 12 * Variables added automaticly like "this" and "arguments" 13 * 14 * "var" 15 * Variables declared with "var" or non-block function declarations 16 * 17 * "let" 18 * Variables declared with "let". 19 * 20 * "const" 21 * Variables declared with "const", or added as const 22 * bindings like inner function expressions and inner class names. 23 * 24 * "import" 25 * Imported binding names exposed from other modules. 26 * 27 * "global" 28 * Variables that reference undeclared global values. 29 */ 30 31 // Location information about the expression immediartely surrounding a 32 // given binding reference. 33 34 function isGeneratedId(id) { 35 return !/\/originalSource/.test(id); 36 } 37 38 export function parseSourceScopes(sourceId) { 39 const ast = getAst(sourceId); 40 if (!ast || !Object.keys(ast).length) { 41 return null; 42 } 43 44 return buildScopeList(ast, sourceId); 45 } 46 47 export function buildScopeList(ast, sourceId) { 48 const { global, lexical } = createGlobalScope(ast, sourceId); 49 50 const state = { 51 // The id for the source that scope list is generated for 52 sourceId, 53 54 // A map of any free variables(variables which are used within the current scope but not 55 // declared within the scope). This changes when a new scope is created. 56 freeVariables: new Map(), 57 58 // A stack of all the free variables created across all the scopes that have 59 // been created. 60 freeVariableStack: [], 61 62 inType: null, 63 64 // The current scope, a new scope is potentially created on a visit to each node 65 // depending in the criteria. Initially set to the lexical global scope which is the 66 // child to the global scope. 67 scope: lexical, 68 69 // A stack of all the existing scopes, this is mainly used retrieve the parent scope 70 // (which is the last scope push onto the stack) on exiting a visited node. 71 scopeStack: [], 72 73 declarationBindingIds: new Set(), 74 }; 75 t.traverse(ast, scopeCollectionVisitor, state); 76 77 for (const [key, freeVariables] of state.freeVariables) { 78 let binding = global.bindings[key]; 79 if (!binding) { 80 binding = { 81 type: "global", 82 refs: [], 83 }; 84 global.bindings[key] = binding; 85 } 86 87 binding.refs = freeVariables.concat(binding.refs); 88 } 89 90 // TODO: This should probably check for ".mjs" extension on the 91 // original file, and should also be skipped if the the generated 92 // code is an ES6 module rather than a script. 93 if ( 94 isGeneratedId(sourceId) || 95 (ast.program.sourceType === "script" && !looksLikeCommonJS(global)) 96 ) { 97 stripModuleScope(global); 98 } 99 100 return toParsedScopes([global], sourceId) || []; 101 } 102 103 function toParsedScopes(children, sourceId) { 104 if (!children || children.length === 0) { 105 return undefined; 106 } 107 return children.map(scope => ({ 108 // Removing unneed information from TempScope such as parent reference. 109 // We also need to convert BabelLocation to the Location type. 110 start: scope.loc.start, 111 end: scope.loc.end, 112 type: 113 scope.type === "module" || scope.type === "function-body" 114 ? "block" 115 : scope.type, 116 scopeKind: "", 117 displayName: scope.displayName, 118 bindings: scope.bindings, 119 children: toParsedScopes(scope.children, sourceId), 120 })); 121 } 122 123 /** 124 * Create a new scope object and link the scope to it parent. 125 * 126 * @param {string} type - scope type 127 * @param {string} displayName - The scope display name 128 * @param {object} parent - The parent object scope 129 * @param {object} loc - The start and end postions (line/columns) of the scope 130 * @returns {object} The newly created scope 131 */ 132 function createTempScope(type, displayName, parent, loc) { 133 const scope = { 134 type, 135 displayName, 136 parent, 137 138 // A list of all the child scopes 139 children: [], 140 loc, 141 142 // All the bindings defined in this scope 143 // bindings = [binding, ...] 144 // binding = { type: "", refs: []} 145 bindings: Object.create(null), 146 }; 147 148 if (parent) { 149 parent.children.push(scope); 150 } 151 return scope; 152 } 153 154 // Sets a new current scope and creates a new map to store the free variables 155 // that may exist in this scope. 156 function pushTempScope(state, type, displayName, loc) { 157 const scope = createTempScope(type, displayName, state.scope, loc); 158 159 state.scope = scope; 160 161 state.freeVariableStack.push(state.freeVariables); 162 state.freeVariables = new Map(); 163 return scope; 164 } 165 166 function isNode(node, type) { 167 return node ? node.type === type : false; 168 } 169 170 // Walks up the scope tree to the top most variable scope 171 function getVarScope(scope) { 172 let s = scope; 173 while (s.type !== "function" && s.type !== "module") { 174 if (!s.parent) { 175 return s; 176 } 177 s = s.parent; 178 } 179 return s; 180 } 181 182 function fromBabelLocation(location, sourceId) { 183 return { 184 sourceId, 185 line: location.line, 186 column: location.column, 187 }; 188 } 189 190 function parseDeclarator( 191 declaratorId, 192 targetScope, 193 type, 194 locationType, 195 declaration, 196 state 197 ) { 198 if (isNode(declaratorId, "Identifier")) { 199 let existing = targetScope.bindings[declaratorId.name]; 200 if (!existing) { 201 existing = { 202 type, 203 refs: [], 204 }; 205 targetScope.bindings[declaratorId.name] = existing; 206 } 207 state.declarationBindingIds.add(declaratorId); 208 existing.refs.push({ 209 type: locationType, 210 start: fromBabelLocation(declaratorId.loc.start, state.sourceId), 211 end: fromBabelLocation(declaratorId.loc.end, state.sourceId), 212 declaration: { 213 start: fromBabelLocation(declaration.loc.start, state.sourceId), 214 end: fromBabelLocation(declaration.loc.end, state.sourceId), 215 }, 216 }); 217 } else if (isNode(declaratorId, "ObjectPattern")) { 218 declaratorId.properties.forEach(prop => { 219 parseDeclarator( 220 prop.value, 221 targetScope, 222 type, 223 locationType, 224 declaration, 225 state 226 ); 227 }); 228 } else if (isNode(declaratorId, "ArrayPattern")) { 229 declaratorId.elements.forEach(item => { 230 parseDeclarator( 231 item, 232 targetScope, 233 type, 234 locationType, 235 declaration, 236 state 237 ); 238 }); 239 } else if (isNode(declaratorId, "AssignmentPattern")) { 240 parseDeclarator( 241 declaratorId.left, 242 targetScope, 243 type, 244 locationType, 245 declaration, 246 state 247 ); 248 } else if (isNode(declaratorId, "RestElement")) { 249 parseDeclarator( 250 declaratorId.argument, 251 targetScope, 252 type, 253 locationType, 254 declaration, 255 state 256 ); 257 } else if (t.isTSParameterProperty(declaratorId)) { 258 parseDeclarator( 259 declaratorId.parameter, 260 targetScope, 261 type, 262 locationType, 263 declaration, 264 state 265 ); 266 } 267 } 268 269 function isLetOrConst(node) { 270 return node.kind === "let" || node.kind === "const"; 271 } 272 273 function hasLexicalDeclaration(node, parent) { 274 const nodes = []; 275 if (t.isSwitchStatement(node)) { 276 for (const caseNode of node.cases) { 277 nodes.push(...caseNode.consequent); 278 } 279 } else { 280 nodes.push(...node.body); 281 } 282 283 const isFunctionBody = t.isFunction(parent, { body: node }); 284 285 return nodes.some( 286 child => 287 isLexicalVariable(child) || 288 t.isClassDeclaration(child) || 289 (!isFunctionBody && t.isFunctionDeclaration(child)) 290 ); 291 } 292 function isLexicalVariable(node) { 293 return isNode(node, "VariableDeclaration") && isLetOrConst(node); 294 } 295 296 // Creates the global scopes for this source, the overall global scope 297 // and a lexical global scope. 298 function createGlobalScope(ast, sourceId) { 299 const global = createTempScope("object", "Global", null, { 300 start: fromBabelLocation(ast.loc.start, sourceId), 301 end: fromBabelLocation(ast.loc.end, sourceId), 302 }); 303 304 const lexical = createTempScope("block", "Lexical Global", global, { 305 start: fromBabelLocation(ast.loc.start, sourceId), 306 end: fromBabelLocation(ast.loc.end, sourceId), 307 }); 308 309 return { global, lexical }; 310 } 311 312 const scopeCollectionVisitor = { 313 // eslint-disable-next-line complexity 314 enter(node, ancestors, state) { 315 state.scopeStack.push(state.scope); 316 317 const parentNode = 318 ancestors.length === 0 ? null : ancestors[ancestors.length - 1].node; 319 320 if (state.inType) { 321 return; 322 } 323 324 if (t.isProgram(node)) { 325 const scope = pushTempScope(state, "module", "Module", { 326 start: fromBabelLocation(node.loc.start, state.sourceId), 327 end: fromBabelLocation(node.loc.end, state.sourceId), 328 }); 329 scope.bindings.this = { 330 type: "implicit", 331 refs: [], 332 }; 333 } else if (t.isFunction(node)) { 334 let { scope } = state; 335 336 if (t.isFunctionExpression(node) && isNode(node.id, "Identifier")) { 337 scope = pushTempScope(state, "block", "Function Expression", { 338 start: fromBabelLocation(node.loc.start, state.sourceId), 339 end: fromBabelLocation(node.loc.end, state.sourceId), 340 }); 341 state.declarationBindingIds.add(node.id); 342 scope.bindings[node.id.name] = { 343 type: "const", 344 refs: [ 345 { 346 type: "fn-expr", 347 start: fromBabelLocation(node.id.loc.start, state.sourceId), 348 end: fromBabelLocation(node.id.loc.end, state.sourceId), 349 declaration: { 350 start: fromBabelLocation(node.loc.start, state.sourceId), 351 end: fromBabelLocation(node.loc.end, state.sourceId), 352 }, 353 }, 354 ], 355 }; 356 } 357 358 if (t.isFunctionDeclaration(node) && isNode(node.id, "Identifier")) { 359 // This ignores Annex B function declaration hoisting, which 360 // is probably a fine assumption. 361 state.declarationBindingIds.add(node.id); 362 const refs = [ 363 { 364 type: "fn-decl", 365 start: fromBabelLocation(node.id.loc.start, state.sourceId), 366 end: fromBabelLocation(node.id.loc.end, state.sourceId), 367 declaration: { 368 start: fromBabelLocation(node.loc.start, state.sourceId), 369 end: fromBabelLocation(node.loc.end, state.sourceId), 370 }, 371 }, 372 ]; 373 374 if (scope.type === "block") { 375 scope.bindings[node.id.name] = { 376 type: "let", 377 refs, 378 }; 379 } else { 380 // Add the binding to the ancestor scope 381 getVarScope(scope).bindings[node.id.name] = { 382 type: "var", 383 refs, 384 }; 385 } 386 } 387 388 scope = pushTempScope( 389 state, 390 "function", 391 getFunctionName(node, parentNode), 392 { 393 // Being at the start of a function doesn't count as 394 // being inside of it. 395 start: fromBabelLocation( 396 node.params[0] ? node.params[0].loc.start : node.loc.start, 397 state.sourceId 398 ), 399 end: fromBabelLocation(node.loc.end, state.sourceId), 400 } 401 ); 402 403 node.params.forEach(param => 404 parseDeclarator(param, scope, "var", "fn-param", node, state) 405 ); 406 407 if (!t.isArrowFunctionExpression(node)) { 408 scope.bindings.this = { 409 type: "implicit", 410 refs: [], 411 }; 412 scope.bindings.arguments = { 413 type: "implicit", 414 refs: [], 415 }; 416 } 417 418 if ( 419 t.isBlockStatement(node.body) && 420 hasLexicalDeclaration(node.body, node) 421 ) { 422 scope = pushTempScope(state, "function-body", "Function Body", { 423 start: fromBabelLocation(node.body.loc.start, state.sourceId), 424 end: fromBabelLocation(node.body.loc.end, state.sourceId), 425 }); 426 } 427 } else if (t.isClass(node)) { 428 if (t.isIdentifier(node.id)) { 429 // For decorated classes, the AST considers the first the decorator 430 // to be the start of the class. For the purposes of mapping class 431 // declarations however, we really want to look for the "class Foo" 432 // piece. To achieve that, we estimate the location of the declaration 433 // instead. 434 let declStart = node.loc.start; 435 if (node.decorators && node.decorators.length) { 436 // Estimate the location of the "class" keyword since it 437 // is unlikely to be a different line than the class name. 438 declStart = { 439 line: node.id.loc.start.line, 440 column: node.id.loc.start.column - "class ".length, 441 }; 442 } 443 444 const declaration = { 445 start: fromBabelLocation(declStart, state.sourceId), 446 end: fromBabelLocation(node.loc.end, state.sourceId), 447 }; 448 449 if (t.isClassDeclaration(node)) { 450 state.declarationBindingIds.add(node.id); 451 state.scope.bindings[node.id.name] = { 452 type: "let", 453 refs: [ 454 { 455 type: "class-decl", 456 start: fromBabelLocation(node.id.loc.start, state.sourceId), 457 end: fromBabelLocation(node.id.loc.end, state.sourceId), 458 declaration, 459 }, 460 ], 461 }; 462 } 463 464 const scope = pushTempScope(state, "block", "Class", { 465 start: fromBabelLocation(node.loc.start, state.sourceId), 466 end: fromBabelLocation(node.loc.end, state.sourceId), 467 }); 468 469 state.declarationBindingIds.add(node.id); 470 scope.bindings[node.id.name] = { 471 type: "const", 472 refs: [ 473 { 474 type: "class-inner", 475 start: fromBabelLocation(node.id.loc.start, state.sourceId), 476 end: fromBabelLocation(node.id.loc.end, state.sourceId), 477 declaration, 478 }, 479 ], 480 }; 481 } 482 } else if (t.isForXStatement(node) || t.isForStatement(node)) { 483 const init = node.init || node.left; 484 if (isNode(init, "VariableDeclaration") && isLetOrConst(init)) { 485 // Debugger will create new lexical environment for the for. 486 pushTempScope(state, "block", "For", { 487 // Being at the start of a for loop doesn't count as 488 // being inside it. 489 start: fromBabelLocation(init.loc.start, state.sourceId), 490 end: fromBabelLocation(node.loc.end, state.sourceId), 491 }); 492 } 493 } else if (t.isCatchClause(node)) { 494 const scope = pushTempScope(state, "block", "Catch", { 495 start: fromBabelLocation(node.loc.start, state.sourceId), 496 end: fromBabelLocation(node.loc.end, state.sourceId), 497 }); 498 parseDeclarator(node.param, scope, "var", "catch", node, state); 499 } else if ( 500 t.isBlockStatement(node) && 501 // Function body's are handled in the function logic above. 502 !t.isFunction(parentNode) && 503 hasLexicalDeclaration(node, parentNode) 504 ) { 505 // Debugger will create new lexical environment for the block. 506 pushTempScope(state, "block", "Block", { 507 start: fromBabelLocation(node.loc.start, state.sourceId), 508 end: fromBabelLocation(node.loc.end, state.sourceId), 509 }); 510 } else if ( 511 t.isVariableDeclaration(node) && 512 (node.kind === "var" || 513 // Lexical declarations in for statements are handled above. 514 !t.isForStatement(parentNode, { init: node }) || 515 !t.isForXStatement(parentNode, { left: node })) 516 ) { 517 // Finds right lexical environment 518 const hoistAt = !isLetOrConst(node) 519 ? getVarScope(state.scope) 520 : state.scope; 521 node.declarations.forEach(declarator => { 522 parseDeclarator( 523 declarator.id, 524 hoistAt, 525 node.kind, 526 node.kind, 527 node, 528 state 529 ); 530 }); 531 } else if ( 532 t.isImportDeclaration(node) && 533 (!node.importKind || node.importKind === "value") 534 ) { 535 node.specifiers.forEach(spec => { 536 if (spec.importKind && spec.importKind !== "value") { 537 return; 538 } 539 540 if (t.isImportNamespaceSpecifier(spec)) { 541 state.declarationBindingIds.add(spec.local); 542 543 state.scope.bindings[spec.local.name] = { 544 // Imported namespaces aren't live import bindings, they are 545 // just normal const bindings. 546 type: "const", 547 refs: [ 548 { 549 type: "import-ns-decl", 550 start: fromBabelLocation(spec.local.loc.start, state.sourceId), 551 end: fromBabelLocation(spec.local.loc.end, state.sourceId), 552 declaration: { 553 start: fromBabelLocation(node.loc.start, state.sourceId), 554 end: fromBabelLocation(node.loc.end, state.sourceId), 555 }, 556 }, 557 ], 558 }; 559 } else { 560 state.declarationBindingIds.add(spec.local); 561 562 state.scope.bindings[spec.local.name] = { 563 type: "import", 564 refs: [ 565 { 566 type: "import-decl", 567 start: fromBabelLocation(spec.local.loc.start, state.sourceId), 568 end: fromBabelLocation(spec.local.loc.end, state.sourceId), 569 importName: t.isImportDefaultSpecifier(spec) 570 ? "default" 571 : spec.imported.name, 572 declaration: { 573 start: fromBabelLocation(node.loc.start, state.sourceId), 574 end: fromBabelLocation(node.loc.end, state.sourceId), 575 }, 576 }, 577 ], 578 }; 579 } 580 }); 581 } else if (t.isTSEnumDeclaration(node)) { 582 state.declarationBindingIds.add(node.id); 583 state.scope.bindings[node.id.name] = { 584 type: "const", 585 refs: [ 586 { 587 type: "ts-enum-decl", 588 start: fromBabelLocation(node.id.loc.start, state.sourceId), 589 end: fromBabelLocation(node.id.loc.end, state.sourceId), 590 declaration: { 591 start: fromBabelLocation(node.loc.start, state.sourceId), 592 end: fromBabelLocation(node.loc.end, state.sourceId), 593 }, 594 }, 595 ], 596 }; 597 } else if (t.isTSModuleDeclaration(node)) { 598 state.declarationBindingIds.add(node.id); 599 state.scope.bindings[node.id.name] = { 600 type: "const", 601 refs: [ 602 { 603 type: "ts-namespace-decl", 604 start: fromBabelLocation(node.id.loc.start, state.sourceId), 605 end: fromBabelLocation(node.id.loc.end, state.sourceId), 606 declaration: { 607 start: fromBabelLocation(node.loc.start, state.sourceId), 608 end: fromBabelLocation(node.loc.end, state.sourceId), 609 }, 610 }, 611 ], 612 }; 613 } else if (t.isTSModuleBlock(node)) { 614 pushTempScope(state, "block", "TypeScript Namespace", { 615 start: fromBabelLocation(node.loc.start, state.sourceId), 616 end: fromBabelLocation(node.loc.end, state.sourceId), 617 }); 618 } else if ( 619 t.isIdentifier(node) && 620 t.isReferenced(node, parentNode) && 621 // Babel doesn't cover this in 'isReferenced' yet, but it should 622 // eventually. 623 !t.isTSEnumMember(parentNode, { id: node }) && 624 !t.isTSModuleDeclaration(parentNode, { id: node }) && 625 // isReferenced above fails to see `var { foo } = ...` as a non-reference 626 // because the direct parent is not enough to know that the pattern is 627 // used within a variable declaration. 628 !state.declarationBindingIds.has(node) 629 ) { 630 let freeVariables = state.freeVariables.get(node.name); 631 if (!freeVariables) { 632 freeVariables = []; 633 state.freeVariables.set(node.name, freeVariables); 634 } 635 636 freeVariables.push({ 637 type: "ref", 638 start: fromBabelLocation(node.loc.start, state.sourceId), 639 end: fromBabelLocation(node.loc.end, state.sourceId), 640 meta: buildMetaBindings(state.sourceId, node, ancestors), 641 }); 642 } else if (isOpeningJSXIdentifier(node, ancestors)) { 643 let freeVariables = state.freeVariables.get(node.name); 644 if (!freeVariables) { 645 freeVariables = []; 646 state.freeVariables.set(node.name, freeVariables); 647 } 648 649 freeVariables.push({ 650 type: "ref", 651 start: fromBabelLocation(node.loc.start, state.sourceId), 652 end: fromBabelLocation(node.loc.end, state.sourceId), 653 meta: buildMetaBindings(state.sourceId, node, ancestors), 654 }); 655 } else if (t.isThisExpression(node)) { 656 let freeVariables = state.freeVariables.get("this"); 657 if (!freeVariables) { 658 freeVariables = []; 659 state.freeVariables.set("this", freeVariables); 660 } 661 662 freeVariables.push({ 663 type: "ref", 664 start: fromBabelLocation(node.loc.start, state.sourceId), 665 end: fromBabelLocation(node.loc.end, state.sourceId), 666 meta: buildMetaBindings(state.sourceId, node, ancestors), 667 }); 668 } else if (t.isClassProperty(parentNode, { value: node })) { 669 const scope = pushTempScope(state, "function", "Class Field", { 670 start: fromBabelLocation(node.loc.start, state.sourceId), 671 end: fromBabelLocation(node.loc.end, state.sourceId), 672 }); 673 scope.bindings.this = { 674 type: "implicit", 675 refs: [], 676 }; 677 scope.bindings.arguments = { 678 type: "implicit", 679 refs: [], 680 }; 681 } else if ( 682 t.isSwitchStatement(node) && 683 hasLexicalDeclaration(node, parentNode) 684 ) { 685 pushTempScope(state, "block", "Switch", { 686 start: fromBabelLocation(node.loc.start, state.sourceId), 687 end: fromBabelLocation(node.loc.end, state.sourceId), 688 }); 689 } 690 691 if ( 692 // In general Flow expressions are deleted, so they can't contain 693 // runtime bindings, but typecasts are the one exception there. 694 (t.isFlow(node) && !t.isTypeCastExpression(node)) || 695 // In general TS items are deleted, but TS has a few wrapper node 696 // types that can contain general JS expressions. 697 (node.type.startsWith("TS") && 698 !t.isTSTypeAssertion(node) && 699 !t.isTSAsExpression(node) && 700 !t.isTSNonNullExpression(node) && 701 !t.isTSModuleDeclaration(node) && 702 !t.isTSModuleBlock(node) && 703 !t.isTSParameterProperty(node) && 704 !t.isTSExportAssignment(node)) 705 ) { 706 // Flag this node as a root "type" node. All items inside of this 707 // will be skipped entirely. 708 state.inType = node; 709 } 710 }, 711 exit(node, ancestors, state) { 712 const currentScope = state.scope; 713 const parentScope = state.scopeStack.pop(); 714 if (!parentScope) { 715 throw new Error("Assertion failure - unsynchronized pop"); 716 } 717 state.scope = parentScope; 718 719 // It is possible, as in the case of function expressions, that a single 720 // node has added multiple scopes, so we need to traverse upward here 721 // rather than jumping stright to 'parentScope'. 722 for ( 723 let scope = currentScope; 724 scope && scope !== parentScope; 725 scope = scope.parent 726 ) { 727 const { freeVariables } = state; 728 state.freeVariables = state.freeVariableStack.pop(); 729 const parentFreeVariables = state.freeVariables; 730 731 // Match up any free variables that match this scope's bindings and 732 // merge then into the refs. 733 for (const key of Object.keys(scope.bindings)) { 734 const binding = scope.bindings[key]; 735 736 const freeVars = freeVariables.get(key); 737 if (freeVars) { 738 binding.refs.push(...freeVars); 739 freeVariables.delete(key); 740 } 741 } 742 743 // Move any undeclared references in this scope into the parent for 744 // processing in higher scopes. 745 for (const [key, value] of freeVariables) { 746 let refs = parentFreeVariables.get(key); 747 if (!refs) { 748 refs = []; 749 parentFreeVariables.set(key, refs); 750 } 751 752 refs.push(...value); 753 } 754 } 755 756 if (state.inType === node) { 757 state.inType = null; 758 } 759 }, 760 }; 761 762 function isOpeningJSXIdentifier(node, ancestors) { 763 if (!t.isJSXIdentifier(node)) { 764 return false; 765 } 766 767 for (let i = ancestors.length - 1; i >= 0; i--) { 768 const { node: parent, key } = ancestors[i]; 769 770 if (t.isJSXOpeningElement(parent) && key === "name") { 771 return true; 772 } else if (!t.isJSXMemberExpression(parent) || key !== "object") { 773 break; 774 } 775 } 776 777 return false; 778 } 779 780 function buildMetaBindings( 781 sourceId, 782 node, 783 ancestors, 784 parentIndex = ancestors.length - 1 785 ) { 786 if (parentIndex <= 1) { 787 return null; 788 } 789 const parent = ancestors[parentIndex].node; 790 const grandparent = ancestors[parentIndex - 1].node; 791 792 // Consider "0, foo" to be equivalent to "foo". 793 if ( 794 t.isSequenceExpression(parent) && 795 parent.expressions.length === 2 && 796 t.isNumericLiteral(parent.expressions[0]) && 797 parent.expressions[1] === node 798 ) { 799 let { start, end } = parent.loc; 800 801 if (t.isCallExpression(grandparent, { callee: parent })) { 802 // Attempt to expand the range around parentheses, e.g. 803 // (0, foo.bar)() 804 start = grandparent.loc.start; 805 end = Object.assign({}, end); 806 end.column += 1; 807 } 808 809 return { 810 type: "inherit", 811 start: fromBabelLocation(start, sourceId), 812 end: fromBabelLocation(end, sourceId), 813 parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1), 814 }; 815 } 816 817 // Consider "Object(foo)", and "__webpack_require__.i(foo)" to be 818 // equivalent to "foo" since they are essentially identity functions. 819 if ( 820 t.isCallExpression(parent) && 821 (t.isIdentifier(parent.callee, { name: "Object" }) || 822 (t.isMemberExpression(parent.callee, { computed: false }) && 823 t.isIdentifier(parent.callee.object, { name: "__webpack_require__" }) && 824 t.isIdentifier(parent.callee.property, { name: "i" }))) && 825 parent.arguments.length === 1 && 826 parent.arguments[0] === node 827 ) { 828 return { 829 type: "inherit", 830 start: fromBabelLocation(parent.loc.start, sourceId), 831 end: fromBabelLocation(parent.loc.end, sourceId), 832 parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1), 833 }; 834 } 835 836 if (t.isMemberExpression(parent, { object: node })) { 837 if (parent.computed) { 838 if (t.isStringLiteral(parent.property)) { 839 return { 840 type: "member", 841 start: fromBabelLocation(parent.loc.start, sourceId), 842 end: fromBabelLocation(parent.loc.end, sourceId), 843 property: parent.property.value, 844 parent: buildMetaBindings( 845 sourceId, 846 parent, 847 ancestors, 848 parentIndex - 1 849 ), 850 }; 851 } 852 } else { 853 return { 854 type: "member", 855 start: fromBabelLocation(parent.loc.start, sourceId), 856 end: fromBabelLocation(parent.loc.end, sourceId), 857 property: parent.property.name, 858 parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1), 859 }; 860 } 861 } 862 if ( 863 t.isCallExpression(parent, { callee: node }) && 864 !parent.arguments.length 865 ) { 866 return { 867 type: "call", 868 start: fromBabelLocation(parent.loc.start, sourceId), 869 end: fromBabelLocation(parent.loc.end, sourceId), 870 parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1), 871 }; 872 } 873 874 return null; 875 } 876 877 function looksLikeCommonJS(rootScope) { 878 const hasRefs = name => 879 rootScope.bindings[name] && !!rootScope.bindings[name].refs.length; 880 881 return ( 882 hasRefs("__dirname") || 883 hasRefs("__filename") || 884 hasRefs("require") || 885 hasRefs("exports") || 886 hasRefs("module") 887 ); 888 } 889 890 function stripModuleScope(rootScope) { 891 const rootLexicalScope = rootScope.children[0]; 892 const moduleScope = rootLexicalScope.children[0]; 893 if (moduleScope.type !== "module") { 894 throw new Error("Assertion failure - should be module"); 895 } 896 897 Object.keys(moduleScope.bindings).forEach(name => { 898 const binding = moduleScope.bindings[name]; 899 if (binding.type === "let" || binding.type === "const") { 900 rootLexicalScope.bindings[name] = binding; 901 } else { 902 rootScope.bindings[name] = binding; 903 } 904 }); 905 rootLexicalScope.children = moduleScope.children; 906 rootLexicalScope.children.forEach(child => { 907 child.parent = rootLexicalScope; 908 }); 909 }