eprec 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/assets/styles.css +469 -2
- package/app/client/app.tsx +2 -34
- package/app/client/edit-session-data.ts +245 -0
- package/app/client/editing-workspace.tsx +869 -0
- package/app/components/layout.tsx +3 -5
- package/app/router.tsx +7 -3
- package/app/routes/index.tsx +22 -19
- package/app-server.ts +3 -1
- package/cli.ts +278 -45
- package/package.json +7 -3
- package/process-course/cli.ts +11 -10
- package/process-course/edits/cli-prompts.test.ts +108 -0
- package/process-course/edits/cli.ts +256 -48
- package/process-course/logging.ts +28 -3
- package/server/bundling.ts +61 -65
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
import type { Argv, Arguments, CommandBuilder, CommandHandler } from 'yargs'
|
|
3
4
|
import yargs from 'yargs/yargs'
|
|
4
5
|
import { hideBin } from 'yargs/helpers'
|
|
6
|
+
import {
|
|
7
|
+
PromptCancelled,
|
|
8
|
+
createInquirerPrompter,
|
|
9
|
+
createPathPicker,
|
|
10
|
+
isInteractive,
|
|
11
|
+
resolveOptionalString,
|
|
12
|
+
type PathPicker,
|
|
13
|
+
type Prompter,
|
|
14
|
+
withSpinner,
|
|
15
|
+
} from '../../cli-ux'
|
|
5
16
|
import { editVideo, buildEditedOutputPath } from './video-editor'
|
|
6
17
|
import { combineVideos } from './combined-video-editor'
|
|
7
18
|
|
|
@@ -24,21 +35,205 @@ export type CombineVideosCommandArgs = {
|
|
|
24
35
|
'padding-ms'?: number
|
|
25
36
|
}
|
|
26
37
|
|
|
38
|
+
type CliUxOptions = {
|
|
39
|
+
interactive: boolean
|
|
40
|
+
pathPicker?: PathPicker
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function buildCombinedOutputPath(video1Path: string, video2Path: string) {
|
|
44
|
+
const dir = path.dirname(video1Path)
|
|
45
|
+
const ext = path.extname(video1Path) || path.extname(video2Path) || '.mp4'
|
|
46
|
+
const name1 = path.parse(video1Path).name
|
|
47
|
+
const name2 = path.parse(video2Path).name
|
|
48
|
+
return path.join(dir, `combined-${name1}-${name2}${ext}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function resolveEditVideoArgs(
|
|
52
|
+
argv: Arguments,
|
|
53
|
+
options: CliUxOptions,
|
|
54
|
+
): Promise<EditVideoCommandArgs> {
|
|
55
|
+
const pathPicker = options.pathPicker
|
|
56
|
+
let input = resolveOptionalString(argv.input)
|
|
57
|
+
if (!input) {
|
|
58
|
+
if (!options.interactive || !pathPicker) {
|
|
59
|
+
throw new Error('Input video path is required.')
|
|
60
|
+
}
|
|
61
|
+
input = await pathPicker.pickExistingFile({
|
|
62
|
+
message: 'Select input video file',
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
let transcript = resolveOptionalString(argv.transcript)
|
|
66
|
+
if (!transcript) {
|
|
67
|
+
if (!options.interactive || !pathPicker) {
|
|
68
|
+
throw new Error('Transcript JSON path is required.')
|
|
69
|
+
}
|
|
70
|
+
transcript = await pathPicker.pickExistingFile({
|
|
71
|
+
message: 'Select transcript JSON file',
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
let edited = resolveOptionalString(argv.edited)
|
|
75
|
+
if (!edited) {
|
|
76
|
+
if (!options.interactive || !pathPicker) {
|
|
77
|
+
throw new Error('Edited transcript path is required.')
|
|
78
|
+
}
|
|
79
|
+
edited = await pathPicker.pickExistingFile({
|
|
80
|
+
message: 'Select edited transcript text file',
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
const defaultOutputPath = buildEditedOutputPath(input)
|
|
84
|
+
const outputPath =
|
|
85
|
+
resolveOptionalString(argv.output) ?? defaultOutputPath
|
|
86
|
+
const paddingMs = resolvePaddingMs(argv['padding-ms'])
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
input,
|
|
90
|
+
transcript,
|
|
91
|
+
edited,
|
|
92
|
+
output: outputPath,
|
|
93
|
+
'padding-ms': paddingMs,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function resolveCombineVideosArgs(
|
|
98
|
+
argv: Arguments,
|
|
99
|
+
options: CliUxOptions,
|
|
100
|
+
): Promise<CombineVideosCommandArgs> {
|
|
101
|
+
const pathPicker = options.pathPicker
|
|
102
|
+
let video1 = resolveOptionalString(argv.video1)
|
|
103
|
+
if (!video1) {
|
|
104
|
+
if (!options.interactive || !pathPicker) {
|
|
105
|
+
throw new Error('First video path is required.')
|
|
106
|
+
}
|
|
107
|
+
video1 = await pathPicker.pickExistingFile({
|
|
108
|
+
message: 'Select first video',
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
let video2 = resolveOptionalString(argv.video2)
|
|
112
|
+
if (!video2) {
|
|
113
|
+
if (!options.interactive || !pathPicker) {
|
|
114
|
+
throw new Error('Second video path is required.')
|
|
115
|
+
}
|
|
116
|
+
video2 = await pathPicker.pickExistingFile({
|
|
117
|
+
message: 'Select second video',
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
let transcript1 = resolveOptionalString(argv.transcript1)
|
|
121
|
+
let transcript2 = resolveOptionalString(argv.transcript2)
|
|
122
|
+
const edited1 = resolveOptionalString(argv.edited1)
|
|
123
|
+
const edited2 = resolveOptionalString(argv.edited2)
|
|
124
|
+
|
|
125
|
+
if (edited1 && !transcript1) {
|
|
126
|
+
if (!options.interactive || !pathPicker) {
|
|
127
|
+
throw new Error('Transcript JSON is required for edited1.')
|
|
128
|
+
}
|
|
129
|
+
transcript1 = await pathPicker.pickExistingFile({
|
|
130
|
+
message: 'Select transcript JSON for first video',
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
if (edited2 && !transcript2) {
|
|
134
|
+
if (!options.interactive || !pathPicker) {
|
|
135
|
+
throw new Error('Transcript JSON is required for edited2.')
|
|
136
|
+
}
|
|
137
|
+
transcript2 = await pathPicker.pickExistingFile({
|
|
138
|
+
message: 'Select transcript JSON for second video',
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let output = resolveOptionalString(argv.output)
|
|
143
|
+
if (!output) {
|
|
144
|
+
if (options.interactive && pathPicker) {
|
|
145
|
+
output = await pathPicker.pickOutputPath({
|
|
146
|
+
message: 'Select output video path',
|
|
147
|
+
defaultPath: buildCombinedOutputPath(video1, video2),
|
|
148
|
+
})
|
|
149
|
+
} else {
|
|
150
|
+
output = buildCombinedOutputPath(video1, video2)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const paddingMs = resolvePaddingMs(argv['padding-ms'])
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
video1,
|
|
157
|
+
transcript1,
|
|
158
|
+
edited1,
|
|
159
|
+
video2,
|
|
160
|
+
transcript2,
|
|
161
|
+
edited2,
|
|
162
|
+
output,
|
|
163
|
+
'padding-ms': paddingMs,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function resolvePaddingMs(value: unknown) {
|
|
168
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
169
|
+
return undefined
|
|
170
|
+
}
|
|
171
|
+
return value
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function createEditVideoHandler(options: CliUxOptions): CommandHandler {
|
|
175
|
+
return async (argv) => {
|
|
176
|
+
const args = await resolveEditVideoArgs(argv, options)
|
|
177
|
+
await withSpinner(
|
|
178
|
+
'Editing video',
|
|
179
|
+
async () => {
|
|
180
|
+
const result = await editVideo({
|
|
181
|
+
inputPath: String(args.input),
|
|
182
|
+
transcriptJsonPath: String(args.transcript),
|
|
183
|
+
editedTextPath: String(args.edited),
|
|
184
|
+
outputPath: String(args.output),
|
|
185
|
+
paddingMs: args['padding-ms'],
|
|
186
|
+
})
|
|
187
|
+
if (!result.success) {
|
|
188
|
+
throw new Error(result.error ?? 'Edit failed.')
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{ successText: 'Edit complete' },
|
|
192
|
+
)
|
|
193
|
+
console.log(`Edited video written to ${args.output}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function createCombineVideosHandler(options: CliUxOptions): CommandHandler {
|
|
198
|
+
return async (argv) => {
|
|
199
|
+
const args = await resolveCombineVideosArgs(argv, options)
|
|
200
|
+
let outputPath = ''
|
|
201
|
+
await withSpinner(
|
|
202
|
+
'Combining videos',
|
|
203
|
+
async () => {
|
|
204
|
+
const result = await combineVideos({
|
|
205
|
+
video1Path: String(args.video1),
|
|
206
|
+
video1TranscriptJsonPath: args.transcript1,
|
|
207
|
+
video1EditedTextPath: args.edited1,
|
|
208
|
+
video2Path: String(args.video2),
|
|
209
|
+
video2TranscriptJsonPath: args.transcript2,
|
|
210
|
+
video2EditedTextPath: args.edited2,
|
|
211
|
+
outputPath: String(args.output),
|
|
212
|
+
overlapPaddingMs: args['padding-ms'],
|
|
213
|
+
})
|
|
214
|
+
if (!result.success) {
|
|
215
|
+
throw new Error(result.error ?? 'Combine failed.')
|
|
216
|
+
}
|
|
217
|
+
outputPath = result.outputPath
|
|
218
|
+
},
|
|
219
|
+
{ successText: 'Combine complete' },
|
|
220
|
+
)
|
|
221
|
+
console.log(`Combined video written to ${outputPath}`)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
27
225
|
export function configureEditVideoCommand(command: Argv) {
|
|
28
226
|
return command
|
|
29
227
|
.option('input', {
|
|
30
228
|
type: 'string',
|
|
31
|
-
demandOption: true,
|
|
32
229
|
describe: 'Input video file',
|
|
33
230
|
})
|
|
34
231
|
.option('transcript', {
|
|
35
232
|
type: 'string',
|
|
36
|
-
demandOption: true,
|
|
37
233
|
describe: 'Transcript JSON path',
|
|
38
234
|
})
|
|
39
235
|
.option('edited', {
|
|
40
236
|
type: 'string',
|
|
41
|
-
demandOption: true,
|
|
42
237
|
describe: 'Edited transcript text path',
|
|
43
238
|
})
|
|
44
239
|
.option('output', {
|
|
@@ -52,31 +247,14 @@ export function configureEditVideoCommand(command: Argv) {
|
|
|
52
247
|
}
|
|
53
248
|
|
|
54
249
|
export async function handleEditVideoCommand(argv: Arguments) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
typeof args.output === 'string' && args.output.trim().length > 0
|
|
58
|
-
? args.output
|
|
59
|
-
: buildEditedOutputPath(String(args.input))
|
|
60
|
-
const result = await editVideo({
|
|
61
|
-
inputPath: String(args.input),
|
|
62
|
-
transcriptJsonPath: String(args.transcript),
|
|
63
|
-
editedTextPath: String(args.edited),
|
|
64
|
-
outputPath,
|
|
65
|
-
paddingMs:
|
|
66
|
-
typeof args['padding-ms'] === 'number' ? args['padding-ms'] : undefined,
|
|
67
|
-
})
|
|
68
|
-
if (!result.success) {
|
|
69
|
-
console.error(result.error ?? 'Edit failed.')
|
|
70
|
-
process.exit(1)
|
|
71
|
-
}
|
|
72
|
-
console.log(`Edited video written to ${outputPath}`)
|
|
250
|
+
const options = createDefaultCliUxOptions()
|
|
251
|
+
await createEditVideoHandler(options)(argv)
|
|
73
252
|
}
|
|
74
253
|
|
|
75
254
|
export function configureCombineVideosCommand(command: Argv) {
|
|
76
255
|
return command
|
|
77
256
|
.option('video1', {
|
|
78
257
|
type: 'string',
|
|
79
|
-
demandOption: true,
|
|
80
258
|
describe: 'First video path',
|
|
81
259
|
})
|
|
82
260
|
.option('transcript1', {
|
|
@@ -89,7 +267,6 @@ export function configureCombineVideosCommand(command: Argv) {
|
|
|
89
267
|
})
|
|
90
268
|
.option('video2', {
|
|
91
269
|
type: 'string',
|
|
92
|
-
demandOption: true,
|
|
93
270
|
describe: 'Second video path',
|
|
94
271
|
})
|
|
95
272
|
.option('transcript2', {
|
|
@@ -102,7 +279,6 @@ export function configureCombineVideosCommand(command: Argv) {
|
|
|
102
279
|
})
|
|
103
280
|
.option('output', {
|
|
104
281
|
type: 'string',
|
|
105
|
-
demandOption: true,
|
|
106
282
|
describe: 'Output video path',
|
|
107
283
|
})
|
|
108
284
|
.option('padding-ms', {
|
|
@@ -112,43 +288,47 @@ export function configureCombineVideosCommand(command: Argv) {
|
|
|
112
288
|
}
|
|
113
289
|
|
|
114
290
|
export async function handleCombineVideosCommand(argv: Arguments) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
video2TranscriptJsonPath:
|
|
124
|
-
typeof args.transcript2 === 'string' ? args.transcript2 : undefined,
|
|
125
|
-
video2EditedTextPath:
|
|
126
|
-
typeof args.edited2 === 'string' ? args.edited2 : undefined,
|
|
127
|
-
outputPath: String(args.output),
|
|
128
|
-
overlapPaddingMs:
|
|
129
|
-
typeof args['padding-ms'] === 'number' ? args['padding-ms'] : undefined,
|
|
130
|
-
})
|
|
131
|
-
if (!result.success) {
|
|
132
|
-
console.error(result.error ?? 'Combine failed.')
|
|
133
|
-
process.exit(1)
|
|
291
|
+
const options = createDefaultCliUxOptions()
|
|
292
|
+
await createCombineVideosHandler(options)(argv)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function createDefaultCliUxOptions(): CliUxOptions {
|
|
296
|
+
const interactive = isInteractive()
|
|
297
|
+
if (!interactive) {
|
|
298
|
+
return { interactive }
|
|
134
299
|
}
|
|
135
|
-
|
|
300
|
+
const prompter = createInquirerPrompter()
|
|
301
|
+
return { interactive, pathPicker: createPathPicker(prompter) }
|
|
136
302
|
}
|
|
137
303
|
|
|
138
|
-
export async function runEditsCli() {
|
|
139
|
-
const
|
|
304
|
+
export async function runEditsCli(rawArgs = hideBin(process.argv)) {
|
|
305
|
+
const interactive = isInteractive()
|
|
306
|
+
const prompter = interactive ? createInquirerPrompter() : null
|
|
307
|
+
const pathPicker = prompter ? createPathPicker(prompter) : undefined
|
|
308
|
+
let args = rawArgs
|
|
309
|
+
|
|
310
|
+
if (interactive && args.length === 0 && prompter) {
|
|
311
|
+
const selection = await promptForEditsCommand(prompter)
|
|
312
|
+
if (!selection) {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
args = selection
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const handlerOptions: CliUxOptions = { interactive, pathPicker }
|
|
319
|
+
const parser = yargs(args)
|
|
140
320
|
.scriptName('video-edits')
|
|
141
321
|
.command(
|
|
142
322
|
'edit-video',
|
|
143
323
|
'Edit a single video using transcript text edits',
|
|
144
324
|
configureEditVideoCommand as CommandBuilder,
|
|
145
|
-
|
|
325
|
+
createEditVideoHandler(handlerOptions),
|
|
146
326
|
)
|
|
147
327
|
.command(
|
|
148
328
|
'combine-videos',
|
|
149
329
|
'Combine two videos with speech-aligned padding',
|
|
150
330
|
configureCombineVideosCommand as CommandBuilder,
|
|
151
|
-
|
|
331
|
+
createCombineVideosHandler(handlerOptions),
|
|
152
332
|
)
|
|
153
333
|
.demandCommand(1)
|
|
154
334
|
.strict()
|
|
@@ -159,9 +339,37 @@ export async function runEditsCli() {
|
|
|
159
339
|
|
|
160
340
|
if (import.meta.main) {
|
|
161
341
|
runEditsCli().catch((error) => {
|
|
342
|
+
if (error instanceof PromptCancelled) {
|
|
343
|
+
console.log('[info] Cancelled.')
|
|
344
|
+
return
|
|
345
|
+
}
|
|
162
346
|
console.error(
|
|
163
347
|
`[error] ${error instanceof Error ? error.message : String(error)}`,
|
|
164
348
|
)
|
|
165
349
|
process.exit(1)
|
|
166
350
|
})
|
|
167
351
|
}
|
|
352
|
+
|
|
353
|
+
async function promptForEditsCommand(
|
|
354
|
+
prompter: Prompter,
|
|
355
|
+
): Promise<string[] | null> {
|
|
356
|
+
const selection = await prompter.select('Choose a command', [
|
|
357
|
+
{
|
|
358
|
+
name: 'Edit a single video using transcript text edits',
|
|
359
|
+
value: 'edit-video',
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'Combine two videos with speech-aligned padding',
|
|
363
|
+
value: 'combine-videos',
|
|
364
|
+
},
|
|
365
|
+
{ name: 'Show help', value: 'help' },
|
|
366
|
+
{ name: 'Exit', value: 'exit' },
|
|
367
|
+
])
|
|
368
|
+
if (selection === 'exit') {
|
|
369
|
+
return null
|
|
370
|
+
}
|
|
371
|
+
if (selection === 'help') {
|
|
372
|
+
return ['--help']
|
|
373
|
+
}
|
|
374
|
+
return [selection]
|
|
375
|
+
}
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import { formatCommand } from '../utils'
|
|
2
2
|
import { buildChapterLogPath } from './paths'
|
|
3
3
|
|
|
4
|
+
type LogHook = () => void
|
|
5
|
+
|
|
6
|
+
let beforeLogHook: LogHook | null = null
|
|
7
|
+
let afterLogHook: LogHook | null = null
|
|
8
|
+
|
|
9
|
+
export function setLogHooks(hooks: {
|
|
10
|
+
beforeLog?: LogHook
|
|
11
|
+
afterLog?: LogHook
|
|
12
|
+
}) {
|
|
13
|
+
beforeLogHook = hooks.beforeLog ?? null
|
|
14
|
+
afterLogHook = hooks.afterLog ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function withLogHooks(callback: () => void) {
|
|
18
|
+
beforeLogHook?.()
|
|
19
|
+
callback()
|
|
20
|
+
afterLogHook?.()
|
|
21
|
+
}
|
|
22
|
+
|
|
4
23
|
export function logCommand(command: string[]) {
|
|
5
|
-
|
|
24
|
+
withLogHooks(() => {
|
|
25
|
+
console.log(`[cmd] ${formatCommand(command)}`)
|
|
26
|
+
})
|
|
6
27
|
}
|
|
7
28
|
|
|
8
29
|
export function logInfo(message: string) {
|
|
9
|
-
|
|
30
|
+
withLogHooks(() => {
|
|
31
|
+
console.log(`[info] ${message}`)
|
|
32
|
+
})
|
|
10
33
|
}
|
|
11
34
|
|
|
12
35
|
export function logWarn(message: string) {
|
|
13
|
-
|
|
36
|
+
withLogHooks(() => {
|
|
37
|
+
console.warn(`[warn] ${message}`)
|
|
38
|
+
})
|
|
14
39
|
}
|
|
15
40
|
|
|
16
41
|
export async function writeChapterLog(
|
package/server/bundling.ts
CHANGED
|
@@ -31,9 +31,7 @@ async function resolvePackageExport(
|
|
|
31
31
|
|
|
32
32
|
if (!(await packageJsonFile.exists())) return null
|
|
33
33
|
|
|
34
|
-
const packageJson = JSON.parse(
|
|
35
|
-
await packageJsonFile.text(),
|
|
36
|
-
) as PackageJson
|
|
34
|
+
const packageJson = JSON.parse(await packageJsonFile.text()) as PackageJson
|
|
37
35
|
|
|
38
36
|
if (!packageJson.exports) {
|
|
39
37
|
const entryFile = packageJson.module || packageJson.main
|
|
@@ -49,9 +47,7 @@ async function resolvePackageExport(
|
|
|
49
47
|
if (!exportEntry) return null
|
|
50
48
|
|
|
51
49
|
const exportPath =
|
|
52
|
-
typeof exportEntry === 'string'
|
|
53
|
-
? exportEntry
|
|
54
|
-
: exportEntry.default
|
|
50
|
+
typeof exportEntry === 'string' ? exportEntry : exportEntry.default
|
|
55
51
|
|
|
56
52
|
if (!exportPath) return null
|
|
57
53
|
|
|
@@ -106,40 +102,40 @@ export function createBundlingRoutes(rootDir: string) {
|
|
|
106
102
|
})
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
105
|
+
const buildResult = await Bun.build({
|
|
106
|
+
entrypoints: [resolved],
|
|
107
|
+
target: 'browser',
|
|
108
|
+
minify: Bun.env.NODE_ENV === 'production',
|
|
109
|
+
splitting: false,
|
|
110
|
+
format: 'esm',
|
|
111
|
+
sourcemap: Bun.env.NODE_ENV === 'production' ? 'none' : 'inline',
|
|
112
|
+
jsx: { importSource: 'remix/component' },
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
if (!buildResult.success) {
|
|
116
|
+
const errorMessage = buildResult.logs
|
|
117
|
+
.map((log) => log.message)
|
|
118
|
+
.join('\n')
|
|
119
|
+
return new Response(errorMessage || 'Build failed', {
|
|
120
|
+
status: 500,
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'text/plain',
|
|
123
|
+
...BUNDLING_CORS_HEADERS,
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const output = buildResult.outputs[0]
|
|
129
|
+
return new Response(output, {
|
|
125
130
|
headers: {
|
|
126
|
-
'Content-Type': '
|
|
131
|
+
'Content-Type': 'application/javascript',
|
|
132
|
+
'Cache-Control':
|
|
133
|
+
Bun.env.NODE_ENV === 'production'
|
|
134
|
+
? 'public, max-age=31536000, immutable'
|
|
135
|
+
: 'no-cache',
|
|
127
136
|
...BUNDLING_CORS_HEADERS,
|
|
128
137
|
},
|
|
129
138
|
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const output = buildResult.outputs[0]
|
|
133
|
-
return new Response(output, {
|
|
134
|
-
headers: {
|
|
135
|
-
'Content-Type': 'application/javascript',
|
|
136
|
-
'Cache-Control':
|
|
137
|
-
Bun.env.NODE_ENV === 'production'
|
|
138
|
-
? 'public, max-age=31536000, immutable'
|
|
139
|
-
: 'no-cache',
|
|
140
|
-
...BUNDLING_CORS_HEADERS,
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
139
|
},
|
|
144
140
|
|
|
145
141
|
'/node_modules/*': async (request: Request) => {
|
|
@@ -172,39 +168,39 @@ export function createBundlingRoutes(rootDir: string) {
|
|
|
172
168
|
})
|
|
173
169
|
}
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
const buildResult = await Bun.build({
|
|
172
|
+
entrypoints: [filepath],
|
|
173
|
+
target: 'browser',
|
|
174
|
+
minify: Bun.env.NODE_ENV === 'production',
|
|
175
|
+
splitting: false,
|
|
176
|
+
format: 'esm',
|
|
177
|
+
sourcemap: Bun.env.NODE_ENV === 'production' ? 'none' : 'inline',
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
if (!buildResult.success) {
|
|
181
|
+
const errorMessage = buildResult.logs
|
|
182
|
+
.map((log) => log.message)
|
|
183
|
+
.join('\n')
|
|
184
|
+
return new Response(errorMessage || 'Build failed', {
|
|
185
|
+
status: 500,
|
|
186
|
+
headers: {
|
|
187
|
+
'Content-Type': 'text/plain',
|
|
188
|
+
...BUNDLING_CORS_HEADERS,
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const output = buildResult.outputs[0]
|
|
194
|
+
return new Response(output, {
|
|
190
195
|
headers: {
|
|
191
|
-
'Content-Type': '
|
|
196
|
+
'Content-Type': 'application/javascript',
|
|
197
|
+
'Cache-Control':
|
|
198
|
+
Bun.env.NODE_ENV === 'production'
|
|
199
|
+
? 'public, max-age=31536000, immutable'
|
|
200
|
+
: 'no-cache',
|
|
192
201
|
...BUNDLING_CORS_HEADERS,
|
|
193
202
|
},
|
|
194
203
|
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const output = buildResult.outputs[0]
|
|
198
|
-
return new Response(output, {
|
|
199
|
-
headers: {
|
|
200
|
-
'Content-Type': 'application/javascript',
|
|
201
|
-
'Cache-Control':
|
|
202
|
-
Bun.env.NODE_ENV === 'production'
|
|
203
|
-
? 'public, max-age=31536000, immutable'
|
|
204
|
-
: 'no-cache',
|
|
205
|
-
...BUNDLING_CORS_HEADERS,
|
|
206
|
-
},
|
|
207
|
-
})
|
|
208
204
|
},
|
|
209
205
|
}
|
|
210
206
|
}
|