Browse Source

Move remaining strings for Environment & Platform to type-checked values.

pull/146/head
Trent Larson 3 weeks ago
parent
commit
2bd191e255
  1. 0
      .env.development
  2. 0
      .env.production
  3. 0
      .env.testing
  4. 12
      index.html
  5. 4
      package.json
  6. 13
      src/electron/preload.js
  7. 21
      src/interfaces/build.ts
  8. 6
      src/main.web.ts
  9. 5
      src/registerServiceWorker.ts
  10. 11
      src/services/PlatformServiceFactory.ts
  11. 3
      src/services/api.ts
  12. 13
      src/utils/logger.ts
  13. 3
      src/views/DeepLinkRedirectView.vue
  14. 2
      tsconfig.node.json
  15. 3
      vite.config.capacitor.mts
  16. 47
      vite.config.common.mts
  17. 3
      vite.config.electron.mts
  18. 3
      vite.config.pywebview.mts
  19. 3
      vite.config.web.mts

0
.env.dev → .env.development

0
.env.prod → .env.production

0
.env.test → .env.testing

12
index.html

@ -15,17 +15,21 @@
<script type="module"> <script type="module">
const platform = process.env.VITE_PLATFORM; const platform = process.env.VITE_PLATFORM;
switch (platform) { switch (platform) {
case 'capacitor': case 'capacitor': // BuildPlatform.Capacitor
import('./src/main.capacitor.ts'); import('./src/main.capacitor.ts');
break; break;
case 'electron': case 'electron': // BuildPlatform.Electron
import('./src/main.electron.ts'); import('./src/main.electron.ts');
break; break;
case 'pywebview': case 'pywebview': // BuildPlatform.PyWebView
import('./src/main.pywebview.ts'); import('./src/main.pywebview.ts');
break; break;
default: case 'web': // BuildPlatform.Web
import('./src/main.web.ts'); import('./src/main.web.ts');
break;
default:
console.error(`Unknown platform: ${platform}`);
throw new Error(`Unknown platform: ${platform}`);
} }
</script> </script>
</body> </body>

4
package.json

@ -12,10 +12,10 @@
"build:electron": "npm run clean:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js", "build:electron": "npm run clean:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
"build:electron-linux": "npm run build:electron && electron-builder --linux AppImage", "build:electron-linux": "npm run build:electron && electron-builder --linux AppImage",
"build:electron-linux-deb": "npm run build:electron && electron-builder --linux deb", "build:electron-linux-deb": "npm run build:electron && electron-builder --linux deb",
"build:electron-linux-prod": "NODE_ENV=production npm run build:electron && electron-builder --linux AppImage", "build:electron-linux-prod": "NODE_ENV=prod npm run build:electron && electron-builder --linux AppImage",
"build:electron-mac": "npm run build:electron-prod && electron-builder --mac", "build:electron-mac": "npm run build:electron-prod && electron-builder --mac",
"build:electron-mac-universal": "npm run build:electron-prod && electron-builder --mac --universal", "build:electron-mac-universal": "npm run build:electron-prod && electron-builder --mac --universal",
"build:electron-prod": "NODE_ENV=production npm run build:electron", "build:electron-prod": "NODE_ENV=prod npm run build:electron",
"build:pywebview": "vite build --config vite.config.pywebview.mts", "build:pywebview": "vite build --config vite.config.pywebview.mts",
"build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts", "build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts",
"check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)", "check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)",

13
src/electron/preload.js

@ -1,9 +1,10 @@
const { contextBridge, ipcRenderer } = require("electron"); const { contextBridge, ipcRenderer } = require("electron");
import { NodeEnv, BuildPlatform } from "@/interfaces/build";
const logger = { const logger = {
log: (message, ...args) => { log: (message, ...args) => {
// Always log in development, log with context in production // Always log in development, log with context in production
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== NodeEnv.Prod) {
/* eslint-disable no-console */ /* eslint-disable no-console */
console.log(`[Preload] ${message}`, ...args); console.log(`[Preload] ${message}`, ...args);
/* eslint-enable no-console */ /* eslint-enable no-console */
@ -23,7 +24,7 @@ const logger = {
}, },
info: (message, ...args) => { info: (message, ...args) => {
// Always log info in development, log with context in production // Always log info in development, log with context in production
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== NodeEnv.Prod) {
/* eslint-disable no-console */ /* eslint-disable no-console */
console.info(`[Preload] ${message}`, ...args); console.info(`[Preload] ${message}`, ...args);
/* eslint-enable no-console */ /* eslint-enable no-console */
@ -53,7 +54,7 @@ const getPath = (pathType) => {
logger.info("Preload script starting..."); logger.info("Preload script starting...");
// Force electron platform in the renderer process // Force electron platform in the renderer process
window.process = { env: { VITE_PLATFORM: "electron" } }; window.process = { env: { VITE_PLATFORM: BuildPlatform.Electron } };
try { try {
contextBridge.exposeInMainWorld("electronAPI", { contextBridge.exposeInMainWorld("electronAPI", {
@ -76,12 +77,12 @@ try {
// Environment info // Environment info
env: { env: {
isElectron: true, isElectron: true,
isDev: process.env.NODE_ENV === "development", isDev: process.env.NODE_ENV === NodeEnv.Dev,
platform: "electron", // Explicitly set platform platform: BuildPlatform.Electron, // Explicitly set platform
}, },
// Path utilities // Path utilities
getBasePath: () => { getBasePath: () => {
return process.env.NODE_ENV === "development" ? "/" : "./"; return process.env.NODE_ENV === NodeEnv.Dev ? "/" : "./";
}, },
}); });

21
src/interfaces/build.ts

@ -0,0 +1,21 @@
export const NodeEnv = {
Dev: "dev",
Test: "test",
Prod: "prod",
} as const;
export type NodeEnv = typeof NodeEnv[keyof typeof NodeEnv];
export const BuildEnv = {
Development: "development",
Testing: "testing",
Production: "production",
} as const;
export type BuildEnv = typeof BuildEnv[keyof typeof BuildEnv];
export const BuildPlatform = {
Web: "web",
Electron: "electron",
Capacitor: "capacitor",
PyWebView: "pywebview",
} as const;
export type BuildPlatform = typeof BuildPlatform[keyof typeof BuildPlatform];

6
src/main.web.ts

@ -1,12 +1,14 @@
import { initBackend } from "absurd-sql/dist/indexeddb-main-thread"; import { initBackend } from "absurd-sql/dist/indexeddb-main-thread";
import { initializeApp } from "./main.common"; import { initializeApp } from "./main.common";
import { logger } from "./utils/logger"; import { logger } from "./utils/logger";
import { BuildPlatform } from "@/interfaces/build";
const platform = process.env.VITE_PLATFORM; const platform = process.env.VITE_PLATFORM;
const pwa_enabled = process.env.VITE_PWA_ENABLED === "true"; const pwa_enabled = process.env.VITE_PWA_ENABLED === "true";
// Only import service worker for web builds // Only import service worker for web builds
if (platform !== "electron" && pwa_enabled) { if (platform !== BuildPlatform.Electron && pwa_enabled) {
import("./registerServiceWorker"); // Web PWA support import("./registerServiceWorker"); // Web PWA support
} }
@ -25,7 +27,7 @@ function sqlInit() {
// workers through the main thread // workers through the main thread
initBackend(worker); initBackend(worker);
} }
if (platform === "web" || platform === "development") { if (platform === BuildPlatform.Web) {
sqlInit(); sqlInit();
} else { } else {
logger.warn("[Web] SQL not initialized for platform", { platform }); logger.warn("[Web] SQL not initialized for platform", { platform });

5
src/registerServiceWorker.ts

@ -1,10 +1,11 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { register } from "register-service-worker"; import { register } from "register-service-worker";
import { NodeEnv, BuildPlatform } from "@/interfaces/build";
// Check if we're in an Electron environment // Check if we're in an Electron environment
const isElectron = const isElectron =
process.env.VITE_PLATFORM === "electron" || process.env.VITE_PLATFORM === BuildPlatform.Electron ||
process.env.VITE_DISABLE_PWA === "true" || process.env.VITE_DISABLE_PWA === "true" ||
window.navigator.userAgent.toLowerCase().includes("electron"); window.navigator.userAgent.toLowerCase().includes("electron");
@ -15,7 +16,7 @@ const isElectron =
if ( if (
!isElectron && !isElectron &&
process.env.VITE_PWA_ENABLED === "true" && process.env.VITE_PWA_ENABLED === "true" &&
process.env.NODE_ENV === "production" process.env.NODE_ENV === NodeEnv.Prod
) { ) {
register(`${process.env.BASE_URL}sw.js`, { register(`${process.env.BASE_URL}sw.js`, {
ready() { ready() {

11
src/services/PlatformServiceFactory.ts

@ -3,6 +3,7 @@ import { WebPlatformService } from "./platforms/WebPlatformService";
import { CapacitorPlatformService } from "./platforms/CapacitorPlatformService"; import { CapacitorPlatformService } from "./platforms/CapacitorPlatformService";
import { ElectronPlatformService } from "./platforms/ElectronPlatformService"; import { ElectronPlatformService } from "./platforms/ElectronPlatformService";
import { PyWebViewPlatformService } from "./platforms/PyWebViewPlatformService"; import { PyWebViewPlatformService } from "./platforms/PyWebViewPlatformService";
import { BuildPlatform } from "@/interfaces/build";
/** /**
* Factory class for creating platform-specific service implementations. * Factory class for creating platform-specific service implementations.
@ -35,19 +36,19 @@ export class PlatformServiceFactory {
return PlatformServiceFactory.instance; return PlatformServiceFactory.instance;
} }
const platform = process.env.VITE_PLATFORM || "web"; const platform = process.env.VITE_PLATFORM || BuildPlatform.Web;
switch (platform) { switch (platform) {
case "capacitor": case BuildPlatform.Capacitor:
PlatformServiceFactory.instance = new CapacitorPlatformService(); PlatformServiceFactory.instance = new CapacitorPlatformService();
break; break;
case "electron": case BuildPlatform.Electron:
PlatformServiceFactory.instance = new ElectronPlatformService(); PlatformServiceFactory.instance = new ElectronPlatformService();
break; break;
case "pywebview": case BuildPlatform.PyWebView:
PlatformServiceFactory.instance = new PyWebViewPlatformService(); PlatformServiceFactory.instance = new PyWebViewPlatformService();
break; break;
case "web": case BuildPlatform.Web:
default: default:
PlatformServiceFactory.instance = new WebPlatformService(); PlatformServiceFactory.instance = new WebPlatformService();
break; break;

3
src/services/api.ts

@ -7,6 +7,7 @@
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { logger, safeStringify } from "../utils/logger"; import { logger, safeStringify } from "../utils/logger";
import { BuildPlatform } from "@/interfaces/build";
/** /**
* Handles API errors with platform-specific logging and error processing. * Handles API errors with platform-specific logging and error processing.
@ -36,7 +37,7 @@ import { logger, safeStringify } from "../utils/logger";
* ``` * ```
*/ */
export const handleApiError = (error: AxiosError, endpoint: string) => { export const handleApiError = (error: AxiosError, endpoint: string) => {
if (process.env.VITE_PLATFORM === "capacitor") { if (process.env.VITE_PLATFORM === BuildPlatform.Capacitor) {
const endpointStr = safeStringify(endpoint); // we've seen this as an object in deep links const endpointStr = safeStringify(endpoint); // we've seen this as an object in deep links
logger.error(`[Capacitor API Error] ${endpointStr}:`, { logger.error(`[Capacitor API Error] ${endpointStr}:`, {
message: error.message, message: error.message,

13
src/utils/logger.ts

@ -1,4 +1,5 @@
import { logToDb } from "../db/databaseUtil"; import { logToDb } from "../db/databaseUtil";
import { BuildPlatform, NodeEnv } from "@/interfaces/build";
export function safeStringify(obj: unknown) { export function safeStringify(obj: unknown) {
const seen = new WeakSet(); const seen = new WeakSet();
@ -21,7 +22,7 @@ export function safeStringify(obj: unknown) {
export const logger = { export const logger = {
debug: (message: string, ...args: unknown[]) => { debug: (message: string, ...args: unknown[]) => {
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== NodeEnv.Prod) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.debug(message, ...args); console.debug(message, ...args);
// const argsString = args.length > 0 ? " - " + safeStringify(args) : ""; // const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
@ -30,8 +31,8 @@ export const logger = {
}, },
log: (message: string, ...args: unknown[]) => { log: (message: string, ...args: unknown[]) => {
if ( if (
process.env.NODE_ENV !== "production" || process.env.NODE_ENV !== NodeEnv.Prod ||
process.env.VITE_PLATFORM === "capacitor" process.env.VITE_PLATFORM === BuildPlatform.Capacitor
) { ) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(message, ...args); console.log(message, ...args);
@ -41,9 +42,9 @@ export const logger = {
}, },
info: (message: string, ...args: unknown[]) => { info: (message: string, ...args: unknown[]) => {
if ( if (
process.env.NODE_ENV !== "production" || process.env.NODE_ENV !== NodeEnv.Prod ||
process.env.VITE_PLATFORM === "capacitor" || process.env.VITE_PLATFORM === BuildPlatform.Capacitor ||
process.env.VITE_PLATFORM === "electron" process.env.VITE_PLATFORM === BuildPlatform.Electron
) { ) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.info(message, ...args); console.info(message, ...args);

3
src/views/DeepLinkRedirectView.vue

@ -100,6 +100,7 @@ import { Component, Vue } from "vue-facing-decorator";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import { APP_SERVER } from "@/constants/app"; import { APP_SERVER } from "@/constants/app";
import { NodeEnv } from "@/interfaces/build";
import { logger } from "@/utils/logger"; import { logger } from "@/utils/logger";
import { errorStringForLog } from "@/libs/endorserServer"; import { errorStringForLog } from "@/libs/endorserServer";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
@ -148,7 +149,7 @@ export default class DeepLinkRedirectView extends Vue {
this.deepLinkUrl = `timesafari://${fullPathWithQuery}`; this.deepLinkUrl = `timesafari://${fullPathWithQuery}`;
this.webUrl = `${APP_SERVER}/${fullPathWithQuery}`; this.webUrl = `${APP_SERVER}/${fullPathWithQuery}`;
this.isDevelopment = process.env.NODE_ENV !== "production"; this.isDevelopment = process.env.NODE_ENV !== NodeEnv.Prod;
this.userAgent = navigator.userAgent; this.userAgent = navigator.userAgent;
this.openDeepLink(); this.openDeepLink();

2
tsconfig.node.json

@ -8,5 +8,5 @@
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"noEmit": true "noEmit": true
}, },
"include": ["vite.config.*"] "include": ["vite.config.*", "./src/interfaces/build.ts"]
} }

3
vite.config.capacitor.mts

@ -1,4 +1,5 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts"; import { createBuildConfig } from "./vite.config.common.mts";
import { BuildPlatform } from "./src/interfaces/build.ts";
export default defineConfig(async () => createBuildConfig('capacitor')); export default defineConfig(async () => createBuildConfig(BuildPlatform.Capacitor));

47
vite.config.common.mts

@ -1,39 +1,40 @@
import { defineConfig, UserConfig, Plugin } from "vite"; import { defineConfig, UserConfig } from "vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import dotenv from "dotenv"; import dotenv from "dotenv";
import { loadAppConfig } from "./vite.config.common-utils.mts"; import { loadAppConfig } from "./vite.config.common-utils.mts";
import path from "path"; import path from "path";
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { NodeEnv, BuildEnv, BuildPlatform } from "./src/interfaces/build.ts";
export type BuildMode = 'web' | 'electron' | 'capacitor' | 'pywebview';
export type BuildEnv = 'dev' | 'test' | 'prod';
// Load environment variables // Load environment variables
if ( let buildEnv: BuildEnv;
process.env.NODE_ENV === 'dev' if (process.env.NODE_ENV === NodeEnv.Dev) {
|| process.env.NODE_ENV === 'test' buildEnv = BuildEnv.Development;
|| process.env.NODE_ENV === 'prod' } else if (process.env.NODE_ENV === NodeEnv.Test) {
) { buildEnv = BuildEnv.Testing;
console.log(`NODE_ENV=${process.env.NODE_ENV}`); } else if (process.env.NODE_ENV === NodeEnv.Prod) {
buildEnv = BuildEnv.Production;
} else { } else {
console.error("NODE_ENV is not set. Invoke with NODE_ENV=dev|test|prod"); console.error("NODE_ENV is not set. Invoke with NODE_ENV=" + Object.values(NodeEnv).join("|"));
throw new Error("NODE_ENV is not set. Invoke with NODE_ENV=dev|test|prod"); throw new Error("NODE_ENV is not set. Invoke with NODE_ENV=" + Object.values(NodeEnv).join("|"));
// process.exit(1);
} }
console.log(`Environment: ${buildEnv}`);
dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); dotenv.config({ path: `.env.${buildEnv}` });
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
export async function createBuildConfig(mode: BuildMode): Promise<UserConfig> { export async function createBuildConfig(platform: BuildPlatform): Promise<UserConfig> {
const appConfig = await loadAppConfig(); const appConfig = await loadAppConfig();
const isElectron = mode === "electron";
const isCapacitor = mode === "capacitor"; console.log(`Platform: ${platform}`);
const isPyWebView = mode === "pywebview"; const isElectron = platform === BuildPlatform.Electron;
const isCapacitor = platform === BuildPlatform.Capacitor;
const isPyWebView = platform === BuildPlatform.PyWebView;
// Explicitly set platform and disable PWA for Electron // Explicitly set platform and disable PWA for Electron
process.env.VITE_PLATFORM = mode; process.env.VITE_PLATFORM = platform;
process.env.VITE_PWA_ENABLED = (isElectron || isPyWebView || isCapacitor) process.env.VITE_PWA_ENABLED = (isElectron || isPyWebView || isCapacitor)
? 'false' ? 'false'
: 'true'; : 'true';
@ -69,7 +70,7 @@ export async function createBuildConfig(mode: BuildMode): Promise<UserConfig> {
}, },
define: { define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.VITE_PLATFORM': JSON.stringify(mode), 'process.env.VITE_PLATFORM': JSON.stringify(platform),
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isElectron), 'process.env.VITE_PWA_ENABLED': JSON.stringify(!isElectron),
'process.env.VITE_DISABLE_PWA': JSON.stringify(isElectron), 'process.env.VITE_DISABLE_PWA': JSON.stringify(isElectron),
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""', __dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
@ -91,10 +92,10 @@ export async function createBuildConfig(mode: BuildMode): Promise<UserConfig> {
'path': path.resolve(__dirname, './src/utils/node-modules/path.js'), 'path': path.resolve(__dirname, './src/utils/node-modules/path.js'),
'fs': path.resolve(__dirname, './src/utils/node-modules/fs.js'), 'fs': path.resolve(__dirname, './src/utils/node-modules/fs.js'),
'crypto': path.resolve(__dirname, './src/utils/node-modules/crypto.js'), 'crypto': path.resolve(__dirname, './src/utils/node-modules/crypto.js'),
'nostr-tools/nip06': mode === 'development' 'nostr-tools/nip06': buildEnv === BuildEnv.Development
? 'nostr-tools/nip06' ? 'nostr-tools/nip06'
: path.resolve(__dirname, 'node_modules/nostr-tools/nip06'), : path.resolve(__dirname, 'node_modules/nostr-tools/nip06'),
'nostr-tools/core': mode === 'development' 'nostr-tools/core': buildEnv === BuildEnv.Development
? 'nostr-tools' ? 'nostr-tools'
: path.resolve(__dirname, 'node_modules/nostr-tools'), : path.resolve(__dirname, 'node_modules/nostr-tools'),
'nostr-tools': path.resolve(__dirname, 'node_modules/nostr-tools'), 'nostr-tools': path.resolve(__dirname, 'node_modules/nostr-tools'),
@ -121,4 +122,4 @@ export async function createBuildConfig(mode: BuildMode): Promise<UserConfig> {
}; };
} }
export default defineConfig(async () => createBuildConfig('web')); export default defineConfig(async () => createBuildConfig(BuildPlatform.Web));

3
vite.config.electron.mts

@ -1,9 +1,10 @@
import { defineConfig, mergeConfig } from "vite"; import { defineConfig, mergeConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts"; import { createBuildConfig } from "./vite.config.common.mts";
import path from 'path'; import path from 'path';
import { BuildPlatform } from "./src/interfaces/build.ts";
export default defineConfig(async () => { export default defineConfig(async () => {
const baseConfig = await createBuildConfig('electron'); const baseConfig = await createBuildConfig(BuildPlatform.Electron);
return mergeConfig(baseConfig, { return mergeConfig(baseConfig, {
build: { build: {

3
vite.config.pywebview.mts

@ -1,4 +1,5 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts"; import { createBuildConfig } from "./vite.config.common.mts";
import { BuildPlatform } from "./src/interfaces/build.ts";
export default defineConfig(async () => createBuildConfig('pywebview')); export default defineConfig(async () => createBuildConfig(BuildPlatform.PyWebView));

3
vite.config.web.mts

@ -2,9 +2,10 @@ import { defineConfig, mergeConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa"; import { VitePWA } from "vite-plugin-pwa";
import { createBuildConfig } from "./vite.config.common.mts"; import { createBuildConfig } from "./vite.config.common.mts";
import { loadPwaConfig } from "./vite.config.common-utils.mts"; import { loadPwaConfig } from "./vite.config.common-utils.mts";
import { BuildPlatform } from "./src/interfaces/build.ts";
export default defineConfig(async () => { export default defineConfig(async () => {
const baseConfig = await createBuildConfig('web'); const baseConfig = await createBuildConfig(BuildPlatform.Web);
const pwaConfig = await loadPwaConfig(); const pwaConfig = await loadPwaConfig();
return mergeConfig(baseConfig, { return mergeConfig(baseConfig, {

Loading…
Cancel
Save