mountly-vite-plugin 1.2.3 → 1.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/README.md +15 -0
- package/dist/embed.d.ts +63 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +814 -42
- package/dist/props.d.ts +41 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -78,6 +78,21 @@ to compose them into a config you already own:
|
|
|
78
78
|
| `mountlyCssAsText` | import CSS as a string, for adopting into a shadow root |
|
|
79
79
|
| `getFrameworkPeerExternals` | the externals list for a peer build |
|
|
80
80
|
|
|
81
|
+
## Script-tag component libraries
|
|
82
|
+
|
|
83
|
+
`defineElementsConfig({ prefix, elements })` turns typed React/Vue/Svelte
|
|
84
|
+
components into lazily-loaded custom elements, and emits `embed.d.ts` and
|
|
85
|
+
`custom-elements.json` alongside `embed.js`.
|
|
86
|
+
|
|
87
|
+
Elements render in light DOM, so the consuming page's design system reaches in.
|
|
88
|
+
Pass `shadow: true` when the host is not one you trust: every element in the
|
|
89
|
+
distribution then renders into its own shadow root, and the build emits one
|
|
90
|
+
stylesheet that it injects into no document.
|
|
91
|
+
|
|
92
|
+
See the [complete guide](https://mountly.dev/concepts/script-embeds/) and the
|
|
93
|
+
[React](../../docs/examples/react-embed) and
|
|
94
|
+
[mixed-framework](../../docs/examples/mixed-embed) examples.
|
|
95
|
+
|
|
81
96
|
## TypeScript
|
|
82
97
|
|
|
83
98
|
`mountlyManifestFragmentPlugin` reads exports from the TypeScript checker and
|
package/dist/embed.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type UserConfig } from "vite";
|
|
2
|
+
import type { MountlyWidgetFramework } from "./index.js";
|
|
3
|
+
import { type PropSpec } from "./props.js";
|
|
4
|
+
export interface MountlyElementEntry {
|
|
5
|
+
/** Browser-compatible component source, also importable directly by your app. */
|
|
6
|
+
component: string;
|
|
7
|
+
exportName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Override the adapter for this element. Inferred from the file extension, so
|
|
10
|
+
* this is only for a component whose extension does not say what it is — a
|
|
11
|
+
* Vue `defineComponent` written in a `.ts` file, say.
|
|
12
|
+
*/
|
|
13
|
+
framework?: MountlyWidgetFramework;
|
|
14
|
+
/** Override the prop table when the component's props type lives elsewhere. */
|
|
15
|
+
props?: PropSpec[];
|
|
16
|
+
/** Core trigger syntax. Defaults to mounting when connected. */
|
|
17
|
+
trigger?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface MountlyElementsConfigOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Namespace for the generated tags. Custom element names need a dash, and
|
|
22
|
+
* the consumer's page is not yours — `acme` gives `<acme-payments-summary>`.
|
|
23
|
+
*/
|
|
24
|
+
prefix: string;
|
|
25
|
+
/** A glob of component files, or an explicit tag → component map. */
|
|
26
|
+
elements: string | string[] | Record<string, string | MountlyElementEntry>;
|
|
27
|
+
/**
|
|
28
|
+
* Project root. Only needed when the config is not loaded from it — a
|
|
29
|
+
* monorepo running the build from elsewhere, say. Defaults to `process.cwd()`.
|
|
30
|
+
*/
|
|
31
|
+
root?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Render every element into a shadow root instead of the host's light DOM.
|
|
34
|
+
*
|
|
35
|
+
* Light DOM is the default because the usual consumer is a page that wants
|
|
36
|
+
* its own design system to reach in — a CMS template, a partner site running
|
|
37
|
+
* your tokens. Turn this on when the host is one you do not trust to leave
|
|
38
|
+
* your component alone, or that you must not affect: the host's CSS cannot
|
|
39
|
+
* reach in, and your CSS stops being emitted into its document.
|
|
40
|
+
*
|
|
41
|
+
* The whole distribution shares the setting, because it describes the kind
|
|
42
|
+
* of host you ship to, not the component.
|
|
43
|
+
*/
|
|
44
|
+
shadow?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Route each `new Worker(…)` that means the platform's Worker through the
|
|
48
|
+
* cross-origin shim. Parsed, not pattern-matched: the same characters in a
|
|
49
|
+
* string, template or comment are content, and an inline worker's source is one
|
|
50
|
+
* of those strings. A call is left alone when any enclosing scope binds
|
|
51
|
+
* `Worker` (a parameter, a catch, a var, a let, a class, an import), since that
|
|
52
|
+
* call constructs the author's Worker.
|
|
53
|
+
*/
|
|
54
|
+
export declare function shimWorkers(code: string): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Build a script-tag distribution: one file registers every tag, and each
|
|
57
|
+
* component, its framework and its CSS arrive only when an element connects.
|
|
58
|
+
*
|
|
59
|
+
* The author writes an ordinary React/Vue/Svelte component. Its props type is
|
|
60
|
+
* read at build time, so the consumer gets real attributes, real properties and
|
|
61
|
+
* real DOM events with nothing to configure on either side.
|
|
62
|
+
*/
|
|
63
|
+
export declare function defineElementsConfig(options: MountlyElementsConfigOptions): UserConfig;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ export { mountlyHostPlugin, type MountlyHostPluginOptions } from "./host.js";
|
|
|
3
3
|
export { mountlyManifestFragmentPlugin, type ManifestFragmentPluginOptions } from "./fragment.js";
|
|
4
4
|
export type MountlyWidgetFramework = "react" | "vue" | "svelte";
|
|
5
5
|
export declare function getFrameworkPeerExternals(framework: MountlyWidgetFramework): Array<string | RegExp>;
|
|
6
|
+
/**
|
|
7
|
+
* The self-contained build's whole promise is "drop this file in a page and it
|
|
8
|
+
* works", so mountly's own runtime helpers are bundled in rather than
|
|
9
|
+
* externalised: the host then needs no import map at all. The peer build still
|
|
10
|
+
* externalises them, because sharing through the host's import map is the point
|
|
11
|
+
* of that build.
|
|
12
|
+
*/
|
|
6
13
|
export declare function getSelfContainedExternals(framework: MountlyWidgetFramework): Array<string | RegExp>;
|
|
7
14
|
export interface MountlyCssAsTextOptions {
|
|
8
15
|
include?: string | RegExp | (string | RegExp)[];
|
|
@@ -63,3 +70,5 @@ export interface DefineMountlyWidgetConfigOptions {
|
|
|
63
70
|
featureExport?: string;
|
|
64
71
|
}
|
|
65
72
|
export declare function defineMountlyWidgetConfig(options?: DefineMountlyWidgetConfigOptions): UserConfig[];
|
|
73
|
+
export { defineElementsConfig, type MountlyElementsConfigOptions, type MountlyElementEntry, } from "./embed.js";
|
|
74
|
+
export { extractProps, type PropSpec, type PropKind } from "./props.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { readFileSync as
|
|
2
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3
3
|
import { exportKeyToSubpath as exportKeyToSubpath3 } from "mountly-manifest";
|
|
4
4
|
|
|
5
5
|
// src/fragment.ts
|
|
@@ -144,7 +144,7 @@ function mountlyManifestFragmentPlugin(options) {
|
|
|
144
144
|
if (options.team) vertical.team = options.team;
|
|
145
145
|
if (options.version) vertical.version = options.version;
|
|
146
146
|
if (options.featureExport) vertical.featureExport = options.featureExport;
|
|
147
|
-
const
|
|
147
|
+
const declarations2 = emitDeclarations(entry, exposeEntries);
|
|
148
148
|
const namedExports = await collectNamedExports([
|
|
149
149
|
...entry ? [entry] : [],
|
|
150
150
|
...Object.values(exposeEntries)
|
|
@@ -158,7 +158,7 @@ function mountlyManifestFragmentPlugin(options) {
|
|
|
158
158
|
mapped[key] = value.startsWith("./") ? value : `./${exportKeyToSubpath(value)}`;
|
|
159
159
|
const exposeEntry = exposeEntries[key];
|
|
160
160
|
const exportNames = exposeEntry ? namedExports.get(exposeEntry) ?? [] : [];
|
|
161
|
-
const declaration =
|
|
161
|
+
const declaration = declarations2.exportDeclarations[key];
|
|
162
162
|
if (exportNames.length > 0 || declaration) {
|
|
163
163
|
typedExports[key] = declaration || exportNames.length > 0 ? {
|
|
164
164
|
...declaration ? { declaration } : {},
|
|
@@ -167,16 +167,16 @@ function mountlyManifestFragmentPlugin(options) {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
vertical.exports = mapped;
|
|
170
|
-
if (moduleTypes.length > 0 ||
|
|
170
|
+
if (moduleTypes.length > 0 || declarations2.rootDeclaration || Object.keys(typedExports).length > 0) {
|
|
171
171
|
vertical.types = {
|
|
172
|
-
...
|
|
172
|
+
...declarations2.rootDeclaration ? { declaration: declarations2.rootDeclaration } : {},
|
|
173
173
|
...moduleTypes.length > 0 ? { module: [...new Set(moduleTypes)].sort() } : {},
|
|
174
174
|
...Object.keys(typedExports).length > 0 ? { exports: typedExports } : {}
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
|
-
} else if (moduleTypes.length > 0 ||
|
|
177
|
+
} else if (moduleTypes.length > 0 || declarations2.rootDeclaration) {
|
|
178
178
|
vertical.types = {
|
|
179
|
-
...
|
|
179
|
+
...declarations2.rootDeclaration ? { declaration: declarations2.rootDeclaration } : {},
|
|
180
180
|
module: [...new Set(moduleTypes)].sort()
|
|
181
181
|
};
|
|
182
182
|
}
|
|
@@ -332,12 +332,12 @@ async function materializeTypeGraph(root, verticalId, source, cacheDir, seen) {
|
|
|
332
332
|
}
|
|
333
333
|
return localFile;
|
|
334
334
|
}
|
|
335
|
-
async function writeTypesFile(
|
|
335
|
+
async function writeTypesFile(manifest2, outFile, root, remotes, strict) {
|
|
336
336
|
if (!outFile) return;
|
|
337
337
|
const cacheDir = join2(dirname2(outFile), ".mountly", "remotes");
|
|
338
338
|
const declarationModules = /* @__PURE__ */ new Map();
|
|
339
339
|
const seen = /* @__PURE__ */ new Map();
|
|
340
|
-
for (const vertical of
|
|
340
|
+
for (const vertical of manifest2.verticals) {
|
|
341
341
|
const rootSpecifier = vertical.alias ?? vertical.id;
|
|
342
342
|
const remoteOrigin = remotes[vertical.id];
|
|
343
343
|
const entries = [
|
|
@@ -374,7 +374,7 @@ async function writeTypesFile(manifest, outFile, root, remotes, strict) {
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
-
const next = codegenManifestTypes(
|
|
377
|
+
const next = codegenManifestTypes(manifest2, {
|
|
378
378
|
resolveDeclarationModule: ({ specifier }) => declarationModules.get(specifier) ?? null,
|
|
379
379
|
resolveExportNames: ({ vertical, exportKey }) => exportNamesForVertical(vertical, exportKey)
|
|
380
380
|
});
|
|
@@ -383,8 +383,8 @@ async function writeTypesFile(manifest, outFile, root, remotes, strict) {
|
|
|
383
383
|
if (prev === next) return;
|
|
384
384
|
writeFileSync2(outFile, next);
|
|
385
385
|
}
|
|
386
|
-
function verticalIdFromSpecifier(
|
|
387
|
-
for (const vertical of
|
|
386
|
+
function verticalIdFromSpecifier(manifest2, specifier) {
|
|
387
|
+
for (const vertical of manifest2.verticals) {
|
|
388
388
|
const roots = [vertical.id, vertical.alias].filter(Boolean);
|
|
389
389
|
for (const root of roots) {
|
|
390
390
|
if (specifier === root || specifier.startsWith(`${root}/`)) {
|
|
@@ -394,10 +394,10 @@ function verticalIdFromSpecifier(manifest, specifier) {
|
|
|
394
394
|
}
|
|
395
395
|
return null;
|
|
396
396
|
}
|
|
397
|
-
function resolveDevUrl(
|
|
398
|
-
const manifestUrl = resolveRemoteImport(
|
|
397
|
+
function resolveDevUrl(manifest2, specifier, remotes) {
|
|
398
|
+
const manifestUrl = resolveRemoteImport(manifest2, specifier);
|
|
399
399
|
if (!manifestUrl) return null;
|
|
400
|
-
const verticalId = verticalIdFromSpecifier(
|
|
400
|
+
const verticalId = verticalIdFromSpecifier(manifest2, specifier);
|
|
401
401
|
const remoteOrigin = verticalId ? remotes[verticalId] : void 0;
|
|
402
402
|
if (!remoteOrigin) return manifestUrl;
|
|
403
403
|
if (manifestUrl.startsWith("/")) {
|
|
@@ -413,9 +413,9 @@ function resolveDevUrl(manifest, specifier, remotes) {
|
|
|
413
413
|
}
|
|
414
414
|
return `${remoteOrigin.replace(/\/$/, "")}/${manifestUrl.replace(/^\/+/, "")}`;
|
|
415
415
|
}
|
|
416
|
-
function isRemoteSpecifier(
|
|
417
|
-
if (resolveRemoteImport(
|
|
418
|
-
return
|
|
416
|
+
function isRemoteSpecifier(manifest2, id) {
|
|
417
|
+
if (resolveRemoteImport(manifest2, id)) return true;
|
|
418
|
+
return manifest2.verticals.some((vertical) => {
|
|
419
419
|
const roots = [vertical.id, vertical.alias].filter(Boolean);
|
|
420
420
|
return roots.some((root) => id.startsWith(`${root}/`));
|
|
421
421
|
});
|
|
@@ -432,7 +432,7 @@ function applyBaseToImportUrl(url, base) {
|
|
|
432
432
|
}
|
|
433
433
|
async function fetchRemoteFragments(remotes) {
|
|
434
434
|
const verticals = [];
|
|
435
|
-
for (const [
|
|
435
|
+
for (const [name2, urlRaw] of Object.entries(remotes)) {
|
|
436
436
|
const trimmed = urlRaw.replace(/\/+$/, "");
|
|
437
437
|
const isFragmentUrl = trimmed.endsWith(".json");
|
|
438
438
|
const fragmentUrl = isFragmentUrl ? trimmed : `${trimmed}/mountly.manifest.fragment.json`;
|
|
@@ -440,7 +440,7 @@ async function fetchRemoteFragments(remotes) {
|
|
|
440
440
|
const response = await fetch(fragmentUrl);
|
|
441
441
|
if (!response.ok) {
|
|
442
442
|
throw new Error(
|
|
443
|
-
`[mountly:host] remote "${
|
|
443
|
+
`[mountly:host] remote "${name2}": failed to fetch ${fragmentUrl} (${response.status}) \u2014 is the remote built and served?`
|
|
444
444
|
);
|
|
445
445
|
}
|
|
446
446
|
const fragment = await response.json();
|
|
@@ -450,7 +450,7 @@ async function fetchRemoteFragments(remotes) {
|
|
|
450
450
|
verticals.push({
|
|
451
451
|
...vertical,
|
|
452
452
|
// A `mountlyRemote` build emits one vertical; key it by the host's remote name.
|
|
453
|
-
id: fragmentVerticals.length === 1 ?
|
|
453
|
+
id: fragmentVerticals.length === 1 ? name2 : vertical.id,
|
|
454
454
|
url: absolute
|
|
455
455
|
});
|
|
456
456
|
}
|
|
@@ -470,7 +470,7 @@ function mountlyHostPlugin(options) {
|
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
472
|
const strictTypes = options.types !== false && typeof options.types === "object" ? options.types.strict ?? true : options.types !== false;
|
|
473
|
-
let
|
|
473
|
+
let manifest2;
|
|
474
474
|
let root = process.cwd();
|
|
475
475
|
let isProduction = false;
|
|
476
476
|
let sourceFiles = [];
|
|
@@ -520,11 +520,11 @@ function mountlyHostPlugin(options) {
|
|
|
520
520
|
async config(_config, env) {
|
|
521
521
|
isProduction = env.command === "build";
|
|
522
522
|
await ensureUrlRemotes();
|
|
523
|
-
|
|
524
|
-
maybeWriteManifest(
|
|
523
|
+
manifest2 = loadCurrentManifest();
|
|
524
|
+
maybeWriteManifest(manifest2);
|
|
525
525
|
typesOutFile = resolveTypesOutFile(root, options.manifest, options.types);
|
|
526
|
-
void writeTypesFile(
|
|
527
|
-
const external = manifestRemoteSpecifiers(
|
|
526
|
+
void writeTypesFile(manifest2, typesOutFile, root, devOrigins, false);
|
|
527
|
+
const external = manifestRemoteSpecifiers(manifest2);
|
|
528
528
|
return {
|
|
529
529
|
build: {
|
|
530
530
|
rollupOptions: {
|
|
@@ -540,15 +540,15 @@ function mountlyHostPlugin(options) {
|
|
|
540
540
|
root = config.root;
|
|
541
541
|
viteBase = config.base;
|
|
542
542
|
await ensureUrlRemotes();
|
|
543
|
-
|
|
543
|
+
manifest2 = loadCurrentManifest();
|
|
544
544
|
typesOutFile = resolveTypesOutFile(root, options.manifest, options.types);
|
|
545
|
-
void writeTypesFile(
|
|
545
|
+
void writeTypesFile(manifest2, typesOutFile, root, devOrigins, false);
|
|
546
546
|
},
|
|
547
547
|
async buildStart() {
|
|
548
548
|
await ensureUrlRemotes();
|
|
549
|
-
|
|
550
|
-
maybeWriteManifest(
|
|
551
|
-
await writeTypesFile(
|
|
549
|
+
manifest2 = loadCurrentManifest();
|
|
550
|
+
maybeWriteManifest(manifest2);
|
|
551
|
+
await writeTypesFile(manifest2, typesOutFile, root, devOrigins, strictTypes);
|
|
552
552
|
for (const file of sourceFiles) this.addWatchFile(file);
|
|
553
553
|
},
|
|
554
554
|
configureServer(server) {
|
|
@@ -556,12 +556,12 @@ function mountlyHostPlugin(options) {
|
|
|
556
556
|
},
|
|
557
557
|
handleHotUpdate(ctx) {
|
|
558
558
|
if (!sourceFiles.includes(ctx.file)) return;
|
|
559
|
-
|
|
560
|
-
void writeTypesFile(
|
|
559
|
+
manifest2 = loadCurrentManifest();
|
|
560
|
+
void writeTypesFile(manifest2, typesOutFile, root, devOrigins, false);
|
|
561
561
|
},
|
|
562
562
|
resolveId(id) {
|
|
563
|
-
if (!isRemoteSpecifier(
|
|
564
|
-
const url = isProduction ? null : resolveDevUrl(
|
|
563
|
+
if (!isRemoteSpecifier(manifest2, id)) return null;
|
|
564
|
+
const url = isProduction ? null : resolveDevUrl(manifest2, id, devOrigins);
|
|
565
565
|
if (url) {
|
|
566
566
|
return { id: url, external: true };
|
|
567
567
|
}
|
|
@@ -573,9 +573,9 @@ function mountlyHostPlugin(options) {
|
|
|
573
573
|
let next = html;
|
|
574
574
|
if (injectImportMap) {
|
|
575
575
|
const imports = Object.fromEntries(
|
|
576
|
-
Object.entries(manifestToImportMap(
|
|
576
|
+
Object.entries(manifestToImportMap(manifest2)).map(([specifier, url]) => [
|
|
577
577
|
specifier,
|
|
578
|
-
isProduction ? applyBaseToImportUrl(url, viteBase) : resolveDevUrl(
|
|
578
|
+
isProduction ? applyBaseToImportUrl(url, viteBase) : resolveDevUrl(manifest2, specifier, devOrigins) ?? url
|
|
579
579
|
])
|
|
580
580
|
);
|
|
581
581
|
const snippet = `<script type="importmap">
|
|
@@ -592,6 +592,779 @@ ${JSON.stringify({ imports }, null, 2)}
|
|
|
592
592
|
};
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
+
// src/embed.ts
|
|
596
|
+
import { globSync, readFileSync as readFileSync2 } from "fs";
|
|
597
|
+
import { basename, dirname as dirname3, extname, relative as relative3, resolve as resolve3 } from "path";
|
|
598
|
+
import { parseAst as parseAst2 } from "vite";
|
|
599
|
+
|
|
600
|
+
// src/props.ts
|
|
601
|
+
import { parseAst } from "vite";
|
|
602
|
+
function kebab(name2) {
|
|
603
|
+
return name2.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
604
|
+
}
|
|
605
|
+
function eventName(prop) {
|
|
606
|
+
return kebab(prop.replace(/^on(?=[A-Z])/, ""));
|
|
607
|
+
}
|
|
608
|
+
function unwrapUnion(type) {
|
|
609
|
+
if (type.type !== "TSUnionType") return type;
|
|
610
|
+
const real = type.types.filter(
|
|
611
|
+
(t) => t.type !== "TSUndefinedKeyword" && t.type !== "TSNullKeyword" && !(t.type === "TSLiteralType" && t.literal?.value === null)
|
|
612
|
+
);
|
|
613
|
+
if (real.length === 1) return unwrapUnion(real[0]);
|
|
614
|
+
if (real.length > 1 && real.every((t) => typeof t.literal?.value === "string")) {
|
|
615
|
+
return { type: "TSStringKeyword" };
|
|
616
|
+
}
|
|
617
|
+
return { type: "TSUnknown" };
|
|
618
|
+
}
|
|
619
|
+
function kindOf(type, types, seen = 0) {
|
|
620
|
+
if (!type || seen > 4) return "auto";
|
|
621
|
+
const resolved = unwrapUnion(type);
|
|
622
|
+
if (resolved.type === "TSLiteralType") {
|
|
623
|
+
const value = resolved.literal?.value;
|
|
624
|
+
if (typeof value === "string") return "string";
|
|
625
|
+
if (typeof value === "number") return "number";
|
|
626
|
+
if (typeof value === "boolean") return "boolean";
|
|
627
|
+
return "json";
|
|
628
|
+
}
|
|
629
|
+
switch (resolved.type) {
|
|
630
|
+
case "TSStringKeyword":
|
|
631
|
+
return "string";
|
|
632
|
+
case "TSNumberKeyword":
|
|
633
|
+
return "number";
|
|
634
|
+
case "TSBooleanKeyword":
|
|
635
|
+
return "boolean";
|
|
636
|
+
case "TSFunctionType":
|
|
637
|
+
return "function";
|
|
638
|
+
case "TSTypeReference": {
|
|
639
|
+
if (resolved.typeArguments) return "json";
|
|
640
|
+
const alias = types.get(resolved.typeName?.name);
|
|
641
|
+
if (alias) return kindOf(alias, types, seen + 1);
|
|
642
|
+
return "auto";
|
|
643
|
+
}
|
|
644
|
+
default:
|
|
645
|
+
return "json";
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function localTypes(body) {
|
|
649
|
+
const types = /* @__PURE__ */ new Map();
|
|
650
|
+
for (const raw of body) {
|
|
651
|
+
const node = raw.type === "ExportNamedDeclaration" && raw.declaration ? raw.declaration : raw;
|
|
652
|
+
if (node.type === "TSInterfaceDeclaration") types.set(node.id.name, node);
|
|
653
|
+
if (node.type === "TSTypeAliasDeclaration") types.set(node.id.name, node.typeAnnotation);
|
|
654
|
+
}
|
|
655
|
+
return types;
|
|
656
|
+
}
|
|
657
|
+
function membersOf(type, types, seen = 0) {
|
|
658
|
+
if (!type || seen > 4) return null;
|
|
659
|
+
if (type.type === "TSTypeLiteral" || type.type === "TSInterfaceBody") {
|
|
660
|
+
return type.body ?? type.members;
|
|
661
|
+
}
|
|
662
|
+
if (type.type === "TSTypeReference" && type.typeName?.type === "Identifier") {
|
|
663
|
+
return membersOf(types.get(type.typeName.name), types, seen + 1);
|
|
664
|
+
}
|
|
665
|
+
if (type.type === "TSInterfaceDeclaration") {
|
|
666
|
+
const inherited = [];
|
|
667
|
+
for (const heritage of type.extends ?? []) {
|
|
668
|
+
const base = membersOf(types.get(heritage.expression?.name), types, seen + 1);
|
|
669
|
+
if (!base) return null;
|
|
670
|
+
inherited.push(...base);
|
|
671
|
+
}
|
|
672
|
+
return [...inherited, ...type.body?.body ?? []];
|
|
673
|
+
}
|
|
674
|
+
if (type.type === "TSIntersectionType") {
|
|
675
|
+
const all = [];
|
|
676
|
+
for (const part of type.types) {
|
|
677
|
+
const members = membersOf(part, types, seen + 1);
|
|
678
|
+
if (!members) return null;
|
|
679
|
+
all.push(...members);
|
|
680
|
+
}
|
|
681
|
+
return all;
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
function isComponent(node) {
|
|
686
|
+
return node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
|
|
687
|
+
}
|
|
688
|
+
function findExport(body, exportName) {
|
|
689
|
+
if (exportName === "default") {
|
|
690
|
+
const node = body.find((n) => n.type === "ExportDefaultDeclaration");
|
|
691
|
+
if (!node) return null;
|
|
692
|
+
if (isComponent(node.declaration)) return node.declaration;
|
|
693
|
+
if (node.declaration.type === "Identifier") return findVariable(body, node.declaration.name);
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
for (const raw of body) {
|
|
697
|
+
if (raw.type !== "ExportNamedDeclaration" || !raw.declaration) continue;
|
|
698
|
+
const declaration = raw.declaration;
|
|
699
|
+
if (declaration.type === "FunctionDeclaration" && declaration.id?.name === exportName) {
|
|
700
|
+
return declaration;
|
|
701
|
+
}
|
|
702
|
+
if (declaration.type === "VariableDeclaration") {
|
|
703
|
+
const match = declaration.declarations.find((d) => d.id?.name === exportName);
|
|
704
|
+
if (match) return match;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return findVariable(body, exportName);
|
|
708
|
+
}
|
|
709
|
+
function findVariable(body, name2) {
|
|
710
|
+
for (const raw of body) {
|
|
711
|
+
const node = raw.type === "ExportNamedDeclaration" && raw.declaration ? raw.declaration : raw;
|
|
712
|
+
if (node.type !== "VariableDeclaration") continue;
|
|
713
|
+
const match = node.declarations.find((d) => d.id?.name === name2);
|
|
714
|
+
if (match) return match;
|
|
715
|
+
}
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
function componentFn(declaration) {
|
|
719
|
+
if (isComponent(declaration)) return declaration;
|
|
720
|
+
const init = declaration.init;
|
|
721
|
+
if (init && isComponent(init)) return init;
|
|
722
|
+
if (init?.type === "CallExpression") {
|
|
723
|
+
const inner = init.arguments?.find((argument) => isComponent(argument));
|
|
724
|
+
if (inner) return inner;
|
|
725
|
+
}
|
|
726
|
+
return void 0;
|
|
727
|
+
}
|
|
728
|
+
function propsType(declaration) {
|
|
729
|
+
const annotation = componentFn(declaration)?.params?.[0]?.typeAnnotation?.typeAnnotation;
|
|
730
|
+
if (annotation) return annotation;
|
|
731
|
+
return declaration.id?.typeAnnotation?.typeAnnotation?.typeArguments?.params?.[0] ?? declaration.init?.typeArguments?.params?.[0];
|
|
732
|
+
}
|
|
733
|
+
function destructuredNames(declaration) {
|
|
734
|
+
const param = componentFn(declaration)?.params?.[0];
|
|
735
|
+
if (param?.type !== "ObjectPattern") return [];
|
|
736
|
+
return param.properties.filter((p) => p.type === "Property" && p.key?.type === "Identifier").map((p) => p.key.name);
|
|
737
|
+
}
|
|
738
|
+
function takesProps(declaration) {
|
|
739
|
+
const fn = componentFn(declaration);
|
|
740
|
+
if (fn) return Boolean(fn.params?.length) || Boolean(propsType(declaration));
|
|
741
|
+
return propsType(declaration) ? true : "unknown";
|
|
742
|
+
}
|
|
743
|
+
function toSpec(name2, kind) {
|
|
744
|
+
if (/^on[A-Z]/.test(name2)) return { name: name2, kind: "event", event: eventName(name2) };
|
|
745
|
+
if (kind === "event" || kind === "function") return { name: name2, kind: "function" };
|
|
746
|
+
return { name: name2, attribute: kebab(name2), kind };
|
|
747
|
+
}
|
|
748
|
+
var VUE_CTOR = {
|
|
749
|
+
String: "string",
|
|
750
|
+
Number: "number",
|
|
751
|
+
Boolean: "boolean",
|
|
752
|
+
Array: "json",
|
|
753
|
+
Object: "json",
|
|
754
|
+
Function: "function"
|
|
755
|
+
};
|
|
756
|
+
function vueOptions(body) {
|
|
757
|
+
const exported = body.find((n) => n.type === "ExportDefaultDeclaration")?.declaration;
|
|
758
|
+
if (!exported) return null;
|
|
759
|
+
const object = exported.type === "CallExpression" ? exported.arguments?.[0] : exported;
|
|
760
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
761
|
+
for (const property of object.properties) {
|
|
762
|
+
if (property.type === "SpreadElement") return null;
|
|
763
|
+
const key = keyOf(property);
|
|
764
|
+
if (key === "mixins" || key === "extends") return null;
|
|
765
|
+
}
|
|
766
|
+
const entry = (key) => object.properties.find((p) => p.type === "Property" && keyOf(p) === key)?.value;
|
|
767
|
+
const declaredProps = entry("props");
|
|
768
|
+
const specs = [];
|
|
769
|
+
if (declaredProps?.type === "ArrayExpression") {
|
|
770
|
+
for (const element of declaredProps.elements) {
|
|
771
|
+
if (typeof element?.value !== "string") return null;
|
|
772
|
+
specs.push(toSpec(element.value, "auto"));
|
|
773
|
+
}
|
|
774
|
+
} else if (declaredProps?.type === "ObjectExpression") {
|
|
775
|
+
for (const property of declaredProps.properties) {
|
|
776
|
+
const name2 = keyOf(property);
|
|
777
|
+
if (!name2) return null;
|
|
778
|
+
const value = property.value;
|
|
779
|
+
const ctor = value?.type === "ObjectExpression" ? value.properties.find((q) => keyOf(q) === "type")?.value?.name : value?.name;
|
|
780
|
+
specs.push(toSpec(name2, ctor ? VUE_CTOR[ctor] ?? "json" : "auto"));
|
|
781
|
+
}
|
|
782
|
+
} else if (declaredProps) {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
const declaredEmits = entry("emits");
|
|
786
|
+
if (declaredEmits?.type === "ArrayExpression") {
|
|
787
|
+
for (const element of declaredEmits.elements) {
|
|
788
|
+
if (typeof element?.value !== "string") return null;
|
|
789
|
+
specs.push({ name: onProp(element.value), kind: "event", event: element.value });
|
|
790
|
+
}
|
|
791
|
+
} else if (declaredEmits) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
return specs.length || declaredProps || declaredEmits ? specs : null;
|
|
795
|
+
}
|
|
796
|
+
function onProp(event) {
|
|
797
|
+
return `on${event.replace(/(^|-)([a-z])/g, (_m, _dash, char) => char.toUpperCase())}`;
|
|
798
|
+
}
|
|
799
|
+
function scriptOf(code) {
|
|
800
|
+
const blocks = [...code.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script>/g)].map((m) => m[1]);
|
|
801
|
+
return blocks.join("\n");
|
|
802
|
+
}
|
|
803
|
+
function keyOf(node) {
|
|
804
|
+
if (node.key?.type === "Identifier") return node.key.name;
|
|
805
|
+
if (node.key?.type === "Literal" && typeof node.key.value === "string") return node.key.value;
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
function specsFrom(members, types) {
|
|
809
|
+
const specs = [];
|
|
810
|
+
for (const member2 of members) {
|
|
811
|
+
if (member2.type !== "TSPropertySignature" && member2.type !== "TSMethodSignature") continue;
|
|
812
|
+
const name2 = keyOf(member2);
|
|
813
|
+
if (!name2) continue;
|
|
814
|
+
specs.push(
|
|
815
|
+
member2.type === "TSMethodSignature" ? toSpec(name2, "function") : toSpec(name2, kindOf(member2.typeAnnotation?.typeAnnotation, types))
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
return specs;
|
|
819
|
+
}
|
|
820
|
+
function macroCall(body, macro) {
|
|
821
|
+
const find = (node, depth = 0) => {
|
|
822
|
+
if (!node || node.type !== "CallExpression" || depth > 3) return void 0;
|
|
823
|
+
if (node.callee?.name === macro) return node;
|
|
824
|
+
for (const argument of node.arguments ?? []) {
|
|
825
|
+
const nested = find(argument, depth + 1);
|
|
826
|
+
if (nested) return nested;
|
|
827
|
+
}
|
|
828
|
+
return void 0;
|
|
829
|
+
};
|
|
830
|
+
for (const raw of body) {
|
|
831
|
+
if (raw.type !== "VariableDeclaration" && raw.type !== "ExpressionStatement") continue;
|
|
832
|
+
const roots = raw.type === "VariableDeclaration" ? raw.declarations.map((d) => d.init) : [raw.expression];
|
|
833
|
+
for (const root of roots) {
|
|
834
|
+
const call = find(root);
|
|
835
|
+
if (call) return call;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return void 0;
|
|
839
|
+
}
|
|
840
|
+
function vueEvents(body, types) {
|
|
841
|
+
const call = macroCall(body, "defineEmits");
|
|
842
|
+
if (!call) return [];
|
|
843
|
+
const array = call.arguments?.[0];
|
|
844
|
+
if (array?.type === "ArrayExpression") {
|
|
845
|
+
const names2 = array.elements.map((element) => element?.value);
|
|
846
|
+
return names2.every((name2) => typeof name2 === "string") ? names2.map((event) => ({ name: onProp(event), kind: "event", event })) : null;
|
|
847
|
+
}
|
|
848
|
+
const members = membersOf(call.typeArguments?.params?.[0], types);
|
|
849
|
+
if (!members) return null;
|
|
850
|
+
const names = members.flatMap((m) => {
|
|
851
|
+
if (m.type === "TSPropertySignature") return [m.key?.value ?? m.key?.name];
|
|
852
|
+
const literal = m.params?.[0]?.typeAnnotation?.typeAnnotation?.literal?.value;
|
|
853
|
+
return literal ? [literal] : [];
|
|
854
|
+
});
|
|
855
|
+
if (!names.every((name2) => typeof name2 === "string")) return null;
|
|
856
|
+
return names.map((event) => ({ name: onProp(event), kind: "event", event }));
|
|
857
|
+
}
|
|
858
|
+
function svelteProps(body) {
|
|
859
|
+
for (const raw of body) {
|
|
860
|
+
if (raw.type !== "VariableDeclaration") continue;
|
|
861
|
+
for (const declarator of raw.declarations) {
|
|
862
|
+
if (declarator.init?.type === "CallExpression" && declarator.init.callee?.name === "$props") {
|
|
863
|
+
return declarator;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
function extractProps(code, options = {}) {
|
|
870
|
+
const file = options.file ?? "component.tsx";
|
|
871
|
+
const sfc = /\.(vue|svelte)$/.exec(file)?.[1];
|
|
872
|
+
const source = sfc ? scriptOf(code) : code;
|
|
873
|
+
const body = parseAst(source, { lang: sfc ? "ts" : "tsx" }).body;
|
|
874
|
+
const types = localTypes(body);
|
|
875
|
+
if (sfc === "vue") {
|
|
876
|
+
const events = vueEvents(body, types);
|
|
877
|
+
if (!events) return null;
|
|
878
|
+
const call = macroCall(body, "defineProps");
|
|
879
|
+
if (call) {
|
|
880
|
+
const members2 = membersOf(call.typeArguments?.params?.[0], types);
|
|
881
|
+
if (!members2) return null;
|
|
882
|
+
return [...specsFrom(members2, types), ...events];
|
|
883
|
+
}
|
|
884
|
+
const options2 = vueOptions(body);
|
|
885
|
+
if (options2) return [...options2, ...events];
|
|
886
|
+
return events.length ? events : null;
|
|
887
|
+
}
|
|
888
|
+
if (sfc === "svelte") {
|
|
889
|
+
const declarator = svelteProps(body);
|
|
890
|
+
if (!declarator) return null;
|
|
891
|
+
const declared2 = declarator.id?.typeAnnotation?.typeAnnotation;
|
|
892
|
+
const members2 = membersOf(declared2, types);
|
|
893
|
+
if (members2) return specsFrom(members2, types);
|
|
894
|
+
if (declared2) return null;
|
|
895
|
+
const names = (declarator.id?.properties ?? []).filter((p) => p.type === "Property" && p.key?.type === "Identifier").map((p) => p.key.name);
|
|
896
|
+
return names.length ? names.map((name2) => toSpec(name2, "auto")) : null;
|
|
897
|
+
}
|
|
898
|
+
const declaration = findExport(body, options.exportName ?? "default");
|
|
899
|
+
if (!declaration) return null;
|
|
900
|
+
const declared = propsType(declaration);
|
|
901
|
+
const members = membersOf(declared, types);
|
|
902
|
+
if (members) return specsFrom(members, types);
|
|
903
|
+
if (declared) return null;
|
|
904
|
+
const destructured = destructuredNames(declaration);
|
|
905
|
+
if (destructured.length) return destructured.map((name2) => toSpec(name2, "auto"));
|
|
906
|
+
return takesProps(declaration) === false ? [] : null;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/embed.ts
|
|
910
|
+
function frameworkFor(file) {
|
|
911
|
+
if (file.endsWith(".vue")) return "vue";
|
|
912
|
+
if (file.endsWith(".svelte")) return "svelte";
|
|
913
|
+
return "react";
|
|
914
|
+
}
|
|
915
|
+
var TS_TYPE = {
|
|
916
|
+
string: "string",
|
|
917
|
+
number: "number",
|
|
918
|
+
boolean: "boolean",
|
|
919
|
+
json: "unknown",
|
|
920
|
+
auto: "unknown",
|
|
921
|
+
function: "(...args: never[]) => unknown"
|
|
922
|
+
};
|
|
923
|
+
function tagFor(file, prefix) {
|
|
924
|
+
return `${prefix}-${kebab(basename(file, extname(file)))}`;
|
|
925
|
+
}
|
|
926
|
+
function normalize(options, root) {
|
|
927
|
+
const prefix = kebab(options.prefix);
|
|
928
|
+
const seen = /* @__PURE__ */ new Set();
|
|
929
|
+
const unique = (entries2) => {
|
|
930
|
+
for (const [tag, entry] of entries2) {
|
|
931
|
+
if (seen.has(tag)) {
|
|
932
|
+
throw new Error(`[mountly] two components both map to <${tag}>: ${entry.component}`);
|
|
933
|
+
}
|
|
934
|
+
seen.add(tag);
|
|
935
|
+
}
|
|
936
|
+
return entries2;
|
|
937
|
+
};
|
|
938
|
+
if (typeof options.elements === "string" || Array.isArray(options.elements)) {
|
|
939
|
+
const patterns = [options.elements].flat();
|
|
940
|
+
const files = patterns.flatMap((pattern) => globSync(pattern, { cwd: root }).sort());
|
|
941
|
+
if (!files.length) {
|
|
942
|
+
throw new Error(`[mountly] no components matched ${patterns.join(", ")}`);
|
|
943
|
+
}
|
|
944
|
+
return unique(files.map((file) => [tagFor(file, prefix), { component: file }]));
|
|
945
|
+
}
|
|
946
|
+
const entries = Object.entries(options.elements).map(
|
|
947
|
+
([name2, entry]) => [
|
|
948
|
+
// The namespace is the point of `prefix`, so it always applies. Naming a
|
|
949
|
+
// key that already carries it is allowed, and means the same tag.
|
|
950
|
+
name2.startsWith(`${prefix}-`) ? name2 : `${prefix}-${kebab(name2)}`,
|
|
951
|
+
typeof entry === "string" ? { component: entry } : entry
|
|
952
|
+
]
|
|
953
|
+
);
|
|
954
|
+
if (!entries.length) throw new Error("[mountly] elements must contain at least one component");
|
|
955
|
+
return unique(entries);
|
|
956
|
+
}
|
|
957
|
+
function declarations(elements) {
|
|
958
|
+
const blocks = elements.map(({ tag, props }) => {
|
|
959
|
+
const id = name(tag);
|
|
960
|
+
const members = props.filter((spec) => spec.kind !== "event").map((spec) => ` ${member(spec.name)}?: ${TS_TYPE[spec.kind] ?? "unknown"};`);
|
|
961
|
+
const events = props.filter((spec) => spec.kind === "event").map((spec) => ` ${JSON.stringify(spec.event)}: CustomEvent;`);
|
|
962
|
+
const block = (kind, lines) => lines.length ? `export interface ${id}${kind} {
|
|
963
|
+
${lines.join("\n")}
|
|
964
|
+
}` : `export interface ${id}${kind} {}`;
|
|
965
|
+
return `${block("Props", members)}
|
|
966
|
+
|
|
967
|
+
${block("EventMap", events)}
|
|
968
|
+
|
|
969
|
+
export interface ${id}Element extends MountlyElement<${id}Props> {
|
|
970
|
+
addEventListener<K extends keyof ${id}EventMap>(
|
|
971
|
+
type: K,
|
|
972
|
+
listener: (this: ${id}Element, event: ${id}EventMap[K]) => unknown,
|
|
973
|
+
options?: boolean | AddEventListenerOptions,
|
|
974
|
+
): void;
|
|
975
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
976
|
+
type: K,
|
|
977
|
+
listener: (this: ${id}Element, event: HTMLElementEventMap[K]) => unknown,
|
|
978
|
+
options?: boolean | AddEventListenerOptions,
|
|
979
|
+
): void;
|
|
980
|
+
addEventListener(
|
|
981
|
+
type: string,
|
|
982
|
+
listener: EventListenerOrEventListenerObject,
|
|
983
|
+
options?: boolean | AddEventListenerOptions,
|
|
984
|
+
): void;
|
|
985
|
+
}`;
|
|
986
|
+
});
|
|
987
|
+
const tagMap = elements.map(({ tag }) => ` ${JSON.stringify(tag)}: ${name(tag)}Element;`);
|
|
988
|
+
const jsx = elements.map(
|
|
989
|
+
({ tag }) => ` ${JSON.stringify(tag)}: ${name(tag)}Props & { children?: unknown; class?: string; id?: string };`
|
|
990
|
+
);
|
|
991
|
+
return `// Generated by mountly-vite-plugin. Do not edit.
|
|
992
|
+
// Self-contained: a consumer with only the script tag needs no package installed.
|
|
993
|
+
|
|
994
|
+
type MountlyElement<Props> = HTMLElement &
|
|
995
|
+
Partial<Props> & {
|
|
996
|
+
/** Retry a failed load, or mount before the configured trigger. */
|
|
997
|
+
mount(): Promise<void>;
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
${blocks.join("\n\n")}
|
|
1001
|
+
|
|
1002
|
+
declare global {
|
|
1003
|
+
interface HTMLElementTagNameMap {
|
|
1004
|
+
${tagMap.join("\n")}
|
|
1005
|
+
}
|
|
1006
|
+
// The global JSX namespace, which Preact, Solid and Vue JSX read.
|
|
1007
|
+
namespace JSX {
|
|
1008
|
+
interface IntrinsicElements {
|
|
1009
|
+
${jsx.join("\n")}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
`;
|
|
1014
|
+
}
|
|
1015
|
+
function reactDeclarations(elements) {
|
|
1016
|
+
const jsx = elements.map(
|
|
1017
|
+
({ tag }) => ` ${JSON.stringify(tag)}: ${name(tag)}Props & { children?: React.ReactNode; class?: string; id?: string; ref?: React.Ref<HTMLElement> };`
|
|
1018
|
+
);
|
|
1019
|
+
const imports = elements.map(({ tag }) => `${name(tag)}Props`).join(", ");
|
|
1020
|
+
return `// Generated by mountly-vite-plugin. Do not edit.
|
|
1021
|
+
// Reference this file instead of embed.d.ts in a React host.
|
|
1022
|
+
/// <reference path="./embed.d.ts" />
|
|
1023
|
+
import type * as React from "react";
|
|
1024
|
+
import type { ${imports} } from "./embed.js";
|
|
1025
|
+
|
|
1026
|
+
declare module "react" {
|
|
1027
|
+
namespace JSX {
|
|
1028
|
+
interface IntrinsicElements {
|
|
1029
|
+
${jsx.join("\n")}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
`;
|
|
1034
|
+
}
|
|
1035
|
+
function member(prop) {
|
|
1036
|
+
return /^[A-Za-z_$][\w$]*$/.test(prop) ? prop : JSON.stringify(prop);
|
|
1037
|
+
}
|
|
1038
|
+
function name(tag) {
|
|
1039
|
+
return tag.replace(/(^|-)([a-z])/g, (_m, _d, char) => char.toUpperCase());
|
|
1040
|
+
}
|
|
1041
|
+
function manifest(elements) {
|
|
1042
|
+
return JSON.stringify(
|
|
1043
|
+
{
|
|
1044
|
+
schemaVersion: "1.0.0",
|
|
1045
|
+
readme: "",
|
|
1046
|
+
modules: elements.map(({ tag, entry, props }) => ({
|
|
1047
|
+
kind: "javascript-module",
|
|
1048
|
+
path: entry.component,
|
|
1049
|
+
declarations: [
|
|
1050
|
+
{
|
|
1051
|
+
kind: "class",
|
|
1052
|
+
name: name(tag),
|
|
1053
|
+
customElement: true,
|
|
1054
|
+
tagName: tag,
|
|
1055
|
+
members: props.filter((spec) => spec.kind !== "event").map((spec) => ({
|
|
1056
|
+
kind: "field",
|
|
1057
|
+
name: spec.name,
|
|
1058
|
+
type: { text: TS_TYPE[spec.kind] ?? "unknown" }
|
|
1059
|
+
})),
|
|
1060
|
+
attributes: props.filter((spec) => spec.attribute).map((spec) => ({
|
|
1061
|
+
name: spec.attribute,
|
|
1062
|
+
fieldName: spec.name,
|
|
1063
|
+
type: { text: TS_TYPE[spec.kind] ?? "unknown" }
|
|
1064
|
+
})),
|
|
1065
|
+
events: props.filter((spec) => spec.kind === "event").map((spec) => ({ name: spec.event, type: { text: "CustomEvent" } }))
|
|
1066
|
+
}
|
|
1067
|
+
],
|
|
1068
|
+
exports: [
|
|
1069
|
+
{
|
|
1070
|
+
kind: "custom-element-definition",
|
|
1071
|
+
name: tag,
|
|
1072
|
+
declaration: { name: name(tag), module: entry.component }
|
|
1073
|
+
}
|
|
1074
|
+
]
|
|
1075
|
+
}))
|
|
1076
|
+
},
|
|
1077
|
+
null,
|
|
1078
|
+
2
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
var FRAMEWORK_PLUGIN = {
|
|
1082
|
+
vue: {
|
|
1083
|
+
prefix: "vite:vue",
|
|
1084
|
+
module: "@vitejs/plugin-vue",
|
|
1085
|
+
load: async () => [(await import("@vitejs/plugin-vue")).default()].flat()
|
|
1086
|
+
},
|
|
1087
|
+
svelte: {
|
|
1088
|
+
prefix: "vite-plugin-svelte",
|
|
1089
|
+
module: "@sveltejs/vite-plugin-svelte",
|
|
1090
|
+
load: async () => [(await import("@sveltejs/vite-plugin-svelte")).svelte()].flat()
|
|
1091
|
+
}
|
|
1092
|
+
// React needs none: `oxc.jsx` below already transforms JSX.
|
|
1093
|
+
};
|
|
1094
|
+
async function frameworkPlugins(frameworks) {
|
|
1095
|
+
const added = [];
|
|
1096
|
+
for (const framework of frameworks) {
|
|
1097
|
+
const entry = FRAMEWORK_PLUGIN[framework];
|
|
1098
|
+
if (!entry) continue;
|
|
1099
|
+
try {
|
|
1100
|
+
added.push(...await entry.load());
|
|
1101
|
+
} catch {
|
|
1102
|
+
throw new Error(
|
|
1103
|
+
`[mountly] ${framework} components need ${entry.module}. Install it, or add your own compiler plugin to the config.`
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return added;
|
|
1108
|
+
}
|
|
1109
|
+
function assertSingleCompiler(plugins = []) {
|
|
1110
|
+
for (const entry of Object.values(FRAMEWORK_PLUGIN)) {
|
|
1111
|
+
const count = plugins.filter((plugin) => plugin?.name === entry.prefix).length;
|
|
1112
|
+
if (count > 1) {
|
|
1113
|
+
throw new Error(
|
|
1114
|
+
`[mountly] ${entry.module} is registered ${count} times. defineElementsConfig adds it for you \u2014 remove it from your own plugins array.`
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
var CSS_TOKEN = "__MOUNTLY_EMBED_CSS__";
|
|
1120
|
+
function resolveCssToken(bundle) {
|
|
1121
|
+
const stylesheet = Object.keys(bundle).find((file) => file.endsWith(".css"));
|
|
1122
|
+
const token = new RegExp(`(["'\`])${CSS_TOKEN}\\1`, "g");
|
|
1123
|
+
for (const output of Object.values(bundle)) {
|
|
1124
|
+
if (output.type !== "chunk" || !output.code.includes(CSS_TOKEN)) continue;
|
|
1125
|
+
if (!stylesheet) {
|
|
1126
|
+
output.code = output.code.replace(token, "void 0");
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
const href = relative3(dirname3(output.fileName), stylesheet).replace(/\\/g, "/");
|
|
1130
|
+
output.code = output.code.replace(
|
|
1131
|
+
token,
|
|
1132
|
+
`new URL(${JSON.stringify(`./${href}`)}, import.meta.url).href`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
var WORKER_SHIM = 'function __mountlyWorker(u,o){var h=String(u);if(new URL(h,location.href).origin===location.origin)return new Worker(u,o);var s=o&&o.type==="module"?"import "+JSON.stringify(h)+";":"importScripts("+JSON.stringify(h)+");";return new Worker(URL.createObjectURL(new Blob([s],{type:"text/javascript"})),o)}';
|
|
1137
|
+
var FUNCTIONS = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
|
|
1138
|
+
var VAR_SCOPES = /* @__PURE__ */ new Set([...FUNCTIONS, "StaticBlock"]);
|
|
1139
|
+
function patternNames(pattern) {
|
|
1140
|
+
if (!pattern) return [];
|
|
1141
|
+
switch (pattern.type) {
|
|
1142
|
+
case "Identifier":
|
|
1143
|
+
return [pattern.name];
|
|
1144
|
+
case "ObjectPattern":
|
|
1145
|
+
return pattern.properties.flatMap(
|
|
1146
|
+
(p) => patternNames(p.type === "RestElement" ? p.argument : p.value)
|
|
1147
|
+
);
|
|
1148
|
+
case "ArrayPattern":
|
|
1149
|
+
return pattern.elements.flatMap(patternNames);
|
|
1150
|
+
case "RestElement":
|
|
1151
|
+
return patternNames(pattern.argument);
|
|
1152
|
+
case "AssignmentPattern":
|
|
1153
|
+
return patternNames(pattern.left);
|
|
1154
|
+
default:
|
|
1155
|
+
return [];
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function children(node) {
|
|
1159
|
+
const out = [];
|
|
1160
|
+
for (const key in node) {
|
|
1161
|
+
if (key === "parent") continue;
|
|
1162
|
+
const value = node[key];
|
|
1163
|
+
if (Array.isArray(value)) {
|
|
1164
|
+
for (const item of value) if (item && typeof item.type === "string") out.push(item);
|
|
1165
|
+
} else if (value && typeof value.type === "string") out.push(value);
|
|
1166
|
+
}
|
|
1167
|
+
return out;
|
|
1168
|
+
}
|
|
1169
|
+
function varNames(node) {
|
|
1170
|
+
if (VAR_SCOPES.has(node.type)) return [];
|
|
1171
|
+
const own = node.type === "VariableDeclaration" && node.kind === "var" ? node.declarations.flatMap((d) => patternNames(d.id)) : [];
|
|
1172
|
+
return [...own, ...children(node).flatMap(varNames)];
|
|
1173
|
+
}
|
|
1174
|
+
function lexicalNames(statements) {
|
|
1175
|
+
return statements.flatMap((statement) => {
|
|
1176
|
+
const declared = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
|
|
1177
|
+
if (!declared) return [];
|
|
1178
|
+
switch (declared.type) {
|
|
1179
|
+
case "VariableDeclaration":
|
|
1180
|
+
return declared.kind === "var" ? [] : declared.declarations.flatMap((d) => patternNames(d.id));
|
|
1181
|
+
case "FunctionDeclaration":
|
|
1182
|
+
case "ClassDeclaration":
|
|
1183
|
+
return declared.id ? [declared.id.name] : [];
|
|
1184
|
+
case "ImportDeclaration":
|
|
1185
|
+
return declared.specifiers.map((spec) => spec.local.name);
|
|
1186
|
+
default:
|
|
1187
|
+
return [];
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
function scopeOf(node, parent) {
|
|
1192
|
+
switch (node.type) {
|
|
1193
|
+
case "Program":
|
|
1194
|
+
return [...lexicalNames(node.body), ...varNames({ type: "Block", body: node.body })];
|
|
1195
|
+
case "FunctionDeclaration":
|
|
1196
|
+
case "FunctionExpression":
|
|
1197
|
+
case "ArrowFunctionExpression":
|
|
1198
|
+
return [
|
|
1199
|
+
...node.params.flatMap(patternNames),
|
|
1200
|
+
// A named function expression sees its own name.
|
|
1201
|
+
...node.type === "FunctionExpression" && node.id ? [node.id.name] : []
|
|
1202
|
+
];
|
|
1203
|
+
case "BlockStatement":
|
|
1204
|
+
return FUNCTIONS.has(parent?.type) && parent?.body === node ? [...lexicalNames(node.body), ...node.body.flatMap(varNames)] : lexicalNames(node.body);
|
|
1205
|
+
case "StaticBlock":
|
|
1206
|
+
return [...lexicalNames(node.body), ...node.body.flatMap(varNames)];
|
|
1207
|
+
case "SwitchStatement":
|
|
1208
|
+
return lexicalNames(node.cases.flatMap((c) => c.consequent));
|
|
1209
|
+
case "ForStatement":
|
|
1210
|
+
case "ForInStatement":
|
|
1211
|
+
case "ForOfStatement": {
|
|
1212
|
+
const head = node.type === "ForStatement" ? node.init : node.left;
|
|
1213
|
+
return head?.type === "VariableDeclaration" && head.kind !== "var" ? head.declarations.flatMap((d) => patternNames(d.id)) : [];
|
|
1214
|
+
}
|
|
1215
|
+
case "CatchClause":
|
|
1216
|
+
return patternNames(node.param);
|
|
1217
|
+
case "ClassExpression":
|
|
1218
|
+
return node.id ? [node.id.name] : [];
|
|
1219
|
+
default:
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function shimWorkers(code) {
|
|
1224
|
+
if (!code.includes("Worker")) return null;
|
|
1225
|
+
const callees = [];
|
|
1226
|
+
const walk = (node, parent, shadowed) => {
|
|
1227
|
+
const scope = scopeOf(node, parent);
|
|
1228
|
+
const hidden = shadowed || Boolean(scope?.includes("Worker"));
|
|
1229
|
+
if (!hidden && node.type === "NewExpression" && node.callee.type === "Identifier" && node.callee.name === "Worker") {
|
|
1230
|
+
callees.push(node.callee);
|
|
1231
|
+
}
|
|
1232
|
+
for (const child of children(node)) {
|
|
1233
|
+
const outside = node.type === "SwitchStatement" && child === node.discriminant;
|
|
1234
|
+
walk(child, node, outside ? shadowed : hidden);
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
walk(parseAst2(code, { lang: "js" }), void 0, false);
|
|
1238
|
+
if (!callees.length) return null;
|
|
1239
|
+
let out = code;
|
|
1240
|
+
for (const { start, end } of callees.sort((a, b) => b.start - a.start)) {
|
|
1241
|
+
out = out.slice(0, start) + "__mountlyWorker" + out.slice(end);
|
|
1242
|
+
}
|
|
1243
|
+
return WORKER_SHIM + out;
|
|
1244
|
+
}
|
|
1245
|
+
function readPropTable(entry, tag, root) {
|
|
1246
|
+
const file = resolve3(root, entry.component);
|
|
1247
|
+
const props = extractProps(readFileSync2(file, "utf8"), {
|
|
1248
|
+
file,
|
|
1249
|
+
exportName: entry.exportName
|
|
1250
|
+
});
|
|
1251
|
+
if (!props) {
|
|
1252
|
+
throw new Error(
|
|
1253
|
+
`[mountly] cannot read the props of ${entry.component} for <${tag}>. Declare the props type in that file, or pass \`props\` on the element entry.`
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
return props;
|
|
1257
|
+
}
|
|
1258
|
+
function defineElementsConfig(options) {
|
|
1259
|
+
const entryId = "virtual:mountly-embed";
|
|
1260
|
+
const widgetPrefix = "virtual:mountly-widget/";
|
|
1261
|
+
const shadow = options.shadow === true;
|
|
1262
|
+
let elements = [];
|
|
1263
|
+
let root = options.root ?? process.cwd();
|
|
1264
|
+
const frameworks = new Set(
|
|
1265
|
+
normalize(options, root).map(([, entry]) => entry.framework ?? frameworkFor(entry.component))
|
|
1266
|
+
);
|
|
1267
|
+
const plugin = {
|
|
1268
|
+
name: "mountly:elements",
|
|
1269
|
+
configResolved(config) {
|
|
1270
|
+
root = config.root;
|
|
1271
|
+
assertSingleCompiler(config.plugins);
|
|
1272
|
+
elements = normalize(options, root).map(([tag, entry]) => ({
|
|
1273
|
+
tag,
|
|
1274
|
+
entry,
|
|
1275
|
+
props: entry.props ?? readPropTable(entry, tag, root),
|
|
1276
|
+
framework: entry.framework ?? frameworkFor(entry.component)
|
|
1277
|
+
}));
|
|
1278
|
+
},
|
|
1279
|
+
resolveId(id) {
|
|
1280
|
+
if (id === entryId || id.startsWith(widgetPrefix)) return `\0${id}`;
|
|
1281
|
+
},
|
|
1282
|
+
load(id) {
|
|
1283
|
+
if (id === `\0${entryId}`) {
|
|
1284
|
+
const definitions = elements.map(
|
|
1285
|
+
({ tag, entry, props }) => `${JSON.stringify(tag)}: {
|
|
1286
|
+
load: () => import(${JSON.stringify(widgetPrefix + tag)}),
|
|
1287
|
+
props: ${JSON.stringify(props)},
|
|
1288
|
+
trigger: ${JSON.stringify(entry.trigger ?? "connected")}
|
|
1289
|
+
}`
|
|
1290
|
+
);
|
|
1291
|
+
return `import { defineElements } from "mountly/embed";
|
|
1292
|
+
defineElements({
|
|
1293
|
+
${definitions.join(",\n ")}
|
|
1294
|
+
});`;
|
|
1295
|
+
}
|
|
1296
|
+
if (id.startsWith(`\0${widgetPrefix}`)) {
|
|
1297
|
+
const tag = id.slice(widgetPrefix.length + 1);
|
|
1298
|
+
const element = elements.find((candidate) => candidate.tag === tag);
|
|
1299
|
+
if (!element) return;
|
|
1300
|
+
const path = resolve3(root, element.entry.component).replace(/\\/g, "/");
|
|
1301
|
+
const widgetOptions = shadow ? `, { shadow: true, cssUrl: ${JSON.stringify(CSS_TOKEN)} }` : "";
|
|
1302
|
+
return `import * as component from ${JSON.stringify(path)};
|
|
1303
|
+
import { createWidget } from ${JSON.stringify(`mountly-${element.framework}`)};
|
|
1304
|
+
export default createWidget(component[${JSON.stringify(element.entry.exportName ?? "default")}]${widgetOptions});`;
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
// Vite has already rewritten `new Worker(new URL(…))` to the emitted file by
|
|
1308
|
+
// now, so only the constructor call needs rerouting.
|
|
1309
|
+
renderChunk(code) {
|
|
1310
|
+
const shimmed = shimWorkers(code);
|
|
1311
|
+
return shimmed ? { code: shimmed, map: null } : null;
|
|
1312
|
+
},
|
|
1313
|
+
// Vite writes the stylesheet from its own `generateBundle`, so the name to
|
|
1314
|
+
// point at only exists once every other plugin has had its turn.
|
|
1315
|
+
generateBundle: {
|
|
1316
|
+
order: "post",
|
|
1317
|
+
handler(_options, bundle) {
|
|
1318
|
+
if (shadow) resolveCssToken(bundle);
|
|
1319
|
+
this.emitFile({ type: "asset", fileName: "embed.d.ts", source: declarations(elements) });
|
|
1320
|
+
this.emitFile({
|
|
1321
|
+
type: "asset",
|
|
1322
|
+
fileName: "embed.react.d.ts",
|
|
1323
|
+
source: reactDeclarations(elements)
|
|
1324
|
+
});
|
|
1325
|
+
this.emitFile({
|
|
1326
|
+
type: "asset",
|
|
1327
|
+
fileName: "custom-elements.json",
|
|
1328
|
+
source: manifest(
|
|
1329
|
+
elements.map((element) => ({
|
|
1330
|
+
...element,
|
|
1331
|
+
entry: {
|
|
1332
|
+
...element.entry,
|
|
1333
|
+
component: relative3(root, resolve3(root, element.entry.component))
|
|
1334
|
+
}
|
|
1335
|
+
}))
|
|
1336
|
+
)
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
return {
|
|
1342
|
+
...options.root ? { root: options.root } : {},
|
|
1343
|
+
// Vite's application pipeline already handles lazy CSS, asset rebasing,
|
|
1344
|
+
// and shared framework chunks. A library build would make us recreate it.
|
|
1345
|
+
base: "./",
|
|
1346
|
+
// Only enable the JSX transform when React is in play: it would otherwise
|
|
1347
|
+
// try to parse a `.vue` or `.svelte` file before its compiler sees it.
|
|
1348
|
+
oxc: frameworks.has("react") ? { jsx: { runtime: "automatic" } } : void 0,
|
|
1349
|
+
plugins: [plugin, frameworkPlugins(frameworks)],
|
|
1350
|
+
// ES workers keep their own `import.meta.url` when the shim imports them
|
|
1351
|
+
// from a blob:, so their relative URLs still resolve against the provider.
|
|
1352
|
+
// Classic (iife) workers would see the blob's URL instead.
|
|
1353
|
+
worker: { format: "es" },
|
|
1354
|
+
build: {
|
|
1355
|
+
sourcemap: true,
|
|
1356
|
+
// Light DOM wants Vite's per-chunk stylesheets, which arrive with the
|
|
1357
|
+
// component that needs them. Shadow roots need the opposite: one
|
|
1358
|
+
// stylesheet nothing injects, which each element adopts for itself.
|
|
1359
|
+
cssCodeSplit: !shadow,
|
|
1360
|
+
rollupOptions: {
|
|
1361
|
+
input: entryId,
|
|
1362
|
+
output: { entryFileNames: "embed.js", chunkFileNames: "assets/[name]-[hash].js" }
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
|
|
595
1368
|
// src/index.ts
|
|
596
1369
|
var PLATFORM_EXTERNALS = ["mountly", /^mountly\//];
|
|
597
1370
|
var FRAMEWORK_EXTERNALS = {
|
|
@@ -608,10 +1381,7 @@ function getFrameworkPeerExternals(framework) {
|
|
|
608
1381
|
return [...PLATFORM_EXTERNALS, ...FRAMEWORK_EXTERNALS[framework]];
|
|
609
1382
|
}
|
|
610
1383
|
function getSelfContainedExternals(framework) {
|
|
611
|
-
return [
|
|
612
|
-
...PLATFORM_EXTERNALS,
|
|
613
|
-
...FRAMEWORK_EXTERNALS[framework].filter((id) => !FRAMEWORK_BUNDLE[framework].includes(id))
|
|
614
|
-
];
|
|
1384
|
+
return FRAMEWORK_EXTERNALS[framework].filter((id) => !FRAMEWORK_BUNDLE[framework].includes(id));
|
|
615
1385
|
}
|
|
616
1386
|
function matchesFilter(id, include) {
|
|
617
1387
|
return include.some(
|
|
@@ -626,7 +1396,7 @@ function mountlyCssAsText(options = {}) {
|
|
|
626
1396
|
load(id) {
|
|
627
1397
|
if (!id.endsWith(".css")) return null;
|
|
628
1398
|
if (includePatterns && !matchesFilter(id, includePatterns)) return null;
|
|
629
|
-
const source =
|
|
1399
|
+
const source = readFileSync3(id, "utf8");
|
|
630
1400
|
return {
|
|
631
1401
|
code: `export default ${JSON.stringify(source)};`,
|
|
632
1402
|
map: { mappings: "" }
|
|
@@ -784,7 +1554,9 @@ function defineMountlyWidgetConfig(options = {}) {
|
|
|
784
1554
|
return configs;
|
|
785
1555
|
}
|
|
786
1556
|
export {
|
|
1557
|
+
defineElementsConfig,
|
|
787
1558
|
defineMountlyWidgetConfig,
|
|
1559
|
+
extractProps,
|
|
788
1560
|
getFrameworkPeerExternals,
|
|
789
1561
|
getSelfContainedExternals,
|
|
790
1562
|
mountlyCssAsText,
|
package/dist/props.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How an attribute string becomes a prop value. Derived from the component's
|
|
3
|
+
* own TypeScript types, so `currency="GBP"` stays a string and `balance="1250"`
|
|
4
|
+
* becomes a number without either side declaring anything.
|
|
5
|
+
*
|
|
6
|
+
* `event` is a callback the component calls out (`onSave`), dispatched as a DOM
|
|
7
|
+
* event. `function` is one the host passes in (`getToken`, `formatPrice`): a
|
|
8
|
+
* property only, never an attribute, and its return value reaches the component.
|
|
9
|
+
*/
|
|
10
|
+
export type PropKind = "string" | "number" | "boolean" | "json" | "auto" | "event" | "function";
|
|
11
|
+
export interface PropSpec {
|
|
12
|
+
/** Prop name the component reads. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Attribute the consumer writes. Kebab-case of `name`; absent for events. */
|
|
15
|
+
attribute?: string;
|
|
16
|
+
kind: PropKind;
|
|
17
|
+
/** DOM event dispatched for a callback prop. */
|
|
18
|
+
event?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function kebab(name: string): string;
|
|
21
|
+
/** `onViewDetails` → `view-details`. */
|
|
22
|
+
export declare function eventName(prop: string): string;
|
|
23
|
+
export interface ExtractOptions {
|
|
24
|
+
/** Path or filename. Its extension selects the component dialect. */
|
|
25
|
+
file?: string;
|
|
26
|
+
/** Named export to read, for `.tsx`/`.ts` components. Defaults to the default export. */
|
|
27
|
+
exportName?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read a component's prop table from its source. This is what lets the element
|
|
31
|
+
* expose real attributes, real properties and real events without the author
|
|
32
|
+
* restating anything they already wrote — in React, Vue or Svelte.
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` when the component takes props but none of them could be read —
|
|
35
|
+
* the caller must fail loudly rather than publish an element that silently
|
|
36
|
+
* ignores everything the consumer sets.
|
|
37
|
+
*
|
|
38
|
+
* Analysis is single-file. A props type imported from another module reads as
|
|
39
|
+
* `null`; declare `props` on the element entry for those.
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractProps(code: string, options?: ExtractOptions): PropSpec[] | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mountly-vite-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Vite lib build plugin for mountly vertical widgets (dual ESM output)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -24,17 +24,27 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
28
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
27
29
|
"tsup": "^8.5.1",
|
|
28
30
|
"typescript": "^7.0.2",
|
|
29
31
|
"vite": "^8.1.5",
|
|
30
|
-
"mountly-manifest": "1.1.
|
|
32
|
+
"mountly-manifest": "1.1.1"
|
|
31
33
|
},
|
|
32
34
|
"peerDependencies": {
|
|
35
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
36
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
33
37
|
"typescript": ">=7.0.0",
|
|
34
38
|
"vite": "^8.2.1",
|
|
35
|
-
"mountly-manifest": "1.1.
|
|
39
|
+
"mountly-manifest": "1.1.1"
|
|
36
40
|
},
|
|
37
41
|
"peerDependenciesMeta": {
|
|
42
|
+
"@sveltejs/vite-plugin-svelte": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@vitejs/plugin-vue": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
38
48
|
"typescript": {
|
|
39
49
|
"optional": true
|
|
40
50
|
}
|