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
@@ -0,0 +1,168 @@
1
+ const buildSqlAuthTemplate = ({ importLines, dbType, queries }) => `
2
+ import { isValidProviderOption, providers } from 'citra'
3
+ ${importLines}
4
+
5
+ type UserHandlerProps = {
6
+ authProvider: string
7
+ db: ${dbType}
8
+ userIdentity: Record<string, unknown>
9
+ }
10
+
11
+ export const getUser = async ({ authProvider, db, userIdentity }: UserHandlerProps) => {
12
+ if (!isValidProviderOption(authProvider)) throw new Error(\`Invalid auth provider: \${authProvider}\`)
13
+ const subject = providers[authProvider].extractSubjectFromIdentity(userIdentity)
14
+ const authSub = \`\${authProvider.toUpperCase()}|\${subject}\`
15
+ ${queries.selectUser}
16
+ }
17
+
18
+ export const createUser = async ({ authProvider, db, userIdentity }: UserHandlerProps) => {
19
+ if (!isValidProviderOption(authProvider)) throw new Error(\`Invalid auth provider: \${authProvider}\`)
20
+ const subject = providers[authProvider].extractSubjectFromIdentity(userIdentity)
21
+ const authSub = \`\${authProvider.toUpperCase()}|\${subject}\`
22
+ ${queries.insertUser}
23
+ }
24
+ `;
25
+ const buildSqlCountTemplate = ({ importLines, dbType, queries }) => `
26
+ ${importLines}
27
+
28
+ export const getCountHistory = async (db: ${dbType}, uid: number) => {
29
+ ${queries.selectHistory}
30
+ }
31
+
32
+ export const createCountHistory = async (db: ${dbType}, count: number) => {
33
+ ${queries.insertHistory}
34
+ }
35
+ `;
36
+ const drizzleQueryOperations = {
37
+ insertHistory: `const [newHistory] = await db.insert(schema.countHistory).values({ count }).returning()
38
+ return newHistory`,
39
+ insertUser: `const [newUser] = await db.insert(schema.users).values({ auth_sub: authSub, metadata: userIdentity }).returning()
40
+ if (!newUser) throw new Error('Failed to create user')
41
+ return newUser`,
42
+ selectHistory: `const [history] = await db.select().from(schema.countHistory).where(eq(schema.countHistory.uid, uid)).execute()
43
+ return history`,
44
+ selectUser: `const [user] = await db.select().from(schema.users).where(eq(schema.users.auth_sub, authSub)).execute()
45
+ return user`
46
+ };
47
+ const libsqlQueryOperations = {
48
+ insertHistory: `const { rows } = await db.execute({ sql: 'INSERT INTO count_history (count) VALUES (?) RETURNING *', args: [count] })
49
+ return rows[0]`,
50
+ insertUser: `const { rows } = await db.execute({ sql: 'INSERT INTO users (auth_sub, metadata) VALUES (?, ?) RETURNING *', args: [authSub, JSON.stringify(userIdentity)] })
51
+ const newUser = rows[0]
52
+ if (!newUser) throw new Error('Failed to create user')
53
+ return newUser`,
54
+ selectHistory: `const { rows } = await db.execute({ sql: 'SELECT * FROM count_history WHERE uid = ? LIMIT 1', args: [uid] })
55
+ return rows[0] ?? null`,
56
+ selectUser: `const { rows } = await db.execute({ sql: 'SELECT * FROM users WHERE auth_sub = ? LIMIT 1', args: [authSub] })
57
+ return rows[0] ?? null`
58
+ };
59
+ const bunSqliteQueryOperations = {
60
+ insertHistory: `db.run('INSERT INTO count_history (count) VALUES (?)', [count])
61
+ const statement = db.query('SELECT * FROM count_history ORDER BY rowid DESC LIMIT 1')
62
+ const [newHistory] = statement.all()
63
+ return newHistory`,
64
+ insertUser: `db.run('INSERT INTO users (auth_sub, metadata) VALUES (?, ?)', [authSub, JSON.stringify(userIdentity)])
65
+ const statement = db.query('SELECT * FROM users WHERE auth_sub = ? LIMIT 1')
66
+ const [newUser] = statement.all(authSub)
67
+ if (!newUser) throw new Error('Failed to create user')
68
+ return newUser`,
69
+ selectHistory: `const statement = db.query('SELECT * FROM count_history WHERE uid = ? LIMIT 1')
70
+ const [history] = statement.all(uid)
71
+ return history ?? null`,
72
+ selectUser: `const statement = db.query('SELECT * FROM users WHERE auth_sub = ? LIMIT 1')
73
+ const [user] = statement.all(authSub)
74
+ return user ?? null`
75
+ };
76
+ const postgresSqlQueryOperations = {
77
+ insertHistory: `const [newHistory] = await db\`
78
+ INSERT INTO count_history (count)
79
+ VALUES (\${count})
80
+ RETURNING *
81
+ \`
82
+ return newHistory`,
83
+ insertUser: `const [newUser] = await db\`
84
+ INSERT INTO users (auth_sub, metadata)
85
+ VALUES (\${authSub}, \${userIdentity})
86
+ RETURNING *
87
+ \`
88
+ if (!newUser) throw new Error('Failed to create user')
89
+ return newUser`,
90
+ selectHistory: `const [history] = await db\`
91
+ SELECT * FROM count_history
92
+ WHERE uid = \${uid}
93
+ LIMIT 1
94
+ \`
95
+ return history ?? null`,
96
+ selectUser: `const [user] = await db\`
97
+ SELECT * FROM users
98
+ WHERE auth_sub = \${authSub}
99
+ LIMIT 1
100
+ \`
101
+ return user ?? null`
102
+ };
103
+ const driverConfigurations = {
104
+ 'postgresql:drizzle:local': {
105
+ dbType: 'BunSQLDatabase<SchemaType>',
106
+ importLines: `
107
+ import { eq } from 'drizzle-orm'
108
+ import { BunSQLDatabase } from 'drizzle-orm/bun-sql'
109
+ import { schema, type SchemaType } from '../../../db/schema'`,
110
+ queries: drizzleQueryOperations
111
+ },
112
+ 'postgresql:drizzle:neon': {
113
+ dbType: 'NeonHttpDatabase<SchemaType>',
114
+ importLines: `
115
+ import { eq } from 'drizzle-orm'
116
+ import { NeonHttpDatabase } from 'drizzle-orm/neon-http'
117
+ import { schema, type SchemaType } from '../../../db/schema'`,
118
+ queries: drizzleQueryOperations
119
+ },
120
+ 'postgresql:sql:local': {
121
+ dbType: 'SQL',
122
+ importLines: `import { SQL } from 'bun'`,
123
+ queries: postgresSqlQueryOperations
124
+ },
125
+ 'postgresql:sql:neon': {
126
+ dbType: 'NeonQueryFunction<false, false>',
127
+ importLines: `import { NeonQueryFunction } from '@neondatabase/serverless'`,
128
+ queries: postgresSqlQueryOperations
129
+ },
130
+ 'sqlite:drizzle:local': {
131
+ dbType: 'BunSQLiteDatabase<SchemaType>',
132
+ importLines: `
133
+ import { eq } from 'drizzle-orm'
134
+ import { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite'
135
+ import { schema, type SchemaType } from '../../../db/schema'`,
136
+ queries: drizzleQueryOperations
137
+ },
138
+ 'sqlite:drizzle:turso': {
139
+ dbType: 'LibSQLDatabase<SchemaType>',
140
+ importLines: `
141
+ import { eq } from 'drizzle-orm'
142
+ import { LibSQLDatabase } from 'drizzle-orm/libsql'
143
+ import { schema, type SchemaType } from '../../../db/schema'`,
144
+ queries: drizzleQueryOperations
145
+ },
146
+ 'sqlite:sql:local': {
147
+ dbType: 'Database',
148
+ importLines: `import { Database } from 'bun:sqlite'`,
149
+ queries: bunSqliteQueryOperations
150
+ },
151
+ 'sqlite:sql:turso': {
152
+ dbType: 'Client',
153
+ importLines: `import { Client } from '@libsql/client'`,
154
+ queries: libsqlQueryOperations
155
+ }
156
+ };
157
+ export const getAuthTemplate = (key) => {
158
+ const configuration = driverConfigurations[key];
159
+ if (!configuration)
160
+ throw new Error(`Unsupported driver configuration: ${key}`);
161
+ return buildSqlAuthTemplate(configuration);
162
+ };
163
+ export const getCountTemplate = (key) => {
164
+ const configuration = driverConfigurations[key];
165
+ if (!configuration)
166
+ throw new Error(`Unsupported driver configuration: ${key}`);
167
+ return buildSqlCountTemplate(configuration);
168
+ };
@@ -1,9 +1,8 @@
1
- import type { DatabaseEngine, ORM } from '../../types';
2
- type ScaffoldDatabaseProps = {
3
- projectName: string;
4
- orm: ORM;
5
- databaseEngine: DatabaseEngine;
1
+ import type { CreateConfiguration } from '../../types';
2
+ type ScaffoldDatabaseProps = Pick<CreateConfiguration, 'projectName' | 'databaseHost' | 'orm' | 'databaseDirectory' | 'authProvider' | 'databaseEngine'> & {
6
3
  databaseDirectory: string;
4
+ templatesDirectory: string;
5
+ backendDirectory: string;
7
6
  };
8
- export declare const scaffoldDatabase: ({ projectName, databaseEngine, databaseDirectory, orm }: ScaffoldDatabaseProps) => void;
7
+ export declare const scaffoldDatabase: ({ projectName, databaseEngine, databaseHost, databaseDirectory, backendDirectory, templatesDirectory, authProvider, orm }: ScaffoldDatabaseProps) => Promise<void>;
9
8
  export {};
@@ -1,14 +1,45 @@
1
- import { mkdirSync } from 'fs';
1
+ import { copyFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { dim, yellow } from 'picocolors';
4
+ import { isDrizzleDialect } from '../../typeGuards';
5
+ import { checkDockerInstalled } from '../../utils/checkDockerInstalled';
4
6
  import { createDrizzleConfig } from '../configurations/generateDrizzleConfig';
5
- export const scaffoldDatabase = ({ projectName, databaseEngine, databaseDirectory, orm }) => {
6
- mkdirSync(join(projectName, databaseDirectory), { recursive: true });
7
- if (databaseEngine !== 'postgresql' && databaseEngine !== 'none') {
8
- console.warn(`${dim('│')}\n${yellow('▲')} Only PostgreSQL support is implemented so far`);
7
+ import { generateDBHandlers } from './generateDBHandlers';
8
+ import { generateDrizzleSchema } from './generateDrizzleSchema';
9
+ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHost, databaseDirectory, backendDirectory, templatesDirectory, authProvider, orm }) => {
10
+ const projectDatabaseDirectory = join(projectName, databaseDirectory);
11
+ const handlerDirectory = join(backendDirectory, 'handlers');
12
+ mkdirSync(projectDatabaseDirectory, { recursive: true });
13
+ mkdirSync(handlerDirectory, { recursive: true });
14
+ const usesAuth = authProvider !== undefined && authProvider !== 'none';
15
+ const handlerFileName = usesAuth
16
+ ? 'userHandlers.ts'
17
+ : 'countHistoryHandlers.ts';
18
+ const dbHandlers = generateDBHandlers({
19
+ databaseEngine,
20
+ databaseHost,
21
+ orm,
22
+ usesAuth
23
+ });
24
+ writeFileSync(join(handlerDirectory, handlerFileName), dbHandlers, 'utf-8');
25
+ if (databaseEngine === 'postgresql' &&
26
+ (databaseHost === undefined || databaseHost === 'none')) {
27
+ await checkDockerInstalled();
28
+ copyFileSync(join(templatesDirectory, 'db', 'docker-compose.db.yml'), join(projectDatabaseDirectory, 'docker-compose.db.yml'));
9
29
  }
10
30
  if (orm === 'drizzle') {
11
- createDrizzleConfig({ databaseEngine, projectName });
31
+ if (!isDrizzleDialect(databaseEngine)) {
32
+ throw new Error('Internal type error: Expected a Drizzle dialect');
33
+ }
34
+ const drizzleSchema = generateDrizzleSchema({
35
+ authProvider,
36
+ databaseEngine,
37
+ databaseHost
38
+ });
39
+ const schemaFilePath = join(projectDatabaseDirectory, 'schema.ts');
40
+ writeFileSync(schemaFilePath, drizzleSchema);
41
+ createDrizzleConfig({ databaseDirectory, databaseEngine, projectName });
42
+ return;
12
43
  }
13
44
  if (orm === 'prisma') {
14
45
  console.warn(`${dim('│')}\n${yellow('▲')} Prisma support is not implemented yet`);
@@ -1,2 +1,2 @@
1
- import { Frontend } from '../../types';
2
- export declare const generateHTMLPage: (frontends: Frontend[]) => string;
1
+ import { CreateConfiguration, Frontend } from '../../types';
2
+ export declare const generateHTMLPage: (frontends: Frontend[], useHTMLScripts: CreateConfiguration["useHTMLScripts"]) => string;
@@ -1,6 +1,10 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateHTMLPage = (frontends) => {
2
+ export const generateHTMLPage = (frontends, useHTMLScripts) => {
3
3
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ const initialCount = useHTMLScripts ? '0' : 'disabled';
5
+ const scriptTagBlock = useHTMLScripts
6
+ ? ` <script src="../scripts/typescript-example.ts"></script>\n`
7
+ : '';
4
8
  return `<!doctype html>
5
9
  <html>
6
10
  <head>
@@ -44,7 +48,7 @@ export const generateHTMLPage = (frontends) => {
44
48
  </nav>
45
49
  <h1>AbsoluteJS + HTML</h1>
46
50
  <button id="counter-button">
47
- count is <span id="counter">0</span>
51
+ count is <span id="counter">${initialCount}</span>
48
52
  </button>
49
53
  <p>
50
54
  Edit <code>example/html/pages/HtmlExample.html</code> save and
@@ -59,8 +63,7 @@ export const generateHTMLPage = (frontends) => {
59
63
  Click on the AbsoluteJS and HTML logos to learn more.
60
64
  </p>
61
65
  </main>
62
- <script src="../scripts/typescript-example.ts"></script>
63
- </body>
66
+ ${scriptTagBlock} </body>
64
67
  </html>
65
68
  `;
66
69
  };
@@ -4,7 +4,7 @@ import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
4
  import { generateHTMLPage } from './generateHTMLPage';
5
5
  export const scaffoldHTML = ({ isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }) => {
6
6
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'HTML5_Badge.svg'), join(projectAssetsDirectory, 'svg', 'HTML5_Badge.svg'));
7
- const htmlPage = generateHTMLPage(frontends);
7
+ const htmlPage = generateHTMLPage(frontends, useHTMLScripts);
8
8
  const pagesDirectory = join(targetDirectory, 'pages');
9
9
  mkdirSync(pagesDirectory, { recursive: true });
10
10
  const htmlFilePath = join(pagesDirectory, 'HTMLExample.html');
@@ -0,0 +1,9 @@
1
+ import type { CreateConfiguration } from '../../types';
2
+ import type { FrameworkFlags } from './computeFlags';
3
+ type CollectDependenciesProps = {
4
+ plugins: string[];
5
+ authProvider: CreateConfiguration['authProvider'];
6
+ flags: FrameworkFlags;
7
+ };
8
+ export declare const collectDependencies: ({ plugins, authProvider, flags }: CollectDependenciesProps) => import("../../types").AvailableDependency[];
9
+ export {};
@@ -0,0 +1,16 @@
1
+ import { defaultDependencies, defaultPlugins, absoluteAuthPlugin, scopedStatePlugin, availablePlugins } from '../../data';
2
+ export const collectDependencies = ({ plugins, authProvider, flags }) => {
3
+ const customSelections = availablePlugins.filter((plugin) => plugins.includes(plugin.value));
4
+ const authPlugins = authProvider === 'absoluteAuth' ? [absoluteAuthPlugin] : [];
5
+ const htmxPlugins = flags.requiresHtmx ? [scopedStatePlugin] : [];
6
+ const allDeps = [
7
+ ...defaultDependencies,
8
+ ...defaultPlugins,
9
+ ...customSelections,
10
+ ...authPlugins,
11
+ ...htmxPlugins
12
+ ];
13
+ const uniqueDeps = Array.from(new Map(allDeps.map((dependency) => [dependency.value, dependency])).values());
14
+ uniqueDeps.sort((firstDep, secondDep) => firstDep.value.localeCompare(secondDep.value));
15
+ return uniqueDeps;
16
+ };
@@ -0,0 +1,9 @@
1
+ import type { FrontendDirectories } from '../../types';
2
+ export type FrameworkFlags = ReturnType<typeof computeFlags>;
3
+ export declare const computeFlags: (dirs: FrontendDirectories) => {
4
+ requiresHtml: boolean;
5
+ requiresHtmx: boolean;
6
+ requiresReact: boolean;
7
+ requiresSvelte: boolean;
8
+ requiresVue: boolean;
9
+ };
@@ -0,0 +1,7 @@
1
+ export const computeFlags = (dirs) => ({
2
+ requiresHtml: dirs.html !== undefined,
3
+ requiresHtmx: dirs.htmx !== undefined,
4
+ requiresReact: dirs.react !== undefined,
5
+ requiresSvelte: dirs.svelte !== undefined,
6
+ requiresVue: dirs.vue !== undefined
7
+ });
@@ -0,0 +1,9 @@
1
+ import type { CreateConfiguration, FrontendDirectories } from '../../types';
2
+ type GenerateBuildBlockProps = {
3
+ assetsDirectory: string;
4
+ buildDirectory: string;
5
+ frontendDirectories: FrontendDirectories;
6
+ tailwind: CreateConfiguration['tailwind'];
7
+ };
8
+ export declare const generateBuildBlock: ({ assetsDirectory, buildDirectory, frontendDirectories, tailwind }: GenerateBuildBlockProps) => string;
9
+ export {};
@@ -0,0 +1,13 @@
1
+ export const generateBuildBlock = ({ assetsDirectory, buildDirectory, frontendDirectories, tailwind }) => {
2
+ const opts = [
3
+ `assetsDirectory: '${assetsDirectory}'`,
4
+ `buildDirectory: '${buildDirectory}'`,
5
+ ...Object.entries(frontendDirectories).map(([f, dir]) => `${f}Directory: 'src/frontend${dir ? `/${dir}` : ''}'`),
6
+ tailwind ? `tailwind: ${JSON.stringify(tailwind)}` : ''
7
+ ]
8
+ .filter(Boolean)
9
+ .join(',\n ');
10
+ const frameworks = ['react', 'svelte', 'vue'];
11
+ const nonFrameworkOnly = frameworks.every((f) => frontendDirectories[f] === undefined);
12
+ return `${nonFrameworkOnly ? '' : 'const manifest = '}await build({\n ${opts}\n});`;
13
+ };
@@ -0,0 +1,4 @@
1
+ import type { CreateConfiguration } from '../../types';
2
+ type GenerateDBBlockProps = Pick<CreateConfiguration, 'databaseEngine' | 'orm' | 'databaseHost'>;
3
+ export declare const generateDBBlock: ({ databaseEngine, orm, databaseHost }: GenerateDBBlockProps) => string;
4
+ export {};
@@ -0,0 +1,69 @@
1
+ export const generateDBBlock = ({ databaseEngine, orm, databaseHost }) => {
2
+ const isLocalPostgres = databaseEngine === 'postgresql' &&
3
+ (!databaseHost || databaseHost === 'none');
4
+ if (isLocalPostgres && orm === 'drizzle') {
5
+ return `
6
+ const sql = new SQL(getEnv("DATABASE_URL"))
7
+ const db = drizzle(sql, { schema })
8
+ `;
9
+ }
10
+ if (isLocalPostgres) {
11
+ return `
12
+ const db = new SQL(getEnv("DATABASE_URL"))
13
+ await db.connect();
14
+ `;
15
+ }
16
+ const isNeonPostgres = databaseEngine === 'postgresql' && databaseHost === 'neon';
17
+ if (isNeonPostgres && orm === 'drizzle') {
18
+ return `
19
+ const sql = neon(getEnv("DATABASE_URL"))
20
+ const db = drizzle(sql, { schema })
21
+ `;
22
+ }
23
+ if (isNeonPostgres) {
24
+ return `
25
+ const db = neon(getEnv("DATABASE_URL"))
26
+ `;
27
+ }
28
+ const isLocalSqlite = databaseEngine === 'sqlite' &&
29
+ (!databaseHost || databaseHost === 'none');
30
+ if (isLocalSqlite && orm === 'drizzle') {
31
+ return `
32
+ const sql = new Database("database.sqlite")
33
+ const db = drizzle(sql, { schema })
34
+ `;
35
+ }
36
+ if (isLocalSqlite) {
37
+ return `
38
+ const db = new Database("database.sqlite")
39
+ `;
40
+ }
41
+ const isTursoSqlite = databaseEngine === 'sqlite' && databaseHost === 'turso';
42
+ if (isTursoSqlite && orm === 'drizzle') {
43
+ return `
44
+ const sql = createClient({ url: getEnv("DATABASE_URL") })
45
+ const db = drizzle(sql, { schema })
46
+ `;
47
+ }
48
+ if (isTursoSqlite) {
49
+ return `
50
+ const db = createClient({ url: getEnv("DATABASE_URL") })
51
+ `;
52
+ }
53
+ if (orm !== 'drizzle') {
54
+ return '';
55
+ }
56
+ const clientInitMap = {
57
+ neon: 'const sql = neon(getEnv("DATABASE_URL"))',
58
+ planetscale: 'const sql = connect({ url: getEnv("DATABASE_URL") })',
59
+ turso: 'const sql = createClient({ url: getEnv("DATABASE_URL") })'
60
+ };
61
+ if (databaseHost === 'none' || databaseHost === undefined) {
62
+ return ``;
63
+ }
64
+ const initLine = clientInitMap[databaseHost];
65
+ return `
66
+ ${initLine}
67
+ const db = drizzle(sql, { schema })
68
+ `;
69
+ };
@@ -0,0 +1,13 @@
1
+ import type { AvailableDependency, CreateConfiguration } from '../../types';
2
+ import type { FrameworkFlags } from './computeFlags';
3
+ type GenerateImportsBlockProps = {
4
+ backendDirectory: string;
5
+ deps: AvailableDependency[];
6
+ flags: FrameworkFlags;
7
+ orm: CreateConfiguration['orm'];
8
+ authProvider: CreateConfiguration['authProvider'];
9
+ databaseEngine: CreateConfiguration['databaseEngine'];
10
+ databaseHost: CreateConfiguration['databaseHost'];
11
+ };
12
+ export declare const generateImportsBlock: ({ backendDirectory, deps, flags, orm, authProvider, databaseEngine, databaseHost }: GenerateImportsBlockProps) => string;
13
+ export {};
@@ -0,0 +1,124 @@
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authProvider, databaseEngine, databaseHost }) => {
4
+ const rawImports = [];
5
+ const pushHandler = (cond, name) => cond &&
6
+ rawImports.push(`import { ${name} } from '@absolutejs/absolute'`);
7
+ pushHandler(flags.requiresHtml, 'handleHTMLPageRequest');
8
+ pushHandler(flags.requiresReact, 'handleReactPageRequest');
9
+ pushHandler(flags.requiresSvelte, 'handleSveltePageRequest');
10
+ pushHandler(flags.requiresVue, 'handleVuePageRequest');
11
+ pushHandler(flags.requiresVue, 'generateHeadElement');
12
+ pushHandler(flags.requiresHtmx, 'handleHTMXPageRequest');
13
+ const nonFrameworkOnly = (flags.requiresHtml || flags.requiresHtmx) &&
14
+ !flags.requiresReact &&
15
+ !flags.requiresSvelte &&
16
+ !flags.requiresVue;
17
+ for (const dependency of deps) {
18
+ const importsList = dependency.imports ?? [];
19
+ const relevantImports = nonFrameworkOnly
20
+ ? importsList.filter((imp) => imp.packageName !== 'asset')
21
+ : importsList;
22
+ if (relevantImports.length === 0)
23
+ continue;
24
+ rawImports.push(`import { ${relevantImports
25
+ .map((imp) => imp.packageName)
26
+ .sort()
27
+ .join(', ')} } from '${dependency.value}'`);
28
+ }
29
+ if (flags.requiresReact)
30
+ rawImports.push(`import { ReactExample } from '../frontend/react/pages/ReactExample'`);
31
+ if (flags.requiresSvelte)
32
+ rawImports.push(`import SvelteExample from '../frontend/svelte/pages/SvelteExample.svelte'`);
33
+ if (flags.requiresVue && !flags.requiresSvelte)
34
+ rawImports.push(`import VueExample from '../frontend/vue/pages/VueExample.vue'`);
35
+ const connectorImports = {
36
+ neon: ["import { neon } from '@neondatabase/serverless'"],
37
+ planetscale: ["import { connect } from '@planetscale/database'"],
38
+ turso: ["import { createClient } from '@libsql/client'"]
39
+ };
40
+ const dialectImports = {
41
+ neon: ["import { drizzle } from 'drizzle-orm/neon-http'"],
42
+ planetscale: [
43
+ "import { drizzle } from 'drizzle-orm/planetscale-serverless'"
44
+ ],
45
+ turso: ["import { drizzle } from 'drizzle-orm/libsql'"]
46
+ };
47
+ const isRemoteHost = databaseHost !== undefined && databaseHost !== 'none';
48
+ if (orm === 'drizzle' && isRemoteHost) {
49
+ const key = databaseHost;
50
+ rawImports.push(...connectorImports[key], ...dialectImports[key]);
51
+ }
52
+ if (orm === 'drizzle' && !isRemoteHost && databaseEngine === 'postgresql')
53
+ rawImports.push(`import { SQL } from 'bun'`, `import { drizzle } from 'drizzle-orm/bun-sql'`);
54
+ if (orm === 'drizzle' && databaseEngine === 'sqlite' && !isRemoteHost)
55
+ rawImports.push(`import { Database } from 'bun:sqlite'`, `import { drizzle } from 'drizzle-orm/bun-sqlite'`);
56
+ if ((orm ?? 'none') === 'none' && databaseEngine === 'sqlite')
57
+ rawImports.push(...(databaseHost === 'turso'
58
+ ? [
59
+ `import { createClient } from '@libsql/client'`,
60
+ `import { getEnv } from '@absolutejs/absolute'`
61
+ ]
62
+ : [`import { Database } from 'bun:sqlite'`]));
63
+ if (orm === 'drizzle') {
64
+ rawImports.push(`import { Elysia } from 'elysia'`, ...(databaseEngine === 'sqlite' && !isRemoteHost
65
+ ? []
66
+ : [`import { getEnv } from '@absolutejs/absolute'`]), authProvider === 'absoluteAuth'
67
+ ? `import { schema, User } from '../../db/schema'`
68
+ : `import { schema } from '../../db/schema'`);
69
+ }
70
+ if ((orm === undefined || orm === 'none') &&
71
+ databaseEngine === 'postgresql')
72
+ rawImports.push(...(isRemoteHost
73
+ ? connectorImports[databaseHost]
74
+ : [`import { SQL } from 'bun'`]), `import { getEnv } from '@absolutejs/absolute'`);
75
+ if (authProvider === 'absoluteAuth')
76
+ rawImports.push(`import { absoluteAuth, instantiateUserSession } from '@absolutejs/auth'`, ...(databaseEngine && databaseEngine !== 'none'
77
+ ? [
78
+ `import { createUser, getUser } from './handlers/userHandlers'`
79
+ ]
80
+ : []));
81
+ if ((authProvider === undefined || authProvider === 'none') &&
82
+ databaseEngine !== undefined &&
83
+ databaseEngine !== 'none')
84
+ rawImports.push(`import { getCountHistory, createCountHistory } from './handlers/countHistoryHandlers'`, `import { t } from 'elysia'`);
85
+ if (flags.requiresVue && flags.requiresSvelte) {
86
+ const utilsDir = join(backendDirectory, 'utils');
87
+ mkdirSync(utilsDir, { recursive: true });
88
+ writeFileSync(join(utilsDir, 'vueImporter.ts'), `import VueExample from "../../frontend/vue/pages/VueExample.vue"\n\nexport const vueImports = { VueExample } as const\n`);
89
+ rawImports.push(`import { vueImports } from './utils/vueImporter'`);
90
+ }
91
+ const importMap = new Map();
92
+ for (const stmt of rawImports) {
93
+ const match = stmt.match(/^import\s+(.+)\s+from\s+['"](.+)['"];?/);
94
+ if (!match)
95
+ continue;
96
+ const [, importClause, modulePath] = match;
97
+ if (!importClause || !modulePath)
98
+ continue;
99
+ const entry = importMap.get(modulePath) ?? {
100
+ defaultImport: null,
101
+ namedImports: new Set()
102
+ };
103
+ importMap.set(modulePath, entry);
104
+ void (importClause.startsWith('{')
105
+ ? importClause
106
+ .slice(1, -1)
107
+ .split(',')
108
+ .map((segment) => segment.trim())
109
+ .filter(Boolean)
110
+ .forEach((name) => entry.namedImports.add(name))
111
+ : (entry.defaultImport = importClause.trim()));
112
+ }
113
+ return Array.from(importMap.entries())
114
+ .sort(([a], [b]) => a.localeCompare(b))
115
+ .map(([path, { defaultImport, namedImports }]) => {
116
+ const parts = [];
117
+ if (defaultImport)
118
+ parts.push(defaultImport);
119
+ if (namedImports.size)
120
+ parts.push(`{ ${[...namedImports].sort().join(', ')} }`);
121
+ return `import ${parts.join(', ')} from '${path}'`;
122
+ })
123
+ .join('\n');
124
+ };
@@ -0,0 +1,10 @@
1
+ import type { AuthProvider, FrontendDirectories } from '../../types';
2
+ import type { FrameworkFlags } from './computeFlags';
3
+ type GenerateRoutesBlockProps = {
4
+ flags: FrameworkFlags;
5
+ frontendDirectories: FrontendDirectories;
6
+ authProvider: AuthProvider;
7
+ buildDirectory: string;
8
+ };
9
+ export declare const generateRoutesBlock: ({ flags, frontendDirectories, authProvider, buildDirectory }: GenerateRoutesBlockProps) => string;
10
+ export {};
@@ -0,0 +1,74 @@
1
+ import { isFrontend } from '../../typeGuards';
2
+ export const generateRoutesBlock = ({ flags, frontendDirectories, authProvider, buildDirectory }) => {
3
+ const routes = [];
4
+ const createHandlerCall = (frontend, directory) => {
5
+ const base = `${buildDirectory}${directory ? `/${directory}` : ''}/pages`;
6
+ if (frontend === 'html')
7
+ return `handleHTMLPageRequest(\`${base}/HTMLExample.html\`)`;
8
+ if (frontend === 'htmx')
9
+ return `handleHTMXPageRequest(\`${base}/HTMXExample.html\`)`;
10
+ if (frontend === 'react')
11
+ return `handleReactPageRequest(
12
+ ReactExample,
13
+ asset(manifest, 'ReactExampleIndex'),
14
+ { initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS') }
15
+ )`;
16
+ if (frontend === 'svelte')
17
+ return `handleSveltePageRequest(
18
+ SvelteExample,
19
+ asset(manifest, 'SvelteExample'),
20
+ asset(manifest, 'SvelteExampleIndex'),
21
+ { initialCount: 0, cssPath: asset(manifest, 'SvelteExampleCSS') }
22
+ )`;
23
+ if (frontend === 'vue')
24
+ return flags.requiresSvelte
25
+ ? `handleVuePageRequest(
26
+ vueImports.VueExample,
27
+ asset(manifest, 'VueExample'),
28
+ asset(manifest, 'VueExampleIndex'),
29
+ generateHeadElement({
30
+ cssPath: asset(manifest, 'VueExampleCSS'),
31
+ title: 'AbsoluteJS + Vue',
32
+ description: 'A Vue.js example with AbsoluteJS'
33
+ }),
34
+ { initialCount: 0 }
35
+ )`
36
+ : `handleVuePageRequest(
37
+ VueExample,
38
+ asset(manifest, 'VueExample'),
39
+ asset(manifest, 'VueExampleIndex'),
40
+ generateHeadElement({
41
+ cssPath: asset(manifest, 'VueExampleCSS'),
42
+ title: 'AbsoluteJS + Vue',
43
+ description: 'A Vue.js example with AbsoluteJS'
44
+ }),
45
+ { initialCount: 0 }
46
+ )`;
47
+ return '';
48
+ };
49
+ Object.entries(frontendDirectories).forEach(([frontend, directory], entryIndex) => {
50
+ if (!isFrontend(frontend))
51
+ return;
52
+ const handlerCall = createHandlerCall(frontend, directory);
53
+ if (entryIndex === 0)
54
+ routes.push(`.get('/', () => ${handlerCall})`);
55
+ if (frontend === 'htmx') {
56
+ routes.push(`.get('/htmx', () => ${handlerCall})`, `.post('/htmx/reset', ({ resetScopedStore }) => resetScopedStore())`, `.get('/htmx/count', ({ scopedStore }) => scopedStore.count)`, `.post('/htmx/increment', ({ scopedStore }) => ++scopedStore.count)`);
57
+ }
58
+ else {
59
+ routes.push(`.get('/${frontend}', () => ${handlerCall})`);
60
+ }
61
+ });
62
+ if (authProvider === undefined || authProvider === 'none') {
63
+ routes.push(`.get('/count/:uid', ({ params: { uid } }) => getCountHistory(db, uid), {
64
+ params: t.Object({
65
+ uid: t.Number()
66
+ })
67
+ })`, `.post('/count', ({ body: { count } }) => createCountHistory(db, count), {
68
+ body: t.Object({
69
+ count: t.Number()
70
+ })
71
+ })`);
72
+ }
73
+ return routes.join('\n ');
74
+ };