summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS1
-rw-r--r--Makefile.am1
-rwxr-xr-xetc/teams.scm1
-rw-r--r--guix/build-system/node.scm2
-rw-r--r--guix/build/json-utils.scm164
-rw-r--r--guix/build/node-build-system.scm127
-rw-r--r--guix/build/tree-sitter-build-system.scm3
-rw-r--r--guix/import/npm-binary.scm1
8 files changed, 175 insertions, 125 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index efe78281376..774f0c3b89c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -283,6 +283,7 @@ gnu/packages/javascript\.scm @guix/javascript
283gnu/packages/node-xyz\.scm @guix/javascript 283gnu/packages/node-xyz\.scm @guix/javascript
284gnu/packages/node\.scm @guix/javascript 284gnu/packages/node\.scm @guix/javascript
285guix/build-system/node\.scm @guix/javascript 285guix/build-system/node\.scm @guix/javascript
286guix/build/json-utils\.scm @guix/javascript
286guix/build/node-build-system\.scm @guix/javascript 287guix/build/node-build-system\.scm @guix/javascript
287guix/import/npm-binary\.scm @guix/javascript 288guix/import/npm-binary\.scm @guix/javascript
288guix/scripts/import/npm-binary\.scm @guix/javascript 289guix/scripts/import/npm-binary\.scm @guix/javascript
diff --git a/Makefile.am b/Makefile.am
index 30b4948bb67..5e1f252b9cc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -266,6 +266,7 @@ MODULES = \
266 guix/build/vim-build-system.scm \ 266 guix/build/vim-build-system.scm \
267 guix/build/waf-build-system.scm \ 267 guix/build/waf-build-system.scm \
268 guix/build/haskell-build-system.scm \ 268 guix/build/haskell-build-system.scm \
269 guix/build/json-utils.scm \
269 guix/build/julia-build-system.scm \ 270 guix/build/julia-build-system.scm \
270 guix/build/kconfig.scm \ 271 guix/build/kconfig.scm \
271 guix/build/linux-module-build-system.scm \ 272 guix/build/linux-module-build-system.scm \
diff --git a/etc/teams.scm b/etc/teams.scm
index 9ae7d830a28..244f681ced1 100755
--- a/etc/teams.scm
+++ b/etc/teams.scm
@@ -881,6 +881,7 @@ and the maven-build-system."
881 "gnu/packages/node-xyz.scm" 881 "gnu/packages/node-xyz.scm"
882 "gnu/packages/node.scm" 882 "gnu/packages/node.scm"
883 "guix/build-system/node.scm" 883 "guix/build-system/node.scm"
884 "guix/build/json-utils.scm"
884 "guix/build/node-build-system.scm" 885 "guix/build/node-build-system.scm"
885 "guix/import/npm-binary.scm" 886 "guix/import/npm-binary.scm"
886 "guix/scripts/import/npm-binary.scm"))) 887 "guix/scripts/import/npm-binary.scm")))
diff --git a/guix/build-system/node.scm b/guix/build-system/node.scm
index c07479b693a..596e2cb3e47 100644
--- a/guix/build-system/node.scm
+++ b/guix/build-system/node.scm
@@ -43,6 +43,7 @@
43(define %node-build-system-modules 43(define %node-build-system-modules
44 ;; Build-side modules imported by default. 44 ;; Build-side modules imported by default.
45 `((guix build node-build-system) 45 `((guix build node-build-system)
46 (guix build json-utils)
46 ,@%default-gnu-imported-modules)) 47 ,@%default-gnu-imported-modules))
47 48
48(define (default-node) 49(define (default-node)
@@ -99,6 +100,7 @@
99 (guile-json (default-guile-json)) 100 (guile-json (default-guile-json))
100 (imported-modules %node-build-system-modules) 101 (imported-modules %node-build-system-modules)
101 (modules '((guix build node-build-system) 102 (modules '((guix build node-build-system)
103 (guix build json-utils)
102 (guix build utils)))) 104 (guix build utils))))
103 "Build SOURCE using NODE and INPUTS." 105 "Build SOURCE using NODE and INPUTS."
104 (define builder 106 (define builder
diff --git a/guix/build/json-utils.scm b/guix/build/json-utils.scm
new file mode 100644
index 00000000000..f80b1176e4d
--- /dev/null
+++ b/guix/build/json-utils.scm
@@ -0,0 +1,164 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016, 2020 Jelle Licht <jlicht@fsfe.org>
3;;; Copyright © 2019, 2021 Timothy Sample <samplet@ngyro.com>
4;;; Copyright © 2021, 2022 Philip McGrath <philip@philipmcgrath.com>
5;;; Copyright © 2024 Daniel Khodabakhsh <d.khodabakhsh@gmail.com>
6;;; Copyright © 2026 Nicolas Graves <ngraves@ngraves.fr>
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify it
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful, but
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23(define-module (guix build json-utils)
24 #:use-module (guix build utils)
25 #:use-module (guix deprecation)
26 #:use-module (guix diagnostics)
27 #:use-module (guix i18n)
28 #:use-module (ice-9 format)
29 #:use-module (ice-9 ftw)
30 #:use-module (ice-9 optargs)
31 #:use-module (ice-9 match)
32 #:use-module (ice-9 regex)
33 #:use-module (json)
34 #:use-module (srfi srfi-1)
35 #:export (with-atomic-json-file-replacement
36 modify-json
37 modify-json-fields
38 delete-fields
39 replace-fields
40 add-fields))
41
42;;;
43;;; JSON modification procedures
44;;;
45
46(define* (with-atomic-json-file-replacement proc
47 #:optional (file "package.json"))
48 "Like 'with-atomic-file-replacement', but PROC is called with a single
49argument---the result of parsing FILE's contents as JSON---and should produce
50a value to be written as JSON to the replacement FILE."
51 (with-atomic-file-replacement file
52 (lambda (in out)
53 (scm->json (proc (json->scm in #:ordered #t)) out #:pretty #t))))
54
55(define* (modify-json #:key (file "package.json") #:rest all-arguments)
56 "Provide package.json modifying callbacks such as (delete-dependencies ...)"
57 (let ((modifications
58 (let loop ((arguments all-arguments))
59 (cond
60 ((null? arguments) '())
61 ((keyword? (car arguments)) (loop (cddr arguments)))
62 (else (cons (car arguments) (loop (cdr arguments))))))))
63 (with-atomic-json-file-replacement
64 (lambda (package)
65 (fold (lambda (modification package)
66 (modification package))
67 package
68 modifications))
69 file)))
70
71(define* (modify-json-fields fields field-modifier
72 #:key
73 (field-path-mapper identity)
74 (insert? #f)
75 (strict? #t))
76 "Return a procedure to supply to `modify-json' which modifies the specified
77JSON file. FIELDS is a list procedure-specific data structures which should
78include the definition of a ``field-path'' in one of two syntaxes: dot-syntax
79string such as @code{\"devDependencies.esbuild\"}, or a list of strings such
80as @code{(list \"devDependencies\" \"esbuild\")}.
81
82FIELD-MODIFIER is a procedure called with three arguments: 1) the original
83field-path, e.g. \"dependencies.typescript\", 2) the field's
84surrounding (parent) JSON data, as an association list, and 3) the field
85name (key), e.g. \"typescript\". The value it returns should be the modified
86JSON data associated with the field; in other words, returning the second
87argument without changing it is a no-op.
88
89FIELD-PATH-MAPPER is a procedure which instructs where the field-path is
90located within the field structure. INSERT? allows the creation of the field
91and any missing intermediate fields, while STRICT? causes an error to be
92thrown if the exact field-path is not found in the data."
93 (lambda (package)
94 (fold
95 (lambda (field package)
96 (let* ((field-path (field-path-mapper field))
97 (field-path (cond
98 ((string? field-path)
99 (string-split field-path #\.))
100 ((and (list? field-path) (every string? field-path))
101 field-path)
102 (else (error (format #f "\
103invalid field value provided, expected string or list of strings, got ~s~%"
104 field-path))))))
105 (let loop ((data package)
106 (field-path field-path))
107 (let* ((key (car field-path))
108 (field-missing? (not (assoc key data)))
109 (data (if (and field-missing? insert?)
110 (acons key '() data)
111 data)))
112 (if field-missing?
113 (if strict?
114 (error (format #f "key ~s was not found in data: ~y~%"
115 key data))
116 data)
117 (if (= (length field-path) 1)
118 (field-modifier field data key)
119 (assoc-set! data key
120 (loop (assoc-ref data key)
121 (cdr field-path)))))))))
122 package
123 fields)))
124
125(define* (delete-fields fields #:key (strict? #t))
126 "Provides a lambda to supply to modify-json which deletes the specified
127 `fields` which is a list of field-paths as mentioned in `modify-json-fields`.
128 Examples:
129 (delete-fields '(
130 (\"path\" \"to\" \"field\")
131 \"path.to.other.field\"))"
132 (modify-json-fields
133 fields
134 (lambda (_ data key)
135 (format #t "deleting field ~s, of value: ~y~%"
136 key (assoc-ref data key))
137 (assoc-remove! data key))
138 #:strict? strict?))
139
140(define* (replace-fields fields #:key (strict? #t) insert?)
141 "Provides a lambda to supply to modify-json which replaces the value of the
142 supplied field. `fields` is a list of pairs, where the first element is the
143 field-path and the second element is the value to replace the target with.
144 Examples:
145 (replace-fields '(
146 ((\"path\" \"to\" \"field\") \"new field value\")
147 (\"path.to.other.field\" \"new field value\")))"
148 (modify-json-fields
149 fields
150 (lambda (field data key)
151 (let ((value (cdr field)))
152 (format #t "setting field ~s to value: ~y~%" key value)
153 (assoc-set! data key value)))
154 #:field-path-mapper (lambda (field) (car field))
155 #:insert? insert?
156 #:strict? strict?))
157
158(define* (add-fields fields)
159 "Like `replace-fields', but can insert new fields as well."
160 (replace-fields fields #:insert? #t))
161
162;;; Local Variables:
163;;; eval: (put 'with-atomic-json-file-replacement 'scheme-indent-function 1)
164;;; End:
diff --git a/guix/build/node-build-system.scm b/guix/build/node-build-system.scm
index 08623f97756..00384da2d41 100644
--- a/guix/build/node-build-system.scm
+++ b/guix/build/node-build-system.scm
@@ -25,6 +25,7 @@
25 25
26(define-module (guix build node-build-system) 26(define-module (guix build node-build-system)
27 #:use-module ((guix build gnu-build-system) #:prefix gnu:) 27 #:use-module ((guix build gnu-build-system) #:prefix gnu:)
28 #:use-module (guix build json-utils)
28 #:use-module (guix build utils) 29 #:use-module (guix build utils)
29 #:use-module (ice-9 format) 30 #:use-module (ice-9 format)
30 #:use-module (ice-9 ftw) 31 #:use-module (ice-9 ftw)
@@ -39,43 +40,12 @@
39 delete-dependencies/except 40 delete-dependencies/except
40 delete-dev-dependencies 41 delete-dev-dependencies
41 delete-dev-dependencies/except 42 delete-dev-dependencies/except
42 delete-fields 43 node-build))
43 add-fields
44 modify-json
45 modify-json-fields
46 node-build
47 replace-fields
48 with-atomic-json-file-replacement))
49 44
50;;; 45;;;
51;;; package.json modification procedures 46;;; Helpers
52;;; 47;;;
53 48
54(define* (with-atomic-json-file-replacement proc
55 #:optional (file "package.json"))
56 "Like 'with-atomic-file-replacement', but PROC is called with a single
57argument---the result of parsing FILE's contents as JSON---and should produce
58a value to be written as JSON to the replacement FILE."
59 (with-atomic-file-replacement file
60 (lambda (in out)
61 (scm->json (proc (json->scm in #:ordered #t)) out #:pretty #t))))
62
63(define* (modify-json #:key (file "package.json") #:rest all-arguments)
64 "Provide package.json modifying callbacks such as (delete-dependencies ...)"
65 (let ((modifications
66 (let loop ((arguments all-arguments))
67 (cond
68 ((null? arguments) '())
69 ((keyword? (car arguments)) (loop (cddr arguments)))
70 (else (cons (car arguments) (loop (cdr arguments))))))))
71 (with-atomic-json-file-replacement
72 (lambda (package)
73 (fold (lambda (modification package)
74 (modification package))
75 package
76 modifications))
77 file)))
78
79(define %dependency-keys 49(define %dependency-keys
80 '("devDependencies" 50 '("devDependencies"
81 "dependencies" 51 "dependencies"
@@ -125,97 +95,6 @@ dependencies."
125 (delete-dependencies/except dependencies-to-preserve 95 (delete-dependencies/except dependencies-to-preserve
126 #:dependency-keys %dev-dependency-keys)) 96 #:dependency-keys %dev-dependency-keys))
127 97
128(define* (modify-json-fields fields field-modifier
129 #:key
130 (field-path-mapper identity)
131 (insert? #f)
132 (strict? #t))
133 "Return a procedure to supply to `modify-json' which modifies the specified
134JSON file. FIELDS is a list procedure-specific data structures which should
135include the definition of a ``field-path'' in one of two syntaxes: dot-syntax
136string such as @code{\"devDependencies.esbuild\"}, or a list of strings such
137as @code{(list \"devDependencies\" \"esbuild\")}.
138
139FIELD-MODIFIER is a procedure called with three arguments: 1) the original
140field-path, e.g. \"dependencies.typescript\", 2) the field's
141surrounding (parent) JSON data, as an association list, and 3) the field
142name (key), e.g. \"typescript\". The value it returns should be the modified
143JSON data associated with the field; in other words, returning the second
144argument without changing it is a no-op.
145
146FIELD-PATH-MAPPER is a procedure which instructs where the field-path is
147located within the field structure. INSERT? allows the creation of the field
148and any missing intermediate fields, while STRICT? causes an error to be
149thrown if the exact field-path is not found in the data."
150 (lambda (package)
151 (fold
152 (lambda (field package)
153 (let* ((field-path (field-path-mapper field))
154 (field-path (cond
155 ((string? field-path)
156 (string-split field-path #\.))
157 ((and (list? field-path) (every string? field-path))
158 field-path)
159 (else (error (format #f "\
160invalid field value provided, expected string or list of strings, got ~s~%"
161 field-path))))))
162 (let loop ((data package)
163 (field-path field-path))
164 (let* ((key (car field-path))
165 (field-missing? (not (assoc key data)))
166 (data (if (and field-missing? insert?)
167 (acons key '() data)
168 data)))
169 (if field-missing?
170 (if strict?
171 (error (format #f "key ~s was not found in data: ~y~%"
172 key data))
173 data)
174 (if (= (length field-path) 1)
175 (field-modifier field data key)
176 (assoc-set! data key
177 (loop (assoc-ref data key)
178 (cdr field-path)))))))))
179 package
180 fields)))
181
182(define* (delete-fields fields #:key (strict? #t))
183 "Provides a lambda to supply to modify-json which deletes the specified
184 `fields` which is a list of field-paths as mentioned in `modify-json-fields`.
185 Examples:
186 (delete-fields '(
187 (\"path\" \"to\" \"field\")
188 \"path.to.other.field\"))"
189 (modify-json-fields
190 fields
191 (lambda (_ data key)
192 (format #t "deleting field ~s, of value: ~y~%"
193 key (assoc-ref data key))
194 (assoc-remove! data key))
195 #:strict? strict?))
196
197(define* (replace-fields fields #:key (strict? #t) insert?)
198 "Provides a lambda to supply to modify-json which replaces the value of the
199 supplied field. `fields` is a list of pairs, where the first element is the
200 field-path and the second element is the value to replace the target with.
201 Examples:
202 (replace-fields '(
203 ((\"path\" \"to\" \"field\") \"new field value\")
204 (\"path.to.other.field\" \"new field value\")))"
205 (modify-json-fields
206 fields
207 (lambda (field data key)
208 (let ((value (cdr field)))
209 (format #t "setting field ~s to value: ~y~%" key value)
210 (assoc-set! data key value)))
211 #:field-path-mapper (lambda (field) (car field))
212 #:insert? insert?
213 #:strict? strict?))
214
215(define* (add-fields fields)
216 "Like `replace-fields', but can insert new fields as well."
217 (replace-fields fields #:insert? #t))
218
219(define (delete-dev-dependencies) 98(define (delete-dev-dependencies)
220 (delete-fields (list "devDependencies" "peerDependencies") 99 (delete-fields (list "devDependencies" "peerDependencies")
221 #:strict? #f)) 100 #:strict? #f))
diff --git a/guix/build/tree-sitter-build-system.scm b/guix/build/tree-sitter-build-system.scm
index 5819594f063..f19fc5f43cb 100644
--- a/guix/build/tree-sitter-build-system.scm
+++ b/guix/build/tree-sitter-build-system.scm
@@ -19,6 +19,7 @@
19 19
20(define-module (guix build tree-sitter-build-system) 20(define-module (guix build tree-sitter-build-system)
21 #:use-module ((guix build node-build-system) #:prefix node:) 21 #:use-module ((guix build node-build-system) #:prefix node:)
22 #:use-module (guix build json-utils)
22 #:use-module (guix build utils) 23 #:use-module (guix build utils)
23 #:use-module (ice-9 match) 24 #:use-module (ice-9 match)
24 #:use-module (ice-9 regex) 25 #:use-module (ice-9 regex)
@@ -48,7 +49,7 @@
48 "Rewrite dependencies in 'package.json'. We remove all runtime dependencies 49 "Rewrite dependencies in 'package.json'. We remove all runtime dependencies
49and replace development dependencies with tree-sitter grammar node modules." 50and replace development dependencies with tree-sitter grammar node modules."
50 51
51 (node:with-atomic-json-file-replacement 52 (with-atomic-json-file-replacement "package.json"
52 (lambda (pkg-meta-alist) 53 (lambda (pkg-meta-alist)
53 (map (match-lambda 54 (map (match-lambda
54 (("dependencies" dependencies ...) 55 (("dependencies" dependencies ...)
diff --git a/guix/import/npm-binary.scm b/guix/import/npm-binary.scm
index 2cbd23ed022..333adaab552 100644
--- a/guix/import/npm-binary.scm
+++ b/guix/import/npm-binary.scm
@@ -25,6 +25,7 @@
25 #:use-module (gcrypt hash) 25 #:use-module (gcrypt hash)
26 #:use-module (gnu packages) 26 #:use-module (gnu packages)
27 #:use-module (guix base32) 27 #:use-module (guix base32)
28 #:use-module (guix build json-utils)
28 #:use-module (guix http-client) 29 #:use-module (guix http-client)
29 #:use-module ((guix import git) #:select (get-tags)) 30 #:use-module ((guix import git) #:select (get-tags))
30 #:use-module (guix import json) 31 #:use-module (guix import json)