Resolve a sibling-file import: a bare path/name (no leading "./") that names * a file relative to the importer's directory. This covers build/markup * grammars whose import string is a sibling filename or directory rather than a * dotted module path: * - SCSS `@use 'vars'` → sibling `_vars.scss` (partial underscore) * - Just `import 'common.just'` → sibling `common.jus
| 1698 | * File/Module-node QN (extension is stripped by fqn_module). Returns a borrowed |
| 1699 | * node or NULL. Several filename conventions are tried in turn. */ |
| 1700 | static const cbm_gbuf_node_t *resolve_sibling_file(const cbm_pipeline_ctx_t *ctx, |
| 1701 | const char *source_rel, |
| 1702 | const char *source_file_qn, |
| 1703 | const char *module_path) { |
| 1704 | if (!module_path || !module_path[0]) { |
| 1705 | return NULL; |
| 1706 | } |
| 1707 | /* Directory of the importing file (empty for repo-root files). */ |
| 1708 | char *dir = path_dirname(source_rel ? source_rel : ""); |
| 1709 | if (!dir) { |
| 1710 | return NULL; |
| 1711 | } |
| 1712 | |
| 1713 | /* Candidate relative paths, in priority order. */ |
| 1714 | char cands[5][PKGMAP_PATH_BUF]; |
| 1715 | int ncand = 0; |
| 1716 | const char *base = module_path; |
| 1717 | /* Skip a leading "./". */ |
| 1718 | if (base[0] == '.' && base[1] == '/') { |
| 1719 | base += 2; |
| 1720 | } |
| 1721 | /* 1. Direct sibling: dir/<module_path>. */ |
| 1722 | snprintf(cands[ncand++], PKGMAP_PATH_BUF, "%s%s%s", dir, dir[0] ? "/" : "", base); |
| 1723 | /* 2. SCSS partial: dir/[subdir/]_<basename>.scss (underscore-prefixed). */ |
| 1724 | { |
| 1725 | const char *slash = strrchr(base, '/'); |
| 1726 | const char *bn = slash ? slash + 1 : base; |
| 1727 | char *dpart = slash ? cbm_strndup(base, (size_t)(slash - base)) : strdup(""); |
| 1728 | if (dpart && bn[0] != '_') { |
| 1729 | snprintf(cands[ncand++], PKGMAP_PATH_BUF, "%s%s%s%s_%s.scss", dir, dir[0] ? "/" : "", |
| 1730 | dpart[0] ? dpart : "", dpart[0] ? "/" : "", bn); |
| 1731 | } |
| 1732 | free(dpart); |
| 1733 | } |
| 1734 | /* 3. Meson subdir: dir/<module_path>/meson.build. */ |
| 1735 | snprintf(cands[ncand++], PKGMAP_PATH_BUF, "%s%s%s/meson.build", dir, dir[0] ? "/" : "", base); |
| 1736 | /* 4. Basename sibling: dir/<basename(module_path)>. Covers include paths |
| 1737 | * that carry a non-relative prefix (Hyprlang `source = ~/.config/.../x.conf`, |
| 1738 | * absolute include paths) but reference a file sitting beside the importer. */ |
| 1739 | { |
| 1740 | const char *slash = strrchr(base, '/'); |
| 1741 | if (slash && slash[1]) { |
| 1742 | snprintf(cands[ncand++], PKGMAP_PATH_BUF, "%s%s%s", dir, dir[0] ? "/" : "", slash + 1); |
| 1743 | } |
| 1744 | } |
| 1745 | |
| 1746 | const cbm_gbuf_node_t *found = NULL; |
| 1747 | for (int i = 0; i < ncand; i++) { |
| 1748 | char *qn = cbm_pipeline_fqn_module(ctx->project_name, cands[i]); |
| 1749 | if (!qn) { |
| 1750 | continue; |
| 1751 | } |
| 1752 | const cbm_gbuf_node_t *n = cbm_gbuf_find_by_qn(ctx->gbuf, qn); |
| 1753 | free(qn); |
| 1754 | if (n && import_targetable_label(n->label) && |
| 1755 | (!source_file_qn || !n->qualified_name || |
| 1756 | strcmp(n->qualified_name, source_file_qn) != 0)) { |
| 1757 | found = n; |
no test coverage detected