Subject: [bug#51838] [PATCH v7 05/41] guix: node-build-system: Add #:absent-dependencies argument.

From: Philip McGrath <[email-masked]>
Date: Thu, 30 Dec 2021 02:44:20 -0500

* guix/build-system/node.scm (lower, node-build): Add optional
argument #:absent-dependencies with default of ''().  Pass it on
to the build-side code.
* guix/build/node-build-system.scm (delete-dependencies): New
procedure.  Respect the #:absent-dependencies argument, removing
specified npm packages from the "dependencies" or "devDependencies"
tables in "package.json".
(%standard-phases): Add 'delete-dependencies after
'patch-dependencies.
---
 guix/build-system/node.scm       | 19 ++++++++++++++++++-
 guix/build/node-build-system.scm | 19 ++++++++++++++++++-
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/guix/build-system/node.scm b/guix/build-system/node.scm
index 24bd677bfc..47af4bb9e2 100644
--- a/guix/build-system/node.scm
+++ b/guix/build-system/node.scm
@@ -49,6 +49,7 @@ (define (default-node)
 (define* (lower name
                 #:key source inputs native-inputs outputs system target
                 (node (default-node))
+                (absent-dependencies ''())
                 #:allow-other-keys
                 #:rest arguments)
   "Return a bag for NAME."
@@ -84,6 +85,7 @@ (define* (node-build name inputs
                      (test-target "test")
                      (tests? #t)
                      (phases '%standard-phases)
+                     (absent-dependencies ''())
                      (outputs '("out"))
                      (search-paths '())
                      (system (%current-system))
@@ -91,7 +93,21 @@ (define* (node-build name inputs
                      (imported-modules %node-build-system-modules)
                      (modules '((guix build node-build-system)
                                 (guix build utils))))
-  "Build SOURCE using NODE and INPUTS."
+  "Build SOURCE using NODE and INPUTS.
+
+The builder will remove Node.js packages listed in ABSENT-DEPENCENCIES from
+the 'package.json' file's 'dependencies' and 'devDependencies' tables.  This
+mechanism can be used both avoid dependencies we don't want (e.g. optional
+features that would increase closure size) and to work around dependencies
+that haven't been packaged for Guix yet (e.g. test utilities)."
+  ;; Before #:absent-dependencies existed, this scenario was often handled by
+  ;; deleting the 'configure phase. Using #:absent-dependencies, instead,
+  ;; retains the check that no dependencies are silently missing and other
+  ;; actions performed by 'npm install', such as building native
+  ;; addons. Having an explicit list of absent dependencies in the package
+  ;; definition should also facilitate future maintenence: for example, if we
+  ;; add a package for a test framework, it should be easy to find all the
+  ;; other packages that use it and enable their tests.
   (define builder
     (with-imported-modules imported-modules
       #~(begin
@@ -103,6 +119,7 @@ (define builder
                       #:test-target #$test-target
                       #:tests? #$tests?
                       #:phases #$phases
+                      #:absent-dependencies #$absent-dependencies
                       #:outputs #$(outputs->gexp outputs)
                       #:search-paths '#$(sexp->gexp
                                          (map search-path-specification->sexp
diff --git a/guix/build/node-build-system.scm b/guix/build/node-build-system.scm
index dc8b6a41c2..e6b0811ca1 100644
--- a/guix/build/node-build-system.scm
+++ b/guix/build/node-build-system.scm
@@ -325,6 +325,22 @@ (define resolve-dependencies
                                deps))))))
   #t)
 
+(define* (delete-dependencies #:key absent-dependencies #:allow-other-keys)
+  "Modify 'package.json' to allow building without the ABSENT-DEPENDENCIES."
+  (define delete-fom-jsobject
+    (match-lambda
+      (('@ . alist)
+       (cons '@ (filter (match-lambda
+                          ((k . v)
+                           (not (member k absent-dependencies))))
+                        alist)))))
+  (with-atomic-json-file-replacement "package.json"
+    (lambda (pkg-meta)
+      (jsobject-update*
+       pkg-meta
+       "devDependencies" '(@) delete-fom-jsobject
+       "dependencies" '(@) delete-fom-jsobject))))
+
 (define* (delete-lockfiles #:key inputs #:allow-other-keys)
   "Delete 'package-lock.json', 'yarn.lock', and 'npm-shrinkwrap.json', if they
 exist."
@@ -431,7 +447,8 @@ (define %standard-phases
   (modify-phases gnu:%standard-phases
     (add-after 'unpack 'set-home set-home)
     (add-before 'configure 'patch-dependencies patch-dependencies)
-    (add-after 'patch-dependencies 'delete-lockfiles delete-lockfiles)
+    (add-after 'patch-dependencies 'delete-dependencies delete-dependencies)
+    (add-after 'delete-dependencies 'delete-lockfiles delete-lockfiles)
     (replace 'configure configure)
     (replace 'build build)
     (replace 'check check)
-- 
2.32.0