From b56a8fd8edd320fd4c3c855e75431f6d090ab491 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Wed, 26 Aug 2026 17:00:49 +0200 Subject: [PATCH 1/2] feat: improve scopedImport with _file attribute and `scoped` in builtins also properly override builtins.nixPath in addition to __nixPath. --- default.nix | 32 +++++++++++++++------ docs/src/content/docs/reference/api.mdx | 13 ++++++++- tests.nix | 38 +++++++++++++++++++++++++ tree/_scoped/foo.nix | 3 ++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/default.nix b/default.nix index 0526e7d..4b3250c 100644 --- a/default.nix +++ b/default.nix @@ -23,16 +23,32 @@ let files = leaves path; in { - imports = if scoped == { } then files else map scoped-import files; + imports = if scoped == { } then files else map scoped-import-module files; }; - scoped-import = builtins.scopedImport ( - { - inherit builtins; - __nixPath = [ ]; - } - // scoped - ); + # Like builtins.scopedImport but: + # - include scope argument in builtins as `builtins.scoped`, for potential reuse in nested invocations + # - empty `__nixPath` and `builtins.nixPath` + scoped-import = + scoped: + builtins.scopedImport ( + { + builtins = builtins // { + nixPath = [ ]; + inherit scoped; + }; + __nixPath = [ ]; + } + // scoped + ); + + scoped-import-module = file: { + # Let's not lose track of the original file even if scope-imported: + _file = file; + imports = [ + (scoped-import scoped file) + ]; + }; leaves = let diff --git a/docs/src/content/docs/reference/api.mdx b/docs/src/content/docs/reference/api.mdx index d545957..b62a003 100644 --- a/docs/src/content/docs/reference/api.mdx +++ b/docs/src/content/docs/reference/api.mdx @@ -169,10 +169,21 @@ The provided attributes are merged into the scope used by `builtins.scopedImport Multiple `.addScoped` calls accumulate: ```nix -import-tree (it: it.addScoped { foo = 42 }) (it: it.addScoped { bar = 99 }) +import-tree (it: it.addScoped { foo = 42 }) (it: it.addScoped { bar = 99 }) ./modules # modules can access both foo and bar ``` +Scoped variables can be accessed inside scoped-imported modules as `builtins.scoped`: + +```nix +# module.nix +{ + imports = [ (import-tree.addScoped (builtins.scoped // { bar = foo; }) ./other-modules) ]; + # other-modules can now access `bar` directly in addition to `foo` and `mylib` +} + +``` + --- ## Output diff --git a/tests.nix b/tests.nix index 5149660..709335e 100644 --- a/tests.nix +++ b/tests.nix @@ -270,6 +270,44 @@ in expected = 22; }; + scoped."test access all scoped variable via scopedImport" = { + expr = + (lib.evalModules { + modules = [ + ( + (lit.addScoped { + foo = 22; + bar = "44"; + }) + ./tree/_scoped + ) + ]; + }).config.scoped; + expected = { + foo = 22; + bar = "44"; + }; + }; + + scoped."test builtins.nixPath and __nixPath are empty via scopedImport" = { + expr = + let + eval = lib.evalModules { + modules = [ + ( + (lit.addScoped { + foo = 22; + bar = "44"; + }) + ./tree/_scoped + ) + ]; + }; + in + eval.config.nixPath == [ ] && eval.config.__nixPath == [ ]; + expected = true; + }; + combinator."test combinator syntax to compose import-tree" = { expr = it (it: it.withLib lib) (it: it.leaves) ./tree/_scoped; expected = [ ./tree/_scoped/foo.nix ]; diff --git a/tree/_scoped/foo.nix b/tree/_scoped/foo.nix index 8badecc..b4d7ba5 100644 --- a/tree/_scoped/foo.nix +++ b/tree/_scoped/foo.nix @@ -1,4 +1,7 @@ { lib, ... }: { options.foo = lib.mkOption { default = foo; }; + options.scoped = lib.mkOption { default = builtins.scoped; }; + options.__nixPath = lib.mkOption { default = __nixPath; }; + options.nixPath = lib.mkOption { default = builtins.nixPath; }; } From ef94a804372226605cb132432214888b95fa4577 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Tue, 1 Sep 2026 13:05:27 +0200 Subject: [PATCH 2/2] fix: make sure builtins.scoped and builtins.nixPath are properly set even if scope include a builtins attribute set. Also allows to explicitly set nixPath via scope. --- default.nix | 18 +++++++++++++----- tests.nix | 38 ++++++++++++++++++++++++++++++++++++-- tree/_scoped/foo.nix | 3 +-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/default.nix b/default.nix index 4b3250c..b2c4820 100644 --- a/default.nix +++ b/default.nix @@ -28,18 +28,26 @@ let # Like builtins.scopedImport but: # - include scope argument in builtins as `builtins.scoped`, for potential reuse in nested invocations - # - empty `__nixPath` and `builtins.nixPath` + # - empty `__nixPath` and `builtins.nixPath` (unless explictely set in `scoped` argument) scoped-import = scoped: builtins.scopedImport ( { - builtins = builtins // { - nixPath = [ ]; - inherit scoped; - }; __nixPath = [ ]; } // scoped + // { + builtins = + scoped.builtins or ( + builtins + // { + nixPath = [ ]; + } + ) + // { + inherit scoped; + }; + } ); scoped-import-module = file: { diff --git a/tests.nix b/tests.nix index 709335e..2429572 100644 --- a/tests.nix +++ b/tests.nix @@ -278,14 +278,48 @@ in (lit.addScoped { foo = 22; bar = "44"; + builtins = { + hello = "world"; + }; }) ./tree/_scoped ) ]; - }).config.scoped; + }).config.builtins.scoped; expected = { foo = 22; bar = "44"; + builtins = { + hello = "world"; + }; + }; + }; + + scoped."test settings builtins via scopedImport" = { + expr = + (lib.evalModules { + modules = [ + ( + (lit.addScoped { + foo = 22; + bar = "44"; + builtins = { + hello = "world"; + }; + }) + ./tree/_scoped + ) + ]; + }).config.builtins; + expected = { + hello = "world"; + scoped = { + foo = 22; + bar = "44"; + builtins = { + hello = "world"; + }; + }; }; }; @@ -304,7 +338,7 @@ in ]; }; in - eval.config.nixPath == [ ] && eval.config.__nixPath == [ ]; + eval.config.builtins.nixPath == [ ] && eval.config.__nixPath == [ ]; expected = true; }; diff --git a/tree/_scoped/foo.nix b/tree/_scoped/foo.nix index b4d7ba5..08175a5 100644 --- a/tree/_scoped/foo.nix +++ b/tree/_scoped/foo.nix @@ -1,7 +1,6 @@ { lib, ... }: { options.foo = lib.mkOption { default = foo; }; - options.scoped = lib.mkOption { default = builtins.scoped; }; options.__nixPath = lib.mkOption { default = __nixPath; }; - options.nixPath = lib.mkOption { default = builtins.nixPath; }; + options.builtins = lib.mkOption { default = builtins; }; }