diff --git a/package.json b/package.json index df1eee2..b7a1c12 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "skate-spots", "private": true, - "version": "1.0.66", + "version": "1.0.67", "type": "module", "homepage": "https://skate-spots.com", "scripts": { diff --git a/src/components/CameraCapture.tsx b/src/components/CameraCapture.tsx index 28b4eb4..179f30a 100644 --- a/src/components/CameraCapture.tsx +++ b/src/components/CameraCapture.tsx @@ -42,11 +42,34 @@ async function isNative(): Promise { } } +// The Camera plugin is provided by Capacitor at runtime on native. We access it +// through the runtime-injected `window.Capacitor.Plugins.Camera` rather than a +// static/dynamic `import('@capacitor/camera')`, so the bundle contains no bare +// `@capacitor/camera` specifier (which a native WebView can't resolve, and which +// isn't installed on every build machine). CameraResultType/CameraSource are +// plain string enums in the plugin, so we pass their string values directly. +interface CapacitorCameraPlugin { + getPhoto(options: { + resultType: string; + source: string; + quality: number; + correctOrientation: boolean; + }): Promise<{ dataUrl?: string }>; +} + +function getNativeCamera(): CapacitorCameraPlugin | null { + const cap = (window as unknown as { + Capacitor?: { Plugins?: { Camera?: CapacitorCameraPlugin } }; + }).Capacitor; + return cap?.Plugins?.Camera ?? null; +} + async function captureNative(): Promise { - const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera'); + const Camera = getNativeCamera(); + if (!Camera) throw new Error('Camera plugin unavailable'); const photo = await Camera.getPhoto({ - resultType: CameraResultType.DataUrl, - source: CameraSource.Camera, + resultType: 'dataUrl', // CameraResultType.DataUrl + source: 'CAMERA', // CameraSource.Camera quality: 90, correctOrientation: true, }); diff --git a/vite.config.ts b/vite.config.ts index 443af0b..15adf71 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -44,22 +44,26 @@ export default defineConfig(() => ({ transformMixedEsModules: true, }, rollupOptions: { - // IMPORTANT: @capacitor/core and @capacitor/camera MUST be bundled (they are - // installed, statically imported, and their JS API layer is real code the - // WebView needs). Leaving them external emitted a bare `import "@capacitor/core"` - // specifier that a native WebView cannot resolve → white-screen boot crash. - // They are therefore intentionally ABSENT from this list. + // Bundling policy — the distinction that matters is STATIC vs DYNAMIC import, + // and whether the package resolves on the build machine: // - // Kept external: - // - nostr-signer-capacitor-plugin: installed from GitHub with a broken package - // entry point, so it CANNOT be bundled. It's only ever used via a guarded - // dynamic import() at runtime on native (see lib/capacitorAmberSigner.ts, - // wrapped in try/catch → returns null on failure), so external is correct - // and safe — a resolution failure degrades gracefully instead of crashing. - // - @capacitor/app|haptics|keyboard|status-bar: NOT installed and never - // statically imported (accessed via window.Capacitor.Plugins), so keeping - // them external prevents a stray import from breaking the build. + // BUNDLED (absent from this list): + // - @capacitor/core — imported STATICALLY (lib/platform.ts). It MUST be + // bundled: leaving it external emitted a bare `import "@capacitor/core"` + // specifier the native WebView can't resolve → white-screen boot crash. + // Capacitor guarantees core is installed, so bundling always resolves. + // + // EXTERNAL (listed below): + // - @capacitor/camera — used only via a GUARDED dynamic import() on native + // (components/CameraCapture.tsx). It may not be installed on every build + // machine, and it's optional/native-only, so it must stay external. The + // dynamic import() remains a runtime call that degrades gracefully. + // - nostr-signer-capacitor-plugin — installed from GitHub with a broken + // package entry point; cannot be bundled. Guarded dynamic import() only. + // - @capacitor/app|haptics|keyboard|status-bar — NOT installed and never + // statically imported (accessed via window.Capacitor.Plugins at runtime). external: [ + '@capacitor/camera', 'nostr-signer-capacitor-plugin', '@capacitor/app', '@capacitor/haptics',