@upgoose/vite-plugin 0.1.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/README.md +102 -0
- package/dist/chunk-FQ6FRVNL.js +103 -0
- package/dist/cli.cjs +181 -0
- package/dist/cli.js +87 -0
- package/dist/index.cjs +193 -0
- package/dist/index.d.cts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +73 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @upgoose/vite-plugin
|
|
2
|
+
|
|
3
|
+
Upload source maps to [Upgoose](https://upgoose.app) automatically on build, so
|
|
4
|
+
error stack traces are symbolicated back to original file, line, column, and
|
|
5
|
+
function names — no more `index-BaRQ9avt.js:1:48211`.
|
|
6
|
+
|
|
7
|
+
Ships two ways to upload:
|
|
8
|
+
|
|
9
|
+
- a **Vite plugin** that runs after `vite build`, and
|
|
10
|
+
- a **CLI** (`upgoose-sourcemaps`) for CI or non-Vite builds.
|
|
11
|
+
|
|
12
|
+
Zero runtime dependencies (uses Node 18+ `fetch`/`FormData`). Vite is an
|
|
13
|
+
optional peer dependency — the CLI works without it.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -D @upgoose/vite-plugin
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Vite plugin
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts
|
|
25
|
+
import { defineConfig } from "vite";
|
|
26
|
+
import { upgooseSourceMaps } from "@upgoose/vite-plugin";
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
// Emit .map files WITHOUT a //# sourceMappingURL comment, so browsers can't
|
|
30
|
+
// fetch your source but the plugin can still upload the maps.
|
|
31
|
+
build: { sourcemap: "hidden" },
|
|
32
|
+
plugins: [
|
|
33
|
+
upgooseSourceMaps({
|
|
34
|
+
projectId: process.env.UPGOOSE_PROJECT_ID,
|
|
35
|
+
apiKey: process.env.UPGOOSE_API_KEY,
|
|
36
|
+
// release defaults to the short git SHA (see "Matching the release" below)
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
On `vite build` the plugin collects every `.map` file from the output
|
|
43
|
+
directory, uploads them in a single request tagged with the release, and (by
|
|
44
|
+
default) deletes them from the build output so they never ship to production.
|
|
45
|
+
|
|
46
|
+
If `apiKey`/`projectId` aren't set, it logs a warning and skips — it never
|
|
47
|
+
fails your build for missing credentials.
|
|
48
|
+
|
|
49
|
+
### Options
|
|
50
|
+
|
|
51
|
+
| Option | Default | Description |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `apiKey` | `UPGOOSE_API_KEY` | Project API key (`ug_…`). |
|
|
54
|
+
| `projectId` | `UPGOOSE_PROJECT_ID` | Project ID or public numeric ID. |
|
|
55
|
+
| `release` | `UPGOOSE_RELEASE` → git short SHA | Tag the maps are stored under. Must match `upgoose.init({ release })`. |
|
|
56
|
+
| `apiUrl` | `UPGOOSE_API_URL` → `https://api.upgoose.app` | API base URL. |
|
|
57
|
+
| `outDir` | Vite's `build.outDir` | Directory scanned for `.map` files. |
|
|
58
|
+
| `clean` | `false` | DELETE existing maps for this release before uploading. |
|
|
59
|
+
| `deleteAfterUpload` | `true` | Remove `.map` files from the build output after upload. |
|
|
60
|
+
| `failOnError` | `false` | Throw (fail the build) on upload error instead of warning. |
|
|
61
|
+
| `silent` | `false` | Suppress `[upgoose]` logs. |
|
|
62
|
+
| `disable` | `false` | Skip the upload entirely (e.g. gate on an env flag). |
|
|
63
|
+
|
|
64
|
+
## CLI
|
|
65
|
+
|
|
66
|
+
For CI pipelines or bundlers other than Vite:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
upgoose-sourcemaps \
|
|
70
|
+
--dir dist \
|
|
71
|
+
--release "$(git rev-parse --short HEAD)" \
|
|
72
|
+
--project-id "$UPGOOSE_PROJECT_ID" \
|
|
73
|
+
--api-key "$UPGOOSE_API_KEY"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Flags fall back to the same env vars as the plugin. Run `upgoose-sourcemaps
|
|
77
|
+
--help` for the full list. Unlike the plugin, the CLI keeps `.map` files on disk
|
|
78
|
+
by default — pass `--delete-after` (or `rm` them yourself before deploying).
|
|
79
|
+
|
|
80
|
+
## Matching the release
|
|
81
|
+
|
|
82
|
+
Symbolication looks maps up by `release`, so the value here **must equal** the
|
|
83
|
+
`release` you pass to the browser SDK:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// app entry
|
|
87
|
+
import upgoose from "@upgoose/browser";
|
|
88
|
+
upgoose.init({
|
|
89
|
+
apiKey: "ug_…",
|
|
90
|
+
apiUrl: "https://api.upgoose.app",
|
|
91
|
+
release: import.meta.env.VITE_BUILD_SHA, // same SHA the plugin uploads under
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The simplest setup: let both default to the git SHA. The plugin uses
|
|
96
|
+
`git rev-parse --short HEAD`; set your app's release from the same value (e.g.
|
|
97
|
+
`VITE_BUILD_SHA=$(git rev-parse --short HEAD)` at build time). If the two
|
|
98
|
+
don't match, the worker won't find the maps and traces stay minified.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
BSD-3-Clause
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/upload.ts
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import { readdir, readFile, unlink } from "fs/promises";
|
|
4
|
+
import { basename, join } from "path";
|
|
5
|
+
var DEFAULT_API_URL = "https://api.upgoose.app";
|
|
6
|
+
function gitShortSha() {
|
|
7
|
+
try {
|
|
8
|
+
const sha = execFileSync("git", ["rev-parse", "--short", "HEAD"], {
|
|
9
|
+
encoding: "utf8",
|
|
10
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11
|
+
}).trim();
|
|
12
|
+
return sha || null;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function resolveConfig(input) {
|
|
18
|
+
const apiKey = input.apiKey ?? process.env.UPGOOSE_API_KEY;
|
|
19
|
+
const projectId = input.projectId != null ? String(input.projectId) : process.env.UPGOOSE_PROJECT_ID;
|
|
20
|
+
const apiUrl = (input.apiUrl ?? process.env.UPGOOSE_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
|
|
21
|
+
const release = input.release ?? process.env.UPGOOSE_RELEASE ?? gitShortSha() ?? void 0;
|
|
22
|
+
const missing = [];
|
|
23
|
+
if (!apiKey) missing.push("apiKey (UPGOOSE_API_KEY)");
|
|
24
|
+
if (!projectId) missing.push("projectId (UPGOOSE_PROJECT_ID)");
|
|
25
|
+
if (!release) missing.push("release (UPGOOSE_RELEASE, or run inside a git checkout)");
|
|
26
|
+
if (missing.length > 0) return { missing };
|
|
27
|
+
return { config: { apiKey, apiUrl, projectId, release }, missing: [] };
|
|
28
|
+
}
|
|
29
|
+
async function findMapFiles(dir) {
|
|
30
|
+
const out = [];
|
|
31
|
+
async function walk(d) {
|
|
32
|
+
let entries;
|
|
33
|
+
try {
|
|
34
|
+
entries = await readdir(d, { withFileTypes: true });
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const full = join(d, entry.name);
|
|
40
|
+
if (entry.isDirectory()) await walk(full);
|
|
41
|
+
else if (entry.isFile() && entry.name.endsWith(".map")) out.push(full);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
await walk(dir);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
async function uploadSourceMaps(config, files, options = {}) {
|
|
48
|
+
const log = options.log ?? (() => {
|
|
49
|
+
});
|
|
50
|
+
if (files.length === 0) {
|
|
51
|
+
return { uploaded: [], release: config.release, deletedFromDisk: 0 };
|
|
52
|
+
}
|
|
53
|
+
const base = `${config.apiUrl}/api/${encodeURIComponent(config.projectId)}/source-maps`;
|
|
54
|
+
if (options.clean) {
|
|
55
|
+
const res2 = await fetch(`${base}/${encodeURIComponent(config.release)}`, {
|
|
56
|
+
method: "DELETE",
|
|
57
|
+
headers: { "X-API-Key": config.apiKey }
|
|
58
|
+
});
|
|
59
|
+
if (res2.ok) {
|
|
60
|
+
const body2 = await res2.json().catch(() => ({}));
|
|
61
|
+
log(`cleaned ${body2.deleted ?? 0} existing map(s) for release ${config.release}`);
|
|
62
|
+
} else {
|
|
63
|
+
log(`clean skipped \u2014 server returned ${res2.status}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const form = new FormData();
|
|
67
|
+
form.append("release", config.release);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const buf = await readFile(file);
|
|
70
|
+
form.append("file", new Blob([buf]), basename(file));
|
|
71
|
+
}
|
|
72
|
+
const res = await fetch(base, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: { "X-API-Key": config.apiKey },
|
|
75
|
+
body: form
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const text = await res.text().catch(() => "");
|
|
79
|
+
throw new Error(
|
|
80
|
+
`source-map upload failed: ${res.status} ${res.statusText}${text ? ` \u2014 ${text}` : ""}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const body = await res.json().catch(() => ({}));
|
|
84
|
+
let deletedFromDisk = 0;
|
|
85
|
+
if (options.deleteAfterUpload) {
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
try {
|
|
88
|
+
await unlink(file);
|
|
89
|
+
deletedFromDisk++;
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { uploaded: body.uploaded ?? [], release: config.release, deletedFromDisk };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
DEFAULT_API_URL,
|
|
99
|
+
gitShortSha,
|
|
100
|
+
resolveConfig,
|
|
101
|
+
findMapFiles,
|
|
102
|
+
uploadSourceMaps
|
|
103
|
+
};
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
var import_node_util = require("util");
|
|
6
|
+
var import_node_path2 = require("path");
|
|
7
|
+
|
|
8
|
+
// src/upload.ts
|
|
9
|
+
var import_node_child_process = require("child_process");
|
|
10
|
+
var import_promises = require("fs/promises");
|
|
11
|
+
var import_node_path = require("path");
|
|
12
|
+
var DEFAULT_API_URL = "https://api.upgoose.app";
|
|
13
|
+
function gitShortSha() {
|
|
14
|
+
try {
|
|
15
|
+
const sha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "--short", "HEAD"], {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
18
|
+
}).trim();
|
|
19
|
+
return sha || null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function resolveConfig(input) {
|
|
25
|
+
const apiKey = input.apiKey ?? process.env.UPGOOSE_API_KEY;
|
|
26
|
+
const projectId = input.projectId != null ? String(input.projectId) : process.env.UPGOOSE_PROJECT_ID;
|
|
27
|
+
const apiUrl = (input.apiUrl ?? process.env.UPGOOSE_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
|
|
28
|
+
const release = input.release ?? process.env.UPGOOSE_RELEASE ?? gitShortSha() ?? void 0;
|
|
29
|
+
const missing = [];
|
|
30
|
+
if (!apiKey) missing.push("apiKey (UPGOOSE_API_KEY)");
|
|
31
|
+
if (!projectId) missing.push("projectId (UPGOOSE_PROJECT_ID)");
|
|
32
|
+
if (!release) missing.push("release (UPGOOSE_RELEASE, or run inside a git checkout)");
|
|
33
|
+
if (missing.length > 0) return { missing };
|
|
34
|
+
return { config: { apiKey, apiUrl, projectId, release }, missing: [] };
|
|
35
|
+
}
|
|
36
|
+
async function findMapFiles(dir) {
|
|
37
|
+
const out = [];
|
|
38
|
+
async function walk(d) {
|
|
39
|
+
let entries;
|
|
40
|
+
try {
|
|
41
|
+
entries = await (0, import_promises.readdir)(d, { withFileTypes: true });
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const full = (0, import_node_path.join)(d, entry.name);
|
|
47
|
+
if (entry.isDirectory()) await walk(full);
|
|
48
|
+
else if (entry.isFile() && entry.name.endsWith(".map")) out.push(full);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
await walk(dir);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
async function uploadSourceMaps(config, files, options = {}) {
|
|
55
|
+
const log = options.log ?? (() => {
|
|
56
|
+
});
|
|
57
|
+
if (files.length === 0) {
|
|
58
|
+
return { uploaded: [], release: config.release, deletedFromDisk: 0 };
|
|
59
|
+
}
|
|
60
|
+
const base = `${config.apiUrl}/api/${encodeURIComponent(config.projectId)}/source-maps`;
|
|
61
|
+
if (options.clean) {
|
|
62
|
+
const res2 = await fetch(`${base}/${encodeURIComponent(config.release)}`, {
|
|
63
|
+
method: "DELETE",
|
|
64
|
+
headers: { "X-API-Key": config.apiKey }
|
|
65
|
+
});
|
|
66
|
+
if (res2.ok) {
|
|
67
|
+
const body2 = await res2.json().catch(() => ({}));
|
|
68
|
+
log(`cleaned ${body2.deleted ?? 0} existing map(s) for release ${config.release}`);
|
|
69
|
+
} else {
|
|
70
|
+
log(`clean skipped \u2014 server returned ${res2.status}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const form = new FormData();
|
|
74
|
+
form.append("release", config.release);
|
|
75
|
+
for (const file of files) {
|
|
76
|
+
const buf = await (0, import_promises.readFile)(file);
|
|
77
|
+
form.append("file", new Blob([buf]), (0, import_node_path.basename)(file));
|
|
78
|
+
}
|
|
79
|
+
const res = await fetch(base, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "X-API-Key": config.apiKey },
|
|
82
|
+
body: form
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const text = await res.text().catch(() => "");
|
|
86
|
+
throw new Error(
|
|
87
|
+
`source-map upload failed: ${res.status} ${res.statusText}${text ? ` \u2014 ${text}` : ""}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const body = await res.json().catch(() => ({}));
|
|
91
|
+
let deletedFromDisk = 0;
|
|
92
|
+
if (options.deleteAfterUpload) {
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
try {
|
|
95
|
+
await (0, import_promises.unlink)(file);
|
|
96
|
+
deletedFromDisk++;
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { uploaded: body.uploaded ?? [], release: config.release, deletedFromDisk };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/cli.ts
|
|
105
|
+
var HELP = `upgoose-sourcemaps \u2014 upload source maps to Upgoose
|
|
106
|
+
|
|
107
|
+
Usage:
|
|
108
|
+
upgoose-sourcemaps [options]
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
--dir <path> Directory to scan for .map files (default: dist)
|
|
112
|
+
--release <tag> Release tag; must match upgoose.init({ release })
|
|
113
|
+
(default: UPGOOSE_RELEASE or the short git SHA)
|
|
114
|
+
--project-id <id> Project ID or public numeric ID (or UPGOOSE_PROJECT_ID)
|
|
115
|
+
--api-key <key> Project API key, ug_\u2026 (or UPGOOSE_API_KEY)
|
|
116
|
+
--api-url <url> API base URL (or UPGOOSE_API_URL; default api.upgoose.app)
|
|
117
|
+
--clean Delete existing maps for this release before uploading
|
|
118
|
+
--delete-after Delete .map files from disk after a successful upload
|
|
119
|
+
--silent Suppress log output
|
|
120
|
+
-h, --help Show this help
|
|
121
|
+
`;
|
|
122
|
+
async function main() {
|
|
123
|
+
const { values } = (0, import_node_util.parseArgs)({
|
|
124
|
+
options: {
|
|
125
|
+
dir: { type: "string" },
|
|
126
|
+
release: { type: "string" },
|
|
127
|
+
"project-id": { type: "string" },
|
|
128
|
+
"api-key": { type: "string" },
|
|
129
|
+
"api-url": { type: "string" },
|
|
130
|
+
clean: { type: "boolean" },
|
|
131
|
+
"delete-after": { type: "boolean" },
|
|
132
|
+
silent: { type: "boolean" },
|
|
133
|
+
help: { type: "boolean", short: "h" }
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (values.help) {
|
|
137
|
+
process.stdout.write(HELP);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const { config, missing } = resolveConfig({
|
|
141
|
+
apiKey: values["api-key"],
|
|
142
|
+
apiUrl: values["api-url"],
|
|
143
|
+
projectId: values["project-id"],
|
|
144
|
+
release: values.release
|
|
145
|
+
});
|
|
146
|
+
if (!config) {
|
|
147
|
+
console.error(`[upgoose] missing required config: ${missing.join(", ")}`);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const dir = (0, import_node_path2.resolve)(process.cwd(), values.dir ?? "dist");
|
|
152
|
+
const files = await findMapFiles(dir);
|
|
153
|
+
if (files.length === 0) {
|
|
154
|
+
console.error(`[upgoose] no .map files found in ${dir}`);
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const log = (msg) => {
|
|
159
|
+
if (!values.silent) console.log(`[upgoose] ${msg}`);
|
|
160
|
+
};
|
|
161
|
+
log(`uploading ${files.length} source map(s) for release "${config.release}"\u2026`);
|
|
162
|
+
try {
|
|
163
|
+
const result = await uploadSourceMaps(config, files, {
|
|
164
|
+
clean: values.clean,
|
|
165
|
+
// CLI keeps maps by default (it's often run as a discrete step); pass
|
|
166
|
+
// --delete-after to remove them, or rm them yourself before deploy.
|
|
167
|
+
deleteAfterUpload: values["delete-after"] ?? false,
|
|
168
|
+
log
|
|
169
|
+
});
|
|
170
|
+
log(
|
|
171
|
+
`done \u2014 uploaded ${result.uploaded.length} map(s)` + (result.deletedFromDisk ? `, removed ${result.deletedFromDisk} from disk` : "")
|
|
172
|
+
);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`[upgoose] ${err instanceof Error ? err.message : String(err)}`);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
main().catch((err) => {
|
|
179
|
+
console.error(`[upgoose] ${err instanceof Error ? err.message : String(err)}`);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
});
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
findMapFiles,
|
|
4
|
+
resolveConfig,
|
|
5
|
+
uploadSourceMaps
|
|
6
|
+
} from "./chunk-FQ6FRVNL.js";
|
|
7
|
+
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
import { parseArgs } from "util";
|
|
10
|
+
import { resolve } from "path";
|
|
11
|
+
var HELP = `upgoose-sourcemaps \u2014 upload source maps to Upgoose
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
upgoose-sourcemaps [options]
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--dir <path> Directory to scan for .map files (default: dist)
|
|
18
|
+
--release <tag> Release tag; must match upgoose.init({ release })
|
|
19
|
+
(default: UPGOOSE_RELEASE or the short git SHA)
|
|
20
|
+
--project-id <id> Project ID or public numeric ID (or UPGOOSE_PROJECT_ID)
|
|
21
|
+
--api-key <key> Project API key, ug_\u2026 (or UPGOOSE_API_KEY)
|
|
22
|
+
--api-url <url> API base URL (or UPGOOSE_API_URL; default api.upgoose.app)
|
|
23
|
+
--clean Delete existing maps for this release before uploading
|
|
24
|
+
--delete-after Delete .map files from disk after a successful upload
|
|
25
|
+
--silent Suppress log output
|
|
26
|
+
-h, --help Show this help
|
|
27
|
+
`;
|
|
28
|
+
async function main() {
|
|
29
|
+
const { values } = parseArgs({
|
|
30
|
+
options: {
|
|
31
|
+
dir: { type: "string" },
|
|
32
|
+
release: { type: "string" },
|
|
33
|
+
"project-id": { type: "string" },
|
|
34
|
+
"api-key": { type: "string" },
|
|
35
|
+
"api-url": { type: "string" },
|
|
36
|
+
clean: { type: "boolean" },
|
|
37
|
+
"delete-after": { type: "boolean" },
|
|
38
|
+
silent: { type: "boolean" },
|
|
39
|
+
help: { type: "boolean", short: "h" }
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (values.help) {
|
|
43
|
+
process.stdout.write(HELP);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const { config, missing } = resolveConfig({
|
|
47
|
+
apiKey: values["api-key"],
|
|
48
|
+
apiUrl: values["api-url"],
|
|
49
|
+
projectId: values["project-id"],
|
|
50
|
+
release: values.release
|
|
51
|
+
});
|
|
52
|
+
if (!config) {
|
|
53
|
+
console.error(`[upgoose] missing required config: ${missing.join(", ")}`);
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const dir = resolve(process.cwd(), values.dir ?? "dist");
|
|
58
|
+
const files = await findMapFiles(dir);
|
|
59
|
+
if (files.length === 0) {
|
|
60
|
+
console.error(`[upgoose] no .map files found in ${dir}`);
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const log = (msg) => {
|
|
65
|
+
if (!values.silent) console.log(`[upgoose] ${msg}`);
|
|
66
|
+
};
|
|
67
|
+
log(`uploading ${files.length} source map(s) for release "${config.release}"\u2026`);
|
|
68
|
+
try {
|
|
69
|
+
const result = await uploadSourceMaps(config, files, {
|
|
70
|
+
clean: values.clean,
|
|
71
|
+
// CLI keeps maps by default (it's often run as a discrete step); pass
|
|
72
|
+
// --delete-after to remove them, or rm them yourself before deploy.
|
|
73
|
+
deleteAfterUpload: values["delete-after"] ?? false,
|
|
74
|
+
log
|
|
75
|
+
});
|
|
76
|
+
log(
|
|
77
|
+
`done \u2014 uploaded ${result.uploaded.length} map(s)` + (result.deletedFromDisk ? `, removed ${result.deletedFromDisk} from disk` : "")
|
|
78
|
+
);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error(`[upgoose] ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
main().catch((err) => {
|
|
85
|
+
console.error(`[upgoose] ${err instanceof Error ? err.message : String(err)}`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
});
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
DEFAULT_API_URL: () => DEFAULT_API_URL,
|
|
24
|
+
default: () => src_default,
|
|
25
|
+
findMapFiles: () => findMapFiles,
|
|
26
|
+
gitShortSha: () => gitShortSha,
|
|
27
|
+
resolveConfig: () => resolveConfig,
|
|
28
|
+
upgooseSourceMaps: () => upgooseSourceMaps,
|
|
29
|
+
uploadSourceMaps: () => uploadSourceMaps
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(src_exports);
|
|
32
|
+
var import_node_path2 = require("path");
|
|
33
|
+
|
|
34
|
+
// src/upload.ts
|
|
35
|
+
var import_node_child_process = require("child_process");
|
|
36
|
+
var import_promises = require("fs/promises");
|
|
37
|
+
var import_node_path = require("path");
|
|
38
|
+
var DEFAULT_API_URL = "https://api.upgoose.app";
|
|
39
|
+
function gitShortSha() {
|
|
40
|
+
try {
|
|
41
|
+
const sha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "--short", "HEAD"], {
|
|
42
|
+
encoding: "utf8",
|
|
43
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
44
|
+
}).trim();
|
|
45
|
+
return sha || null;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function resolveConfig(input) {
|
|
51
|
+
const apiKey = input.apiKey ?? process.env.UPGOOSE_API_KEY;
|
|
52
|
+
const projectId = input.projectId != null ? String(input.projectId) : process.env.UPGOOSE_PROJECT_ID;
|
|
53
|
+
const apiUrl = (input.apiUrl ?? process.env.UPGOOSE_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
|
|
54
|
+
const release = input.release ?? process.env.UPGOOSE_RELEASE ?? gitShortSha() ?? void 0;
|
|
55
|
+
const missing = [];
|
|
56
|
+
if (!apiKey) missing.push("apiKey (UPGOOSE_API_KEY)");
|
|
57
|
+
if (!projectId) missing.push("projectId (UPGOOSE_PROJECT_ID)");
|
|
58
|
+
if (!release) missing.push("release (UPGOOSE_RELEASE, or run inside a git checkout)");
|
|
59
|
+
if (missing.length > 0) return { missing };
|
|
60
|
+
return { config: { apiKey, apiUrl, projectId, release }, missing: [] };
|
|
61
|
+
}
|
|
62
|
+
async function findMapFiles(dir) {
|
|
63
|
+
const out = [];
|
|
64
|
+
async function walk(d) {
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = await (0, import_promises.readdir)(d, { withFileTypes: true });
|
|
68
|
+
} catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const full = (0, import_node_path.join)(d, entry.name);
|
|
73
|
+
if (entry.isDirectory()) await walk(full);
|
|
74
|
+
else if (entry.isFile() && entry.name.endsWith(".map")) out.push(full);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
await walk(dir);
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
async function uploadSourceMaps(config, files, options = {}) {
|
|
81
|
+
const log = options.log ?? (() => {
|
|
82
|
+
});
|
|
83
|
+
if (files.length === 0) {
|
|
84
|
+
return { uploaded: [], release: config.release, deletedFromDisk: 0 };
|
|
85
|
+
}
|
|
86
|
+
const base = `${config.apiUrl}/api/${encodeURIComponent(config.projectId)}/source-maps`;
|
|
87
|
+
if (options.clean) {
|
|
88
|
+
const res2 = await fetch(`${base}/${encodeURIComponent(config.release)}`, {
|
|
89
|
+
method: "DELETE",
|
|
90
|
+
headers: { "X-API-Key": config.apiKey }
|
|
91
|
+
});
|
|
92
|
+
if (res2.ok) {
|
|
93
|
+
const body2 = await res2.json().catch(() => ({}));
|
|
94
|
+
log(`cleaned ${body2.deleted ?? 0} existing map(s) for release ${config.release}`);
|
|
95
|
+
} else {
|
|
96
|
+
log(`clean skipped \u2014 server returned ${res2.status}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const form = new FormData();
|
|
100
|
+
form.append("release", config.release);
|
|
101
|
+
for (const file of files) {
|
|
102
|
+
const buf = await (0, import_promises.readFile)(file);
|
|
103
|
+
form.append("file", new Blob([buf]), (0, import_node_path.basename)(file));
|
|
104
|
+
}
|
|
105
|
+
const res = await fetch(base, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: { "X-API-Key": config.apiKey },
|
|
108
|
+
body: form
|
|
109
|
+
});
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
const text = await res.text().catch(() => "");
|
|
112
|
+
throw new Error(
|
|
113
|
+
`source-map upload failed: ${res.status} ${res.statusText}${text ? ` \u2014 ${text}` : ""}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const body = await res.json().catch(() => ({}));
|
|
117
|
+
let deletedFromDisk = 0;
|
|
118
|
+
if (options.deleteAfterUpload) {
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
try {
|
|
121
|
+
await (0, import_promises.unlink)(file);
|
|
122
|
+
deletedFromDisk++;
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { uploaded: body.uploaded ?? [], release: config.release, deletedFromDisk };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/index.ts
|
|
131
|
+
function upgooseSourceMaps(options = {}) {
|
|
132
|
+
let absOutDir = "";
|
|
133
|
+
let sourcemapEnabled;
|
|
134
|
+
const log = (msg) => {
|
|
135
|
+
if (!options.silent) console.log(`[upgoose] ${msg}`);
|
|
136
|
+
};
|
|
137
|
+
const warn = (msg) => {
|
|
138
|
+
if (!options.silent) console.warn(`[upgoose] ${msg}`);
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
name: "upgoose-source-maps",
|
|
142
|
+
apply: "build",
|
|
143
|
+
enforce: "post",
|
|
144
|
+
configResolved(config) {
|
|
145
|
+
absOutDir = (0, import_node_path2.resolve)(config.root, options.outDir ?? config.build.outDir);
|
|
146
|
+
sourcemapEnabled = config.build.sourcemap;
|
|
147
|
+
},
|
|
148
|
+
async closeBundle() {
|
|
149
|
+
if (options.disable) return;
|
|
150
|
+
const { config, missing } = resolveConfig(options);
|
|
151
|
+
if (!config) {
|
|
152
|
+
warn(`source-map upload skipped \u2014 missing ${missing.join(", ")}.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!sourcemapEnabled) {
|
|
156
|
+
warn(
|
|
157
|
+
`build.sourcemap is disabled \u2014 nothing to upload. Set build.sourcemap: "hidden" so .map files are emitted without exposing your source.`
|
|
158
|
+
);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const files = await findMapFiles(absOutDir);
|
|
162
|
+
if (files.length === 0) {
|
|
163
|
+
warn(`no .map files found in ${absOutDir}.`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
log(`uploading ${files.length} source map(s) for release "${config.release}"\u2026`);
|
|
167
|
+
try {
|
|
168
|
+
const result = await uploadSourceMaps(config, files, {
|
|
169
|
+
clean: options.clean,
|
|
170
|
+
deleteAfterUpload: options.deleteAfterUpload ?? true,
|
|
171
|
+
log
|
|
172
|
+
});
|
|
173
|
+
log(
|
|
174
|
+
`uploaded ${result.uploaded.length} map(s)` + (result.deletedFromDisk ? `, removed ${result.deletedFromDisk} from the build output` : "")
|
|
175
|
+
);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
if (options.failOnError) throw err;
|
|
179
|
+
warn(message);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
var src_default = upgooseSourceMaps;
|
|
185
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
186
|
+
0 && (module.exports = {
|
|
187
|
+
DEFAULT_API_URL,
|
|
188
|
+
findMapFiles,
|
|
189
|
+
gitShortSha,
|
|
190
|
+
resolveConfig,
|
|
191
|
+
upgooseSourceMaps,
|
|
192
|
+
uploadSourceMaps
|
|
193
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
declare const DEFAULT_API_URL = "https://api.upgoose.app";
|
|
4
|
+
interface ResolveInput {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
projectId?: string | number;
|
|
8
|
+
release?: string;
|
|
9
|
+
}
|
|
10
|
+
interface ResolvedConfig {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
apiUrl: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
release: string;
|
|
15
|
+
}
|
|
16
|
+
/** Best-effort `git rev-parse --short HEAD`; null outside a git checkout. */
|
|
17
|
+
declare function gitShortSha(): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Merge explicit options with env vars and a git-SHA fallback. Returns either
|
|
20
|
+
* a fully-resolved config or the list of human-readable missing fields, so the
|
|
21
|
+
* caller can warn-and-skip (plugin) or hard-fail (CLI) as it sees fit.
|
|
22
|
+
*/
|
|
23
|
+
declare function resolveConfig(input: ResolveInput): {
|
|
24
|
+
config?: ResolvedConfig;
|
|
25
|
+
missing: string[];
|
|
26
|
+
};
|
|
27
|
+
/** Recursively collect absolute paths of every `.map` file under `dir`. */
|
|
28
|
+
declare function findMapFiles(dir: string): Promise<string[]>;
|
|
29
|
+
interface UploadOptions {
|
|
30
|
+
/** DELETE existing maps for this release before uploading (re-deploy/amend). */
|
|
31
|
+
clean?: boolean;
|
|
32
|
+
/** Remove each `.map` from disk after a successful upload so it never ships. */
|
|
33
|
+
deleteAfterUpload?: boolean;
|
|
34
|
+
log?: (msg: string) => void;
|
|
35
|
+
}
|
|
36
|
+
interface UploadResult {
|
|
37
|
+
uploaded: string[];
|
|
38
|
+
release: string;
|
|
39
|
+
deletedFromDisk: number;
|
|
40
|
+
}
|
|
41
|
+
/** Upload every map in `files` in a single multipart request. */
|
|
42
|
+
declare function uploadSourceMaps(config: ResolvedConfig, files: string[], options?: UploadOptions): Promise<UploadResult>;
|
|
43
|
+
|
|
44
|
+
interface UpgooseSourceMapsOptions {
|
|
45
|
+
/** Project API key (`ug_…`). Falls back to `UPGOOSE_API_KEY`. */
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
/** Project ID or public numeric ID. Falls back to `UPGOOSE_PROJECT_ID`. */
|
|
48
|
+
projectId?: string | number;
|
|
49
|
+
/**
|
|
50
|
+
* Release tag the maps are stored under. MUST match `upgoose.init({ release })`.
|
|
51
|
+
* Falls back to `UPGOOSE_RELEASE`, then the short git SHA.
|
|
52
|
+
*/
|
|
53
|
+
release?: string;
|
|
54
|
+
/** API base URL. Falls back to `UPGOOSE_API_URL`, then https://api.upgoose.app. */
|
|
55
|
+
apiUrl?: string;
|
|
56
|
+
/** Directory to scan for `.map` files. Defaults to Vite's resolved `build.outDir`. */
|
|
57
|
+
outDir?: string;
|
|
58
|
+
/** DELETE existing maps for this release before uploading. Default: false. */
|
|
59
|
+
clean?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Delete each `.map` from disk after a successful upload so it never ships
|
|
62
|
+
* to production. Default: true — set false to keep maps in the output dir.
|
|
63
|
+
*/
|
|
64
|
+
deleteAfterUpload?: boolean;
|
|
65
|
+
/** Throw (failing the build) if the upload errors. Default: false (warn only). */
|
|
66
|
+
failOnError?: boolean;
|
|
67
|
+
/** Suppress all `[upgoose]` log output. Default: false. */
|
|
68
|
+
silent?: boolean;
|
|
69
|
+
/** Skip the upload entirely — handy for gating on an env flag. Default: false. */
|
|
70
|
+
disable?: boolean;
|
|
71
|
+
}
|
|
72
|
+
declare function upgooseSourceMaps(options?: UpgooseSourceMapsOptions): Plugin;
|
|
73
|
+
|
|
74
|
+
export { DEFAULT_API_URL, type ResolveInput, type ResolvedConfig, type UpgooseSourceMapsOptions, type UploadOptions, type UploadResult, upgooseSourceMaps as default, findMapFiles, gitShortSha, resolveConfig, upgooseSourceMaps, uploadSourceMaps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
declare const DEFAULT_API_URL = "https://api.upgoose.app";
|
|
4
|
+
interface ResolveInput {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
projectId?: string | number;
|
|
8
|
+
release?: string;
|
|
9
|
+
}
|
|
10
|
+
interface ResolvedConfig {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
apiUrl: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
release: string;
|
|
15
|
+
}
|
|
16
|
+
/** Best-effort `git rev-parse --short HEAD`; null outside a git checkout. */
|
|
17
|
+
declare function gitShortSha(): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Merge explicit options with env vars and a git-SHA fallback. Returns either
|
|
20
|
+
* a fully-resolved config or the list of human-readable missing fields, so the
|
|
21
|
+
* caller can warn-and-skip (plugin) or hard-fail (CLI) as it sees fit.
|
|
22
|
+
*/
|
|
23
|
+
declare function resolveConfig(input: ResolveInput): {
|
|
24
|
+
config?: ResolvedConfig;
|
|
25
|
+
missing: string[];
|
|
26
|
+
};
|
|
27
|
+
/** Recursively collect absolute paths of every `.map` file under `dir`. */
|
|
28
|
+
declare function findMapFiles(dir: string): Promise<string[]>;
|
|
29
|
+
interface UploadOptions {
|
|
30
|
+
/** DELETE existing maps for this release before uploading (re-deploy/amend). */
|
|
31
|
+
clean?: boolean;
|
|
32
|
+
/** Remove each `.map` from disk after a successful upload so it never ships. */
|
|
33
|
+
deleteAfterUpload?: boolean;
|
|
34
|
+
log?: (msg: string) => void;
|
|
35
|
+
}
|
|
36
|
+
interface UploadResult {
|
|
37
|
+
uploaded: string[];
|
|
38
|
+
release: string;
|
|
39
|
+
deletedFromDisk: number;
|
|
40
|
+
}
|
|
41
|
+
/** Upload every map in `files` in a single multipart request. */
|
|
42
|
+
declare function uploadSourceMaps(config: ResolvedConfig, files: string[], options?: UploadOptions): Promise<UploadResult>;
|
|
43
|
+
|
|
44
|
+
interface UpgooseSourceMapsOptions {
|
|
45
|
+
/** Project API key (`ug_…`). Falls back to `UPGOOSE_API_KEY`. */
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
/** Project ID or public numeric ID. Falls back to `UPGOOSE_PROJECT_ID`. */
|
|
48
|
+
projectId?: string | number;
|
|
49
|
+
/**
|
|
50
|
+
* Release tag the maps are stored under. MUST match `upgoose.init({ release })`.
|
|
51
|
+
* Falls back to `UPGOOSE_RELEASE`, then the short git SHA.
|
|
52
|
+
*/
|
|
53
|
+
release?: string;
|
|
54
|
+
/** API base URL. Falls back to `UPGOOSE_API_URL`, then https://api.upgoose.app. */
|
|
55
|
+
apiUrl?: string;
|
|
56
|
+
/** Directory to scan for `.map` files. Defaults to Vite's resolved `build.outDir`. */
|
|
57
|
+
outDir?: string;
|
|
58
|
+
/** DELETE existing maps for this release before uploading. Default: false. */
|
|
59
|
+
clean?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Delete each `.map` from disk after a successful upload so it never ships
|
|
62
|
+
* to production. Default: true — set false to keep maps in the output dir.
|
|
63
|
+
*/
|
|
64
|
+
deleteAfterUpload?: boolean;
|
|
65
|
+
/** Throw (failing the build) if the upload errors. Default: false (warn only). */
|
|
66
|
+
failOnError?: boolean;
|
|
67
|
+
/** Suppress all `[upgoose]` log output. Default: false. */
|
|
68
|
+
silent?: boolean;
|
|
69
|
+
/** Skip the upload entirely — handy for gating on an env flag. Default: false. */
|
|
70
|
+
disable?: boolean;
|
|
71
|
+
}
|
|
72
|
+
declare function upgooseSourceMaps(options?: UpgooseSourceMapsOptions): Plugin;
|
|
73
|
+
|
|
74
|
+
export { DEFAULT_API_URL, type ResolveInput, type ResolvedConfig, type UpgooseSourceMapsOptions, type UploadOptions, type UploadResult, upgooseSourceMaps as default, findMapFiles, gitShortSha, resolveConfig, upgooseSourceMaps, uploadSourceMaps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_API_URL,
|
|
3
|
+
findMapFiles,
|
|
4
|
+
gitShortSha,
|
|
5
|
+
resolveConfig,
|
|
6
|
+
uploadSourceMaps
|
|
7
|
+
} from "./chunk-FQ6FRVNL.js";
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
import { resolve } from "path";
|
|
11
|
+
function upgooseSourceMaps(options = {}) {
|
|
12
|
+
let absOutDir = "";
|
|
13
|
+
let sourcemapEnabled;
|
|
14
|
+
const log = (msg) => {
|
|
15
|
+
if (!options.silent) console.log(`[upgoose] ${msg}`);
|
|
16
|
+
};
|
|
17
|
+
const warn = (msg) => {
|
|
18
|
+
if (!options.silent) console.warn(`[upgoose] ${msg}`);
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
name: "upgoose-source-maps",
|
|
22
|
+
apply: "build",
|
|
23
|
+
enforce: "post",
|
|
24
|
+
configResolved(config) {
|
|
25
|
+
absOutDir = resolve(config.root, options.outDir ?? config.build.outDir);
|
|
26
|
+
sourcemapEnabled = config.build.sourcemap;
|
|
27
|
+
},
|
|
28
|
+
async closeBundle() {
|
|
29
|
+
if (options.disable) return;
|
|
30
|
+
const { config, missing } = resolveConfig(options);
|
|
31
|
+
if (!config) {
|
|
32
|
+
warn(`source-map upload skipped \u2014 missing ${missing.join(", ")}.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!sourcemapEnabled) {
|
|
36
|
+
warn(
|
|
37
|
+
`build.sourcemap is disabled \u2014 nothing to upload. Set build.sourcemap: "hidden" so .map files are emitted without exposing your source.`
|
|
38
|
+
);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const files = await findMapFiles(absOutDir);
|
|
42
|
+
if (files.length === 0) {
|
|
43
|
+
warn(`no .map files found in ${absOutDir}.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
log(`uploading ${files.length} source map(s) for release "${config.release}"\u2026`);
|
|
47
|
+
try {
|
|
48
|
+
const result = await uploadSourceMaps(config, files, {
|
|
49
|
+
clean: options.clean,
|
|
50
|
+
deleteAfterUpload: options.deleteAfterUpload ?? true,
|
|
51
|
+
log
|
|
52
|
+
});
|
|
53
|
+
log(
|
|
54
|
+
`uploaded ${result.uploaded.length} map(s)` + (result.deletedFromDisk ? `, removed ${result.deletedFromDisk} from the build output` : "")
|
|
55
|
+
);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
58
|
+
if (options.failOnError) throw err;
|
|
59
|
+
warn(message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
var src_default = upgooseSourceMaps;
|
|
65
|
+
export {
|
|
66
|
+
DEFAULT_API_URL,
|
|
67
|
+
src_default as default,
|
|
68
|
+
findMapFiles,
|
|
69
|
+
gitShortSha,
|
|
70
|
+
resolveConfig,
|
|
71
|
+
upgooseSourceMaps,
|
|
72
|
+
uploadSourceMaps
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@upgoose/vite-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Upload source maps to Upgoose on build so error stack traces are symbolicated (Vite plugin + CLI)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"upgoose-sourcemaps": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"vite": ">=4"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"vite": {
|
|
37
|
+
"optional": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"tsup": "^8.0.0",
|
|
43
|
+
"typescript": "^5.7.2",
|
|
44
|
+
"vite": "^6.0.6",
|
|
45
|
+
"vitest": "^4.1.4"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"error-tracking",
|
|
49
|
+
"source-maps",
|
|
50
|
+
"sourcemap",
|
|
51
|
+
"symbolication",
|
|
52
|
+
"vite-plugin",
|
|
53
|
+
"upgoose"
|
|
54
|
+
],
|
|
55
|
+
"license": "BSD-3-Clause",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "git+https://github.com/randell/upgoose.git",
|
|
59
|
+
"directory": "packages/vite-plugin"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://upgoose.app/docs#source-maps",
|
|
62
|
+
"bugs": "https://github.com/randell/upgoose/issues",
|
|
63
|
+
"sideEffects": false
|
|
64
|
+
}
|