@dev-to/vue-loader 0.2.2

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 ADDED
@@ -0,0 +1,19 @@
1
+ # @dev-to/vue-loader
2
+
3
+ Vue host-side loader that mounts remote Vue components served by a DevTo-enabled Vite server.
4
+
5
+ ## Usage
6
+
7
+ ```vue
8
+ <script setup lang="ts">
9
+ import { VueLoader } from '@dev-to/vue-loader'
10
+ </script>
11
+
12
+ <template>
13
+ <VueLoader
14
+ origin="http://localhost:5173"
15
+ name="MyCard"
16
+ :component-props="{ title: 'Hello' }"
17
+ />
18
+ </template>
19
+ ```
@@ -0,0 +1,133 @@
1
+ import { type PropType, type VNodeChild } from 'vue';
2
+ import { type DevToDiscoveryContract, type DevToVueBridgeContract } from '@dev-to/shared';
3
+ export declare const DEFAULT_DISCOVERY_ENDPOINT: "/__dev_to__/discovery.json";
4
+ export declare const DEFAULT_CONTRACT_ENDPOINT: "/__dev_to__/vue/contract.js";
5
+ export declare const DEFAULT_INIT_ENDPOINT: "/__dev_to__/vue/init.js";
6
+ export declare function loadDiscoveryContract(origin: string, discoveryEndpoint?: string): Promise<DevToDiscoveryContract>;
7
+ export declare function loadBridgeContract(origin: string, contractEndpoint?: string): Promise<DevToVueBridgeContract>;
8
+ export declare function ensureBridgeInit(origin: string, contract: DevToVueBridgeContract): Promise<void>;
9
+ export interface VueEntryResolutionResult {
10
+ success: true;
11
+ entryUrl: string;
12
+ }
13
+ export interface VueEntryResolutionError {
14
+ success: false;
15
+ error: {
16
+ message: string;
17
+ type: 'COMPONENT_NOT_FOUND' | 'CONTRACT_LOAD_FAILED';
18
+ componentName?: string;
19
+ errorReason?: string;
20
+ setupGuide?: {
21
+ title: string;
22
+ steps: string[];
23
+ };
24
+ };
25
+ }
26
+ export type VueEntryResolution = VueEntryResolutionResult | VueEntryResolutionError;
27
+ export declare function resolveVueEntry(origin: string, componentName: string, contractEndpoint?: string): Promise<VueEntryResolution>;
28
+ export declare function resolveVueEntryForLoader(origin: string, componentName: string, contractEndpoint?: string): Promise<{
29
+ success: true;
30
+ entryUrl: string;
31
+ } | {
32
+ success: false;
33
+ loaderError: VueLoaderError;
34
+ }>;
35
+ export interface VueLoaderError {
36
+ message: string;
37
+ origin?: string;
38
+ componentName?: string;
39
+ type?: string;
40
+ errorReason?: string;
41
+ statusCode?: number | string;
42
+ setupGuide?: {
43
+ title: string;
44
+ steps: string[];
45
+ };
46
+ }
47
+ export interface VueLoaderProps<P extends Record<string, unknown> = Record<string, unknown>> {
48
+ origin?: string;
49
+ name?: string;
50
+ url?: string;
51
+ contractEndpoint?: string;
52
+ componentProps?: P;
53
+ loading?: () => VNodeChild;
54
+ externalError?: VueLoaderError | null;
55
+ renderError?: (error: Error | VueLoaderError) => VNodeChild;
56
+ }
57
+ export declare const VueLoader: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
58
+ origin: {
59
+ type: StringConstructor;
60
+ default: undefined;
61
+ };
62
+ name: {
63
+ type: StringConstructor;
64
+ default: undefined;
65
+ };
66
+ url: {
67
+ type: StringConstructor;
68
+ default: undefined;
69
+ };
70
+ contractEndpoint: {
71
+ type: StringConstructor;
72
+ default: undefined;
73
+ };
74
+ componentProps: {
75
+ type: PropType<Record<string, unknown>>;
76
+ default: () => {};
77
+ };
78
+ loading: {
79
+ type: PropType<() => VNodeChild>;
80
+ default: undefined;
81
+ };
82
+ externalError: {
83
+ type: PropType<VueLoaderError | null>;
84
+ default: null;
85
+ };
86
+ renderError: {
87
+ type: PropType<(error: Error | VueLoaderError) => VNodeChild>;
88
+ default: undefined;
89
+ };
90
+ }>, () => VNodeChild, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
91
+ origin: {
92
+ type: StringConstructor;
93
+ default: undefined;
94
+ };
95
+ name: {
96
+ type: StringConstructor;
97
+ default: undefined;
98
+ };
99
+ url: {
100
+ type: StringConstructor;
101
+ default: undefined;
102
+ };
103
+ contractEndpoint: {
104
+ type: StringConstructor;
105
+ default: undefined;
106
+ };
107
+ componentProps: {
108
+ type: PropType<Record<string, unknown>>;
109
+ default: () => {};
110
+ };
111
+ loading: {
112
+ type: PropType<() => VNodeChild>;
113
+ default: undefined;
114
+ };
115
+ externalError: {
116
+ type: PropType<VueLoaderError | null>;
117
+ default: null;
118
+ };
119
+ renderError: {
120
+ type: PropType<(error: Error | VueLoaderError) => VNodeChild>;
121
+ default: undefined;
122
+ };
123
+ }>> & Readonly<{}>, {
124
+ url: string;
125
+ origin: string;
126
+ name: string;
127
+ contractEndpoint: string;
128
+ componentProps: Record<string, unknown>;
129
+ loading: () => VNodeChild;
130
+ externalError: VueLoaderError | null;
131
+ renderError: (error: Error | VueLoaderError) => VNodeChild;
132
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
133
+ //# sourceMappingURL=VueLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VueLoader.d.ts","sourceRoot":"","sources":["../src/VueLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_CONTRACT_ENDPOINT, DEFAULT_DISCOVERY_ENDPOINT, DEFAULT_INIT_ENDPOINT, VueLoader, ensureBridgeInit, loadBridgeContract, loadDiscoveryContract, resolveVueEntry, resolveVueEntryForLoader, } from './VueLoader.js';
2
+ export type { VueEntryResolution, VueEntryResolutionError, VueEntryResolutionResult, VueLoaderError, VueLoaderProps, } from './VueLoader.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,qBAAqB,EACrB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,wBAAwB,GACzB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,cAAc,GACf,MAAM,gBAAgB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,453 @@
1
+ import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from "vue";
2
+ import { DEV_TO_DEBUG_HTML_PATH, DEV_TO_DISCOVERY_PATH, DEV_TO_VUE_CONTRACT_PATH, DEV_TO_VUE_INIT_PATH } from "@dev-to/shared";
3
+ function isRecord(value) {
4
+ return 'object' == typeof value && null !== value;
5
+ }
6
+ function unwrapDefault(value) {
7
+ if (!isRecord(value)) return value;
8
+ const defaultValue = value['default'];
9
+ return null != defaultValue ? defaultValue : value;
10
+ }
11
+ function toError(value) {
12
+ return value instanceof Error ? value : new Error(String(value));
13
+ }
14
+ function resolveRemoteRuntime(runtimeModule) {
15
+ const runtimeRecord = isRecord(runtimeModule) ? runtimeModule : {};
16
+ const vueCandidate = runtimeRecord['default'] ?? runtimeRecord['Vue'] ?? runtimeModule;
17
+ if (!isRecord(vueCandidate) || 'function' != typeof vueCandidate['createApp'] || 'function' != typeof vueCandidate['h'] || 'function' != typeof vueCandidate['defineComponent']) throw new Error('Invalid Vue runtime module from dev server.');
18
+ return vueCandidate;
19
+ }
20
+ const DEFAULT_DISCOVERY_ENDPOINT = DEV_TO_DISCOVERY_PATH;
21
+ const DEFAULT_CONTRACT_ENDPOINT = DEV_TO_VUE_CONTRACT_PATH;
22
+ const DEFAULT_INIT_ENDPOINT = DEV_TO_VUE_INIT_PATH;
23
+ function resolveEndpointUrl(origin, endpoint) {
24
+ if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) return endpoint;
25
+ if (endpoint.startsWith('/')) return `${origin}${endpoint}`;
26
+ return `${origin}/${endpoint}`;
27
+ }
28
+ function isBridgeContract(value) {
29
+ if (!isRecord(value)) return false;
30
+ const paths = value['paths'];
31
+ const events = value['events'];
32
+ if (!isRecord(paths) || 'string' != typeof paths['vueRuntime']) return false;
33
+ return !(!isRecord(events) || 'string' != typeof events['fullReload']);
34
+ }
35
+ function resolveContract(initModule) {
36
+ const moduleRecord = isRecord(initModule) ? initModule : {};
37
+ const contractCandidate = moduleRecord['DEV_TO_VUE_CONTRACT'] ?? moduleRecord['default'] ?? null;
38
+ if (!contractCandidate) throw new Error('Dev server contract not found. Please ensure `@dev-to/vue-plugin` (devToVuePlugin) is enabled in the Vite dev server.');
39
+ if (!isBridgeContract(contractCandidate)) throw new Error('Invalid dev server contract.');
40
+ return contractCandidate;
41
+ }
42
+ const nativeImport = new Function('url', 'return import(url)');
43
+ const discoveryCache = new Map();
44
+ const contractCache = new Map();
45
+ const initCache = new Map();
46
+ async function loadDiscoveryContract(origin, discoveryEndpoint) {
47
+ const endpoint = discoveryEndpoint || DEFAULT_DISCOVERY_ENDPOINT;
48
+ const discoveryUrl = resolveEndpointUrl(origin, endpoint);
49
+ if (discoveryCache.has(discoveryUrl)) return discoveryCache.get(discoveryUrl);
50
+ const p = fetch(discoveryUrl).then(async (resp)=>{
51
+ if (!resp.ok) throw new Error(`Discovery endpoint returned ${resp.status}: ${resp.statusText}`);
52
+ const data = await resp.json();
53
+ if (!data || 'object' != typeof data) throw new Error('Invalid discovery response: not a JSON object');
54
+ return data;
55
+ }).catch((e)=>{
56
+ discoveryCache.delete(discoveryUrl);
57
+ throw e instanceof Error ? e : new Error(String(e));
58
+ });
59
+ discoveryCache.set(discoveryUrl, p);
60
+ return p;
61
+ }
62
+ async function loadBridgeContract(origin, contractEndpoint) {
63
+ const endpoint = contractEndpoint || DEFAULT_CONTRACT_ENDPOINT;
64
+ const initUrl = resolveEndpointUrl(origin, endpoint);
65
+ if (contractCache.has(initUrl)) return contractCache.get(initUrl);
66
+ const p = nativeImport(initUrl).then((m)=>resolveContract(m)).catch(async (e)=>{
67
+ contractCache.delete(initUrl);
68
+ const err = e instanceof Error ? e : new Error(String(e));
69
+ if (err.message.includes('Failed to fetch')) try {
70
+ const resp = await fetch(initUrl, {
71
+ method: 'HEAD'
72
+ }).catch(()=>null);
73
+ if (resp) err.statusCode = resp.status;
74
+ } catch {}
75
+ throw err;
76
+ });
77
+ contractCache.set(initUrl, p);
78
+ return p;
79
+ }
80
+ async function ensureBridgeInit(origin, contract) {
81
+ const initPath = contract?.paths?.initClient || DEFAULT_INIT_ENDPOINT;
82
+ const initUrl = resolveEndpointUrl(origin, initPath);
83
+ if (initCache.has(initUrl)) return initCache.get(initUrl);
84
+ const p = nativeImport(initUrl).then(()=>void 0).catch((e)=>{
85
+ initCache.delete(initUrl);
86
+ throw e instanceof Error ? e : new Error(String(e));
87
+ });
88
+ initCache.set(initUrl, p);
89
+ return p;
90
+ }
91
+ async function resolveVueEntry(origin, componentName, contractEndpoint) {
92
+ try {
93
+ const contract = await loadBridgeContract(origin, contractEndpoint);
94
+ const dev = contract?.dev || {};
95
+ const componentMap = dev.componentMap || {};
96
+ const entry = componentMap[componentName] || componentMap['*'];
97
+ if (!entry) {
98
+ const errorMessage = `Component "${componentName}" not found in devComponentMap`;
99
+ const isWildcardMode = !!componentMap['*'];
100
+ const errorReason = isWildcardMode ? `The component "${componentName}" is not configured, and the wildcard fallback is also missing or invalid.` : `The component "${componentName}" is not configured in devComponentMap. Please add it to your Vite configuration.`;
101
+ return {
102
+ success: false,
103
+ error: {
104
+ message: errorMessage,
105
+ type: 'COMPONENT_NOT_FOUND',
106
+ componentName,
107
+ errorReason,
108
+ setupGuide: {
109
+ title: 'Setup Guide',
110
+ steps: [
111
+ "import { devToVuePlugin } from '@dev-to/vue-plugin'",
112
+ '',
113
+ "// Option 1: Shorthand (Default)",
114
+ `devToVuePlugin('${componentName}')`,
115
+ '',
116
+ "// Option 2: Explicit Mapping",
117
+ "devToVuePlugin({",
118
+ ` '${componentName}': '/', // Default entry`,
119
+ " 'Other': 'src/Card.vue' // Specific file",
120
+ "})"
121
+ ]
122
+ }
123
+ }
124
+ };
125
+ }
126
+ const entryUrl = entry.startsWith('http://') || entry.startsWith('https://') ? entry : `${origin}${entry.startsWith('/') ? '' : '/'}${entry}`;
127
+ return {
128
+ success: true,
129
+ entryUrl
130
+ };
131
+ } catch (e) {
132
+ const errorMessage = e instanceof Error ? e.message : String(e);
133
+ return {
134
+ success: false,
135
+ error: {
136
+ message: errorMessage,
137
+ type: 'CONTRACT_LOAD_FAILED'
138
+ }
139
+ };
140
+ }
141
+ }
142
+ async function resolveVueEntryForLoader(origin, componentName, contractEndpoint) {
143
+ const r = await resolveVueEntry(origin, componentName, contractEndpoint);
144
+ if (r.success) return r;
145
+ const e = r.error;
146
+ return {
147
+ success: false,
148
+ loaderError: {
149
+ message: e.message,
150
+ origin,
151
+ componentName: e.componentName || componentName,
152
+ type: e.type,
153
+ errorReason: e.errorReason,
154
+ setupGuide: e.setupGuide
155
+ }
156
+ };
157
+ }
158
+ function safeLocationHref() {
159
+ if ("u" < typeof window) return '';
160
+ try {
161
+ return window.location.href;
162
+ } catch {
163
+ return '';
164
+ }
165
+ }
166
+ function safeOrigin() {
167
+ if ("u" < typeof window) return '';
168
+ try {
169
+ return window.location.origin;
170
+ } catch {
171
+ return '';
172
+ }
173
+ }
174
+ function getOriginFromFinalJsUrl(finalJsUrl) {
175
+ let origin = '';
176
+ try {
177
+ const urlParam = finalJsUrl.match(/url=([^&]+)/)?.[1] || '';
178
+ const decoded = urlParam ? decodeURIComponent(urlParam) : finalJsUrl;
179
+ const parsed = decoded.startsWith('http') ? new URL(decoded) : new URL(decoded, safeLocationHref() || 'http://localhost');
180
+ origin = parsed.origin;
181
+ } catch {
182
+ origin = safeOrigin();
183
+ }
184
+ return origin;
185
+ }
186
+ function syncProps(target, next) {
187
+ for (const key of Object.keys(target))if (!(key in next)) delete target[key];
188
+ for (const [key, value] of Object.entries(next))target[key] = value;
189
+ }
190
+ const VueLoader = defineComponent({
191
+ name: 'VueLoader',
192
+ props: {
193
+ origin: {
194
+ type: String,
195
+ default: void 0
196
+ },
197
+ name: {
198
+ type: String,
199
+ default: void 0
200
+ },
201
+ url: {
202
+ type: String,
203
+ default: void 0
204
+ },
205
+ contractEndpoint: {
206
+ type: String,
207
+ default: void 0
208
+ },
209
+ componentProps: {
210
+ type: Object,
211
+ default: ()=>({})
212
+ },
213
+ loading: {
214
+ type: Function,
215
+ default: void 0
216
+ },
217
+ externalError: {
218
+ type: Object,
219
+ default: null
220
+ },
221
+ renderError: {
222
+ type: Function,
223
+ default: void 0
224
+ }
225
+ },
226
+ setup (props) {
227
+ const containerRef = ref(null);
228
+ const appRef = ref(null);
229
+ const runtimeRef = ref(null);
230
+ const componentRef = ref(null);
231
+ const propsStateRef = ref(null);
232
+ const state = ref({
233
+ isReady: false,
234
+ error: null
235
+ });
236
+ const version = ref(0);
237
+ const resolvedUrl = ref(props.url);
238
+ const fullReloadListenerRef = ref(null);
239
+ const ensureFullReloadListener = (eventName)=>{
240
+ if (!eventName || "u" < typeof window) return;
241
+ const current = fullReloadListenerRef.value;
242
+ if (current?.eventName === eventName) return;
243
+ if (current) window.removeEventListener(current.eventName, current.handler);
244
+ const handler = ()=>{
245
+ version.value += 1;
246
+ };
247
+ window.addEventListener(eventName, handler);
248
+ fullReloadListenerRef.value = {
249
+ eventName,
250
+ handler
251
+ };
252
+ };
253
+ const unmountRemoteApp = ()=>{
254
+ try {
255
+ appRef.value?.unmount?.();
256
+ } catch {}
257
+ appRef.value = null;
258
+ propsStateRef.value = null;
259
+ };
260
+ const renderRemoteApp = (force = false)=>{
261
+ const runtime = runtimeRef.value;
262
+ const ComponentRef = componentRef.value;
263
+ if (!runtime || !ComponentRef) return;
264
+ if (!containerRef.value) return;
265
+ if (appRef.value && !force) return;
266
+ unmountRemoteApp();
267
+ const propsState = runtime.reactive({});
268
+ syncProps(propsState, props.componentProps ?? {});
269
+ const Wrapper = runtime.defineComponent({
270
+ name: 'DevToVueWrapper',
271
+ setup () {
272
+ return ()=>runtime.h(ComponentRef, propsState);
273
+ }
274
+ });
275
+ const app = runtime.createApp(Wrapper);
276
+ app.mount(containerRef.value);
277
+ appRef.value = app;
278
+ propsStateRef.value = propsState;
279
+ state.value.isReady = true;
280
+ };
281
+ const loadViteComponent = async (entryUrl, exportName, currentVersion)=>{
282
+ if (!entryUrl) throw new Error('Not found entry url.');
283
+ const originFromUrl = getOriginFromFinalJsUrl(entryUrl);
284
+ const connector = entryUrl.includes('?') ? '&' : '?';
285
+ const jsUrlWithParam = currentVersion > 0 ? `${entryUrl}${connector}v=${currentVersion}` : entryUrl;
286
+ try {
287
+ const contract = await loadBridgeContract(originFromUrl, props.contractEndpoint);
288
+ await ensureBridgeInit(originFromUrl, contract);
289
+ ensureFullReloadListener(contract.events.fullReload);
290
+ const runtimeModule = await nativeImport(`${originFromUrl}${contract.paths.vueRuntime}`);
291
+ const runtime = resolveRemoteRuntime(runtimeModule);
292
+ const moduleNs = await nativeImport(jsUrlWithParam);
293
+ const moduleRecord = isRecord(moduleNs) ? moduleNs : {};
294
+ const exportCandidate = exportName ? moduleRecord[exportName] : void 0;
295
+ const candidate = exportName ? exportCandidate ?? moduleRecord['default'] ?? moduleNs : moduleRecord['default'] ?? moduleNs;
296
+ const ComponentValue = unwrapDefault(candidate);
297
+ if (ComponentValue) return {
298
+ Component: ComponentValue,
299
+ runtime
300
+ };
301
+ throw new Error('Vite Dev Component Load Fail: Component not found in module.');
302
+ } catch (e) {
303
+ const err = toError(e);
304
+ if (err.message.includes('Failed to fetch')) try {
305
+ const resp = await fetch(jsUrlWithParam, {
306
+ method: 'HEAD'
307
+ }).catch(()=>null);
308
+ if (resp) err.statusCode = resp.status;
309
+ } catch {}
310
+ throw err;
311
+ }
312
+ };
313
+ watch(()=>[
314
+ props.url,
315
+ props.origin,
316
+ props.name,
317
+ props.contractEndpoint
318
+ ], (values, _old, onInvalidate)=>{
319
+ const [directUrl, origin, name, contractEndpoint] = values;
320
+ let cancelled = false;
321
+ onInvalidate(()=>{
322
+ cancelled = true;
323
+ });
324
+ if (directUrl) {
325
+ resolvedUrl.value = directUrl;
326
+ state.value.error = null;
327
+ return;
328
+ }
329
+ if (!origin || !name) {
330
+ if (!directUrl && (origin || name)) state.value.error = new Error('Missing dev server origin or component name for resolution.');
331
+ return;
332
+ }
333
+ state.value.isReady = false;
334
+ state.value.error = null;
335
+ resolveVueEntryForLoader(origin, name, contractEndpoint).then((res)=>{
336
+ if (cancelled) return;
337
+ if (true === res.success) resolvedUrl.value = res.entryUrl;
338
+ else state.value.error = res.loaderError;
339
+ });
340
+ }, {
341
+ immediate: true
342
+ });
343
+ watch(()=>[
344
+ resolvedUrl.value,
345
+ props.name,
346
+ version.value
347
+ ], (values, _old, onInvalidate)=>{
348
+ const [url, name, currentVersion] = values;
349
+ if (!url) return;
350
+ let cancelled = false;
351
+ onInvalidate(()=>{
352
+ cancelled = true;
353
+ });
354
+ state.value.isReady = false;
355
+ if (state.value.error instanceof Error) state.value.error = null;
356
+ loadViteComponent(url, name, currentVersion).then(({ Component, runtime })=>{
357
+ if (cancelled) return;
358
+ runtimeRef.value = runtime;
359
+ componentRef.value = Component;
360
+ renderRemoteApp(true);
361
+ }).catch((err)=>{
362
+ if (cancelled) return;
363
+ state.value.error = err instanceof Error ? err : new Error(String(err));
364
+ state.value.isReady = false;
365
+ unmountRemoteApp();
366
+ });
367
+ }, {
368
+ immediate: true
369
+ });
370
+ watch(()=>props.componentProps, (next)=>{
371
+ if (!propsStateRef.value) return;
372
+ syncProps(propsStateRef.value, next || {});
373
+ }, {
374
+ deep: true
375
+ });
376
+ onMounted(()=>{
377
+ renderRemoteApp(false);
378
+ });
379
+ onBeforeUnmount(()=>{
380
+ unmountRemoteApp();
381
+ if ("u" < typeof window) return;
382
+ const current = fullReloadListenerRef.value;
383
+ if (current) window.removeEventListener(current.eventName, current.handler);
384
+ });
385
+ const renderErrorNode = (err)=>{
386
+ if (props.renderError) return props.renderError(err);
387
+ const isLoaderErrorObject = !(err instanceof Error);
388
+ const message = err.message || String(err);
389
+ const currentOrigin = (isLoaderErrorObject ? err.origin : void 0) || props.origin || (props.url ? getOriginFromFinalJsUrl(props.url) : '');
390
+ const debugPanelUrl = `${currentOrigin}${DEV_TO_DEBUG_HTML_PATH}`;
391
+ return h('div', {
392
+ style: {
393
+ border: '1px solid #e5e7eb',
394
+ padding: '10px',
395
+ borderRadius: '6px'
396
+ }
397
+ }, [
398
+ h('div', {
399
+ style: {
400
+ fontWeight: '600',
401
+ marginBottom: '6px'
402
+ }
403
+ }, 'DevTo Vue Loader Error'),
404
+ h('pre', {
405
+ style: {
406
+ fontSize: '12px',
407
+ whiteSpace: 'pre-wrap'
408
+ }
409
+ }, message),
410
+ currentOrigin ? h('div', {
411
+ style: {
412
+ fontSize: '12px',
413
+ marginTop: '6px'
414
+ }
415
+ }, [
416
+ h('span', 'Origin: '),
417
+ h('code', currentOrigin)
418
+ ]) : null,
419
+ currentOrigin ? h('div', {
420
+ style: {
421
+ fontSize: '12px',
422
+ marginTop: '6px'
423
+ }
424
+ }, [
425
+ h('a', {
426
+ href: debugPanelUrl,
427
+ target: '_blank',
428
+ rel: 'noreferrer'
429
+ }, debugPanelUrl)
430
+ ]) : null
431
+ ]);
432
+ };
433
+ return ()=>{
434
+ if (props.externalError) return renderErrorNode(props.externalError);
435
+ if (state.value.error) return renderErrorNode(state.value.error);
436
+ const children = [
437
+ h('div', {
438
+ ref: containerRef,
439
+ class: 'vdev-loader-container',
440
+ 'data-is': 'VueLoader'
441
+ })
442
+ ];
443
+ if (!state.value.isReady) {
444
+ const loadingNode = props.loading ? props.loading() : h('div', 'Loading...');
445
+ children.push(loadingNode);
446
+ }
447
+ return h('div', {
448
+ class: 'vdev-loader-root'
449
+ }, children);
450
+ };
451
+ }
452
+ });
453
+ export { DEFAULT_CONTRACT_ENDPOINT, DEFAULT_DISCOVERY_ENDPOINT, DEFAULT_INIT_ENDPOINT, VueLoader, ensureBridgeInit, loadBridgeContract, loadDiscoveryContract, resolveVueEntry, resolveVueEntryForLoader };
@@ -0,0 +1,521 @@
1
+ (function(root, factory) {
2
+ if ('object' == typeof exports && 'object' == typeof module) module.exports = factory(require("Vue"));
3
+ else if ('function' == typeof define && define.amd) define([
4
+ "Vue"
5
+ ], factory);
6
+ else if ('object' == typeof exports) exports["DevToVueLoader"] = factory(require("Vue"));
7
+ else root["DevToVueLoader"] = factory(root["Vue"]);
8
+ })(globalThis, (__rspack_external_vue)=>(()=>{
9
+ "use strict";
10
+ var __webpack_modules__ = {
11
+ vue (module1) {
12
+ module1.exports = __rspack_external_vue;
13
+ }
14
+ };
15
+ var __webpack_module_cache__ = {};
16
+ function __webpack_require__(moduleId) {
17
+ var cachedModule = __webpack_module_cache__[moduleId];
18
+ if (void 0 !== cachedModule) return cachedModule.exports;
19
+ var module1 = __webpack_module_cache__[moduleId] = {
20
+ exports: {}
21
+ };
22
+ __webpack_modules__[moduleId](module1, module1.exports, __webpack_require__);
23
+ return module1.exports;
24
+ }
25
+ (()=>{
26
+ __webpack_require__.d = (exports1, definition)=>{
27
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
28
+ enumerable: true,
29
+ get: definition[key]
30
+ });
31
+ };
32
+ })();
33
+ (()=>{
34
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
35
+ })();
36
+ (()=>{
37
+ __webpack_require__.r = (exports1)=>{
38
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
39
+ value: 'Module'
40
+ });
41
+ Object.defineProperty(exports1, '__esModule', {
42
+ value: true
43
+ });
44
+ };
45
+ })();
46
+ var __webpack_exports__ = {};
47
+ (()=>{
48
+ __webpack_require__.r(__webpack_exports__);
49
+ __webpack_require__.d(__webpack_exports__, {
50
+ resolveVueEntry: ()=>resolveVueEntry,
51
+ resolveVueEntryForLoader: ()=>resolveVueEntryForLoader,
52
+ loadBridgeContract: ()=>loadBridgeContract,
53
+ DEFAULT_CONTRACT_ENDPOINT: ()=>DEFAULT_CONTRACT_ENDPOINT,
54
+ DEFAULT_DISCOVERY_ENDPOINT: ()=>DEFAULT_DISCOVERY_ENDPOINT,
55
+ VueLoader: ()=>VueLoader,
56
+ ensureBridgeInit: ()=>ensureBridgeInit,
57
+ DEFAULT_INIT_ENDPOINT: ()=>DEFAULT_INIT_ENDPOINT,
58
+ loadDiscoveryContract: ()=>loadDiscoveryContract
59
+ });
60
+ var external_Vue_ = __webpack_require__("vue");
61
+ const DEV_TO_NAMESPACE = 'dev_to';
62
+ const DEV_TO_BASE_PATH = `/__${DEV_TO_NAMESPACE}__`;
63
+ const DEV_TO_DISCOVERY_PATH = `${DEV_TO_BASE_PATH}/discovery.json`;
64
+ const DEV_TO_DEBUG_HTML_PATH = `${DEV_TO_BASE_PATH}/debug.html`;
65
+ const DEV_TO_VUE_NAMESPACE = 'vue';
66
+ const DEV_TO_VUE_BASE_PATH = `${DEV_TO_BASE_PATH}/${DEV_TO_VUE_NAMESPACE}`;
67
+ const DEV_TO_VUE_CONTRACT_PATH = `${DEV_TO_VUE_BASE_PATH}/contract.js`;
68
+ const DEV_TO_VUE_INIT_PATH = `${DEV_TO_VUE_BASE_PATH}/init.js`;
69
+ function isRecord(value) {
70
+ return 'object' == typeof value && null !== value;
71
+ }
72
+ function unwrapDefault(value) {
73
+ if (!isRecord(value)) return value;
74
+ const defaultValue = value['default'];
75
+ return null != defaultValue ? defaultValue : value;
76
+ }
77
+ function toError(value) {
78
+ return value instanceof Error ? value : new Error(String(value));
79
+ }
80
+ function resolveRemoteRuntime(runtimeModule) {
81
+ const runtimeRecord = isRecord(runtimeModule) ? runtimeModule : {};
82
+ const vueCandidate = runtimeRecord['default'] ?? runtimeRecord['Vue'] ?? runtimeModule;
83
+ if (!isRecord(vueCandidate) || 'function' != typeof vueCandidate['createApp'] || 'function' != typeof vueCandidate['h'] || 'function' != typeof vueCandidate['defineComponent']) throw new Error('Invalid Vue runtime module from dev server.');
84
+ return vueCandidate;
85
+ }
86
+ const DEFAULT_DISCOVERY_ENDPOINT = DEV_TO_DISCOVERY_PATH;
87
+ const DEFAULT_CONTRACT_ENDPOINT = DEV_TO_VUE_CONTRACT_PATH;
88
+ const DEFAULT_INIT_ENDPOINT = DEV_TO_VUE_INIT_PATH;
89
+ function resolveEndpointUrl(origin, endpoint) {
90
+ if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) return endpoint;
91
+ if (endpoint.startsWith('/')) return `${origin}${endpoint}`;
92
+ return `${origin}/${endpoint}`;
93
+ }
94
+ function isBridgeContract(value) {
95
+ if (!isRecord(value)) return false;
96
+ const paths = value['paths'];
97
+ const events = value['events'];
98
+ if (!isRecord(paths) || 'string' != typeof paths['vueRuntime']) return false;
99
+ return !(!isRecord(events) || 'string' != typeof events['fullReload']);
100
+ }
101
+ function resolveContract(initModule) {
102
+ const moduleRecord = isRecord(initModule) ? initModule : {};
103
+ const contractCandidate = moduleRecord['DEV_TO_VUE_CONTRACT'] ?? moduleRecord['default'] ?? null;
104
+ if (!contractCandidate) throw new Error('Dev server contract not found. Please ensure `@dev-to/vue-plugin` (devToVuePlugin) is enabled in the Vite dev server.');
105
+ if (!isBridgeContract(contractCandidate)) throw new Error('Invalid dev server contract.');
106
+ return contractCandidate;
107
+ }
108
+ const nativeImport = new Function('url', 'return import(url)');
109
+ const discoveryCache = new Map();
110
+ const contractCache = new Map();
111
+ const initCache = new Map();
112
+ async function loadDiscoveryContract(origin, discoveryEndpoint) {
113
+ const endpoint = discoveryEndpoint || DEFAULT_DISCOVERY_ENDPOINT;
114
+ const discoveryUrl = resolveEndpointUrl(origin, endpoint);
115
+ if (discoveryCache.has(discoveryUrl)) return discoveryCache.get(discoveryUrl);
116
+ const p = fetch(discoveryUrl).then(async (resp)=>{
117
+ if (!resp.ok) throw new Error(`Discovery endpoint returned ${resp.status}: ${resp.statusText}`);
118
+ const data = await resp.json();
119
+ if (!data || 'object' != typeof data) throw new Error('Invalid discovery response: not a JSON object');
120
+ return data;
121
+ }).catch((e)=>{
122
+ discoveryCache.delete(discoveryUrl);
123
+ throw e instanceof Error ? e : new Error(String(e));
124
+ });
125
+ discoveryCache.set(discoveryUrl, p);
126
+ return p;
127
+ }
128
+ async function loadBridgeContract(origin, contractEndpoint) {
129
+ const endpoint = contractEndpoint || DEFAULT_CONTRACT_ENDPOINT;
130
+ const initUrl = resolveEndpointUrl(origin, endpoint);
131
+ if (contractCache.has(initUrl)) return contractCache.get(initUrl);
132
+ const p = nativeImport(initUrl).then((m)=>resolveContract(m)).catch(async (e)=>{
133
+ contractCache.delete(initUrl);
134
+ const err = e instanceof Error ? e : new Error(String(e));
135
+ if (err.message.includes('Failed to fetch')) try {
136
+ const resp = await fetch(initUrl, {
137
+ method: 'HEAD'
138
+ }).catch(()=>null);
139
+ if (resp) err.statusCode = resp.status;
140
+ } catch {}
141
+ throw err;
142
+ });
143
+ contractCache.set(initUrl, p);
144
+ return p;
145
+ }
146
+ async function ensureBridgeInit(origin, contract) {
147
+ const initPath = contract?.paths?.initClient || DEFAULT_INIT_ENDPOINT;
148
+ const initUrl = resolveEndpointUrl(origin, initPath);
149
+ if (initCache.has(initUrl)) return initCache.get(initUrl);
150
+ const p = nativeImport(initUrl).then(()=>void 0).catch((e)=>{
151
+ initCache.delete(initUrl);
152
+ throw e instanceof Error ? e : new Error(String(e));
153
+ });
154
+ initCache.set(initUrl, p);
155
+ return p;
156
+ }
157
+ async function resolveVueEntry(origin, componentName, contractEndpoint) {
158
+ try {
159
+ const contract = await loadBridgeContract(origin, contractEndpoint);
160
+ const dev = contract?.dev || {};
161
+ const componentMap = dev.componentMap || {};
162
+ const entry = componentMap[componentName] || componentMap['*'];
163
+ if (!entry) {
164
+ const errorMessage = `Component "${componentName}" not found in devComponentMap`;
165
+ const isWildcardMode = !!componentMap['*'];
166
+ const errorReason = isWildcardMode ? `The component "${componentName}" is not configured, and the wildcard fallback is also missing or invalid.` : `The component "${componentName}" is not configured in devComponentMap. Please add it to your Vite configuration.`;
167
+ return {
168
+ success: false,
169
+ error: {
170
+ message: errorMessage,
171
+ type: 'COMPONENT_NOT_FOUND',
172
+ componentName,
173
+ errorReason,
174
+ setupGuide: {
175
+ title: 'Setup Guide',
176
+ steps: [
177
+ "import { devToVuePlugin } from '@dev-to/vue-plugin'",
178
+ '',
179
+ "// Option 1: Shorthand (Default)",
180
+ `devToVuePlugin('${componentName}')`,
181
+ '',
182
+ "// Option 2: Explicit Mapping",
183
+ "devToVuePlugin({",
184
+ ` '${componentName}': '/', // Default entry`,
185
+ " 'Other': 'src/Card.vue' // Specific file",
186
+ "})"
187
+ ]
188
+ }
189
+ }
190
+ };
191
+ }
192
+ const entryUrl = entry.startsWith('http://') || entry.startsWith('https://') ? entry : `${origin}${entry.startsWith('/') ? '' : '/'}${entry}`;
193
+ return {
194
+ success: true,
195
+ entryUrl
196
+ };
197
+ } catch (e) {
198
+ const errorMessage = e instanceof Error ? e.message : String(e);
199
+ return {
200
+ success: false,
201
+ error: {
202
+ message: errorMessage,
203
+ type: 'CONTRACT_LOAD_FAILED'
204
+ }
205
+ };
206
+ }
207
+ }
208
+ async function resolveVueEntryForLoader(origin, componentName, contractEndpoint) {
209
+ const r = await resolveVueEntry(origin, componentName, contractEndpoint);
210
+ if (r.success) return r;
211
+ const e = r.error;
212
+ return {
213
+ success: false,
214
+ loaderError: {
215
+ message: e.message,
216
+ origin,
217
+ componentName: e.componentName || componentName,
218
+ type: e.type,
219
+ errorReason: e.errorReason,
220
+ setupGuide: e.setupGuide
221
+ }
222
+ };
223
+ }
224
+ function safeLocationHref() {
225
+ if ("u" < typeof window) return '';
226
+ try {
227
+ return window.location.href;
228
+ } catch {
229
+ return '';
230
+ }
231
+ }
232
+ function safeOrigin() {
233
+ if ("u" < typeof window) return '';
234
+ try {
235
+ return window.location.origin;
236
+ } catch {
237
+ return '';
238
+ }
239
+ }
240
+ function getOriginFromFinalJsUrl(finalJsUrl) {
241
+ let origin = '';
242
+ try {
243
+ const urlParam = finalJsUrl.match(/url=([^&]+)/)?.[1] || '';
244
+ const decoded = urlParam ? decodeURIComponent(urlParam) : finalJsUrl;
245
+ const parsed = decoded.startsWith('http') ? new URL(decoded) : new URL(decoded, safeLocationHref() || 'http://localhost');
246
+ origin = parsed.origin;
247
+ } catch {
248
+ origin = safeOrigin();
249
+ }
250
+ return origin;
251
+ }
252
+ function syncProps(target, next) {
253
+ for (const key of Object.keys(target))if (!(key in next)) delete target[key];
254
+ for (const [key, value] of Object.entries(next))target[key] = value;
255
+ }
256
+ const VueLoader = (0, external_Vue_.defineComponent)({
257
+ name: 'VueLoader',
258
+ props: {
259
+ origin: {
260
+ type: String,
261
+ default: void 0
262
+ },
263
+ name: {
264
+ type: String,
265
+ default: void 0
266
+ },
267
+ url: {
268
+ type: String,
269
+ default: void 0
270
+ },
271
+ contractEndpoint: {
272
+ type: String,
273
+ default: void 0
274
+ },
275
+ componentProps: {
276
+ type: Object,
277
+ default: ()=>({})
278
+ },
279
+ loading: {
280
+ type: Function,
281
+ default: void 0
282
+ },
283
+ externalError: {
284
+ type: Object,
285
+ default: null
286
+ },
287
+ renderError: {
288
+ type: Function,
289
+ default: void 0
290
+ }
291
+ },
292
+ setup (props) {
293
+ const containerRef = (0, external_Vue_.ref)(null);
294
+ const appRef = (0, external_Vue_.ref)(null);
295
+ const runtimeRef = (0, external_Vue_.ref)(null);
296
+ const componentRef = (0, external_Vue_.ref)(null);
297
+ const propsStateRef = (0, external_Vue_.ref)(null);
298
+ const state = (0, external_Vue_.ref)({
299
+ isReady: false,
300
+ error: null
301
+ });
302
+ const version = (0, external_Vue_.ref)(0);
303
+ const resolvedUrl = (0, external_Vue_.ref)(props.url);
304
+ const fullReloadListenerRef = (0, external_Vue_.ref)(null);
305
+ const ensureFullReloadListener = (eventName)=>{
306
+ if (!eventName || "u" < typeof window) return;
307
+ const current = fullReloadListenerRef.value;
308
+ if (current?.eventName === eventName) return;
309
+ if (current) window.removeEventListener(current.eventName, current.handler);
310
+ const handler = ()=>{
311
+ version.value += 1;
312
+ };
313
+ window.addEventListener(eventName, handler);
314
+ fullReloadListenerRef.value = {
315
+ eventName,
316
+ handler
317
+ };
318
+ };
319
+ const unmountRemoteApp = ()=>{
320
+ try {
321
+ appRef.value?.unmount?.();
322
+ } catch {}
323
+ appRef.value = null;
324
+ propsStateRef.value = null;
325
+ };
326
+ const renderRemoteApp = (force = false)=>{
327
+ const runtime = runtimeRef.value;
328
+ const ComponentRef = componentRef.value;
329
+ if (!runtime || !ComponentRef) return;
330
+ if (!containerRef.value) return;
331
+ if (appRef.value && !force) return;
332
+ unmountRemoteApp();
333
+ const propsState = runtime.reactive({});
334
+ syncProps(propsState, props.componentProps ?? {});
335
+ const Wrapper = runtime.defineComponent({
336
+ name: 'DevToVueWrapper',
337
+ setup () {
338
+ return ()=>runtime.h(ComponentRef, propsState);
339
+ }
340
+ });
341
+ const app = runtime.createApp(Wrapper);
342
+ app.mount(containerRef.value);
343
+ appRef.value = app;
344
+ propsStateRef.value = propsState;
345
+ state.value.isReady = true;
346
+ };
347
+ const loadViteComponent = async (entryUrl, exportName, currentVersion)=>{
348
+ if (!entryUrl) throw new Error('Not found entry url.');
349
+ const originFromUrl = getOriginFromFinalJsUrl(entryUrl);
350
+ const connector = entryUrl.includes('?') ? '&' : '?';
351
+ const jsUrlWithParam = currentVersion > 0 ? `${entryUrl}${connector}v=${currentVersion}` : entryUrl;
352
+ try {
353
+ const contract = await loadBridgeContract(originFromUrl, props.contractEndpoint);
354
+ await ensureBridgeInit(originFromUrl, contract);
355
+ ensureFullReloadListener(contract.events.fullReload);
356
+ const runtimeModule = await nativeImport(`${originFromUrl}${contract.paths.vueRuntime}`);
357
+ const runtime = resolveRemoteRuntime(runtimeModule);
358
+ const moduleNs = await nativeImport(jsUrlWithParam);
359
+ const moduleRecord = isRecord(moduleNs) ? moduleNs : {};
360
+ const exportCandidate = exportName ? moduleRecord[exportName] : void 0;
361
+ const candidate = exportName ? exportCandidate ?? moduleRecord['default'] ?? moduleNs : moduleRecord['default'] ?? moduleNs;
362
+ const ComponentValue = unwrapDefault(candidate);
363
+ if (ComponentValue) return {
364
+ Component: ComponentValue,
365
+ runtime
366
+ };
367
+ throw new Error('Vite Dev Component Load Fail: Component not found in module.');
368
+ } catch (e) {
369
+ const err = toError(e);
370
+ if (err.message.includes('Failed to fetch')) try {
371
+ const resp = await fetch(jsUrlWithParam, {
372
+ method: 'HEAD'
373
+ }).catch(()=>null);
374
+ if (resp) err.statusCode = resp.status;
375
+ } catch {}
376
+ throw err;
377
+ }
378
+ };
379
+ (0, external_Vue_.watch)(()=>[
380
+ props.url,
381
+ props.origin,
382
+ props.name,
383
+ props.contractEndpoint
384
+ ], (values, _old, onInvalidate)=>{
385
+ const [directUrl, origin, name, contractEndpoint] = values;
386
+ let cancelled = false;
387
+ onInvalidate(()=>{
388
+ cancelled = true;
389
+ });
390
+ if (directUrl) {
391
+ resolvedUrl.value = directUrl;
392
+ state.value.error = null;
393
+ return;
394
+ }
395
+ if (!origin || !name) {
396
+ if (!directUrl && (origin || name)) state.value.error = new Error('Missing dev server origin or component name for resolution.');
397
+ return;
398
+ }
399
+ state.value.isReady = false;
400
+ state.value.error = null;
401
+ resolveVueEntryForLoader(origin, name, contractEndpoint).then((res)=>{
402
+ if (cancelled) return;
403
+ if (true === res.success) resolvedUrl.value = res.entryUrl;
404
+ else state.value.error = res.loaderError;
405
+ });
406
+ }, {
407
+ immediate: true
408
+ });
409
+ (0, external_Vue_.watch)(()=>[
410
+ resolvedUrl.value,
411
+ props.name,
412
+ version.value
413
+ ], (values, _old, onInvalidate)=>{
414
+ const [url, name, currentVersion] = values;
415
+ if (!url) return;
416
+ let cancelled = false;
417
+ onInvalidate(()=>{
418
+ cancelled = true;
419
+ });
420
+ state.value.isReady = false;
421
+ if (state.value.error instanceof Error) state.value.error = null;
422
+ loadViteComponent(url, name, currentVersion).then(({ Component, runtime })=>{
423
+ if (cancelled) return;
424
+ runtimeRef.value = runtime;
425
+ componentRef.value = Component;
426
+ renderRemoteApp(true);
427
+ }).catch((err)=>{
428
+ if (cancelled) return;
429
+ state.value.error = err instanceof Error ? err : new Error(String(err));
430
+ state.value.isReady = false;
431
+ unmountRemoteApp();
432
+ });
433
+ }, {
434
+ immediate: true
435
+ });
436
+ (0, external_Vue_.watch)(()=>props.componentProps, (next)=>{
437
+ if (!propsStateRef.value) return;
438
+ syncProps(propsStateRef.value, next || {});
439
+ }, {
440
+ deep: true
441
+ });
442
+ (0, external_Vue_.onMounted)(()=>{
443
+ renderRemoteApp(false);
444
+ });
445
+ (0, external_Vue_.onBeforeUnmount)(()=>{
446
+ unmountRemoteApp();
447
+ if ("u" < typeof window) return;
448
+ const current = fullReloadListenerRef.value;
449
+ if (current) window.removeEventListener(current.eventName, current.handler);
450
+ });
451
+ const renderErrorNode = (err)=>{
452
+ if (props.renderError) return props.renderError(err);
453
+ const isLoaderErrorObject = !(err instanceof Error);
454
+ const message = err.message || String(err);
455
+ const currentOrigin = (isLoaderErrorObject ? err.origin : void 0) || props.origin || (props.url ? getOriginFromFinalJsUrl(props.url) : '');
456
+ const debugPanelUrl = `${currentOrigin}${DEV_TO_DEBUG_HTML_PATH}`;
457
+ return (0, external_Vue_.h)('div', {
458
+ style: {
459
+ border: '1px solid #e5e7eb',
460
+ padding: '10px',
461
+ borderRadius: '6px'
462
+ }
463
+ }, [
464
+ (0, external_Vue_.h)('div', {
465
+ style: {
466
+ fontWeight: '600',
467
+ marginBottom: '6px'
468
+ }
469
+ }, 'DevTo Vue Loader Error'),
470
+ (0, external_Vue_.h)('pre', {
471
+ style: {
472
+ fontSize: '12px',
473
+ whiteSpace: 'pre-wrap'
474
+ }
475
+ }, message),
476
+ currentOrigin ? (0, external_Vue_.h)('div', {
477
+ style: {
478
+ fontSize: '12px',
479
+ marginTop: '6px'
480
+ }
481
+ }, [
482
+ (0, external_Vue_.h)('span', 'Origin: '),
483
+ (0, external_Vue_.h)('code', currentOrigin)
484
+ ]) : null,
485
+ currentOrigin ? (0, external_Vue_.h)('div', {
486
+ style: {
487
+ fontSize: '12px',
488
+ marginTop: '6px'
489
+ }
490
+ }, [
491
+ (0, external_Vue_.h)('a', {
492
+ href: debugPanelUrl,
493
+ target: '_blank',
494
+ rel: 'noreferrer'
495
+ }, debugPanelUrl)
496
+ ]) : null
497
+ ]);
498
+ };
499
+ return ()=>{
500
+ if (props.externalError) return renderErrorNode(props.externalError);
501
+ if (state.value.error) return renderErrorNode(state.value.error);
502
+ const children = [
503
+ (0, external_Vue_.h)('div', {
504
+ ref: containerRef,
505
+ class: 'vdev-loader-container',
506
+ 'data-is': 'VueLoader'
507
+ })
508
+ ];
509
+ if (!state.value.isReady) {
510
+ const loadingNode = props.loading ? props.loading() : (0, external_Vue_.h)('div', 'Loading...');
511
+ children.push(loadingNode);
512
+ }
513
+ return (0, external_Vue_.h)('div', {
514
+ class: 'vdev-loader-root'
515
+ }, children);
516
+ };
517
+ }
518
+ });
519
+ })();
520
+ return __webpack_exports__;
521
+ })());
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@dev-to/vue-loader",
3
+ "version": "0.2.2",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./umd": "./dist/index.umd.js"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "peerDependencies": {
19
+ "typescript": ">=5.0.0",
20
+ "vue": ">=3.0.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/YangYongAn/dev-to.git",
25
+ "directory": "packages/vue-loader"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@dev-to/shared": "1.0.4"
32
+ },
33
+ "scripts": {
34
+ "build": "rslib build",
35
+ "dev": "rslib build --watch",
36
+ "lint": "pnpm -w lint",
37
+ "test": "echo 'test @dev-to/vue-loader'"
38
+ }
39
+ }