Skip to content

Commit bb9eec5

Browse files
authored
Merge pull request #609 from devforth/next
Next
2 parents fcd7c25 + 87aff58 commit bb9eec5

18 files changed

Lines changed: 459 additions & 63 deletions

File tree

adminforth/commands/callTsProxy.js

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ import dotenv from "dotenv";
88
const currentFilePath = import.meta.url;
99
const currentFileFolder = path.dirname(currentFilePath).replace("file:", "");
1010

11+
function getLocalBinPath(currentDirectory) {
12+
return path.join(currentDirectory, "node_modules", ".bin");
13+
}
14+
15+
function getLocalBinExecutable(currentDirectory, command) {
16+
const extension = process.platform === "win32" ? ".cmd" : "";
17+
const executablePath = path.join(getLocalBinPath(currentDirectory), `${command}${extension}`);
18+
return fs.existsSync(executablePath) ? executablePath : command;
19+
}
20+
21+
function getEnvWithLocalBin(currentDirectory) {
22+
const pathKey = process.platform === "win32" ? "Path" : "PATH";
23+
const localBinPath = getLocalBinPath(currentDirectory);
24+
const currentPath = process.env[pathKey] || "";
25+
26+
return {
27+
...process.env,
28+
[pathKey]: [localBinPath, currentPath].filter(Boolean).join(path.delimiter),
29+
};
30+
}
31+
1132
export function callTsProxy(tsCode, silent=false) {
1233

1334
const currentDirectory = process.cwd();
@@ -22,28 +43,37 @@ export function callTsProxy(tsCode, silent=false) {
2243

2344
process.env.HEAVY_DEBUG && console.log("🌐 Calling tsproxy with code:", path.join(currentFileFolder, "proxy.ts"));
2445
return new Promise((resolve, reject) => {
25-
const child = spawn("tsx", [path.join(currentFileFolder, "proxy.ts")], {
26-
env: process.env,
46+
const child = spawn(getLocalBinExecutable(currentDirectory, "tsx"), [path.join(currentFileFolder, "proxy.ts")], {
47+
env: getEnvWithLocalBin(currentDirectory),
2748
});
2849
let stderr = "";
29-
let stdoutLogs = [];
50+
let stdout = "";
3051

3152
child.stdout.on("data", (data) => {
32-
stdoutLogs.push(data.toString());
53+
stdout += data.toString();
3354
});
3455

3556
child.stderr.on("data", (data) => {
3657
stderr += data;
3758
});
3859

60+
child.on("error", (error) => {
61+
reject(error);
62+
});
63+
3964
child.on("close", (code) => {
40-
const tsProxyResult = stdoutLogs.find(log => log.includes('>>>>>>>'));
41-
const preparedStdout = tsProxyResult.slice(tsProxyResult.indexOf('>>>>>>>') + 7, tsProxyResult.lastIndexOf('<<<<<<<'));
42-
const preparedStdoutLogs = stdoutLogs.filter(log => !log.includes('>>>>>>>'));
65+
const resultStart = stdout.indexOf('>>>>>>>');
66+
const resultEnd = stdout.lastIndexOf('<<<<<<<');
67+
if (resultStart === -1 || resultEnd === -1 || resultEnd < resultStart) {
68+
reject(new Error(`Invalid JSON from tsproxy. stdout: ${stdout}, stderr: ${stderr}`));
69+
return;
70+
}
71+
const preparedStdout = stdout.slice(resultStart + 7, resultEnd);
72+
const preparedStdoutLogs = stdout.slice(0, resultStart);
4373
if (code === 0) {
4474
try {
45-
for (const log of preparedStdoutLogs) {
46-
console.log(log);
75+
if (preparedStdoutLogs) {
76+
process.stdout.write(preparedStdoutLogs);
4777
}
4878
const parsed = JSON.parse(preparedStdout);
4979
if (!silent) {

adminforth/commands/createApp/templates/package.json.hbs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
"typescript": "6.0.3",
3333
"tsx": "4.11.2",
3434
"@types/express": "^4.17.21",
35-
"@types/node": "latest",
35+
"@types/node": "latest"{{#if includePrismaMigrations}},
3636
"@prisma/client": "latest",
3737
"prisma": "^7.0.0"
38+
{{/if}}
3839
}
39-
}
40+
}

adminforth/commands/createApp/utils.js

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ function detectAdminforthVersion() {
3737
}
3838

3939
const adminforthVersion = detectAdminforthVersion();
40+
const SUPPORTED_DB_URL_SCHEMES = ['sqlite://', 'postgresql://', 'mongodb://', 'mysql://', 'clickhouse://'];
41+
const PRISMA_MIGRATION_DB_PROTOCOLS = ['sqlite', 'postgres', 'postgresql', 'mysql'];
4042

4143

4244
export function parseArgumentsIntoOptions(rawArgs) {
@@ -56,7 +58,6 @@ export function parseArgumentsIntoOptions(rawArgs) {
5658
appName: args['--app-name'],
5759
db: args['--db'],
5860
useNpm: args['--use-npm'],
59-
includePrismaMigrations: args['--include-prisma-migrations'],
6061
};
6162
}
6263

@@ -94,8 +95,19 @@ export async function promptForMissingOptions(options) {
9495
});
9596
}
9697

97-
if (!options.includePrismaMigrations) {
98-
questions.push({
98+
const answers = await inquirer.prompt(questions);
99+
const resolvedOptions = {
100+
...options,
101+
appName: options.appName || answers.appName,
102+
db: options.db || answers.db,
103+
useNpm: options.useNpm || answers.useNpm,
104+
};
105+
106+
if (
107+
resolvedOptions.includePrismaMigrations === undefined &&
108+
isPrismaMigrationDbUrl(resolvedOptions.db)
109+
) {
110+
const prismaAnswer = await inquirer.prompt([{
99111
type: 'select',
100112
name: 'includePrismaMigrations',
101113
message: 'Include Prisma migrations? >',
@@ -104,18 +116,13 @@ export async function promptForMissingOptions(options) {
104116
{ name: 'No', value: false },
105117
],
106118
default: true,
107-
});
108-
119+
}]);
120+
resolvedOptions.includePrismaMigrations = prismaAnswer.includePrismaMigrations;
121+
} else {
122+
resolvedOptions.includePrismaMigrations = Boolean(resolvedOptions.includePrismaMigrations);
109123
}
110124

111-
const answers = await inquirer.prompt(questions);
112-
return {
113-
...options,
114-
appName: options.appName || answers.appName,
115-
db: options.db || answers.db,
116-
useNpm: options.useNpm || answers.useNpm,
117-
includePrismaMigrations: options.includePrismaMigrations || answers.includePrismaMigrations,
118-
};
125+
return resolvedOptions;
119126
}
120127

121128
function checkNodeVersion(minRequiredVersion = 20) {
@@ -134,6 +141,15 @@ function parseConnectionString(dbUrl) {
134141
return new ConnectionString(dbUrl);
135142
}
136143

144+
function isPrismaMigrationDbUrl(dbUrl) {
145+
try {
146+
const connectionString = parseConnectionString(dbUrl);
147+
return PRISMA_MIGRATION_DB_PROTOCOLS.includes(connectionString.protocol);
148+
} catch {
149+
return false;
150+
}
151+
}
152+
137153
function detectDbProvider(protocol) {
138154
if (protocol.startsWith('sqlite')) {
139155
return 'sqlite';
@@ -143,25 +159,27 @@ function detectDbProvider(protocol) {
143159
return 'mongodb';
144160
} else if (protocol.startsWith('mysql')) {
145161
return 'mysql';
162+
} else if (protocol.startsWith('clickhouse')) {
163+
return 'clickhouse';
146164
}
147165

148-
const message = `Unknown database provider for ${protocol}. Only SQLite, PostgreSQL, and MongoDB are supported now.`;
166+
const message = `Unknown database provider for ${protocol}. Supported database URL schemes: ${SUPPORTED_DB_URL_SCHEMES.join(', ')}.`;
149167
throw new Error(message);
150168
}
151169

152170
function generateDbUrlForPrisma(connectionString) {
171+
if (!PRISMA_MIGRATION_DB_PROTOCOLS.includes(connectionString.protocol))
172+
return null;
153173
if (connectionString.protocol.startsWith('sqlite'))
154174
return `file:${connectionString.host}`;
155-
if (connectionString.protocol.startsWith('mongodb'))
156-
return null;
157175
return connectionString.toString();
158176
}
159177

160178
function generateDbUrlForPrismaProd(connectionString) {
179+
if (!PRISMA_MIGRATION_DB_PROTOCOLS.includes(connectionString.protocol))
180+
return null;
161181
if (connectionString.protocol.startsWith('sqlite'))
162182
return `file:/code/db/${connectionString.host}`;
163-
if (connectionString.protocol.startsWith('mongodb'))
164-
return null;
165183
return connectionString.toString();
166184
}
167185

@@ -400,7 +418,7 @@ async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations,
400418
data: {
401419
appName,
402420
adminforthVersion: adminforthVersion,
403-
includePrismaMigrations,
421+
includePrismaMigrations: Boolean(resolvedPrismaDbUrl),
404422
},
405423
},
406424
{
@@ -425,7 +443,7 @@ async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations,
425443
)
426444
}
427445

428-
if (includePrismaMigrations) {
446+
if (resolvedPrismaDbUrl) {
429447
templateTasks.push(
430448
{
431449
src: 'schema.prisma.hbs',
@@ -506,7 +524,7 @@ function generateFinalInstructionsPnpm(skipPrismaSetup, options) {
506524
${chalk.dim('// Go to the project directory')}
507525
${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
508526

509-
if (options.includePrismaMigrations)
527+
if (options.includePrismaMigrations && !skipPrismaSetup)
510528
instruction += `
511529
${chalk.dim('// Generate and apply initial migration')}
512530
${chalk.dim('$')}${chalk.cyan(' pnpm makemigration --name init && pnpm migrate:local')}\n`;
@@ -528,7 +546,7 @@ function generateFinalInstructionsNpm(skipPrismaSetup, options) {
528546
${chalk.dim('// Go to the project directory')}
529547
${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
530548

531-
if (options.includePrismaMigrations)
549+
if (options.includePrismaMigrations && !skipPrismaSetup)
532550
instruction += `
533551
${chalk.dim('// Generate and apply initial migration')}
534552
${chalk.dim('$')}${chalk.cyan(' npm run makemigration -- --name init && npm run migrate:local')}\n`;

0 commit comments

Comments
 (0)