Doubleshot Toolkit

repository·main·Indexed 42 days ago

https://github.com/doubleshotjs/doubleshot

A toolkit for building desktop applications by integrating Node.js backend frameworks, such as NestJS, with Electron using a split-process architecture. It includes a builder for managing the lifecycle of main and renderer processes, a Runner for development, and specialized NestJS modules for Electron IPC transport and window management.

Tokens
10.6K
Snippets
38
Records
51
Agent score
52%

What's inside Doubleshot

  1. Overview of Doubleshot

    main

    Doubleshot is a collection of tools designed to integrate a Node.js backend framework (like NestJS) with an Electron main process. It follows a 'split node' architecture, allowing developers to build desktop applications where the backend and frontend are decoupled. This architecture is designed to facilitate a future transition from a Client/Server (C/S) desktop model to a Browser/Server (B/S) web application model with minimal code modifications.

    Warning: This project is in an early stage; it is not recommended for production environments.

  2. Core features of Doubleshot

    main

    Doubleshot provides several specialized packages to manage the lifecycle and communication of a desktop application:

    • Runner: A tool to start and build both the frontend (renderer) and backend (main) processes.
    • Builder: A tool to run or build the Electron main process or a standalone Node backend.
    • NestJS Electron IPC Transport: Provides simple Inter-Process Communication (IPC) for NestJS applications.
    • NestJS Electron Module: A NestJS module used to launch Electron windows.
    • Build Tooling: Powered by tsdown for fast builds and electron-builder for packaging.
  3. Configure Debugging and Development

    main

    Use the debugCfg property to control development behavior:

    • enabled: Enables debug mode.
    • args: Command-line arguments passed to the application. Can be a simple array of strings or a DevArgs object specifying separate arguments for node or electron processes.
    • env: A record of environment variables to set.
    • sourcemapType: Set to 'file' or 'inline' to control how sourcemaps are generated.
    • buildOnly: If true, only prebuilds files and does not run the application.

    Additionally, you can use runOnly: true in the top-level UserConfig to skip the prebuild step and run the application directly.

    export interface DebugConfig {
      enabled?: boolean;
      args?: string[] | DevArgs;
      env?: Record<string, string>;
      sourcemapType?: 'file' | 'inline';
      buildOnly?: boolean;
    }
  4. Understand ElectronModuleProviderValue types

    main

    The ElectronModuleProviderValue defines the types of values that can be returned by the useFactory function in ElectronModule.registerAsync. This value is what will be provided via dependency injection.

    Supported types:

    • A single BrowserWindow instance.
    • An array of BrowserWindow instances (BrowserWindow[]).
    • An object containing a window: { win: BrowserWindow }.
  5. Configure bundling with UserTsdownConfig

    main

    The UserTsdownConfig options allow you to control how files are bundled using tsdown. These options are applied to the main entry point.

    • entry: The entry file path (supports a single string). Note: tsdownConfig will override this.
    • outDir: The output directory for bundled files.
    • tsconfig: Path to a tsconfig.json file.
    • external: Packages that should not be bundled (mapped to tsdown's deps.neverBundle).
    • noExternal: Packages that should always be bundled (mapped to tsdown's deps.alwaysBundle).
    • tsdownConfig: A path to a tsdown configuration file or a direct TsdownConfig object.
    export interface UserTsdownConfig {
      outDir?: string;
      tsconfig?: string;
      external?: string[];
      noExternal?: string[];
      entry?: string;
      tsdownConfig?: string | TsdownConfig;
    }
  6. Configure Electron-specific settings

    main

    When type is set to 'electron', you can use the electron property in your configuration to manage the Electron lifecycle and build process.

    ElectronConfig

    • preload: Configuration for the preload script (uses PreloadTsdownConfig).
    • build: Configuration for electron-builder (ElectronBuildConfig).
    • rendererUrl: The URL of the renderer process in development mode.
    • waitForRenderer: Whether to wait for the renderer process to be ready.
    • waitTimeout: Timeout in milliseconds for waiting for the renderer.

    ElectronBuildConfig

    • disabled: Set to true to skip electron-builder.
    • config: A path to an electron-builder config file or an ElectronBuilderConfiguration object.
    • cliOptions: Direct electron-builder CLI options.
    • afterBuild: An async function executed after the electron-builder build completes.
    export interface ElectronConfig {
      preload?: PreloadTsdownConfig;
      build?: ElectronBuildConfig;
      rendererUrl?: string | string[];
      waitForRenderer?: boolean;
      waitTimeout?: number;
    }
  7. Configure tsdown for @doubleshot/nest-electron

    main

    The @doubleshot/nest-electron package uses tsdown for its build configuration. The configuration defines the entry point, output format, and compilation targets.

    Key settings used in this package:

    • entry: The source file to build (e.g., src/index.ts).
    • format: The module format (set to cjs for CommonJS).
    • platform: The target runtime (set to node).
    • target: The ECMAScript target (e.g., es2015).
    • unbundle: Whether to keep dependencies unbundled (set to true).
    • clean: Whether to clean the output directory before building (set to true).
    • fixedExtension: Whether to force a specific file extension (set to false).
    • dts: Whether to generate TypeScript declaration files (set to true).
    import { defineConfig } from 'tsdown'
    
    export default defineConfig({
      entry: ['src/index.ts'],
      format: 'cjs',
      platform: 'node',
      target: 'es2015',
      unbundle: true,
      clean: true,
      fixedExtension: false,
      dts: true,
    })
  8. Configure VitePluginDoubleshot options

    main

    The VitePluginDoubleshotConfig interface defines the configuration available to the plugin. It extends InlineConfig from @doubleshot/builder with specific overrides for Electron and Renderer URL management.

    Configuration Keys

    KeyTypeDescription
    rendererUrlstringThe URL of the renderer process in development mode. If omitted, the plugin attempts to resolve it automatically from the Vite server.
    electronOmit<ElectronConfig, 'rendererUrl'> & { rendererUrl?: string }Configuration for the Electron process.
    configureForMode(userConfig: InlineConfig, mode: string) => (void | InlineConfig) | Promise<(void | InlineConfig)>A hook that allows you to override the plugin configuration based on the Vite mode ('development' or 'production').
    debugbooleanEnables debug mode. This can be automatically enabled by passing the --dsb-debug flag in process.argv during execution.
  9. Configure ElectronModule with static options

    main

    When registering ElectronModule synchronously, use the ElectronModuleOptions interface to define the window(s) managed by the module. You can provide a single BrowserWindow or an array of ElectronModuleWindowInfo objects if you want to assign specific names to windows.

    Options:

    • name?: A string identifier for the module.
    • win: Either a single BrowserWindow or an array of ElectronModuleWindowInfo objects (where each object contains a win: BrowserWindow and an optional name: string).
    • isGlobal?: A boolean indicating if the module should be registered globally.
    // Example of the shape of ElectronModuleOptions
    const options: ElectronModuleOptions = {
      name: 'main-window',
      win: { name: 'primary', win: mainWindowInstance },
      isGlobal: true
    };
  10. Configure the @doubleshot/builder build process via tsdown

    main

    The @doubleshot/builder package uses tsdown for its build configuration. The configuration defines entry points, output formats, and target environments.

    Key configuration options used in this project:

    • entry: An array of file paths serving as the starting points for the build (e.g., src/index.ts, src/cli.ts).
    • format: The module output format (e.g., 'cjs' for CommonJS).
    • platform: The target runtime platform (e.g., 'node').
    • target: The specific engine version to target (e.g., 'node14').
    • clean: Boolean indicating whether to clean the output directory before building.
    • fixedExtension: Boolean determining if the output file extension should be fixed.
    • dts: Boolean indicating whether to generate TypeScript declaration files.
    import { defineConfig } from 'tsdown'
    
    export default defineConfig({
      entry: ['src/index.ts', 'src/cli.ts'],
      format: 'cjs',
      platform: 'node',
      target: 'node14',
      clean: true,
      fixedExtension: false,
      dts: true,
    })
  11. Configure ElectronModule with async options

    main

    Use ElectronModuleAsyncOptions when you need to initialize your Electron windows using asynchronous logic or dependency injection (e.g., fetching a URL from a ConfigService).

    Options:

    • imports?: Standard NestJS module imports.
    • name?: A string or array of strings to identify the module.
    • useFactory: An async or sync factory function that returns an ElectronModuleProviderValue (a BrowserWindow, an array of BrowserWindows, or an object { win: BrowserWindow }).
    • inject?: An array of dependency injection tokens to be passed as arguments to useFactory.
    • isGlobal?: A boolean to make the module global.
    ElectronModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => {
        const url = configService.get('APP_URL')
        const win = new BrowserWindow()
        win.loadURL(url)
        return win
      },
      inject: [ConfigService]
    })