{"version":3,"file":"build-sO3PcZxl.mjs","sources":["../src/services/build.ts"],"sourcesContent":["import {\n  browserEntry,\n  buildBrowserBuildConfig,\n  buildNodeBuildConfig,\n  findBundledSharedDeps,\n} from \"@powerhousedao/shared/build-config\";\nimport type { BuiltPiece } from \"@powerhousedao/shared/build-pieces\";\nimport {\n  assertPiecesOutDir,\n  buildPieces,\n  expandEntryGlobs,\n  pieceListPath,\n  planPieces,\n  syncDistManifest,\n} from \"@powerhousedao/shared/build-pieces\";\nimport {\n  findSharedImports,\n  EXTERNALIZABLE_SHARED_SPECIFIERS,\n} from \"@powerhousedao/shared/connect\";\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { detect, resolveCommand, type Agent } from \"package-manager-detector\";\nimport { readPackage } from \"read-pkg\";\nimport { build as tsdownBuild } from \"tsdown\";\nimport type { BuildArgs } from \"../types.js\";\n\n/**\n * A Powerhouse package's `powerhouse.manifest.json` \"name\" must match its\n * `package.json` \"name\" — Connect and the registry resolve installed versions\n * by treating the manifest name as the npm package name, so a mismatch silently\n * breaks resolution. Fail the build early if they diverge. No-op when the\n * project has no manifest (nothing to compare).\n */\nexport async function assertManifestNameMatchesPackage(projectPath: string) {\n  const manifestPath = join(projectPath, \"powerhouse.manifest.json\");\n  if (!existsSync(manifestPath)) return;\n\n  const { name: packageName } = await readPackage({ cwd: projectPath });\n\n  let manifestName: unknown;\n  try {\n    manifestName = (\n      JSON.parse(readFileSync(manifestPath, \"utf-8\")) as { name?: unknown }\n    ).name;\n  } catch {\n    throw new Error(\n      `Failed to parse \"powerhouse.manifest.json\" at ${manifestPath}. Make sure it is valid JSON.`,\n    );\n  }\n\n  if (manifestName !== packageName) {\n    throw new Error(\n      `Package name mismatch — \"package.json\" and \"powerhouse.manifest.json\" must have the same \"name\":\\n` +\n        `  package.json             \"name\": ${JSON.stringify(packageName)}\\n` +\n        `  powerhouse.manifest.json \"name\": ${JSON.stringify(manifestName)}\\n\\n` +\n        `Update one so they match, then run the build again.`,\n    );\n  }\n}\n\nexport async function runBuild(args: BuildArgs) {\n  const { outDir } = args;\n  const projectRoot = process.cwd();\n\n  // Fail fast if the manifest name and package.json name have drifted apart.\n  await assertManifestNameMatchesPackage(projectRoot);\n\n  const target = {\n    projectRoot,\n    outDir,\n    pieces: planPieces(projectRoot, outDir),\n  };\n  // Before any bundler runs: an out-dir a host will never read from is worth\n  // nothing built, and the failure names what the contract is.\n  assertPiecesOutDir(target);\n  const sharedDeps = !args.noSharedDeps;\n\n  const detectResult = await detect();\n  const agent = detectResult?.agent ?? \"npm\";\n\n  // Before any bundler runs, so declining leaves nothing half built.\n  const typesOk = emitTypes(agent, outDir);\n  if (!typesOk && !args.ignoreTypeErrors) {\n    await confirmBuildDespiteTypeErrors();\n  }\n\n  await tsdownBuild({\n    ...buildBrowserBuildConfig({ sharedDeps }),\n    outDir: join(outDir, \"browser\"),\n  });\n\n  // Advisory: a shared dep the source imports but the output no longer\n  // references as a bare import was inlined despite the external set.\n  if (sharedDeps) {\n    const imported = findSharedImportsInSources(projectRoot, browserEntry);\n    const bundled = findBundledSharedDeps(\n      imported,\n      readDistBrowserFiles(join(outDir, \"browser\")),\n    );\n    if (bundled.length > 0) {\n      console.warn(\n        `⚠ shared deps bundled instead of externalized: ${bundled.join(\", \")} — check your neverBundle config`,\n      );\n    }\n  }\n\n  await tsdownBuild({\n    ...buildNodeBuildConfig({ sharedDeps }),\n    outDir: join(outDir, \"node\"),\n  });\n\n  // After the node build: it cleans <outDir>/node, where the pieces land. The\n  // built list is the gate, so a `bundle:` entry is validated with no piece dir.\n  let built: BuiltPiece[] = [];\n  if (existsSync(pieceListPath(target))) {\n    const pkg = await readPackage({ cwd: projectRoot });\n    built = await buildPieces(\n      target,\n      {\n        name: pkg.name,\n        version: pkg.version,\n        license: typeof pkg.license === \"string\" ? pkg.license : undefined,\n      },\n      { bundle: tsdownBuild },\n    );\n  }\n  syncDistManifest(target, built);\n\n  const executeLocalCommand = resolveCommand(agent, \"execute-local\", [\n    \"tailwindcss\",\n    \"-i\",\n    \"./style.css\",\n    \"-o\",\n    \"./dist/style.css\",\n  ]);\n  if (executeLocalCommand === null) {\n    console.error(\n      \"You need to have tailwindcss installed to use the `build` command.\",\n    );\n    process.exit(1);\n  }\n  execSync(\n    `${executeLocalCommand.command} ${executeLocalCommand.args.join(\" \")}`,\n  );\n\n  // Last, so it is the line a finished build leaves on screen.\n  if (!typesOk) console.warn(`\\n${UNSAFE_BUILD_WARNING}`);\n}\n\n// Runs tsc, which checks the project and writes its declarations either way.\n// Returns whether it reported no type errors.\nfunction emitTypes(agent: Agent, outDir: string): boolean {\n  const tscCommand = resolveCommand(agent, \"execute-local\", [\"tsc\", \"--build\"]);\n  if (tscCommand === null) {\n    console.error(\n      \"You need to have typescript installed to use the `build` command.\",\n    );\n    process.exit(1);\n  }\n  console.log(\"\\n▶ Type-checking and emitting types via tsc...\");\n  try {\n    execSync(`${tscCommand.command} ${tscCommand.args.join(\" \")}`, {\n      stdio: \"inherit\",\n    });\n    console.log(\"✔ Types emitted to\", join(outDir, \"types\"));\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfunction isInteractive(): boolean {\n  return Boolean(process.stdin.isTTY) && !process.env.CI;\n}\n\nconst TYPE_ERROR_RISK =\n  \"A package built with type errors can load and still fail at runtime.\";\n\nconst UNSAFE_BUILD_WARNING =\n  \"⚠ Built despite type errors. \" +\n  TYPE_ERROR_RISK +\n  \" Fix them before you publish or deploy it.\";\n\nasync function confirmBuildDespiteTypeErrors(): Promise<void> {\n  const hint =\n    \"Fix them and build again. --ignore-type-errors skips this check, at the risk of shipping that failure.\";\n  if (!isInteractive()) {\n    console.error(\n      `\\n✘ tsc reported the type errors above. ${TYPE_ERROR_RISK}\\n${hint}`,\n    );\n    process.exit(1);\n  }\n  const enquirer = await import(\"enquirer\");\n  let confirmed: boolean;\n  try {\n    const answer = await enquirer.default.prompt<{ confirmed: boolean }>({\n      type: \"confirm\",\n      name: \"confirmed\",\n      message: `tsc reported the type errors above. ${TYPE_ERROR_RISK} Build anyway?`,\n      initial: false,\n    });\n    confirmed = answer.confirmed;\n  } catch {\n    // Ctrl-C at the prompt is a decline.\n    confirmed = false;\n  }\n  if (!confirmed) {\n    console.error(`Build cancelled. ${hint}`);\n    process.exit(1);\n  }\n}\n\n/**\n * Every shared specifier imported from the entry sources; the post-build\n * scan compares these against the built output.\n *\n * Scoped to the specifiers the build actually externalizes: a specifier the\n * vendor does not publish (the bare `@powerhousedao/shared` root, an\n * unvendored subpath) is deliberately bundled, so reporting it as \"bundled\n * instead of externalized\" would be a false alarm.\n */\nfunction findSharedImportsInSources(root: string, globs: string[]): string[] {\n  const found = new Set<string>();\n  for (const file of expandEntryGlobs(root, globs)) {\n    const src = readFileSync(file, \"utf8\");\n    for (const spec of findSharedImports(\n      src,\n      EXTERNALIZABLE_SHARED_SPECIFIERS,\n    )) {\n      found.add(spec);\n    }\n  }\n  return [...found].sort();\n}\n\n/**\n * The built JS outputs of a browser build (entry files and chunks).\n */\nfunction readDistBrowserFiles(\n  dir: string,\n): { path: string; content: string }[] {\n  const out: { path: string; content: string }[] = [];\n  if (!existsSync(dir)) return out;\n  const walk = (d: string) => {\n    for (const e of readdirSync(d, { withFileTypes: true })) {\n      const p = join(d, e.name);\n      if (e.isDirectory()) walk(p);\n      else if (e.isFile() && e.name.endsWith(\".js\")) {\n        out.push({ path: p, content: readFileSync(p, \"utf8\") });\n      }\n    }\n  };\n  walk(dir);\n  return out;\n}\n"],"names":["tsdownBuild"],"mappings":"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","debug_id":"50b5ce1d-8048-5eb0-bcaf-e4e24228a0c0"}