Handle CommonJS `require("path")` call_expression nodes. The local name * is derived from the enclosing variable_declarator / assignment when * possible (so `const foo = require('./foo')` emits local_name="foo"), * otherwise falls back to the last path component. */
| 443 | * possible (so `const foo = require('./foo')` emits local_name="foo"), |
| 444 | * otherwise falls back to the last path component. */ |
| 445 | static bool process_commonjs_require(CBMExtractCtx *ctx, TSNode call) { |
| 446 | CBMArena *a = ctx->arena; |
| 447 | if (ts_node_child_count(call) < MIN_WOLFRAM_CHILDREN) { |
| 448 | return false; |
| 449 | } |
| 450 | /* Callee must be the identifier "require". */ |
| 451 | TSNode fn = ts_node_child_by_field_name(call, TS_FIELD("function")); |
| 452 | if (ts_node_is_null(fn)) { |
| 453 | fn = ts_node_child(call, 0); |
| 454 | } |
| 455 | if (ts_node_is_null(fn)) { |
| 456 | return false; |
| 457 | } |
| 458 | const char *fn_kind = ts_node_type(fn); |
| 459 | if (strcmp(fn_kind, "identifier") != 0) { |
| 460 | return false; |
| 461 | } |
| 462 | char *fn_name = cbm_node_text(a, fn, ctx->source); |
| 463 | if (!fn_name || strcmp(fn_name, "require") != 0) { |
| 464 | return false; |
| 465 | } |
| 466 | |
| 467 | /* First string literal child of the argument list is the module path. */ |
| 468 | TSNode args = ts_node_child_by_field_name(call, TS_FIELD("arguments")); |
| 469 | if (ts_node_is_null(args)) { |
| 470 | return false; |
| 471 | } |
| 472 | uint32_t argc = ts_node_named_child_count(args); |
| 473 | char *path = NULL; |
| 474 | for (uint32_t i = 0; i < argc; i++) { |
| 475 | TSNode arg = ts_node_named_child(args, i); |
| 476 | const char *ak = ts_node_type(arg); |
| 477 | if (strcmp(ak, "string") == 0 || strcmp(ak, "string_literal") == 0 || |
| 478 | strcmp(ak, "template_string") == 0) { |
| 479 | path = strip_quotes(a, cbm_node_text(a, arg, ctx->source)); |
| 480 | break; |
| 481 | } |
| 482 | } |
| 483 | if (!path || !path[0]) { |
| 484 | return false; |
| 485 | } |
| 486 | |
| 487 | /* Infer local name from enclosing variable_declarator. Tree-sitter's JS |
| 488 | * grammar wraps `const foo = require(..)` as |
| 489 | * lexical_declaration → variable_declarator { name: identifier, value: call } */ |
| 490 | const char *local_name = NULL; |
| 491 | TSNode parent = ts_node_parent(call); |
| 492 | if (!ts_node_is_null(parent) && strcmp(ts_node_type(parent), "variable_declarator") == 0) { |
| 493 | TSNode name_node = ts_node_child_by_field_name(parent, TS_FIELD("name")); |
| 494 | if (!ts_node_is_null(name_node) && strcmp(ts_node_type(name_node), "identifier") == 0) { |
| 495 | local_name = cbm_node_text(a, name_node, ctx->source); |
| 496 | } |
| 497 | } |
| 498 | if (!local_name) { |
| 499 | local_name = path_last(a, path); |
| 500 | } |
| 501 | |
| 502 | CBMImport imp = {.local_name = local_name, .module_path = path}; |
no test coverage detected