flora is a fast and secure runtime that lets you write discord bots for your servers, with a rich TypeScript SDK, without worrying about running infrastructure. [mirror]
1import path from 'node:path'
2
3import { build } from 'rolldown'
4import type { OutputAsset, OutputChunk } from 'rolldown'
5
6import type { DeploySourceMapMode } from './types'
7
8export const DEFAULT_DEPLOY_ENTRY = 'src/main.ts'
9export const DEFAULT_DEPLOY_SOURCEMAP: DeploySourceMapMode = 'none'
10
11export type DeploymentSourceMapFile = {
12 path: string
13 contents: string
14}
15
16export type BundleDeployOptions = {
17 cwd?: string
18 root?: string
19 entry?: string
20 sourcemap?: DeploySourceMapMode
21 minify?: boolean
22 external?: string[]
23}
24
25export type BundleDeployResult = {
26 entry: string
27 bundle: string
28 sourceMap?: DeploymentSourceMapFile
29}
30
31export async function bundleDeploymentSource(
32 options: BundleDeployOptions = {}
33): Promise<BundleDeployResult> {
34 const cwd = path.resolve(options.cwd ?? process.cwd())
35 const root = path.resolve(cwd, options.root ?? '.')
36 const entryAbs = path.resolve(root, options.entry ?? DEFAULT_DEPLOY_ENTRY)
37 const entry = toRelativePath(entryAbs, root)
38
39 const sourcemap = options.sourcemap ?? DEFAULT_DEPLOY_SOURCEMAP
40 const minify = options.minify ?? false
41
42 const result = await build({
43 cwd: root,
44 input: entryAbs,
45 external: options.external,
46 write: false,
47 output: {
48 format: 'esm',
49 sourcemap: sourcemap === 'none' ? false : sourcemap === 'inline' ? 'inline' : true,
50 minify,
51 exports: 'named'
52 }
53 })
54
55 const chunk = result.output.find(
56 (output): output is OutputChunk => output.type === 'chunk' && output.isEntry
57 )
58
59 if (!chunk) {
60 throw new Error(`Failed to bundle entry ${entry}`)
61 }
62
63 if (sourcemap !== 'external') {
64 return {
65 entry,
66 bundle: chunk.code
67 }
68 }
69
70 const sourceMapAsset = result.output.find(
71 (output): output is OutputAsset => output.type === 'asset' && output.fileName.endsWith('.map')
72 )
73
74 if (!sourceMapAsset) {
75 throw new Error(`Expected external sourcemap for entry ${entry}`)
76 }
77
78 return {
79 entry,
80 bundle: chunk.code,
81 sourceMap: {
82 path: sourceMapAsset.fileName,
83 contents: toText(sourceMapAsset.source)
84 }
85 }
86}
87
88function toRelativePath(filePath: string, root: string): string {
89 const rel = path.relative(root, filePath).replace(/\\/g, '/')
90
91 if (!rel || rel.startsWith('..')) {
92 throw new Error(`Entry file is not inside ${root}`)
93 }
94
95 return rel
96}
97
98function toText(value: string | Uint8Array): string {
99 if (typeof value === 'string') {
100 return value
101 }
102
103 return Buffer.from(value).toString('utf8')
104}