@traceletdev/cli 0.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Moiz Imran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # Tracelet
2
+
3
+ > **Small traces. Big clarity.**
4
+
5
+ Tracelet is a modern performance toolkit that brings **Lighthouse-level insight** and **ESLint-level ergonomics** into one seamless workflow.
6
+
7
+ [![Go Version](https://img.shields.io/badge/go-1.23+-00ADD8?style=flat&logo=go)](https://golang.org/)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ ## Features
11
+
12
+ - **šŸš€ Fast** - Every operation completes in seconds
13
+ - **šŸŽÆ Deterministic** - Reproducible metrics regardless of environment
14
+ - **šŸ”§ Integrated** - Fits into your dev loop, editor, and CI
15
+ - **šŸ‘ļø Transparent** - See what's happening, not just summaries
16
+
17
+ ## Quick Start
18
+
19
+ ### Install via npm (Recommended)
20
+
21
+ ```bash
22
+ # Install tracelet CLI
23
+ npm install -D @traceletdev/cli
24
+
25
+ # Install framework plugin (choose one)
26
+ npm install -D @traceletdev/next # for Next.js
27
+ npm install -D @traceletdev/vite # for Vite
28
+
29
+ # Initialize config
30
+ npx tracelet init
31
+
32
+ # Lint
33
+ npx tracelet lint
34
+
35
+ # Probe
36
+ npx tracelet probe http://localhost:3000
37
+ ```
38
+
39
+ See [Framework Integration](#framework-integration) below for setup details.
40
+
41
+ ### Build from source
42
+
43
+ ```bash
44
+ # Build
45
+ go build -o tracelet ./cmd/tracelet
46
+
47
+ # Initialize
48
+ ./tracelet init
49
+
50
+ # Lint
51
+ ./tracelet lint
52
+
53
+ # Probe
54
+ ./tracelet probe http://localhost:3000
55
+ ```
56
+
57
+ ## What's Included
58
+
59
+ ### šŸ“‹ Lint
60
+
61
+ Performance budgets as code. Enforce route-level JavaScript limits with ESLint-style rules.
62
+
63
+ ```bash
64
+ tracelet lint
65
+ # Route JS(gzip) Verdict
66
+ # / 9KB āœ…
67
+ # /product 47KB āŒ over budget
68
+ ```
69
+
70
+ ### šŸ” Probe
71
+
72
+ Chrome-based performance audits in 2-3 seconds. Collect TTFB, FCP, LCP, CLS, TBT-Lite, FSI.
73
+
74
+ ```bash
75
+ tracelet probe http://localhost:3000 --profile mobile
76
+ ```
77
+
78
+ ### šŸ–„ļø HUD
79
+
80
+ Real-time overlay for development. See live performance feedback in your browser.
81
+
82
+ ```bash
83
+ tracelet hud
84
+ # Then add <script src="http://localhost:3111/overlay.js"></script> to your app
85
+ ```
86
+
87
+ The overlay shows a **Routes** tab (lint budgets), a **Metrics** tab (live Web Vitals), and a
88
+ **Components** tab (React re-render counts). React tracking needs its hook loaded *before* React —
89
+ add this to `<head>`, before your app bundle:
90
+
91
+ ```html
92
+ <script src="http://localhost:3111/hook.js"></script>
93
+ ```
94
+
95
+ See [`@traceletdev/react`](./packages/tracelet-react/README.md) for the bundled-app (`import`) setup.
96
+
97
+ ### šŸ”„ CI
98
+
99
+ Automated checks with baseline comparison. GitHub Action ready.
100
+
101
+ ```bash
102
+ tracelet ci --compare .tracelet/baseline.json --format markdown
103
+ ```
104
+
105
+ ### šŸ“ VS Code
106
+
107
+ In-editor diagnostics with quick fixes. See issues as you code.
108
+
109
+ Install the extension and get instant feedback on save.
110
+
111
+ ## Documentation
112
+
113
+ - [Overview](./docs/overview.md) - Philosophy and features
114
+ - [Configuration](./docs/config.md) - Config reference
115
+ - [Rules](./docs/rules.md) - Rule catalog
116
+ - [API Reference](./docs/api.md) - CLI and programmatic APIs
117
+ - [Framework Adapters](./docs/adapters.md) - Next.js and Vite integration
118
+
119
+ ## Framework Integration
120
+
121
+ ### Next.js
122
+
123
+ 1. Install packages:
124
+
125
+ ```bash
126
+ npm install -D @traceletdev/cli @traceletdev/next
127
+ ```
128
+
129
+ 2. Add postbuild script to `package.json`:
130
+
131
+ ```json
132
+ {
133
+ "scripts": {
134
+ "postbuild": "node node_modules/@traceletdev/next/collect.js"
135
+ }
136
+ }
137
+ ```
138
+
139
+ 3. Build your app:
140
+
141
+ ```bash
142
+ npm run build
143
+ # Stats automatically collected
144
+ ```
145
+
146
+ 4. Lint with tracelet:
147
+
148
+ ```bash
149
+ npx tracelet lint
150
+ ```
151
+
152
+ ### Vite
153
+
154
+ 1. Install packages:
155
+
156
+ ```bash
157
+ npm install -D @traceletdev/cli @traceletdev/vite
158
+ ```
159
+
160
+ 2. Add plugin to `vite.config.js`:
161
+
162
+ ```js
163
+ import { defineConfig } from 'vite';
164
+ import tracelet from '@traceletdev/vite';
165
+
166
+ export default defineConfig({
167
+ plugins: [
168
+ tracelet(),
169
+ // your other plugins
170
+ ],
171
+ });
172
+ ```
173
+
174
+ 3. Build your app:
175
+
176
+ ```bash
177
+ npm run build
178
+ # Stats automatically collected during build
179
+ ```
180
+
181
+ 4. Lint with tracelet:
182
+
183
+ ```bash
184
+ npx tracelet lint
185
+ ```
186
+
187
+ ## Adapters (Legacy)
188
+
189
+ For projects not using npm packages, see adapter files in `adapters/` directory:
190
+
191
+ - **Next.js** - `node adapters/next-collect.js` after build
192
+ - **Vite** - Use `adapters/vite-plugin-tracelet.cjs` in `vite.config.js`
193
+
194
+ ## Examples
195
+
196
+ See [`examples/`](./examples) — drop-in [config examples](./examples/config-examples)
197
+ (basic, strict, CI) and pointers to the runnable Next.js/Vite fixture apps.
198
+
199
+ ## Development
200
+
201
+ ```bash
202
+ # Run tests
203
+ go test ./tests/integration -v
204
+
205
+ # Build CLI
206
+ go build -o tracelet ./cmd/tracelet
207
+
208
+ # Build VS Code extension
209
+ cd ui/vscode-extension
210
+ npm install
211
+ npm run compile
212
+ ```
213
+
214
+ ## Project Structure
215
+
216
+ ```
217
+ tracelet/
218
+ ā”œā”€ cmd/tracelet/ # CLI entrypoint
219
+ ā”œā”€ internal/ # Core packages
220
+ │ ā”œā”€ config/ # Config loader
221
+ │ ā”œā”€ lint/ # Linting engine
222
+ │ ā”œā”€ probe/ # Chrome probe
223
+ │ ā”œā”€ hud/ # HUD server
224
+ │ ā”œā”€ ci/ # CI helpers
225
+ │ └─ reporters/ # Output formatters
226
+ ā”œā”€ adapters/ # Framework adapters
227
+ ā”œā”€ ui/vscode-extension/ # VS Code extension
228
+ ā”œā”€ tests/ # Tests and fixtures
229
+ └─ docs/ # Documentation
230
+ ```
231
+
232
+ ## License
233
+
234
+ MIT
235
+
236
+ ## Contributing
237
+
238
+ Contributions welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines and
239
+ our [Code of Conduct](./CODE_OF_CONDUCT.md). Release history is in
240
+ [CHANGELOG.md](./CHANGELOG.md).
241
+
242
+ ---
243
+
244
+ **Tracelet** — performance you can lint.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { spawn } = require('child_process');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ // Platform detection
10
+ const platform = process.platform;
11
+ const arch = process.arch;
12
+
13
+ // Map Node.js platform/arch to our binary names
14
+ const platformMap = {
15
+ 'darwin': platform === 'darwin' ? (arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64') : null,
16
+ 'linux': platform === 'linux' ? (arch === 'arm64' ? 'linux-arm64' : 'linux-x64') : null,
17
+ 'win32': platform === 'win32' ? (arch === 'arm64' ? 'win32-arm64' : 'win32-x64') : null,
18
+ };
19
+
20
+ const binaryDir = platformMap[platform] || platformMap[process.platform];
21
+ if (!binaryDir) {
22
+ console.error(`[tracelet] Unsupported platform: ${platform}-${arch}`);
23
+ process.exit(1);
24
+ }
25
+
26
+ const binaryName = platform === 'win32' ? 'tracelet.exe' : 'tracelet';
27
+ const binaryPath = path.join(__dirname, '..', 'binaries', binaryDir, binaryName);
28
+
29
+ // Check if binary exists
30
+ if (!fs.existsSync(binaryPath)) {
31
+ console.error(`[tracelet] Binary not found at ${binaryPath}`);
32
+ console.error(`[tracelet] Please run 'npm run postinstall' or reinstall the package`);
33
+ process.exit(1);
34
+ }
35
+
36
+ // Make binary executable on Unix-like systems
37
+ if (platform !== 'win32') {
38
+ try {
39
+ fs.chmodSync(binaryPath, '755');
40
+ } catch (e) {
41
+ // Ignore if chmod fails
42
+ }
43
+ }
44
+
45
+ // Spawn binary with forwarded args
46
+ const args = process.argv.slice(2);
47
+ const child = spawn(binaryPath, args, {
48
+ stdio: 'inherit',
49
+ cwd: process.cwd(),
50
+ });
51
+
52
+ child.on('error', (err) => {
53
+ console.error(`[tracelet] Failed to spawn binary:`, err.message);
54
+ process.exit(1);
55
+ });
56
+
57
+ child.on('exit', (code) => {
58
+ process.exit(code || 0);
59
+ });
60
+
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@traceletdev/cli",
3
+ "version": "0.5.0",
4
+ "description": "Performance toolkit that brings Lighthouse-level insight and ESLint-level ergonomics",
5
+ "type": "commonjs",
6
+ "main": "bin/tracelet.js",
7
+ "bin": {
8
+ "tracelet": "bin/tracelet.js"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "scripts": {
14
+ "postinstall": "node scripts/postinstall.js",
15
+ "build:bin": "echo 'Run: goreleaser release --snapshot to build binaries, or use go build'",
16
+ "pack:bin": "node scripts/pack-binaries.js",
17
+ "prepare:publish": "npm run pack:bin && npm run test:packages",
18
+ "test:packages": "echo 'Package structure tests would go here'",
19
+ "publish:all": "node scripts/publish-all.js",
20
+ "publish:dry": "node scripts/publish-all.js --dry-run",
21
+ "format": "prettier --write \"{adapters,packages,scripts,examples}/**/*.{js,cjs,mjs,json}\" \"ui/vscode-extension/src/**/*.ts\" \"*.json\"",
22
+ "format:check": "prettier --check \"{adapters,packages,scripts,examples}/**/*.{js,cjs,mjs,json}\" \"ui/vscode-extension/src/**/*.ts\" \"*.json\"",
23
+ "lint": "eslint .",
24
+ "lint:fix": "eslint . --fix",
25
+ "prepare": "husky"
26
+ },
27
+ "engines": {
28
+ "node": ">=18.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/js": "^9.39.4",
32
+ "eslint": "^9.39.4",
33
+ "eslint-config-prettier": "^10.1.8",
34
+ "globals": "^16.5.0",
35
+ "husky": "^9.1.7",
36
+ "prettier": "^3.6.2",
37
+ "typescript": "^5.9.3",
38
+ "typescript-eslint": "^8.62.1"
39
+ },
40
+ "workspaces": [
41
+ "packages/*"
42
+ ],
43
+ "keywords": [
44
+ "performance",
45
+ "budgets",
46
+ "linting",
47
+ "monitoring",
48
+ "nextjs",
49
+ "vite",
50
+ "web-vitals"
51
+ ],
52
+ "author": "Moiz Imran <moizwasti@gmail.com>",
53
+ "license": "MIT",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/traceletdev/tracelet.git"
57
+ },
58
+ "files": [
59
+ "bin/",
60
+ "binaries/",
61
+ "scripts/",
62
+ "README.md",
63
+ "LICENSE",
64
+ "package.json"
65
+ ]
66
+ }
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ /**
9
+ * Pack Go binaries into npm package structure
10
+ *
11
+ * Expects binaries to be built and placed in dist/ directory
12
+ * (by GoReleaser or manual build)
13
+ *
14
+ * Structure:
15
+ * dist/
16
+ * tracelet_darwin_amd64/tracelet
17
+ * tracelet_darwin_arm64/tracelet
18
+ * tracelet_linux_amd64/tracelet
19
+ * tracelet_linux_arm64/tracelet
20
+ * tracelet_windows_amd64/tracelet.exe
21
+ * tracelet_windows_arm64/tracelet.exe
22
+ *
23
+ * Output:
24
+ * binaries/
25
+ * darwin-x64/tracelet
26
+ * darwin-arm64/tracelet
27
+ * linux-x64/tracelet
28
+ * linux-arm64/tracelet
29
+ * win32-x64/tracelet.exe
30
+ * win32-arm64/tracelet.exe
31
+ */
32
+
33
+ const distDir = path.join(__dirname, '..', 'dist');
34
+ const binariesDir = path.join(__dirname, '..', 'binaries');
35
+
36
+ // Map from GoReleaser naming to npm naming
37
+ const platformMap = {
38
+ darwin: {
39
+ amd64: { dir: 'darwin-x64', name: 'tracelet' },
40
+ arm64: { dir: 'darwin-arm64', name: 'tracelet' },
41
+ },
42
+ linux: {
43
+ amd64: { dir: 'linux-x64', name: 'tracelet' },
44
+ arm64: { dir: 'linux-arm64', name: 'tracelet' },
45
+ },
46
+ windows: {
47
+ amd64: { dir: 'win32-x64', name: 'tracelet.exe' },
48
+ arm64: { dir: 'win32-arm64', name: 'tracelet.exe' },
49
+ },
50
+ };
51
+
52
+ function packBinaries() {
53
+ if (!fs.existsSync(distDir)) {
54
+ console.error('dist/ directory not found. Run GoReleaser or build binaries first.');
55
+ process.exit(1);
56
+ }
57
+
58
+ // Create binaries directory structure
59
+ if (!fs.existsSync(binariesDir)) {
60
+ fs.mkdirSync(binariesDir, { recursive: true });
61
+ }
62
+
63
+ let packed = 0;
64
+ let skipped = 0;
65
+
66
+ // Find all directories in dist/
67
+ const entries = fs.readdirSync(distDir, { withFileTypes: true });
68
+
69
+ for (const entry of entries) {
70
+ if (!entry.isDirectory()) continue;
71
+
72
+ const dirName = entry.name;
73
+ // Expected format: tracelet_<os>_<arch> or tracelet_<os>_<arch>v7
74
+ const match = dirName.match(/^tracelet_(darwin|linux|windows)_(amd64|arm64)(?:v\d+)?$/);
75
+ if (!match) {
76
+ console.warn(`Skipping unknown directory: ${dirName}`);
77
+ skipped++;
78
+ continue;
79
+ }
80
+
81
+ const [, goos, goarch] = match;
82
+ const mapping = platformMap[goos]?.[goarch];
83
+ if (!mapping) {
84
+ console.warn(`No mapping for ${goos}/${goarch}`);
85
+ skipped++;
86
+ continue;
87
+ }
88
+
89
+ const sourceDir = path.join(distDir, dirName);
90
+ const sourceBinary = path.join(sourceDir, mapping.name);
91
+ const targetDir = path.join(binariesDir, mapping.dir);
92
+ const targetBinary = path.join(targetDir, mapping.name);
93
+
94
+ if (!fs.existsSync(sourceBinary)) {
95
+ console.warn(`Binary not found: ${sourceBinary}`);
96
+ skipped++;
97
+ continue;
98
+ }
99
+
100
+ // Create target directory
101
+ if (!fs.existsSync(targetDir)) {
102
+ fs.mkdirSync(targetDir, { recursive: true });
103
+ }
104
+
105
+ // Copy binary
106
+ fs.copyFileSync(sourceBinary, targetBinary);
107
+
108
+ // Make executable on Unix
109
+ if (goos !== 'windows') {
110
+ fs.chmodSync(targetBinary, '755');
111
+ }
112
+
113
+ console.log(`āœ“ Packed ${goos}/${goarch} → ${mapping.dir}/${mapping.name}`);
114
+ packed++;
115
+ }
116
+
117
+ if (packed === 0 && skipped > 0) {
118
+ console.error('No binaries were packed. Check that dist/ contains built binaries.');
119
+ process.exit(1);
120
+ }
121
+
122
+ console.log(`\nāœ“ Packed ${packed} binaries to binaries/`);
123
+ if (skipped > 0) {
124
+ console.warn(`⚠ Skipped ${skipped} entries`);
125
+ }
126
+ }
127
+
128
+ if (require.main === module) {
129
+ packBinaries();
130
+ }
131
+
132
+ module.exports = packBinaries;
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ // Platform detection
9
+ const platform = process.platform;
10
+ const arch = process.arch;
11
+
12
+ // Map Node.js platform/arch to our binary names
13
+ let binaryDir;
14
+ if (platform === 'darwin') {
15
+ binaryDir = arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
16
+ } else if (platform === 'linux') {
17
+ binaryDir = arch === 'arm64' ? 'linux-arm64' : 'linux-x64';
18
+ } else if (platform === 'win32') {
19
+ binaryDir = arch === 'arm64' ? 'win32-arm64' : 'win32-x64';
20
+ } else {
21
+ console.warn(`[tracelet] Unsupported platform: ${platform}-${arch}`);
22
+ process.exit(0);
23
+ }
24
+
25
+ const binaryName = platform === 'win32' ? 'tracelet.exe' : 'tracelet';
26
+ const sourcePath = path.join(__dirname, '..', 'binaries', binaryDir, binaryName);
27
+ const targetPath = path.join(__dirname, '..', 'node_modules', '.bin', binaryName);
28
+
29
+ // Check if source binary exists
30
+ if (!fs.existsSync(sourcePath)) {
31
+ console.warn(`[tracelet] Binary not found for ${platform}-${arch}, skipping postinstall`);
32
+ console.warn(`[tracelet] You may need to build binaries first`);
33
+ process.exit(0);
34
+ }
35
+
36
+ // Ensure .bin directory exists
37
+ const binDir = path.dirname(targetPath);
38
+ if (!fs.existsSync(binDir)) {
39
+ fs.mkdirSync(binDir, { recursive: true });
40
+ }
41
+
42
+ // Copy or symlink binary
43
+ try {
44
+ // Try symlink first (works on Unix and Windows with proper permissions)
45
+ if (fs.existsSync(targetPath)) {
46
+ fs.unlinkSync(targetPath);
47
+ }
48
+
49
+ if (platform === 'win32') {
50
+ // Windows: copy instead of symlink for better compatibility
51
+ fs.copyFileSync(sourcePath, targetPath);
52
+ } else {
53
+ // Unix: use symlink
54
+ fs.symlinkSync(path.relative(binDir, sourcePath), targetPath);
55
+ }
56
+
57
+ // Make executable on Unix
58
+ if (platform !== 'win32') {
59
+ fs.chmodSync(targetPath, '755');
60
+ }
61
+
62
+ console.log(`[tracelet] Binary linked successfully for ${platform}-${arch}`);
63
+ } catch (err) {
64
+ console.warn(`[tracelet] Failed to link binary:`, err.message);
65
+ console.warn(`[tracelet] You can still use 'npx tracelet' or the full path`);
66
+ }
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ /**
6
+ * Publish all packages to npm
7
+ *
8
+ * Ensures version sync across packages and publishes:
9
+ * - @traceletdev/cli (main package)
10
+ * - packages/tracelet-next -> @traceletdev/next
11
+ * - packages/tracelet-vite -> @traceletdev/vite
12
+ * - packages/tracelet-react -> @traceletdev/react
13
+ *
14
+ * Usage:
15
+ * node scripts/publish-all.js [version]
16
+ *
17
+ * If version is provided, updates all package.json files
18
+ * If not, publishes current versions
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const { execSync } = require('child_process');
24
+
25
+ const packages = [
26
+ { name: '@traceletdev/cli', path: '.' },
27
+ { name: '@traceletdev/next', path: 'packages/tracelet-next' },
28
+ { name: '@traceletdev/vite', path: 'packages/tracelet-vite' },
29
+ { name: '@traceletdev/react', path: 'packages/tracelet-react' },
30
+ ];
31
+
32
+ function getVersion(pkgPath) {
33
+ const pkgJsonPath = path.join(pkgPath, 'package.json');
34
+ if (!fs.existsSync(pkgJsonPath)) {
35
+ return null;
36
+ }
37
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
38
+ return pkg.version;
39
+ }
40
+
41
+ function setVersion(pkgPath, version) {
42
+ const pkgJsonPath = path.join(pkgPath, 'package.json');
43
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
44
+ pkg.version = version;
45
+ fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + '\n');
46
+ console.log(`āœ“ Updated ${pkg.name} to ${version}`);
47
+ }
48
+
49
+ function publishPackage(pkgPath, dryRun = false) {
50
+ const cmd = dryRun ? 'npm publish --dry-run' : 'npm publish';
51
+ console.log(`\nšŸ“¦ Publishing ${path.basename(pkgPath)}...`);
52
+ try {
53
+ execSync(cmd, { cwd: pkgPath, stdio: 'inherit' });
54
+ console.log(`āœ“ Published ${path.basename(pkgPath)}`);
55
+ } catch (err) {
56
+ console.error(`āœ— Failed to publish ${path.basename(pkgPath)}`);
57
+ throw err;
58
+ }
59
+ }
60
+
61
+ function main() {
62
+ const args = process.argv.slice(2);
63
+ const dryRun = args.includes('--dry-run');
64
+ let newVersion = args.find(arg => !arg.startsWith('--'));
65
+
66
+ // Check if binaries are built
67
+ const binariesDir = path.join(process.cwd(), 'binaries');
68
+ if (!fs.existsSync(binariesDir)) {
69
+ console.warn('⚠ binaries/ directory not found. Run "npm run pack:bin" first.');
70
+ console.warn('⚠ Publishing without binaries will result in broken package.');
71
+ if (!dryRun) {
72
+ console.error('āœ— Aborting publish. Build binaries first.');
73
+ process.exit(1);
74
+ }
75
+ }
76
+
77
+ // Get current versions
78
+ const versions = packages
79
+ .map(pkg => ({
80
+ ...pkg,
81
+ version: getVersion(pkg.path),
82
+ }))
83
+ .filter(pkg => pkg.version !== null);
84
+
85
+ if (versions.length === 0) {
86
+ console.error('No packages found with package.json');
87
+ process.exit(1);
88
+ }
89
+
90
+ const currentVersion = versions[0].version;
91
+ console.log(`Current version: ${currentVersion}`);
92
+
93
+ // Update versions if new version provided
94
+ if (newVersion) {
95
+ if (!/^\d+\.\d+\.\d+/.test(newVersion)) {
96
+ console.error('Invalid version format. Use semantic versioning (e.g., 0.5.0)');
97
+ process.exit(1);
98
+ }
99
+ console.log(`\nšŸ“ Updating all packages to ${newVersion}...`);
100
+ versions.forEach(pkg => setVersion(pkg.path, newVersion));
101
+ }
102
+
103
+ // Publish packages
104
+ console.log(`\nšŸš€ Publishing packages${dryRun ? ' (dry run)' : ''}...`);
105
+ versions.forEach(pkg => {
106
+ publishPackage(pkg.path, dryRun);
107
+ });
108
+
109
+ console.log('\nāœ… All packages published successfully!');
110
+ }
111
+
112
+ if (require.main === module) {
113
+ main();
114
+ }
115
+
116
+ module.exports = { getVersion, setVersion, publishPackage };