Severity: Low
Summary
Found by code review on PR #2954 (#2865). jq has two import forms:
import "m" as m; — a module import, binding a namespace of defs (m::f)
import "f" as $d; — a data import, reading f.json and binding its
parsed contents to $d (jq wraps it in an array: [{"z":9}])
succinctly implements only the first. Parser::parse_import reads and then
discards the $, so Import carries no way to tell the two apart, and
ModuleLoader resolves every import as a module — a data import fails with
module not found.
Repro
Confirmed live against jq 1.7.1 and succinctly on main (pre-dates #2865).
$ cat data.json
{"z":9}
$ jq -L . -nc 'import "data" as $d; $d'
[{"z":9}]
$ succinctly jq -L . -nc 'import "data" as $d; $d'
jq: error: module not found: data
Note jq resolves data.json, not data.jq — the .jq-suffix rule (#2702)
does not apply to a data import, and jq wraps the file's contents in an array.
Root cause
src/jq/parser.rs, parse_import:
let alias = if self.peek() == Some('$') {
self.next();
self.parse_ident()?
} else {
self.parse_ident()?
};
Both arms produce the same bare identifier; Import { path, alias, metadata }
(src/jq/expr.rs) has no field recording which spelling it was. Downstream,
ModuleLoader::process_program calls load_module for every entry in
program.imports.
Suggested fix direction
- Add a flag to
Import (e.g. data: bool) set by the $ branch of
parse_import. One construction site, so the change is contained.
- In
ModuleLoader, route a data import to a JSON read rather than a module
load: resolve <path>.json against the same search path, parse it, wrap it
in a one-element array, and bind it as the named $ variable alongside the
--arg/--argjson bindings run_jq already assembles.
- Confirm the details against the oracle first — the
.json suffix rule, the
array wrapping, what happens when the file is missing or is not valid JSON,
and whether $__loc__/metadata interact.
Why this is separate from #2865
#2865 is transitive include/import processing; it neither introduced nor
fixed this. It does make a module declaring a data import reachable, and
handles that by skipping unresolvable imports in the transitive path (a module
declaring one was harmless before, so making it fatal would have been a
regression) — ModuleLoader::module_resolves. That skip is a
regression-avoidance measure, not data-import support, and should be removed
once this lands.
Severity: Low
Summary
Found by code review on PR #2954 (#2865). jq has two
importforms:import "m" as m;— a module import, binding a namespace of defs (m::f)import "f" as $d;— a data import, readingf.jsonand binding itsparsed contents to
$d(jq wraps it in an array:[{"z":9}])succinctly implements only the first.
Parser::parse_importreads and thendiscards the
$, soImportcarries no way to tell the two apart, andModuleLoaderresolves every import as a module — a data import fails withmodule not found.Repro
Confirmed live against jq 1.7.1 and
succinctlyonmain(pre-dates #2865).Note jq resolves
data.json, notdata.jq— the.jq-suffix rule (#2702)does not apply to a data import, and jq wraps the file's contents in an array.
Root cause
src/jq/parser.rs,parse_import:Both arms produce the same bare identifier;
Import { path, alias, metadata }(
src/jq/expr.rs) has no field recording which spelling it was. Downstream,ModuleLoader::process_programcallsload_modulefor every entry inprogram.imports.Suggested fix direction
Import(e.g.data: bool) set by the$branch ofparse_import. One construction site, so the change is contained.ModuleLoader, route a data import to a JSON read rather than a moduleload: resolve
<path>.jsonagainst the same search path, parse it, wrap itin a one-element array, and bind it as the named
$variable alongside the--arg/--argjsonbindingsrun_jqalready assembles..jsonsuffix rule, thearray wrapping, what happens when the file is missing or is not valid JSON,
and whether
$__loc__/metadata interact.Why this is separate from #2865
#2865 is transitive
include/importprocessing; it neither introduced norfixed this. It does make a module declaring a data import reachable, and
handles that by skipping unresolvable imports in the transitive path (a module
declaring one was harmless before, so making it fatal would have been a
regression) —
ModuleLoader::module_resolves. That skip is aregression-avoidance measure, not data-import support, and should be removed
once this lands.