@remix-run/cli 0.3.3 → 0.4.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/README.md +105 -1
- package/dist/index.js +1 -1
- package/dist/lib/app-root.d.ts +11 -0
- package/dist/lib/app-root.d.ts.map +1 -0
- package/dist/lib/app-root.js +38 -0
- package/dist/lib/bootstrap-project.js +2 -2
- package/dist/lib/cli-context.d.ts +6 -0
- package/dist/lib/cli-context.d.ts.map +1 -1
- package/dist/lib/cli-context.js +12 -1
- package/dist/lib/cli.d.ts +3 -2
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +42 -14
- package/dist/lib/commands/completion.js +4 -4
- package/dist/lib/commands/db.d.ts +4 -0
- package/dist/lib/commands/db.d.ts.map +1 -0
- package/dist/lib/commands/db.js +283 -0
- package/dist/lib/commands/doctor.d.ts +5 -0
- package/dist/lib/commands/doctor.d.ts.map +1 -1
- package/dist/lib/commands/doctor.js +26 -251
- package/dist/lib/commands/help.d.ts.map +1 -1
- package/dist/lib/commands/help.js +17 -9
- package/dist/lib/commands/new.js +6 -6
- package/dist/lib/commands/routes.js +6 -6
- package/dist/lib/commands/test.d.ts +20 -1
- package/dist/lib/commands/test.d.ts.map +1 -1
- package/dist/lib/commands/test.js +353 -10
- package/dist/lib/commands/version.js +3 -3
- package/dist/lib/completion.d.ts +1 -1
- package/dist/lib/completion.d.ts.map +1 -1
- package/dist/lib/completion.js +229 -6
- package/dist/lib/controller-files.d.ts +0 -8
- package/dist/lib/controller-files.d.ts.map +1 -1
- package/dist/lib/controller-files.js +0 -23
- package/dist/lib/controller-ownership.d.ts +4 -12
- package/dist/lib/controller-ownership.d.ts.map +1 -1
- package/dist/lib/controller-ownership.js +31 -127
- package/dist/lib/database-command.d.ts +20 -0
- package/dist/lib/database-command.d.ts.map +1 -0
- package/dist/lib/database-command.js +7 -0
- package/dist/lib/doctor/controller-findings.d.ts +2 -2
- package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
- package/dist/lib/doctor/controller-findings.js +23 -39
- package/dist/lib/doctor/controllers.d.ts +1 -2
- package/dist/lib/doctor/controllers.d.ts.map +1 -1
- package/dist/lib/doctor/controllers.js +4 -7
- package/dist/lib/doctor/environment.js +2 -2
- package/dist/lib/doctor/fixes.d.ts.map +1 -1
- package/dist/lib/doctor/fixes.js +46 -7
- package/dist/lib/doctor/project.d.ts.map +1 -1
- package/dist/lib/doctor/project.js +3 -212
- package/dist/lib/doctor/run.d.ts +9 -0
- package/dist/lib/doctor/run.d.ts.map +1 -0
- package/dist/lib/doctor/run.js +237 -0
- package/dist/lib/doctor/types.d.ts +2 -1
- package/dist/lib/doctor/types.d.ts.map +1 -1
- package/dist/lib/doctor/types.js +4 -1
- package/dist/lib/errors.d.ts +30 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +55 -0
- package/dist/lib/help-text.js +1 -1
- package/dist/lib/parse-args.d.ts.map +1 -1
- package/dist/lib/parse-args.js +17 -4
- package/dist/lib/remix-config.d.ts +75 -0
- package/dist/lib/remix-config.d.ts.map +1 -0
- package/dist/lib/remix-config.js +411 -0
- package/dist/lib/reporter.js +1 -1
- package/dist/lib/route-map.js +13 -33
- package/package.json +12 -6
- package/schema/remix.json +242 -0
- package/src/lib/app-root.ts +46 -0
- package/src/lib/cli-context.ts +17 -0
- package/src/lib/cli.ts +41 -6
- package/src/lib/commands/db.ts +377 -0
- package/src/lib/commands/doctor.ts +27 -329
- package/src/lib/commands/help.ts +10 -1
- package/src/lib/commands/test.ts +453 -12
- package/src/lib/completion.ts +321 -6
- package/src/lib/controller-files.ts +0 -38
- package/src/lib/controller-ownership.ts +50 -203
- package/src/lib/database-command.ts +31 -0
- package/src/lib/doctor/controller-findings.ts +30 -47
- package/src/lib/doctor/controllers.ts +2 -6
- package/src/lib/doctor/fixes.ts +55 -6
- package/src/lib/doctor/project.ts +0 -236
- package/src/lib/doctor/run.ts +311 -0
- package/src/lib/doctor/types.ts +6 -5
- package/src/lib/errors.ts +65 -0
- package/src/lib/parse-args.ts +19 -3
- package/src/lib/remix-config.ts +639 -0
- package/src/lib/route-map.ts +11 -48
- package/template/.agents/skills/remix/SKILL.md +39 -28
- package/template/.agents/skills/remix/references/assets-and-browser-modules.md +53 -10
- package/template/.agents/skills/remix/references/auth-and-sessions.md +5 -5
- package/template/.agents/skills/remix/references/data-and-validation.md +15 -15
- package/template/.agents/skills/remix/references/hydration-frames-navigation.md +54 -8
- package/template/.agents/skills/remix/references/middleware-and-server.md +68 -6
- package/template/.agents/skills/remix/references/mixins-styling-events.md +2 -0
- package/template/.agents/skills/remix/references/routing-and-controllers.md +8 -8
- package/template/.agents/skills/remix/references/testing-patterns.md +19 -18
- package/template/AGENTS.md +6 -3
- package/template/README.md +6 -3
- package/template/app/actions/controller.tsx +1 -1
- package/template/app/{ui → actions}/document.tsx +7 -5
- package/template/app/{ui/scaffold-home-page.tsx → actions/home-page.tsx} +2 -3
- package/template/app/actions/public/entry.ts +48 -0
- package/template/app/{assets → actions/public}/prompt-button.tsx +29 -25
- package/template/app/assets.ts +19 -8
- package/template/app/middleware/render.tsx +29 -1
- package/template/hmr.ts +46 -0
- package/template/package.json +4 -3
- package/template/server.ts +8 -1
- package/template/tsconfig.json +1 -1
- package/dist/lib/doctor/controller-fix-plans.d.ts +0 -4
- package/dist/lib/doctor/controller-fix-plans.d.ts.map +0 -1
- package/dist/lib/doctor/controller-fix-plans.js +0 -119
- package/dist/lib/doctor/controller-placeholders.d.ts +0 -3
- package/dist/lib/doctor/controller-placeholders.d.ts.map +0 -1
- package/dist/lib/doctor/controller-placeholders.js +0 -52
- package/src/lib/doctor/controller-fix-plans.ts +0 -168
- package/src/lib/doctor/controller-placeholders.ts +0 -70
- package/template/app/assets/entry.ts +0 -8
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
import * as process from 'node:process'
|
|
4
|
+
|
|
5
|
+
import type { Database } from '@remix-run/data-table'
|
|
6
|
+
import { runRemixDb } from '@remix-run/data-table/cli'
|
|
7
|
+
import { loadMigrations, loadSeed } from '@remix-run/data-table/migrations/node'
|
|
8
|
+
|
|
9
|
+
import { findAppRoot } from '../app-root.ts'
|
|
10
|
+
import type { CliContext } from '../cli-context.ts'
|
|
11
|
+
import {
|
|
12
|
+
isDatabaseCommand,
|
|
13
|
+
type DatabaseCommandInvocation,
|
|
14
|
+
type DatabaseCommandPlan,
|
|
15
|
+
} from '../database-command.ts'
|
|
16
|
+
import {
|
|
17
|
+
dbConfigRequired,
|
|
18
|
+
dbForceRequired,
|
|
19
|
+
invalidOptionValue,
|
|
20
|
+
remixConfigNotFound,
|
|
21
|
+
renderCliError,
|
|
22
|
+
toCliError,
|
|
23
|
+
unknownCommand,
|
|
24
|
+
} from '../errors.ts'
|
|
25
|
+
import { formatHelpText } from '../help-text.ts'
|
|
26
|
+
import { parseArgs } from '../parse-args.ts'
|
|
27
|
+
import {
|
|
28
|
+
loadRemixConfig,
|
|
29
|
+
type RemixDbAdapterConfig,
|
|
30
|
+
type RemixDbCommandConfig,
|
|
31
|
+
type RemixDbString,
|
|
32
|
+
} from '../remix-config.ts'
|
|
33
|
+
|
|
34
|
+
const connectionOption = { flag: '--connection-env', type: 'string' } as const
|
|
35
|
+
const journalOption = { flag: '--journal-table', type: 'string' } as const
|
|
36
|
+
const migrationsOption = { flag: '--migrations', type: 'string' } as const
|
|
37
|
+
const seedOption = { flag: '--seed', type: 'string' } as const
|
|
38
|
+
|
|
39
|
+
export async function runDbCommand(argv: string[], context: CliContext): Promise<number> {
|
|
40
|
+
if (argv.length === 0 || argv.includes('-h') || argv.includes('--help')) {
|
|
41
|
+
process.stdout.write(getDbCommandHelpText())
|
|
42
|
+
return 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
let invocation = parseDbCommandArgs(argv)
|
|
47
|
+
let resolved = await resolveDbConfig(context)
|
|
48
|
+
let plan = resolveDatabaseCommandPlan(invocation, resolved.config, context.cwd)
|
|
49
|
+
return await runDatabaseCommand(plan, resolved.configDir)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
renderCliError(toCliError(error), { helpText: getDbCommandHelpText(process.stderr) }),
|
|
53
|
+
)
|
|
54
|
+
return 1
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getDbCommandHelpText(target: NodeJS.WriteStream = process.stdout): string {
|
|
59
|
+
return formatHelpText(
|
|
60
|
+
{
|
|
61
|
+
description: 'Manage the current app database.',
|
|
62
|
+
examples: [
|
|
63
|
+
'remix db wipe --force',
|
|
64
|
+
'remix db migrate',
|
|
65
|
+
'remix db migrate --to 20260715123000_add_users',
|
|
66
|
+
'remix db status',
|
|
67
|
+
'remix db seed',
|
|
68
|
+
'remix db reset --force',
|
|
69
|
+
],
|
|
70
|
+
options: [
|
|
71
|
+
{
|
|
72
|
+
description: 'Read the database connection from an environment variable',
|
|
73
|
+
label: '--connection-env <name>',
|
|
74
|
+
},
|
|
75
|
+
{ description: 'Confirm a destructive command (wipe and reset only)', label: '--force' },
|
|
76
|
+
{
|
|
77
|
+
description: 'Use a migration journal table (migrate, status, and reset only)',
|
|
78
|
+
label: '--journal-table <name>',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
description: 'Load migrations from a directory (migrate, status, and reset only)',
|
|
82
|
+
label: '--migrations <path>',
|
|
83
|
+
},
|
|
84
|
+
{ description: 'Run a SQL seed file (seed and reset only)', label: '--seed <path>' },
|
|
85
|
+
{
|
|
86
|
+
description: 'Stop after applying the specified migration (migrate only)',
|
|
87
|
+
label: '--to <migration>',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
usage: [
|
|
91
|
+
'remix db wipe --force [options]',
|
|
92
|
+
'remix db migrate [--to <migration>] [options]',
|
|
93
|
+
'remix db status [options]',
|
|
94
|
+
'remix db seed [options]',
|
|
95
|
+
'remix db reset --force [options]',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
target,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseDbCommandArgs(argv: string[]): DatabaseCommandInvocation {
|
|
103
|
+
let [command, ...commandArgv] = argv
|
|
104
|
+
|
|
105
|
+
if (!isDatabaseCommand(command)) {
|
|
106
|
+
throw unknownCommand(`db ${command}`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (command === 'migrate') {
|
|
110
|
+
let parsed = parseArgs(
|
|
111
|
+
commandArgv,
|
|
112
|
+
{
|
|
113
|
+
connectionEnv: connectionOption,
|
|
114
|
+
journalTable: journalOption,
|
|
115
|
+
migrations: migrationsOption,
|
|
116
|
+
to: { flag: '--to', type: 'string' },
|
|
117
|
+
},
|
|
118
|
+
{ maxPositionals: 0 },
|
|
119
|
+
)
|
|
120
|
+
return { command, ...parsed.options }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (command === 'reset') {
|
|
124
|
+
let parsed = parseArgs(
|
|
125
|
+
commandArgv,
|
|
126
|
+
{
|
|
127
|
+
connectionEnv: connectionOption,
|
|
128
|
+
force: { flag: '--force', type: 'boolean' },
|
|
129
|
+
journalTable: journalOption,
|
|
130
|
+
migrations: migrationsOption,
|
|
131
|
+
seed: seedOption,
|
|
132
|
+
},
|
|
133
|
+
{ maxPositionals: 0 },
|
|
134
|
+
)
|
|
135
|
+
if (!parsed.options.force) throw dbForceRequired(command)
|
|
136
|
+
let { force: _, ...options } = parsed.options
|
|
137
|
+
return { command, ...options }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (command === 'wipe') {
|
|
141
|
+
let parsed = parseArgs(
|
|
142
|
+
commandArgv,
|
|
143
|
+
{
|
|
144
|
+
connectionEnv: connectionOption,
|
|
145
|
+
force: { flag: '--force', type: 'boolean' },
|
|
146
|
+
},
|
|
147
|
+
{ maxPositionals: 0 },
|
|
148
|
+
)
|
|
149
|
+
if (!parsed.options.force) throw dbForceRequired(command)
|
|
150
|
+
return { command, connectionEnv: parsed.options.connectionEnv }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (command === 'seed') {
|
|
154
|
+
let parsed = parseArgs(
|
|
155
|
+
commandArgv,
|
|
156
|
+
{ connectionEnv: connectionOption, seed: seedOption },
|
|
157
|
+
{ maxPositionals: 0 },
|
|
158
|
+
)
|
|
159
|
+
return { command, ...parsed.options }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let parsed = parseArgs(
|
|
163
|
+
commandArgv,
|
|
164
|
+
{
|
|
165
|
+
connectionEnv: connectionOption,
|
|
166
|
+
journalTable: journalOption,
|
|
167
|
+
migrations: migrationsOption,
|
|
168
|
+
},
|
|
169
|
+
{ maxPositionals: 0 },
|
|
170
|
+
)
|
|
171
|
+
return { command, ...parsed.options }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function resolveDbConfig(
|
|
175
|
+
context: CliContext,
|
|
176
|
+
): Promise<{ config: RemixDbCommandConfig; configDir: string }> {
|
|
177
|
+
let configPath: string
|
|
178
|
+
let config
|
|
179
|
+
|
|
180
|
+
if (context.configPath !== undefined) {
|
|
181
|
+
configPath = path.resolve(context.cwd, context.configPath)
|
|
182
|
+
config = await context.loadConfig()
|
|
183
|
+
} else {
|
|
184
|
+
let configDir = await findAppRoot(context.cwd, 'remix.json')
|
|
185
|
+
if (configDir === null) throw remixConfigNotFound(path.join(context.cwd, 'remix.json'))
|
|
186
|
+
configPath = path.join(configDir, 'remix.json')
|
|
187
|
+
config = await loadRemixConfig(configDir, undefined)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (config.db === undefined) throw dbConfigRequired(configPath)
|
|
191
|
+
return { config: config.db, configDir: path.dirname(configPath) }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function resolveDatabaseCommandPlan(
|
|
195
|
+
invocation: DatabaseCommandInvocation,
|
|
196
|
+
config: RemixDbCommandConfig,
|
|
197
|
+
cwd: string,
|
|
198
|
+
): DatabaseCommandPlan {
|
|
199
|
+
let adapter = overrideConnection(config.adapter, invocation.connectionEnv, cwd)
|
|
200
|
+
let migrations = invocation.migrations
|
|
201
|
+
? path.resolve(cwd, invocation.migrations)
|
|
202
|
+
: config.migrations?.directory
|
|
203
|
+
let journalTable = invocation.journalTable ?? config.migrations?.journalTable
|
|
204
|
+
let seed = invocation.seed ? path.resolve(cwd, invocation.seed) : config.seed
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
(invocation.command === 'migrate' ||
|
|
208
|
+
invocation.command === 'reset' ||
|
|
209
|
+
invocation.command === 'status') &&
|
|
210
|
+
migrations === undefined
|
|
211
|
+
) {
|
|
212
|
+
throw invalidOptionValue(
|
|
213
|
+
`Database command "${invocation.command}" requires db.migrations.directory or --migrations`,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (invocation.command === 'seed' && seed === undefined) {
|
|
218
|
+
throw invalidOptionValue('Database command "seed" requires db.seed or --seed')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
adapter,
|
|
223
|
+
command: invocation.command,
|
|
224
|
+
journalTable,
|
|
225
|
+
migrations,
|
|
226
|
+
seed,
|
|
227
|
+
to: invocation.to,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function overrideConnection(
|
|
232
|
+
adapter: RemixDbCommandConfig['adapter'],
|
|
233
|
+
environmentName: string | undefined,
|
|
234
|
+
cwd: string,
|
|
235
|
+
): RemixDbCommandConfig['adapter'] {
|
|
236
|
+
if (environmentName === undefined) return adapter
|
|
237
|
+
if (adapter.type === 'sqlite') {
|
|
238
|
+
// Resolve a relative sqlite filename here, against the directory the
|
|
239
|
+
// command was invoked from (like --migrations and --seed). Filenames from
|
|
240
|
+
// remix.json resolve against the config directory instead, which would
|
|
241
|
+
// silently retarget the path.
|
|
242
|
+
let filename = process.env[environmentName]
|
|
243
|
+
if (filename === undefined || filename === '') {
|
|
244
|
+
throw invalidOptionValue(`Database environment variable ${environmentName} is not set`)
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
...adapter,
|
|
248
|
+
filename: filename === ':memory:' ? filename : path.resolve(cwd, filename),
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (adapter.type === 'postgres') {
|
|
252
|
+
return { ...adapter, connectionString: { env: environmentName } }
|
|
253
|
+
}
|
|
254
|
+
return { ...adapter, uri: { env: environmentName } }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function runDatabaseCommand(plan: DatabaseCommandPlan, configDir: string): Promise<number> {
|
|
258
|
+
let db = await createConfiguredDatabase(plan.adapter, configDir)
|
|
259
|
+
let result: number
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
result = await executeDatabaseCommand(plan, db)
|
|
263
|
+
} catch (error) {
|
|
264
|
+
try {
|
|
265
|
+
await db.close()
|
|
266
|
+
} catch (closeError) {
|
|
267
|
+
throw new AggregateError([error, closeError], 'Database command and close both failed', {
|
|
268
|
+
cause: error,
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
throw error
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
await db.close()
|
|
275
|
+
return result
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function executeDatabaseCommand(plan: DatabaseCommandPlan, db: Database): Promise<number> {
|
|
279
|
+
if (plan.command === 'wipe') {
|
|
280
|
+
return runRemixDb({ command: plan.command, db })
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (plan.command === 'seed') {
|
|
284
|
+
if (plan.seed === undefined) {
|
|
285
|
+
throw invalidOptionValue('Database command "seed" requires db.seed or --seed')
|
|
286
|
+
}
|
|
287
|
+
return runRemixDb({ command: plan.command, db, seed: await loadSeed(plan.seed) })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (plan.migrations === undefined) {
|
|
291
|
+
throw invalidOptionValue(
|
|
292
|
+
`Database command "${plan.command}" requires db.migrations.directory or --migrations`,
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let migrations = await loadMigrations(plan.migrations)
|
|
297
|
+
|
|
298
|
+
if (plan.command === 'migrate') {
|
|
299
|
+
return runRemixDb({
|
|
300
|
+
command: plan.command,
|
|
301
|
+
db,
|
|
302
|
+
migrations,
|
|
303
|
+
to: plan.to,
|
|
304
|
+
journalTable: plan.journalTable,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (plan.command === 'reset') {
|
|
309
|
+
let seed = plan.seed === undefined ? undefined : await loadSeed(plan.seed)
|
|
310
|
+
return runRemixDb({
|
|
311
|
+
command: plan.command,
|
|
312
|
+
db,
|
|
313
|
+
migrations,
|
|
314
|
+
seed,
|
|
315
|
+
journalTable: plan.journalTable,
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return runRemixDb({
|
|
320
|
+
command: plan.command,
|
|
321
|
+
db,
|
|
322
|
+
migrations,
|
|
323
|
+
journalTable: plan.journalTable,
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function createConfiguredDatabase(
|
|
328
|
+
adapter: RemixDbAdapterConfig,
|
|
329
|
+
configDir: string,
|
|
330
|
+
): Promise<Database> {
|
|
331
|
+
// Adapter packages are imported lazily because their database drivers are
|
|
332
|
+
// optional peer dependencies; only the configured adapter's driver needs to
|
|
333
|
+
// be installed.
|
|
334
|
+
if (adapter.type === 'sqlite') {
|
|
335
|
+
let { createSqliteDatabase } = await import('@remix-run/data-table-sqlite')
|
|
336
|
+
let filename = resolveDbString(adapter.filename)
|
|
337
|
+
if (filename !== ':memory:') {
|
|
338
|
+
// Filenames from remix.json are config-relative; explicit flag overrides
|
|
339
|
+
// arrive here already resolved against the invocation directory.
|
|
340
|
+
filename = path.resolve(configDir, filename)
|
|
341
|
+
await fs.mkdir(path.dirname(filename), { recursive: true })
|
|
342
|
+
}
|
|
343
|
+
return createSqliteDatabase({
|
|
344
|
+
filename,
|
|
345
|
+
foreignKeys: adapter.foreignKeys,
|
|
346
|
+
busyTimeout: adapter.busyTimeout,
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (adapter.type === 'postgres') {
|
|
351
|
+
let { createPostgresDatabase } = await import('@remix-run/data-table-postgres')
|
|
352
|
+
return createPostgresDatabase(
|
|
353
|
+
{ connectionString: resolveDbString(adapter.connectionString) },
|
|
354
|
+
{
|
|
355
|
+
maintenanceDatabase: adapter.maintenanceDatabase,
|
|
356
|
+
template: adapter.template,
|
|
357
|
+
},
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let { createMysqlDatabase } = await import('@remix-run/data-table-mysql')
|
|
362
|
+
return createMysqlDatabase(
|
|
363
|
+
{ uri: resolveDbString(adapter.uri), multipleStatements: true },
|
|
364
|
+
{ characterSet: adapter.characterSet, collation: adapter.collation },
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function resolveDbString(value: RemixDbString): string {
|
|
369
|
+
if (typeof value === 'string') return value
|
|
370
|
+
// An env var that is set but empty is treated as unset so it falls back to
|
|
371
|
+
// the configured default instead of producing an empty connection value.
|
|
372
|
+
let resolved = process.env[value.env] || value.default
|
|
373
|
+
if (resolved === undefined || resolved === '') {
|
|
374
|
+
throw invalidOptionValue(`Database environment variable ${value.env} is not set`)
|
|
375
|
+
}
|
|
376
|
+
return resolved
|
|
377
|
+
}
|