create-absolutejs 0.10.2 → 0.11.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 (75) hide show
  1. package/dist/constants.d.ts +2 -2
  2. package/dist/constants.js +2 -2
  3. package/dist/data.d.ts +10 -10
  4. package/dist/data.js +104 -95
  5. package/dist/generators/angular/generateAngularPage.d.ts +7 -0
  6. package/dist/generators/angular/generateAngularPage.js +175 -0
  7. package/dist/generators/angular/scaffoldAngular.d.ts +2 -0
  8. package/dist/generators/angular/scaffoldAngular.js +34 -0
  9. package/dist/generators/configurations/generateEslintConfig.d.ts +2 -0
  10. package/dist/generators/configurations/generateEslintConfig.js +253 -0
  11. package/dist/generators/configurations/generatePackageJson.js +26 -3
  12. package/dist/generators/configurations/scaffoldConfigurationFiles.js +28 -3
  13. package/dist/generators/db/dockerInitTemplates.d.ts +9 -9
  14. package/dist/generators/db/dockerInitTemplates.js +9 -9
  15. package/dist/generators/db/generateDatabaseTypes.js +5 -0
  16. package/dist/generators/db/generateDockerContainer.js +4 -2
  17. package/dist/generators/db/generateDrizzleSchema.js +16 -6
  18. package/dist/generators/db/handlerTemplates.d.ts +5 -0
  19. package/dist/generators/db/handlerTemplates.js +6 -0
  20. package/dist/generators/db/scaffoldDocker.js +42 -30
  21. package/dist/generators/html/generateHTMLPage.d.ts +1 -1
  22. package/dist/generators/html/generateHTMLPage.js +2 -2
  23. package/dist/generators/html/scaffoldHTML.d.ts +1 -1
  24. package/dist/generators/html/scaffoldHTML.js +2 -2
  25. package/dist/generators/htmx/generateHTMXPage.d.ts +1 -1
  26. package/dist/generators/htmx/generateHTMXPage.js +2 -2
  27. package/dist/generators/htmx/scaffoldHTMX.d.ts +1 -1
  28. package/dist/generators/htmx/scaffoldHTMX.js +2 -2
  29. package/dist/generators/project/computeFlags.d.ts +1 -0
  30. package/dist/generators/project/computeFlags.js +1 -0
  31. package/dist/generators/project/generateBuildBlock.d.ts +2 -1
  32. package/dist/generators/project/generateBuildBlock.js +11 -7
  33. package/dist/generators/project/generateDBBlock.js +6 -0
  34. package/dist/generators/project/generateImportsBlock.js +39 -27
  35. package/dist/generators/project/generateMarkupCSS.js +4 -0
  36. package/dist/generators/project/generateRoutesBlock.d.ts +1 -2
  37. package/dist/generators/project/generateRoutesBlock.js +28 -17
  38. package/dist/generators/project/generateServer.js +5 -10
  39. package/dist/generators/project/scaffoldFrontends.js +20 -1
  40. package/dist/generators/react/generateReactComponents.d.ts +2 -2
  41. package/dist/generators/react/generateReactComponents.js +33 -33
  42. package/dist/generators/react/scaffoldReact.d.ts +1 -1
  43. package/dist/generators/react/scaffoldReact.js +2 -2
  44. package/dist/generators/svelte/generateSveltePage.d.ts +1 -1
  45. package/dist/generators/svelte/generateSveltePage.js +20 -2
  46. package/dist/generators/svelte/scaffoldSvelte.d.ts +1 -1
  47. package/dist/generators/svelte/scaffoldSvelte.js +2 -2
  48. package/dist/generators/vue/generateVuePage.d.ts +1 -1
  49. package/dist/generators/vue/generateVuePage.js +20 -2
  50. package/dist/generators/vue/scaffoldVue.d.ts +1 -1
  51. package/dist/generators/vue/scaffoldVue.js +2 -2
  52. package/dist/questions/databaseEngine.d.ts +1 -1
  53. package/dist/questions/frontendDirectoryConfigurations.d.ts +1 -1
  54. package/dist/questions/frontends.d.ts +1 -1
  55. package/dist/questions/frontends.js +3 -3
  56. package/dist/scaffold.js +14 -2
  57. package/dist/templates/assets/svg/angular.svg +18 -0
  58. package/dist/templates/configurations/{eslint.config.mjs → eslint.config.example.mjs} +10 -10
  59. package/dist/templates/configurations/tsconfig.example.json +12 -12
  60. package/dist/templates/react/components/App.tsx +2 -2
  61. package/dist/templates/react/components/Head.tsx +7 -7
  62. package/dist/templates/react/components/OAuthLink.tsx +2 -2
  63. package/dist/templates/styles/colors.ts +6 -8
  64. package/dist/templates/styles/reset.css +15 -0
  65. package/dist/templates/svelte/components/Counter.svelte +4 -0
  66. package/dist/templates/vue/components/CountButton.vue +1 -1
  67. package/dist/typeGuards.d.ts +6 -6
  68. package/dist/typeGuards.js +6 -6
  69. package/dist/types.d.ts +1 -0
  70. package/dist/utils/checkDockerInstalled.d.ts +4 -4
  71. package/dist/utils/checkDockerInstalled.js +78 -71
  72. package/dist/utils/parseCommandLineOptions.js +13 -16
  73. package/dist/versions.d.ts +45 -33
  74. package/dist/versions.js +55 -42
  75. package/package.json +10 -9
@@ -1,5 +1,5 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateHTMXPage = (isSingle, frontends) => {
2
+ export const generateHTMXPage = (isSingle, frontends, editBasePath) => {
3
3
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
4
  return `<!doctype html>
5
5
  <html>
@@ -73,7 +73,7 @@ export const generateHTMXPage = (isSingle, frontends) => {
73
73
  >
74
74
  </button>
75
75
  <p>
76
- Edit <code>example/htmx/pages/HtmxHome.html</code> and save
76
+ Edit <code>${editBasePath}/pages/HTMXExample.html</code> and save
77
77
  to test HMR.
78
78
  </p>
79
79
  ${frontends.length > 1
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldHTMX: ({ targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldHTMX: ({ editBasePath, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }: ScaffoldFrontendProps) => void;
@@ -3,7 +3,7 @@ import { join } from 'path';
3
3
  import { Glob } from 'bun';
4
4
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
5
5
  import { generateHTMXPage } from './generateHTMXPage';
6
- export const scaffoldHTMX = ({ targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }) => {
6
+ export const scaffoldHTMX = ({ editBasePath, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }) => {
7
7
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-black.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-black.svg'));
8
8
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-white.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-white.svg'));
9
9
  const glob = new Glob('htmx*.min.js');
@@ -15,7 +15,7 @@ export const scaffoldHTMX = ({ targetDirectory, templatesDirectory, projectAsset
15
15
  copyFileSync(src, dest);
16
16
  break;
17
17
  }
18
- const htmxPage = generateHTMXPage(isSingleFrontend, frontends);
18
+ const htmxPage = generateHTMXPage(isSingleFrontend, frontends, editBasePath);
19
19
  const pagesDirectory = join(targetDirectory, 'pages');
20
20
  mkdirSync(pagesDirectory, { recursive: true });
21
21
  const htmxFilePath = join(pagesDirectory, 'HTMXExample.html');
@@ -1,6 +1,7 @@
1
1
  import type { FrontendDirectories } from '../../types';
2
2
  export type FrameworkFlags = ReturnType<typeof computeFlags>;
3
3
  export declare const computeFlags: (dirs: FrontendDirectories) => {
4
+ requiresAngular: boolean;
4
5
  requiresHtml: boolean;
5
6
  requiresHtmx: boolean;
6
7
  requiresReact: boolean;
@@ -1,4 +1,5 @@
1
1
  export const computeFlags = (dirs) => ({
2
+ requiresAngular: dirs.angular !== undefined,
2
3
  requiresHtml: dirs.html !== undefined,
3
4
  requiresHtmx: dirs.htmx !== undefined,
4
5
  requiresReact: dirs.react !== undefined,
@@ -1,10 +1,11 @@
1
1
  import type { CreateConfiguration, FrontendDirectories } from '../../types';
2
2
  type GenerateBuildBlockProps = {
3
3
  assetsDirectory: string;
4
+ backendDirectory: string;
4
5
  buildDirectory: string;
5
6
  frontendDirectories: FrontendDirectories;
6
7
  publicDirectory: string;
7
8
  tailwind: CreateConfiguration['tailwind'];
8
9
  };
9
- export declare const generateBuildBlock: ({ assetsDirectory, buildDirectory, frontendDirectories, publicDirectory, tailwind }: GenerateBuildBlockProps) => string;
10
+ export declare const generateBuildBlock: ({ assetsDirectory, backendDirectory, buildDirectory, frontendDirectories, publicDirectory, tailwind }: GenerateBuildBlockProps) => string;
10
11
  export {};
@@ -1,4 +1,6 @@
1
- export const generateBuildBlock = ({ assetsDirectory, buildDirectory, frontendDirectories, publicDirectory, tailwind }) => {
1
+ import { writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ export const generateBuildBlock = ({ assetsDirectory, backendDirectory, buildDirectory, frontendDirectories, publicDirectory, tailwind }) => {
2
4
  const configEntries = [
3
5
  `assetsDirectory: '${assetsDirectory}'`,
4
6
  `buildDirectory: '${buildDirectory}'`,
@@ -7,11 +9,13 @@ export const generateBuildBlock = ({ assetsDirectory, buildDirectory, frontendDi
7
9
  tailwind ? `tailwind: ${JSON.stringify(tailwind)}` : ''
8
10
  ]
9
11
  .filter(Boolean)
10
- .join(',\n ');
11
- return `const buildConfig: BuildConfig = {
12
- ${configEntries}
13
- };
12
+ .join(',\n\t');
13
+ const configContent = `import { defineConfig } from '@absolutejs/absolute'
14
14
 
15
- const isDev = env.NODE_ENV === 'development';
16
- const result = isDev ? await devBuild(buildConfig) : await build(buildConfig);`;
15
+ export default defineConfig({
16
+ \t${configEntries}
17
+ })
18
+ `;
19
+ writeFileSync(join(backendDirectory, '..', '..', 'absolute.config.ts'), configContent);
20
+ return `const { absolutejs, manifest } = await prepare()`;
17
21
  };
@@ -64,6 +64,12 @@ export const generateDBBlock = ({ databaseEngine, orm, databaseHost }) => {
64
64
  return `
65
65
  const pool = createPool(getEnv("DATABASE_URL"))
66
66
  const db = drizzle(pool, { schema, mode: 'default' })
67
+ `;
68
+ }
69
+ if (databaseEngine === 'mssql' && hostKey === 'none') {
70
+ return `
71
+ const pool = await connect(getEnv("DATABASE_URL"))
72
+ const db = drizzle({ client: pool }, { schema })
67
73
  `;
68
74
  }
69
75
  if (databaseEngine === 'postgresql' && databaseHost === 'neon') {
@@ -4,6 +4,8 @@ import { isDrizzleDialect } from '../../typeGuards';
4
4
  export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authOption, databaseEngine, databaseHost, frontendDirectories }) => {
5
5
  const rawImports = [];
6
6
  const pushHandler = (cond, name, pkg = '@absolutejs/absolute') => cond && rawImports.push(`import { ${name} } from '${pkg}'`);
7
+ pushHandler(flags.requiresAngular, 'handleAngularPageRequest', '@absolutejs/absolute/angular');
8
+ pushHandler(flags.requiresAngular, 'generateHeadElement');
7
9
  pushHandler(flags.requiresHtml, 'handleHTMLPageRequest');
8
10
  pushHandler(flags.requiresReact, 'handleReactPageRequest');
9
11
  pushHandler(flags.requiresSvelte, 'handleSveltePageRequest', '@absolutejs/absolute/svelte');
@@ -11,6 +13,7 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
11
13
  pushHandler(flags.requiresVue, 'generateHeadElement');
12
14
  pushHandler(flags.requiresHtmx, 'handleHTMXPageRequest');
13
15
  const nonFrameworkOnly = (flags.requiresHtml || flags.requiresHtmx) &&
16
+ !flags.requiresAngular &&
14
17
  !flags.requiresReact &&
15
18
  !flags.requiresSvelte &&
16
19
  !flags.requiresVue;
@@ -26,7 +29,6 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
26
29
  .sort()
27
30
  .join(', ')} } from '${dependency.value}'`);
28
31
  }
29
- rawImports.push(`import { env } from 'bun'`);
30
32
  const buildExamplePath = (dir, file) => `../frontend${dir ? `/${dir}` : ''}/pages/${file}`;
31
33
  const reactDir = frontendDirectories.react;
32
34
  const svelteDir = frontendDirectories.svelte;
@@ -63,6 +65,19 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
63
65
  : [`import { schema } from '../../db/schema'`])
64
66
  ]
65
67
  };
68
+ const getPostgresqlOrmDatabaseImports = () => {
69
+ if (!isRemoteHost)
70
+ return [
71
+ `import { SQL } from 'bun'`,
72
+ `import { drizzle } from 'drizzle-orm/bun-sql'`
73
+ ];
74
+ if (databaseHost === 'planetscale')
75
+ return [
76
+ `import { drizzle } from 'drizzle-orm/node-postgres'`,
77
+ `import { Pool } from 'pg'`
78
+ ];
79
+ return [];
80
+ };
66
81
  const ormDatabaseImports = {
67
82
  drizzle: {
68
83
  gel: [
@@ -73,23 +88,17 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
73
88
  `import { drizzle } from 'drizzle-orm/mysql2'`,
74
89
  `import { createPool } from 'mysql2/promise'`
75
90
  ],
91
+ mssql: [
92
+ `import { connect } from 'mssql'`,
93
+ `import { drizzle } from 'drizzle-orm/node-mssql'`
94
+ ],
76
95
  mysql: !isRemoteHost
77
96
  ? [
78
97
  `import { drizzle } from 'drizzle-orm/mysql2'`,
79
98
  `import { createPool } from 'mysql2/promise'`
80
99
  ]
81
100
  : [],
82
- postgresql: !isRemoteHost
83
- ? [
84
- `import { SQL } from 'bun'`,
85
- `import { drizzle } from 'drizzle-orm/bun-sql'`
86
- ]
87
- : isRemoteHost && databaseHost === 'planetscale'
88
- ? [
89
- `import { drizzle } from 'drizzle-orm/node-postgres'`,
90
- `import { Pool } from 'pg'`
91
- ]
92
- : [],
101
+ postgresql: getPostgresqlOrmDatabaseImports(),
93
102
  singlestore: [
94
103
  `import { drizzle } from 'drizzle-orm/singlestore'`,
95
104
  `import { createPool } from 'mysql2/promise'`
@@ -102,6 +111,22 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
102
111
  : []
103
112
  }
104
113
  };
114
+ const getPostgresqlNoOrmImports = () => {
115
+ if (isRemoteHost && databaseHost === 'neon')
116
+ return [
117
+ ...connectorImports[databaseHost],
118
+ `import { getEnv } from '@absolutejs/absolute'`
119
+ ];
120
+ if (isRemoteHost && databaseHost === 'planetscale')
121
+ return [
122
+ `import { Pool } from 'pg'`,
123
+ `import { getEnv } from '@absolutejs/absolute'`
124
+ ];
125
+ return [
126
+ `import { SQL } from 'bun'`,
127
+ `import { getEnv } from '@absolutejs/absolute'`
128
+ ];
129
+ };
105
130
  const noOrmImports = {
106
131
  cockroachdb: [
107
132
  `import { SQL } from 'bun'`,
@@ -132,20 +157,7 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
132
157
  `import { SQL } from 'bun'`,
133
158
  `import { getEnv } from '@absolutejs/absolute'`
134
159
  ],
135
- postgresql: isRemoteHost && databaseHost === 'neon'
136
- ? [
137
- ...connectorImports[databaseHost],
138
- `import { getEnv } from '@absolutejs/absolute'`
139
- ]
140
- : isRemoteHost && databaseHost === 'planetscale'
141
- ? [
142
- `import { Pool } from 'pg'`,
143
- `import { getEnv } from '@absolutejs/absolute'`
144
- ]
145
- : [
146
- `import { SQL } from 'bun'`,
147
- `import { getEnv } from '@absolutejs/absolute'`
148
- ],
160
+ postgresql: getPostgresqlNoOrmImports(),
149
161
  singlestore: [
150
162
  `import { createPool } from 'mysql2/promise'`,
151
163
  `import { getEnv } from '@absolutejs/absolute'`
@@ -160,7 +172,7 @@ export const generateImportsBlock = ({ backendDirectory, deps, flags, orm, authO
160
172
  if (orm === 'drizzle') {
161
173
  rawImports.push(...ormImports[orm]);
162
174
  }
163
- if (orm == 'drizzle' &&
175
+ if (orm === 'drizzle' &&
164
176
  isRemoteHost &&
165
177
  !(databaseEngine === 'postgresql' && databaseHost === 'planetscale')) {
166
178
  rawImports.push(...connectorImports[databaseHost], ...dialectImports[databaseHost]);
@@ -53,6 +53,10 @@ h1 {
53
53
  filter: drop-shadow(0 0 2rem ${color});
54
54
  }
55
55
 
56
+ button:hover {
57
+ border-color: ${color};
58
+ }
59
+
56
60
  nav {
57
61
  display: flex;
58
62
  gap: 4rem;
@@ -5,7 +5,6 @@ type GenerateRoutesBlockProps = {
5
5
  flags: FrameworkFlags;
6
6
  frontendDirectories: FrontendDirectories;
7
7
  authOption: AuthOption;
8
- buildDirectory: string;
9
8
  };
10
- export declare const generateRoutesBlock: ({ databaseEngine, flags, frontendDirectories, authOption, buildDirectory }: GenerateRoutesBlockProps) => string;
9
+ export declare const generateRoutesBlock: ({ databaseEngine, flags, frontendDirectories, authOption }: GenerateRoutesBlockProps) => string;
11
10
  export {};
@@ -1,8 +1,8 @@
1
1
  import { isFrontend } from '../../typeGuards';
2
- export const generateRoutesBlock = ({ databaseEngine, flags, frontendDirectories, authOption, buildDirectory }) => {
2
+ export const generateRoutesBlock = ({ databaseEngine, flags, frontendDirectories, authOption }) => {
3
3
  const hasDatabase = databaseEngine !== undefined && databaseEngine !== 'none';
4
4
  const routes = [];
5
- const wrap = (handlerCall) => authOption === 'abs'
5
+ const wrap = (handlerCall, isAsync = false) => authOption === 'abs'
6
6
  ? `async ({ cookie: { auth_provider, user_session_id }, store: { session }, status }) => {
7
7
  const { user, error } = await getStatus(session, user_session_id);
8
8
 
@@ -15,29 +15,39 @@ export const generateRoutesBlock = ({ databaseEngine, flags, frontendDirectories
15
15
 
16
16
  return ${handlerCall};
17
17
  }`
18
- : `() => ${handlerCall}`;
18
+ : `${isAsync ? 'async ' : ''}() => ${handlerCall}`;
19
19
  const createHandlerCall = (frontend, directory) => {
20
- const base = `${buildDirectory}${directory ? `/${directory}` : ''}/pages`;
20
+ if (frontend === 'angular')
21
+ return `handleAngularPageRequest(
22
+ () => import('../frontend${directory ? `/${directory}` : ''}/pages/angular-example'),
23
+ asset(manifest, 'AngularExample'),
24
+ asset(manifest, 'AngularExampleIndex'),
25
+ generateHeadElement({
26
+ cssPath: asset(manifest, 'AngularExampleCSS'),
27
+ title: 'AbsoluteJS + Angular'
28
+ }),
29
+ { initialCount: 0 }
30
+ )`;
21
31
  if (frontend === 'html')
22
- return `handleHTMLPageRequest(\`${base}/HTMLExample.html\`)`;
32
+ return `handleHTMLPageRequest(asset(manifest, 'HTMLExample'))`;
23
33
  if (frontend === 'htmx')
24
- return `handleHTMXPageRequest(\`${base}/HTMXExample.html\`)`;
34
+ return `handleHTMXPageRequest(asset(manifest, 'HTMXExample'))`;
25
35
  if (frontend === 'react') {
26
36
  const reactProps = authOption === 'abs'
27
- ? `{ initialCount: 0, cssPath: asset(result, 'ReactExampleCSS'), user, providerConfiguration }`
28
- : `{ initialCount: 0, cssPath: asset(result, 'ReactExampleCSS') }`;
37
+ ? `{ initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS'), user, providerConfiguration }`
38
+ : `{ initialCount: 0, cssPath: asset(manifest, 'ReactExampleCSS') }`;
29
39
  return `handleReactPageRequest(
30
40
  ReactExample,
31
- asset(result, 'ReactExampleIndex'),
41
+ asset(manifest, 'ReactExampleIndex'),
32
42
  ${reactProps}
33
43
  )`;
34
44
  }
35
45
  if (frontend === 'svelte')
36
46
  return `handleSveltePageRequest(
37
47
  SvelteExample,
38
- asset(result, 'SvelteExample'),
39
- asset(result, 'SvelteExampleIndex'),
40
- { initialCount: 0, cssPath: asset(result, 'SvelteExampleCSS') }
48
+ asset(manifest, 'SvelteExample'),
49
+ asset(manifest, 'SvelteExampleIndex'),
50
+ { initialCount: 0, cssPath: asset(manifest, 'SvelteExampleCSS') }
41
51
  )`;
42
52
  if (frontend === 'vue') {
43
53
  const vueComponent = flags.requiresSvelte
@@ -45,10 +55,10 @@ export const generateRoutesBlock = ({ databaseEngine, flags, frontendDirectories
45
55
  : 'VueExample';
46
56
  return `handleVuePageRequest(
47
57
  ${vueComponent},
48
- asset(result, 'VueExample'),
49
- asset(result, 'VueExampleIndex'),
58
+ asset(manifest, 'VueExample'),
59
+ asset(manifest, 'VueExampleIndex'),
50
60
  generateHeadElement({
51
- cssPath: asset(result, 'VueExampleCSS'),
61
+ cssPath: asset(manifest, 'VueExampleCSS'),
52
62
  title: 'AbsoluteJS + Vue',
53
63
  description: 'A Vue.js example with AbsoluteJS'
54
64
  }),
@@ -60,10 +70,11 @@ export const generateRoutesBlock = ({ databaseEngine, flags, frontendDirectories
60
70
  Object.entries(frontendDirectories).forEach(([frontend, directory], entryIndex) => {
61
71
  if (!isFrontend(frontend))
62
72
  return;
63
- const handlerCall = createHandlerCall(frontend, directory);
73
+ const handlerCall = createHandlerCall(frontend, directory ?? '');
64
74
  if (!handlerCall)
65
75
  return;
66
- const handler = wrap(handlerCall);
76
+ const isAsync = frontend === 'angular';
77
+ const handler = wrap(handlerCall, isAsync);
67
78
  if (entryIndex === 0) {
68
79
  routes.push(`.get('/', ${handler})`);
69
80
  }
@@ -22,6 +22,7 @@ export const generateServerFile = ({ tailwind, authOption, plugins, buildDirecto
22
22
  });
23
23
  const manifestBlock = generateBuildBlock({
24
24
  assetsDirectory,
25
+ backendDirectory,
25
26
  buildDirectory,
26
27
  frontendDirectories,
27
28
  publicDirectory,
@@ -59,31 +60,25 @@ export const generateServerFile = ({ tailwind, authOption, plugins, buildDirecto
59
60
  })`;
60
61
  const routesBlock = generateRoutesBlock({
61
62
  authOption,
62
- buildDirectory,
63
63
  databaseEngine,
64
64
  flags,
65
65
  frontendDirectories
66
66
  });
67
- const hmrBlock = `
68
- if (
69
- typeof result.hmrState !== 'string' &&
70
- typeof result.manifest === 'object'
71
- ) {
72
- server.use(hmr(result.hmrState, result.manifest));
73
- }`;
74
67
  const content = `${importsBlock}
75
68
 
76
69
  ${manifestBlock}
77
70
  ${dbBlock ? `${dbBlock}\n` : ''}
78
71
  const server = new Elysia()
72
+ .use(absolutejs)
79
73
  ${useBlock}${authOption === 'abs' ? `\n${guardBlock}` : ''}
80
74
  ${routesBlock}
81
75
  .use(networking)
82
76
  .on('error', err => {
83
77
  const { request } = err
84
78
  console.error(\`Server error on \${request.method} \${request.url}: \${err.message}\`)
85
- });
86
- ${hmrBlock}
79
+ })
80
+
81
+ export type Server = typeof server
87
82
  `;
88
83
  writeFileSync(serverFilePath, content);
89
84
  };
@@ -1,5 +1,6 @@
1
1
  import { copyFileSync, cpSync, mkdirSync } from 'fs';
2
2
  import { join } from 'path';
3
+ import { scaffoldAngular } from '../angular/scaffoldAngular';
3
4
  import { scaffoldHTML } from '../html/scaffoldHTML';
4
5
  import { scaffoldHTMX } from '../htmx/scaffoldHTMX';
5
6
  import { scaffoldReact } from '../react/scaffoldReact';
@@ -25,12 +26,16 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
25
26
  const targetDirectory = join(frontendDirectory, directory);
26
27
  if (!isSingleFrontend)
27
28
  mkdirSync(targetDirectory);
29
+ const editBasePath = directory
30
+ ? `src/frontend/${directory}`
31
+ : 'src/frontend';
28
32
  switch (frontendName) {
29
33
  case 'react':
30
34
  scaffoldReact({
31
35
  absProviders,
32
36
  assetsDirectory,
33
37
  authOption,
38
+ editBasePath,
34
39
  frontends,
35
40
  isSingleFrontend,
36
41
  projectAssetsDirectory,
@@ -43,6 +48,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
43
48
  absProviders,
44
49
  assetsDirectory,
45
50
  authOption,
51
+ editBasePath,
46
52
  frontends,
47
53
  isSingleFrontend,
48
54
  projectAssetsDirectory,
@@ -55,6 +61,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
55
61
  absProviders,
56
62
  assetsDirectory,
57
63
  authOption,
64
+ editBasePath,
58
65
  frontends,
59
66
  projectAssetsDirectory,
60
67
  targetDirectory,
@@ -63,13 +70,24 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
63
70
  copyFileSync(join(templatesDirectory, 'types', 'vue-shim.d.ts'), join(typesDirectory, 'vue-shim.d.ts'));
64
71
  break;
65
72
  case 'angular':
66
- console.warn('Angular is not yet supported. Refer to the documentation for more information.');
73
+ scaffoldAngular({
74
+ absProviders,
75
+ assetsDirectory,
76
+ authOption,
77
+ editBasePath,
78
+ frontends,
79
+ isSingleFrontend,
80
+ projectAssetsDirectory,
81
+ targetDirectory,
82
+ templatesDirectory
83
+ });
67
84
  break;
68
85
  case 'html':
69
86
  scaffoldHTML({
70
87
  absProviders,
71
88
  assetsDirectory,
72
89
  authOption,
90
+ editBasePath,
73
91
  frontends,
74
92
  isSingleFrontend,
75
93
  projectAssetsDirectory,
@@ -83,6 +101,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
83
101
  absProviders,
84
102
  assetsDirectory,
85
103
  authOption,
104
+ editBasePath,
86
105
  frontends,
87
106
  isSingleFrontend,
88
107
  projectAssetsDirectory,
@@ -1,6 +1,6 @@
1
1
  import { ProviderOption } from '@absolutejs/auth';
2
2
  import { AuthOption, Frontend } from '../../types';
3
- export declare const generateAppComponent: (frontends: Frontend[]) => string;
3
+ export declare const generateAppComponent: (frontends: Frontend[], editBasePath: string) => string;
4
4
  export declare const generateDropdownComponent: (frontends: Frontend[]) => string;
5
- export declare const generateSignInComponent: (absProviders: ProviderOption[] | undefined) => string;
6
5
  export declare const generateReactExamplePage: (authOption: AuthOption) => string;
6
+ export declare const generateSignInComponent: (absProviders: ProviderOption[] | undefined) => string;
@@ -1,5 +1,5 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateAppComponent = (frontends) => {
2
+ export const generateAppComponent = (frontends, editBasePath) => {
3
3
  const exploreBlock = frontends.length > 1
4
4
  ? `\n\t\t\t<p style={{ marginTop: '2rem' }}>\n\t\t\t\tExplore the other pages to see multiple frameworks running\n\t\t\t\ttogether.\n\t\t\t</p>`
5
5
  : '';
@@ -33,7 +33,7 @@ export const App = ({ initialCount }: AppProps) => {
33
33
  count is {count}
34
34
  </button>
35
35
  <p>
36
- Edit <code>example/react/pages/ReactExample.tsx</code> and save
36
+ Edit <code>${editBasePath}/pages/ReactExample.tsx</code> and save
37
37
  to test HMR.
38
38
  </p>${exploreBlock}
39
39
  <p
@@ -73,37 +73,6 @@ export const generateDropdownComponent = (frontends) => {
73
73
  );
74
74
  `;
75
75
  };
76
- export const generateSignInComponent = (absProviders) => `import { OAuthLink } from './OAuthLink';
77
-
78
- export const SignIn = () => (
79
- <details
80
- onPointerEnter={(event) => {
81
- if (event.pointerType === 'mouse') {
82
- event.currentTarget.open = true;
83
- }
84
- }}
85
- onPointerLeave={(event) => {
86
- if (event.pointerType === 'mouse') {
87
- event.currentTarget.open = false;
88
- }
89
- }}
90
- >
91
- <summary>Sign In</summary>
92
- <nav>
93
- ${absProviders && absProviders.length > 0
94
- ? absProviders
95
- .map((provider) => {
96
- const logoUrl = `/assets/svg/google-logo.svg`;
97
- const name = provider.charAt(0).toUpperCase() +
98
- provider.slice(1).toLowerCase();
99
- return `<OAuthLink provider="${provider}" logoUrl="${logoUrl}" name="${name}" mode="in" />`;
100
- })
101
- .join('\n\t\t\t')
102
- : `<p>No OAuth providers configured</p>`}
103
- </nav>
104
- </details>
105
- );
106
- `;
107
76
  export const generateReactExamplePage = (authOption) => {
108
77
  const useBlockReturn = authOption === 'abs';
109
78
  const propsType = `
@@ -178,3 +147,34 @@ ${authOption === 'abs' ? extractProps : ''}
178
147
  ${closing}
179
148
  `;
180
149
  };
150
+ export const generateSignInComponent = (absProviders) => `import { OAuthLink } from './OAuthLink';
151
+
152
+ export const SignIn = () => (
153
+ <details
154
+ onPointerEnter={(event) => {
155
+ if (event.pointerType === 'mouse') {
156
+ event.currentTarget.open = true;
157
+ }
158
+ }}
159
+ onPointerLeave={(event) => {
160
+ if (event.pointerType === 'mouse') {
161
+ event.currentTarget.open = false;
162
+ }
163
+ }}
164
+ >
165
+ <summary>Sign In</summary>
166
+ <nav>
167
+ ${absProviders && absProviders.length > 0
168
+ ? absProviders
169
+ .map((provider) => {
170
+ const logoUrl = `/assets/svg/google-logo.svg`;
171
+ const name = provider.charAt(0).toUpperCase() +
172
+ provider.slice(1).toLowerCase();
173
+ return `<OAuthLink provider="${provider}" logoUrl="${logoUrl}" name="${name}" mode="in" />`;
174
+ })
175
+ .join('\n\t\t\t')
176
+ : `<p>No OAuth providers configured</p>`}
177
+ </nav>
178
+ </details>
179
+ );
180
+ `;
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldReact: ({ isSingleFrontend, authOption, targetDirectory, templatesDirectory, frontends, assetsDirectory, projectAssetsDirectory, absProviders }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldReact: ({ isSingleFrontend, authOption, editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders }: ScaffoldFrontendProps) => void;
@@ -2,14 +2,14 @@ import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
4
  import { generateAppComponent, generateDropdownComponent, generateReactExamplePage, generateSignInComponent } from './generateReactComponents';
5
- export const scaffoldReact = ({ isSingleFrontend, authOption, targetDirectory, templatesDirectory, frontends, assetsDirectory, projectAssetsDirectory, absProviders }) => {
5
+ export const scaffoldReact = ({ isSingleFrontend, authOption, editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders }) => {
6
6
  mkdirSync(join(projectAssetsDirectory, 'svg'), { recursive: true });
7
7
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'react.svg'), join(projectAssetsDirectory, 'svg', 'react.svg'));
8
8
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'google-logo.svg'), join(projectAssetsDirectory, 'svg', 'google-logo.svg'));
9
9
  cpSync(join(templatesDirectory, 'react'), targetDirectory, {
10
10
  recursive: true
11
11
  });
12
- const appComponent = generateAppComponent(frontends);
12
+ const appComponent = generateAppComponent(frontends, editBasePath);
13
13
  writeFileSync(join(targetDirectory, 'components', 'App.tsx'), appComponent, 'utf-8');
14
14
  const dropdownComponent = generateDropdownComponent(frontends);
15
15
  writeFileSync(join(targetDirectory, 'components', 'Dropdown.tsx'), dropdownComponent, 'utf-8');
@@ -1,2 +1,2 @@
1
1
  import { Frontend } from '../../types';
2
- export declare const generateSveltePage: (frontends: Frontend[]) => string;
2
+ export declare const generateSveltePage: (frontends: Frontend[], editBasePath: string) => string;
@@ -1,5 +1,5 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateSveltePage = (frontends) => {
2
+ export const generateSveltePage = (frontends, editBasePath) => {
3
3
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
4
  return `<script lang="ts">
5
5
  type SvelteExampleProps = {
@@ -77,7 +77,7 @@ export const generateSveltePage = (frontends) => {
77
77
  <h1>AbsoluteJS + Svelte</h1>
78
78
  <Counter {initialCount} />
79
79
  <p>
80
- Edit <code>example/svelte/pages/SvelteExample.svelte</code> and save
80
+ Edit <code>${editBasePath}/pages/SvelteExample.svelte</code> and save
81
81
  to test HMR.
82
82
  </p>
83
83
  ${frontends.length > 1
@@ -149,6 +149,10 @@ ${frontends.length > 1
149
149
  filter: drop-shadow(0 0 2rem #ff3e00);
150
150
  }
151
151
 
152
+ button:hover {
153
+ border-color: #ff3e00;
154
+ }
155
+
152
156
  nav {
153
157
  display: flex;
154
158
  gap: 4rem;
@@ -241,10 +245,24 @@ ${frontends.length > 1
241
245
  }
242
246
  }
243
247
 
248
+ code {
249
+ background-color: rgba(255, 255, 255, 0.08);
250
+ border: 1px solid rgba(255, 255, 255, 0.1);
251
+ border-radius: 0.375rem;
252
+ font-family: 'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
253
+ font-size: 0.875em;
254
+ padding: 0.2rem 0.5rem;
255
+ }
256
+
244
257
  @media (prefers-color-scheme: light) {
245
258
  header {
246
259
  background-color: #ffffff;
247
260
  }
261
+
262
+ code {
263
+ background-color: rgba(0, 0, 0, 0.06);
264
+ border-color: rgba(0, 0, 0, 0.1);
265
+ }
248
266
  }
249
267
  </style>
250
268
  `;
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldSvelte: ({ isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldSvelte: ({ editBasePath, isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory }: ScaffoldFrontendProps) => void;