create-absolutejs 0.3.8 → 0.3.10

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.
Files changed (54) hide show
  1. package/README.md +17 -15
  2. package/dist/data.d.ts +4 -2
  3. package/dist/data.js +23 -8
  4. package/dist/generators/configurations/generateDrizzleConfig.d.ts +2 -1
  5. package/dist/generators/configurations/generateDrizzleConfig.js +16 -6
  6. package/dist/generators/configurations/generatePackageJson.d.ts +2 -2
  7. package/dist/generators/configurations/generatePackageJson.js +39 -12
  8. package/dist/generators/db/generateDBHandlers.d.ts +6 -0
  9. package/dist/generators/db/generateDBHandlers.js +11 -0
  10. package/dist/generators/db/generateDrizzleSchema.d.ts +8 -0
  11. package/dist/generators/db/generateDrizzleSchema.js +122 -0
  12. package/dist/generators/db/handlerTemplates.d.ts +52 -0
  13. package/dist/generators/db/handlerTemplates.js +168 -0
  14. package/dist/generators/db/scaffoldDatabase.d.ts +5 -6
  15. package/dist/generators/db/scaffoldDatabase.js +37 -6
  16. package/dist/generators/html/generateHTMLPage.d.ts +2 -2
  17. package/dist/generators/html/generateHTMLPage.js +7 -4
  18. package/dist/generators/html/scaffoldHTML.js +1 -1
  19. package/dist/generators/project/collectDependencies.d.ts +9 -0
  20. package/dist/generators/project/collectDependencies.js +16 -0
  21. package/dist/generators/project/computeFlags.d.ts +9 -0
  22. package/dist/generators/project/computeFlags.js +7 -0
  23. package/dist/generators/project/generateBuildBlock.d.ts +9 -0
  24. package/dist/generators/project/generateBuildBlock.js +13 -0
  25. package/dist/generators/project/generateDBBlock.d.ts +4 -0
  26. package/dist/generators/project/generateDBBlock.js +69 -0
  27. package/dist/generators/project/generateImportsBlock.d.ts +13 -0
  28. package/dist/generators/project/generateImportsBlock.js +124 -0
  29. package/dist/generators/project/generateRoutesBlock.d.ts +10 -0
  30. package/dist/generators/project/generateRoutesBlock.js +74 -0
  31. package/dist/generators/project/generateServer.d.ts +3 -4
  32. package/dist/generators/project/generateServer.js +46 -170
  33. package/dist/generators/project/generateUseBlock.d.ts +6 -0
  34. package/dist/generators/project/generateUseBlock.js +28 -0
  35. package/dist/messages.js +13 -13
  36. package/dist/prompt.js +6 -7
  37. package/dist/questions/databaseEngine.d.ts +1 -1
  38. package/dist/questions/databaseEngine.js +2 -1
  39. package/dist/questions/databaseHost.d.ts +1 -1
  40. package/dist/questions/databaseHost.js +10 -30
  41. package/dist/questions/directoryConfiguration.js +2 -2
  42. package/dist/questions/orm.d.ts +2 -1
  43. package/dist/questions/orm.js +13 -7
  44. package/dist/scaffold.d.ts +1 -1
  45. package/dist/scaffold.js +16 -8
  46. package/dist/templates/db/docker-compose.db.yml +15 -0
  47. package/dist/typeGuards.d.ts +3 -1
  48. package/dist/typeGuards.js +5 -19
  49. package/dist/types.d.ts +3 -1
  50. package/dist/utils/checkDockerInstalled.d.ts +2 -0
  51. package/dist/utils/checkDockerInstalled.js +179 -0
  52. package/dist/utils/parseCommandLineOptions.js +47 -15
  53. package/package.json +2 -2
  54. package/dist/templates/html/pages/HTMLExample.html +0 -66
@@ -1,7 +1,6 @@
1
- import type { AvailableDependency, CreateConfiguration } from '../../types';
2
- type CreateServerFileProps = Pick<CreateConfiguration, 'tailwind' | 'authProvider' | 'plugins' | 'buildDirectory' | 'assetsDirectory' | 'frontendDirectories'> & {
3
- availablePlugins: AvailableDependency[];
1
+ import type { CreateConfiguration } from '../../types';
2
+ type CreateServerFileProps = Pick<CreateConfiguration, 'tailwind' | 'authProvider' | 'databaseEngine' | 'plugins' | 'buildDirectory' | 'databaseHost' | 'orm' | 'assetsDirectory' | 'frontendDirectories'> & {
4
3
  backendDirectory: string;
5
4
  };
6
- export declare const generateServerFile: ({ tailwind, frontendDirectories, backendDirectory, authProvider, availablePlugins, buildDirectory, assetsDirectory, plugins }: CreateServerFileProps) => void;
5
+ export declare const generateServerFile: ({ tailwind, authProvider, plugins, buildDirectory, databaseEngine, databaseHost, orm, assetsDirectory, frontendDirectories, backendDirectory }: CreateServerFileProps) => void;
7
6
  export {};
@@ -1,179 +1,55 @@
1
- import { mkdirSync, writeFileSync } from 'fs';
1
+ import { writeFileSync, mkdirSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { UNFOUND_INDEX } from '../../constants';
4
- import { absoluteAuthPlugin, defaultDependencies, defaultPlugins, scopedStatePlugin } from '../../data';
5
- export const generateServerFile = ({ tailwind, frontendDirectories, backendDirectory, authProvider, availablePlugins, buildDirectory, assetsDirectory, plugins }) => {
3
+ import { collectDependencies } from './collectDependencies';
4
+ import { computeFlags } from './computeFlags';
5
+ import { generateBuildBlock } from './generateBuildBlock';
6
+ import { generateDBBlock } from './generateDBBlock';
7
+ import { generateImportsBlock } from './generateImportsBlock';
8
+ import { generateRoutesBlock } from './generateRoutesBlock';
9
+ import { generateUseBlock } from './generateUseBlock';
10
+ export const generateServerFile = ({ tailwind, authProvider, plugins, buildDirectory, databaseEngine, databaseHost, orm, assetsDirectory, frontendDirectories, backendDirectory }) => {
6
11
  const serverFilePath = join(backendDirectory, 'server.ts');
7
- const htmlDirectory = frontendDirectories['html'];
8
- const reactDirectory = frontendDirectories['react'];
9
- const svelteDirectory = frontendDirectories['svelte'];
10
- const vueDirectory = frontendDirectories['vue'];
11
- const htmxDirectory = frontendDirectories['htmx'];
12
- const requiresHtml = htmlDirectory !== undefined;
13
- const requiresReact = reactDirectory !== undefined;
14
- const requiresSvelte = svelteDirectory !== undefined;
15
- const requiresVue = vueDirectory !== undefined;
16
- const requiresHtmx = htmxDirectory !== undefined;
17
- const nonFrameworkOnly = (requiresHtml || requiresHtmx) &&
18
- !requiresReact &&
19
- !requiresSvelte &&
20
- !requiresVue;
21
- const selectedCustomPlugins = availablePlugins.filter(({ value }) => plugins.indexOf(value) !== UNFOUND_INDEX);
22
- const authenticationPlugins = authProvider === 'absoluteAuth' ? [absoluteAuthPlugin] : [];
23
- const htmxPlugins = requiresHtmx ? [scopedStatePlugin] : [];
24
- const allDependencies = [
25
- ...defaultDependencies,
26
- ...defaultPlugins,
27
- ...selectedCustomPlugins,
28
- ...authenticationPlugins,
29
- ...htmxPlugins
30
- ];
31
- const uniqueDependencies = Array.from(new Map(allDependencies.map((dependency) => [dependency.value, dependency])).values()).sort((a, b) => a.value.localeCompare(b.value));
32
- const importLines = uniqueDependencies.flatMap(({ value, imports }) => {
33
- const filteredImports = nonFrameworkOnly && imports
34
- ? imports.filter(({ packageName }) => packageName !== 'asset')
35
- : imports;
36
- return filteredImports && filteredImports.length > 0
37
- ? [
38
- `import { ${filteredImports
39
- .map(({ packageName }) => packageName)
40
- .join(', ')} } from '${value}';`
41
- ]
42
- : [];
12
+ const flags = computeFlags(frontendDirectories);
13
+ const deps = collectDependencies({ authProvider, flags, plugins });
14
+ const importsBlock = generateImportsBlock({
15
+ authProvider,
16
+ backendDirectory,
17
+ databaseEngine,
18
+ databaseHost,
19
+ deps,
20
+ flags,
21
+ orm
43
22
  });
44
- const absoluteImportIdx = importLines.findIndex((line) => line.includes("from '@absolutejs/absolute'"));
45
- if (absoluteImportIdx !== UNFOUND_INDEX && importLines[absoluteImportIdx]) {
46
- const existingItems = importLines[absoluteImportIdx]
47
- .replace(/import\s*\{\s*|\}\s*from.*$/g, '')
48
- .split(',')
49
- .map((item) => item.trim())
50
- .filter((value) => value.length > 0);
51
- const additionalItems = [
52
- requiresHtml &&
53
- !existingItems.includes('handleHTMLPageRequest') &&
54
- 'handleHTMLPageRequest',
55
- requiresReact &&
56
- !existingItems.includes('handleReactPageRequest') &&
57
- 'handleReactPageRequest',
58
- requiresSvelte &&
59
- !existingItems.includes('handleSveltePageRequest') &&
60
- 'handleSveltePageRequest',
61
- requiresVue &&
62
- !existingItems.includes('handleVuePageRequest') &&
63
- 'handleVuePageRequest',
64
- requiresVue &&
65
- !existingItems.includes('generateHeadElement') &&
66
- 'generateHeadElement',
67
- requiresHtmx &&
68
- !existingItems.includes('handleHTMXPageRequest') &&
69
- 'handleHTMXPageRequest'
70
- ].filter((value) => typeof value === 'string');
71
- importLines[absoluteImportIdx] = `import { ${[
72
- ...existingItems,
73
- ...additionalItems
74
- ].join(', ')} } from '@absolutejs/absolute';`;
75
- }
76
- if (reactDirectory !== undefined) {
77
- const reactImportSource = reactDirectory === ''
78
- ? '../frontend/pages/ReactExample'
79
- : `../frontend/${reactDirectory}/pages/ReactExample`;
80
- importLines.push(`import { ReactExample } from '${reactImportSource}';`);
81
- }
82
- if (requiresSvelte) {
83
- const svelteImportSource = svelteDirectory === ''
84
- ? '../frontend/pages/SvelteExample'
85
- : `../frontend/${svelteDirectory}/pages/SvelteExample`;
86
- importLines.push(`import SvelteExample from '${svelteImportSource}.svelte';`);
87
- }
88
- if (requiresVue) {
89
- const vueImportSource = vueDirectory === ''
90
- ? '../frontend/pages/VueExample'
91
- : `../frontend/${vueDirectory}/pages/VueExample`;
92
- const vueImportLine = requiresSvelte
93
- ? `import { vueImports } from './utils/vueImporter';\n\nconst { VueExample } = vueImports;`
94
- : `import VueExample from '${vueImportSource}.vue';`;
95
- importLines.push(vueImportLine);
96
- }
97
- if (requiresVue && requiresSvelte) {
98
- const vueImporter = `// This file is auto-generated by the AbsoluteJS project generator.
99
- // It is required to use Vue and Svelte together in the same project.
100
- // This is due to how the Vue Official plugin handles non .ts files.
101
-
102
- import VueExample from "../../frontend/vue/pages/VueExample.vue";
103
-
104
- export const vueImports = {
105
- VueExample
106
- } as const;
107
- `;
108
- const backendUtilsDirectory = join(backendDirectory, 'utils');
109
- mkdirSync(backendUtilsDirectory, { recursive: true });
110
- writeFileSync(join(backendUtilsDirectory, 'vueImporter.ts'), vueImporter);
111
- }
112
- const useStatements = uniqueDependencies
113
- .flatMap(({ imports }) => imports ?? [])
114
- .filter((entry) => entry.isPlugin)
115
- .map((entry) => {
116
- if (entry.config === undefined)
117
- return `.use(${entry.packageName})`;
118
- if (entry.config === null)
119
- return `.use(${entry.packageName}())`;
120
- return `.use(${entry.packageName}(${JSON.stringify(entry.config)}))`;
23
+ const manifestBlock = generateBuildBlock({
24
+ assetsDirectory,
25
+ buildDirectory,
26
+ frontendDirectories,
27
+ tailwind
121
28
  });
122
- const manifestOptions = [
123
- `assetsDirectory: '${assetsDirectory}'`,
124
- `buildDirectory: '${buildDirectory}'`,
125
- ...Object.entries(frontendDirectories).map(([frameworkName, directory]) => `${frameworkName}Directory: 'src/frontend/${directory}'`),
126
- tailwind ? `tailwind: ${JSON.stringify(tailwind)}` : ''
127
- ].filter(Boolean);
128
- const manifestDeclaration = `${nonFrameworkOnly ? '' : 'const manifest = '}await build({
129
- ${manifestOptions.join(',\n ')}
130
- });`;
131
- const routesData = Object.entries(frontendDirectories).reduce((acc, [frameworkName, directory], index) => {
132
- let handler;
133
- switch (frameworkName) {
134
- case 'html':
135
- handler = `handleHTMLPageRequest(\`${buildDirectory}${directory ? `/${directory}` : ''}/pages/HTMLExample.html\`)`;
136
- break;
137
- case 'react':
138
- handler = `handleReactPageRequest(ReactExample, asset(manifest, 'ReactExampleIndex'), { initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS') })`;
139
- break;
140
- case 'svelte':
141
- handler = `handleSveltePageRequest(SvelteExample, asset(manifest, 'SvelteExample'), asset(manifest, 'SvelteExampleIndex'), { initialCount: 0, cssPath: asset(manifest, 'SvelteExampleCSS') })`;
142
- break;
143
- case 'vue':
144
- handler = `handleVuePageRequest(VueExample, asset(manifest, 'VueExample'), asset(manifest, 'VueExampleIndex'), generateHeadElement({ cssPath: asset(manifest, 'VueExampleCSS'), title: 'AbsoluteJS + Vue' }), { initialCount: 0 })`;
145
- break;
146
- case 'htmx':
147
- handler = `handleHTMXPageRequest(\`${buildDirectory}${directory ? `/${directory}` : ''}/pages/HTMXExample.html\`)`;
148
- if (index === 0) {
149
- acc.indexRoute = `.get('/', () => ${handler})`;
150
- }
151
- acc.otherRoutes.push(`.get('/htmx', () => ${handler})`, `.post('/htmx/reset', ({ resetScopedStore }) => resetScopedStore())`, `.get('/htmx/count', ({ scopedStore }) => scopedStore.count)`, `.post('/htmx/increment', ({ scopedStore }) => ++scopedStore.count)`);
152
- return acc;
153
- default:
154
- return acc;
155
- }
156
- if (index === 0) {
157
- acc.indexRoute = `.get('/', () => ${handler})`;
158
- }
159
- acc.otherRoutes.push(`.get('/${frameworkName}', () => ${handler})`);
160
- return acc;
161
- }, { indexRoute: null, otherRoutes: [] });
162
- const routes = [routesData.indexRoute ?? '', ...routesData.otherRoutes]
163
- .filter(Boolean)
164
- .join('\n ');
165
- const useLines = useStatements.map((s) => ` ${s}`).join('\n');
166
- const serverFileContent = `${importLines.join('\n')}
167
-
168
- ${manifestDeclaration}
29
+ const dbBlock = generateDBBlock({ databaseEngine, databaseHost, orm });
30
+ const useBlock = generateUseBlock({
31
+ databaseEngine,
32
+ deps,
33
+ orm
34
+ });
35
+ const routesBlock = generateRoutesBlock({
36
+ authProvider,
37
+ buildDirectory,
38
+ flags,
39
+ frontendDirectories
40
+ });
41
+ const content = `${importsBlock}
169
42
 
43
+ ${manifestBlock}
44
+ ${dbBlock}
170
45
  new Elysia()
171
- ${useLines}
172
- ${routes}
173
- .on('error', (err) => {
174
- const { request } = err;
175
- console.error(\`Server error on \${request.method} \${request.url}: \${err.message}\`);
46
+ ${useBlock}
47
+ ${routesBlock}
48
+ .on('error', err => {
49
+ const { request } = err
50
+ console.error(\`Server error on \${request.method} \${request.url}: \${err.message}\`)
176
51
  });
177
52
  `;
178
- writeFileSync(serverFilePath, serverFileContent);
53
+ mkdirSync(backendDirectory, { recursive: true });
54
+ writeFileSync(serverFilePath, content);
179
55
  };
@@ -0,0 +1,6 @@
1
+ import type { AvailableDependency, DatabaseEngine, ORM } from '../../types';
2
+ export declare const generateUseBlock: ({ deps, databaseEngine, orm }: {
3
+ deps: AvailableDependency[];
4
+ databaseEngine: DatabaseEngine;
5
+ orm: ORM;
6
+ }) => string;
@@ -0,0 +1,28 @@
1
+ export const generateUseBlock = ({ deps, databaseEngine, orm }) => deps
2
+ .flatMap((dependency) => dependency.imports ?? [])
3
+ .filter((pluginImport) => pluginImport.isPlugin)
4
+ .map((pluginImport) => {
5
+ const isAuth = pluginImport.packageName === 'absoluteAuth';
6
+ if (isAuth) {
7
+ const baseConfigString = pluginImport.config !== null
8
+ ? JSON.stringify(pluginImport.config).slice(1, -1)
9
+ : '';
10
+ const hasDatabase = databaseEngine !== undefined && databaseEngine !== 'none';
11
+ const hasOrm = orm !== undefined && orm !== 'none';
12
+ const instantiate = 'instantiateUserSession';
13
+ const pluginGeneric = hasOrm ? '<User>' : '';
14
+ const callback = hasDatabase
15
+ ? `async ({ authProvider, providerInstance, tokenResponse, userSessionId, session }) => ${instantiate}({ authProvider, providerInstance, session, tokenResponse, userSessionId, createUser: (userIdentity) => createUser({ authProvider, db, userIdentity }), getUser: (userIdentity) => getUser({ authProvider, db, userIdentity }) })`
16
+ : `({ authProvider, tokenResponse, userSessionId }) => { console.log(\`Successfully authorized OAuth2 with \${authProvider} (session: \${userSessionId})\`, tokenResponse); }`;
17
+ const mergedConfig = `{ ${baseConfigString}${baseConfigString ? ',' : ''} onCallbackSuccess: ${callback} }`;
18
+ return `.use(absoluteAuth${pluginGeneric}(${mergedConfig}))`;
19
+ }
20
+ if (pluginImport.config === undefined) {
21
+ return `.use(${pluginImport.packageName})`;
22
+ }
23
+ if (pluginImport.config === null) {
24
+ return `.use(${pluginImport.packageName}())`;
25
+ }
26
+ return `.use(${pluginImport.packageName}(${JSON.stringify(pluginImport.config)}))`;
27
+ })
28
+ .join('\n');
package/dist/messages.js CHANGED
@@ -12,36 +12,36 @@ Options:
12
12
  ${cyan('--debug, -d')} Display a summary of the project configuration after creation
13
13
 
14
14
  ${cyan('--angular')} Include an Angular frontend
15
- ${cyan('--angular-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Angular frontend
16
- ${cyan('--assets')} ${dim(cyan('<dir>'))} Directory name for your static assets
15
+ ${cyan('--angular-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Angular frontend
16
+ ${cyan('--assets')} ${dim(cyan('<directory>'))} Directory name for your static assets
17
17
  ${cyan('--auth')} ${dim(cyan('<plugin>'))} Pre-configured auth plugin (currently only "absolute-auth") or 'none'
18
18
  ${cyan('--biome')} Use Biome for code quality and formatting
19
- ${cyan('--build')} ${dim(cyan('<dir>'))} Output directory for build artifacts
20
- ${cyan('--database')} ${dim(cyan('<dir>'))} Directory name for your database files
19
+ ${cyan('--build')} ${dim(cyan('<directory>'))} Output directory for build artifacts
20
+ ${cyan('--db')} ${dim(cyan('<engine>'))} Database engine (postgresql | mysql | sqlite | mongodb | mariadb | gel | singlestore | cockroachdb | mssql) or 'none'
21
+ ${cyan('--db-dir')} ${dim(cyan('<directory>'))} Directory name for your database files
22
+ ${cyan('--db-host')} ${dim(cyan('<host>'))} Database host provider (neon | planetscale) or 'none'
21
23
  ${cyan('--directory')} ${dim(cyan('<mode>'))} Directory-naming strategy: "default" or "custom"
22
- ${cyan('--engine')} ${dim(cyan('<engine>'))} Database engine (postgresql | mysql | sqlite | mongodb | redis | singlestore | cockroachdb | mssql) or 'none'
23
- ${cyan('--eslint+prettier')} Use ESLint + Prettier for code quality and formatting
24
+ ${cyan('--eslint+prettier')} Use ESLint + Prettier for code quality and formatting
24
25
  ${cyan('--git')} Initialize a Git repository
25
- ${cyan('--host')} ${dim(cyan('<host>'))} Database host provider (neon | planetscale | supabase | turso | vercel | upstash | atlas) or 'none'
26
26
  ${cyan('--html')} Include a plain HTML frontend
27
- ${cyan('--html-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the HTML frontend
28
- ${cyan('--html-scripts')} Enable HTML scripting with TypeScript
27
+ ${cyan('--html-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the HTML frontend
28
+ ${cyan('--html-scripts')} Enable HTML scripting with TypeScript
29
29
  ${cyan('--htmx')} Include an HTMX frontend
30
- ${cyan('--htmx-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the HTMX frontend
30
+ ${cyan('--htmx-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the HTMX frontend
31
31
  ${cyan('--install')} Use the same package manager to install dependencies
32
32
  ${cyan('--lts')} Use LTS versions of required packages
33
33
  ${cyan('--orm')} ${dim(cyan('<orm>'))} ORM to configure: "drizzle" | "prisma" | 'none'
34
34
  ${cyan('--plugin')} ${dim(cyan('<plugin>'))} Elysia plugin(s) to include (repeatable); 'none' skips plugin setup
35
35
  ${cyan('--react')} Include a React frontend
36
- ${cyan('--react-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the React frontend
36
+ ${cyan('--react-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the React frontend
37
37
  ${cyan('--skip')} Skip non-required prompts; uses 'none' for all optional configs
38
38
  ${cyan('--svelte')} Include a Svelte frontend
39
- ${cyan('--svelte-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Svelte frontend
39
+ ${cyan('--svelte-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Svelte frontend
40
40
  ${cyan('--tailwind')} Include Tailwind CSS setup
41
41
  ${cyan('--tailwind-input')} ${dim(cyan('<file>'))} Path to your Tailwind CSS entry file
42
42
  ${cyan('--tailwind-output')} ${dim(cyan('<file>'))} Path for the generated Tailwind CSS bundle
43
43
  ${cyan('--vue')} Include a Vue frontend
44
- ${cyan('--vue-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Vue frontend
44
+ ${cyan('--vue-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Vue frontend
45
45
  `;
46
46
  export const getOutroMessage = ({ projectName, packageManager, installDependenciesNow }) => `${green('Created successfully')}, you can now run:\n\n` +
47
47
  `${cyan('cd')} ${projectName}\n` +
package/dist/prompt.js CHANGED
@@ -23,11 +23,10 @@ export const prompt = async (argumentConfiguration) => {
23
23
  // 4. Frontend(s)
24
24
  const frontends = argumentConfiguration.frontends?.filter((frontend) => frontend !== undefined) ?? (await getFrontends());
25
25
  // 5. HTML scripting option (if HTML was selected)
26
- const useHTMLScripts = !frontends.includes('html') ||
27
- argumentConfiguration.useHTMLScripts === undefined
28
- ? false
29
- : (argumentConfiguration.useHTMLScripts ??
30
- (await getHtmlScriptingOption()));
26
+ const useHTMLScripts = frontends.includes('html')
27
+ ? (argumentConfiguration.useHTMLScripts ??
28
+ (await getHtmlScriptingOption()))
29
+ : false;
31
30
  // 6. Database engine
32
31
  const databaseEngine = argumentConfiguration.databaseEngine ?? (await getDatabaseEngine());
33
32
  // 7. Database host
@@ -35,7 +34,7 @@ export const prompt = async (argumentConfiguration) => {
35
34
  (await getDatabaseHost(databaseEngine));
36
35
  // 8. ORM choice
37
36
  const orm = databaseEngine !== undefined && databaseEngine !== 'none'
38
- ? (argumentConfiguration.orm ?? (await getORM()))
37
+ ? (argumentConfiguration.orm ?? (await getORM(databaseEngine)))
39
38
  : undefined;
40
39
  // 9. Configuration type
41
40
  let directoryConfig = argumentConfiguration.directoryConfig ?? (await getConfigurationType());
@@ -46,7 +45,7 @@ export const prompt = async (argumentConfiguration) => {
46
45
  directoryConfig,
47
46
  useTailwind
48
47
  });
49
- // 11. Framework-specific directories
48
+ // 11. Framework specific directories
50
49
  const frontendDirectories = await getFrontendDirectoryConfigurations(directoryConfig, frontends, argumentConfiguration.frontendDirectories);
51
50
  // If the user specified a custom directory configuration, we need to update the configuration type
52
51
  if (argumentConfiguration.frontendDirectories !== undefined)
@@ -1 +1 @@
1
- export declare const getDatabaseEngine: () => Promise<"postgresql" | "mysql" | "sqlite" | "mongodb" | "redis" | "singlestore" | "cockroachdb" | "mssql" | undefined>;
1
+ export declare const getDatabaseEngine: () => Promise<"gel" | "mysql" | "postgresql" | "sqlite" | "singlestore" | "mongodb" | "mariadb" | "cockroachdb" | "mssql" | undefined>;
@@ -9,7 +9,8 @@ export const getDatabaseEngine = async () => {
9
9
  { label: cyan('PostgreSQL'), value: 'postgresql' },
10
10
  { label: magenta('SQLite'), value: 'sqlite' },
11
11
  { label: green('MySQL'), value: 'mysql' },
12
- { label: red('Redis'), value: 'redis' },
12
+ { label: red('MariaDB'), value: 'mariadb' },
13
+ { label: cyan('Gel'), value: 'gel' },
13
14
  { label: green('MongoDB'), value: 'mongodb' },
14
15
  { label: magenta('SingleStore'), value: 'singlestore' },
15
16
  { label: yellow('SQL Server'), value: 'mssql' },
@@ -1,2 +1,2 @@
1
1
  import type { DatabaseEngine } from '../types';
2
- export declare const getDatabaseHost: (databaseEngine: DatabaseEngine) => Promise<"neon" | "planetscale" | "supabase" | "turso" | "upstash" | "atlas" | undefined>;
2
+ export declare const getDatabaseHost: (databaseEngine: DatabaseEngine) => Promise<"neon" | "planetscale" | "turso" | undefined>;
@@ -1,4 +1,4 @@
1
- import { select, isCancel, confirm } from '@clack/prompts';
1
+ import { select, isCancel } from '@clack/prompts';
2
2
  import { cyan } from 'picocolors';
3
3
  import { abort } from '../utils/abort';
4
4
  export const getDatabaseHost = async (databaseEngine) => {
@@ -6,46 +6,26 @@ export const getDatabaseHost = async (databaseEngine) => {
6
6
  const databaseHost = await select({
7
7
  message: 'Select database host:',
8
8
  options: [
9
+ { label: 'None', value: 'none' },
9
10
  { label: cyan('Neon'), value: 'neon' },
10
- { label: cyan('Supabase'), value: 'supabase' },
11
- { label: 'None', value: 'none' }
11
+ { label: cyan('PlanetScale'), value: 'planetscale' }
12
12
  ]
13
13
  });
14
14
  if (isCancel(databaseHost))
15
15
  abort();
16
16
  return databaseHost === 'none' ? undefined : databaseHost;
17
17
  }
18
- if (databaseEngine === 'mysql') {
19
- const databaseHost = await confirm({
20
- message: 'Are you using PlanetScale?'
21
- });
22
- if (isCancel(databaseHost))
23
- abort();
24
- return databaseHost ? 'planetscale' : undefined;
25
- }
26
18
  if (databaseEngine === 'sqlite') {
27
- const databaseHost = await confirm({
28
- message: 'Are you using Turso?'
29
- });
30
- if (isCancel(databaseHost))
31
- abort();
32
- return databaseHost ? 'turso' : undefined;
33
- }
34
- if (databaseEngine === 'mongodb') {
35
- const databaseHost = await confirm({
36
- message: 'Are you using Atlas?'
37
- });
38
- if (isCancel(databaseHost))
39
- abort();
40
- return databaseHost ? 'atlas' : undefined;
41
- }
42
- if (databaseEngine === 'redis') {
43
- const databaseHost = await confirm({
44
- message: 'Are you using Upstash?'
19
+ const databaseHost = await select({
20
+ message: 'Select database host:',
21
+ options: [
22
+ { label: 'None', value: 'none' },
23
+ { label: cyan('Turso'), value: 'turso' }
24
+ ]
45
25
  });
46
26
  if (isCancel(databaseHost))
47
27
  abort();
48
- return databaseHost ? 'upstash' : undefined;
28
+ return databaseHost === 'none' ? undefined : databaseHost;
49
29
  }
50
30
  return undefined;
51
31
  };
@@ -5,7 +5,7 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
5
5
  return {
6
6
  assetsDirectory: argumentConfiguration.assetsDirectory ?? 'src/backend/assets',
7
7
  buildDirectory: argumentConfiguration.buildDirectory ?? 'build',
8
- databaseDirectory: databaseEngine
8
+ databaseDirectory: databaseEngine !== undefined && databaseEngine !== 'none'
9
9
  ? (argumentConfiguration.databaseDirectory ?? 'db')
10
10
  : undefined,
11
11
  tailwind: useTailwind
@@ -58,7 +58,7 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
58
58
  }
59
59
  // Database
60
60
  let databaseDirectory;
61
- if (databaseEngine !== undefined) {
61
+ if (databaseEngine !== undefined && databaseEngine !== 'none') {
62
62
  databaseDirectory =
63
63
  argumentConfiguration.databaseDirectory ??
64
64
  (await text({
@@ -1 +1,2 @@
1
- export declare const getORM: () => Promise<"drizzle" | "prisma" | undefined>;
1
+ import { DatabaseEngine } from '../types';
2
+ export declare const getORM: (databaseEngine: Exclude<DatabaseEngine, "none" | undefined>) => Promise<"drizzle" | "prisma" | undefined>;
@@ -1,14 +1,20 @@
1
1
  import { select, isCancel } from '@clack/prompts';
2
2
  import { cyan, magenta } from 'picocolors';
3
+ import { isDrizzleDialect, isPrismaDialect } from '../typeGuards';
3
4
  import { abort } from '../utils/abort';
4
- export const getORM = async () => {
5
+ export const getORM = async (databaseEngine) => {
6
+ const options = [
7
+ { label: 'None', value: 'none' }
8
+ ];
9
+ if (isDrizzleDialect(databaseEngine)) {
10
+ options.push({ label: cyan('Drizzle'), value: 'drizzle' });
11
+ }
12
+ if (isPrismaDialect(databaseEngine)) {
13
+ options.push({ label: magenta('Prisma'), value: 'prisma' });
14
+ }
5
15
  const orm = await select({
6
- message: 'Choose an ORM (optional):',
7
- options: [
8
- { label: 'None', value: 'none' },
9
- { label: cyan('Drizzle'), value: 'drizzle' },
10
- { label: magenta('Prisma'), value: 'prisma' }
11
- ]
16
+ message: 'Choose an ORM for your database:',
17
+ options
12
18
  });
13
19
  if (isCancel(orm))
14
20
  abort();
@@ -4,5 +4,5 @@ type ScaffoldProps = {
4
4
  packageManager: PackageManager;
5
5
  latest: boolean;
6
6
  };
7
- export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, packageManager }: ScaffoldProps) => Promise<void>;
7
+ export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, packageManager }: ScaffoldProps) => Promise<void>;
8
8
  export {};
package/dist/scaffold.js CHANGED
@@ -4,16 +4,13 @@ import { fileURLToPath } from 'node:url';
4
4
  import { formatProject } from './commands/formatProject';
5
5
  import { initializeGit } from './commands/initializeGit';
6
6
  import { installDependencies } from './commands/installDependencies';
7
- import { availablePlugins } from './data';
8
7
  import { addConfigurationFiles } from './generators/configurations/addConfigurationFiles';
9
8
  import { createPackageJson } from './generators/configurations/generatePackageJson';
10
9
  import { initalizeRoot } from './generators/configurations/initializeRoot';
11
10
  import { scaffoldDatabase } from './generators/db/scaffoldDatabase';
12
11
  import { generateServerFile } from './generators/project/generateServer';
13
12
  import { scaffoldFrontends } from './generators/project/scaffoldFrontends';
14
- export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine,
15
- // databaseHost,
16
- useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, packageManager }) => {
13
+ export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, packageManager }) => {
17
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
18
15
  const templatesDirectory = join(__dirname, '/templates');
19
16
  const { frontendDirectory, backendDirectory, projectAssetsDirectory } = initalizeRoot(projectName, templatesDirectory);
@@ -29,8 +26,11 @@ useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authPro
29
26
  createPackageJson({
30
27
  authProvider,
31
28
  codeQualityTool,
29
+ databaseEngine,
30
+ databaseHost,
32
31
  frontendDirectories,
33
32
  latest,
33
+ orm,
34
34
  plugins,
35
35
  projectName,
36
36
  useTailwind
@@ -38,20 +38,28 @@ useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authPro
38
38
  generateServerFile({
39
39
  assetsDirectory,
40
40
  authProvider,
41
- availablePlugins,
42
41
  backendDirectory,
43
42
  buildDirectory,
43
+ databaseEngine,
44
+ databaseHost,
44
45
  frontendDirectories,
46
+ orm,
45
47
  plugins,
46
48
  tailwind
47
49
  });
48
50
  void (databaseDirectory !== undefined &&
49
- scaffoldDatabase({
51
+ databaseEngine !== 'none' &&
52
+ databaseEngine !== undefined &&
53
+ (await scaffoldDatabase({
54
+ authProvider,
55
+ backendDirectory,
50
56
  databaseDirectory,
51
57
  databaseEngine,
58
+ databaseHost,
52
59
  orm,
53
- projectName
54
- }));
60
+ projectName,
61
+ templatesDirectory
62
+ })));
55
63
  scaffoldFrontends({
56
64
  frontendDirectories,
57
65
  frontendDirectory,
@@ -0,0 +1,15 @@
1
+ services:
2
+ db:
3
+ image: postgres:15
4
+ restart: always
5
+ environment:
6
+ POSTGRES_USER: postgres
7
+ POSTGRES_PASSWORD: postgres
8
+ POSTGRES_DB: appdb
9
+ ports:
10
+ - "5432:5432"
11
+ volumes:
12
+ - db_data:/var/lib/postgresql/data
13
+
14
+ volumes:
15
+ db_data:
@@ -1,6 +1,8 @@
1
- import type { AuthProvider, CodeQualityTool, DatabaseEngine, DatabaseHost, Frontend, ORM } from './types';
1
+ import type { AuthProvider, AvailableDrizzleDialect, CodeQualityTool, DatabaseEngine, DatabaseHost, Frontend, ORM } from './types';
2
2
  export declare const isAuthProvider: (value: string | undefined) => value is AuthProvider;
3
3
  export declare const isDirectoryConfig: (value: string) => value is "default" | "custom";
4
+ export declare const isDrizzleDialect: (value: string | undefined) => value is AvailableDrizzleDialect;
5
+ export declare const isPrismaDialect: (value: string | undefined) => value is string;
4
6
  export declare const isDatabaseEngine: (value: string | undefined) => value is DatabaseEngine;
5
7
  export declare const isDatabaseHost: (value: string | undefined) => value is DatabaseHost;
6
8
  export declare const isORM: (value: string | undefined) => value is ORM;
@@ -1,24 +1,10 @@
1
- import { frontendLabels } from './data';
1
+ import { availableDatabaseEngines, availableDatabaseHosts, availableDrizzleDialects, availablePrismaDialects, frontendLabels } from './data';
2
2
  export const isAuthProvider = (value) => value === 'absoluteAuth' || value === 'none' || value === undefined;
3
3
  export const isDirectoryConfig = (value) => value === 'default' || value === 'custom';
4
- export const isDatabaseEngine = (value) => value === 'postgresql' ||
5
- value === 'mysql' ||
6
- value === 'sqlite' ||
7
- value === 'mongodb' ||
8
- value === 'redis' ||
9
- value === 'singlestore' ||
10
- value === 'cockroachdb' ||
11
- value === 'mssql' ||
12
- value === 'none' ||
13
- value === undefined;
14
- export const isDatabaseHost = (value) => value === 'neon' ||
15
- value === 'planetscale' ||
16
- value === 'supabase' ||
17
- value === 'turso' ||
18
- value === 'vercel' ||
19
- value === 'upstash' ||
20
- value === 'atlas' ||
21
- value === undefined;
4
+ export const isDrizzleDialect = (value) => availableDrizzleDialects.some((dialect) => dialect === value);
5
+ export const isPrismaDialect = (value) => availablePrismaDialects.some((dialect) => dialect === value);
6
+ export const isDatabaseEngine = (value) => availableDatabaseEngines.some((engine) => engine === value);
7
+ export const isDatabaseHost = (value) => availableDatabaseHosts.some((host) => host === value);
22
8
  export const isORM = (value) => value === 'drizzle' || value === 'prisma' || value === undefined;
23
9
  export const isCodeQualityTool = (value) => value === 'eslint+prettier' || value === 'biome' || value === undefined;
24
10
  export const isFrontend = (value) => value !== undefined && Object.keys(frontendLabels).includes(value);