{ lib, config, pkgs, ... }: let inherit (lib) mkOption mkEnableOption mkIf types importJSON mapAttrs' mapAttrsToList removePrefix pathIsDirectory hasSuffix hasPrefix attrNames filter head ; inherit (lib.strings) sanitizeDerivationName ; yaml = pkgs.formats.yaml { }; json = pkgs.formats.json { }; cfg = config.services.extranix; module-eval = module-name: module: let ignored-eval = lib.evalModules { modules = module.ignored-modules; inherit (module) specialArgs; }; ignored-opts-doc = pkgs.nixosOptionsDoc { inherit (ignored-eval) options; }; ignored-opts = importJSON "${ignored-opts-doc.optionsJSON}/share/doc/nixos/options.json"; eval = lib.evalModules { modules = module.paths ++ module.ignored-modules; inherit (module) specialArgs; }; opts-doc = pkgs.nixosOptionsDoc { inherit (eval) options; }; opts = importJSON "${opts-doc.optionsJSON}/share/doc/nixos/options.json"; filtered-opts = removeAttrs opts (attrNames ignored-opts); path-translation = let translations = map ( { base, url }: { url = "${url}${if hasSuffix "/" url then "" else "/"}"; base = let base1 = toString base; in base1 + (if hasSuffix "/" base1 then "" else "/"); } ) module.path-translations; in path: let fullPath = path + (if pathIsDirectory path then "/default.nix" else ""); fitting = filter ({ base, ... }: hasPrefix base fullPath) translations; translate-info = head ( fitting ++ [ (throw "${fullPath} is not in any base path of ${module-name}") ] ); innerPath = removePrefix translate-info.base fullPath; in { name = "<${innerPath}>"; url = "${translate-info.url}${innerPath}"; }; result = json.generate "options-extranix.json" { last_update = "-/-"; options = mapAttrsToList (title: val: { inherit title; inherit (val) type readOnly description loc ; example = val.example.text or ""; default = val.default.text or ""; declarations = map path-translation val.declarations; }) filtered-opts; }; in result; options-files = mapAttrs' (name: value: { name = sanitizeDerivationName name; value = module-eval name value; }) cfg.modules; webroot = pkgs.callPackage ./webroot.nix { inherit options-files; inherit (cfg) static-data; settings = yaml.generate "config.yaml" cfg.settings; hugo-theme-extranix-options-search = pkgs.callPackage ./hugo-theme-extranix-options-search.nix { }; }; in { options.services.extranix = { enable = mkEnableOption "extranix documentation"; modules = mkOption { type = let module-mod.options = { specialArgs = mkOption { type = types.attrs; default = { }; }; paths = mkOption { type = types.listOf types.path; }; ignored-modules = mkOption { type = types.listOf types.deferredModule; default = [ ]; }; path-translations = mkOption { type = let path-mod.options = { base = mkOption { type = types.path; }; url = mkOption { type = types.str; }; }; in types.listOf (types.submodule path-mod); }; }; in types.attrsOf (types.submodule module-mod); }; settings = mkOption { type = yaml.type; }; static-data = mkOption { type = types.path; }; host = mkOption { type = types.str; }; }; config = mkIf cfg.enable { services = { extranix = { settings = { markup.goldmark.renderer.unsafe = true; theme = "extranix-options-search"; params.releases = mapAttrsToList (name: _: { inherit name; value = sanitizeDerivationName name; }) cfg.modules; }; }; nginx = { enable = true; virtualHosts.${cfg.host}.locations."/".alias = "${webroot}/"; }; }; }; }