🌳
pt0/deployF/dockerF/genBundleTarAI.mts
1import esbuild from 'esbuild'
6import { getTarCmd } from './getTarCmdF.mts'
8import { tsAbsPath } from '../../ptDirF.mts'
11import fs from 'fs'
13// CJS deps (node-fetch, typescript via eslint, ...) bundled into ESM need require/__filename/__dirname shims.
14// Namespace+aliased imports avoid binding collisions with bundled code (which also imports url/fileURLToPath).
15const cjsShimBanner = `import {createRequire as _cr} from 'module'; const require=_cr(import.meta.url); import * as _nu from 'url'; const __filename=_nu.fileURLToPath(import.meta.url); const __dirname=require('path').dirname(__filename);`
17/**
18 * esbuild the entrypoint into a single tree-shaken `bundle.mjs` (no node_modules), tarred for ADD.
19 * Returns {dfCpTarStr, dockerPathsA, tarSpec} so it drops into getDockerBaseStr as a swap for the
20 * multi-GB node_modules layer. `jiti` stays external (an eslint dynamic import the cronjob never
21 * invokes at runtime). Bundle is built on the dev PC, so no in-cluster/kaniko special-casing is
22 * needed — the pre-built tar is just ADD'd.
23 */
24const commonExternalNativeDeps = [
25 'pg-native', 'mysql2', 'mysql', 'sqlite3', 'oracledb', 'tedious', 'pg-query-stream',
26 'chromium-bidi', 'better-sqlite3', 'sharp', 'canvas', 'onnxruntime-node',
27 'next', '@swc/core', '@swc/wasm', 'critters', 'react-server-dom-webpack',
28 'react-server-dom-turbopack', '@vercel/turbopack-ecmascript-runtime',
31export const genBundleTar = async ({entrypointAbsPath, bundleName, externalA = []}: {entrypointAbsPath: string, bundleName: string, externalA?: string[]}) => {
32 const gitSha = await getHeadGitSha()
33 const tarFilePtPath = `tmpdockertars/bundle-${bundleName}-${gitSha}.tar.gz`
34 const tarAbs = tsAbsPath(tarFilePtPath)
35 const stageDir = `${ptDir}/tmpdockertars/bundle-${bundleName}-${gitSha}`
37 if (!await fileExists(tarAbs)) {
38 console.log(chalkYellow(`bundle: esbuild ${bundleName} (${gitSha})...`))
39 fs.mkdirSync(`${ptDir}/tmpdockertars`, {recursive: true})
40 fs.rmSync(stageDir, {recursive: true, force: true})
41 fs.mkdirSync(stageDir, {recursive: true})
42 await esbuild.build({
43 entryPoints: [entrypointAbsPath],
44 bundle: true, platform: 'node', format: 'esm', target: 'node24',
45 external: ['jiti', ...commonExternalNativeDeps, ...externalA],
46 banner: {js: cjsShimBanner},
47 define: { 'process.env.IS_BUNDLE': '"true"' },
48 treeShaking: false, // lodash _.chain wrapper methods are on a prototype — tree-shaking removes them; disabling keeps lodash chain working
49 outfile: `${stageDir}/bundle.mjs`,
50 logLevel: 'warning',
51 })
52 guardBundleValidAwait({bundleAbsPath: `${stageDir}/bundle.mjs`})
53 await liveSpawnThrow({cmd: `${getTarCmd()} -zcf ${tarAbs} -C ${stageDir} bundle.mjs`, isQuiet: true})
54 }
56 const dfCpTarStr = [
57 `# esbuild bundle (${bundleName})`,
58 `ADD ${tarFilePtPath} .`,
59 `#^ ${humanBytes(await fileSizeBytes(tarAbs))}`,
60 ].join('\n') + '\n'
62 return {dfCpTarStr, dockerPathsA: [], tarSpec: {type: 'bundle', tarPath: tarFilePtPath, bundleName}, bundleAbsPath: `${stageDir}/bundle.mjs`}