mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
feat(shared-uload): add shared package with ShareModal and cross-app link creation
Provides ShareModal component and initSharedUload/createShortLink utilities for other apps to create uLoad short links with source tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
98ca01f466
commit
8050da31ea
7 changed files with 506 additions and 0 deletions
31
packages/shared-uload/package.json
Normal file
31
packages/shared-uload/package.json
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "@manacore/shared-uload",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"svelte": "./src/index.ts",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"svelte": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"default": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@manacore/local-store": "workspace:*",
|
||||||
|
"@manacore/shared-icons": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
244
packages/shared-uload/src/ShareModal.svelte
Normal file
244
packages/shared-uload/src/ShareModal.svelte
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { X, Copy, QrCode, Link, ArrowSquareOut } from '@manacore/shared-icons';
|
||||||
|
import type { CreatedLink, CreateShortLinkOptions } from './types';
|
||||||
|
import { createShortLink, isSharedUloadReady, getBaseUrl } from './create-link';
|
||||||
|
import { getQrCodeUrl, getShortUrl, downloadQrCode } from './utils';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
url: string;
|
||||||
|
title?: string;
|
||||||
|
source: string;
|
||||||
|
description?: string;
|
||||||
|
onCreated?: (link: CreatedLink) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { visible, onClose, url, title = '', source, description = '', onCreated }: Props = $props();
|
||||||
|
|
||||||
|
let customCode = $state('');
|
||||||
|
let useCustomCode = $state(false);
|
||||||
|
let createdLink = $state<CreatedLink | null>(null);
|
||||||
|
let creating = $state(false);
|
||||||
|
let error = $state('');
|
||||||
|
let copied = $state(false);
|
||||||
|
let showQr = $state(false);
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
customCode = '';
|
||||||
|
useCustomCode = false;
|
||||||
|
createdLink = null;
|
||||||
|
creating = false;
|
||||||
|
error = '';
|
||||||
|
copied = false;
|
||||||
|
showQr = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
reset();
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCreate() {
|
||||||
|
if (!isSharedUloadReady()) {
|
||||||
|
error = 'uLoad ist nicht initialisiert';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
creating = true;
|
||||||
|
error = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options: CreateShortLinkOptions = {
|
||||||
|
url,
|
||||||
|
title: title || undefined,
|
||||||
|
source,
|
||||||
|
description: description || undefined,
|
||||||
|
customCode: useCustomCode && customCode ? customCode : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const link = await createShortLink(options);
|
||||||
|
createdLink = link;
|
||||||
|
onCreated?.(link);
|
||||||
|
} catch (e) {
|
||||||
|
error = e instanceof Error ? e.message : 'Fehler beim Erstellen';
|
||||||
|
} finally {
|
||||||
|
creating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyToClipboard(text: string) {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
copied = true;
|
||||||
|
setTimeout(() => (copied = false), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Escape' && visible) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBackdropClick(e: MouseEvent) {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window onkeydown={handleKeydown} />
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
|
||||||
|
style="z-index: 9990;"
|
||||||
|
onclick={handleBackdropClick}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
|
<div
|
||||||
|
class="w-full max-w-md rounded-2xl border border-white/10 bg-gray-900 shadow-2xl"
|
||||||
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2.5">
|
||||||
|
<Link size={18} class="text-indigo-400" />
|
||||||
|
<h3 class="text-base font-semibold text-white">Kurzlink erstellen</h3>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onclick={handleClose}
|
||||||
|
class="rounded-lg p-1.5 text-gray-400 hover:bg-white/10 hover:text-white transition-colors"
|
||||||
|
aria-label="Schliessen"
|
||||||
|
>
|
||||||
|
<X size={18} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-5">
|
||||||
|
{#if !createdLink}
|
||||||
|
<!-- Create State -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- URL Preview -->
|
||||||
|
<div class="rounded-lg bg-white/5 px-3 py-2.5">
|
||||||
|
<p class="text-xs text-gray-400 mb-1">Ziel-URL</p>
|
||||||
|
<p class="text-sm text-white truncate">{url}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if title}
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs text-gray-400 mb-1">Titel</label>
|
||||||
|
<p class="text-sm text-white">{title}</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Custom Code Toggle -->
|
||||||
|
<div>
|
||||||
|
<label class="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={useCustomCode}
|
||||||
|
class="rounded border-gray-600 bg-gray-800 text-indigo-500 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
|
<span class="text-sm text-gray-300">Eigenen Kurzcode verwenden</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{#if useCustomCode}
|
||||||
|
<div class="mt-2 flex items-center gap-2">
|
||||||
|
<span class="text-sm text-gray-500">ulo.ad/</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={customCode}
|
||||||
|
placeholder="mein-code"
|
||||||
|
class="flex-1 rounded-lg border border-white/10 bg-white/5 px-3 py-2 text-sm text-white placeholder-gray-500 focus:border-indigo-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<p class="text-sm text-red-400">{error}</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onclick={handleCreate}
|
||||||
|
disabled={creating}
|
||||||
|
class="w-full rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-indigo-500 disabled:opacity-50 transition-colors"
|
||||||
|
>
|
||||||
|
{creating ? 'Erstelle...' : 'Kurzlink erstellen'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<!-- Created State -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div
|
||||||
|
class="rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-4 py-3 text-center"
|
||||||
|
>
|
||||||
|
<p class="text-xs text-emerald-400 mb-1">Kurzlink erstellt</p>
|
||||||
|
<p class="font-mono text-lg font-semibold text-emerald-300">
|
||||||
|
{createdLink.shortUrl}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if showQr}
|
||||||
|
<div class="flex flex-col items-center gap-3">
|
||||||
|
<div class="rounded-xl bg-white p-3">
|
||||||
|
<img
|
||||||
|
src={getQrCodeUrl(createdLink.shortUrl, 200)}
|
||||||
|
alt="QR Code für {createdLink.shortCode}"
|
||||||
|
class="h-44 w-44"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onclick={() => downloadQrCode(createdLink!.shortCode, getBaseUrl())}
|
||||||
|
class="text-sm text-indigo-400 hover:text-indigo-300 transition-colors"
|
||||||
|
>
|
||||||
|
QR herunterladen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
onclick={() => copyToClipboard(createdLink!.shortUrl)}
|
||||||
|
class="flex flex-1 items-center justify-center gap-2 rounded-lg border border-white/10 px-3 py-2.5 text-sm font-medium text-white hover:bg-white/5 transition-colors"
|
||||||
|
>
|
||||||
|
<Copy size={16} />
|
||||||
|
{copied ? 'Kopiert!' : 'Kopieren'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={() => (showQr = !showQr)}
|
||||||
|
class="flex items-center justify-center gap-2 rounded-lg border border-white/10 px-3 py-2.5 text-sm font-medium text-white hover:bg-white/5 transition-colors {showQr
|
||||||
|
? 'bg-white/10'
|
||||||
|
: ''}"
|
||||||
|
>
|
||||||
|
<QrCode size={16} />
|
||||||
|
QR
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="/my/links"
|
||||||
|
target="_blank"
|
||||||
|
class="flex items-center justify-center gap-2 rounded-lg border border-white/10 px-3 py-2.5 text-sm font-medium text-white hover:bg-white/5 transition-colors"
|
||||||
|
title="In uLoad öffnen"
|
||||||
|
>
|
||||||
|
<ArrowSquareOut size={16} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer: Source badge -->
|
||||||
|
<div class="border-t border-white/10 px-5 py-3">
|
||||||
|
<p class="text-xs text-gray-500">
|
||||||
|
Erstellt via <span class="text-gray-400">{source}</span> · Sichtbar in uLoad
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
106
packages/shared-uload/src/create-link.ts
Normal file
106
packages/shared-uload/src/create-link.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { createLocalStore, type LocalCollection } from '@manacore/local-store';
|
||||||
|
import type { UloadLink, CreateShortLinkOptions, CreatedLink } from './types';
|
||||||
|
import { generateShortCode, getShortUrl, getQrCodeUrl } from './utils';
|
||||||
|
|
||||||
|
let _linkCollection: LocalCollection<UloadLink> | null = null;
|
||||||
|
let _store: ReturnType<typeof createLocalStore> | null = null;
|
||||||
|
let _baseUrl: string | undefined;
|
||||||
|
let _initPromise: Promise<void> | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the shared uLoad utility.
|
||||||
|
*
|
||||||
|
* Option A: Pass an existing linkCollection (from inside uLoad's own app).
|
||||||
|
* Option B: Call with no collection — it opens the uLoad IndexedDB directly (from any other app).
|
||||||
|
*/
|
||||||
|
export function initSharedUload(
|
||||||
|
linkCollectionOrOptions?: LocalCollection<UloadLink> | { baseUrl?: string },
|
||||||
|
options?: { baseUrl?: string }
|
||||||
|
): void {
|
||||||
|
if (linkCollectionOrOptions && 'insert' in linkCollectionOrOptions) {
|
||||||
|
// Option A: Existing collection
|
||||||
|
_linkCollection = linkCollectionOrOptions;
|
||||||
|
_baseUrl = options?.baseUrl;
|
||||||
|
} else {
|
||||||
|
// Option B: Self-initialize by opening the uLoad database
|
||||||
|
const opts = linkCollectionOrOptions as { baseUrl?: string } | undefined;
|
||||||
|
_baseUrl = opts?.baseUrl;
|
||||||
|
|
||||||
|
_store = createLocalStore({
|
||||||
|
appId: 'uload',
|
||||||
|
collections: [
|
||||||
|
{
|
||||||
|
name: 'links',
|
||||||
|
indexes: [
|
||||||
|
'shortCode',
|
||||||
|
'isActive',
|
||||||
|
'folderId',
|
||||||
|
'order',
|
||||||
|
'clickCount',
|
||||||
|
'source',
|
||||||
|
'[folderId+order]',
|
||||||
|
'[isActive+order]',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
_initPromise = _store.initialize().then(() => {
|
||||||
|
_linkCollection = _store!.collection<UloadLink>('links');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureReady(): Promise<LocalCollection<UloadLink>> {
|
||||||
|
if (_initPromise) {
|
||||||
|
await _initPromise;
|
||||||
|
}
|
||||||
|
if (!_linkCollection) {
|
||||||
|
throw new Error(
|
||||||
|
'@manacore/shared-uload not initialized. Call initSharedUload() in your app layout.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _linkCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a short link from any app.
|
||||||
|
* The link is inserted into the uLoad local-store and syncs automatically.
|
||||||
|
*/
|
||||||
|
export async function createShortLink(options: CreateShortLinkOptions): Promise<CreatedLink> {
|
||||||
|
const collection = await ensureReady();
|
||||||
|
|
||||||
|
const shortCode = options.customCode || generateShortCode();
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
const shortUrl = getShortUrl(shortCode, _baseUrl);
|
||||||
|
const qrCodeUrl = getQrCodeUrl(shortUrl);
|
||||||
|
|
||||||
|
await collection.insert({
|
||||||
|
id,
|
||||||
|
shortCode,
|
||||||
|
customCode: options.customCode || null,
|
||||||
|
originalUrl: options.url,
|
||||||
|
title: options.title || null,
|
||||||
|
description: options.description || null,
|
||||||
|
isActive: true,
|
||||||
|
clickCount: 0,
|
||||||
|
folderId: null,
|
||||||
|
order: 0,
|
||||||
|
source: options.source,
|
||||||
|
expiresAt: options.expiresAt || null,
|
||||||
|
qrCodeUrl,
|
||||||
|
} as UloadLink);
|
||||||
|
|
||||||
|
return { id, shortCode, shortUrl, qrCodeUrl };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if shared-uload has been initialized.
|
||||||
|
*/
|
||||||
|
export function isSharedUloadReady(): boolean {
|
||||||
|
return _linkCollection !== null || _initPromise !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBaseUrl(): string | undefined {
|
||||||
|
return _baseUrl;
|
||||||
|
}
|
||||||
12
packages/shared-uload/src/index.ts
Normal file
12
packages/shared-uload/src/index.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Types
|
||||||
|
export type { UloadLink, CreateShortLinkOptions, CreatedLink, AppSource } from './types';
|
||||||
|
export { APP_SOURCE_LABELS } from './types';
|
||||||
|
|
||||||
|
// Core API
|
||||||
|
export { initSharedUload, createShortLink, isSharedUloadReady } from './create-link';
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
export { generateShortCode, getQrCodeUrl, getShortUrl, downloadQrCode, QR_API } from './utils';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
export { default as ShareModal } from './ShareModal.svelte';
|
||||||
66
packages/shared-uload/src/types.ts
Normal file
66
packages/shared-uload/src/types.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import type { BaseRecord } from '@manacore/local-store';
|
||||||
|
|
||||||
|
export interface UloadLink extends BaseRecord {
|
||||||
|
shortCode: string;
|
||||||
|
customCode?: string | null;
|
||||||
|
originalUrl: string;
|
||||||
|
title?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
isActive: boolean;
|
||||||
|
password?: string | null;
|
||||||
|
maxClicks?: number | null;
|
||||||
|
expiresAt?: string | null;
|
||||||
|
clickCount: number;
|
||||||
|
qrCodeUrl?: string | null;
|
||||||
|
utmSource?: string | null;
|
||||||
|
utmMedium?: string | null;
|
||||||
|
utmCampaign?: string | null;
|
||||||
|
folderId?: string | null;
|
||||||
|
order: number;
|
||||||
|
source?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateShortLinkOptions {
|
||||||
|
url: string;
|
||||||
|
title?: string;
|
||||||
|
customCode?: string;
|
||||||
|
source: string;
|
||||||
|
tags?: string[];
|
||||||
|
expiresAt?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatedLink {
|
||||||
|
id: string;
|
||||||
|
shortCode: string;
|
||||||
|
shortUrl: string;
|
||||||
|
qrCodeUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppSource =
|
||||||
|
| 'calendar'
|
||||||
|
| 'contacts'
|
||||||
|
| 'todo'
|
||||||
|
| 'chat'
|
||||||
|
| 'storage'
|
||||||
|
| 'presi'
|
||||||
|
| 'mukke'
|
||||||
|
| 'cards'
|
||||||
|
| 'picture'
|
||||||
|
| 'uload'
|
||||||
|
| 'manacore'
|
||||||
|
| (string & {});
|
||||||
|
|
||||||
|
export const APP_SOURCE_LABELS: Record<string, string> = {
|
||||||
|
calendar: 'Kalender',
|
||||||
|
contacts: 'Kontakte',
|
||||||
|
todo: 'Todo',
|
||||||
|
chat: 'Chat',
|
||||||
|
storage: 'Storage',
|
||||||
|
presi: 'Presi',
|
||||||
|
mukke: 'Mukke',
|
||||||
|
cards: 'Cards',
|
||||||
|
picture: 'Picture',
|
||||||
|
uload: 'uLoad',
|
||||||
|
manacore: 'ManaCore',
|
||||||
|
};
|
||||||
29
packages/shared-uload/src/utils.ts
Normal file
29
packages/shared-uload/src/utils.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
export const QR_API = 'https://api.qrserver.com/v1/create-qr-code';
|
||||||
|
|
||||||
|
export function generateShortCode(length = 6): string {
|
||||||
|
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let code = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
code += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQrCodeUrl(shortUrl: string, size = 400): string {
|
||||||
|
return `${QR_API}/?size=${size}x${size}&data=${encodeURIComponent(shortUrl)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShortUrl(shortCode: string, baseUrl?: string): string {
|
||||||
|
const base =
|
||||||
|
baseUrl || (typeof window !== 'undefined' ? window.location.origin : 'https://ulo.ad');
|
||||||
|
return `${base}/${shortCode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function downloadQrCode(shortCode: string, baseUrl?: string): void {
|
||||||
|
const shortUrl = getShortUrl(shortCode, baseUrl);
|
||||||
|
const url = getQrCodeUrl(shortUrl, 400);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `qr-${shortCode}.png`;
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
18
packages/shared-uload/tsconfig.json
Normal file
18
packages/shared-uload/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["svelte"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue