@simpill/utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +787 -0
- package/README.md +186 -0
- package/__tests__/README.md +32 -0
- package/__tests__/e2e/all-packages-resolve.e2e.test.ts +40 -0
- package/__tests__/integration/env-and-async.integration.test.ts +12 -0
- package/__tests__/integration/errors-and-uuid.integration.test.ts +14 -0
- package/__tests__/integration/object-and-array.integration.test.ts +15 -0
- package/__tests__/unit/@simpill/_resolver/resolve-packages.unit.test.ts +47 -0
- package/__tests__/unit/@simpill/array.utils/array.utils.unit.test.ts +11 -0
- package/__tests__/unit/@simpill/async.utils/async.utils.unit.test.ts +12 -0
- package/__tests__/unit/@simpill/cache.utils/cache.utils.unit.test.ts +21 -0
- package/__tests__/unit/@simpill/env.utils/env.utils.unit.test.ts +13 -0
- package/__tests__/unit/@simpill/errors.utils/errors.utils.unit.test.ts +13 -0
- package/__tests__/unit/@simpill/object.utils/object.utils.unit.test.ts +11 -0
- package/__tests__/unit/@simpill/patterns.utils/patterns.utils.unit.test.ts +23 -0
- package/__tests__/unit/@simpill/string.utils/string.utils.unit.test.ts +11 -0
- package/__tests__/unit/@simpill/time.utils/time.utils.unit.test.ts +12 -0
- package/__tests__/unit/@simpill/uuid.utils/uuid.utils.unit.test.ts +12 -0
- package/docs/PUBLISHING_AND_PACKAGES.md +258 -0
- package/docs/template/.env.sample +0 -0
- package/docs/template/README.md +0 -0
- package/docs/template/TEMPLATE.md +1040 -0
- package/docs/template/assets/logo-banner.svg +20 -0
- package/docs/template/package.json +14 -0
- package/index.ts +89 -0
- package/package.json +87 -0
- package/scripts/README.md +57 -0
- package/scripts/github/github-set-all-topics.js +120 -0
- package/scripts/github/github-set-repo-topics.sh +33 -0
- package/scripts/github/github-set-repos-public.sh +71 -0
- package/scripts/lib/package-topics.js +57 -0
- package/scripts/lib/publish-order.js +140 -0
- package/scripts/lib/sync-repo-links.js +75 -0
- package/scripts/monorepo/install-hooks.sh +64 -0
- package/scripts/monorepo/monorepo-clean.sh +7 -0
- package/scripts/monorepo/monorepo-sync-deps.js +81 -0
- package/scripts/monorepo/monorepo-verify-deps.js +37 -0
- package/scripts/monorepo/use-local-utils-at-root.js +49 -0
- package/scripts/publish/publish-all.sh +152 -0
- package/scripts/utils/utils-fix-repo-metadata.js +61 -0
- package/scripts/utils/utils-prepare-all.sh +107 -0
- package/scripts/utils/utils-set-npm-keywords.js +132 -0
- package/scripts/utils/utils-update-readme-badges.js +83 -0
- package/scripts/utils/utils-use-local-deps.js +43 -0
- package/scripts/utils/utils-verify-all.sh +45 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Rewrite root package.json so @simpill deps point to local utils (file:./utils/@simpill-*.utils).
|
|
4
|
+
* Run from repo root. After this, run npm install so root uses built local packages.
|
|
5
|
+
* Use npm run sync:deps to restore npm ^version deps.
|
|
6
|
+
*/
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
const REPO_ROOT = path.join(__dirname, "..", "..");
|
|
11
|
+
const UTILS = path.join(REPO_ROOT, "utils");
|
|
12
|
+
const ROOT_PKG = path.join(REPO_ROOT, "package.json");
|
|
13
|
+
|
|
14
|
+
const dirs = fs
|
|
15
|
+
.readdirSync(UTILS, { withFileTypes: true })
|
|
16
|
+
.filter((d) => d.isDirectory() && d.name.startsWith("@simpill-") && d.name.endsWith(".utils"))
|
|
17
|
+
.map((d) => d.name)
|
|
18
|
+
.sort();
|
|
19
|
+
|
|
20
|
+
const deps = {};
|
|
21
|
+
for (const dir of dirs) {
|
|
22
|
+
const pkgPath = path.join(UTILS, dir, "package.json");
|
|
23
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
24
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
25
|
+
const name = pkg.name;
|
|
26
|
+
if (name && name.startsWith("@simpill/")) {
|
|
27
|
+
deps[name] = `file:./utils/${dir}`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rootPkg = JSON.parse(fs.readFileSync(ROOT_PKG, "utf8"));
|
|
32
|
+
rootPkg.dependencies = rootPkg.dependencies || {};
|
|
33
|
+
let changed = false;
|
|
34
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
35
|
+
if (rootPkg.dependencies[name] !== spec) {
|
|
36
|
+
rootPkg.dependencies[name] = spec;
|
|
37
|
+
changed = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (changed) {
|
|
41
|
+
const keys = Object.keys(rootPkg.dependencies).sort();
|
|
42
|
+
const sorted = {};
|
|
43
|
+
for (const k of keys) sorted[k] = rootPkg.dependencies[k];
|
|
44
|
+
rootPkg.dependencies = sorted;
|
|
45
|
+
fs.writeFileSync(ROOT_PKG, JSON.stringify(rootPkg, null, 2) + "\n", "utf8");
|
|
46
|
+
console.log("Root package.json now uses file:./utils/@simpill-*.utils. Run: npm install");
|
|
47
|
+
} else {
|
|
48
|
+
console.log("Root already points to local utils.");
|
|
49
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Publish all @simpill packages to GitHub (push) and npm in dependency order.
|
|
4
|
+
# Requires: gh (GitHub CLI) and npm logged in with access to @simpill scope.
|
|
5
|
+
# =============================================================================
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./scripts/publish/publish-all.sh # GitHub push + npm publish (prompts)
|
|
8
|
+
# ./scripts/publish/publish-all.sh --dry-run # No push, npm publish --dry-run
|
|
9
|
+
# ./scripts/publish/publish-all.sh --skip-github # Only npm publish
|
|
10
|
+
# ./scripts/publish/publish-all.sh --yes # Skip confirmations
|
|
11
|
+
# =============================================================================
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
+
LIB_DIR="$REPO_ROOT/scripts/lib"
|
|
18
|
+
|
|
19
|
+
DRY_RUN=false
|
|
20
|
+
SKIP_GITHUB=false
|
|
21
|
+
YES=false
|
|
22
|
+
|
|
23
|
+
for arg in "$@"; do
|
|
24
|
+
case "$arg" in
|
|
25
|
+
--dry-run) DRY_RUN=true ;;
|
|
26
|
+
--skip-github) SKIP_GITHUB=true ;;
|
|
27
|
+
--npm-only) SKIP_GITHUB=true ;;
|
|
28
|
+
--yes|-y) YES=true ;;
|
|
29
|
+
*)
|
|
30
|
+
echo "Unknown option: $arg"
|
|
31
|
+
echo "Usage: $0 [--dry-run] [--skip-github|--npm-only] [--yes|-y]"
|
|
32
|
+
exit 1
|
|
33
|
+
;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
echo "============================================"
|
|
38
|
+
echo "Publish all @simpill packages"
|
|
39
|
+
echo "============================================"
|
|
40
|
+
echo " --dry-run: $DRY_RUN"
|
|
41
|
+
echo " --skip-github: $SKIP_GITHUB"
|
|
42
|
+
echo " --yes: $YES"
|
|
43
|
+
echo "============================================"
|
|
44
|
+
|
|
45
|
+
# -----------------------------------------------------------------------------
|
|
46
|
+
# 1. Check GitHub CLI and auth (unless skipped)
|
|
47
|
+
# -----------------------------------------------------------------------------
|
|
48
|
+
if [ "$SKIP_GITHUB" = false ]; then
|
|
49
|
+
if ! command -v gh &>/dev/null; then
|
|
50
|
+
echo "Error: GitHub CLI (gh) not found. Install it or use --skip-github."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
if ! gh auth status &>/dev/null; then
|
|
54
|
+
echo "Error: Not logged in to GitHub. Run: gh auth login"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
if [ "$DRY_RUN" = false ] && [ "$YES" = false ]; then
|
|
58
|
+
echo "GitHub: will push current branch. Continue? [y/N]"
|
|
59
|
+
read -r ans
|
|
60
|
+
if [ "$ans" != "y" ] && [ "$ans" != "Y" ]; then
|
|
61
|
+
echo "Aborted."
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
if [ "$DRY_RUN" = false ]; then
|
|
66
|
+
echo "Pushing to GitHub..."
|
|
67
|
+
git -C "$REPO_ROOT" push
|
|
68
|
+
echo "GitHub push done."
|
|
69
|
+
else
|
|
70
|
+
echo "[dry-run] Skipping GitHub push."
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# -----------------------------------------------------------------------------
|
|
75
|
+
# 2. Check npm auth and @simpill scope
|
|
76
|
+
# -----------------------------------------------------------------------------
|
|
77
|
+
if ! command -v npm &>/dev/null; then
|
|
78
|
+
echo "Error: npm not found."
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
if ! npm whoami &>/dev/null; then
|
|
82
|
+
echo "Error: Not logged in to npm. Run: npm login"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
if [ "$DRY_RUN" = false ] && [ "$YES" = false ]; then
|
|
86
|
+
echo "Publish all packages to npm under @simpill? [y/N]"
|
|
87
|
+
read -r ans
|
|
88
|
+
if [ "$ans" != "y" ] && [ "$ans" != "Y" ]; then
|
|
89
|
+
echo "Aborted."
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# -----------------------------------------------------------------------------
|
|
95
|
+
# 3. Get publish order (topological)
|
|
96
|
+
# -----------------------------------------------------------------------------
|
|
97
|
+
ORDER=()
|
|
98
|
+
while IFS= read -r line; do
|
|
99
|
+
[ -n "$line" ] && ORDER+=("$line")
|
|
100
|
+
done < <(node "$LIB_DIR/publish-order.js" order "$REPO_ROOT")
|
|
101
|
+
|
|
102
|
+
echo "Publish order (${#ORDER[@]} packages):"
|
|
103
|
+
for d in "${ORDER[@]}"; do
|
|
104
|
+
echo " - $d"
|
|
105
|
+
done
|
|
106
|
+
|
|
107
|
+
# -----------------------------------------------------------------------------
|
|
108
|
+
# 4. Publish each package: backup package.json, rewrite file: -> ^ver, publish, restore
|
|
109
|
+
# -----------------------------------------------------------------------------
|
|
110
|
+
FAILED=()
|
|
111
|
+
for dir in "${ORDER[@]}"; do
|
|
112
|
+
pkg_path="$REPO_ROOT/utils/$dir/package.json"
|
|
113
|
+
if [ ! -f "$pkg_path" ]; then
|
|
114
|
+
echo " Skip $dir (no package.json)"
|
|
115
|
+
continue
|
|
116
|
+
fi
|
|
117
|
+
name=$(node -e "console.log(require('$pkg_path').name)")
|
|
118
|
+
echo "----------------------------------------"
|
|
119
|
+
echo "Publishing: $name ($dir)"
|
|
120
|
+
echo "----------------------------------------"
|
|
121
|
+
backup="$pkg_path.publish-backup"
|
|
122
|
+
cp "$pkg_path" "$backup"
|
|
123
|
+
trap "mv -f '$backup' '$pkg_path'" EXIT
|
|
124
|
+
node "$LIB_DIR/publish-order.js" rewrite "$dir" "$REPO_ROOT" > "$pkg_path"
|
|
125
|
+
set +e
|
|
126
|
+
if [ "$DRY_RUN" = true ]; then
|
|
127
|
+
(cd "$REPO_ROOT/utils/$dir" && npm publish --access public --dry-run)
|
|
128
|
+
else
|
|
129
|
+
(cd "$REPO_ROOT/utils/$dir" && npm publish --access public)
|
|
130
|
+
fi
|
|
131
|
+
ret=$?
|
|
132
|
+
set -e
|
|
133
|
+
mv -f "$backup" "$pkg_path"
|
|
134
|
+
trap - EXIT
|
|
135
|
+
if [ $ret -ne 0 ]; then
|
|
136
|
+
echo " FAILED: $name"
|
|
137
|
+
FAILED+=("$name")
|
|
138
|
+
else
|
|
139
|
+
echo " OK: $name"
|
|
140
|
+
fi
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
echo ""
|
|
144
|
+
echo "============================================"
|
|
145
|
+
echo "Publish summary"
|
|
146
|
+
echo "============================================"
|
|
147
|
+
if [ ${#FAILED[@]} -eq 0 ]; then
|
|
148
|
+
echo "All packages published successfully."
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
echo "Failed: ${FAILED[*]}"
|
|
152
|
+
exit 1
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fix repository, bugs, homepage in each utils package for standalone GitHub repos.
|
|
4
|
+
* Replaces file:../@simpill-*.utils deps with github:SkinnnyJay/<name>.utils.
|
|
5
|
+
* Run from repo root. Requires utils/@simpill-*.utils to exist.
|
|
6
|
+
*/
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
const REPO_ROOT = path.join(__dirname, "..", "..");
|
|
11
|
+
const UTILS = path.join(REPO_ROOT, "utils");
|
|
12
|
+
const OWNER = "SkinnnyJay";
|
|
13
|
+
|
|
14
|
+
const dirs = fs.readdirSync(UTILS, { withFileTypes: true })
|
|
15
|
+
.filter((d) => d.isDirectory() && d.name.startsWith("@simpill-") && d.name.endsWith(".utils"))
|
|
16
|
+
.map((d) => d.name);
|
|
17
|
+
|
|
18
|
+
for (const dir of dirs) {
|
|
19
|
+
const shortName = dir.replace(/^@simpill-/, "");
|
|
20
|
+
const pkgPath = path.join(UTILS, dir, "package.json");
|
|
21
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
22
|
+
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
24
|
+
let changed = false;
|
|
25
|
+
|
|
26
|
+
if (!pkg.repository || pkg.repository.url.includes("simpill.git")) {
|
|
27
|
+
pkg.repository = { type: "git", url: `https://github.com/${OWNER}/${shortName}.git` };
|
|
28
|
+
changed = true;
|
|
29
|
+
}
|
|
30
|
+
if (pkg.repository && pkg.repository.directory && pkg.repository.directory !== ".") {
|
|
31
|
+
pkg.repository.directory = ".";
|
|
32
|
+
changed = true;
|
|
33
|
+
}
|
|
34
|
+
if (!pkg.bugs || pkg.bugs.url.includes("simpill/issues")) {
|
|
35
|
+
pkg.bugs = { url: `https://github.com/${OWNER}/${shortName}/issues` };
|
|
36
|
+
changed = true;
|
|
37
|
+
}
|
|
38
|
+
if (!pkg.homepage || pkg.homepage.includes("simpill/tree")) {
|
|
39
|
+
pkg.homepage = `https://github.com/${OWNER}/${shortName}#readme`;
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (pkg.dependencies) {
|
|
44
|
+
for (const [dep, spec] of Object.entries(pkg.dependencies)) {
|
|
45
|
+
if (dep.startsWith("@simpill/") && typeof spec === "string" && spec.startsWith("file:../")) {
|
|
46
|
+
const match = spec.match(/file:\.\.\/@simpill-(.+\.utils)/);
|
|
47
|
+
if (match) {
|
|
48
|
+
pkg.dependencies[dep] = `github:${OWNER}/${match[1]}`;
|
|
49
|
+
changed = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (changed) {
|
|
56
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
57
|
+
console.log("Updated:", dir);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log("Done. Check utils/@simpill-*.utils/package.json and commit + push each repo.");
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Prepare all utils packages for npm: install, audit --fix, typecheck, test, build
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# Run from repo root: ./scripts/utils/utils-prepare-all.sh
|
|
8
|
+
# Exits 0 only if every package passes all steps.
|
|
9
|
+
# Fix any reported failures in the failing package, then re-run.
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
13
|
+
UTILS_DIR="$REPO_ROOT/utils"
|
|
14
|
+
SCRIPTS_LIB="$REPO_ROOT/scripts/lib"
|
|
15
|
+
cd "$REPO_ROOT"
|
|
16
|
+
|
|
17
|
+
FAILED_PACKAGES=()
|
|
18
|
+
TOTAL=0
|
|
19
|
+
PASSED=0
|
|
20
|
+
|
|
21
|
+
# Process in dependency order so typecheck/build can resolve file:../@simpill-*.utils
|
|
22
|
+
ORDERED_DIRS=()
|
|
23
|
+
while IFS= read -r line; do
|
|
24
|
+
[ -n "$line" ] && ORDERED_DIRS+=("$UTILS_DIR/$line")
|
|
25
|
+
done < <(node "$SCRIPTS_LIB/publish-order.js" order "$REPO_ROOT" 2>/dev/null)
|
|
26
|
+
if [ ${#ORDERED_DIRS[@]} -eq 0 ]; then
|
|
27
|
+
# Fallback: alphabetical
|
|
28
|
+
for d in "$UTILS_DIR"/@simpill-*.utils; do
|
|
29
|
+
[ -d "$d" ] && [ -f "$d/package.json" ] && ORDERED_DIRS+=("$d")
|
|
30
|
+
done
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
for d in "${ORDERED_DIRS[@]}"; do
|
|
34
|
+
[ -d "$d" ] || continue
|
|
35
|
+
[ -f "$d/package.json" ] || continue
|
|
36
|
+
name=$(basename "$d")
|
|
37
|
+
TOTAL=$((TOTAL + 1))
|
|
38
|
+
echo "----------------------------------------"
|
|
39
|
+
echo " $name"
|
|
40
|
+
echo "----------------------------------------"
|
|
41
|
+
|
|
42
|
+
failed=false
|
|
43
|
+
if ! (cd "$d" && npm install --no-audit --no-fund --silent 2>&1) >/dev/null; then
|
|
44
|
+
echo " install ✗"
|
|
45
|
+
failed=true
|
|
46
|
+
else
|
|
47
|
+
echo " install ✓"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if [ "$failed" = false ]; then
|
|
51
|
+
if (cd "$d" && npm audit fix --force 2>&1) >/dev/null; then
|
|
52
|
+
echo " audit ✓"
|
|
53
|
+
else
|
|
54
|
+
echo " audit ⚠ (issues remain; run 'npm audit' in $d)"
|
|
55
|
+
# Do not set failed=true: audit often exits 1 for unfixable issues; still run typecheck/test/build
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if [ "$failed" = false ]; then
|
|
60
|
+
if ! (cd "$d" && npm run typecheck --if-present 2>&1) >/dev/null; then
|
|
61
|
+
echo " typecheck ✗"
|
|
62
|
+
failed=true
|
|
63
|
+
else
|
|
64
|
+
echo " typecheck ✓"
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [ "$failed" = false ]; then
|
|
69
|
+
if ! (cd "$d" && npm test --silent 2>&1) >/dev/null; then
|
|
70
|
+
echo " test ✗"
|
|
71
|
+
failed=true
|
|
72
|
+
else
|
|
73
|
+
echo " test ✓"
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [ "$failed" = false ]; then
|
|
78
|
+
if ! (cd "$d" && npm run build --silent 2>&1) >/dev/null; then
|
|
79
|
+
echo " build ✗"
|
|
80
|
+
failed=true
|
|
81
|
+
else
|
|
82
|
+
echo " build ✓"
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [ "$failed" = true ]; then
|
|
87
|
+
FAILED_PACKAGES+=("$name")
|
|
88
|
+
else
|
|
89
|
+
PASSED=$((PASSED + 1))
|
|
90
|
+
fi
|
|
91
|
+
echo ""
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
echo "============================================"
|
|
95
|
+
echo "Prepare all utils – summary"
|
|
96
|
+
echo "============================================"
|
|
97
|
+
echo " Passed: $PASSED / $TOTAL"
|
|
98
|
+
if [ ${#FAILED_PACKAGES[@]} -gt 0 ]; then
|
|
99
|
+
echo " Failed: ${FAILED_PACKAGES[*]}"
|
|
100
|
+
echo ""
|
|
101
|
+
echo "Fix issues in each package above, then re-run: ./scripts/utils/utils-prepare-all.sh"
|
|
102
|
+
echo "============================================"
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
echo " All packages ready for npm."
|
|
106
|
+
echo "============================================"
|
|
107
|
+
exit 0
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Set npm package.json "keywords" from each package's TOPICS.md (if present)
|
|
4
|
+
* or from the shared package-topics list. Run from repo root.
|
|
5
|
+
* Requires utils/@simpill-*.utils to exist.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node scripts/utils/utils-set-npm-keywords.js # update package.json from TOPICS.md or fallback
|
|
9
|
+
* node scripts/utils/utils-set-npm-keywords.js --write-topics-md # write TOPICS.md in each package, then update keywords
|
|
10
|
+
* node scripts/utils/utils-set-npm-keywords.js --dry-run # print what would be set
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
|
|
16
|
+
const REPO_ROOT = path.resolve(__dirname, "..", "..");
|
|
17
|
+
const UTILS_DIR = path.join(REPO_ROOT, "utils");
|
|
18
|
+
const MAX_KEYWORDS = 20;
|
|
19
|
+
|
|
20
|
+
const { BASE_TOPICS, PACKAGE_TOPICS } = require("../lib/package-topics.js");
|
|
21
|
+
|
|
22
|
+
function getPackageDirs() {
|
|
23
|
+
if (!fs.existsSync(UTILS_DIR)) return [];
|
|
24
|
+
return fs
|
|
25
|
+
.readdirSync(UTILS_DIR, { withFileTypes: true })
|
|
26
|
+
.filter(
|
|
27
|
+
(d) =>
|
|
28
|
+
d.isDirectory() &&
|
|
29
|
+
d.name.startsWith("@simpill-") &&
|
|
30
|
+
d.name.endsWith(".utils")
|
|
31
|
+
)
|
|
32
|
+
.map((d) => ({ dir: d.name, shortName: d.name.replace(/^@simpill-/, "") }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse TOPICS.md format: lines like `- \`topic\``
|
|
37
|
+
*/
|
|
38
|
+
function parseTopicsMd(filePath) {
|
|
39
|
+
if (!fs.existsSync(filePath)) return null;
|
|
40
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
41
|
+
const names = [];
|
|
42
|
+
const re = /^\s*-\s*`([^`]+)`/gm;
|
|
43
|
+
let m;
|
|
44
|
+
while ((m = re.exec(content)) !== null) names.push(m[1]);
|
|
45
|
+
return names.length ? names : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getKeywordsFromFallback(shortName) {
|
|
49
|
+
const extra = PACKAGE_TOPICS[shortName] || [shortName.replace(".utils", "")];
|
|
50
|
+
const combined = [...BASE_TOPICS, ...extra];
|
|
51
|
+
return [...new Set(combined)].slice(0, MAX_KEYWORDS);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getKeywordsForPackage(pkgDir, shortName) {
|
|
55
|
+
const topicsPath = path.join(UTILS_DIR, pkgDir, "TOPICS.md");
|
|
56
|
+
const fromFile = parseTopicsMd(topicsPath);
|
|
57
|
+
if (fromFile && fromFile.length > 0) {
|
|
58
|
+
return [...new Set(["simpill", ...fromFile])].slice(0, MAX_KEYWORDS);
|
|
59
|
+
}
|
|
60
|
+
return getKeywordsFromFallback(shortName);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function writeTopicsMd(pkgDir, shortName) {
|
|
64
|
+
const keywords = getKeywordsFromFallback(shortName);
|
|
65
|
+
const topicsPath = path.join(UTILS_DIR, pkgDir, "TOPICS.md");
|
|
66
|
+
const lines = [
|
|
67
|
+
"# npm keywords (and GitHub topics)",
|
|
68
|
+
"",
|
|
69
|
+
"Used by `scripts/utils/utils-set-npm-keywords.js` to set package.json keywords.",
|
|
70
|
+
"Edit this file to customize; one topic per line in backticks.",
|
|
71
|
+
"",
|
|
72
|
+
...keywords.map((k) => `- \`${k}\``),
|
|
73
|
+
"",
|
|
74
|
+
];
|
|
75
|
+
fs.writeFileSync(topicsPath, lines.join("\n"), "utf8");
|
|
76
|
+
return keywords;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function main() {
|
|
80
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
81
|
+
const writeTopicsMdFlag = process.argv.includes("--write-topics-md");
|
|
82
|
+
|
|
83
|
+
const packages = getPackageDirs();
|
|
84
|
+
if (packages.length === 0) {
|
|
85
|
+
console.log("No utils/@simpill-*.utils packages found. Exiting.");
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log(
|
|
90
|
+
writeTopicsMdFlag
|
|
91
|
+
? "Writing TOPICS.md per package and updating package.json keywords...\n"
|
|
92
|
+
: "Updating package.json keywords from each package's TOPICS.md (or fallback)...\n"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
for (const { dir, shortName } of packages) {
|
|
96
|
+
const pkgPath = path.join(UTILS_DIR, dir, "package.json");
|
|
97
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
98
|
+
|
|
99
|
+
let keywords;
|
|
100
|
+
if (writeTopicsMdFlag) {
|
|
101
|
+
keywords = writeTopicsMd(dir, shortName);
|
|
102
|
+
console.log(` ${dir}: wrote TOPICS.md, keywords (${keywords.length})`);
|
|
103
|
+
} else {
|
|
104
|
+
keywords = getKeywordsForPackage(dir, shortName);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (dryRun) {
|
|
108
|
+
console.log(` ${dir}: (dry run) ${keywords.slice(0, 8).join(", ")}${keywords.length > 8 ? "..." : ""}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
113
|
+
const prev = pkg.keywords || [];
|
|
114
|
+
const same =
|
|
115
|
+
Array.isArray(prev) &&
|
|
116
|
+
prev.length === keywords.length &&
|
|
117
|
+
prev.every((k, i) => k === keywords[i]);
|
|
118
|
+
if (same) continue;
|
|
119
|
+
|
|
120
|
+
pkg.keywords = keywords;
|
|
121
|
+
fs.writeFileSync(
|
|
122
|
+
pkgPath,
|
|
123
|
+
JSON.stringify(pkg, null, 2) + "\n",
|
|
124
|
+
"utf8"
|
|
125
|
+
);
|
|
126
|
+
console.log(` ${dir}: updated keywords (${keywords.length})`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log("\nDone.");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
main();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Updates each utils package README.md:
|
|
4
|
+
* - Puts an Install section at the very top (npm + GitHub)
|
|
5
|
+
* - Adds npm and GitHub badges after the first line (banner) if missing
|
|
6
|
+
* - Removes duplicate ## Installation section from the body
|
|
7
|
+
*
|
|
8
|
+
* Usage: node scripts/utils/utils-update-readme-badges.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
const REPO = "SkinnnyJay/simpill-utils";
|
|
15
|
+
const REPO_URL = `https://github.com/${REPO}`;
|
|
16
|
+
|
|
17
|
+
const utilsDir = path.join(__dirname, "..", "..", "utils");
|
|
18
|
+
const dirs = fs
|
|
19
|
+
.readdirSync(utilsDir, { withFileTypes: true })
|
|
20
|
+
.filter((d) => d.isDirectory() && d.name.startsWith("@simpill-"))
|
|
21
|
+
.map((d) => d.name);
|
|
22
|
+
|
|
23
|
+
function removeSection(readme, heading) {
|
|
24
|
+
const idx = readme.indexOf(heading);
|
|
25
|
+
if (idx === -1) return readme;
|
|
26
|
+
const afterHeading = readme.slice(idx);
|
|
27
|
+
const nextH2 = afterHeading.slice(heading.length).match(/\n## /);
|
|
28
|
+
const end = nextH2 ? idx + heading.length + nextH2.index : readme.length;
|
|
29
|
+
const before = readme.slice(0, idx).trimEnd();
|
|
30
|
+
let after = readme.slice(end).trimStart();
|
|
31
|
+
after = after.replace(/^---\s*\n?/, "");
|
|
32
|
+
return before + (after ? "\n\n---\n\n" + after : "");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const dir of dirs) {
|
|
36
|
+
const pkgPath = path.join(utilsDir, dir, "package.json");
|
|
37
|
+
const readmePath = path.join(utilsDir, dir, "README.md");
|
|
38
|
+
if (!fs.existsSync(pkgPath) || !fs.existsSync(readmePath)) continue;
|
|
39
|
+
|
|
40
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
41
|
+
const name = pkg.name;
|
|
42
|
+
const npmName = name.replace("/", "%2f");
|
|
43
|
+
let readme = fs.readFileSync(readmePath, "utf8");
|
|
44
|
+
|
|
45
|
+
// Remove existing ## Installation / ## Install so we don't duplicate
|
|
46
|
+
readme = removeSection(readme, "## Installation");
|
|
47
|
+
readme = removeSection(readme, "## Install");
|
|
48
|
+
|
|
49
|
+
// Install block at the very top
|
|
50
|
+
const installBlock = `## Install
|
|
51
|
+
|
|
52
|
+
**npm**
|
|
53
|
+
\`\`\`bash
|
|
54
|
+
npm install ${name}
|
|
55
|
+
\`\`\`
|
|
56
|
+
|
|
57
|
+
**GitHub** (from monorepo)
|
|
58
|
+
\`\`\`bash
|
|
59
|
+
git clone https://github.com/${REPO}.git && cd simpill/utils/${dir} && npm install && npm run build
|
|
60
|
+
\`\`\`
|
|
61
|
+
Then in your project: \`npm install /path/to/simpill/utils/${dir}\` or \`npm link\` from that directory.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
// Badges after first line (banner) if not already present
|
|
68
|
+
const npmBadge = `[](https://www.npmjs.com/package/${name})`;
|
|
69
|
+
const ghBadge = `[](${REPO_URL}/tree/main/utils/${dir})`;
|
|
70
|
+
const badgeLine = `\n<p align="center">\n ${npmBadge}\n ${ghBadge}\n</p>\n`;
|
|
71
|
+
|
|
72
|
+
let out = installBlock + readme.trimStart();
|
|
73
|
+
|
|
74
|
+
if (!out.includes("img.shields.io/npm/v/")) {
|
|
75
|
+
const firstLineEnd = out.indexOf("\n");
|
|
76
|
+
out = out.slice(0, firstLineEnd + 1) + badgeLine + out.slice(firstLineEnd + 1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fs.writeFileSync(readmePath, out, "utf8");
|
|
80
|
+
console.log("Updated:", dir);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log("Done.");
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Rewrite each utils package to use file:../@simpill-<name>.utils for @simpill deps
|
|
4
|
+
* so that build/test use local packages. Run from repo root.
|
|
5
|
+
*/
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
const REPO_ROOT = path.join(__dirname, "..", "..");
|
|
10
|
+
const UTILS = path.join(REPO_ROOT, "utils");
|
|
11
|
+
const OWNER = "SkinnnyJay";
|
|
12
|
+
|
|
13
|
+
const dirs = fs
|
|
14
|
+
.readdirSync(UTILS, { withFileTypes: true })
|
|
15
|
+
.filter((d) => d.isDirectory() && d.name.startsWith("@simpill-") && d.name.endsWith(".utils"))
|
|
16
|
+
.map((d) => d.name)
|
|
17
|
+
.sort();
|
|
18
|
+
|
|
19
|
+
let changed = 0;
|
|
20
|
+
for (const dir of dirs) {
|
|
21
|
+
const pkgPath = path.join(UTILS, dir, "package.json");
|
|
22
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
24
|
+
|
|
25
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
26
|
+
const section = pkg[key];
|
|
27
|
+
if (!section || typeof section !== "object") continue;
|
|
28
|
+
for (const [name, spec] of Object.entries(section)) {
|
|
29
|
+
if (!name.startsWith("@simpill/") || typeof spec !== "string") continue;
|
|
30
|
+
const m = spec.match(/^github:([^/]+)\/(.+)$/);
|
|
31
|
+
if (m && m[1] === OWNER && m[2].endsWith(".utils")) {
|
|
32
|
+
const depDir = `@simpill-${m[2]}`;
|
|
33
|
+
if (depDir !== dir && dirs.includes(depDir)) {
|
|
34
|
+
section[name] = `file:../${depDir}`;
|
|
35
|
+
changed++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
41
|
+
}
|
|
42
|
+
console.log("Updated", changed, "dependency entries to file:../ in utils packages.");
|
|
43
|
+
process.exit(0);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Verify all utils – build and test every package under utils/@simpill-*.utils
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# Run from repo root: ./scripts/utils/utils-verify-all.sh
|
|
8
|
+
# Exits 0 if all packages build and test pass; 1 otherwise.
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
12
|
+
cd "$REPO_ROOT"
|
|
13
|
+
|
|
14
|
+
FAILED_BUILD=()
|
|
15
|
+
FAILED_TEST=()
|
|
16
|
+
|
|
17
|
+
for d in utils/@simpill-*.utils; do
|
|
18
|
+
name=$(basename "$d")
|
|
19
|
+
if (cd "$d" && npm run build --silent 2>&1) >/dev/null; then
|
|
20
|
+
echo " build $name ✓"
|
|
21
|
+
else
|
|
22
|
+
echo " build $name ✗"
|
|
23
|
+
FAILED_BUILD+=("$name")
|
|
24
|
+
fi
|
|
25
|
+
if (cd "$d" && npm test --silent 2>&1) >/dev/null; then
|
|
26
|
+
echo " test $name ✓"
|
|
27
|
+
else
|
|
28
|
+
echo " test $name ✗"
|
|
29
|
+
FAILED_TEST+=("$name")
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
|
|
33
|
+
echo ""
|
|
34
|
+
echo "============================================"
|
|
35
|
+
echo "Verify all utils – summary"
|
|
36
|
+
echo "============================================"
|
|
37
|
+
if [ ${#FAILED_BUILD[@]} -eq 0 ] && [ ${#FAILED_TEST[@]} -eq 0 ]; then
|
|
38
|
+
echo "All packages: build and test passed."
|
|
39
|
+
echo "============================================"
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
[ ${#FAILED_BUILD[@]} -gt 0 ] && echo "Build failed: ${FAILED_BUILD[*]}"
|
|
43
|
+
[ ${#FAILED_TEST[@]} -gt 0 ] && echo "Test failed: ${FAILED_TEST[*]}"
|
|
44
|
+
echo "============================================"
|
|
45
|
+
exit 1
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"resolveJsonModule": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["index.ts", "scripts/**/*.js", "__tests__/**/*.ts"],
|
|
13
|
+
"exclude": ["node_modules", "utils"]
|
|
14
|
+
}
|