11.stylix/stylix/mk-target.nix
NAHO cb6bbed75e
stylix/mk-target: rename normalizeConfig function to normalize (#2081)
Rename the normalizeConfig function to normalize to generalize the name
beyond the specific config and options module arguments, following
commit 1272e6858e ("stylix/mk-target: rename mkConfig function to
callModule").

Fixes: 6153df31ce ("stylix/mk-target: normalize options argument identically to config")
Link: https://github.com/nix-community/stylix/pull/2081
2025-12-10 09:15:11 -08:00

353 lines
10 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
Provides a consistent target interface, minimizing boilerplate and
automatically safeguarding declarations related to disabled options.
# Type
```
mkTarget :: AttrSet -> ModuleBody
```
Where `ModuleBody` is a module that doesn't take any arguments. This allows
the caller to use module arguments.
# Examples
The `modules/«MODULE»/«PLATFORM».nix` modules should use this function as
follows:
```nix
{ mkTarget, lib... }:
mkTarget {
unconditionalConfig =
lib.mkIf complexCondition {
home.packages = [ pkgs.hello ];
};
config = [
{ programs.«name».theme.name = "stylix"; }
(
{ colors }:
{
programs.«name».theme.background = colors.base00;
}
)
(
{ fonts }:
{
programs.«name».font.name = fonts.monospace.name;
}
)
];
}
```
# Inputs
`config` (Attribute set)
: `autoEnable` (Boolean)
: Whether the target should be automatically enabled by default according
to the `stylix.autoEnable` option.
This should be disabled if manual setup is required or if auto-enabling
causes issues.
The default (`true`) is inherited from `mkEnableTargetWith`.
`autoEnableExpr` (String)
: A string representation of `autoEnable`, for use in documentation.
Not required if `autoEnable` is a literal `true` or `false`, but **must**
be used when `autoEnable` is a dynamic expression.
E.g. `"pkgs.stdenv.hostPlatform.isLinux"`.
`autoWrapEnableExpr` (Boolean)
: Whether to automatically wrap `autoEnableExpr` with parenthesis, when it
contains a potentially problematic infix.
The default (`true`) is inherited from `mkEnableTargetWith`.
`config` (List or attribute set or function or path)
: Configuration functions that are automatically safeguarded when any of
their arguments is disabled. The provided `cfg` argument conveniently
aliases to `config.stylix.targets.${name}`.
For example, the following configuration is not merged if the stylix
colors option is null:
```nix
(
{ colors }:
{
programs.«name».theme.background = colors.base00;
}
)
```
The `cfg` alias can be accessed as follows:
```nix
(
{ cfg }:
{
programs.«name».extension.enable = cfg.extension.enable;
}
)
```
`enableExample` (Boolean or literal expression)
: An example to include on the enable option. The default is calculated
automatically by `mkEnableTargetWith` and depends on `autoEnable` and
whether an `autoEnableExpr` is used.
`humanName` (String)
: The descriptive target name passed to the lib.mkEnableOption function
when generating the `stylix.targets.${name}.enable` option.
  `imports` (List)
  : The `imports` option forwarded to the Nixpkgs module system.
`options` (List or attribute set or function or path)
: Additional options to be added in the `stylix.targets.${name}` namespace,
normalized identically to `config`.
For example, an extension guard used in the configuration can be declared
as follows:
```nix
{ extension.enable = lib.mkEnableOption "the bloated dependency"; }
```
`name` (String)
: The target name used to generate options in the `stylix.targets.${name}`
namespace.
`unconditionalConfig` (Attribute set or function or path)
: This argument mirrors the `config` argument but intentionally lacks
automatic safeguarding and should only be used for complex configurations
where `config` is unsuitable.
# Environment
The function is provided alongside module arguments in any modules imported
through `/stylix/autoload.nix`.
*/
# TODO: Ideally, in the future, this function returns an actual module by better
# integrating with the /stylix/autoload.nix logic, allowing the following target
# simplification and preventing access to unguarded module arguments by
# requiring /modules/<MODULE>/<PLATFORM>.nix files to be attribute sets instead
# of modules:
#
# {
# unconditionalConfig =
# { lib, pkgs }:
# lib.mkIf complexCondition {
# home.packages = [ pkgs.hello ];
# };
#
# config = [
# { programs.example.theme.name = "stylix"; }
#
# (
# { colors }:
# {
# programs.example.theme.background = colors.base00;
# }
# )
#
# (
# { fonts }:
# {
# programs.example.font.name = fonts.monospace.name;
# }
# )
# ];
# }
{ humanName, name }:
let
humanName' = humanName;
name' = name;
in
{
autoEnable ? null,
autoEnableExpr ? null,
autoWrapEnableExpr ? null,
config ? [ ],
enableExample ? null,
humanName ? humanName',
imports ? [ ],
name ? name',
options ? [ ],
unconditionalConfig ? { },
}@args:
let
mkTargetConfig = config;
module =
{ config, lib, ... }:
let
callModule =
let
areArgumentsEnabled = lib.flip lib.pipe [
lib.attrsToList
(builtins.all (
{ name, value }: value.enable or (value != null) && cfg.${name}.enable or true
))
];
in
safeguard: config':
let
arguments = getArguments config';
in
if builtins.isFunction config' then
if safeguard then
lib.mkIf (areArgumentsEnabled arguments) (config' arguments)
else
config' arguments
else if builtins.isAttrs config' then
config'
else if builtins.isPath config' then
throw "stylix: unexpected unresolved path: ${toString config'}"
else
throw "stylix: mkTarget expected a configuration to be a function, an attribute set, or a path, but got ${builtins.typeOf config'}: ${
lib.generators.toPretty { } config'
}";
cfg = config.stylix.targets.${name};
getArguments =
function:
lib.genAttrs
(lib.pipe function [
lib.functionArgs
builtins.attrNames
])
(
argument:
if argument == "cfg" then
cfg
else
(
config':
let
inherit (cfg.${argument}) override;
in
if override == null then
config'
else if builtins.typeOf override != builtins.typeOf config' then
throw "stylix: expected `config.stylix.targets.${name}.${argument}.override` to be a ${builtins.typeOf config'}, but got: ${builtins.typeOf override}"
else if builtins.isAttrs override then
lib.recursiveUpdate config' override
else
override
)
(
if argument == "colors" then
config.lib.stylix.colors
else
config.stylix.${argument} or (throw "stylix: mkTarget expected one of ${
lib.concatMapStringsSep ", " (expected: "`${expected}`") (
lib.naturalSort (
[
"cfg"
"colors"
]
++ builtins.attrNames config.stylix
)
)
}, but got: ${argument}")
)
);
normalize =
config:
map (lib.fix (
self: config':
if builtins.isPath config' then self (import config') else config'
)) (lib.toList config);
normalizedConfig = normalize mkTargetConfig;
normalizedOptions = normalize options;
in
{
imports =
lib.singleton {
options.stylix.targets.${name} =
lib.genAttrs
(lib.concatLists (
map (lib.flip lib.pipe [
(
config': lib.optionalAttrs (builtins.isFunction config') (getArguments config')
)
builtins.attrNames
(lib.remove "cfg")
]) (normalizedConfig ++ normalizedOptions)
))
(
argument:
let
config = "`${
if argument == "colors" then
"config.lib.stylix.colors"
else
"config.stylix.${argument}"
}`";
in
{
enable = lib.mkEnableOption "${config} for ${humanName}" // {
default = true;
example = false;
};
override = lib.mkOption {
default = null;
description = ''
Attribute sets are recursively merged with ${config},
while all other non-`null` types override ${config}.
'';
type = lib.types.anything;
};
}
);
}
++ imports
++ map (option: {
options.stylix.targets.${name} = callModule false option;
}) normalizedOptions;
options.stylix.targets.${name}.enable =
let
enableArgs = {
name = humanName;
}
// lib.optionalAttrs (args ? autoEnable) { inherit autoEnable; }
// lib.optionalAttrs (args ? autoEnableExpr) { inherit autoEnableExpr; }
// lib.optionalAttrs (args ? autoWrapEnableExpr) {
autoWrapExpr = autoWrapEnableExpr;
}
// lib.optionalAttrs (args ? enableExample) { example = enableExample; };
in
config.lib.stylix.mkEnableTargetWith enableArgs;
config = lib.mkIf (config.stylix.enable && cfg.enable) (
lib.mkMerge (
lib.singleton (callModule false unconditionalConfig)
++ map (callModule true) normalizedConfig
)
);
};
in
{
imports = [ module ];
}