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