create-absolutejs 0.7.0 → 0.8.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/dist/generators/configurations/generatePackageJson.js +17 -40
- package/dist/generators/db/dockerInitTemplates.d.ts +4 -0
- package/dist/generators/db/dockerInitTemplates.js +4 -0
- package/dist/generators/db/generateDockerContainer.js +41 -4
- package/dist/generators/db/handlerTemplates.d.ts +1 -1
- package/dist/generators/db/handlerTemplates.js +1 -1
- package/dist/generators/db/scaffoldDatabase.d.ts +3 -1
- package/dist/generators/db/scaffoldDatabase.js +4 -2
- package/dist/generators/db/scaffoldDocker.d.ts +3 -1
- package/dist/generators/db/scaffoldDocker.js +21 -11
- package/dist/generators/project/generateImportsBlock.js +4 -1
- package/dist/index.js +11 -2
- package/dist/scaffold.d.ts +3 -1
- package/dist/scaffold.js +8 -4
- package/dist/utils/checkDockerInstalled.d.ts +9 -1
- package/dist/utils/checkDockerInstalled.js +137 -39
- package/dist/utils/checkSqliteInstalled.js +13 -13
- package/dist/versions.d.ts +2 -1
- package/dist/versions.js +2 -1
- package/package.json +1 -1
|
@@ -5,37 +5,16 @@ import { green } from 'picocolors';
|
|
|
5
5
|
import { absoluteAuthPlugin, availablePlugins, defaultDependencies, defaultPlugins, eslintAndPrettierDependencies, eslintReactDependencies } from '../../data';
|
|
6
6
|
import { getPackageVersion } from '../../utils/getPackageVersion';
|
|
7
7
|
import { versions } from '../../versions';
|
|
8
|
-
import { initTemplates } from '../db/dockerInitTemplates';
|
|
9
8
|
import { computeFlags } from '../project/computeFlags';
|
|
10
|
-
const
|
|
11
|
-
cockroachdb:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
mariadb: {
|
|
20
|
-
clientCmd: 'MYSQL_PWD=userpassword mariadb -h127.0.0.1 -u user database',
|
|
21
|
-
waitCmd: initTemplates.mariadb.wait
|
|
22
|
-
},
|
|
23
|
-
mssql: {
|
|
24
|
-
clientCmd: '/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1',
|
|
25
|
-
waitCmd: initTemplates.mssql.wait
|
|
26
|
-
},
|
|
27
|
-
mysql: {
|
|
28
|
-
clientCmd: 'MYSQL_PWD=userpassword mysql -h127.0.0.1 -u user database',
|
|
29
|
-
waitCmd: initTemplates.mysql.wait
|
|
30
|
-
},
|
|
31
|
-
postgresql: {
|
|
32
|
-
clientCmd: 'psql -h localhost -U user -d database',
|
|
33
|
-
waitCmd: initTemplates.postgresql.wait
|
|
34
|
-
},
|
|
35
|
-
singlestore: {
|
|
36
|
-
clientCmd: 'singlestore -u root -ppassword -D database',
|
|
37
|
-
waitCmd: initTemplates.singlestore.wait
|
|
38
|
-
}
|
|
9
|
+
const dbClientCommands = {
|
|
10
|
+
cockroachdb: 'cockroach sql --insecure --database=database',
|
|
11
|
+
gel: 'gel -H localhost -P 5656 -u admin --tls-security insecure -b main',
|
|
12
|
+
mariadb: 'MYSQL_PWD=userpassword mariadb -h127.0.0.1 -u user database',
|
|
13
|
+
mongodb: 'mongosh -u user -p password --authenticationDatabase admin database',
|
|
14
|
+
mssql: '/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1',
|
|
15
|
+
mysql: 'MYSQL_PWD=userpassword mysql -h127.0.0.1 -u user database',
|
|
16
|
+
postgresql: 'psql -h localhost -U user -d database',
|
|
17
|
+
singlestore: 'singlestore -u root -ppassword -D database'
|
|
39
18
|
};
|
|
40
19
|
export const createPackageJson = ({ projectName, authOption, plugins, databaseEngine, orm, databaseHost, useTailwind, latest, frontendDirectories, codeQualityTool }) => {
|
|
41
20
|
const s = spinner();
|
|
@@ -111,7 +90,7 @@ export const createPackageJson = ({ projectName, authOption, plugins, databaseEn
|
|
|
111
90
|
if (latest)
|
|
112
91
|
s.stop(green('Package versions resolved'));
|
|
113
92
|
const scripts = {
|
|
114
|
-
dev: '
|
|
93
|
+
dev: 'absolutejs dev',
|
|
115
94
|
format: `prettier --write "./**/*.{js,ts,css,json,mjs,md${flags.requiresReact ? ',jsx,tsx' : ''}${flags.requiresSvelte ? ',svelte' : ''}${flags.requiresVue ? ',vue' : ''}${flags.requiresHtml || flags.requiresHtmx ? ',html' : ''}}"`,
|
|
116
95
|
lint: 'eslint ./src',
|
|
117
96
|
test: 'echo "Error: no test specified" && exit 1',
|
|
@@ -121,20 +100,15 @@ export const createPackageJson = ({ projectName, authOption, plugins, databaseEn
|
|
|
121
100
|
if (isLocal &&
|
|
122
101
|
databaseEngine !== undefined &&
|
|
123
102
|
databaseEngine !== 'none' &&
|
|
124
|
-
databaseEngine !== 'sqlite'
|
|
125
|
-
|
|
126
|
-
const config = dbScripts[databaseEngine];
|
|
103
|
+
databaseEngine !== 'sqlite') {
|
|
104
|
+
const clientCmd = dbClientCommands[databaseEngine];
|
|
127
105
|
const dockerPrefix = `docker compose -p ${databaseEngine} -f db/docker-compose.db.yml`;
|
|
128
|
-
scripts['db:up'] = `${dockerPrefix} up -d db`;
|
|
129
|
-
scripts['postdb:up'] =
|
|
130
|
-
`${dockerPrefix} exec db bash -lc '${config.waitCmd}'`;
|
|
106
|
+
scripts['db:up'] = `${dockerPrefix} up -d --wait db`;
|
|
131
107
|
scripts['db:down'] = `${dockerPrefix} down`;
|
|
132
108
|
scripts['db:reset'] = `${dockerPrefix} down -v`;
|
|
133
109
|
scripts[`db:${databaseEngine}`] =
|
|
134
|
-
`${dockerPrefix} exec -it db bash -lc '${
|
|
135
|
-
scripts['predev'] = 'bun db:up';
|
|
110
|
+
`${dockerPrefix} exec -it db bash -lc '${clientCmd}'`;
|
|
136
111
|
scripts[`predb:${databaseEngine}`] = 'bun db:up';
|
|
137
|
-
scripts['postdev'] = 'bun db:down';
|
|
138
112
|
scripts[`postdb:${databaseEngine}`] = 'bun db:down';
|
|
139
113
|
}
|
|
140
114
|
if (isLocal &&
|
|
@@ -156,6 +130,9 @@ export const createPackageJson = ({ projectName, authOption, plugins, databaseEn
|
|
|
156
130
|
if (isLocal && databaseEngine === 'gel') {
|
|
157
131
|
dependencies['gel'] = resolveVersion('gel', versions['gel']);
|
|
158
132
|
}
|
|
133
|
+
if (databaseEngine === 'mongodb') {
|
|
134
|
+
dependencies['mongodb'] = resolveVersion('mongodb', versions['mongodb']);
|
|
135
|
+
}
|
|
159
136
|
if (isLocal && databaseEngine === 'sqlite') {
|
|
160
137
|
scripts['db:sqlite'] = 'sqlite3 db/database.sqlite';
|
|
161
138
|
scripts['db:init'] = 'sqlite3 db/database.sqlite < db/init.sql';
|
|
@@ -29,6 +29,10 @@ export declare const initTemplates: {
|
|
|
29
29
|
readonly cli: "MYSQL_PWD=userpassword mariadb -h127.0.0.1 -u user database -e";
|
|
30
30
|
readonly wait: "until mariadb-admin ping -h127.0.0.1 --silent; do sleep 1; done";
|
|
31
31
|
};
|
|
32
|
+
readonly mongodb: {
|
|
33
|
+
readonly cli: "mongosh -u user -p password --authenticationDatabase admin database --eval";
|
|
34
|
+
readonly wait: "for i in $(seq 1 60); do mongosh -u user -p password --authenticationDatabase admin --eval \"db.adminCommand(\\\"ping\\\")\" --quiet 2>/dev/null && exit 0; sleep 1; done; exit 1";
|
|
35
|
+
};
|
|
32
36
|
readonly mssql: {
|
|
33
37
|
readonly cli: "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1 -Q";
|
|
34
38
|
readonly wait: "until /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1 -Q \"SELECT 1\" >/dev/null 2>&1; do sleep 1; done";
|
|
@@ -122,6 +122,10 @@ export const initTemplates = {
|
|
|
122
122
|
cli: 'MYSQL_PWD=userpassword mariadb -h127.0.0.1 -u user database -e',
|
|
123
123
|
wait: 'until mariadb-admin ping -h127.0.0.1 --silent; do sleep 1; done'
|
|
124
124
|
},
|
|
125
|
+
mongodb: {
|
|
126
|
+
cli: 'mongosh -u user -p password --authenticationDatabase admin database --eval',
|
|
127
|
+
wait: 'for i in $(seq 1 60); do mongosh -u user -p password --authenticationDatabase admin --eval "db.adminCommand(\\"ping\\")" --quiet 2>/dev/null && exit 0; sleep 1; done; exit 1'
|
|
128
|
+
},
|
|
125
129
|
mssql: {
|
|
126
130
|
cli: '/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1 -Q',
|
|
127
131
|
wait: 'until /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1 -Q "SELECT 1" >/dev/null 2>&1; do sleep 1; done'
|
|
@@ -4,6 +4,10 @@ const templates = {
|
|
|
4
4
|
env: {
|
|
5
5
|
COCKROACH_DATABASE: 'database'
|
|
6
6
|
},
|
|
7
|
+
healthcheck: {
|
|
8
|
+
startPeriod: '5s',
|
|
9
|
+
test: 'cockroach sql --insecure -e "select 1" >/dev/null 2>&1'
|
|
10
|
+
},
|
|
7
11
|
image: 'cockroachdb/cockroach:latest-v25.3',
|
|
8
12
|
port: '26257:26257',
|
|
9
13
|
volumePath: '/cockroach/cockroach-data'
|
|
@@ -12,6 +16,10 @@ const templates = {
|
|
|
12
16
|
env: {
|
|
13
17
|
GEL_SERVER_SECURITY: 'insecure_dev_mode'
|
|
14
18
|
},
|
|
19
|
+
healthcheck: {
|
|
20
|
+
startPeriod: '30s',
|
|
21
|
+
test: 'gel query -H localhost -P 5656 -u admin --tls-security insecure "select 1" >/dev/null 2>&1'
|
|
22
|
+
},
|
|
15
23
|
image: 'geldata/gel:latest',
|
|
16
24
|
port: '5656:5656',
|
|
17
25
|
volumePath: '/var/lib/gel/data'
|
|
@@ -23,6 +31,10 @@ const templates = {
|
|
|
23
31
|
MYSQL_ROOT_PASSWORD: 'rootpassword',
|
|
24
32
|
MYSQL_USER: 'user'
|
|
25
33
|
},
|
|
34
|
+
healthcheck: {
|
|
35
|
+
startPeriod: '5s',
|
|
36
|
+
test: 'mariadb-admin ping -h127.0.0.1 --silent'
|
|
37
|
+
},
|
|
26
38
|
image: 'mariadb:11.4',
|
|
27
39
|
port: '3306:3306',
|
|
28
40
|
volumePath: '/var/lib/mysql'
|
|
@@ -33,6 +45,10 @@ const templates = {
|
|
|
33
45
|
MONGO_INITDB_ROOT_PASSWORD: 'password',
|
|
34
46
|
MONGO_INITDB_ROOT_USERNAME: 'user'
|
|
35
47
|
},
|
|
48
|
+
healthcheck: {
|
|
49
|
+
startPeriod: '5s',
|
|
50
|
+
test: 'mongosh -u user -p password --authenticationDatabase admin --eval "db.adminCommand(\'ping\')" --quiet'
|
|
51
|
+
},
|
|
36
52
|
image: 'mongo:7.0',
|
|
37
53
|
port: '27017:27017',
|
|
38
54
|
volumePath: '/data/db'
|
|
@@ -42,6 +58,10 @@ const templates = {
|
|
|
42
58
|
ACCEPT_EULA: 'Y',
|
|
43
59
|
MSSQL_SA_PASSWORD: 'SApassword1'
|
|
44
60
|
},
|
|
61
|
+
healthcheck: {
|
|
62
|
+
startPeriod: '30s',
|
|
63
|
+
test: '/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P SApassword1 -Q "SELECT 1" >/dev/null 2>&1'
|
|
64
|
+
},
|
|
45
65
|
image: 'mcr.microsoft.com/mssql/server:2022-latest',
|
|
46
66
|
port: '1433:1433',
|
|
47
67
|
volumePath: '/var/opt/mssql'
|
|
@@ -53,6 +73,10 @@ const templates = {
|
|
|
53
73
|
MYSQL_ROOT_PASSWORD: 'rootpassword',
|
|
54
74
|
MYSQL_USER: 'user'
|
|
55
75
|
},
|
|
76
|
+
healthcheck: {
|
|
77
|
+
startPeriod: '5s',
|
|
78
|
+
test: 'mysqladmin ping -h127.0.0.1 --silent'
|
|
79
|
+
},
|
|
56
80
|
image: 'mysql:8.0',
|
|
57
81
|
port: '3306:3306',
|
|
58
82
|
volumePath: '/var/lib/mysql'
|
|
@@ -63,6 +87,10 @@ const templates = {
|
|
|
63
87
|
POSTGRES_PASSWORD: 'password',
|
|
64
88
|
POSTGRES_USER: 'user'
|
|
65
89
|
},
|
|
90
|
+
healthcheck: {
|
|
91
|
+
startPeriod: '5s',
|
|
92
|
+
test: 'pg_isready -U user -h localhost --quiet'
|
|
93
|
+
},
|
|
66
94
|
image: 'postgres:15',
|
|
67
95
|
port: '5432:5432',
|
|
68
96
|
volumePath: '/var/lib/postgresql/data'
|
|
@@ -71,6 +99,10 @@ const templates = {
|
|
|
71
99
|
env: {
|
|
72
100
|
ROOT_PASSWORD: 'password'
|
|
73
101
|
},
|
|
102
|
+
healthcheck: {
|
|
103
|
+
startPeriod: '30s',
|
|
104
|
+
test: 'singlestore -u root -ppassword -e "SELECT 1" >/dev/null 2>&1'
|
|
105
|
+
},
|
|
74
106
|
image: 'ghcr.io/singlestore-labs/singlestoredb-dev', // NOTE: No tag specified due to data persistence
|
|
75
107
|
port: '3306:3306',
|
|
76
108
|
volumePath: '/data'
|
|
@@ -82,8 +114,8 @@ export const generateDockerContainer = (databaseEngine) => {
|
|
|
82
114
|
databaseEngine === 'sqlite') {
|
|
83
115
|
throw new Error('Internal type error: Expected a valid local database engine');
|
|
84
116
|
}
|
|
85
|
-
const {
|
|
86
|
-
const
|
|
117
|
+
const { command, env, healthcheck, image, port, volumePath } = templates[databaseEngine];
|
|
118
|
+
const commandLine = command ? `\n command: ${command}` : '';
|
|
87
119
|
const envLines = Object.entries(env)
|
|
88
120
|
.map(([key, value]) => ` ${key}: ${value}`)
|
|
89
121
|
.join('\n');
|
|
@@ -94,8 +126,13 @@ export const generateDockerContainer = (databaseEngine) => {
|
|
|
94
126
|
environment:
|
|
95
127
|
${envLines}
|
|
96
128
|
ports:
|
|
97
|
-
- "${port}"
|
|
98
|
-
|
|
129
|
+
- "${port}"${commandLine}
|
|
130
|
+
healthcheck:
|
|
131
|
+
test: ["CMD-SHELL", "${healthcheck.test.replaceAll('"', '\\"')}"]
|
|
132
|
+
interval: 2s
|
|
133
|
+
timeout: 5s
|
|
134
|
+
retries: 30
|
|
135
|
+
start_period: ${healthcheck.startPeriod}
|
|
99
136
|
volumes:
|
|
100
137
|
- db_data:${volumePath}
|
|
101
138
|
|
|
@@ -30,7 +30,7 @@ declare const driverConfigurations: {
|
|
|
30
30
|
readonly importLines: "";
|
|
31
31
|
readonly queries: QueryOperations;
|
|
32
32
|
};
|
|
33
|
-
readonly 'mongodb:
|
|
33
|
+
readonly 'mongodb:sql:local': {
|
|
34
34
|
readonly dbType: "Db";
|
|
35
35
|
readonly importLines: "";
|
|
36
36
|
readonly queries: QueryOperations;
|
|
@@ -4,5 +4,7 @@ type ScaffoldDatabaseProps = Pick<CreateConfiguration, 'projectName' | 'database
|
|
|
4
4
|
backendDirectory: string;
|
|
5
5
|
typesDirectory: string;
|
|
6
6
|
};
|
|
7
|
-
export declare const scaffoldDatabase: ({ projectName, databaseEngine, databaseHost, databaseDirectory, backendDirectory, authOption, orm, typesDirectory }: ScaffoldDatabaseProps) => Promise<
|
|
7
|
+
export declare const scaffoldDatabase: ({ projectName, databaseEngine, databaseHost, databaseDirectory, backendDirectory, authOption, orm, typesDirectory }: ScaffoldDatabaseProps) => Promise<{
|
|
8
|
+
dockerFreshInstall: boolean;
|
|
9
|
+
}>;
|
|
8
10
|
export {};
|
|
@@ -37,12 +37,13 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
|
|
|
37
37
|
databaseEngine !== 'sqlite' &&
|
|
38
38
|
databaseEngine !== undefined &&
|
|
39
39
|
databaseEngine !== 'none') {
|
|
40
|
-
await scaffoldDocker({
|
|
40
|
+
const { dockerFreshInstall } = await scaffoldDocker({
|
|
41
41
|
authOption,
|
|
42
42
|
databaseEngine,
|
|
43
43
|
projectDatabaseDirectory,
|
|
44
44
|
projectName
|
|
45
45
|
});
|
|
46
|
+
return { dockerFreshInstall };
|
|
46
47
|
}
|
|
47
48
|
if (orm === 'drizzle') {
|
|
48
49
|
if (!isDrizzleDialect(databaseEngine)) {
|
|
@@ -60,9 +61,10 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
|
|
|
60
61
|
databaseHost
|
|
61
62
|
});
|
|
62
63
|
writeFileSync(join(typesDirectory, 'databaseTypes.ts'), drizzleTypes);
|
|
63
|
-
return;
|
|
64
|
+
return { dockerFreshInstall: false };
|
|
64
65
|
}
|
|
65
66
|
if (orm === 'prisma') {
|
|
66
67
|
console.warn(`${dim('│')}\n${yellow('▲')} Prisma support is not implemented yet`);
|
|
67
68
|
}
|
|
69
|
+
return { dockerFreshInstall: false };
|
|
68
70
|
};
|
|
@@ -5,5 +5,7 @@ type ScaffoldDockerProps = {
|
|
|
5
5
|
authOption: AuthOption;
|
|
6
6
|
projectName: string;
|
|
7
7
|
};
|
|
8
|
-
export declare const scaffoldDocker: ({ databaseEngine, projectDatabaseDirectory, projectName, authOption }: ScaffoldDockerProps) => Promise<
|
|
8
|
+
export declare const scaffoldDocker: ({ databaseEngine, projectDatabaseDirectory, projectName, authOption }: ScaffoldDockerProps) => Promise<{
|
|
9
|
+
dockerFreshInstall: boolean;
|
|
10
|
+
}>;
|
|
9
11
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { $ } from 'bun';
|
|
4
|
-
import { checkDockerInstalled } from '../../utils/checkDockerInstalled';
|
|
4
|
+
import { checkDockerInstalled, ensureDockerDaemonRunning, resolveDockerExe, shutdownDockerDaemon } from '../../utils/checkDockerInstalled';
|
|
5
5
|
import { countHistoryTables, initTemplates, userTables } from './dockerInitTemplates';
|
|
6
6
|
import { generateDockerContainer } from './generateDockerContainer';
|
|
7
7
|
export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory, projectName, authOption }) => {
|
|
@@ -10,20 +10,30 @@ export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory,
|
|
|
10
10
|
databaseEngine === 'sqlite') {
|
|
11
11
|
throw new Error('Internal type error: databaseEngine must be defined and not "none" or "sqlite"');
|
|
12
12
|
}
|
|
13
|
-
await checkDockerInstalled();
|
|
13
|
+
const { freshInstall } = await checkDockerInstalled(databaseEngine);
|
|
14
|
+
const { daemonWasStarted } = await ensureDockerDaemonRunning();
|
|
14
15
|
const dbContainer = generateDockerContainer(databaseEngine);
|
|
15
16
|
writeFileSync(join(projectDatabaseDirectory, 'docker-compose.db.yml'), dbContainer, 'utf-8');
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
17
|
+
const docker = resolveDockerExe();
|
|
18
|
+
const hasSchemaInit = databaseEngine in userTables;
|
|
19
|
+
if (hasSchemaInit) {
|
|
20
|
+
const dbKey = databaseEngine;
|
|
21
|
+
const { wait, cli } = initTemplates[dbKey];
|
|
20
22
|
const usesAuth = authOption !== undefined && authOption !== 'none';
|
|
21
23
|
const dbCommand = usesAuth
|
|
22
|
-
? userTables[
|
|
23
|
-
: countHistoryTables[
|
|
24
|
-
await $
|
|
25
|
-
await $
|
|
24
|
+
? userTables[dbKey]
|
|
25
|
+
: countHistoryTables[dbKey];
|
|
26
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d db`.cwd(projectName);
|
|
27
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml exec -T db \
|
|
26
28
|
bash -lc '${wait} && ${cli} "${dbCommand}"'`.cwd(projectName);
|
|
27
|
-
await $
|
|
29
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`.cwd(projectName);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d --wait db`.cwd(projectName);
|
|
33
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`.cwd(projectName);
|
|
34
|
+
}
|
|
35
|
+
if (daemonWasStarted) {
|
|
36
|
+
await shutdownDockerDaemon();
|
|
28
37
|
}
|
|
38
|
+
return { dockerFreshInstall: freshInstall };
|
|
29
39
|
};
|
|
@@ -115,7 +115,10 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
|
|
|
115
115
|
`import { SQL } from 'bun'`,
|
|
116
116
|
`import { getEnv } from '@absolutejs/absolute'`
|
|
117
117
|
],
|
|
118
|
-
mongodb: [
|
|
118
|
+
mongodb: [
|
|
119
|
+
`import { MongoClient } from 'mongodb'`,
|
|
120
|
+
`import { getEnv } from '@absolutejs/absolute'`
|
|
121
|
+
],
|
|
119
122
|
mssql: [
|
|
120
123
|
`import { connect } from 'mssql'`,
|
|
121
124
|
`import { getEnv } from '@absolutejs/absolute'`
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { exit } from 'process';
|
|
2
|
+
import { exit, platform } from 'process';
|
|
3
3
|
import { outro } from '@clack/prompts';
|
|
4
|
+
import { cyan, yellow } from 'picocolors';
|
|
4
5
|
import { getDebugMessage, getOutroMessage, helpMessage } from './messages';
|
|
5
6
|
import { prompt } from './prompt';
|
|
6
7
|
import { scaffold } from './scaffold';
|
|
@@ -13,7 +14,12 @@ if (help === true) {
|
|
|
13
14
|
exit(0);
|
|
14
15
|
}
|
|
15
16
|
const response = await prompt(argumentConfiguration);
|
|
16
|
-
|
|
17
|
+
const { dockerFreshInstall } = await scaffold({
|
|
18
|
+
envVariables,
|
|
19
|
+
latest,
|
|
20
|
+
packageManager,
|
|
21
|
+
response
|
|
22
|
+
});
|
|
17
23
|
const debugMessage = debug !== false
|
|
18
24
|
? getDebugMessage({
|
|
19
25
|
packageManager,
|
|
@@ -26,3 +32,6 @@ const outroMessage = getOutroMessage({
|
|
|
26
32
|
projectName: response.projectName
|
|
27
33
|
});
|
|
28
34
|
outro(outroMessage + debugMessage);
|
|
35
|
+
if (dockerFreshInstall && platform === 'win32') {
|
|
36
|
+
console.log(`\n${yellow('▲')} Docker was freshly installed. Restart your terminal for ${cyan('docker')} to be available in PATH.\n`);
|
|
37
|
+
}
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -5,5 +5,7 @@ type ScaffoldProps = {
|
|
|
5
5
|
latest: boolean;
|
|
6
6
|
envVariables: string[] | undefined;
|
|
7
7
|
};
|
|
8
|
-
export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<
|
|
8
|
+
export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<{
|
|
9
|
+
dockerFreshInstall: boolean;
|
|
10
|
+
}>;
|
|
9
11
|
export {};
|
package/dist/scaffold.js
CHANGED
|
@@ -51,10 +51,11 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
|
|
|
51
51
|
plugins,
|
|
52
52
|
tailwind
|
|
53
53
|
});
|
|
54
|
-
|
|
54
|
+
let dockerFreshInstall = false;
|
|
55
|
+
if (databaseDirectory !== undefined &&
|
|
55
56
|
databaseEngine !== 'none' &&
|
|
56
|
-
databaseEngine !== undefined
|
|
57
|
-
|
|
57
|
+
databaseEngine !== undefined) {
|
|
58
|
+
const result = await scaffoldDatabase({
|
|
58
59
|
authOption,
|
|
59
60
|
backendDirectory,
|
|
60
61
|
databaseDirectory,
|
|
@@ -63,7 +64,9 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
|
|
|
63
64
|
orm,
|
|
64
65
|
projectName,
|
|
65
66
|
typesDirectory
|
|
66
|
-
})
|
|
67
|
+
});
|
|
68
|
+
dockerFreshInstall = result.dockerFreshInstall;
|
|
69
|
+
}
|
|
67
70
|
scaffoldFrontends({
|
|
68
71
|
absProviders,
|
|
69
72
|
assetsDirectory,
|
|
@@ -87,4 +90,5 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
|
|
|
87
90
|
if (initializeGitNow) {
|
|
88
91
|
await initializeGit(projectName);
|
|
89
92
|
}
|
|
93
|
+
return { dockerFreshInstall };
|
|
90
94
|
};
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
export declare const resolveDockerExe: () => string;
|
|
1
2
|
export declare const hasDocker: () => Promise<boolean>;
|
|
2
|
-
export declare const
|
|
3
|
+
export declare const isDockerDaemonRunning: () => Promise<boolean>;
|
|
4
|
+
export declare const ensureDockerDaemonRunning: () => Promise<{
|
|
5
|
+
daemonWasStarted: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const shutdownDockerDaemon: () => Promise<void>;
|
|
8
|
+
export declare const checkDockerInstalled: (databaseEngine?: string) => Promise<{
|
|
9
|
+
freshInstall: boolean;
|
|
10
|
+
}>;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
1
2
|
import os from 'os';
|
|
2
3
|
import { env, platform } from 'process';
|
|
3
4
|
import { confirm, spinner } from '@clack/prompts';
|
|
4
5
|
import { $ } from 'bun';
|
|
5
6
|
import { dim, yellow } from 'picocolors';
|
|
6
7
|
const DOCKER_URL = 'https://www.docker.com/products/docker-desktop';
|
|
8
|
+
const DOCKER_WIN_INSTALLER_URL = 'https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe';
|
|
9
|
+
const DOCKER_WIN_BIN_PATH = 'C:\\Program Files\\Docker\\Docker\\resources\\bin';
|
|
7
10
|
const isWSL = () => env.WSL_DISTRO_NAME !== undefined || /microsoft/i.test(os.release());
|
|
8
11
|
let hostEnv;
|
|
9
12
|
if (platform === 'win32') {
|
|
@@ -59,45 +62,48 @@ const configureDocker = async () => {
|
|
|
59
62
|
await $ `sudo systemctl enable --now docker`.quiet().nothrow();
|
|
60
63
|
spin.stop('Docker daemon running & permissions configured');
|
|
61
64
|
};
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const installWindowsDirectDownload = async () => {
|
|
66
|
+
const spin = spinner();
|
|
67
|
+
spin.start('Downloading Docker Desktop installer…');
|
|
68
|
+
const tmpDir = await import('os').then((os) => os.tmpdir());
|
|
69
|
+
const installerPath = `${tmpDir}\\DockerDesktopInstaller.exe`;
|
|
70
|
+
try {
|
|
71
|
+
const res = await Bun.fetch(DOCKER_WIN_INSTALLER_URL);
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
return false;
|
|
74
|
+
const buffer = await res.arrayBuffer();
|
|
75
|
+
await Bun.write(installerPath, buffer);
|
|
76
|
+
spin.stop('Docker Desktop installer downloaded');
|
|
77
|
+
spin.start('Running Docker Desktop installer…');
|
|
78
|
+
const runRes = await $ `powershell.exe -NoProfile -Command "Start-Process -FilePath '${installerPath}' -ArgumentList 'install','--quiet','--accept-license' -Wait -Verb RunAs"`
|
|
67
79
|
.quiet()
|
|
68
80
|
.nothrow();
|
|
69
|
-
spin.stop(
|
|
81
|
+
spin.stop(runRes.exitCode === 0
|
|
70
82
|
? 'Docker Desktop installed'
|
|
71
|
-
: '
|
|
72
|
-
return
|
|
83
|
+
: 'Installer failed');
|
|
84
|
+
return runRes.exitCode === 0;
|
|
73
85
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const res = await $ `choco install docker-desktop -y`.quiet().nothrow();
|
|
78
|
-
spin.stop(res.exitCode === 0
|
|
79
|
-
? 'Docker Desktop installed'
|
|
80
|
-
: 'Chocolatey install failed');
|
|
81
|
-
return res.exitCode === 0;
|
|
86
|
+
catch {
|
|
87
|
+
spin.stop('Direct download failed');
|
|
88
|
+
return false;
|
|
82
89
|
}
|
|
83
|
-
|
|
90
|
+
};
|
|
91
|
+
const installWindowsOpenBrowser = async () => {
|
|
92
|
+
await $ `powershell.exe -NoProfile -Command "Start-Process '${DOCKER_URL}'"`
|
|
93
|
+
.quiet()
|
|
94
|
+
.nothrow();
|
|
95
|
+
console.log(`Opened Docker Desktop download page. Install it, then run this again.`);
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
const installWindows = async () => {
|
|
99
|
+
if (await installWindowsDirectDownload())
|
|
100
|
+
return true;
|
|
101
|
+
await installWindowsOpenBrowser();
|
|
84
102
|
return false;
|
|
85
103
|
};
|
|
86
104
|
const installWSL = async () => {
|
|
87
105
|
if ((await $ `docker.exe --version`.quiet().nothrow()).exitCode === 0)
|
|
88
106
|
return true;
|
|
89
|
-
if (await commandExists('powershell.exe')) {
|
|
90
|
-
const spin = spinner();
|
|
91
|
-
spin.start('Installing Docker Desktop on Windows via winget');
|
|
92
|
-
const res = await $ `powershell.exe -NoProfile -Command winget install -e --id Docker.DockerDesktop`
|
|
93
|
-
.quiet()
|
|
94
|
-
.nothrow();
|
|
95
|
-
spin.stop(res.exitCode === 0
|
|
96
|
-
? 'Docker Desktop installed'
|
|
97
|
-
: 'winget install failed');
|
|
98
|
-
if (res.exitCode === 0)
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
107
|
if (await aptInstall())
|
|
102
108
|
return true;
|
|
103
109
|
return runGetDocker();
|
|
@@ -150,30 +156,122 @@ const installLinux = async () => {
|
|
|
150
156
|
console.log(`Automatic Linux install failed. See ${DOCKER_URL}`);
|
|
151
157
|
return false;
|
|
152
158
|
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
export const
|
|
156
|
-
if (
|
|
159
|
+
const DAEMON_WAIT_ATTEMPTS = 30;
|
|
160
|
+
const DAEMON_WAIT_INTERVAL_MS = 2000;
|
|
161
|
+
export const resolveDockerExe = () => {
|
|
162
|
+
if (platform === 'win32') {
|
|
163
|
+
const fullPath = `${DOCKER_WIN_BIN_PATH}\\docker.exe`;
|
|
164
|
+
if (existsSync(fullPath))
|
|
165
|
+
return fullPath;
|
|
166
|
+
}
|
|
167
|
+
return 'docker';
|
|
168
|
+
};
|
|
169
|
+
export const hasDocker = async () => {
|
|
170
|
+
const docker = resolveDockerExe();
|
|
171
|
+
return ((await $ `${docker} --version`.quiet().nothrow()).exitCode === 0 &&
|
|
172
|
+
(await $ `${docker} compose version`.quiet().nothrow()).exitCode === 0);
|
|
173
|
+
};
|
|
174
|
+
export const isDockerDaemonRunning = async () => {
|
|
175
|
+
const docker = resolveDockerExe();
|
|
176
|
+
return (await $ `${docker} info`.quiet().nothrow()).exitCode === 0;
|
|
177
|
+
};
|
|
178
|
+
const waitForDaemonReady = async () => {
|
|
179
|
+
for (let attempt = 0; attempt < DAEMON_WAIT_ATTEMPTS; attempt++) {
|
|
180
|
+
if (await isDockerDaemonRunning())
|
|
181
|
+
return true;
|
|
182
|
+
await new Promise((resolve) => setTimeout(resolve, DAEMON_WAIT_INTERVAL_MS));
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
};
|
|
186
|
+
const startDockerDaemon = async () => {
|
|
187
|
+
const spin = spinner();
|
|
188
|
+
spin.start('Starting Docker daemon…');
|
|
189
|
+
const docker = resolveDockerExe();
|
|
190
|
+
const desktopRes = await $ `${docker} desktop start`.quiet().nothrow();
|
|
191
|
+
if (desktopRes.exitCode === 0 && (await waitForDaemonReady())) {
|
|
192
|
+
spin.stop('Docker Desktop started');
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (platform === 'darwin') {
|
|
196
|
+
await $ `open -a Docker`.quiet().nothrow();
|
|
197
|
+
if (await waitForDaemonReady()) {
|
|
198
|
+
spin.stop('Docker Desktop started');
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
spin.stop('Docker daemon did not start');
|
|
202
|
+
throw new Error('Docker daemon did not start. Please start Docker Desktop manually.');
|
|
203
|
+
}
|
|
204
|
+
if (platform === 'win32') {
|
|
205
|
+
const dockerDesktopExe = 'C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe';
|
|
206
|
+
if (existsSync(dockerDesktopExe)) {
|
|
207
|
+
await $ `powershell.exe -NoProfile -Command "Start-Process '${dockerDesktopExe}'"`
|
|
208
|
+
.quiet()
|
|
209
|
+
.nothrow();
|
|
210
|
+
if (await waitForDaemonReady()) {
|
|
211
|
+
spin.stop('Docker Desktop started');
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
spin.stop('Docker Desktop start failed');
|
|
216
|
+
throw new Error('Docker Desktop failed to start. Please start it manually.');
|
|
217
|
+
}
|
|
218
|
+
await ensureSudo();
|
|
219
|
+
const systemctlRes = await $ `sudo systemctl start docker`.quiet().nothrow();
|
|
220
|
+
if (systemctlRes.exitCode !== 0) {
|
|
221
|
+
await $ `sudo service docker start`.quiet().nothrow();
|
|
222
|
+
}
|
|
223
|
+
if (await waitForDaemonReady()) {
|
|
224
|
+
spin.stop('Docker daemon started');
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
spin.stop('Docker daemon did not start');
|
|
228
|
+
throw new Error('Docker daemon did not start. Please start it manually.');
|
|
229
|
+
};
|
|
230
|
+
export const ensureDockerDaemonRunning = async () => {
|
|
231
|
+
if (await isDockerDaemonRunning()) {
|
|
232
|
+
return { daemonWasStarted: false };
|
|
233
|
+
}
|
|
234
|
+
await startDockerDaemon();
|
|
235
|
+
return { daemonWasStarted: true };
|
|
236
|
+
};
|
|
237
|
+
export const shutdownDockerDaemon = async () => {
|
|
238
|
+
const docker = resolveDockerExe();
|
|
239
|
+
const desktopRes = await $ `${docker} desktop shutdown`.quiet().nothrow();
|
|
240
|
+
if (desktopRes.exitCode === 0)
|
|
157
241
|
return;
|
|
242
|
+
if (platform !== 'win32') {
|
|
243
|
+
await ensureSudo();
|
|
244
|
+
await $ `sudo systemctl stop docker`.quiet().nothrow();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
export const checkDockerInstalled = async (databaseEngine) => {
|
|
248
|
+
if (await hasDocker())
|
|
249
|
+
return { freshInstall: false };
|
|
250
|
+
const dbLabel = databaseEngine ?? 'database';
|
|
158
251
|
const proceed = await confirm({
|
|
159
252
|
initialValue: true,
|
|
160
|
-
message:
|
|
253
|
+
message: `Docker Engine and Compose plugin are required for your local ${dbLabel}. Install them now?`
|
|
161
254
|
});
|
|
162
255
|
if (!proceed)
|
|
163
|
-
return;
|
|
256
|
+
return { freshInstall: false };
|
|
164
257
|
switch (hostEnv) {
|
|
165
258
|
case 'windows':
|
|
166
|
-
if (await installWindows())
|
|
167
|
-
|
|
259
|
+
if (await installWindows()) {
|
|
260
|
+
if (!env.PATH?.includes(DOCKER_WIN_BIN_PATH)) {
|
|
261
|
+
env.PATH = `${DOCKER_WIN_BIN_PATH};${env.PATH}`;
|
|
262
|
+
}
|
|
263
|
+
return { freshInstall: true };
|
|
264
|
+
}
|
|
168
265
|
break;
|
|
169
266
|
case 'wsl':
|
|
170
267
|
if (await installWSL())
|
|
171
|
-
return;
|
|
268
|
+
return { freshInstall: true };
|
|
172
269
|
break;
|
|
173
270
|
case 'linux':
|
|
174
271
|
if (await installLinux())
|
|
175
|
-
return;
|
|
272
|
+
return { freshInstall: true };
|
|
176
273
|
break;
|
|
177
274
|
}
|
|
178
275
|
console.log(`Couldn't install Docker automatically. Download it from ${DOCKER_URL}`);
|
|
276
|
+
return { freshInstall: false };
|
|
179
277
|
};
|
|
@@ -57,24 +57,24 @@ const apkInstallSqlite = async () => {
|
|
|
57
57
|
spin.stop(res.exitCode === 0 ? 'sqlite3 installed' : 'apk install failed');
|
|
58
58
|
return res.exitCode === 0;
|
|
59
59
|
};
|
|
60
|
+
const hasWinget = async () => (await $ `powershell.exe -NoProfile -Command "Get-Command winget"`
|
|
61
|
+
.quiet()
|
|
62
|
+
.nothrow()).exitCode === 0;
|
|
60
63
|
const installWindowsSqlite = async () => {
|
|
61
|
-
if (await
|
|
64
|
+
if (await hasWinget()) {
|
|
62
65
|
const spin = spinner();
|
|
66
|
+
spin.start('Updating winget sources');
|
|
67
|
+
await $ `powershell.exe -NoProfile -Command winget source update`
|
|
68
|
+
.quiet()
|
|
69
|
+
.nothrow();
|
|
70
|
+
spin.stop('winget sources updated');
|
|
63
71
|
spin.start('Installing sqlite3 with winget');
|
|
64
|
-
|
|
72
|
+
await $ `powershell.exe -NoProfile -Command "Start-Process winget -ArgumentList 'install','-e','--id','SQLite.SQLite','--accept-package-agreements','--accept-source-agreements' -Verb RunAs -Wait"`
|
|
65
73
|
.quiet()
|
|
66
74
|
.nothrow();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (await commandExists('choco')) {
|
|
71
|
-
const spin = spinner();
|
|
72
|
-
spin.start('Installing sqlite3 with Chocolatey');
|
|
73
|
-
const res = await $ `choco install sqlite -y`.quiet().nothrow();
|
|
74
|
-
spin.stop(res.exitCode === 0
|
|
75
|
-
? 'sqlite3 installed'
|
|
76
|
-
: 'Chocolatey install failed');
|
|
77
|
-
return res.exitCode === 0;
|
|
75
|
+
const installed = await hasSqlite();
|
|
76
|
+
spin.stop(installed ? 'sqlite3 installed' : 'winget install failed');
|
|
77
|
+
return installed;
|
|
78
78
|
}
|
|
79
79
|
console.log(`Automatic Windows install failed. Get sqlite3 from ${SQLITE_URL}`);
|
|
80
80
|
return false;
|
package/dist/versions.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Run `bun run check-versions` to compare against latest npm versions.
|
|
5
5
|
*/
|
|
6
6
|
export declare const versions: {
|
|
7
|
-
readonly '@absolutejs/absolute': "0.13.
|
|
7
|
+
readonly '@absolutejs/absolute': "0.13.5";
|
|
8
8
|
readonly '@absolutejs/auth': "0.22.0";
|
|
9
9
|
readonly '@elysiajs/static': "1.4.7";
|
|
10
10
|
readonly elysia: "1.4.22";
|
|
@@ -43,6 +43,7 @@ export declare const versions: {
|
|
|
43
43
|
readonly '@types/mssql': "9.1.9";
|
|
44
44
|
readonly '@types/pg': "8.16.0";
|
|
45
45
|
readonly gel: "2.2.0";
|
|
46
|
+
readonly mongodb: "6.12.0";
|
|
46
47
|
readonly mssql: "12.2.0";
|
|
47
48
|
readonly mysql2: "3.16.3";
|
|
48
49
|
readonly pg: "8.18.0";
|
package/dist/versions.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export const versions = {
|
|
7
7
|
/* ── Core ─────────────────────────────────────────────── */
|
|
8
|
-
'@absolutejs/absolute': '0.13.
|
|
8
|
+
'@absolutejs/absolute': '0.13.5',
|
|
9
9
|
'@absolutejs/auth': '0.22.0',
|
|
10
10
|
'@elysiajs/static': '1.4.7',
|
|
11
11
|
elysia: '1.4.22',
|
|
@@ -55,6 +55,7 @@ export const versions = {
|
|
|
55
55
|
'@types/mssql': '9.1.9',
|
|
56
56
|
'@types/pg': '8.16.0',
|
|
57
57
|
gel: '2.2.0',
|
|
58
|
+
mongodb: '6.12.0',
|
|
58
59
|
mssql: '12.2.0',
|
|
59
60
|
mysql2: '3.16.3',
|
|
60
61
|
pg: '8.18.0'
|
package/package.json
CHANGED