Rename the normalizeConfig function to normalize to generalize the name beyond the specific config and options module arguments, following commit1272e6858e("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
353 lines
10 KiB
Nix
353 lines
10 KiB
Nix
/**
|
||
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 ];
|
||
}
|