@monocle.sh/vite-plugin 0.2.1 → 0.3.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 +22 -8
- package/dist/vite.d.mts +1 -0
- package/dist/vite.mjs +120 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -70,14 +70,28 @@ unless it is one of the placeholders above.
|
|
|
70
70
|
|
|
71
71
|
## How it works
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
When every entry of `vite build` is an HTML page (the default `index.html`
|
|
74
|
+
setup), the plugin writes the release into the page as
|
|
75
|
+
`<meta name="monocle-build" content="{…}">`, which the SDK reads in
|
|
76
|
+
`initMonocle()`. Your JavaScript chunks never contain the release, so two builds
|
|
77
|
+
of the same code produce identical chunks, file names and debug IDs: a deploy
|
|
78
|
+
only uploads the source maps of the chunks that changed. A `<meta>` tag is not
|
|
79
|
+
affected by Content Security Policies. This needs `@monocle.sh/browser-sdk`
|
|
80
|
+
0.13.0 or later; with an older SDK the plugin warns and falls back to the
|
|
81
|
+
bundle.
|
|
82
|
+
|
|
83
|
+
Otherwise (dev server, SSR, library builds, frameworks rendering the HTML
|
|
84
|
+
themselves), the plugin prepends `globalThis.__MONOCLE_BUILD__ = {…}` to each of
|
|
85
|
+
your modules that imports `@monocle.sh/browser-sdk`. This targets application
|
|
86
|
+
code rather than relying on `define`, which Vite does not apply to pre-bundled
|
|
87
|
+
dependencies. During SSR, the global is also set on the server, where the
|
|
88
|
+
Browser SDK stays inactive.
|
|
89
|
+
|
|
90
|
+
Vite and Rolldown also put the hashed file name of every imported chunk into
|
|
91
|
+
the importing chunk, so changing one chunk renames the chunks that import it,
|
|
92
|
+
up to the entry. To keep those unchanged too, enable Vite's
|
|
93
|
+
[`build.chunkImportMap`](https://vite.dev/config/build-options) (Vite 8,
|
|
94
|
+
experimental).
|
|
81
95
|
|
|
82
96
|
## Source maps
|
|
83
97
|
|
package/dist/vite.d.mts
CHANGED
package/dist/vite.mjs
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
3
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
4
|
import MagicString from "magic-string";
|
|
4
5
|
import { rm } from "node:fs/promises";
|
|
5
6
|
import { injectDebugIds, uploadSourcemaps } from "@monocle.sh/sourcemaps";
|
|
7
|
+
//#region src/core/browser_sdk.ts
|
|
8
|
+
/**
|
|
9
|
+
* What the Browser SDK the application resolves can read.
|
|
10
|
+
*/
|
|
11
|
+
var BrowserSdk = class {
|
|
12
|
+
/**
|
|
13
|
+
* First `@monocle.sh/browser-sdk` reading `<meta name="monocle-build">`.
|
|
14
|
+
*/
|
|
15
|
+
static pageReleaseSince = "0.13.0";
|
|
16
|
+
static #atLeast(version, minimum) {
|
|
17
|
+
const parse = (value) => value.split(/[-+]/)[0].split(".").map(Number);
|
|
18
|
+
const [actual, expected] = [parse(version), parse(minimum)];
|
|
19
|
+
for (let index = 0; index < 3; index++) {
|
|
20
|
+
const difference = (actual[index] ?? 0) - (expected[index] ?? 0);
|
|
21
|
+
if (Number.isNaN(difference)) return false;
|
|
22
|
+
if (difference !== 0) return difference > 0;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Where the installed SDK reads the release: `page` from the HTML, `bundle` only from the
|
|
28
|
+
* global, `missing` when the application does not resolve it, so nothing reads a page tag. A
|
|
29
|
+
* linked local copy (workspace) is assumed current; one that cannot be inspected (Yarn PnP
|
|
30
|
+
* archives…) reads the bundle.
|
|
31
|
+
*/
|
|
32
|
+
static releaseSource(root) {
|
|
33
|
+
let path;
|
|
34
|
+
try {
|
|
35
|
+
path = createRequire(join(root, "package.json")).resolve("@monocle.sh/browser-sdk/package.json");
|
|
36
|
+
} catch {
|
|
37
|
+
return "missing";
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
if (!realpathSync(path).includes(`${sep}node_modules${sep}`)) return "page";
|
|
41
|
+
const { version } = JSON.parse(readFileSync(path, "utf8"));
|
|
42
|
+
return typeof version === "string" && this.#atLeast(version, this.pageReleaseSince) ? "page" : "bundle";
|
|
43
|
+
} catch {
|
|
44
|
+
return "bundle";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
6
49
|
//#region ../otel-utils/dist/service-version.mjs
|
|
7
50
|
var ServiceVersionDetector = class {
|
|
8
51
|
/**
|
|
@@ -327,7 +370,10 @@ var BuildMetadataResolver = class {
|
|
|
327
370
|
//#endregion
|
|
328
371
|
//#region src/core/release_injection.ts
|
|
329
372
|
/**
|
|
330
|
-
*
|
|
373
|
+
* Expose the release to the Browser SDK. Builds with an HTML page write it into a
|
|
374
|
+
* `<meta name="monocle-build">` tag: JavaScript chunks then stay byte-identical between builds of
|
|
375
|
+
* the same code, so their hashes and debug IDs do too. Other builds (SSR, library, dev server)
|
|
376
|
+
* prepend a global assignment to every module importing the Browser SDK.
|
|
331
377
|
*
|
|
332
378
|
* Import declarations are hoisted, so the SDK module is evaluated first, but it only reads the
|
|
333
379
|
* global when `initMonocle()` runs, which always happens after the importing module starts
|
|
@@ -339,6 +385,10 @@ var ReleaseInjection = class {
|
|
|
339
385
|
* Read by `@monocle.sh/browser-sdk`. Keep both sides in sync.
|
|
340
386
|
*/
|
|
341
387
|
static globalName = "__MONOCLE_BUILD__";
|
|
388
|
+
/**
|
|
389
|
+
* Name of the `<meta>` tag the Browser SDK reads when the global is not set.
|
|
390
|
+
*/
|
|
391
|
+
static metaName = "monocle-build";
|
|
342
392
|
static sdkImport = /["']@monocle\.sh\/browser-sdk(?:\/[^"'\s]*)?["']/;
|
|
343
393
|
/**
|
|
344
394
|
* Stylesheets, markup and data can mention the package without being JavaScript. Markup is
|
|
@@ -353,6 +403,15 @@ var ReleaseInjection = class {
|
|
|
353
403
|
static snippet(metadata) {
|
|
354
404
|
return `globalThis.${this.globalName}=${JSON.stringify(metadata)};`;
|
|
355
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* URI-encoded JSON: without quotes, it survives Vite 5, which does not escape attributes.
|
|
408
|
+
*/
|
|
409
|
+
static meta(metadata) {
|
|
410
|
+
return {
|
|
411
|
+
name: this.metaName,
|
|
412
|
+
content: encodeURIComponent(JSON.stringify(metadata))
|
|
413
|
+
};
|
|
414
|
+
}
|
|
356
415
|
static shouldInject(options) {
|
|
357
416
|
return !this.nonScriptIds.test(options.id) && this.sdkImport.test(options.code);
|
|
358
417
|
}
|
|
@@ -409,6 +468,13 @@ var MonocleBuild = class {
|
|
|
409
468
|
this.#log(value);
|
|
410
469
|
return value;
|
|
411
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* The release for the `<meta>` tag of an HTML page, when injection is on.
|
|
473
|
+
*/
|
|
474
|
+
pageMetadata() {
|
|
475
|
+
if (!this.enabled || this.#options.options.inject === false) return;
|
|
476
|
+
return this.resolve();
|
|
477
|
+
}
|
|
412
478
|
transform(options) {
|
|
413
479
|
if (!this.enabled || this.#options.options.inject === false) return;
|
|
414
480
|
if (!ReleaseInjection.shouldInject(options)) return;
|
|
@@ -529,12 +595,47 @@ var SourcemapsStep = class {
|
|
|
529
595
|
/**
|
|
530
596
|
* Translate Vite hooks to the bundler-agnostic Monocle build.
|
|
531
597
|
*/
|
|
532
|
-
var MonocleVitePlugin = class {
|
|
598
|
+
var MonocleVitePlugin = class MonocleVitePlugin {
|
|
599
|
+
/**
|
|
600
|
+
* Client builds whose every entry is an HTML page, the only ones where the release can live
|
|
601
|
+
* in the page instead of the chunks. Vite builds `index.html` without an explicit input.
|
|
602
|
+
* Frameworks rendering the HTML themselves set another input, per environment since Vite 6.
|
|
603
|
+
*/
|
|
604
|
+
static #htmlBuild(config) {
|
|
605
|
+
const { build } = config;
|
|
606
|
+
if (config.consumer === "server" || build.ssr || build.lib) return false;
|
|
607
|
+
const input = (build.rolldownOptions ?? build.rollupOptions)?.input;
|
|
608
|
+
if (input === void 0) return true;
|
|
609
|
+
const entries = typeof input === "string" ? [input] : Object.values(input);
|
|
610
|
+
return entries.length > 0 && entries.every((entry) => /\.html?$/.test(entry));
|
|
611
|
+
}
|
|
533
612
|
static create(options = {}) {
|
|
534
613
|
const plugin = { name: "@monocle.sh/vite-plugin" };
|
|
535
614
|
if (options.enabled === false) return plugin;
|
|
536
615
|
let build;
|
|
537
616
|
let resolved;
|
|
617
|
+
let sdkReadsPage;
|
|
618
|
+
/**
|
|
619
|
+
* Whether the release can go into the HTML pages of this build: a build with injection on,
|
|
620
|
+
* for an installed Browser SDK reading the page. Detected once, with a warning for older
|
|
621
|
+
* SDKs. Without the SDK, nothing reads a page tag: none is written.
|
|
622
|
+
*/
|
|
623
|
+
function pageSupported() {
|
|
624
|
+
if (!resolved || resolved.command !== "build" || options.inject === false) return false;
|
|
625
|
+
if (sdkReadsPage === void 0) {
|
|
626
|
+
const source = BrowserSdk.releaseSource(resolved.root);
|
|
627
|
+
sdkReadsPage = source === "page";
|
|
628
|
+
if (source === "bundle") resolved.logger.warn(`[monocle] @monocle.sh/browser-sdk < ${BrowserSdk.pageReleaseSince} reads the release from the bundle only, so every build changes all chunk hashes. Upgrade it to keep unchanged chunks identical.`);
|
|
629
|
+
}
|
|
630
|
+
return sdkReadsPage;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Whether modules of this environment leave the release to its HTML page. Vite 5 has no
|
|
634
|
+
* environments: the top-level configuration applies.
|
|
635
|
+
*/
|
|
636
|
+
function releaseInPage(environment) {
|
|
637
|
+
return !!resolved && MonocleVitePlugin.#htmlBuild(environment?.config ?? resolved) && pageSupported();
|
|
638
|
+
}
|
|
538
639
|
const sourcemaps = new SourcemapsStep({
|
|
539
640
|
options,
|
|
540
641
|
env: process.env
|
|
@@ -576,6 +677,7 @@ var MonocleVitePlugin = class {
|
|
|
576
677
|
code: ReleaseInjection.sdkImport
|
|
577
678
|
},
|
|
578
679
|
handler(code, id) {
|
|
680
|
+
if (releaseInPage(this.environment)) return;
|
|
579
681
|
return build?.transform({
|
|
580
682
|
code,
|
|
581
683
|
id
|
|
@@ -583,6 +685,20 @@ var MonocleVitePlugin = class {
|
|
|
583
685
|
}
|
|
584
686
|
},
|
|
585
687
|
/**
|
|
688
|
+
* Builds only call it for HTML entries. When some modules still got the global (mixed
|
|
689
|
+
* HTML and script entries), the SDK prefers the global, so the tag is harmless.
|
|
690
|
+
*/
|
|
691
|
+
transformIndexHtml() {
|
|
692
|
+
if (!pageSupported()) return;
|
|
693
|
+
const metadata = build?.pageMetadata();
|
|
694
|
+
if (!metadata) return;
|
|
695
|
+
return [{
|
|
696
|
+
tag: "meta",
|
|
697
|
+
attrs: ReleaseInjection.meta(metadata),
|
|
698
|
+
injectTo: "head-prepend"
|
|
699
|
+
}];
|
|
700
|
+
},
|
|
701
|
+
/**
|
|
586
702
|
* Sequential and last, so compression or copy plugins see the injected files.
|
|
587
703
|
*/
|
|
588
704
|
writeBundle: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monocle.sh/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"magic-string": "^0.30.21",
|
|
42
|
-
"@monocle.sh/sourcemaps": "^0.
|
|
42
|
+
"@monocle.sh/sourcemaps": "^0.2.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@playwright/test": "^1.63.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"tsdown": "^0.21.10",
|
|
50
50
|
"vite": "^8.3.4",
|
|
51
51
|
"vue": "^3.5.44",
|
|
52
|
-
"@monocle.sh/browser-sdk": "0.
|
|
52
|
+
"@monocle.sh/browser-sdk": "0.13.0",
|
|
53
53
|
"@monocle.sh/otel-utils": "1.9.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|