Skip to content

Improve static build#2117

Merged
mpscholten merged 3 commits intomasterfrom
improve-static-build
Sep 11, 2025
Merged

Improve static build#2117
mpscholten merged 3 commits intomasterfrom
improve-static-build

Conversation

@mpscholten
Copy link
Copy Markdown
Member

Example: App that has a static/Frontend directory that contains an esbuild bundled JS app.

{
    inputs = {
        ihp.url = "path:///Users/marc/digitallyinduced/ihp-pro";
        ihp.inputs.nixpkgs.url = "github:mpscholten/nixpkgs/fix-sitemap2";
        nixpkgs.follows = "ihp/nixpkgs";
        flake-parts.follows = "ihp/flake-parts";
        devenv.follows = "ihp/devenv";
        systems.follows = "ihp/systems";
    };

    outputs = inputs@{ ihp, flake-parts, systems, self, ... }:
        flake-parts.lib.mkFlake { inherit inputs; } {

            systems = import systems;
            imports = [ ihp.flakeModules.default ];

            perSystem = { pkgs, system, config, ... }: {
                ihp = {
                    # ...

                    static.extraDirs.Frontend = self.packages.${system}.frontend;
                    static.makeBundling = false;
                };


                packages.frontend =
                    let
                        node-modules = pkgs.mkYarnModules {
                            pname = "${config.ihp.appName}-frontend-deps";
                            packageJSON = ./Frontend/package.json;
                            yarnLock = ./Frontend/yarn.lock;
                            version = "1.0.0";
                        };
                        filter = ihp.inputs.nix-filter.lib;
                    in pkgs.stdenv.mkDerivation {
                        name = "${config.ihp.appName}-frontend";
                        src = filter {
                            root = ./Frontend;
                            include = ["src" "lib" "hooks" "components" "types" (filter.matchExt "js") (filter.matchExt "ts") (filter.matchExt "tsx") (filter.matchExt "json") (filter.matchExt "css")];
                            exclude = ["node_modules"];
                        };
                        nativeBuildInputs = [pkgs.yarn node-modules pkgs.esbuild];
                        buildPhase = ''
                        ln -s ${node-modules}/node_modules ./node_modules
                        export PATH="node_modules/bin:$PATH"

                        mkdir -p ihp-node_modules
                        ln -s ${ihp}/ihp/data/DataSync ihp-node_modules/ihp-datasync

                        ${node-modules}/node_modules/.bin/tailwindcss -i ./src/styles/globals.css -o ./src/tailwind.css

                        NODE_PATH=ihp-node_modules:node_modules ${pkgs.esbuild}/bin/esbuild src/index.tsx \
                            --preserve-symlinks \
                            --bundle \
                            --loader:.woff=file \
                            --loader:.woff2=file \
                            --loader:.ttf=file \
                            --loader:.svg=file \
                            --loader:.png=file \
                            --loader:.gif=file \
                            --main-fields=module,main \
                            --define:global=globalThis \
                            --define:process.env.NODE_ENV=\"production\" \
                            --minify \
                            --legal-comments=none \
                            --outfile=$out/main.js
                        '';
                        allowedReferences = [];
                    };
            };

        };
}

Running nix build will now produce an app with a static/Frontend directory. It will internally call nix build .#frontend as specified by static.extraDirs.Frontend = self.packages.${system}.frontend.

With static.makeBundling = false we can skip the make static/app.css step done typically by IHP. This can speed up deployment a bit when it's not used anyways.

@mpscholten
Copy link
Copy Markdown
Member Author

Quick idea: We could remove the hardcoded bootstrap-5.2.1 in the IHP repository with this feature.

E.g. we could drop something like this into the ihp-boilerplate:

static.extraDirs."/vendor/bootstrap-5.2.1" = pkgs.twitterBootstrap;

@mpscholten
Copy link
Copy Markdown
Member Author

Another cool use of this API: adding the Inter font to my app, without google fonts:

                    static.extraDirs.Inter = pkgs.inter.overrideAttrs (old: {
                        postInstall = ''
                            mkdir -p $out
                            cp -r web/* $out/
                        '';
                    });
# <link href="/Inter/inter.css" rel="stylesheet">

@amitaibu
Copy link
Copy Markdown
Collaborator

amitaibu commented Sep 2, 2025

This could also be used for building Tailwind?

@mpscholten
Copy link
Copy Markdown
Member Author

Yes, exactly. The first code example actually calls tailwind:

                packages.frontend =
                    let
                        node-modules = pkgs.mkYarnModules {
                            pname = "${config.ihp.appName}-frontend-deps";
                            packageJSON = ./Frontend/package.json;
                            yarnLock = ./Frontend/yarn.lock;
                            version = "1.0.0";
                        };
                        filter = ihp.inputs.nix-filter.lib;
                    in pkgs.stdenv.mkDerivation {
                        name = "${config.ihp.appName}-frontend";
                        src = filter {
                            root = ./Frontend;
                            include = ["src" "lib" "hooks" "components" "types" (filter.matchExt "js") (filter.matchExt "ts") (filter.matchExt "tsx") (filter.matchExt "json") (filter.matchExt "css")];
                            exclude = ["node_modules"];
                        };
                        nativeBuildInputs = [pkgs.yarn node-modules pkgs.esbuild];
                        buildPhase = ''
                        ln -s ${node-modules}/node_modules ./node_modules
                        export PATH="node_modules/bin:$PATH"

                        mkdir -p ihp-node_modules
                        ln -s ${ihp}/ihp/data/DataSync ihp-node_modules/ihp-datasync

                        ${node-modules}/node_modules/.bin/tailwindcss -i ./src/styles/globals.css -o ./src/tailwind.css

                        NODE_PATH=ihp-node_modules:node_modules ${pkgs.esbuild}/bin/esbuild src/index.tsx \
                            --preserve-symlinks \
                            --bundle \
                            --loader:.woff=file \
                            --loader:.woff2=file \
                            --loader:.ttf=file \
                            --loader:.svg=file \
                            --loader:.png=file \
                            --loader:.gif=file \
                            --main-fields=module,main \
                            --define:global=globalThis \
                            --define:process.env.NODE_ENV=\"production\" \
                            --minify \
                            --legal-comments=none \
                            --outfile=$out/main.js
                        '';
                        allowedReferences = [];
                    };

So we'd only need to remove the esbuild stuff around it and it works for tailwind 👍

@mpscholten mpscholten merged commit 80c5423 into master Sep 11, 2025
1 check failed
@mpscholten mpscholten deleted the improve-static-build branch September 11, 2025 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants