create-absolutejs 0.5.0 → 0.6.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/data.d.ts +1 -1
- package/dist/data.js +2 -2
- package/dist/generators/configurations/generatePackageJson.d.ts +2 -2
- package/dist/generators/configurations/generatePackageJson.js +12 -4
- package/dist/generators/configurations/initializeRoot.js +2 -2
- package/dist/generators/db/generateDatabaseTypes.d.ts +3 -3
- package/dist/generators/db/generateDatabaseTypes.js +12 -7
- package/dist/generators/db/generateDrizzleSchema.d.ts +3 -3
- package/dist/generators/db/generateDrizzleSchema.js +4 -6
- package/dist/generators/db/generateSqliteSchema.d.ts +2 -2
- package/dist/generators/db/generateSqliteSchema.js +1 -1
- package/dist/generators/db/handlerTemplates.d.ts +40 -20
- package/dist/generators/db/handlerTemplates.js +131 -91
- package/dist/generators/db/scaffoldDatabase.d.ts +2 -2
- package/dist/generators/db/scaffoldDatabase.js +6 -6
- package/dist/generators/db/scaffoldDocker.d.ts +3 -3
- package/dist/generators/db/scaffoldDocker.js +2 -2
- package/dist/generators/html/scaffoldHTML.js +2 -2
- package/dist/generators/project/collectDependencies.d.ts +2 -2
- package/dist/generators/project/collectDependencies.js +2 -2
- package/dist/generators/project/generateAbsoluteAuthConfig.d.ts +2 -0
- package/dist/generators/project/generateAbsoluteAuthConfig.js +128 -0
- package/dist/generators/project/generateDBBlock.js +17 -10
- package/dist/generators/project/generateImportsBlock.d.ts +2 -2
- package/dist/generators/project/generateImportsBlock.js +28 -23
- package/dist/generators/project/generateRoutesBlock.d.ts +3 -3
- package/dist/generators/project/generateRoutesBlock.js +57 -46
- package/dist/generators/project/generateServer.d.ts +2 -2
- package/dist/generators/project/generateServer.js +28 -12
- package/dist/generators/project/scaffoldBackend.d.ts +6 -0
- package/dist/generators/project/scaffoldBackend.js +23 -0
- package/dist/generators/project/scaffoldFrontends.d.ts +2 -2
- package/dist/generators/project/scaffoldFrontends.js +18 -3
- package/dist/generators/react/generateReactComponents.d.ts +5 -0
- package/dist/generators/react/generateReactComponents.js +126 -0
- package/dist/generators/react/scaffoldReact.d.ts +1 -1
- package/dist/generators/react/scaffoldReact.js +11 -2
- package/dist/index.js +1 -1
- package/dist/messages.d.ts +1 -1
- package/dist/messages.js +5 -4
- package/dist/prompt.js +4 -3
- package/dist/questions/authOption.d.ts +1 -0
- package/dist/questions/{authProvider.js → authOption.js} +5 -5
- package/dist/questions/databaseHost.js +12 -0
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.js +13 -9
- package/dist/templates/assets/svg/google-logo.svg +7 -0
- package/dist/templates/react/components/OAuthLink.tsx +39 -0
- package/dist/templates/react/components/ProfilePicture.tsx +56 -0
- package/dist/templates/styles/tailwind.css +1 -0
- package/dist/typeGuards.d.ts +2 -2
- package/dist/typeGuards.js +1 -1
- package/dist/types.d.ts +7 -2
- package/dist/utils/abort.js +1 -1
- package/dist/utils/parseCommandLineOptions.js +30 -9
- package/dist/utils/t3-utils.js +1 -1
- package/package.json +2 -1
- package/dist/generators/project/generateUseBlock.d.ts +0 -6
- package/dist/generators/project/generateUseBlock.js +0 -32
- package/dist/generators/react/generateReactPage.d.ts +0 -2
- package/dist/generators/react/generateReactPage.js +0 -23
- package/dist/questions/authProvider.d.ts +0 -1
- package/dist/templates/react/pages/ReactExample.tsx +0 -18
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { isDrizzleDialect } from '../../typeGuards';
|
|
4
|
-
export const generateImportsBlock = ({ backendDirectory, deps, flags, orm,
|
|
4
|
+
export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authOption, databaseEngine, databaseHost, frontendDirectories }) => {
|
|
5
5
|
const rawImports = [];
|
|
6
6
|
const pushHandler = (cond, name) => cond &&
|
|
7
7
|
rawImports.push(`import { ${name} } from '@absolutejs/absolute'`);
|
|
@@ -38,12 +38,12 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
38
38
|
if (flags.requiresVue && !flags.requiresSvelte && vueDir !== undefined)
|
|
39
39
|
rawImports.push(`import VueExample from '${buildExamplePath(vueDir, 'VueExample.vue')}'`);
|
|
40
40
|
const connectorImports = {
|
|
41
|
-
neon: [`import {
|
|
42
|
-
planetscale: [`import {
|
|
41
|
+
neon: [`import { neon } from '@neondatabase/serverless'`],
|
|
42
|
+
planetscale: [`import { Client } from '@planetscale/database'`],
|
|
43
43
|
turso: [`import { createClient } from '@libsql/client'`]
|
|
44
44
|
};
|
|
45
45
|
const dialectImports = {
|
|
46
|
-
neon: [`import { drizzle } from 'drizzle-orm/neon-
|
|
46
|
+
neon: [`import { drizzle } from 'drizzle-orm/neon-http'`],
|
|
47
47
|
planetscale: [
|
|
48
48
|
`import { drizzle } from 'drizzle-orm/planetscale-serverless'`
|
|
49
49
|
],
|
|
@@ -58,11 +58,8 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
58
58
|
...(databaseEngine === 'sqlite' && !isRemoteHost
|
|
59
59
|
? []
|
|
60
60
|
: [`import { getEnv } from '@absolutejs/absolute'`]),
|
|
61
|
-
...(
|
|
62
|
-
? [
|
|
63
|
-
`import { schema } from '../../db/schema'`,
|
|
64
|
-
`import { User } from '../types/databaseTypes'`
|
|
65
|
-
]
|
|
61
|
+
...(authOption === 'abs'
|
|
62
|
+
? [`import { schema } from '../../db/schema'`]
|
|
66
63
|
: [`import { schema } from '../../db/schema'`])
|
|
67
64
|
]
|
|
68
65
|
};
|
|
@@ -87,7 +84,12 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
87
84
|
`import { SQL } from 'bun'`,
|
|
88
85
|
`import { drizzle } from 'drizzle-orm/bun-sql'`
|
|
89
86
|
]
|
|
90
|
-
:
|
|
87
|
+
: isRemoteHost && databaseHost === 'planetscale'
|
|
88
|
+
? [
|
|
89
|
+
`import { drizzle } from 'drizzle-orm/node-postgres'`,
|
|
90
|
+
`import { Pool } from 'pg'`
|
|
91
|
+
]
|
|
92
|
+
: [],
|
|
91
93
|
singlestore: [
|
|
92
94
|
`import { drizzle } from 'drizzle-orm/singlestore'`,
|
|
93
95
|
`import { createPool } from 'mysql2/promise'`
|
|
@@ -127,15 +129,20 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
127
129
|
`import { SQL } from 'bun'`,
|
|
128
130
|
`import { getEnv } from '@absolutejs/absolute'`
|
|
129
131
|
],
|
|
130
|
-
postgresql: isRemoteHost
|
|
132
|
+
postgresql: isRemoteHost && databaseHost === 'neon'
|
|
131
133
|
? [
|
|
132
134
|
...connectorImports[databaseHost],
|
|
133
135
|
`import { getEnv } from '@absolutejs/absolute'`
|
|
134
136
|
]
|
|
135
|
-
:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
: isRemoteHost && databaseHost === 'planetscale'
|
|
138
|
+
? [
|
|
139
|
+
`import { Pool } from 'pg'`,
|
|
140
|
+
`import { getEnv } from '@absolutejs/absolute'`
|
|
141
|
+
]
|
|
142
|
+
: [
|
|
143
|
+
`import { SQL } from 'bun'`,
|
|
144
|
+
`import { getEnv } from '@absolutejs/absolute'`
|
|
145
|
+
],
|
|
139
146
|
singlestore: [
|
|
140
147
|
`import { createPool } from 'mysql2/promise'`,
|
|
141
148
|
`import { getEnv } from '@absolutejs/absolute'`
|
|
@@ -150,7 +157,9 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
150
157
|
if (orm === 'drizzle') {
|
|
151
158
|
rawImports.push(...ormImports[orm]);
|
|
152
159
|
}
|
|
153
|
-
if (orm
|
|
160
|
+
if (orm == 'drizzle' &&
|
|
161
|
+
isRemoteHost &&
|
|
162
|
+
!(databaseEngine === 'postgresql' && databaseHost === 'planetscale')) {
|
|
154
163
|
rawImports.push(...connectorImports[databaseHost], ...dialectImports[databaseHost]);
|
|
155
164
|
}
|
|
156
165
|
if (orm === 'drizzle' && isDrizzleDialect(databaseEngine)) {
|
|
@@ -159,13 +168,9 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authP
|
|
|
159
168
|
if (noOrm && hasDatabase && noOrmImports[databaseEngine]) {
|
|
160
169
|
rawImports.push(...noOrmImports[databaseEngine]);
|
|
161
170
|
}
|
|
162
|
-
if (
|
|
163
|
-
rawImports.push(`import {
|
|
164
|
-
|
|
165
|
-
`import { createUser, getUser } from './handlers/userHandlers'`
|
|
166
|
-
]
|
|
167
|
-
: []));
|
|
168
|
-
if (hasDatabase && (authProvider === undefined || authProvider === 'none'))
|
|
171
|
+
if (authOption === 'abs')
|
|
172
|
+
rawImports.push(`import { absoluteAuthConfig } from './utils/absoluteAuthConfig'`, `import { t } from 'elysia'`, `import { authProviderOption, providers, userSessionIdTypebox, getStatus } from '@absolutejs/auth'`);
|
|
173
|
+
if (hasDatabase && (authOption === undefined || authOption === 'none'))
|
|
169
174
|
rawImports.push(`import { getCountHistory, createCountHistory } from './handlers/countHistoryHandlers'`, `import { t } from 'elysia'`);
|
|
170
175
|
if (flags.requiresVue && flags.requiresSvelte) {
|
|
171
176
|
const utilsDir = join(backendDirectory, 'utils');
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AuthOption, FrontendDirectories } from '../../types';
|
|
2
2
|
import type { FrameworkFlags } from './computeFlags';
|
|
3
3
|
type GenerateRoutesBlockProps = {
|
|
4
4
|
flags: FrameworkFlags;
|
|
5
5
|
frontendDirectories: FrontendDirectories;
|
|
6
|
-
|
|
6
|
+
authOption: AuthOption;
|
|
7
7
|
buildDirectory: string;
|
|
8
8
|
};
|
|
9
|
-
export declare const generateRoutesBlock: ({ flags, frontendDirectories,
|
|
9
|
+
export declare const generateRoutesBlock: ({ flags, frontendDirectories, authOption, buildDirectory }: GenerateRoutesBlockProps) => string;
|
|
10
10
|
export {};
|
|
@@ -1,73 +1,84 @@
|
|
|
1
1
|
import { isFrontend } from '../../typeGuards';
|
|
2
|
-
export const generateRoutesBlock = ({ flags, frontendDirectories,
|
|
2
|
+
export const generateRoutesBlock = ({ flags, frontendDirectories, authOption, buildDirectory }) => {
|
|
3
3
|
const routes = [];
|
|
4
|
+
const wrap = (handlerCall) => authOption === 'abs'
|
|
5
|
+
? `async ({ cookie: { auth_provider, user_session_id }, store: { session }, status }) => {
|
|
6
|
+
const { user, error } = await getStatus(session, user_session_id);
|
|
7
|
+
|
|
8
|
+
if (error) {
|
|
9
|
+
return status(error.code, error.message);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const providerConfiguration =
|
|
13
|
+
auth_provider.value && providers[auth_provider.value];
|
|
14
|
+
|
|
15
|
+
return ${handlerCall};
|
|
16
|
+
}`
|
|
17
|
+
: `() => ${handlerCall}`;
|
|
4
18
|
const createHandlerCall = (frontend, directory) => {
|
|
5
19
|
const base = `${buildDirectory}${directory ? `/${directory}` : ''}/pages`;
|
|
6
20
|
if (frontend === 'html')
|
|
7
21
|
return `handleHTMLPageRequest(\`${base}/HTMLExample.html\`)`;
|
|
8
22
|
if (frontend === 'htmx')
|
|
9
23
|
return `handleHTMXPageRequest(\`${base}/HTMXExample.html\`)`;
|
|
10
|
-
if (frontend === 'react')
|
|
24
|
+
if (frontend === 'react') {
|
|
25
|
+
const reactProps = authOption === 'abs'
|
|
26
|
+
? `{ initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS'), user, providerConfiguration }`
|
|
27
|
+
: `{ initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS') }`;
|
|
11
28
|
return `handleReactPageRequest(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
ReactExample,
|
|
30
|
+
asset(manifest, 'ReactExampleIndex'),
|
|
31
|
+
${reactProps}
|
|
32
|
+
)`;
|
|
33
|
+
}
|
|
16
34
|
if (frontend === 'svelte')
|
|
17
35
|
return `handleSveltePageRequest(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (frontend === 'vue')
|
|
24
|
-
|
|
25
|
-
?
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
)`;
|
|
36
|
+
SvelteExample,
|
|
37
|
+
asset(manifest, 'SvelteExample'),
|
|
38
|
+
asset(manifest, 'SvelteExampleIndex'),
|
|
39
|
+
{ initialCount: 0, cssPath: asset(manifest, 'SvelteExampleCSS') }
|
|
40
|
+
)`;
|
|
41
|
+
if (frontend === 'vue') {
|
|
42
|
+
const vueComponent = flags.requiresSvelte
|
|
43
|
+
? 'vueImports.VueExample'
|
|
44
|
+
: 'VueExample';
|
|
45
|
+
return `handleVuePageRequest(
|
|
46
|
+
${vueComponent},
|
|
47
|
+
asset(manifest, 'VueExample'),
|
|
48
|
+
asset(manifest, 'VueExampleIndex'),
|
|
49
|
+
generateHeadElement({
|
|
50
|
+
cssPath: asset(manifest, 'VueExampleCSS'),
|
|
51
|
+
title: 'AbsoluteJS + Vue',
|
|
52
|
+
description: 'A Vue.js example with AbsoluteJS'
|
|
53
|
+
}),
|
|
54
|
+
{ initialCount: 0 }
|
|
55
|
+
)`;
|
|
56
|
+
}
|
|
47
57
|
return '';
|
|
48
58
|
};
|
|
49
59
|
Object.entries(frontendDirectories).forEach(([frontend, directory], entryIndex) => {
|
|
50
60
|
if (!isFrontend(frontend))
|
|
51
61
|
return;
|
|
52
62
|
const handlerCall = createHandlerCall(frontend, directory);
|
|
53
|
-
if (
|
|
54
|
-
|
|
63
|
+
if (!handlerCall)
|
|
64
|
+
return;
|
|
65
|
+
const handler = wrap(handlerCall);
|
|
66
|
+
if (entryIndex === 0) {
|
|
67
|
+
routes.push(`.get('/', ${handler})`);
|
|
68
|
+
}
|
|
55
69
|
if (frontend === 'htmx') {
|
|
56
|
-
routes.push(`.get('/htmx',
|
|
70
|
+
routes.push(`.get('/htmx', ${handler})`);
|
|
71
|
+
routes.push(`.post('/htmx/reset', ({ resetScopedStore }) => resetScopedStore())`, `.get('/htmx/count', ({ scopedStore }) => scopedStore.count)`, `.post('/htmx/increment', ({ scopedStore }) => ++scopedStore.count)`);
|
|
57
72
|
}
|
|
58
73
|
else {
|
|
59
|
-
routes.push(`.get('/${frontend}',
|
|
74
|
+
routes.push(`.get('/${frontend}', ${handler})`);
|
|
60
75
|
}
|
|
61
76
|
});
|
|
62
|
-
if (
|
|
77
|
+
if (authOption === undefined || authOption === 'none') {
|
|
63
78
|
routes.push(`.get('/count/:uid', ({ params: { uid } }) => getCountHistory(db, uid), {
|
|
64
|
-
params: t.Object({
|
|
65
|
-
uid: t.Number()
|
|
66
|
-
})
|
|
79
|
+
params: t.Object({ uid: t.Number() })
|
|
67
80
|
})`, `.post('/count', ({ body: { count } }) => createCountHistory(db, count), {
|
|
68
|
-
body: t.Object({
|
|
69
|
-
count: t.Number()
|
|
70
|
-
})
|
|
81
|
+
body: t.Object({ count: t.Number() })
|
|
71
82
|
})`);
|
|
72
83
|
}
|
|
73
84
|
return routes.join('\n ');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CreateConfiguration } from '../../types';
|
|
2
|
-
type CreateServerFileProps = Pick<CreateConfiguration, 'tailwind' | '
|
|
2
|
+
type CreateServerFileProps = Pick<CreateConfiguration, 'tailwind' | 'authOption' | 'databaseEngine' | 'plugins' | 'buildDirectory' | 'databaseHost' | 'orm' | 'assetsDirectory' | 'frontendDirectories'> & {
|
|
3
3
|
backendDirectory: string;
|
|
4
4
|
};
|
|
5
|
-
export declare const generateServerFile: ({ tailwind,
|
|
5
|
+
export declare const generateServerFile: ({ tailwind, authOption, plugins, buildDirectory, databaseEngine, databaseHost, orm, assetsDirectory, frontendDirectories, backendDirectory }: CreateServerFileProps) => void;
|
|
6
6
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { collectDependencies } from './collectDependencies';
|
|
4
4
|
import { computeFlags } from './computeFlags';
|
|
@@ -6,13 +6,12 @@ import { generateBuildBlock } from './generateBuildBlock';
|
|
|
6
6
|
import { generateDBBlock } from './generateDBBlock';
|
|
7
7
|
import { generateImportsBlock } from './generateImportsBlock';
|
|
8
8
|
import { generateRoutesBlock } from './generateRoutesBlock';
|
|
9
|
-
|
|
10
|
-
export const generateServerFile = ({ tailwind, authProvider, plugins, buildDirectory, databaseEngine, databaseHost, orm, assetsDirectory, frontendDirectories, backendDirectory }) => {
|
|
9
|
+
export const generateServerFile = ({ tailwind, authOption, plugins, buildDirectory, databaseEngine, databaseHost, orm, assetsDirectory, frontendDirectories, backendDirectory }) => {
|
|
11
10
|
const serverFilePath = join(backendDirectory, 'server.ts');
|
|
12
11
|
const flags = computeFlags(frontendDirectories);
|
|
13
|
-
const deps = collectDependencies({
|
|
12
|
+
const deps = collectDependencies({ authOption, flags, plugins });
|
|
14
13
|
const importsBlock = generateImportsBlock({
|
|
15
|
-
|
|
14
|
+
authOption,
|
|
16
15
|
backendDirectory,
|
|
17
16
|
databaseEngine,
|
|
18
17
|
databaseHost,
|
|
@@ -31,13 +30,30 @@ export const generateServerFile = ({ tailwind, authProvider, plugins, buildDirec
|
|
|
31
30
|
if (databaseEngine && databaseEngine !== 'none') {
|
|
32
31
|
dbBlock = generateDBBlock({ databaseEngine, databaseHost, orm });
|
|
33
32
|
}
|
|
34
|
-
const useBlock =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
const useBlock = deps
|
|
34
|
+
.flatMap((dependency) => dependency.imports ?? [])
|
|
35
|
+
.filter((pluginImport) => pluginImport.isPlugin)
|
|
36
|
+
.map((pluginImport) => {
|
|
37
|
+
if (pluginImport.packageName === 'absoluteAuth') {
|
|
38
|
+
return `.use(absoluteAuth(absoluteAuthConfig(db)))`;
|
|
39
|
+
}
|
|
40
|
+
if (pluginImport.config === undefined) {
|
|
41
|
+
return `.use(${pluginImport.packageName})`;
|
|
42
|
+
}
|
|
43
|
+
if (pluginImport.config === null) {
|
|
44
|
+
return `.use(${pluginImport.packageName}())`;
|
|
45
|
+
}
|
|
46
|
+
return `.use(${pluginImport.packageName}(${JSON.stringify(pluginImport.config)}))`;
|
|
47
|
+
})
|
|
48
|
+
.join('\n');
|
|
49
|
+
const guardBlock = `.guard({
|
|
50
|
+
cookie: t.Cookie({
|
|
51
|
+
auth_provider: t.Optional(authProviderOption),
|
|
52
|
+
user_session_id: userSessionIdTypebox
|
|
53
|
+
})
|
|
54
|
+
})`;
|
|
39
55
|
const routesBlock = generateRoutesBlock({
|
|
40
|
-
|
|
56
|
+
authOption,
|
|
41
57
|
buildDirectory,
|
|
42
58
|
flags,
|
|
43
59
|
frontendDirectories
|
|
@@ -48,12 +64,12 @@ ${manifestBlock}
|
|
|
48
64
|
${dbBlock}
|
|
49
65
|
new Elysia()
|
|
50
66
|
${useBlock}
|
|
67
|
+
${authOption === 'abs' ? guardBlock : ''}
|
|
51
68
|
${routesBlock}
|
|
52
69
|
.on('error', err => {
|
|
53
70
|
const { request } = err
|
|
54
71
|
console.error(\`Server error on \${request.method} \${request.url}: \${err.message}\`)
|
|
55
72
|
});
|
|
56
73
|
`;
|
|
57
|
-
mkdirSync(backendDirectory, { recursive: true });
|
|
58
74
|
writeFileSync(serverFilePath, content);
|
|
59
75
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CreateConfiguration } from '../../types';
|
|
2
|
+
type ScaffoldBackendProps = Pick<CreateConfiguration, 'assetsDirectory' | 'absProviders' | 'authOption' | 'buildDirectory' | 'databaseEngine' | 'databaseHost' | 'frontendDirectories' | 'orm' | 'plugins' | 'tailwind'> & {
|
|
3
|
+
backendDirectory: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const scaffoldBackend: ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, tailwind }: ScaffoldBackendProps) => void;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { generateAbsoluteAuthConfig } from './generateAbsoluteAuthConfig';
|
|
4
|
+
import { generateServerFile } from './generateServer';
|
|
5
|
+
export const scaffoldBackend = ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, tailwind }) => {
|
|
6
|
+
generateServerFile({
|
|
7
|
+
assetsDirectory,
|
|
8
|
+
authOption,
|
|
9
|
+
backendDirectory,
|
|
10
|
+
buildDirectory,
|
|
11
|
+
databaseEngine,
|
|
12
|
+
databaseHost,
|
|
13
|
+
frontendDirectories,
|
|
14
|
+
orm,
|
|
15
|
+
plugins,
|
|
16
|
+
tailwind
|
|
17
|
+
});
|
|
18
|
+
if (authOption === 'abs') {
|
|
19
|
+
mkdirSync(join(backendDirectory, 'utils'), { recursive: true });
|
|
20
|
+
const absoluteAuthConfig = generateAbsoluteAuthConfig(absProviders);
|
|
21
|
+
writeFileSync(join(backendDirectory, 'utils', 'absoluteAuthConfig.ts'), absoluteAuthConfig, 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CreateConfiguration } from '../../types';
|
|
2
|
-
type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories' | 'frontends'> & {
|
|
2
|
+
type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories' | 'assetsDirectory' | 'frontends' | 'authOption' | 'absProviders'> & {
|
|
3
3
|
frontendDirectory: string;
|
|
4
4
|
templatesDirectory: string;
|
|
5
5
|
projectAssetsDirectory: string;
|
|
6
6
|
};
|
|
7
|
-
export declare const scaffoldFrontends: ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }: ScaffoldFrontendsProps) => void;
|
|
7
|
+
export declare const scaffoldFrontends: ({ frontendDirectory, assetsDirectory, absProviders, authOption, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }: ScaffoldFrontendsProps) => void;
|
|
8
8
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { cpSync, mkdirSync } from '
|
|
2
|
-
import { join } from '
|
|
1
|
+
import { cpSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
3
|
import { scaffoldHTML } from '../html/scaffoldHTML';
|
|
4
4
|
import { scaffoldHTMX } from '../htmx/scaffoldHTMX';
|
|
5
5
|
import { scaffoldReact } from '../react/scaffoldReact';
|
|
6
6
|
import { scaffoldSvelte } from '../svelte/scaffoldSvelte';
|
|
7
7
|
import { scaffoldVue } from '../vue/scaffoldVue';
|
|
8
|
-
export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }) => {
|
|
8
|
+
export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProviders, authOption, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }) => {
|
|
9
9
|
const stylesTargetDirectory = join(frontendDirectory, 'styles');
|
|
10
10
|
cpSync(join(templatesDirectory, 'styles'), stylesTargetDirectory, {
|
|
11
11
|
recursive: true
|
|
@@ -25,6 +25,9 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
|
|
|
25
25
|
switch (frontendName) {
|
|
26
26
|
case 'react':
|
|
27
27
|
scaffoldReact({
|
|
28
|
+
absProviders,
|
|
29
|
+
assetsDirectory,
|
|
30
|
+
authOption,
|
|
28
31
|
frontends,
|
|
29
32
|
isSingleFrontend,
|
|
30
33
|
projectAssetsDirectory,
|
|
@@ -34,6 +37,9 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
|
|
|
34
37
|
break;
|
|
35
38
|
case 'svelte':
|
|
36
39
|
scaffoldSvelte({
|
|
40
|
+
absProviders,
|
|
41
|
+
assetsDirectory,
|
|
42
|
+
authOption,
|
|
37
43
|
frontends,
|
|
38
44
|
isSingleFrontend,
|
|
39
45
|
projectAssetsDirectory,
|
|
@@ -43,6 +49,9 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
|
|
|
43
49
|
break;
|
|
44
50
|
case 'vue':
|
|
45
51
|
scaffoldVue({
|
|
52
|
+
absProviders,
|
|
53
|
+
assetsDirectory,
|
|
54
|
+
authOption,
|
|
46
55
|
frontends,
|
|
47
56
|
projectAssetsDirectory,
|
|
48
57
|
targetDirectory,
|
|
@@ -54,6 +63,9 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
|
|
|
54
63
|
break;
|
|
55
64
|
case 'html':
|
|
56
65
|
scaffoldHTML({
|
|
66
|
+
absProviders,
|
|
67
|
+
assetsDirectory,
|
|
68
|
+
authOption,
|
|
57
69
|
frontends,
|
|
58
70
|
isSingleFrontend,
|
|
59
71
|
projectAssetsDirectory,
|
|
@@ -64,6 +76,9 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
|
|
|
64
76
|
break;
|
|
65
77
|
case 'htmx':
|
|
66
78
|
scaffoldHTMX({
|
|
79
|
+
absProviders,
|
|
80
|
+
assetsDirectory,
|
|
81
|
+
authOption,
|
|
67
82
|
frontends,
|
|
68
83
|
isSingleFrontend,
|
|
69
84
|
projectAssetsDirectory,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ProviderOption } from '@absolutejs/auth';
|
|
2
|
+
import { AuthOption, Frontend } from '../../types';
|
|
3
|
+
export declare const generateDropdownComponent: (frontends: Frontend[]) => string;
|
|
4
|
+
export declare const generateSignInComponent: (absProviders: ProviderOption[] | undefined) => string;
|
|
5
|
+
export declare const generateReactExamplePage: (authOption: AuthOption) => string;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { formatNavLink } from '../../utils/formatNavLink';
|
|
2
|
+
export const generateDropdownComponent = (frontends) => {
|
|
3
|
+
const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
|
|
4
|
+
return `import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
export const Dropdown = () => {
|
|
7
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<details
|
|
11
|
+
onPointerEnter={() => setIsOpen(true)}
|
|
12
|
+
onPointerLeave={() => setIsOpen(false)}
|
|
13
|
+
open={isOpen}
|
|
14
|
+
>
|
|
15
|
+
<summary>Pages</summary>
|
|
16
|
+
<nav>
|
|
17
|
+
${navLinks}
|
|
18
|
+
</nav>
|
|
19
|
+
</details>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
`;
|
|
23
|
+
};
|
|
24
|
+
export const generateSignInComponent = (absProviders) => `import { useState } from 'react';
|
|
25
|
+
import { OAuthLink } from './OAuthLink';
|
|
26
|
+
|
|
27
|
+
export const SignIn = () => {
|
|
28
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<details
|
|
32
|
+
onPointerEnter={() => setIsOpen(true)}
|
|
33
|
+
onPointerLeave={() => setIsOpen(false)}
|
|
34
|
+
open={isOpen}
|
|
35
|
+
>
|
|
36
|
+
<summary>Sign In</summary>
|
|
37
|
+
<nav>
|
|
38
|
+
${absProviders && absProviders.length > 0
|
|
39
|
+
? absProviders
|
|
40
|
+
.map((provider) => {
|
|
41
|
+
const logoUrl = `/assets/svg/google-logo.svg`;
|
|
42
|
+
const name = provider.charAt(0).toUpperCase() +
|
|
43
|
+
provider.slice(1).toLowerCase();
|
|
44
|
+
return `<OAuthLink provider="${provider}" logoUrl="${logoUrl}" name="${name}" mode="in" />`;
|
|
45
|
+
})
|
|
46
|
+
.join('\n\t\t\t')
|
|
47
|
+
: `<p>No OAuth providers configured</p>`}
|
|
48
|
+
</nav>
|
|
49
|
+
</details>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
export const generateReactExamplePage = (authOption) => {
|
|
54
|
+
const useBlockReturn = authOption === 'abs';
|
|
55
|
+
const propsType = `
|
|
56
|
+
type ReactExampleProps = {
|
|
57
|
+
initialCount: number;
|
|
58
|
+
cssPath: string;
|
|
59
|
+
${authOption === 'abs' ? 'user: User | null;\n\tproviderConfiguration: ProviderConfiguration | undefined;' : ''}
|
|
60
|
+
};
|
|
61
|
+
`;
|
|
62
|
+
const fnSignature = authOption === 'abs'
|
|
63
|
+
? `export const ReactExample = ({ initialCount, cssPath, user, providerConfiguration }: ReactExampleProps) => {`
|
|
64
|
+
: `export const ReactExample = ({ initialCount, cssPath }: ReactExampleProps) => (`;
|
|
65
|
+
const extractProps = ` const userImage =
|
|
66
|
+
user?.metadata && providerConfiguration?.picture
|
|
67
|
+
? extractPropFromIdentity(
|
|
68
|
+
user.metadata,
|
|
69
|
+
providerConfiguration.picture,
|
|
70
|
+
'string'
|
|
71
|
+
)
|
|
72
|
+
: undefined;
|
|
73
|
+
|
|
74
|
+
const givenName =
|
|
75
|
+
user?.metadata && providerConfiguration?.givenName
|
|
76
|
+
? extractPropFromIdentity(
|
|
77
|
+
user.metadata,
|
|
78
|
+
providerConfiguration.givenName,
|
|
79
|
+
'string'
|
|
80
|
+
)
|
|
81
|
+
: undefined;
|
|
82
|
+
|
|
83
|
+
const familyName =
|
|
84
|
+
user?.metadata && providerConfiguration?.familyName
|
|
85
|
+
? extractPropFromIdentity(
|
|
86
|
+
user.metadata,
|
|
87
|
+
providerConfiguration.familyName,
|
|
88
|
+
'string'
|
|
89
|
+
)
|
|
90
|
+
: undefined;`;
|
|
91
|
+
const userButtonsBlock = authOption === 'abs'
|
|
92
|
+
? `{user ? <ProfilePicture userImage={userImage} givenName={givenName} familyName={familyName} /> : <SignIn />}`
|
|
93
|
+
: ``;
|
|
94
|
+
const rightGroup = authOption === 'abs'
|
|
95
|
+
? `<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
|
96
|
+
<Dropdown />
|
|
97
|
+
${userButtonsBlock}
|
|
98
|
+
</div>`
|
|
99
|
+
: `<Dropdown />`;
|
|
100
|
+
const closing = authOption === 'abs' ? `};` : `);`;
|
|
101
|
+
return `
|
|
102
|
+
${authOption === 'abs' ? `import { User } from '../../../types/databaseTypes';\nimport { extractPropFromIdentity, ProviderConfiguration } from '@absolutejs/auth';` : ''}
|
|
103
|
+
import { App } from '../components/App';
|
|
104
|
+
import { Dropdown } from '../components/Dropdown';
|
|
105
|
+
import { Head } from '../components/Head';
|
|
106
|
+
${authOption === 'abs' ? `import { ProfilePicture } from '../components/ProfilePicture';\nimport { SignIn } from '../components/SignIn';` : ''}
|
|
107
|
+
|
|
108
|
+
${propsType}
|
|
109
|
+
|
|
110
|
+
${fnSignature}
|
|
111
|
+
${authOption === 'abs' ? extractProps : ''}
|
|
112
|
+
${useBlockReturn ? 'return (' : ''}
|
|
113
|
+
<html>
|
|
114
|
+
<Head cssPath={cssPath} />
|
|
115
|
+
<body>
|
|
116
|
+
<header>
|
|
117
|
+
<a href="/">AbsoluteJS</a>
|
|
118
|
+
${rightGroup}
|
|
119
|
+
</header>
|
|
120
|
+
<App initialCount={initialCount} />
|
|
121
|
+
</body>
|
|
122
|
+
</html>
|
|
123
|
+
${useBlockReturn ? ');' : ''}
|
|
124
|
+
${closing}
|
|
125
|
+
`;
|
|
126
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ScaffoldFrontendProps } from '../../types';
|
|
2
|
-
export declare const scaffoldReact: ({ isSingleFrontend, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
|
|
2
|
+
export declare const scaffoldReact: ({ isSingleFrontend, authOption, targetDirectory, templatesDirectory, frontends, assetsDirectory, projectAssetsDirectory, absProviders }: ScaffoldFrontendProps) => void;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { generateMarkupCSS } from '../project/generateMarkupCSS';
|
|
4
|
-
import { generateDropdownComponent } from './
|
|
5
|
-
export const scaffoldReact = ({ isSingleFrontend, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }) => {
|
|
4
|
+
import { generateDropdownComponent, generateReactExamplePage, generateSignInComponent } from './generateReactComponents';
|
|
5
|
+
export const scaffoldReact = ({ isSingleFrontend, authOption, targetDirectory, templatesDirectory, frontends, assetsDirectory, projectAssetsDirectory, absProviders }) => {
|
|
6
|
+
mkdirSync(join(projectAssetsDirectory, 'svg'), { recursive: true });
|
|
6
7
|
copyFileSync(join(templatesDirectory, 'assets', 'svg', 'react.svg'), join(projectAssetsDirectory, 'svg', 'react.svg'));
|
|
8
|
+
copyFileSync(join(templatesDirectory, 'assets', 'svg', 'google-logo.svg'), join(projectAssetsDirectory, 'svg', 'google-logo.svg'));
|
|
7
9
|
cpSync(join(templatesDirectory, 'react'), targetDirectory, {
|
|
8
10
|
recursive: true
|
|
9
11
|
});
|
|
10
12
|
const dropdownComponent = generateDropdownComponent(frontends);
|
|
11
13
|
writeFileSync(join(targetDirectory, 'components', 'Dropdown.tsx'), dropdownComponent, 'utf-8');
|
|
14
|
+
if (authOption === 'abs') {
|
|
15
|
+
const signInComponent = generateSignInComponent(absProviders);
|
|
16
|
+
writeFileSync(join(targetDirectory, 'components', 'SignIn.tsx'), signInComponent, 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
const pageComponent = generateReactExamplePage(authOption);
|
|
19
|
+
mkdirSync(join(targetDirectory, 'pages'), { recursive: true });
|
|
20
|
+
writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), pageComponent, 'utf-8');
|
|
12
21
|
const cssOutputDir = join(targetDirectory, 'styles');
|
|
13
22
|
mkdirSync(cssOutputDir, { recursive: true });
|
|
14
23
|
const cssOutputFile = join(cssOutputDir, 'react-example.css');
|
package/dist/index.js
CHANGED
package/dist/messages.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ type DebugMessageProps = {
|
|
|
10
10
|
response: CreateConfiguration;
|
|
11
11
|
packageManager: string;
|
|
12
12
|
};
|
|
13
|
-
export declare const getDebugMessage: ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm,
|
|
13
|
+
export declare const getDebugMessage: ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
|
|
14
14
|
export {};
|