diff --git a/lib/modules.nix b/lib/modules.nix index d8ae497fb2d8..1e8ba3471dd0 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -467,7 +467,9 @@ rec { disabledModules = m.disabledModules or []; imports = m.require or [] ++ m.imports or []; options = {}; - config = addFreeformType (addMeta (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"])); + config = + lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module." + addFreeformType (addMeta (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"])); }; applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then diff --git a/lib/types.nix b/lib/types.nix index f5d13ea10d28..68dfa5843dee 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -540,11 +540,31 @@ rec { }; # A module to be imported in some other part of the configuration. - deferredModule = mkOptionType { + deferredModule = deferredModuleWith { }; + + # A module to be imported in some other part of the configuration. + # `staticModules`' options will be added to the documentation, unlike + # options declared via `config`. + deferredModuleWith = attrs@{ staticModules ? [] }: mkOptionType { name = "deferredModule"; description = "module"; check = x: isAttrs x || isFunction x || path.check x; - merge = loc: defs: map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs; + merge = loc: defs: staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs; + inherit (submoduleWith { modules = staticModules; }) + getSubOptions + getSubModules; + substSubModules = m: deferredModuleWith (attrs // { + staticModules = m; + }); + functor = defaultFunctor "deferredModuleWith" // { + type = types.deferredModuleWith; + payload = { + inherit staticModules; + }; + binOp = lhs: rhs: { + staticModules = lhs.staticModules ++ rhs.staticModules; + }; + }; }; # The type of a type!