create-absolutejs 0.5.1 → 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.
Files changed (61) hide show
  1. package/dist/data.d.ts +1 -1
  2. package/dist/data.js +2 -2
  3. package/dist/generators/configurations/generatePackageJson.d.ts +2 -2
  4. package/dist/generators/configurations/generatePackageJson.js +6 -2
  5. package/dist/generators/configurations/initializeRoot.js +2 -2
  6. package/dist/generators/db/generateDatabaseTypes.d.ts +3 -3
  7. package/dist/generators/db/generateDatabaseTypes.js +6 -6
  8. package/dist/generators/db/generateDrizzleSchema.d.ts +3 -3
  9. package/dist/generators/db/generateDrizzleSchema.js +4 -6
  10. package/dist/generators/db/generateSqliteSchema.d.ts +2 -2
  11. package/dist/generators/db/generateSqliteSchema.js +1 -1
  12. package/dist/generators/db/handlerTemplates.d.ts +27 -27
  13. package/dist/generators/db/handlerTemplates.js +59 -92
  14. package/dist/generators/db/scaffoldDatabase.d.ts +2 -2
  15. package/dist/generators/db/scaffoldDatabase.js +6 -6
  16. package/dist/generators/db/scaffoldDocker.d.ts +3 -3
  17. package/dist/generators/db/scaffoldDocker.js +2 -2
  18. package/dist/generators/html/scaffoldHTML.js +2 -2
  19. package/dist/generators/project/collectDependencies.d.ts +2 -2
  20. package/dist/generators/project/collectDependencies.js +2 -2
  21. package/dist/generators/project/generateAbsoluteAuthConfig.d.ts +2 -0
  22. package/dist/generators/project/generateAbsoluteAuthConfig.js +128 -0
  23. package/dist/generators/project/generateDBBlock.js +8 -2
  24. package/dist/generators/project/generateImportsBlock.d.ts +2 -2
  25. package/dist/generators/project/generateImportsBlock.js +8 -15
  26. package/dist/generators/project/generateRoutesBlock.d.ts +3 -3
  27. package/dist/generators/project/generateRoutesBlock.js +57 -46
  28. package/dist/generators/project/generateServer.d.ts +2 -2
  29. package/dist/generators/project/generateServer.js +28 -12
  30. package/dist/generators/project/scaffoldBackend.d.ts +6 -0
  31. package/dist/generators/project/scaffoldBackend.js +23 -0
  32. package/dist/generators/project/scaffoldFrontends.d.ts +2 -2
  33. package/dist/generators/project/scaffoldFrontends.js +18 -3
  34. package/dist/generators/react/generateReactComponents.d.ts +5 -0
  35. package/dist/generators/react/generateReactComponents.js +126 -0
  36. package/dist/generators/react/scaffoldReact.d.ts +1 -1
  37. package/dist/generators/react/scaffoldReact.js +11 -2
  38. package/dist/index.js +1 -1
  39. package/dist/messages.d.ts +1 -1
  40. package/dist/messages.js +4 -3
  41. package/dist/prompt.js +4 -3
  42. package/dist/questions/authOption.d.ts +1 -0
  43. package/dist/questions/{authProvider.js → authOption.js} +5 -5
  44. package/dist/scaffold.d.ts +1 -1
  45. package/dist/scaffold.js +13 -9
  46. package/dist/templates/assets/svg/google-logo.svg +7 -0
  47. package/dist/templates/react/components/OAuthLink.tsx +39 -0
  48. package/dist/templates/react/components/ProfilePicture.tsx +56 -0
  49. package/dist/typeGuards.d.ts +2 -2
  50. package/dist/typeGuards.js +1 -1
  51. package/dist/types.d.ts +7 -2
  52. package/dist/utils/abort.js +1 -1
  53. package/dist/utils/parseCommandLineOptions.js +29 -8
  54. package/dist/utils/t3-utils.js +1 -1
  55. package/package.json +2 -1
  56. package/dist/generators/project/generateUseBlock.d.ts +0 -6
  57. package/dist/generators/project/generateUseBlock.js +0 -32
  58. package/dist/generators/react/generateReactPage.d.ts +0 -2
  59. package/dist/generators/react/generateReactPage.js +0 -23
  60. package/dist/questions/authProvider.d.ts +0 -1
  61. package/dist/templates/react/pages/ReactExample.tsx +0 -18
@@ -1,11 +1,11 @@
1
- import { cpSync, mkdirSync } from 'node:fs';
2
- import { join } from 'node:path';
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 './generateReactPage';
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { exit } from 'node:process';
2
+ import { exit } from 'process';
3
3
  import { outro } from '@clack/prompts';
4
4
  import { getDebugMessage, getOutroMessage, helpMessage } from './messages';
5
5
  import { prompt } from './prompt';
@@ -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, authProvider, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
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 {};
package/dist/messages.js CHANGED
@@ -11,10 +11,11 @@ Options:
11
11
  ${cyan('--help, -h')} Show this help message and exit
12
12
  ${cyan('--debug, -d')} Display a summary of the project configuration after creation
13
13
 
14
+ ${cyan('--abs-provider')} A provider for Absolute-Auth (eg. 'google', 'github', 'discord') the full list is available at https://absolutejs.com/documentation/absolute-auth
14
15
  ${cyan('--angular')} Include an Angular frontend
15
16
  ${cyan('--angular-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Angular frontend
16
17
  ${cyan('--assets')} ${dim(cyan('<directory>'))} Directory name for your static assets
17
- ${cyan('--auth')} ${dim(cyan('<plugin>'))} Pre-configured auth plugin (currently only "absolute-auth") or 'none'
18
+ ${cyan('--auth')} ${dim(cyan('<plugin>'))} Pre-configured auth plugin (currently only "abs") or 'none'
18
19
  ${cyan('--biome')} Use Biome for code quality and formatting
19
20
  ${cyan('--build')} ${dim(cyan('<directory>'))} Output directory for build artifacts
20
21
  ${cyan('--db')} ${dim(cyan('<engine>'))} Database engine (postgresql | mysql | sqlite | mongodb | mariadb | gel | singlestore | cockroachdb | mssql) or 'none'
@@ -47,7 +48,7 @@ export const getOutroMessage = ({ projectName, packageManager, installDependenci
47
48
  `${cyan('cd')} ${projectName}\n` +
48
49
  `${installDependenciesNow ? '' : `${cyan(`${packageManager} install`)}\n`}` +
49
50
  `${cyan(`${packageManager} dev`)}`; // TODO: Some package managers need run
50
- export const getDebugMessage = ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authProvider, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
51
+ export const getDebugMessage = ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
51
52
  const htmlScriptingValue = useHTMLScripts
52
53
  ? blueBright('TypeScript')
53
54
  : dim('None');
@@ -73,7 +74,7 @@ export const getDebugMessage = ({ response: { projectName, codeQualityTool, dire
73
74
  ['Database Host', databaseHost && databaseHost !== 'none' ? databaseHost : dim('None')],
74
75
  ['Database Directory', databaseDirectory ?? dim('None')],
75
76
  ['ORM', orm ?? dim('None')],
76
- ['Auth Provider', authProvider && authProvider !== 'none' ? authProvider : dim('None')],
77
+ ['Auth Provider', authOption && authOption !== 'none' ? authOption : dim('None')],
77
78
  ['Plugins', plugins.length && !plugins.includes('none') ? plugins.join(', ') : dim('None')],
78
79
  ['Initialize Git', initializeGitNow ? green('Yes') : red('No')],
79
80
  ['Install Dependencies', installDependenciesNow ? green('Yes') : red('No')],
package/dist/prompt.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getAuthProvider } from './questions/authProvider';
1
+ import { getAuthOption } from './questions/authOption';
2
2
  import { getCodeQualityTool } from './questions/codeQualityTool';
3
3
  import { getConfigurationType } from './questions/configurationType';
4
4
  import { getDatabaseEngine } from './questions/databaseEngine';
@@ -51,7 +51,7 @@ export const prompt = async (argumentConfiguration) => {
51
51
  if (argumentConfiguration.frontendDirectories !== undefined)
52
52
  directoryConfig = 'custom';
53
53
  // 12. Auth provider
54
- const authProvider = argumentConfiguration.authProvider ?? (await getAuthProvider());
54
+ const authOption = argumentConfiguration.authOption ?? (await getAuthOption());
55
55
  // 13. Additional plugins
56
56
  const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await getPlugins());
57
57
  // 14. Initialize Git repository
@@ -60,8 +60,9 @@ export const prompt = async (argumentConfiguration) => {
60
60
  const installDependenciesNow = argumentConfiguration.installDependenciesNow ??
61
61
  (await getInstallDependencies());
62
62
  const values = {
63
+ absProviders: argumentConfiguration.absProviders?.filter((provider) => provider !== undefined),
63
64
  assetsDirectory,
64
- authProvider,
65
+ authOption,
65
66
  buildDirectory,
66
67
  codeQualityTool,
67
68
  databaseDirectory,
@@ -0,0 +1 @@
1
+ export declare const getAuthOption: () => Promise<"abs" | undefined>;
@@ -1,15 +1,15 @@
1
1
  import { select, isCancel } from '@clack/prompts';
2
2
  import { cyan } from 'picocolors';
3
3
  import { abort } from '../utils/abort';
4
- export const getAuthProvider = async () => {
5
- const authProvider = await select({
4
+ export const getAuthOption = async () => {
5
+ const authOption = await select({
6
6
  message: 'Auth provider:',
7
7
  options: [
8
8
  { label: 'None', value: 'none' },
9
- { label: cyan('Absolute Auth'), value: 'absoluteAuth' }
9
+ { label: cyan('Absolute Auth'), value: 'abs' }
10
10
  ]
11
11
  });
12
- if (isCancel(authProvider))
12
+ if (isCancel(authOption))
13
13
  abort();
14
- return authProvider === 'none' ? undefined : authProvider;
14
+ return authOption === 'none' ? undefined : authOption;
15
15
  };
@@ -5,5 +5,5 @@ type ScaffoldProps = {
5
5
  latest: boolean;
6
6
  envVariables: string[] | undefined;
7
7
  };
8
- export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<void>;
8
+ export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<void>;
9
9
  export {};
package/dist/scaffold.js CHANGED
@@ -1,6 +1,6 @@
1
- import { copyFileSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
1
+ import { copyFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
4
  import { formatProject } from './commands/formatProject';
5
5
  import { initializeGit } from './commands/initializeGit';
6
6
  import { installDependencies } from './commands/installDependencies';
@@ -8,9 +8,9 @@ import { createPackageJson } from './generators/configurations/generatePackageJs
8
8
  import { initalizeRoot } from './generators/configurations/initializeRoot';
9
9
  import { scaffoldConfigurationFiles } from './generators/configurations/scaffoldConfigurationFiles';
10
10
  import { scaffoldDatabase } from './generators/db/scaffoldDatabase';
11
- import { generateServerFile } from './generators/project/generateServer';
11
+ import { scaffoldBackend } from './generators/project/scaffoldBackend';
12
12
  import { scaffoldFrontends } from './generators/project/scaffoldFrontends';
13
- export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authProvider, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }) => {
13
+ export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }) => {
14
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
15
  const templatesDirectory = join(__dirname, '/templates');
16
16
  const { frontendDirectory, backendDirectory, projectAssetsDirectory, typesDirectory } = initalizeRoot(projectName, templatesDirectory);
@@ -27,7 +27,7 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
27
27
  templatesDirectory
28
28
  });
29
29
  createPackageJson({
30
- authProvider,
30
+ authOption,
31
31
  codeQualityTool,
32
32
  databaseEngine,
33
33
  databaseHost,
@@ -38,9 +38,10 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
38
38
  projectName,
39
39
  useTailwind
40
40
  });
41
- generateServerFile({
41
+ scaffoldBackend({
42
+ absProviders,
42
43
  assetsDirectory,
43
- authProvider,
44
+ authOption,
44
45
  backendDirectory,
45
46
  buildDirectory,
46
47
  databaseEngine,
@@ -54,7 +55,7 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
54
55
  databaseEngine !== 'none' &&
55
56
  databaseEngine !== undefined &&
56
57
  (await scaffoldDatabase({
57
- authProvider,
58
+ authOption,
58
59
  backendDirectory,
59
60
  databaseDirectory,
60
61
  databaseEngine,
@@ -64,6 +65,9 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
64
65
  typesDirectory
65
66
  })));
66
67
  scaffoldFrontends({
68
+ absProviders,
69
+ assetsDirectory,
70
+ authOption,
67
71
  frontendDirectories,
68
72
  frontendDirectory,
69
73
  frontends,
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
2
+ <path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
3
+ <path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
4
+ <path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
5
+ <path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
6
+ <path fill="none" d="M0 0h48v48H0z" />
7
+ </svg>
@@ -0,0 +1,39 @@
1
+ import { ProviderOption } from 'citra';
2
+
3
+ type OAuthLinkProps = {
4
+ logoUrl: string;
5
+ name: string;
6
+ provider: Lowercase<ProviderOption> | undefined;
7
+ mode: 'in' | 'up';
8
+ };
9
+
10
+ export const OAuthLink = ({
11
+ provider,
12
+ logoUrl,
13
+ name,
14
+ mode
15
+ }: OAuthLinkProps) => {
16
+ const buttonText = `Sign ${mode} with ${name}`;
17
+
18
+ return (
19
+ <a
20
+ style={{
21
+ alignItems: 'center',
22
+ display: 'flex',
23
+ gap: '0.5rem'
24
+ }}
25
+ href={provider ? `/oauth2/${provider}/authorization` : undefined}
26
+ >
27
+ {provider ? (
28
+ <img
29
+ style={{ height: '2rem', width: '2rem' }}
30
+ alt={`${name} logo`}
31
+ src={logoUrl}
32
+ />
33
+ ) : (
34
+ <p>Provider not configured</p>
35
+ )}
36
+ <span>{buttonText}</span>
37
+ </a>
38
+ );
39
+ };
@@ -0,0 +1,56 @@
1
+ type ProfilePictureProps = {
2
+ userImage: string | null | undefined;
3
+ givenName?: string;
4
+ familyName?: string;
5
+ color?: string;
6
+ width?: string;
7
+ height?: string;
8
+ fontSize?: string;
9
+ };
10
+
11
+ export const ProfilePicture = ({
12
+ userImage,
13
+ givenName = '',
14
+ familyName = '',
15
+ color = '#4285F4',
16
+ width = '2rem',
17
+ height = '2rem',
18
+ fontSize = '0.85rem'
19
+ }: ProfilePictureProps) => {
20
+ if (userImage) {
21
+ return (
22
+ <img
23
+ alt="Profile"
24
+ src={userImage}
25
+ style={{
26
+ borderRadius: '50%',
27
+ height,
28
+ objectFit: 'cover',
29
+ width
30
+ }}
31
+ />
32
+ );
33
+ }
34
+
35
+ const initials = `${givenName.charAt(0).toUpperCase()}${familyName.charAt(0).toUpperCase()}`;
36
+
37
+ return (
38
+ <div
39
+ style={{
40
+ alignItems: 'center',
41
+ backgroundColor: color,
42
+ borderRadius: '50%',
43
+ color: 'white',
44
+ display: 'flex',
45
+ fontSize,
46
+ fontWeight: 600,
47
+ height,
48
+ justifyContent: 'center',
49
+ userSelect: 'none',
50
+ width
51
+ }}
52
+ >
53
+ {initials}
54
+ </div>
55
+ );
56
+ };
@@ -1,5 +1,5 @@
1
- import type { AuthProvider, AvailableDrizzleDialect, CodeQualityTool, DatabaseEngine, DatabaseHost, Frontend, ORM } from './types';
2
- export declare const isAuthProvider: (value: string | undefined) => value is AuthProvider;
1
+ import type { AuthOption, AvailableDrizzleDialect, CodeQualityTool, DatabaseEngine, DatabaseHost, Frontend, ORM } from './types';
2
+ export declare const isValidAuthOption: (value: string | undefined) => value is AuthOption;
3
3
  export declare const isDirectoryConfig: (value: string) => value is "default" | "custom";
4
4
  export declare const isDrizzleDialect: (value: string | undefined) => value is AvailableDrizzleDialect;
5
5
  export declare const isPrismaDialect: (value: string | undefined) => value is string;
@@ -1,5 +1,5 @@
1
1
  import { availableDatabaseEngines, availableDatabaseHosts, availableDrizzleDialects, availablePrismaDialects, frontendLabels } from './data';
2
- export const isAuthProvider = (value) => value === 'absoluteAuth' || value === 'none' || value === undefined;
2
+ export const isValidAuthOption = (value) => value === 'abs' || value === 'none' || value === undefined;
3
3
  export const isDirectoryConfig = (value) => value === 'default' || value === 'custom';
4
4
  export const isDrizzleDialect = (value) => availableDrizzleDialects.some((dialect) => dialect === value);
5
5
  export const isPrismaDialect = (value) => availablePrismaDialects.some((dialect) => dialect === value);
package/dist/types.d.ts CHANGED
@@ -1,5 +1,9 @@
1
+ import { ProviderOption } from '@absolutejs/auth';
1
2
  import { availableAuthProviders, availableCodeQualityTools, availableDatabaseEngines, availableDatabaseHosts, availableDirectoryConfigurations, availableDrizzleDialects, availableFrontends, availableORMs, availablePrismaDialects } from './data';
2
3
  export type ScaffoldFrontendProps = {
4
+ absProviders: ProviderOption[] | undefined;
5
+ assetsDirectory: string;
6
+ authOption: AuthOption;
3
7
  targetDirectory: string;
4
8
  templatesDirectory: string;
5
9
  projectAssetsDirectory: string;
@@ -21,7 +25,7 @@ export type AvailableDependency = {
21
25
  latestVersion: string;
22
26
  };
23
27
  export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
24
- export type AuthProvider = (typeof availableAuthProviders)[number] | undefined;
28
+ export type AuthOption = (typeof availableAuthProviders)[number] | undefined;
25
29
  export type AvailableDrizzleDialect = (typeof availableDrizzleDialects)[number];
26
30
  export type AvailablePrismaDialect = (typeof availablePrismaDialects)[number];
27
31
  export type DatabaseEngine = (typeof availableDatabaseEngines)[number] | undefined;
@@ -34,8 +38,9 @@ export type TailwindConfig = {
34
38
  output: string;
35
39
  } | undefined;
36
40
  export type CreateConfiguration = {
41
+ absProviders: ProviderOption[] | undefined;
37
42
  assetsDirectory: string;
38
- authProvider: AuthProvider;
43
+ authOption: AuthOption;
39
44
  buildDirectory: string;
40
45
  directoryConfig: DirectoryConfiguration;
41
46
  databaseEngine: DatabaseEngine;
@@ -1,4 +1,4 @@
1
- import { exit } from 'node:process';
1
+ import { exit } from 'process';
2
2
  import { cancel } from '@clack/prompts';
3
3
  /* eslint-disable */
4
4
  export function abort() {
@@ -1,14 +1,16 @@
1
- import { argv, exit } from 'node:process';
2
- import { parseArgs } from 'node:util';
1
+ import { argv, exit } from 'process';
2
+ import { parseArgs } from 'util';
3
+ import { isValidProviderOption, providers } from '@absolutejs/auth';
3
4
  import { DEFAULT_ARG_LENGTH } from '../constants';
4
5
  import { availableAuthProviders, availableDatabaseEngines, availableDatabaseHosts, availableDirectoryConfigurations, availableDrizzleDialects, availableORMs, availablePrismaDialects } from '../data';
5
- import { isAuthProvider, isDatabaseEngine, isDatabaseHost, isDirectoryConfig, isDrizzleDialect, isORM, isPrismaDialect } from '../typeGuards';
6
+ import { isValidAuthOption, isDatabaseEngine, isDatabaseHost, isDirectoryConfig, isDrizzleDialect, isORM, isPrismaDialect } from '../typeGuards';
6
7
  export const parseCommandLineOptions = () => {
7
8
  const { values, positionals } = parseArgs({
8
9
  allowNegative: true,
9
10
  allowPositionals: true,
10
11
  args: argv.slice(DEFAULT_ARG_LENGTH),
11
12
  options: {
13
+ 'abs-provider': { multiple: true, type: 'string' },
12
14
  angular: { type: 'boolean' },
13
15
  'angular-dir': { type: 'string' },
14
16
  assets: { type: 'string' },
@@ -47,15 +49,33 @@ export const parseCommandLineOptions = () => {
47
49
  });
48
50
  const errors = [];
49
51
  const projectName = positionals[0] ?? (values.skip ? 'absolutejs-project' : undefined);
50
- let authProvider;
51
- if (values.auth !== undefined && !isAuthProvider(values.auth)) {
52
+ let authOption;
53
+ if (values.auth !== undefined && !isValidAuthOption(values.auth)) {
52
54
  errors.push(`Invalid auth provider: "${values.auth}". Expected: [ ${availableAuthProviders.join(', ')} ]`);
53
55
  }
54
56
  else if (values.auth !== undefined) {
55
- authProvider = values.auth;
57
+ authOption = values.auth;
56
58
  }
57
59
  else if (values.skip) {
58
- authProvider = 'none';
60
+ authOption = 'none';
61
+ }
62
+ const absProviders = [];
63
+ if (values['abs-provider'] !== undefined) {
64
+ if (authOption === undefined || authOption === 'none') {
65
+ authOption = 'abs';
66
+ }
67
+ else if (authOption !== 'abs') {
68
+ errors.push(`Invalid auth configuration: "--abs-provider" specified but auth provider is set to "${authOption}". "--abs-provider" can only be used with "abs" auth provider.`);
69
+ }
70
+ for (const provider of values['abs-provider']) {
71
+ if (!isValidProviderOption(provider)) {
72
+ errors.push(`Invalid Absolute-Auth provider: "${provider}". Expected: ${Object.keys(providers).join(', ')}`);
73
+ continue;
74
+ }
75
+ else {
76
+ absProviders.push(provider);
77
+ }
78
+ }
59
79
  }
60
80
  let databaseEngine;
61
81
  if (values.db !== undefined && !isDatabaseEngine(values.db)) {
@@ -216,8 +236,9 @@ export const parseCommandLineOptions = () => {
216
236
  }
217
237
  values.env = validEnv.length ? validEnv : undefined;
218
238
  const argumentConfiguration = {
239
+ absProviders: absProviders.length ? absProviders : undefined,
219
240
  assetsDirectory: values.assets,
220
- authProvider,
241
+ authOption,
221
242
  buildDirectory: values.build,
222
243
  codeQualityTool,
223
244
  databaseDirectory,
@@ -1,4 +1,4 @@
1
- import { env } from 'node:process';
1
+ import { env } from 'process';
2
2
  /**
3
3
  * @author Adapted from the create-t3-app project
4
4
  * @see https://github.com/t3-oss/create-t3-app