{"version":3,"file":"bundle.mjs","names":[],"sources":["../../../src/builders/web/bundle.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bundleAssetPolicyPlugin } from './bundleAssetPolicy';\nimport { PLUGIN_ROOTS_FILE } from './webDevRoots';\nimport { writeSyncWebPluginModules } from './webPluginGenerate';\nimport { scanWebPlugins } from './webPluginScan';\nimport {\n  WEB_PLUGIN_RUNTIME_SPECIFIERS,\n  webPluginCssAlias,\n  webPluginOverridePlugin,\n  webPluginRuntimeAliases,\n  webPluginRuntimeGlobal,\n} from './webPluginVite';\n\n/** Shared runtime packages every plugin's client code must resolve to the host copies. */\nconst DEDUPED_RUNTIMES = [\n  'react',\n  'react-dom',\n  '@tanstack/store',\n  '@tanstack/react-store',\n  '@agimon-ai/doompi-web-components',\n  // The sealed transport is a module singleton whose nonce counters every\n  // plugin shares; a second copy would start counting at zero.\n  '@agimon-ai/doompi-web-security',\n  // CodeMirror's state and view are singletons in all but name: a document\n  // built against one copy is rejected by an editor from the other, and a\n  // grammar chunk resolved separately from the editor is exactly how a second\n  // copy appears.\n  '@codemirror/state',\n  '@codemirror/view',\n];\n\nexport interface BundleCockpitWebOptions {\n  hostRoot: string;\n  /** Package roots to scan for doompiWeb manifests, usually the installed composition. */\n  pluginRoots: readonly string[];\n  /** Output directory; receives web/, plugins/, generated/, and pluginRoots.json. */\n  outDir: string;\n  onNotice?: (message: string) => void;\n}\n\nexport interface BundleCockpitWebResult {\n  /** Full synchronized SPA retained for CLI compatibility. */\n  assetsDir: string;\n  /** Standalone session plugin composition artifacts. */\n  pluginsDir: string;\n  pluginIds: string[];\n}\n\n/**\n * Builds the cockpit SPA against the installed plugin set.\n *\n * This is what `doompi sync` calls: manifests come from the given package\n * roots (no hardcoded plugin dependencies anywhere), the generated entry\n * modules are aliased over the committed builtin registry, and Vite compiles\n * the host shell and each plugin's shipped web source into one browser bundle.\n * The roots file beside\n * the bundle lets the dev server serve the same composition with hot reload.\n */\nexport async function bundleCockpitWeb(options: BundleCockpitWebOptions): Promise<BundleCockpitWebResult> {\n  const notice = options.onNotice ?? ((): void => {});\n  const hostRoot = options.hostRoot;\n  const plugins = scanWebPlugins(hostRoot, options.pluginRoots, notice);\n  notice(\n    `bundling the cockpit with ${String(plugins.length)} web plugin(s): ${plugins.map((p) => p.pluginId).join(', ')}`,\n  );\n\n  const generatedDir = path.join(options.outDir, 'generated');\n  const assetsDir = path.join(options.outDir, 'web');\n  const pluginsDir = path.join(options.outDir, 'plugins');\n  const generated = writeSyncWebPluginModules(plugins, generatedDir, hostRoot);\n  const roots = [...new Set(options.pluginRoots.map((root) => path.resolve(root)))];\n  fs.writeFileSync(path.join(options.outDir, PLUGIN_ROOTS_FILE), `${JSON.stringify(roots, null, 2)}\\n`);\n\n  // Vite and its plugins are regular dependencies so this works from an\n  // installed package, not only the workspace.\n  const { build } = await import('vite');\n  const { default: react } = await import('@vitejs/plugin-react');\n  const { default: tailwindcss } = await import('@tailwindcss/vite');\n\n  const clientRoot = path.join(hostRoot, 'src', 'web');\n\n  await build({\n    configFile: false,\n    envDir: false,\n    logLevel: 'warn',\n    root: clientRoot,\n    plugins: [webPluginOverridePlugin(clientRoot, generated), react(), tailwindcss(), bundleAssetPolicyPlugin()],\n    resolve: {\n      dedupe: [...DEDUPED_RUNTIMES],\n      alias: [...webPluginRuntimeAliases(clientRoot), webPluginCssAlias(generated)],\n    },\n    build: {\n      outDir: assetsDir,\n      emptyOutDir: true,\n      sourcemap: false,\n    },\n  });\n\n  await build({\n    configFile: false,\n    envDir: false,\n    logLevel: 'warn',\n    base: './',\n    // Plugin sources are already-built ESM and can retain import.meta.url around\n    // Vite asset placeholders. Give the classic IIFE the URL of the script that\n    // was dynamically served, rather than letting Rolldown replace it with {}.\n    define: {\n      'import.meta.url': '(document.currentScript && document.currentScript.src) || document.baseURI',\n    },\n    plugins: [react(), tailwindcss()],\n    resolve: {\n      dedupe: [...DEDUPED_RUNTIMES],\n      alias: [webPluginCssAlias(generated)],\n    },\n    build: {\n      cssCodeSplit: false,\n      outDir: pluginsDir,\n      emptyOutDir: true,\n      sourcemap: false,\n      assetsInlineLimit: 0,\n      manifest: 'manifest.json',\n      rollupOptions: {\n        input: generated.compositionModulePath,\n        external: [...WEB_PLUGIN_RUNTIME_SPECIFIERS],\n        output: {\n          format: 'iife',\n          name: 'DoomPiWebPluginCompositionBundle',\n          entryFileNames: 'composition.js',\n          globals: Object.fromEntries(\n            WEB_PLUGIN_RUNTIME_SPECIFIERS.map((specifier) => [specifier, webPluginRuntimeGlobal(specifier)]),\n          ),\n          assetFileNames: 'assets/[name]-[hash][extname]',\n        },\n      },\n    },\n  });\n  fs.writeFileSync(path.join(pluginsDir, 'index.html'), '<!doctype html>\\n<title>DoomPi plugin composition</title>\\n');\n\n  return { assetsDir, pluginsDir, pluginIds: plugins.map((plugin) => plugin.pluginId) };\n}\n"],"mappings":";;;;;;;;;AAgBA,MAAM,mBAAmB;CACvB;CACA;CACA;CACA;CACA;CAGA;CAKA;CACA;AACF;;;;;;;;;;;AA6BA,eAAsB,iBAAiB,SAAmE;CACxG,MAAM,SAAS,QAAQ,mBAAyB,CAAC;CACjD,MAAM,WAAW,QAAQ;CACzB,MAAM,UAAU,eAAe,UAAU,QAAQ,aAAa,MAAM;CACpE,OACE,6BAA6B,OAAO,QAAQ,MAAM,EAAE,kBAAkB,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,IAAI,GAChH;CAEA,MAAM,eAAe,KAAK,KAAK,QAAQ,QAAQ,WAAW;CAC1D,MAAM,YAAY,KAAK,KAAK,QAAQ,QAAQ,KAAK;CACjD,MAAM,aAAa,KAAK,KAAK,QAAQ,QAAQ,SAAS;CACtD,MAAM,YAAY,0BAA0B,SAAS,cAAc,QAAQ;CAC3E,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,QAAQ,YAAY,KAAK,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC;CAChF,GAAG,cAAc,KAAK,KAAK,QAAQ,QAAQ,iBAAiB,GAAG,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;CAIpG,MAAM,EAAE,UAAU,MAAM,OAAO;CAC/B,MAAM,EAAE,SAAS,UAAU,MAAM,OAAO;CACxC,MAAM,EAAE,SAAS,gBAAgB,MAAM,OAAO;CAE9C,MAAM,aAAa,KAAK,KAAK,UAAU,OAAO,KAAK;CAEnD,MAAM,MAAM;EACV,YAAY;EACZ,QAAQ;EACR,UAAU;EACV,MAAM;EACN,SAAS;GAAC,wBAAwB,YAAY,SAAS;GAAG,MAAM;GAAG,YAAY;GAAG,wBAAwB;EAAC;EAC3G,SAAS;GACP,QAAQ,CAAC,GAAG,gBAAgB;GAC5B,OAAO,CAAC,GAAG,wBAAwB,UAAU,GAAG,kBAAkB,SAAS,CAAC;EAC9E;EACA,OAAO;GACL,QAAQ;GACR,aAAa;GACb,WAAW;EACb;CACF,CAAC;CAED,MAAM,MAAM;EACV,YAAY;EACZ,QAAQ;EACR,UAAU;EACV,MAAM;EAIN,QAAQ,EACN,mBAAmB,6EACrB;EACA,SAAS,CAAC,MAAM,GAAG,YAAY,CAAC;EAChC,SAAS;GACP,QAAQ,CAAC,GAAG,gBAAgB;GAC5B,OAAO,CAAC,kBAAkB,SAAS,CAAC;EACtC;EACA,OAAO;GACL,cAAc;GACd,QAAQ;GACR,aAAa;GACb,WAAW;GACX,mBAAmB;GACnB,UAAU;GACV,eAAe;IACb,OAAO,UAAU;IACjB,UAAU,CAAC,GAAG,6BAA6B;IAC3C,QAAQ;KACN,QAAQ;KACR,MAAM;KACN,gBAAgB;KAChB,SAAS,OAAO,YACd,8BAA8B,KAAK,cAAc,CAAC,WAAW,uBAAuB,SAAS,CAAC,CAAC,CACjG;KACA,gBAAgB;IAClB;GACF;EACF;CACF,CAAC;CACD,GAAG,cAAc,KAAK,KAAK,YAAY,YAAY,GAAG,6DAA6D;CAEnH,OAAO;EAAE;EAAW;EAAY,WAAW,QAAQ,KAAK,WAAW,OAAO,QAAQ;CAAE;AACtF"}