@monocle.sh/vite-plugin 0.1.0 → 0.2.1
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 +42 -1
- package/dist/vite.d.mts +32 -1
- package/dist/vite.mjs +147 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@ Detects your release when Vite builds or serves your app, and hands it to
|
|
|
4
4
|
[`@monocle.sh/browser-sdk`](../browser-sdk). Monocle then sees each new version
|
|
5
5
|
of your front end as a deployment, without setting `serviceVersion` by hand.
|
|
6
6
|
|
|
7
|
+
After each production build, it also gives your bundles debug IDs and uploads
|
|
8
|
+
their source maps, so Monocle shows errors with your original code.
|
|
9
|
+
|
|
7
10
|
## Installation
|
|
8
11
|
|
|
9
12
|
```sh
|
|
@@ -40,7 +43,7 @@ Empty values and the placeholders `0.0.0`, `0.0.1`, `1.0.0`, `unknown`, `dev`,
|
|
|
40
43
|
and `latest` are skipped. When available, the commit SHA, repository URL
|
|
41
44
|
(credentials removed) and platform deployment ID are attached too.
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Release detection makes no network calls, and only reads the variables above.
|
|
44
47
|
|
|
45
48
|
## Options
|
|
46
49
|
|
|
@@ -50,9 +53,18 @@ monocle({
|
|
|
50
53
|
inject: true, // expose the release to the Browser SDK
|
|
51
54
|
enabled: true, // turn the whole plugin off
|
|
52
55
|
verbose: false, // log the detected release and its source
|
|
56
|
+
sourcemaps: {
|
|
57
|
+
apiKey: process.env.MONOCLE_API_KEY, // default; without a key nothing is uploaded
|
|
58
|
+
apiUrl: 'https://api.monocle.sh',
|
|
59
|
+
release: 'v2.4.0', // shown next to the files, defaults to the detected release
|
|
60
|
+
deleteAfterUpload: true, // default when the plugin enabled source maps itself
|
|
61
|
+
failOnError: false, // log upload errors instead of failing the build
|
|
62
|
+
},
|
|
53
63
|
})
|
|
54
64
|
```
|
|
55
65
|
|
|
66
|
+
Pass `sourcemaps: false` to turn source map handling off.
|
|
67
|
+
|
|
56
68
|
A `serviceVersion` passed to `initMonocle()` wins over the injected release,
|
|
57
69
|
unless it is one of the placeholders above.
|
|
58
70
|
|
|
@@ -66,3 +78,32 @@ not apply to pre-bundled dependencies. It needs no inline `<script>`, so it
|
|
|
66
78
|
works with SSR frameworks that don't use `index.html` and with strict CSPs.
|
|
67
79
|
During SSR, the global is also set on the server, where the Browser SDK stays
|
|
68
80
|
inactive.
|
|
81
|
+
|
|
82
|
+
## Source maps
|
|
83
|
+
|
|
84
|
+
During `vite build`, the plugin:
|
|
85
|
+
|
|
86
|
+
1. Turns on `build.sourcemap: 'hidden'` if you haven't configured
|
|
87
|
+
`build.sourcemap` and an API key is set. Maps are generated without a
|
|
88
|
+
`sourceMappingURL` comment, so browsers never request them. An explicit
|
|
89
|
+
`build.sourcemap: false` disables the steps below. With
|
|
90
|
+
`build.sourcemap: true`, it logs a warning: the bundles reference their maps,
|
|
91
|
+
so browsers can download them.
|
|
92
|
+
2. Once the bundle is written, injects a debug ID into each JavaScript chunk
|
|
93
|
+
and its map with [`@monocle.sh/sourcemaps`](../sourcemaps). A one-line
|
|
94
|
+
snippet registers the ID, and the Browser SDK sends it with every error.
|
|
95
|
+
3. With an API key (`sourcemaps.apiKey` or `MONOCLE_API_KEY`), uploads each
|
|
96
|
+
chunk and its map. Files Monocle already has are skipped. Missing sources
|
|
97
|
+
are only embedded from source files inside the Vite `root`. Without a key, it
|
|
98
|
+
only injects the maps you configured and logs a hint.
|
|
99
|
+
4. Deletes the `.map` files if the plugin turned source maps on itself, even
|
|
100
|
+
when the upload fails, so they are never deployed. Set
|
|
101
|
+
`deleteAfterUpload: false` to keep them, or `true` to delete the maps you
|
|
102
|
+
configured after a successful upload.
|
|
103
|
+
|
|
104
|
+
Upload errors never fail the build unless you set `failOnError: true`: the
|
|
105
|
+
plugin logs them. Maps you configured are kept, so you can upload them later
|
|
106
|
+
with `monocle sourcemaps upload <directory>`. Watch builds are skipped.
|
|
107
|
+
|
|
108
|
+
Matching uses debug IDs, never file names or releases, so the same file
|
|
109
|
+
uploaded twice is stored once and redeploys stay resolvable.
|
package/dist/vite.d.mts
CHANGED
|
@@ -23,6 +23,36 @@ interface MonoclePluginOptions {
|
|
|
23
23
|
* Log the detected release and where it came from.
|
|
24
24
|
*/
|
|
25
25
|
verbose?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Give production bundles debug IDs and upload their source maps to Monocle, so stack traces
|
|
28
|
+
* show your original code. Enabled by default for builds; pass `false` to turn it off.
|
|
29
|
+
*/
|
|
30
|
+
sourcemaps?: false | MonocleSourcemapsOptions;
|
|
31
|
+
}
|
|
32
|
+
interface MonocleSourcemapsOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Monocle API key used to upload. Defaults to `MONOCLE_API_KEY`. Without a key, the plugin
|
|
35
|
+
* does not turn source maps on, and only injects debug IDs into maps you configured.
|
|
36
|
+
*/
|
|
37
|
+
apiKey?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Defaults to `https://api.monocle.sh`.
|
|
40
|
+
*/
|
|
41
|
+
apiUrl?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Release shown next to the uploaded files. Defaults to the detected release.
|
|
44
|
+
*/
|
|
45
|
+
release?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Source maps the plugin turned on itself (`build.sourcemap` not configured) are deleted
|
|
48
|
+
* once processed, even when the upload fails, unless this is `false`. Maps you configured are
|
|
49
|
+
* kept, unless this is `true`: they are then deleted after a successful upload.
|
|
50
|
+
*/
|
|
51
|
+
deleteAfterUpload?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Fail the build when injection or upload fails. Defaults to `false`: errors are logged.
|
|
54
|
+
*/
|
|
55
|
+
failOnError?: boolean;
|
|
26
56
|
}
|
|
27
57
|
//#endregion
|
|
28
58
|
//#region src/vite.d.ts
|
|
@@ -34,7 +64,8 @@ declare class MonocleVitePlugin {
|
|
|
34
64
|
}
|
|
35
65
|
/**
|
|
36
66
|
* Detect the release at build time and expose it to `@monocle.sh/browser-sdk`, in dev and build.
|
|
67
|
+
* After each build, give the bundles debug IDs and upload their source maps.
|
|
37
68
|
*/
|
|
38
69
|
declare function monocle(options?: MonoclePluginOptions): Plugin;
|
|
39
70
|
//#endregion
|
|
40
|
-
export { type MonoclePluginOptions, MonocleVitePlugin, monocle };
|
|
71
|
+
export { type MonoclePluginOptions, type MonocleSourcemapsOptions, MonocleVitePlugin, monocle };
|
package/dist/vite.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { readFileSync, statSync } from "node:fs";
|
|
2
1
|
import { dirname, join, resolve } from "node:path";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
3
|
import MagicString from "magic-string";
|
|
4
|
+
import { rm } from "node:fs/promises";
|
|
5
|
+
import { injectDebugIds, uploadSourcemaps } from "@monocle.sh/sourcemaps";
|
|
4
6
|
//#region ../otel-utils/dist/service-version.mjs
|
|
5
7
|
var ServiceVersionDetector = class {
|
|
6
8
|
/**
|
|
@@ -419,6 +421,110 @@ var MonocleBuild = class {
|
|
|
419
421
|
}
|
|
420
422
|
};
|
|
421
423
|
//#endregion
|
|
424
|
+
//#region src/core/sourcemaps_step.ts
|
|
425
|
+
/**
|
|
426
|
+
* Inject debug IDs into the written chunks, then upload their source maps when an API key is
|
|
427
|
+
* available. Bundler-agnostic: adapters call `sourcemapDefault` while configuring and `run` once
|
|
428
|
+
* the output is on disk.
|
|
429
|
+
*/
|
|
430
|
+
var SourcemapsStep = class {
|
|
431
|
+
#options;
|
|
432
|
+
#enabledHiddenMaps = false;
|
|
433
|
+
#missingApiKey = false;
|
|
434
|
+
#processed = /* @__PURE__ */ new Set();
|
|
435
|
+
constructor(options) {
|
|
436
|
+
this.#options = options;
|
|
437
|
+
}
|
|
438
|
+
get #settings() {
|
|
439
|
+
return this.#options.options.sourcemaps || {};
|
|
440
|
+
}
|
|
441
|
+
get #apiKey() {
|
|
442
|
+
return this.#settings.apiKey ?? this.#options.env?.MONOCLE_API_KEY;
|
|
443
|
+
}
|
|
444
|
+
get enabled() {
|
|
445
|
+
return this.#options.options.enabled !== false && this.#options.options.sourcemaps !== false;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Turn on hidden source maps when the user did not configure them and an API key can upload
|
|
449
|
+
* them: the maps are generated without a `sourceMappingURL` comment pointing browsers to
|
|
450
|
+
* them, and deleted once processed.
|
|
451
|
+
*/
|
|
452
|
+
sourcemapDefault(configured) {
|
|
453
|
+
if (!this.enabled || configured !== void 0) return;
|
|
454
|
+
if (!this.#apiKey) {
|
|
455
|
+
this.#missingApiKey = true;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
this.#enabledHiddenMaps = true;
|
|
459
|
+
return "hidden";
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Explain the resolved build configuration: why no source map is generated, or that the
|
|
463
|
+
* configured ones are referenced by the bundles, so browsers download them.
|
|
464
|
+
*/
|
|
465
|
+
checkConfig(options) {
|
|
466
|
+
if (this.#missingApiKey) options.logger.info("[monocle] Set MONOCLE_API_KEY or sourcemaps.apiKey to generate and upload source maps.");
|
|
467
|
+
if (this.enabled && options.sourcemap === true) options.logger.warn("[monocle] build.sourcemap is true: browsers can download your source maps. Use 'hidden' to upload them without serving them.");
|
|
468
|
+
}
|
|
469
|
+
async #deleteSourcemaps(options) {
|
|
470
|
+
const count = (await Promise.all(options.paths.map((path) => rm(path).then(() => true, () => false)))).filter(Boolean).length;
|
|
471
|
+
if (count > 0) options.logger.info(`[monocle] Removed ${count} source map(s) from the output.`);
|
|
472
|
+
}
|
|
473
|
+
async #process(options) {
|
|
474
|
+
const { logger } = options;
|
|
475
|
+
const settings = this.#settings;
|
|
476
|
+
const { files } = await injectDebugIds({
|
|
477
|
+
directory: options.directory,
|
|
478
|
+
files: options.files
|
|
479
|
+
});
|
|
480
|
+
for (const file of files) if (file.status === "invalid_sourcemap") logger.warn(`[monocle] ${file.error}`);
|
|
481
|
+
const ready = files.filter((file) => file.debugId && file.sourcemap);
|
|
482
|
+
if (ready.length === 0) return;
|
|
483
|
+
const apiKey = this.#apiKey;
|
|
484
|
+
if (!apiKey) {
|
|
485
|
+
logger.info(`[monocle] Debug IDs injected into ${ready.length} file(s). Set MONOCLE_API_KEY or sourcemaps.apiKey to upload source maps.`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const result = await uploadSourcemaps({
|
|
489
|
+
directory: options.directory,
|
|
490
|
+
files: ready.map((file) => file.file),
|
|
491
|
+
apiKey,
|
|
492
|
+
apiUrl: settings.apiUrl,
|
|
493
|
+
release: settings.release ?? options.release,
|
|
494
|
+
root: options.root,
|
|
495
|
+
logger: {
|
|
496
|
+
info: () => {},
|
|
497
|
+
warn: (message) => logger.warn(`[monocle] ${message}`)
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
logger.info(`[monocle] Source maps: ${result.uploaded.length} uploaded, ${result.skipped.length} already uploaded.`);
|
|
501
|
+
if (!this.#enabledHiddenMaps && settings.deleteAfterUpload) await this.#deleteSourcemaps({
|
|
502
|
+
paths: ready.map((file) => file.sourcemap),
|
|
503
|
+
logger
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Process one output directory once. Errors are logged unless `failOnError` is set. The maps
|
|
508
|
+
* the plugin enabled are deleted even when the upload fails, so they are never deployed.
|
|
509
|
+
*/
|
|
510
|
+
async run(options) {
|
|
511
|
+
if (!this.enabled || this.#processed.has(options.directory)) return;
|
|
512
|
+
this.#processed.add(options.directory);
|
|
513
|
+
try {
|
|
514
|
+
await this.#process(options);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
const message = `[monocle] Could not process source maps: ${error.message}`;
|
|
517
|
+
if (this.#settings.failOnError) throw new Error(message, { cause: error });
|
|
518
|
+
options.logger.error(message);
|
|
519
|
+
} finally {
|
|
520
|
+
if (this.#enabledHiddenMaps && this.#settings.deleteAfterUpload !== false) await this.#deleteSourcemaps({
|
|
521
|
+
paths: options.files.map((file) => `${file}.map`),
|
|
522
|
+
logger: options.logger
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
//#endregion
|
|
422
528
|
//#region src/vite.ts
|
|
423
529
|
/**
|
|
424
530
|
* Translate Vite hooks to the bundler-agnostic Monocle build.
|
|
@@ -428,6 +534,11 @@ var MonocleVitePlugin = class {
|
|
|
428
534
|
const plugin = { name: "@monocle.sh/vite-plugin" };
|
|
429
535
|
if (options.enabled === false) return plugin;
|
|
430
536
|
let build;
|
|
537
|
+
let resolved;
|
|
538
|
+
const sourcemaps = new SourcemapsStep({
|
|
539
|
+
options,
|
|
540
|
+
env: process.env
|
|
541
|
+
});
|
|
431
542
|
return {
|
|
432
543
|
...plugin,
|
|
433
544
|
/**
|
|
@@ -435,7 +546,17 @@ var MonocleVitePlugin = class {
|
|
|
435
546
|
* JavaScript instead of a raw component, wherever `monocle()` sits in `plugins`.
|
|
436
547
|
*/
|
|
437
548
|
enforce: "post",
|
|
549
|
+
config(config, env) {
|
|
550
|
+
if (env.command !== "build") return;
|
|
551
|
+
const sourcemap = sourcemaps.sourcemapDefault(config.build?.sourcemap);
|
|
552
|
+
if (sourcemap) return { build: { sourcemap } };
|
|
553
|
+
},
|
|
438
554
|
configResolved(config) {
|
|
555
|
+
resolved = config;
|
|
556
|
+
if (config.command === "build") sourcemaps.checkConfig({
|
|
557
|
+
sourcemap: config.build.sourcemap,
|
|
558
|
+
logger: config.logger
|
|
559
|
+
});
|
|
439
560
|
build = new MonocleBuild({
|
|
440
561
|
options,
|
|
441
562
|
cwd: config.root,
|
|
@@ -460,12 +581,37 @@ var MonocleVitePlugin = class {
|
|
|
460
581
|
id
|
|
461
582
|
});
|
|
462
583
|
}
|
|
584
|
+
},
|
|
585
|
+
/**
|
|
586
|
+
* Sequential and last, so compression or copy plugins see the injected files.
|
|
587
|
+
*/
|
|
588
|
+
writeBundle: {
|
|
589
|
+
sequential: true,
|
|
590
|
+
order: "post",
|
|
591
|
+
async handler(output, bundle) {
|
|
592
|
+
if (!resolved || resolved.command !== "build" || resolved.build.watch) return;
|
|
593
|
+
if (!resolved.build.sourcemap) return;
|
|
594
|
+
const directory = resolve(resolved.root, output.dir ?? dirname(output.file ?? "."));
|
|
595
|
+
const files = Object.values(bundle).filter((item) => item.type === "chunk").map((chunk) => join(directory, chunk.fileName));
|
|
596
|
+
try {
|
|
597
|
+
await sourcemaps.run({
|
|
598
|
+
directory,
|
|
599
|
+
files,
|
|
600
|
+
root: resolved.root,
|
|
601
|
+
release: build?.resolve()?.version,
|
|
602
|
+
logger: resolved.logger
|
|
603
|
+
});
|
|
604
|
+
} catch (error) {
|
|
605
|
+
this.error(error);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
463
608
|
}
|
|
464
609
|
};
|
|
465
610
|
}
|
|
466
611
|
};
|
|
467
612
|
/**
|
|
468
613
|
* Detect the release at build time and expose it to `@monocle.sh/browser-sdk`, in dev and build.
|
|
614
|
+
* After each build, give the bundles debug IDs and upload their source maps.
|
|
469
615
|
*/
|
|
470
616
|
function monocle(options = {}) {
|
|
471
617
|
return MonocleVitePlugin.create(options);
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monocle.sh/vite-plugin",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Vite plugin that injects your release into the Monocle Browser SDK at build time",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Vite plugin that injects your release into the Monocle Browser SDK and uploads source maps at build time",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"monocle",
|
|
7
7
|
"observability",
|
|
8
8
|
"opentelemetry",
|
|
9
9
|
"release",
|
|
10
|
+
"sourcemaps",
|
|
10
11
|
"vite",
|
|
11
12
|
"vite-plugin"
|
|
12
13
|
],
|
|
@@ -37,7 +38,8 @@
|
|
|
37
38
|
"tag": "latest"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"magic-string": "^0.30.21"
|
|
41
|
+
"magic-string": "^0.30.21",
|
|
42
|
+
"@monocle.sh/sourcemaps": "^0.1.0"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@playwright/test": "^1.63.0",
|
|
@@ -47,8 +49,8 @@
|
|
|
47
49
|
"tsdown": "^0.21.10",
|
|
48
50
|
"vite": "^8.3.4",
|
|
49
51
|
"vue": "^3.5.44",
|
|
50
|
-
"@monocle.sh/browser-sdk": "0.
|
|
51
|
-
"@monocle.sh/otel-utils": "1.
|
|
52
|
+
"@monocle.sh/browser-sdk": "0.12.0",
|
|
53
|
+
"@monocle.sh/otel-utils": "1.9.0"
|
|
52
54
|
},
|
|
53
55
|
"peerDependencies": {
|
|
54
56
|
"vite": ">=5.0.0"
|