vue-fast-mount 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jan Müller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # vue-fast-mount
2
+
3
+ > Vite plugin for faster mounting of Vue components in Vitest.
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/DerYeger/yeger/actions/workflows/ci.yml">
7
+ <img alt="CI" src="https://img.shields.io/github/actions/workflow/status/DerYeger/yeger/ci.yml?branch=main&label=ci&logo=github&color=#4DC71F">
8
+ </a>
9
+ <a href="https://www.npmjs.com/package/vue-fast-mount">
10
+ <img alt="NPM" src="https://img.shields.io/npm/v/vue-fast-mount?logo=npm">
11
+ </a>
12
+ <a href="https://app.codecov.io/gh/DerYeger/yeger/tree/main/packages/vue-fast-mount">
13
+ <img alt="Coverage" src="https://codecov.io/gh/DerYeger/yeger/branch/main/graph/badge.svg?token=DjcvNlg4hd&flag=vue-fast-mount">
14
+ </a>
15
+ </p>
16
+
17
+ ## Features
18
+
19
+ While `shallowMount` already skips mounting child components, it still imports them and their transitive dependencies.
20
+ With `fastMount`, SFCs are transformed to omit static imports of stubbed components.
21
+ This enables much faster tests, even for complex Vue applications with large import graphs.
22
+
23
+ > Warning: While tests have been hand-crafted to cover expected usage scenarios, the implementation is mostly AI-generated.
24
+ > Use this package with caution.
25
+
26
+ > Note: This package only supports `<script setup>` components.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pnpm install -D vue-fast-mount
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ First, add the plugin to your Vite(st) config.
37
+
38
+ ```ts vitest.config.ts
39
+ import vue from '@vitejs/plugin-vue'
40
+ import { defineConfig } from 'vitest/config'
41
+ import { vueFastMount } from 'vue-fast-mount/plugin'
42
+
43
+ export default defineConfig({
44
+ plugins: [vue(), vueFastMount()],
45
+ })
46
+ ```
47
+
48
+ Then, use `fastMount` as a replacement for `shallowMount`:
49
+
50
+ ```ts
51
+ import { describe, expect, test } from 'vitest'
52
+ import { fastMount } from 'vue-fast-mount'
53
+
54
+ describe('MyComponent', () => {
55
+ test('mounts the component', async () => {
56
+ const wrapper = await fastMount(import('./MyComponent.vue'))
57
+ expect(wrapper.exists()).toBe(true)
58
+ })
59
+ })
60
+ ```
61
+
62
+ > Note: The wrapper's HTML will differ from `shallowMount` if you are using aliased imports.
@@ -0,0 +1,7 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region src/plugin.d.ts
4
+ declare function vueFastMount(): Plugin;
5
+ //#endregion
6
+ export { vueFastMount };
7
+ //# sourceMappingURL=plugin.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;iBASgB,YAAA,CAAA,GAAgB,MAAA"}
@@ -0,0 +1,432 @@
1
+ import { normalizePath } from "vite";
2
+
3
+ //#region src/plugin-helpers.ts
4
+ const FAST_MOUNT_QUERY_KEY = "__vfm";
5
+ const FAST_MOUNT_QUERY_VALUE = "1";
6
+ const FAST_MOUNT_KEEP_QUERY_KEY = "__vfm_keep";
7
+ function isFastMountRuntimeImportSource(source) {
8
+ return source === "vue-fast-mount";
9
+ }
10
+ function escapeForRegExp(value) {
11
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12
+ }
13
+ function appendFastMountQuery(specifier, keepBindings = []) {
14
+ if (!specifier.includes(".vue")) return specifier;
15
+ const hashIndex = specifier.indexOf("#");
16
+ const hash = hashIndex === -1 ? "" : specifier.slice(hashIndex);
17
+ const base = hashIndex === -1 ? specifier : specifier.slice(0, hashIndex);
18
+ if (new RegExp(`(?:\\?|&)${FAST_MOUNT_QUERY_KEY}=`).test(base)) return specifier;
19
+ return `${base}${base.includes("?") ? "&" : "?"}${FAST_MOUNT_QUERY_KEY}=${FAST_MOUNT_QUERY_VALUE}${keepBindings.length ? `&${FAST_MOUNT_KEEP_QUERY_KEY}=${encodeURIComponent(keepBindings.join(","))}` : ""}${hash}`;
20
+ }
21
+ function findMatchingBracket(value, start, open, close) {
22
+ let depth = 0;
23
+ for (let index = start; index < value.length; index += 1) {
24
+ const character = value[index];
25
+ if (character === open) {
26
+ depth += 1;
27
+ continue;
28
+ }
29
+ if (character === close) {
30
+ depth -= 1;
31
+ if (depth === 0) return index;
32
+ }
33
+ }
34
+ return -1;
35
+ }
36
+ function extractExplicitlyUnstubbedComponents(callExpression) {
37
+ const stubsMatch = /\bstubs\s*:/.exec(callExpression);
38
+ if (!stubsMatch) return [];
39
+ const stubsObjectStart = callExpression.indexOf("{", stubsMatch.index);
40
+ if (stubsObjectStart === -1) return [];
41
+ const stubsObjectEnd = findMatchingBracket(callExpression, stubsObjectStart, "{", "}");
42
+ if (stubsObjectEnd === -1) return [];
43
+ const stubsObject = callExpression.slice(stubsObjectStart + 1, stubsObjectEnd);
44
+ const keepBindings = /* @__PURE__ */ new Set();
45
+ for (const match of stubsObject.matchAll(/(?:^|,)\s*(?:['"]([A-Za-z_$][\w$-]*)['"]|([A-Za-z_$][\w$]*))\s*:\s*false\b/g)) {
46
+ const componentName = match[1] ?? match[2];
47
+ if (componentName) keepBindings.add(componentName);
48
+ }
49
+ return [...keepBindings];
50
+ }
51
+ function getCallExpression(code, callStart) {
52
+ const openParenthesis = code.indexOf("(", callStart);
53
+ if (openParenthesis === -1) return null;
54
+ const closeParenthesis = findMatchingBracket(code, openParenthesis, "(", ")");
55
+ if (closeParenthesis === -1) return null;
56
+ return code.slice(callStart, closeParenthesis + 1);
57
+ }
58
+ function parseFastMountAliases(code) {
59
+ const aliases = /* @__PURE__ */ new Set();
60
+ for (const match of code.matchAll(/import\s*{([^}]*)}\s*from\s*['"]([^'"]+)['"]/g)) {
61
+ const source = match[2];
62
+ if (!source || !isFastMountRuntimeImportSource(source)) continue;
63
+ const specifiers = match[1]?.split(",") ?? [];
64
+ for (const specifier of specifiers) {
65
+ const trimmedSpecifier = specifier.trim();
66
+ if (!trimmedSpecifier) continue;
67
+ const aliasMatch = /^fastMount(?:\s+as\s+([A-Za-z_$][\w$]*))?$/.exec(trimmedSpecifier);
68
+ if (!aliasMatch) continue;
69
+ aliases.add(aliasMatch[1] ?? "fastMount");
70
+ }
71
+ }
72
+ return [...aliases];
73
+ }
74
+ function rewriteFastMountCallsites(code) {
75
+ const aliases = parseFastMountAliases(code);
76
+ if (!aliases.length) return code;
77
+ let transformedCode = code;
78
+ for (const alias of aliases) {
79
+ const escapedAlias = escapeForRegExp(alias);
80
+ const pattern = new RegExp(`(^|[^\\w$.])(${escapedAlias})\\s*\\(\\s*import\\s*\\(\\s*(["'])([^"']+)\\3\\s*\\)`, "gm");
81
+ transformedCode = transformedCode.replace(pattern, (match, prefix, name, quote, specifier, offset, fullCode) => {
82
+ const callExpression = getCallExpression(fullCode, offset + prefix.length);
83
+ const rewrittenSpecifier = appendFastMountQuery(specifier, callExpression ? extractExplicitlyUnstubbedComponents(callExpression) : []);
84
+ if (rewrittenSpecifier === specifier) return match;
85
+ return `${prefix}${name}(import(${quote}${rewrittenSpecifier}${quote})`;
86
+ });
87
+ }
88
+ return transformedCode;
89
+ }
90
+ function toPascalCase(value) {
91
+ return value.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
92
+ }
93
+ function toCamelCase(value) {
94
+ return value.replace(/-([a-zA-Z])/g, (_, character) => character.toUpperCase());
95
+ }
96
+ function toKebabCase(value) {
97
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
98
+ }
99
+ function getComponentBindingName(tagName) {
100
+ const firstCharacter = tagName.charAt(0);
101
+ if (firstCharacter && firstCharacter === firstCharacter.toUpperCase()) return tagName;
102
+ if (tagName.includes("-")) return toPascalCase(tagName);
103
+ return "";
104
+ }
105
+ function collectPropsAndEmitsFromAttributes(attributes) {
106
+ const props = /* @__PURE__ */ new Set();
107
+ const emits = /* @__PURE__ */ new Set();
108
+ for (const match of attributes.matchAll(/(?:@|:)[A-Za-z_$][\w$:-]*|v-bind:[A-Za-z_$][\w$-]*|v-on:[A-Za-z_$][\w$:-]*|v-model(?::[A-Za-z_$][\w$-]*)?|[A-Za-z][\w-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?/g)) {
109
+ const token = match[0]?.trim();
110
+ if (!token) continue;
111
+ const key = token.split("=")[0]?.trim() ?? "";
112
+ if (!key) continue;
113
+ if (key.startsWith("v-model")) {
114
+ const argument = key.slice(7);
115
+ const modelName = argument.startsWith(":") ? toCamelCase(argument.slice(1)) : "modelValue";
116
+ props.add(modelName);
117
+ emits.add(`update:${modelName}`);
118
+ continue;
119
+ }
120
+ if (key.startsWith("@") || key.startsWith("v-on:")) {
121
+ const eventName = key.startsWith("@") ? key.slice(1) : key.slice(5);
122
+ if (eventName) emits.add(eventName);
123
+ continue;
124
+ }
125
+ if (key.startsWith(":")) {
126
+ props.add(toCamelCase(key.slice(1)));
127
+ continue;
128
+ }
129
+ if (key.startsWith("v-bind:")) {
130
+ props.add(toCamelCase(key.slice(7)));
131
+ continue;
132
+ }
133
+ if (key.startsWith("v-")) continue;
134
+ if ([
135
+ "class",
136
+ "style",
137
+ "key",
138
+ "ref",
139
+ "slot",
140
+ "is"
141
+ ].includes(key)) continue;
142
+ if (key.startsWith("data-") || key.startsWith("aria-")) continue;
143
+ props.add(toCamelCase(key));
144
+ }
145
+ return {
146
+ props,
147
+ emits
148
+ };
149
+ }
150
+ function getTemplateComponentBindings(code) {
151
+ const templateMatch = /<template\b[^>]*>([\s\S]*?)<\/template>/.exec(code);
152
+ if (!templateMatch) return {
153
+ bindings: /* @__PURE__ */ new Set(),
154
+ usageByBinding: /* @__PURE__ */ new Map()
155
+ };
156
+ const templateContent = templateMatch[1] ?? "";
157
+ const bindings = /* @__PURE__ */ new Set();
158
+ const usageByBinding = /* @__PURE__ */ new Map();
159
+ const tagMatcher = /<\/?([A-Za-z][\w-]*)\b/g;
160
+ const openingTagMatcher = /<([A-Za-z][\w-]*)\b([^>]*)>/g;
161
+ for (const tagMatch of templateContent.matchAll(tagMatcher)) {
162
+ const tagName = tagMatch[1];
163
+ if (!tagName) continue;
164
+ const bindingName = getComponentBindingName(tagName);
165
+ if (bindingName) bindings.add(bindingName);
166
+ }
167
+ for (const openingTagMatch of templateContent.matchAll(openingTagMatcher)) {
168
+ const tagName = openingTagMatch[1];
169
+ const attributes = openingTagMatch[2] ?? "";
170
+ if (!tagName) continue;
171
+ const bindingName = getComponentBindingName(tagName);
172
+ if (!bindingName || !bindings.has(bindingName)) continue;
173
+ const existingUsage = usageByBinding.get(bindingName) ?? {
174
+ props: /* @__PURE__ */ new Set(),
175
+ emits: /* @__PURE__ */ new Set()
176
+ };
177
+ const extractedUsage = collectPropsAndEmitsFromAttributes(attributes);
178
+ for (const prop of extractedUsage.props) existingUsage.props.add(prop);
179
+ for (const emittedEvent of extractedUsage.emits) existingUsage.emits.add(emittedEvent);
180
+ usageByBinding.set(bindingName, existingUsage);
181
+ }
182
+ return {
183
+ bindings,
184
+ usageByBinding
185
+ };
186
+ }
187
+ function parseNamedSpecifiers(clause) {
188
+ const trimmedClause = clause.trim();
189
+ if (!trimmedClause.startsWith("{") || !trimmedClause.endsWith("}")) return null;
190
+ const content = trimmedClause.slice(1, -1).trim();
191
+ if (!content) return [];
192
+ if (/\btype\b/.test(content)) return null;
193
+ const entries = content.split(",").map((entry) => entry.trim()).filter(Boolean);
194
+ const specifiers = [];
195
+ for (const entry of entries) {
196
+ const aliasMatch = /^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/.exec(entry);
197
+ if (!aliasMatch) return null;
198
+ specifiers.push({
199
+ imported: aliasMatch[1],
200
+ local: aliasMatch[2] ?? aliasMatch[1]
201
+ });
202
+ }
203
+ return specifiers;
204
+ }
205
+ function splitImportClause(clause) {
206
+ let depth = 0;
207
+ for (let index = 0; index < clause.length; index += 1) {
208
+ const character = clause[index];
209
+ if (character === "{") {
210
+ depth += 1;
211
+ continue;
212
+ }
213
+ if (character === "}") {
214
+ depth = Math.max(0, depth - 1);
215
+ continue;
216
+ }
217
+ if (character === "," && depth === 0) return [clause.slice(0, index).trim(), clause.slice(index + 1).trim()];
218
+ }
219
+ return [clause.trim(), void 0];
220
+ }
221
+ function parseImportClause(clause) {
222
+ const [head, tail] = splitImportClause(clause);
223
+ const specs = [];
224
+ if (head.startsWith("{")) {
225
+ const named = parseNamedSpecifiers(head);
226
+ if (!named) return null;
227
+ for (const specifier of named) specs.push({
228
+ kind: "named",
229
+ imported: specifier.imported,
230
+ local: specifier.local
231
+ });
232
+ return specs;
233
+ }
234
+ if (head.startsWith("*")) {
235
+ const namespaceMatch = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(head);
236
+ if (!namespaceMatch || tail) return null;
237
+ specs.push({
238
+ kind: "namespace",
239
+ local: namespaceMatch[1]
240
+ });
241
+ return specs;
242
+ }
243
+ if (!/^[A-Za-z_$][\w$]*$/.test(head)) return null;
244
+ specs.push({
245
+ kind: "default",
246
+ local: head
247
+ });
248
+ if (!tail) return specs;
249
+ if (tail.startsWith("{")) {
250
+ const named = parseNamedSpecifiers(tail);
251
+ if (!named) return null;
252
+ for (const specifier of named) specs.push({
253
+ kind: "named",
254
+ imported: specifier.imported,
255
+ local: specifier.local
256
+ });
257
+ return specs;
258
+ }
259
+ const namespaceMatch = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(tail);
260
+ if (!namespaceMatch) return null;
261
+ specs.push({
262
+ kind: "namespace",
263
+ local: namespaceMatch[1]
264
+ });
265
+ return specs;
266
+ }
267
+ function stringifyImportClause(specifiers) {
268
+ const defaultSpecifier = specifiers.find((specifier) => specifier.kind === "default");
269
+ const namespaceSpecifier = specifiers.find((specifier) => specifier.kind === "namespace");
270
+ const namedSpecifiers = specifiers.filter((specifier) => specifier.kind === "named");
271
+ const clauseParts = [];
272
+ if (defaultSpecifier?.kind === "default") clauseParts.push(defaultSpecifier.local);
273
+ if (namespaceSpecifier?.kind === "namespace") clauseParts.push(`* as ${namespaceSpecifier.local}`);
274
+ if (namedSpecifiers.length) {
275
+ const namedClause = namedSpecifiers.map((specifier) => specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`).join(", ");
276
+ clauseParts.push(`{ ${namedClause} }`);
277
+ }
278
+ return clauseParts.join(", ");
279
+ }
280
+ function createPlaceholderComponentDeclarations(locals, usageByBinding) {
281
+ if (!locals.length) return "";
282
+ return locals.map((local) => {
283
+ const usage = usageByBinding.get(local);
284
+ const props = usage ? [...usage.props].sort() : [];
285
+ const emits = usage ? [...usage.emits].sort() : [];
286
+ return `const ${local} = { name: '${local}'${props.length ? `, props: ${JSON.stringify(props)}` : ""}${emits.length ? `, emits: ${JSON.stringify(emits)}` : ""}, render() { const normalizedAttrs = Object.fromEntries(Object.entries(this.$attrs).map(([key, value]) => [key.replace(/[A-Z]/g, (character) => '-' + character.toLowerCase()), value])); return h('${`${toKebabCase(local)}-stub`}', { ...normalizedAttrs, ...this.$props }) } }`;
287
+ }).join("\n");
288
+ }
289
+ function ensureVueHImport(script) {
290
+ if (/import\s*{[^}]*\bh\b[^}]*}\s*from\s*['"]vue['"]/.test(script)) return script;
291
+ return `import { h } from 'vue'\n${script}`;
292
+ }
293
+ function collectTopLevelImportStatements(script) {
294
+ const statements = [];
295
+ const lines = script.split("\n");
296
+ const lineOffsets = [];
297
+ let offset = 0;
298
+ for (const line of lines) {
299
+ lineOffsets.push(offset);
300
+ offset += line.length + 1;
301
+ }
302
+ let lineIndex = 0;
303
+ while (lineIndex < lines.length) {
304
+ const line = lines[lineIndex] ?? "";
305
+ const trimmedLine = line.trim();
306
+ if (!trimmedLine) {
307
+ lineIndex += 1;
308
+ continue;
309
+ }
310
+ if (!trimmedLine.startsWith("import")) break;
311
+ const startLine = lineIndex;
312
+ let statement = line;
313
+ lineIndex += 1;
314
+ while (lineIndex < lines.length) {
315
+ const normalized = statement.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "").trim();
316
+ if (/^import\s+type\s+.+\s+from\s+['"][^'"]+['"]\s*;?$/.test(normalized) || /^import\s+.+\s+from\s+['"][^'"]+['"]\s*;?$/.test(normalized) || /^import\s+['"][^'"]+['"]\s*;?$/.test(normalized)) break;
317
+ statement += `\n${lines[lineIndex] ?? ""}`;
318
+ lineIndex += 1;
319
+ }
320
+ const start = lineOffsets[startLine];
321
+ if (start === void 0) continue;
322
+ const end = start + statement.length;
323
+ statements.push({
324
+ statement,
325
+ start,
326
+ end
327
+ });
328
+ }
329
+ return statements;
330
+ }
331
+ function pruneTemplateOnlyImportsInScriptSetup(script, templateBindings, usageByBinding, keepBindings) {
332
+ if (!templateBindings.size) return script;
333
+ const importStatements = collectTopLevelImportStatements(script);
334
+ if (!importStatements.length) return script;
335
+ let scriptWithoutImports = script;
336
+ for (const importStatement of [...importStatements].reverse()) scriptWithoutImports = `${scriptWithoutImports.slice(0, importStatement.start)}${" ".repeat(importStatement.end - importStatement.start)}${scriptWithoutImports.slice(importStatement.end)}`;
337
+ const replacements = [];
338
+ for (const importStatement of importStatements) {
339
+ const normalizedStatement = importStatement.statement.trim();
340
+ if (normalizedStatement.startsWith("import type ")) continue;
341
+ if (/^import\s+['"][^'"]+['"]\s*;?$/.test(normalizedStatement)) continue;
342
+ const importClauseMatch = /^import\s+([\s\S]+?)\s+from\s+['"][^'"]+['"]\s*;?$/.exec(normalizedStatement);
343
+ if (!importClauseMatch) continue;
344
+ const importClause = importClauseMatch[1];
345
+ if (!importClause) continue;
346
+ const parsedClause = parseImportClause(importClause);
347
+ if (!parsedClause) continue;
348
+ const removableLocals = /* @__PURE__ */ new Set();
349
+ for (const specifier of parsedClause) {
350
+ const localName = specifier.local;
351
+ if (keepBindings.has(localName)) continue;
352
+ if (!templateBindings.has(localName)) continue;
353
+ if (new RegExp(`\\b${escapeForRegExp(localName)}\\b`).test(scriptWithoutImports)) continue;
354
+ removableLocals.add(localName);
355
+ }
356
+ if (!removableLocals.size) continue;
357
+ const keptSpecifiers = parsedClause.filter((specifier) => !removableLocals.has(specifier.local));
358
+ const placeholderDeclarations = createPlaceholderComponentDeclarations([...removableLocals], usageByBinding);
359
+ if (!keptSpecifiers.length) {
360
+ replacements.push({
361
+ start: importStatement.start,
362
+ end: importStatement.end,
363
+ value: placeholderDeclarations
364
+ });
365
+ continue;
366
+ }
367
+ const importSourceMatch = /from\s+(['"][^'"]+['"])/.exec(normalizedStatement);
368
+ if (!importSourceMatch) continue;
369
+ replacements.push({
370
+ start: importStatement.start,
371
+ end: importStatement.end,
372
+ value: `${`import ${stringifyImportClause(keptSpecifiers)} from ${importSourceMatch[1]}`}${placeholderDeclarations ? `\n${placeholderDeclarations}` : ""}`
373
+ });
374
+ }
375
+ if (!replacements.length) return script;
376
+ let transformedScript = script;
377
+ for (const replacement of replacements.sort((left, right) => right.start - left.start)) transformedScript = `${transformedScript.slice(0, replacement.start)}${replacement.value}${transformedScript.slice(replacement.end)}`;
378
+ return ensureVueHImport(transformedScript);
379
+ }
380
+ function transformFastMountVueSource(code, keepBindings) {
381
+ const scriptMatcher = /<script\b([^>]*)>([\s\S]*?)<\/script>/g;
382
+ let scriptMatch = null;
383
+ for (const currentMatch of code.matchAll(scriptMatcher)) if (currentMatch[1]?.includes("setup")) {
384
+ scriptMatch = currentMatch;
385
+ break;
386
+ }
387
+ if (!scriptMatch || !scriptMatch[2]) return code;
388
+ const { bindings: templateBindings, usageByBinding } = getTemplateComponentBindings(code);
389
+ if (!templateBindings.size) return code;
390
+ const originalScript = scriptMatch[2];
391
+ const transformedScript = pruneTemplateOnlyImportsInScriptSetup(originalScript, templateBindings, usageByBinding, keepBindings);
392
+ if (transformedScript === originalScript) return code;
393
+ const fullMatch = scriptMatch[0];
394
+ const scriptContentStart = (scriptMatch.index ?? 0) + fullMatch.indexOf(originalScript);
395
+ const scriptContentEnd = scriptContentStart + originalScript.length;
396
+ return `${code.slice(0, scriptContentStart)}${transformedScript}${code.slice(scriptContentEnd)}`;
397
+ }
398
+ function shouldTransformVueFastMountId(id) {
399
+ if (!id.includes(".vue") || !id.includes("?")) return false;
400
+ const query = id.slice(id.indexOf("?") + 1);
401
+ if (!new RegExp(`(?:^|&)${FAST_MOUNT_QUERY_KEY}=${FAST_MOUNT_QUERY_VALUE}(?:&|$)`).test(query)) return false;
402
+ return !/(?:^|&)type=/.test(query);
403
+ }
404
+ function getKeepBindingsFromId(id) {
405
+ const queryStart = id.indexOf("?");
406
+ if (queryStart === -1) return /* @__PURE__ */ new Set();
407
+ const keep = new URLSearchParams(id.slice(queryStart + 1)).get(FAST_MOUNT_KEEP_QUERY_KEY);
408
+ if (!keep) return /* @__PURE__ */ new Set();
409
+ return new Set(keep.split(",").map((binding) => binding.trim()).filter(Boolean));
410
+ }
411
+
412
+ //#endregion
413
+ //#region src/plugin.ts
414
+ function vueFastMount() {
415
+ return {
416
+ name: "vue-fast-mount",
417
+ enforce: "pre",
418
+ transform(code, id) {
419
+ if (normalizePath(id).includes("/node_modules/")) return null;
420
+ if (shouldTransformVueFastMountId(id)) {
421
+ const transformedCode = transformFastMountVueSource(code, getKeepBindingsFromId(id));
422
+ return transformedCode === code ? null : transformedCode;
423
+ }
424
+ const transformedCode = rewriteFastMountCallsites(code);
425
+ return transformedCode === code ? null : transformedCode;
426
+ }
427
+ };
428
+ }
429
+
430
+ //#endregion
431
+ export { vueFastMount };
432
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin-helpers.ts","../src/plugin.ts"],"sourcesContent":["const FAST_MOUNT_QUERY_KEY = '__vfm'\nconst FAST_MOUNT_QUERY_VALUE = '1'\nconst FAST_MOUNT_KEEP_QUERY_KEY = '__vfm_keep'\n\nfunction isFastMountRuntimeImportSource(source: string): boolean {\n return source === 'vue-fast-mount'\n}\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction appendFastMountQuery(specifier: string, keepBindings: string[] = []): string {\n if (!specifier.includes('.vue')) {\n return specifier\n }\n\n const hashIndex = specifier.indexOf('#')\n const hash = hashIndex === -1 ? '' : specifier.slice(hashIndex)\n const base = hashIndex === -1 ? specifier : specifier.slice(0, hashIndex)\n\n if (new RegExp(`(?:\\\\?|&)${FAST_MOUNT_QUERY_KEY}=`).test(base)) {\n return specifier\n }\n\n const separator = base.includes('?') ? '&' : '?'\n const keepQuery = keepBindings.length\n ? `&${FAST_MOUNT_KEEP_QUERY_KEY}=${encodeURIComponent(keepBindings.join(','))}`\n : ''\n\n return `${base}${separator}${FAST_MOUNT_QUERY_KEY}=${FAST_MOUNT_QUERY_VALUE}${keepQuery}${hash}`\n}\n\nfunction findMatchingBracket(value: string, start: number, open: string, close: string): number {\n let depth = 0\n\n for (let index = start; index < value.length; index += 1) {\n const character = value[index]\n\n if (character === open) {\n depth += 1\n continue\n }\n\n if (character === close) {\n depth -= 1\n\n if (depth === 0) {\n return index\n }\n }\n }\n\n return -1\n}\n\nfunction extractExplicitlyUnstubbedComponents(callExpression: string): string[] {\n const stubsMatch = /\\bstubs\\s*:/.exec(callExpression)\n\n if (!stubsMatch) {\n return []\n }\n\n const stubsObjectStart = callExpression.indexOf('{', stubsMatch.index)\n\n if (stubsObjectStart === -1) {\n return []\n }\n\n const stubsObjectEnd = findMatchingBracket(callExpression, stubsObjectStart, '{', '}')\n\n if (stubsObjectEnd === -1) {\n return []\n }\n\n const stubsObject = callExpression.slice(stubsObjectStart + 1, stubsObjectEnd)\n const keepBindings = new Set<string>()\n\n const literalFalseMatcher =\n /(?:^|,)\\s*(?:['\"]([A-Za-z_$][\\w$-]*)['\"]|([A-Za-z_$][\\w$]*))\\s*:\\s*false\\b/g\n\n for (const match of stubsObject.matchAll(literalFalseMatcher)) {\n const componentName = match[1] ?? match[2]\n\n if (componentName) {\n keepBindings.add(componentName)\n }\n }\n\n return [...keepBindings]\n}\n\nfunction getCallExpression(code: string, callStart: number): string | null {\n const openParenthesis = code.indexOf('(', callStart)\n\n if (openParenthesis === -1) {\n return null\n }\n\n const closeParenthesis = findMatchingBracket(code, openParenthesis, '(', ')')\n\n if (closeParenthesis === -1) {\n return null\n }\n\n return code.slice(callStart, closeParenthesis + 1)\n}\n\nfunction parseFastMountAliases(code: string): string[] {\n const aliases = new Set<string>()\n const importMatcher = /import\\s*{([^}]*)}\\s*from\\s*['\"]([^'\"]+)['\"]/g\n\n for (const match of code.matchAll(importMatcher)) {\n const source = match[2]\n\n if (!source || !isFastMountRuntimeImportSource(source)) {\n continue\n }\n\n const specifiers = match[1]?.split(',') ?? []\n\n for (const specifier of specifiers) {\n const trimmedSpecifier = specifier.trim()\n\n if (!trimmedSpecifier) {\n continue\n }\n\n const aliasMatch = /^fastMount(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?$/.exec(trimmedSpecifier)\n\n if (!aliasMatch) {\n continue\n }\n\n aliases.add(aliasMatch[1] ?? 'fastMount')\n }\n }\n\n return [...aliases]\n}\n\nexport function rewriteFastMountCallsites(code: string): string {\n const aliases = parseFastMountAliases(code)\n\n if (!aliases.length) {\n return code\n }\n\n let transformedCode = code\n\n for (const alias of aliases) {\n const escapedAlias = escapeForRegExp(alias)\n const pattern = new RegExp(\n `(^|[^\\\\w$.])(${escapedAlias})\\\\s*\\\\(\\\\s*import\\\\s*\\\\(\\\\s*([\"'])([^\"']+)\\\\3\\\\s*\\\\)`,\n 'gm',\n )\n\n transformedCode = transformedCode.replace(\n pattern,\n (\n match,\n prefix: string,\n name: string,\n quote: string,\n specifier: string,\n offset: number,\n fullCode: string,\n ) => {\n const aliasStart = offset + prefix.length\n const callExpression = getCallExpression(fullCode, aliasStart)\n const keepBindings = callExpression\n ? extractExplicitlyUnstubbedComponents(callExpression)\n : []\n const rewrittenSpecifier = appendFastMountQuery(specifier, keepBindings)\n\n if (rewrittenSpecifier === specifier) {\n return match\n }\n\n return `${prefix}${name}(import(${quote}${rewrittenSpecifier}${quote})`\n },\n )\n }\n\n return transformedCode\n}\n\nfunction toPascalCase(value: string): string {\n return value\n .split('-')\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n\nfunction toCamelCase(value: string): string {\n return value.replace(/-([a-zA-Z])/g, (_, character: string) => character.toUpperCase())\n}\n\nfunction toKebabCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/_/g, '-')\n .toLowerCase()\n}\n\ntype ComponentTemplateUsage = {\n props: Set<string>\n emits: Set<string>\n}\n\ntype TemplateComponentUsage = {\n bindings: Set<string>\n usageByBinding: Map<string, ComponentTemplateUsage>\n}\n\nfunction getComponentBindingName(tagName: string): string {\n const firstCharacter = tagName.charAt(0)\n\n if (firstCharacter && firstCharacter === firstCharacter.toUpperCase()) {\n return tagName\n }\n\n if (tagName.includes('-')) {\n return toPascalCase(tagName)\n }\n\n return ''\n}\n\nfunction collectPropsAndEmitsFromAttributes(attributes: string): ComponentTemplateUsage {\n const props = new Set<string>()\n const emits = new Set<string>()\n\n const attributeMatcher =\n /(?:@|:)[A-Za-z_$][\\w$:-]*|v-bind:[A-Za-z_$][\\w$-]*|v-on:[A-Za-z_$][\\w$:-]*|v-model(?::[A-Za-z_$][\\w$-]*)?|[A-Za-z][\\w-]*(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+))?/g\n\n for (const match of attributes.matchAll(attributeMatcher)) {\n const token = match[0]?.trim()\n\n if (!token) {\n continue\n }\n\n const key = token.split('=')[0]?.trim() ?? ''\n\n if (!key) {\n continue\n }\n\n if (key.startsWith('v-model')) {\n const argument = key.slice('v-model'.length)\n const modelName = argument.startsWith(':') ? toCamelCase(argument.slice(1)) : 'modelValue'\n props.add(modelName)\n emits.add(`update:${modelName}`)\n continue\n }\n\n if (key.startsWith('@') || key.startsWith('v-on:')) {\n const eventName = key.startsWith('@') ? key.slice(1) : key.slice('v-on:'.length)\n\n if (eventName) {\n emits.add(eventName)\n }\n continue\n }\n\n if (key.startsWith(':')) {\n props.add(toCamelCase(key.slice(1)))\n continue\n }\n\n if (key.startsWith('v-bind:')) {\n props.add(toCamelCase(key.slice('v-bind:'.length)))\n continue\n }\n\n if (key.startsWith('v-')) {\n continue\n }\n\n if (['class', 'style', 'key', 'ref', 'slot', 'is'].includes(key)) {\n continue\n }\n\n if (key.startsWith('data-') || key.startsWith('aria-')) {\n continue\n }\n\n props.add(toCamelCase(key))\n }\n\n return { props, emits }\n}\n\nfunction getTemplateComponentBindings(code: string): TemplateComponentUsage {\n const templateMatch = /<template\\b[^>]*>([\\s\\S]*?)<\\/template>/.exec(code)\n\n if (!templateMatch) {\n return {\n bindings: new Set<string>(),\n usageByBinding: new Map<string, ComponentTemplateUsage>(),\n }\n }\n\n const templateContent = templateMatch[1] ?? ''\n\n const bindings = new Set<string>()\n const usageByBinding = new Map<string, ComponentTemplateUsage>()\n const tagMatcher = /<\\/?([A-Za-z][\\w-]*)\\b/g\n const openingTagMatcher = /<([A-Za-z][\\w-]*)\\b([^>]*)>/g\n\n for (const tagMatch of templateContent.matchAll(tagMatcher)) {\n const tagName = tagMatch[1]\n\n if (!tagName) {\n continue\n }\n\n const bindingName = getComponentBindingName(tagName)\n\n if (bindingName) {\n bindings.add(bindingName)\n }\n }\n\n for (const openingTagMatch of templateContent.matchAll(openingTagMatcher)) {\n const tagName = openingTagMatch[1]\n const attributes = openingTagMatch[2] ?? ''\n\n if (!tagName) {\n continue\n }\n\n const bindingName = getComponentBindingName(tagName)\n\n if (!bindingName || !bindings.has(bindingName)) {\n continue\n }\n\n const existingUsage = usageByBinding.get(bindingName) ?? {\n props: new Set<string>(),\n emits: new Set<string>(),\n }\n const extractedUsage = collectPropsAndEmitsFromAttributes(attributes)\n\n for (const prop of extractedUsage.props) {\n existingUsage.props.add(prop)\n }\n\n for (const emittedEvent of extractedUsage.emits) {\n existingUsage.emits.add(emittedEvent)\n }\n\n usageByBinding.set(bindingName, existingUsage)\n }\n\n return { bindings, usageByBinding }\n}\n\ntype ImportClauseSpec =\n | { kind: 'default'; local: string }\n | { kind: 'namespace'; local: string }\n | { kind: 'named'; imported: string; local: string }\n\nfunction parseNamedSpecifiers(clause: string): Array<{ imported: string; local: string }> | null {\n const trimmedClause = clause.trim()\n\n if (!trimmedClause.startsWith('{') || !trimmedClause.endsWith('}')) {\n return null\n }\n\n const content = trimmedClause.slice(1, -1).trim()\n\n if (!content) {\n return []\n }\n\n if (/\\btype\\b/.test(content)) {\n return null\n }\n\n const entries = content\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n const specifiers: Array<{ imported: string; local: string }> = []\n\n for (const entry of entries) {\n const aliasMatch = /^([A-Za-z_$][\\w$]*)(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?$/.exec(entry)\n\n if (!aliasMatch) {\n return null\n }\n\n specifiers.push({\n imported: aliasMatch[1]!,\n local: aliasMatch[2] ?? aliasMatch[1]!,\n })\n }\n\n return specifiers\n}\n\nfunction splitImportClause(clause: string): [string, string | undefined] {\n let depth = 0\n\n for (let index = 0; index < clause.length; index += 1) {\n const character = clause[index]\n\n if (character === '{') {\n depth += 1\n continue\n }\n\n if (character === '}') {\n depth = Math.max(0, depth - 1)\n continue\n }\n\n if (character === ',' && depth === 0) {\n return [clause.slice(0, index).trim(), clause.slice(index + 1).trim()]\n }\n }\n\n return [clause.trim(), undefined]\n}\n\nfunction parseImportClause(clause: string): ImportClauseSpec[] | null {\n const [head, tail] = splitImportClause(clause)\n const specs: ImportClauseSpec[] = []\n\n if (head.startsWith('{')) {\n const named = parseNamedSpecifiers(head)\n\n if (!named) {\n return null\n }\n\n for (const specifier of named) {\n specs.push({ kind: 'named', imported: specifier.imported, local: specifier.local })\n }\n\n return specs\n }\n\n if (head.startsWith('*')) {\n const namespaceMatch = /^\\*\\s+as\\s+([A-Za-z_$][\\w$]*)$/.exec(head)\n\n if (!namespaceMatch || tail) {\n return null\n }\n\n specs.push({ kind: 'namespace', local: namespaceMatch[1]! })\n return specs\n }\n\n if (!/^[A-Za-z_$][\\w$]*$/.test(head)) {\n return null\n }\n\n specs.push({ kind: 'default', local: head })\n\n if (!tail) {\n return specs\n }\n\n if (tail.startsWith('{')) {\n const named = parseNamedSpecifiers(tail)\n\n if (!named) {\n return null\n }\n\n for (const specifier of named) {\n specs.push({ kind: 'named', imported: specifier.imported, local: specifier.local })\n }\n\n return specs\n }\n\n const namespaceMatch = /^\\*\\s+as\\s+([A-Za-z_$][\\w$]*)$/.exec(tail)\n\n if (!namespaceMatch) {\n return null\n }\n\n specs.push({ kind: 'namespace', local: namespaceMatch[1]! })\n return specs\n}\n\nfunction stringifyImportClause(specifiers: ImportClauseSpec[]): string {\n const defaultSpecifier = specifiers.find((specifier) => specifier.kind === 'default')\n const namespaceSpecifier = specifiers.find((specifier) => specifier.kind === 'namespace')\n const namedSpecifiers = specifiers.filter(\n (specifier): specifier is Extract<ImportClauseSpec, { kind: 'named' }> =>\n specifier.kind === 'named',\n )\n const clauseParts: string[] = []\n\n if (defaultSpecifier?.kind === 'default') {\n clauseParts.push(defaultSpecifier.local)\n }\n\n if (namespaceSpecifier?.kind === 'namespace') {\n clauseParts.push(`* as ${namespaceSpecifier.local}`)\n }\n\n if (namedSpecifiers.length) {\n const namedClause = namedSpecifiers\n .map((specifier) =>\n specifier.imported === specifier.local\n ? specifier.imported\n : `${specifier.imported} as ${specifier.local}`,\n )\n .join(', ')\n\n clauseParts.push(`{ ${namedClause} }`)\n }\n\n return clauseParts.join(', ')\n}\n\ntype ImportStatement = {\n statement: string\n start: number\n end: number\n}\n\nfunction createPlaceholderComponentDeclarations(\n locals: string[],\n usageByBinding: Map<string, ComponentTemplateUsage>,\n): string {\n if (!locals.length) {\n return ''\n }\n\n return locals\n .map((local) => {\n const usage = usageByBinding.get(local)\n const props = usage ? [...usage.props].sort() : []\n const emits = usage ? [...usage.emits].sort() : []\n const propsPart = props.length ? `, props: ${JSON.stringify(props)}` : ''\n const emitsPart = emits.length ? `, emits: ${JSON.stringify(emits)}` : ''\n const stubTag = `${toKebabCase(local)}-stub`\n\n return `const ${local} = { name: '${local}'${propsPart}${emitsPart}, render() { const normalizedAttrs = Object.fromEntries(Object.entries(this.$attrs).map(([key, value]) => [key.replace(/[A-Z]/g, (character) => '-' + character.toLowerCase()), value])); return h('${stubTag}', { ...normalizedAttrs, ...this.$props }) } }`\n })\n .join('\\n')\n}\n\nfunction ensureVueHImport(script: string): string {\n if (/import\\s*{[^}]*\\bh\\b[^}]*}\\s*from\\s*['\"]vue['\"]/.test(script)) {\n return script\n }\n\n return `import { h } from 'vue'\\n${script}`\n}\n\nfunction collectTopLevelImportStatements(script: string): ImportStatement[] {\n const statements: ImportStatement[] = []\n const lines = script.split('\\n')\n const lineOffsets: number[] = []\n let offset = 0\n\n for (const line of lines) {\n lineOffsets.push(offset)\n offset += line.length + 1\n }\n\n let lineIndex = 0\n\n while (lineIndex < lines.length) {\n const line = lines[lineIndex] ?? ''\n const trimmedLine = line.trim()\n\n if (!trimmedLine) {\n lineIndex += 1\n continue\n }\n\n if (!trimmedLine.startsWith('import')) {\n break\n }\n\n const startLine = lineIndex\n let statement = line\n lineIndex += 1\n\n while (lineIndex < lines.length) {\n const normalized = statement\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/[^\\n]*/g, '')\n .trim()\n\n if (\n /^import\\s+type\\s+.+\\s+from\\s+['\"][^'\"]+['\"]\\s*;?$/.test(normalized) ||\n /^import\\s+.+\\s+from\\s+['\"][^'\"]+['\"]\\s*;?$/.test(normalized) ||\n /^import\\s+['\"][^'\"]+['\"]\\s*;?$/.test(normalized)\n ) {\n break\n }\n\n statement += `\\n${lines[lineIndex] ?? ''}`\n lineIndex += 1\n }\n\n const start = lineOffsets[startLine]\n\n if (start === undefined) {\n continue\n }\n\n const end = start + statement.length\n statements.push({ statement, start, end })\n }\n\n return statements\n}\n\nfunction pruneTemplateOnlyImportsInScriptSetup(\n script: string,\n templateBindings: Set<string>,\n usageByBinding: Map<string, ComponentTemplateUsage>,\n keepBindings: Set<string>,\n): string {\n if (!templateBindings.size) {\n return script\n }\n\n const importStatements = collectTopLevelImportStatements(script)\n\n if (!importStatements.length) {\n return script\n }\n\n let scriptWithoutImports = script\n\n for (const importStatement of [...importStatements].reverse()) {\n scriptWithoutImports = `${scriptWithoutImports.slice(0, importStatement.start)}${' '.repeat(importStatement.end - importStatement.start)}${scriptWithoutImports.slice(importStatement.end)}`\n }\n\n const replacements: Array<{ start: number; end: number; value: string }> = []\n\n for (const importStatement of importStatements) {\n const normalizedStatement = importStatement.statement.trim()\n\n if (normalizedStatement.startsWith('import type ')) {\n continue\n }\n\n if (/^import\\s+['\"][^'\"]+['\"]\\s*;?$/.test(normalizedStatement)) {\n continue\n }\n\n const importClauseMatch = /^import\\s+([\\s\\S]+?)\\s+from\\s+['\"][^'\"]+['\"]\\s*;?$/.exec(\n normalizedStatement,\n )\n\n if (!importClauseMatch) {\n continue\n }\n\n const importClause = importClauseMatch[1]\n\n if (!importClause) {\n continue\n }\n\n const parsedClause = parseImportClause(importClause)\n\n if (!parsedClause) {\n continue\n }\n\n const removableLocals = new Set<string>()\n\n for (const specifier of parsedClause) {\n const localName = specifier.local\n\n if (keepBindings.has(localName)) {\n continue\n }\n\n if (!templateBindings.has(localName)) {\n continue\n }\n\n if (new RegExp(`\\\\b${escapeForRegExp(localName)}\\\\b`).test(scriptWithoutImports)) {\n continue\n }\n\n removableLocals.add(localName)\n }\n\n if (!removableLocals.size) {\n continue\n }\n\n const keptSpecifiers = parsedClause.filter((specifier) => !removableLocals.has(specifier.local))\n const placeholderDeclarations = createPlaceholderComponentDeclarations(\n [...removableLocals],\n usageByBinding,\n )\n\n if (!keptSpecifiers.length) {\n replacements.push({\n start: importStatement.start,\n end: importStatement.end,\n value: placeholderDeclarations,\n })\n continue\n }\n\n const importSourceMatch = /from\\s+(['\"][^'\"]+['\"])/.exec(normalizedStatement)\n\n if (!importSourceMatch) {\n continue\n }\n\n replacements.push({\n start: importStatement.start,\n end: importStatement.end,\n value: `${`import ${stringifyImportClause(keptSpecifiers)} from ${importSourceMatch[1]}`}${placeholderDeclarations ? `\\n${placeholderDeclarations}` : ''}`,\n })\n }\n\n if (!replacements.length) {\n return script\n }\n\n let transformedScript = script\n\n for (const replacement of replacements.sort((left, right) => right.start - left.start)) {\n transformedScript = `${transformedScript.slice(0, replacement.start)}${replacement.value}${transformedScript.slice(replacement.end)}`\n }\n\n return ensureVueHImport(transformedScript)\n}\n\nexport function transformFastMountVueSource(code: string, keepBindings: Set<string>): string {\n const scriptMatcher = /<script\\b([^>]*)>([\\s\\S]*?)<\\/script>/g\n let scriptMatch: RegExpExecArray | null = null\n\n for (const currentMatch of code.matchAll(scriptMatcher)) {\n if (currentMatch[1]?.includes('setup')) {\n scriptMatch = currentMatch\n break\n }\n }\n\n if (!scriptMatch || !scriptMatch[2]) {\n return code\n }\n\n const { bindings: templateBindings, usageByBinding } = getTemplateComponentBindings(code)\n\n if (!templateBindings.size) {\n return code\n }\n\n const originalScript = scriptMatch[2]\n const transformedScript = pruneTemplateOnlyImportsInScriptSetup(\n originalScript,\n templateBindings,\n usageByBinding,\n keepBindings,\n )\n\n if (transformedScript === originalScript) {\n return code\n }\n\n const fullMatch = scriptMatch[0]\n const scriptContentStart = (scriptMatch.index ?? 0) + fullMatch.indexOf(originalScript)\n const scriptContentEnd = scriptContentStart + originalScript.length\n\n return `${code.slice(0, scriptContentStart)}${transformedScript}${code.slice(scriptContentEnd)}`\n}\n\nexport function shouldTransformVueFastMountId(id: string): boolean {\n if (!id.includes('.vue') || !id.includes('?')) {\n return false\n }\n\n const query = id.slice(id.indexOf('?') + 1)\n\n if (!new RegExp(`(?:^|&)${FAST_MOUNT_QUERY_KEY}=${FAST_MOUNT_QUERY_VALUE}(?:&|$)`).test(query)) {\n return false\n }\n\n return !/(?:^|&)type=/.test(query)\n}\n\nexport function getKeepBindingsFromId(id: string): Set<string> {\n const queryStart = id.indexOf('?')\n\n if (queryStart === -1) {\n return new Set<string>()\n }\n\n const params = new URLSearchParams(id.slice(queryStart + 1))\n const keep = params.get(FAST_MOUNT_KEEP_QUERY_KEY)\n\n if (!keep) {\n return new Set<string>()\n }\n\n return new Set(\n keep\n .split(',')\n .map((binding) => binding.trim())\n .filter(Boolean),\n )\n}\n\nexport const internalPluginHelpers = {\n collectTopLevelImportStatements,\n createPlaceholderComponentDeclarations,\n ensureVueHImport,\n parseImportClause,\n parseNamedSpecifiers,\n pruneTemplateOnlyImportsInScriptSetup,\n stringifyImportClause,\n}\n","import { normalizePath, type Plugin } from 'vite'\n\nimport {\n getKeepBindingsFromId,\n rewriteFastMountCallsites,\n shouldTransformVueFastMountId,\n transformFastMountVueSource,\n} from './plugin-helpers'\n\nexport function vueFastMount(): Plugin {\n return {\n name: 'vue-fast-mount',\n enforce: 'pre',\n transform(code, id) {\n if (normalizePath(id).includes('/node_modules/')) {\n return null\n }\n\n if (shouldTransformVueFastMountId(id)) {\n const transformedCode = transformFastMountVueSource(code, getKeepBindingsFromId(id))\n return transformedCode === code ? null : transformedCode\n }\n\n const transformedCode = rewriteFastMountCallsites(code)\n return transformedCode === code ? null : transformedCode\n },\n }\n}\n"],"mappings":";;;AAAA,MAAM,uBAAuB;AAC7B,MAAM,yBAAyB;AAC/B,MAAM,4BAA4B;AAElC,SAAS,+BAA+B,QAAyB;AAC/D,QAAO,WAAW;;AAGpB,SAAS,gBAAgB,OAAuB;AAC9C,QAAO,MAAM,QAAQ,uBAAuB,OAAO;;AAGrD,SAAS,qBAAqB,WAAmB,eAAyB,EAAE,EAAU;AACpF,KAAI,CAAC,UAAU,SAAS,OAAO,CAC7B,QAAO;CAGT,MAAM,YAAY,UAAU,QAAQ,IAAI;CACxC,MAAM,OAAO,cAAc,KAAK,KAAK,UAAU,MAAM,UAAU;CAC/D,MAAM,OAAO,cAAc,KAAK,YAAY,UAAU,MAAM,GAAG,UAAU;AAEzE,KAAI,IAAI,OAAO,YAAY,qBAAqB,GAAG,CAAC,KAAK,KAAK,CAC5D,QAAO;AAQT,QAAO,GAAG,OALQ,KAAK,SAAS,IAAI,GAAG,MAAM,MAKhB,qBAAqB,GAAG,yBAJnC,aAAa,SAC3B,IAAI,0BAA0B,GAAG,mBAAmB,aAAa,KAAK,IAAI,CAAC,KAC3E,KAEsF;;AAG5F,SAAS,oBAAoB,OAAe,OAAe,MAAc,OAAuB;CAC9F,IAAI,QAAQ;AAEZ,MAAK,IAAI,QAAQ,OAAO,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACxD,MAAM,YAAY,MAAM;AAExB,MAAI,cAAc,MAAM;AACtB,YAAS;AACT;;AAGF,MAAI,cAAc,OAAO;AACvB,YAAS;AAET,OAAI,UAAU,EACZ,QAAO;;;AAKb,QAAO;;AAGT,SAAS,qCAAqC,gBAAkC;CAC9E,MAAM,aAAa,cAAc,KAAK,eAAe;AAErD,KAAI,CAAC,WACH,QAAO,EAAE;CAGX,MAAM,mBAAmB,eAAe,QAAQ,KAAK,WAAW,MAAM;AAEtE,KAAI,qBAAqB,GACvB,QAAO,EAAE;CAGX,MAAM,iBAAiB,oBAAoB,gBAAgB,kBAAkB,KAAK,IAAI;AAEtF,KAAI,mBAAmB,GACrB,QAAO,EAAE;CAGX,MAAM,cAAc,eAAe,MAAM,mBAAmB,GAAG,eAAe;CAC9E,MAAM,+BAAe,IAAI,KAAa;AAKtC,MAAK,MAAM,SAAS,YAAY,SAF9B,8EAE2D,EAAE;EAC7D,MAAM,gBAAgB,MAAM,MAAM,MAAM;AAExC,MAAI,cACF,cAAa,IAAI,cAAc;;AAInC,QAAO,CAAC,GAAG,aAAa;;AAG1B,SAAS,kBAAkB,MAAc,WAAkC;CACzE,MAAM,kBAAkB,KAAK,QAAQ,KAAK,UAAU;AAEpD,KAAI,oBAAoB,GACtB,QAAO;CAGT,MAAM,mBAAmB,oBAAoB,MAAM,iBAAiB,KAAK,IAAI;AAE7E,KAAI,qBAAqB,GACvB,QAAO;AAGT,QAAO,KAAK,MAAM,WAAW,mBAAmB,EAAE;;AAGpD,SAAS,sBAAsB,MAAwB;CACrD,MAAM,0BAAU,IAAI,KAAa;AAGjC,MAAK,MAAM,SAAS,KAAK,SAFH,gDAE0B,EAAE;EAChD,MAAM,SAAS,MAAM;AAErB,MAAI,CAAC,UAAU,CAAC,+BAA+B,OAAO,CACpD;EAGF,MAAM,aAAa,MAAM,IAAI,MAAM,IAAI,IAAI,EAAE;AAE7C,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,mBAAmB,UAAU,MAAM;AAEzC,OAAI,CAAC,iBACH;GAGF,MAAM,aAAa,6CAA6C,KAAK,iBAAiB;AAEtF,OAAI,CAAC,WACH;AAGF,WAAQ,IAAI,WAAW,MAAM,YAAY;;;AAI7C,QAAO,CAAC,GAAG,QAAQ;;AAGrB,SAAgB,0BAA0B,MAAsB;CAC9D,MAAM,UAAU,sBAAsB,KAAK;AAE3C,KAAI,CAAC,QAAQ,OACX,QAAO;CAGT,IAAI,kBAAkB;AAEtB,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,eAAe,gBAAgB,MAAM;EAC3C,MAAM,UAAU,IAAI,OAClB,gBAAgB,aAAa,wDAC7B,KACD;AAED,oBAAkB,gBAAgB,QAChC,UAEE,OACA,QACA,MACA,OACA,WACA,QACA,aACG;GAEH,MAAM,iBAAiB,kBAAkB,UADtB,SAAS,OAAO,OAC2B;GAI9D,MAAM,qBAAqB,qBAAqB,WAH3B,iBACjB,qCAAqC,eAAe,GACpD,EAAE,CACkE;AAExE,OAAI,uBAAuB,UACzB,QAAO;AAGT,UAAO,GAAG,SAAS,KAAK,UAAU,QAAQ,qBAAqB,MAAM;IAExE;;AAGH,QAAO;;AAGT,SAAS,aAAa,OAAuB;AAC3C,QAAO,MACJ,MAAM,IAAI,CACV,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,SAAS,YAAY,OAAuB;AAC1C,QAAO,MAAM,QAAQ,iBAAiB,GAAG,cAAsB,UAAU,aAAa,CAAC;;AAGzF,SAAS,YAAY,OAAuB;AAC1C,QAAO,MACJ,QAAQ,sBAAsB,QAAQ,CACtC,QAAQ,MAAM,IAAI,CAClB,aAAa;;AAalB,SAAS,wBAAwB,SAAyB;CACxD,MAAM,iBAAiB,QAAQ,OAAO,EAAE;AAExC,KAAI,kBAAkB,mBAAmB,eAAe,aAAa,CACnE,QAAO;AAGT,KAAI,QAAQ,SAAS,IAAI,CACvB,QAAO,aAAa,QAAQ;AAG9B,QAAO;;AAGT,SAAS,mCAAmC,YAA4C;CACtF,MAAM,wBAAQ,IAAI,KAAa;CAC/B,MAAM,wBAAQ,IAAI,KAAa;AAK/B,MAAK,MAAM,SAAS,WAAW,SAF7B,mKAEuD,EAAE;EACzD,MAAM,QAAQ,MAAM,IAAI,MAAM;AAE9B,MAAI,CAAC,MACH;EAGF,MAAM,MAAM,MAAM,MAAM,IAAI,CAAC,IAAI,MAAM,IAAI;AAE3C,MAAI,CAAC,IACH;AAGF,MAAI,IAAI,WAAW,UAAU,EAAE;GAC7B,MAAM,WAAW,IAAI,MAAM,EAAiB;GAC5C,MAAM,YAAY,SAAS,WAAW,IAAI,GAAG,YAAY,SAAS,MAAM,EAAE,CAAC,GAAG;AAC9E,SAAM,IAAI,UAAU;AACpB,SAAM,IAAI,UAAU,YAAY;AAChC;;AAGF,MAAI,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,QAAQ,EAAE;GAClD,MAAM,YAAY,IAAI,WAAW,IAAI,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAe;AAEhF,OAAI,UACF,OAAM,IAAI,UAAU;AAEtB;;AAGF,MAAI,IAAI,WAAW,IAAI,EAAE;AACvB,SAAM,IAAI,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;AACpC;;AAGF,MAAI,IAAI,WAAW,UAAU,EAAE;AAC7B,SAAM,IAAI,YAAY,IAAI,MAAM,EAAiB,CAAC,CAAC;AACnD;;AAGF,MAAI,IAAI,WAAW,KAAK,CACtB;AAGF,MAAI;GAAC;GAAS;GAAS;GAAO;GAAO;GAAQ;GAAK,CAAC,SAAS,IAAI,CAC9D;AAGF,MAAI,IAAI,WAAW,QAAQ,IAAI,IAAI,WAAW,QAAQ,CACpD;AAGF,QAAM,IAAI,YAAY,IAAI,CAAC;;AAG7B,QAAO;EAAE;EAAO;EAAO;;AAGzB,SAAS,6BAA6B,MAAsC;CAC1E,MAAM,gBAAgB,0CAA0C,KAAK,KAAK;AAE1E,KAAI,CAAC,cACH,QAAO;EACL,0BAAU,IAAI,KAAa;EAC3B,gCAAgB,IAAI,KAAqC;EAC1D;CAGH,MAAM,kBAAkB,cAAc,MAAM;CAE5C,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,iCAAiB,IAAI,KAAqC;CAChE,MAAM,aAAa;CACnB,MAAM,oBAAoB;AAE1B,MAAK,MAAM,YAAY,gBAAgB,SAAS,WAAW,EAAE;EAC3D,MAAM,UAAU,SAAS;AAEzB,MAAI,CAAC,QACH;EAGF,MAAM,cAAc,wBAAwB,QAAQ;AAEpD,MAAI,YACF,UAAS,IAAI,YAAY;;AAI7B,MAAK,MAAM,mBAAmB,gBAAgB,SAAS,kBAAkB,EAAE;EACzE,MAAM,UAAU,gBAAgB;EAChC,MAAM,aAAa,gBAAgB,MAAM;AAEzC,MAAI,CAAC,QACH;EAGF,MAAM,cAAc,wBAAwB,QAAQ;AAEpD,MAAI,CAAC,eAAe,CAAC,SAAS,IAAI,YAAY,CAC5C;EAGF,MAAM,gBAAgB,eAAe,IAAI,YAAY,IAAI;GACvD,uBAAO,IAAI,KAAa;GACxB,uBAAO,IAAI,KAAa;GACzB;EACD,MAAM,iBAAiB,mCAAmC,WAAW;AAErE,OAAK,MAAM,QAAQ,eAAe,MAChC,eAAc,MAAM,IAAI,KAAK;AAG/B,OAAK,MAAM,gBAAgB,eAAe,MACxC,eAAc,MAAM,IAAI,aAAa;AAGvC,iBAAe,IAAI,aAAa,cAAc;;AAGhD,QAAO;EAAE;EAAU;EAAgB;;AAQrC,SAAS,qBAAqB,QAAmE;CAC/F,MAAM,gBAAgB,OAAO,MAAM;AAEnC,KAAI,CAAC,cAAc,WAAW,IAAI,IAAI,CAAC,cAAc,SAAS,IAAI,CAChE,QAAO;CAGT,MAAM,UAAU,cAAc,MAAM,GAAG,GAAG,CAAC,MAAM;AAEjD,KAAI,CAAC,QACH,QAAO,EAAE;AAGX,KAAI,WAAW,KAAK,QAAQ,CAC1B,QAAO;CAGT,MAAM,UAAU,QACb,MAAM,IAAI,CACV,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,OAAO,QAAQ;CAClB,MAAM,aAAyD,EAAE;AAEjE,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAa,sDAAsD,KAAK,MAAM;AAEpF,MAAI,CAAC,WACH,QAAO;AAGT,aAAW,KAAK;GACd,UAAU,WAAW;GACrB,OAAO,WAAW,MAAM,WAAW;GACpC,CAAC;;AAGJ,QAAO;;AAGT,SAAS,kBAAkB,QAA8C;CACvE,IAAI,QAAQ;AAEZ,MAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;EACrD,MAAM,YAAY,OAAO;AAEzB,MAAI,cAAc,KAAK;AACrB,YAAS;AACT;;AAGF,MAAI,cAAc,KAAK;AACrB,WAAQ,KAAK,IAAI,GAAG,QAAQ,EAAE;AAC9B;;AAGF,MAAI,cAAc,OAAO,UAAU,EACjC,QAAO,CAAC,OAAO,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,MAAM,QAAQ,EAAE,CAAC,MAAM,CAAC;;AAI1E,QAAO,CAAC,OAAO,MAAM,EAAE,OAAU;;AAGnC,SAAS,kBAAkB,QAA2C;CACpE,MAAM,CAAC,MAAM,QAAQ,kBAAkB,OAAO;CAC9C,MAAM,QAA4B,EAAE;AAEpC,KAAI,KAAK,WAAW,IAAI,EAAE;EACxB,MAAM,QAAQ,qBAAqB,KAAK;AAExC,MAAI,CAAC,MACH,QAAO;AAGT,OAAK,MAAM,aAAa,MACtB,OAAM,KAAK;GAAE,MAAM;GAAS,UAAU,UAAU;GAAU,OAAO,UAAU;GAAO,CAAC;AAGrF,SAAO;;AAGT,KAAI,KAAK,WAAW,IAAI,EAAE;EACxB,MAAM,iBAAiB,iCAAiC,KAAK,KAAK;AAElE,MAAI,CAAC,kBAAkB,KACrB,QAAO;AAGT,QAAM,KAAK;GAAE,MAAM;GAAa,OAAO,eAAe;GAAK,CAAC;AAC5D,SAAO;;AAGT,KAAI,CAAC,qBAAqB,KAAK,KAAK,CAClC,QAAO;AAGT,OAAM,KAAK;EAAE,MAAM;EAAW,OAAO;EAAM,CAAC;AAE5C,KAAI,CAAC,KACH,QAAO;AAGT,KAAI,KAAK,WAAW,IAAI,EAAE;EACxB,MAAM,QAAQ,qBAAqB,KAAK;AAExC,MAAI,CAAC,MACH,QAAO;AAGT,OAAK,MAAM,aAAa,MACtB,OAAM,KAAK;GAAE,MAAM;GAAS,UAAU,UAAU;GAAU,OAAO,UAAU;GAAO,CAAC;AAGrF,SAAO;;CAGT,MAAM,iBAAiB,iCAAiC,KAAK,KAAK;AAElE,KAAI,CAAC,eACH,QAAO;AAGT,OAAM,KAAK;EAAE,MAAM;EAAa,OAAO,eAAe;EAAK,CAAC;AAC5D,QAAO;;AAGT,SAAS,sBAAsB,YAAwC;CACrE,MAAM,mBAAmB,WAAW,MAAM,cAAc,UAAU,SAAS,UAAU;CACrF,MAAM,qBAAqB,WAAW,MAAM,cAAc,UAAU,SAAS,YAAY;CACzF,MAAM,kBAAkB,WAAW,QAChC,cACC,UAAU,SAAS,QACtB;CACD,MAAM,cAAwB,EAAE;AAEhC,KAAI,kBAAkB,SAAS,UAC7B,aAAY,KAAK,iBAAiB,MAAM;AAG1C,KAAI,oBAAoB,SAAS,YAC/B,aAAY,KAAK,QAAQ,mBAAmB,QAAQ;AAGtD,KAAI,gBAAgB,QAAQ;EAC1B,MAAM,cAAc,gBACjB,KAAK,cACJ,UAAU,aAAa,UAAU,QAC7B,UAAU,WACV,GAAG,UAAU,SAAS,MAAM,UAAU,QAC3C,CACA,KAAK,KAAK;AAEb,cAAY,KAAK,KAAK,YAAY,IAAI;;AAGxC,QAAO,YAAY,KAAK,KAAK;;AAS/B,SAAS,uCACP,QACA,gBACQ;AACR,KAAI,CAAC,OAAO,OACV,QAAO;AAGT,QAAO,OACJ,KAAK,UAAU;EACd,MAAM,QAAQ,eAAe,IAAI,MAAM;EACvC,MAAM,QAAQ,QAAQ,CAAC,GAAG,MAAM,MAAM,CAAC,MAAM,GAAG,EAAE;EAClD,MAAM,QAAQ,QAAQ,CAAC,GAAG,MAAM,MAAM,CAAC,MAAM,GAAG,EAAE;AAKlD,SAAO,SAAS,MAAM,cAAc,MAAM,GAJxB,MAAM,SAAS,YAAY,KAAK,UAAU,MAAM,KAAK,KACrD,MAAM,SAAS,YAAY,KAAK,UAAU,MAAM,KAAK,GAGJ,sMAFnD,GAAG,YAAY,MAAM,CAAC,OAE2O;GACjR,CACD,KAAK,KAAK;;AAGf,SAAS,iBAAiB,QAAwB;AAChD,KAAI,kDAAkD,KAAK,OAAO,CAChE,QAAO;AAGT,QAAO,4BAA4B;;AAGrC,SAAS,gCAAgC,QAAmC;CAC1E,MAAM,aAAgC,EAAE;CACxC,MAAM,QAAQ,OAAO,MAAM,KAAK;CAChC,MAAM,cAAwB,EAAE;CAChC,IAAI,SAAS;AAEb,MAAK,MAAM,QAAQ,OAAO;AACxB,cAAY,KAAK,OAAO;AACxB,YAAU,KAAK,SAAS;;CAG1B,IAAI,YAAY;AAEhB,QAAO,YAAY,MAAM,QAAQ;EAC/B,MAAM,OAAO,MAAM,cAAc;EACjC,MAAM,cAAc,KAAK,MAAM;AAE/B,MAAI,CAAC,aAAa;AAChB,gBAAa;AACb;;AAGF,MAAI,CAAC,YAAY,WAAW,SAAS,CACnC;EAGF,MAAM,YAAY;EAClB,IAAI,YAAY;AAChB,eAAa;AAEb,SAAO,YAAY,MAAM,QAAQ;GAC/B,MAAM,aAAa,UAChB,QAAQ,qBAAqB,GAAG,CAChC,QAAQ,eAAe,GAAG,CAC1B,MAAM;AAET,OACE,oDAAoD,KAAK,WAAW,IACpE,6CAA6C,KAAK,WAAW,IAC7D,iCAAiC,KAAK,WAAW,CAEjD;AAGF,gBAAa,KAAK,MAAM,cAAc;AACtC,gBAAa;;EAGf,MAAM,QAAQ,YAAY;AAE1B,MAAI,UAAU,OACZ;EAGF,MAAM,MAAM,QAAQ,UAAU;AAC9B,aAAW,KAAK;GAAE;GAAW;GAAO;GAAK,CAAC;;AAG5C,QAAO;;AAGT,SAAS,sCACP,QACA,kBACA,gBACA,cACQ;AACR,KAAI,CAAC,iBAAiB,KACpB,QAAO;CAGT,MAAM,mBAAmB,gCAAgC,OAAO;AAEhE,KAAI,CAAC,iBAAiB,OACpB,QAAO;CAGT,IAAI,uBAAuB;AAE3B,MAAK,MAAM,mBAAmB,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAC3D,wBAAuB,GAAG,qBAAqB,MAAM,GAAG,gBAAgB,MAAM,GAAG,IAAI,OAAO,gBAAgB,MAAM,gBAAgB,MAAM,GAAG,qBAAqB,MAAM,gBAAgB,IAAI;CAG5L,MAAM,eAAqE,EAAE;AAE7E,MAAK,MAAM,mBAAmB,kBAAkB;EAC9C,MAAM,sBAAsB,gBAAgB,UAAU,MAAM;AAE5D,MAAI,oBAAoB,WAAW,eAAe,CAChD;AAGF,MAAI,iCAAiC,KAAK,oBAAoB,CAC5D;EAGF,MAAM,oBAAoB,qDAAqD,KAC7E,oBACD;AAED,MAAI,CAAC,kBACH;EAGF,MAAM,eAAe,kBAAkB;AAEvC,MAAI,CAAC,aACH;EAGF,MAAM,eAAe,kBAAkB,aAAa;AAEpD,MAAI,CAAC,aACH;EAGF,MAAM,kCAAkB,IAAI,KAAa;AAEzC,OAAK,MAAM,aAAa,cAAc;GACpC,MAAM,YAAY,UAAU;AAE5B,OAAI,aAAa,IAAI,UAAU,CAC7B;AAGF,OAAI,CAAC,iBAAiB,IAAI,UAAU,CAClC;AAGF,OAAI,IAAI,OAAO,MAAM,gBAAgB,UAAU,CAAC,KAAK,CAAC,KAAK,qBAAqB,CAC9E;AAGF,mBAAgB,IAAI,UAAU;;AAGhC,MAAI,CAAC,gBAAgB,KACnB;EAGF,MAAM,iBAAiB,aAAa,QAAQ,cAAc,CAAC,gBAAgB,IAAI,UAAU,MAAM,CAAC;EAChG,MAAM,0BAA0B,uCAC9B,CAAC,GAAG,gBAAgB,EACpB,eACD;AAED,MAAI,CAAC,eAAe,QAAQ;AAC1B,gBAAa,KAAK;IAChB,OAAO,gBAAgB;IACvB,KAAK,gBAAgB;IACrB,OAAO;IACR,CAAC;AACF;;EAGF,MAAM,oBAAoB,0BAA0B,KAAK,oBAAoB;AAE7E,MAAI,CAAC,kBACH;AAGF,eAAa,KAAK;GAChB,OAAO,gBAAgB;GACvB,KAAK,gBAAgB;GACrB,OAAO,GAAG,UAAU,sBAAsB,eAAe,CAAC,QAAQ,kBAAkB,OAAO,0BAA0B,KAAK,4BAA4B;GACvJ,CAAC;;AAGJ,KAAI,CAAC,aAAa,OAChB,QAAO;CAGT,IAAI,oBAAoB;AAExB,MAAK,MAAM,eAAe,aAAa,MAAM,MAAM,UAAU,MAAM,QAAQ,KAAK,MAAM,CACpF,qBAAoB,GAAG,kBAAkB,MAAM,GAAG,YAAY,MAAM,GAAG,YAAY,QAAQ,kBAAkB,MAAM,YAAY,IAAI;AAGrI,QAAO,iBAAiB,kBAAkB;;AAG5C,SAAgB,4BAA4B,MAAc,cAAmC;CAC3F,MAAM,gBAAgB;CACtB,IAAI,cAAsC;AAE1C,MAAK,MAAM,gBAAgB,KAAK,SAAS,cAAc,CACrD,KAAI,aAAa,IAAI,SAAS,QAAQ,EAAE;AACtC,gBAAc;AACd;;AAIJ,KAAI,CAAC,eAAe,CAAC,YAAY,GAC/B,QAAO;CAGT,MAAM,EAAE,UAAU,kBAAkB,mBAAmB,6BAA6B,KAAK;AAEzF,KAAI,CAAC,iBAAiB,KACpB,QAAO;CAGT,MAAM,iBAAiB,YAAY;CACnC,MAAM,oBAAoB,sCACxB,gBACA,kBACA,gBACA,aACD;AAED,KAAI,sBAAsB,eACxB,QAAO;CAGT,MAAM,YAAY,YAAY;CAC9B,MAAM,sBAAsB,YAAY,SAAS,KAAK,UAAU,QAAQ,eAAe;CACvF,MAAM,mBAAmB,qBAAqB,eAAe;AAE7D,QAAO,GAAG,KAAK,MAAM,GAAG,mBAAmB,GAAG,oBAAoB,KAAK,MAAM,iBAAiB;;AAGhG,SAAgB,8BAA8B,IAAqB;AACjE,KAAI,CAAC,GAAG,SAAS,OAAO,IAAI,CAAC,GAAG,SAAS,IAAI,CAC3C,QAAO;CAGT,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,IAAI,GAAG,EAAE;AAE3C,KAAI,CAAC,IAAI,OAAO,UAAU,qBAAqB,GAAG,uBAAuB,SAAS,CAAC,KAAK,MAAM,CAC5F,QAAO;AAGT,QAAO,CAAC,eAAe,KAAK,MAAM;;AAGpC,SAAgB,sBAAsB,IAAyB;CAC7D,MAAM,aAAa,GAAG,QAAQ,IAAI;AAElC,KAAI,eAAe,GACjB,wBAAO,IAAI,KAAa;CAI1B,MAAM,OADS,IAAI,gBAAgB,GAAG,MAAM,aAAa,EAAE,CAAC,CACxC,IAAI,0BAA0B;AAElD,KAAI,CAAC,KACH,wBAAO,IAAI,KAAa;AAG1B,QAAO,IAAI,IACT,KACG,MAAM,IAAI,CACV,KAAK,YAAY,QAAQ,MAAM,CAAC,CAChC,OAAO,QAAQ,CACnB;;;;;ACpyBH,SAAgB,eAAuB;AACrC,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAM,IAAI;AAClB,OAAI,cAAc,GAAG,CAAC,SAAS,iBAAiB,CAC9C,QAAO;AAGT,OAAI,8BAA8B,GAAG,EAAE;IACrC,MAAM,kBAAkB,4BAA4B,MAAM,sBAAsB,GAAG,CAAC;AACpF,WAAO,oBAAoB,OAAO,OAAO;;GAG3C,MAAM,kBAAkB,0BAA0B,KAAK;AACvD,UAAO,oBAAoB,OAAO,OAAO;;EAE5C"}
@@ -0,0 +1,12 @@
1
+ import { VueWrapper, mount } from "@vue/test-utils";
2
+ import { ComponentPublicInstance } from "vue";
3
+
4
+ //#region src/runtime.d.ts
5
+ type ImportedComponent<TImport> = TImport extends {
6
+ default: infer TComponent;
7
+ } ? TComponent : TImport;
8
+ type MountedInstance<TImport> = ImportedComponent<TImport> extends (abstract new (...args: any[]) => infer TInstance) ? TInstance : ComponentPublicInstance;
9
+ declare function fastMount<TImport>(componentImport: Promise<TImport>, options?: Parameters<typeof mount>[1]): Promise<VueWrapper<MountedInstance<TImport>>>;
10
+ //#endregion
11
+ export { fastMount };
12
+ //# sourceMappingURL=runtime.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/runtime.ts"],"mappings":";;;;KAIK,iBAAA,YAA6B,OAAA;EAAkB,OAAA;AAAA,IAChD,UAAA,GACA,OAAA;AAAA,KAEC,eAAA,YACH,iBAAA,CAAkB,OAAA,4BAAkC,IAAA,+BAChD,SAAA,GACA,uBAAA;AAAA,iBAEgB,SAAA,SAAA,CACpB,eAAA,EAAiB,OAAA,CAAQ,OAAA,GACzB,OAAA,GAAU,UAAA,QAAkB,KAAA,OAC3B,OAAA,CAAQ,UAAA,CAAW,eAAA,CAAgB,OAAA"}
@@ -0,0 +1,11 @@
1
+ import { mount } from "@vue/test-utils";
2
+
3
+ //#region src/runtime.ts
4
+ async function fastMount(componentImport, options) {
5
+ const importedComponent = await componentImport;
6
+ return mount(importedComponent && typeof importedComponent === "object" && "default" in importedComponent ? importedComponent.default : importedComponent, options);
7
+ }
8
+
9
+ //#endregion
10
+ export { fastMount };
11
+ //# sourceMappingURL=runtime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type { VueWrapper } from '@vue/test-utils'\nimport { mount } from '@vue/test-utils'\nimport type { ComponentPublicInstance } from 'vue'\n\ntype ImportedComponent<TImport> = TImport extends { default: infer TComponent }\n ? TComponent\n : TImport\n\ntype MountedInstance<TImport> =\n ImportedComponent<TImport> extends abstract new (...args: any[]) => infer TInstance\n ? TInstance\n : ComponentPublicInstance\n\nexport async function fastMount<TImport>(\n componentImport: Promise<TImport>,\n options?: Parameters<typeof mount>[1],\n): Promise<VueWrapper<MountedInstance<TImport>>> {\n const importedComponent = await componentImport\n const component =\n importedComponent && typeof importedComponent === 'object' && 'default' in importedComponent\n ? importedComponent.default\n : importedComponent\n\n return mount(\n component as Parameters<typeof mount>[0],\n options as Parameters<typeof mount>[1],\n ) as VueWrapper<MountedInstance<TImport>>\n}\n"],"mappings":";;;AAaA,eAAsB,UACpB,iBACA,SAC+C;CAC/C,MAAM,oBAAoB,MAAM;AAMhC,QAAO,MAJL,qBAAqB,OAAO,sBAAsB,YAAY,aAAa,oBACvE,kBAAkB,UAClB,mBAIJ,QACD"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "vue-fast-mount",
3
+ "version": "0.0.1",
4
+ "description": "Vite plugin for faster mounting of Vue components in Vitest.",
5
+ "keywords": [
6
+ "library",
7
+ "vite-plugin",
8
+ "vue"
9
+ ],
10
+ "homepage": "https://github.com/DerYeger/yeger/tree/main/packages/vue-fast-mount",
11
+ "bugs": {
12
+ "url": "https://github.com/DerYeger/yeger/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": {
16
+ "name": "Jan Müller",
17
+ "url": "https://github.com/DerYeger"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/DerYeger/yeger.git",
22
+ "directory": "packages/vue-fast-mount"
23
+ },
24
+ "funding": "https://github.com/sponsors/DerYeger",
25
+ "files": [
26
+ "dist",
27
+ "LICENSE"
28
+ ],
29
+ "type": "module",
30
+ "types": "dist/runtime.d.mts",
31
+ "exports": {
32
+ ".": {
33
+ "import": {
34
+ "types": "./dist/runtime.d.mts",
35
+ "default": "./dist/runtime.mjs"
36
+ }
37
+ },
38
+ "./plugin": {
39
+ "import": {
40
+ "types": "./dist/plugin.d.mts",
41
+ "default": "./dist/plugin.mjs"
42
+ }
43
+ }
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "devDependencies": {
49
+ "@vitejs/plugin-vue": "6.0.4",
50
+ "@vitest/browser-playwright": "4.0.18",
51
+ "@vitest/coverage-v8": "4.0.18",
52
+ "@vitest/ui": "4.0.18",
53
+ "@vue/test-utils": "2.4.6",
54
+ "publint": "0.3.17",
55
+ "tsdown": "0.20.3",
56
+ "typescript": "5.9.3",
57
+ "vite": "7.3.1",
58
+ "vitest": "4.0.18",
59
+ "vue": "3.5.28",
60
+ "vue-tsc": "3.2.4",
61
+ "@yeger/tsconfig": "2.1.2",
62
+ "@yeger/vitest-utils": "1.2.2"
63
+ },
64
+ "peerDependencies": {
65
+ "@vue/test-utils": "^2.0.0",
66
+ "vite": "^7.0.0",
67
+ "vue": "^3.0.0"
68
+ },
69
+ "scripts": {
70
+ "build": "tsdown",
71
+ "publint": "publint run --strict",
72
+ "test": "vitest",
73
+ "typecheck": "vue-tsc"
74
+ }
75
+ }