1# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2#
3# SPDX-License-Identifier: CC0-1.0
4
5[tools]
6node = "26"
7
8[tasks.fmt]
9description = "Format all files with Biome"
10run = "npm run fmt"
11
12[tasks.lint]
13description = "Lint all files with Biome"
14run = "npm run lint"
15
16[tasks.typecheck]
17description = "Type-check all packages with TypeScript"
18run = "npm run typecheck"
19
20[tasks.check]
21description = "Format, lint, and type-check"
22depends = ["fmt", "lint", "typecheck"]
23
24[tasks."release:bump"]
25description = "Bump one or more package versions"
26usage = '''
27arg "<version>" help="SemVer increment or exact version"
28arg "<packages>" help="Package selectors" var=#true var_min=1
29flag "--preid <preid>" help="Prerelease identifier for pre* increments"
30'''
31shell = "bash -c"
32run = '''
33set -euo pipefail
34
35version=${usage_version:?}
36preid=${usage_preid:-}
37eval "packages=(${usage_packages:?})"
38
39exact_version_re='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'
40is_exact_version=false
41if [[ "$version" =~ $exact_version_re ]]; then
42 is_exact_version=true
43fi
44
45case "$version" in
46 patch|minor|major|prepatch|preminor|premajor|prerelease) ;;
47 *)
48 if [[ "$is_exact_version" != true ]]; then
49 echo "Error: unsupported version '$version'" >&2
50 echo "Use patch, minor, major, prepatch, preminor, premajor, prerelease, or an exact SemVer." >&2
51 exit 1
52 fi
53 ;;
54esac
55
56if [[ -n "$preid" ]]; then
57 case "$version" in
58 prepatch|preminor|premajor|prerelease) ;;
59 *)
60 echo "Error: --preid only applies to prepatch, preminor, premajor, or prerelease" >&2
61 exit 1
62 ;;
63 esac
64fi
65
66resolved_dirs=$(bash scripts/resolve-release-packages.bash "${packages[@]}")
67mapfile -t dirs <<< "$resolved_dirs"
68
69if [[ "$is_exact_version" == true && -n "${dirs[1]:-}" ]]; then
70 echo "Error: exact versions may only be applied to one package at a time" >&2
71 exit 1
72fi
73
74for dir in "${dirs[@]}"; do
75 args=(version "$version" --no-git-tag-version --workspace "$dir")
76 if [[ -n "$preid" ]]; then
77 args+=(--preid "$preid")
78 fi
79 echo "Bumping $dir"
80 npm "${args[@]}"
81done
82'''
83
84[tasks."release:pack"]
85description = "Check and dry-run npm package packing"
86usage = '''
87arg "<packages>" help="Package selectors" var=#true var_min=1
88'''
89shell = "bash -c"
90run = '''
91set -euo pipefail
92
93eval "packages=(${usage_packages:?})"
94resolved_dirs=$(bash scripts/resolve-release-packages.bash "${packages[@]}")
95mapfile -t dirs <<< "$resolved_dirs"
96
97mise run check
98
99for dir in "${dirs[@]}"; do
100 echo "Packing $dir (dry run)"
101 npm pack --dry-run "./$dir"
102done
103'''
104
105[tasks."release:publish"]
106description = "Check, dry-run, and publish npm packages"
107usage = '''
108arg "<packages>" help="Package selectors" var=#true var_min=1
109flag "--tag <tag>" help="npm dist-tag" default="latest"
110flag "--otp <otp>" help="One-time password for npm two-factor auth"
111'''
112shell = "bash -c"
113run = '''
114set -euo pipefail
115
116package_version() {
117 node -e 'const fs = require("node:fs"); const path = require("node:path"); const pkg = JSON.parse(fs.readFileSync(path.join(process.argv[1], "package.json"), "utf8")); console.log(pkg.version);' "$1"
118}
119
120eval "packages=(${usage_packages:?})"
121tag=${usage_tag:?}
122otp=${usage_otp:-}
123resolved_dirs=$(bash scripts/resolve-release-packages.bash "${packages[@]}")
124mapfile -t dirs <<< "$resolved_dirs"
125
126if [[ -n "$otp" && -n "${dirs[1]:-}" ]]; then
127 echo "Error: --otp may only be used with one package at a time" >&2
128 echo "Use an npm automation token for multi-package publishing, or publish each package separately with a fresh OTP." >&2
129 exit 1
130fi
131
132for dir in "${dirs[@]}"; do
133 version=$(package_version "$dir")
134 if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+- && "$tag" == latest ]]; then
135 echo "Error: refusing to publish prerelease $dir@$version with tag latest" >&2
136 echo "Use --tag beta or another prerelease dist-tag." >&2
137 exit 1
138 fi
139done
140
141mise run check
142
143publish_args=(--access public --tag "$tag")
144if [[ -n "$otp" ]]; then
145 publish_args+=(--otp "$otp")
146fi
147
148for dir in "${dirs[@]}"; do
149 echo "Publishing $dir (dry run)"
150 npm publish --dry-run "${publish_args[@]}" "./$dir"
151done
152
153for dir in "${dirs[@]}"; do
154 echo "Publishing $dir"
155 npm publish "${publish_args[@]}" "./$dir"
156done
157'''