create-absolutejs 0.13.7 → 0.13.9

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 (42) hide show
  1. package/dist/generators/angular/generateAngularPage.d.ts +2 -2
  2. package/dist/generators/angular/generateAngularPage.js +29 -2
  3. package/dist/generators/angular/scaffoldAngular.d.ts +1 -1
  4. package/dist/generators/angular/scaffoldAngular.js +13 -5
  5. package/dist/generators/db/scaffoldDatabase.js +8 -2
  6. package/dist/generators/html/generateHTMLPage.d.ts +1 -1
  7. package/dist/generators/html/generateHTMLPage.js +18 -1
  8. package/dist/generators/html/scaffoldHTML.d.ts +1 -1
  9. package/dist/generators/html/scaffoldHTML.js +10 -4
  10. package/dist/generators/htmx/generateHTMXPage.d.ts +1 -1
  11. package/dist/generators/htmx/generateHTMXPage.js +22 -1
  12. package/dist/generators/htmx/scaffoldHTMX.d.ts +1 -1
  13. package/dist/generators/htmx/scaffoldHTMX.js +13 -6
  14. package/dist/generators/project/scaffoldFrontends.d.ts +2 -2
  15. package/dist/generators/project/scaffoldFrontends.js +7 -1
  16. package/dist/generators/react/generateReactComponents.d.ts +1 -1
  17. package/dist/generators/react/generateReactComponents.js +25 -1
  18. package/dist/generators/react/scaffoldReact.d.ts +1 -1
  19. package/dist/generators/react/scaffoldReact.js +10 -2
  20. package/dist/generators/svelte/generateSveltePage.d.ts +1 -1
  21. package/dist/generators/svelte/generateSveltePage.js +22 -1
  22. package/dist/generators/svelte/scaffoldSvelte.d.ts +1 -1
  23. package/dist/generators/svelte/scaffoldSvelte.js +11 -5
  24. package/dist/generators/vue/generateVuePage.d.ts +1 -1
  25. package/dist/generators/vue/generateVuePage.js +23 -1
  26. package/dist/generators/vue/scaffoldVue.d.ts +1 -1
  27. package/dist/generators/vue/scaffoldVue.js +10 -4
  28. package/dist/messages.d.ts +1 -1
  29. package/dist/messages.js +3 -1
  30. package/dist/prompt.js +19 -14
  31. package/dist/questions/directoryConfiguration.js +7 -5
  32. package/dist/questions/frontendDirectoryConfigurations.js +3 -2
  33. package/dist/questions/includeExamples.d.ts +1 -0
  34. package/dist/questions/includeExamples.js +11 -0
  35. package/dist/scaffold.d.ts +1 -1
  36. package/dist/scaffold.js +2 -1
  37. package/dist/types.d.ts +2 -0
  38. package/dist/utils/checkSqliteInstalled.js +10 -2
  39. package/dist/utils/interactive.d.ts +6 -0
  40. package/dist/utils/interactive.js +13 -0
  41. package/dist/utils/parseCommandLineOptions.js +11 -1
  42. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { Frontend } from '../../types';
2
- export declare const generateAngularPage: (_frontends: Frontend[]) => string;
3
- export declare const generateAngularPageHtml: () => string;
2
+ export declare const generateAngularPage: (_frontends: Frontend[], includeExamples: boolean) => "import { Component } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { defineAngularPage } from '@absolutejs/absolute/angular';\n\ntype AngularPageProps = {\n\tinitialCount: number;\n};\n\n@Component({\n\timports: [CommonModule],\n\tselector: 'angular-page',\n\tstandalone: true,\n\ttemplateUrl: '../templates/angular-example.html'\n})\nexport class AngularExampleComponent {}\n\nexport const page = defineAngularPage<AngularPageProps>({\n\tcomponent: AngularExampleComponent\n});\n" | "import { Component, inject, InjectionToken } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { defineAngularPage } from '@absolutejs/absolute/angular';\nimport { DropdownComponent } from '../components/dropdown.component';\nimport { AppComponent } from '../components/app.component';\n\nexport const INITIAL_COUNT = new InjectionToken<number>('INITIAL_COUNT');\n\ntype AngularPageProps = {\n\tinitialCount: number;\n};\n\n@Component({\n\timports: [CommonModule, DropdownComponent, AppComponent],\n\tselector: 'angular-page',\n\tstandalone: true,\n\ttemplateUrl: '../templates/angular-example.html'\n})\nexport class AngularExampleComponent {\n\tinitialCount: number = 0;\n\n\tconstructor() {\n\t\tconst initialCountToken = inject(INITIAL_COUNT, { optional: true });\n\t\tthis.initialCount = initialCountToken ?? 0;\n\t}\n}\n\nexport const page = defineAngularPage<AngularPageProps>({\n\tcomponent: AngularExampleComponent\n});\n";
3
+ export declare const generateAngularPageHtml: (includeExamples: boolean) => "<main></main>\n" | "<header>\n\t<a href=\"/\">AbsoluteJS</a>\n\t<app-dropdown></app-dropdown>\n</header>\n<app-root [initialCount]=\"initialCount\"></app-root>\n";
4
4
  export declare const generateAppComponent: (isSingleFrontend: boolean) => string;
5
5
  export declare const generateAppComponentCss: () => string;
6
6
  export declare const generateAppComponentHtml: (frontends: Frontend[], editBasePath: string) => string;
@@ -1,6 +1,28 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
2
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
- export const generateAngularPage = (_frontends) => `import { Component, inject, InjectionToken } from '@angular/core';
3
+ export const generateAngularPage = (_frontends, includeExamples) => {
4
+ if (!includeExamples)
5
+ return `import { Component } from '@angular/core';
6
+ import { CommonModule } from '@angular/common';
7
+ import { defineAngularPage } from '@absolutejs/absolute/angular';
8
+
9
+ type AngularPageProps = {
10
+ initialCount: number;
11
+ };
12
+
13
+ @Component({
14
+ imports: [CommonModule],
15
+ selector: 'angular-page',
16
+ standalone: true,
17
+ templateUrl: '../templates/angular-example.html'
18
+ })
19
+ export class AngularExampleComponent {}
20
+
21
+ export const page = defineAngularPage<AngularPageProps>({
22
+ component: AngularExampleComponent
23
+ });
24
+ `;
25
+ return `import { Component, inject, InjectionToken } from '@angular/core';
4
26
  import { CommonModule } from '@angular/common';
5
27
  import { defineAngularPage } from '@absolutejs/absolute/angular';
6
28
  import { DropdownComponent } from '../components/dropdown.component';
@@ -31,12 +53,17 @@ export const page = defineAngularPage<AngularPageProps>({
31
53
  component: AngularExampleComponent
32
54
  });
33
55
  `;
34
- export const generateAngularPageHtml = () => `<header>
56
+ };
57
+ export const generateAngularPageHtml = (includeExamples) => {
58
+ if (!includeExamples)
59
+ return `<main></main>\n`;
60
+ return `<header>
35
61
  <a href="/">AbsoluteJS</a>
36
62
  <app-dropdown></app-dropdown>
37
63
  </header>
38
64
  <app-root [initialCount]="initialCount"></app-root>
39
65
  `;
66
+ };
40
67
  export const generateAppComponent = (isSingleFrontend) => `import { Component, Input, ViewEncapsulation } from '@angular/core';
41
68
  import { CommonModule } from '@angular/common';
42
69
  import { CounterComponent } from './counter.component';
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldAngular: ({ editBasePath, isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesDirectory, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldAngular: ({ editBasePath, includeExamples, isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesDirectory, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
@@ -2,16 +2,24 @@ import { copyFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
4
  import { generateAngularPage, generateAngularPageHtml, generateAppComponent, generateAppComponentCss, generateAppComponentHtml, generateCounterComponent, generateCounterComponentCss, generateCounterComponentHtml, generateDropdownComponent, generateDropdownComponentHtml } from './generateAngularPage';
5
- export const scaffoldAngular = ({ editBasePath, isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesDirectory, stylesIndexesDirectory }) => {
5
+ export const scaffoldAngular = ({ editBasePath, includeExamples, isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesDirectory, stylesIndexesDirectory }) => {
6
+ const pagesDirectory = join(targetDirectory, 'pages');
7
+ const templatesDir = join(targetDirectory, 'templates');
8
+ if (!includeExamples) {
9
+ mkdirSync(pagesDirectory, { recursive: true });
10
+ mkdirSync(templatesDir, { recursive: true });
11
+ writeFileSync(join(pagesDirectory, 'angular-example.ts'), generateAngularPage(frontends, false), 'utf-8');
12
+ writeFileSync(join(templatesDir, 'angular-example.html'), generateAngularPageHtml(false), 'utf-8');
13
+ writeFileSync(join(stylesIndexesDirectory, 'angular-example.css'), `@import url('../reset.css');\n\nangular-page {\n\tdisplay: flex;\n\tflex: 1;\n\tflex-direction: column;\n}\n`, 'utf-8');
14
+ return;
15
+ }
6
16
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'angular.svg'), join(projectAssetsDirectory, 'svg', 'angular.svg'));
7
17
  const componentsDirectory = join(targetDirectory, 'components');
8
18
  mkdirSync(componentsDirectory, { recursive: true });
9
- const pagesDirectory = join(targetDirectory, 'pages');
10
19
  mkdirSync(pagesDirectory, { recursive: true });
11
- const templatesDir = join(targetDirectory, 'templates');
12
20
  mkdirSync(templatesDir, { recursive: true });
13
- writeFileSync(join(pagesDirectory, 'angular-example.ts'), generateAngularPage(frontends), 'utf-8');
14
- writeFileSync(join(templatesDir, 'angular-example.html'), generateAngularPageHtml(), 'utf-8');
21
+ writeFileSync(join(pagesDirectory, 'angular-example.ts'), generateAngularPage(frontends, true), 'utf-8');
22
+ writeFileSync(join(templatesDir, 'angular-example.html'), generateAngularPageHtml(true), 'utf-8');
15
23
  writeFileSync(join(componentsDirectory, 'dropdown.component.ts'), generateDropdownComponent(frontends), 'utf-8');
16
24
  writeFileSync(join(templatesDir, 'dropdown.component.html'), generateDropdownComponentHtml(frontends), 'utf-8');
17
25
  writeFileSync(join(componentsDirectory, 'app.component.ts'), generateAppComponent(isSingleFrontend), 'utf-8');
@@ -1,6 +1,5 @@
1
1
  import { mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { $ } from 'bun';
4
3
  import { dim, yellow } from 'picocolors';
5
4
  import { isDrizzleDialect } from '../../typeGuards';
6
5
  import { checkSqliteInstalled } from '../../utils/checkSqliteInstalled';
@@ -52,11 +51,18 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
52
51
  writeFileSync(join(projectDatabaseDirectory, 'schema.sql'), generateRelationalSchema(databaseEngine, authOption));
53
52
  }
54
53
  if (databaseEngine === 'sqlite') {
54
+ // The sqlite3 CLI is a developer convenience (a db shell), never a
55
+ // scaffold requirement — seeding goes through bun:sqlite below, which
56
+ // ships with the runtime. The check only nudges interactive users;
57
+ // headless runs (the Studio container, CI) skip it entirely.
55
58
  void ((orm === undefined || orm === 'none') &&
56
59
  (await checkSqliteInstalled()));
57
60
  const sqliteSchema = generateSqliteSchema(authOption);
58
61
  writeFileSync(join(projectDatabaseDirectory, 'schema.sql'), sqliteSchema);
59
- await $ `sqlite3 ${databaseDirectory}/database.sqlite ".read ${join(databaseDirectory, 'schema.sql')}"`.cwd(projectName);
62
+ const { Database } = await import('bun:sqlite');
63
+ const seeded = new Database(join(projectName, databaseDirectory, 'database.sqlite'));
64
+ seeded.run(sqliteSchema);
65
+ seeded.close();
60
66
  }
61
67
  if ((databaseHost === 'none' || databaseHost === undefined) &&
62
68
  databaseEngine !== 'sqlite' &&
@@ -1,2 +1,2 @@
1
1
  import { CreateConfiguration, Frontend } from '../../types';
2
- export declare const generateHTMLPage: (frontends: Frontend[], useHTMLScripts: CreateConfiguration["useHTMLScripts"], editBasePath: string, isSingleFrontend: boolean) => string;
2
+ export declare const generateHTMLPage: (frontends: Frontend[], useHTMLScripts: CreateConfiguration["useHTMLScripts"], editBasePath: string, isSingleFrontend: boolean, includeExamples: boolean) => string;
@@ -1,5 +1,22 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateHTMLPage = (frontends, useHTMLScripts, editBasePath, isSingleFrontend) => {
2
+ export const generateHTMLPage = (frontends, useHTMLScripts, editBasePath, isSingleFrontend, includeExamples) => {
3
+ const cssHref = `${isSingleFrontend ? '../../' : '../../../'}styles/indexes/html-example.css`;
4
+ if (!includeExamples) {
5
+ return `<!doctype html>
6
+ <html>
7
+ <head>
8
+ <title>AbsoluteJS + HTML</title>
9
+ <meta charset="utf-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
11
+ <link rel="stylesheet" type="text/css" href="${cssHref}" />
12
+ <link rel="icon" href="/assets/ico/favicon.ico" />
13
+ </head>
14
+ <body>
15
+ <main></main>
16
+ </body>
17
+ </html>
18
+ `;
19
+ }
3
20
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
21
  const initialCount = useHTMLScripts ? '0' : 'disabled';
5
22
  const scriptTagBlock = useHTMLScripts
@@ -2,5 +2,5 @@ import { ScaffoldFrontendProps } from '../../types';
2
2
  type ScaffoldHTMLProps = ScaffoldFrontendProps & {
3
3
  useHTMLScripts: boolean;
4
4
  };
5
- export declare const scaffoldHTML: ({ editBasePath, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldHTMLProps) => void;
5
+ export declare const scaffoldHTML: ({ editBasePath, includeExamples, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldHTMLProps) => void;
6
6
  export {};
@@ -2,10 +2,17 @@ import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
4
  import { generateHTMLPage } from './generateHTMLPage';
5
- export const scaffoldHTML = ({ editBasePath, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }) => {
6
- copyFileSync(join(templatesDirectory, 'assets', 'svg', 'HTML5_Badge.svg'), join(projectAssetsDirectory, 'svg', 'HTML5_Badge.svg'));
7
- const htmlPage = generateHTMLPage(frontends, useHTMLScripts, editBasePath, isSingleFrontend);
5
+ export const scaffoldHTML = ({ editBasePath, includeExamples, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }) => {
8
6
  const pagesDirectory = join(targetDirectory, 'pages');
7
+ const cssOutputFile = join(stylesIndexesDirectory, 'html-example.css');
8
+ if (!includeExamples) {
9
+ mkdirSync(pagesDirectory, { recursive: true });
10
+ writeFileSync(join(pagesDirectory, 'HTMLExample.html'), generateHTMLPage(frontends, useHTMLScripts, editBasePath, isSingleFrontend, false), 'utf-8');
11
+ writeFileSync(cssOutputFile, `@import url('../reset.css');`, 'utf-8');
12
+ return;
13
+ }
14
+ copyFileSync(join(templatesDirectory, 'assets', 'svg', 'HTML5_Badge.svg'), join(projectAssetsDirectory, 'svg', 'HTML5_Badge.svg'));
15
+ const htmlPage = generateHTMLPage(frontends, useHTMLScripts, editBasePath, isSingleFrontend, true);
9
16
  mkdirSync(pagesDirectory, { recursive: true });
10
17
  const htmlFilePath = join(pagesDirectory, 'HTMLExample.html');
11
18
  writeFileSync(htmlFilePath, htmlPage, 'utf-8');
@@ -13,7 +20,6 @@ export const scaffoldHTML = ({ editBasePath, isSingleFrontend, targetDirectory,
13
20
  cpSync(join(templatesDirectory, 'html', 'scripts'), scriptsDirectory, {
14
21
  recursive: true
15
22
  });
16
- const cssOutputFile = join(stylesIndexesDirectory, 'html-example.css');
17
23
  const htmlCSS = generateMarkupCSS('html', '#e34f26');
18
24
  writeFileSync(cssOutputFile, htmlCSS, 'utf-8');
19
25
  };
@@ -1,2 +1,2 @@
1
1
  import { Frontend } from '../../types';
2
- export declare const generateHTMXPage: (isSingle: boolean, frontends: Frontend[], editBasePath: string) => string;
2
+ export declare const generateHTMXPage: (isSingle: boolean, frontends: Frontend[], editBasePath: string, includeExamples: boolean) => string;
@@ -1,5 +1,26 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateHTMXPage = (isSingle, frontends, editBasePath) => {
2
+ export const generateHTMXPage = (isSingle, frontends, editBasePath, includeExamples) => {
3
+ if (!includeExamples) {
4
+ return `<!doctype html>
5
+ <html>
6
+ <head>
7
+ <title>AbsoluteJS + HTMX</title>
8
+ <meta charset="utf-8" />
9
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
10
+ <link
11
+ rel="stylesheet"
12
+ type="text/css"
13
+ href="${isSingle ? '../../' : '../../../'}styles/indexes/htmx-example.css"
14
+ />
15
+ <link rel="icon" href="/assets/ico/favicon.ico" />
16
+ <script src="${isSingle ? '' : '/htmx'}/htmx.min.js"></script>
17
+ </head>
18
+ <body>
19
+ <main></main>
20
+ </body>
21
+ </html>
22
+ `;
23
+ }
3
24
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
25
  return `<!doctype html>
5
26
  <html>
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldHTMX: ({ editBasePath, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldHTMX: ({ editBasePath, includeExamples, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
@@ -3,9 +3,10 @@ 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 = ({ editBasePath, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend, stylesIndexesDirectory }) => {
7
- copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-black.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-black.svg'));
8
- copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-white.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-white.svg'));
6
+ export const scaffoldHTMX = ({ editBasePath, includeExamples, targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend, stylesIndexesDirectory }) => {
7
+ const pagesDirectory = join(targetDirectory, 'pages');
8
+ const cssOutputFile = join(stylesIndexesDirectory, 'htmx-example.css');
9
+ // htmx.min.js is the framework runtime, copied in both modes.
9
10
  const glob = new Glob('htmx*.min.js');
10
11
  for (const relativePath of glob.scanSync({
11
12
  cwd: join(templatesDirectory, 'htmx')
@@ -15,12 +16,18 @@ export const scaffoldHTMX = ({ editBasePath, targetDirectory, templatesDirectory
15
16
  copyFileSync(src, dest);
16
17
  break;
17
18
  }
18
- const htmxPage = generateHTMXPage(isSingleFrontend, frontends, editBasePath);
19
- const pagesDirectory = join(targetDirectory, 'pages');
19
+ if (!includeExamples) {
20
+ mkdirSync(pagesDirectory, { recursive: true });
21
+ writeFileSync(join(pagesDirectory, 'HTMXExample.html'), generateHTMXPage(isSingleFrontend, frontends, editBasePath, false), 'utf-8');
22
+ writeFileSync(cssOutputFile, `@import url('../reset.css');`, 'utf-8');
23
+ return;
24
+ }
25
+ copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-black.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-black.svg'));
26
+ copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-white.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-white.svg'));
27
+ const htmxPage = generateHTMXPage(isSingleFrontend, frontends, editBasePath, true);
20
28
  mkdirSync(pagesDirectory, { recursive: true });
21
29
  const htmxFilePath = join(pagesDirectory, 'HTMXExample.html');
22
30
  writeFileSync(htmxFilePath, htmxPage, 'utf-8');
23
- const cssOutputFile = join(stylesIndexesDirectory, 'htmx-example.css');
24
31
  const htmxCSS = generateMarkupCSS('htmx', '#3465a4');
25
32
  writeFileSync(cssOutputFile, htmxCSS, 'utf-8');
26
33
  };
@@ -1,9 +1,9 @@
1
1
  import type { CreateConfiguration } from '../../types';
2
- type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories' | 'assetsDirectory' | 'frontends' | 'authOption' | 'absProviders' | 'useTailwind'> & {
2
+ type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories' | 'assetsDirectory' | 'frontends' | 'authOption' | 'absProviders' | 'includeExamples' | 'useTailwind'> & {
3
3
  frontendDirectory: string;
4
4
  templatesDirectory: string;
5
5
  projectAssetsDirectory: string;
6
6
  typesDirectory: string;
7
7
  };
8
- export declare const scaffoldFrontends: ({ frontendDirectory, assetsDirectory, absProviders, authOption, templatesDirectory, projectAssetsDirectory, typesDirectory, useHTMLScripts, useTailwind, frontendDirectories, frontends }: ScaffoldFrontendsProps) => void;
8
+ export declare const scaffoldFrontends: ({ frontendDirectory, assetsDirectory, absProviders, authOption, includeExamples, templatesDirectory, projectAssetsDirectory, typesDirectory, useHTMLScripts, useTailwind, frontendDirectories, frontends }: ScaffoldFrontendsProps) => void;
9
9
  export {};
@@ -6,7 +6,7 @@ import { scaffoldHTMX } from '../htmx/scaffoldHTMX';
6
6
  import { scaffoldReact } from '../react/scaffoldReact';
7
7
  import { scaffoldSvelte } from '../svelte/scaffoldSvelte';
8
8
  import { scaffoldVue } from '../vue/scaffoldVue';
9
- export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProviders, authOption, templatesDirectory, projectAssetsDirectory, typesDirectory, useHTMLScripts, useTailwind, frontendDirectories, frontends }) => {
9
+ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProviders, authOption, includeExamples, templatesDirectory, projectAssetsDirectory, typesDirectory, useHTMLScripts, useTailwind, frontendDirectories, frontends }) => {
10
10
  const srcDirectory = dirname(frontendDirectory);
11
11
  const globalStylesDirectory = join(srcDirectory, 'styles');
12
12
  const stylesIndexesDirectory = join(globalStylesDirectory, 'indexes');
@@ -42,6 +42,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
42
42
  authOption,
43
43
  editBasePath,
44
44
  frontends,
45
+ includeExamples,
45
46
  isSingleFrontend,
46
47
  projectAssetsDirectory,
47
48
  stylesDirectory: stylesTargetDirectory,
@@ -57,6 +58,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
57
58
  authOption,
58
59
  editBasePath,
59
60
  frontends,
61
+ includeExamples,
60
62
  isSingleFrontend,
61
63
  projectAssetsDirectory,
62
64
  stylesDirectory: stylesTargetDirectory,
@@ -72,6 +74,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
72
74
  authOption,
73
75
  editBasePath,
74
76
  frontends,
77
+ includeExamples,
75
78
  projectAssetsDirectory,
76
79
  stylesDirectory: stylesTargetDirectory,
77
80
  stylesIndexesDirectory,
@@ -87,6 +90,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
87
90
  authOption,
88
91
  editBasePath,
89
92
  frontends,
93
+ includeExamples,
90
94
  isSingleFrontend,
91
95
  projectAssetsDirectory,
92
96
  stylesDirectory: stylesTargetDirectory,
@@ -102,6 +106,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
102
106
  authOption,
103
107
  editBasePath,
104
108
  frontends,
109
+ includeExamples,
105
110
  isSingleFrontend,
106
111
  projectAssetsDirectory,
107
112
  stylesDirectory: stylesTargetDirectory,
@@ -118,6 +123,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
118
123
  authOption,
119
124
  editBasePath,
120
125
  frontends,
126
+ includeExamples,
121
127
  isSingleFrontend,
122
128
  projectAssetsDirectory,
123
129
  stylesDirectory: stylesTargetDirectory,
@@ -2,5 +2,5 @@ import { ProviderOption } from '@absolutejs/auth';
2
2
  import { AuthOption, Frontend } from '../../types';
3
3
  export declare const generateAppComponent: (frontends: Frontend[], editBasePath: string) => string;
4
4
  export declare const generateDropdownComponent: (frontends: Frontend[]) => string;
5
- export declare const generateReactExamplePage: (authOption: AuthOption) => string;
5
+ export declare const generateReactExamplePage: (authOption: AuthOption, includeExamples: boolean) => string;
6
6
  export declare const generateSignInComponent: (absProviders: ProviderOption[] | undefined) => string;
@@ -73,7 +73,31 @@ export const generateDropdownComponent = (frontends) => {
73
73
  );
74
74
  `;
75
75
  };
76
- export const generateReactExamplePage = (authOption) => {
76
+ export const generateReactExamplePage = (authOption, includeExamples) => {
77
+ if (!includeExamples) {
78
+ const authImports = authOption === 'abs'
79
+ ? `import type { User } from '../../../types/databaseTypes';\nimport type { ProviderConfiguration } from '@absolutejs/auth';\n`
80
+ : '';
81
+ const authProps = authOption === 'abs'
82
+ ? '\n\tuser: User | null;\n\tproviderConfiguration: ProviderConfiguration | undefined;'
83
+ : '';
84
+ return `${authImports}import { Head } from '../components/Head';
85
+
86
+ type ReactExampleProps = {
87
+ initialCount: number;
88
+ cssPath: string;${authProps}
89
+ };
90
+
91
+ export const ReactExample = ({ cssPath }: ReactExampleProps) => (
92
+ <html>
93
+ <Head cssPath={cssPath} />
94
+ <body>
95
+ <main></main>
96
+ </body>
97
+ </html>
98
+ );
99
+ `;
100
+ }
77
101
  const useBlockReturn = authOption === 'abs';
78
102
  const propsType = `
79
103
  type ReactExampleProps = {
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldReact: ({ authOption, editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldReact: ({ authOption, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
@@ -2,7 +2,15 @@ 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 = ({ authOption, editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }) => {
5
+ export const scaffoldReact = ({ authOption, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }) => {
6
+ if (!includeExamples) {
7
+ mkdirSync(join(targetDirectory, 'components'), { recursive: true });
8
+ copyFileSync(join(templatesDirectory, 'react', 'components', 'Head.tsx'), join(targetDirectory, 'components', 'Head.tsx'));
9
+ mkdirSync(join(targetDirectory, 'pages'), { recursive: true });
10
+ writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), generateReactExamplePage(authOption, false), 'utf-8');
11
+ writeFileSync(join(stylesIndexesDirectory, 'react-example.css'), `@import url('../reset.css');`, 'utf-8');
12
+ return;
13
+ }
6
14
  mkdirSync(join(projectAssetsDirectory, 'svg'), { recursive: true });
7
15
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'react.svg'), join(projectAssetsDirectory, 'svg', 'react.svg'));
8
16
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'google-logo.svg'), join(projectAssetsDirectory, 'svg', 'google-logo.svg'));
@@ -17,7 +25,7 @@ export const scaffoldReact = ({ authOption, editBasePath, targetDirectory, templ
17
25
  const signInComponent = generateSignInComponent(absProviders);
18
26
  writeFileSync(join(targetDirectory, 'components', 'SignIn.tsx'), signInComponent, 'utf-8');
19
27
  }
20
- const pageComponent = generateReactExamplePage(authOption);
28
+ const pageComponent = generateReactExamplePage(authOption, true);
21
29
  mkdirSync(join(targetDirectory, 'pages'), { recursive: true });
22
30
  writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), pageComponent, 'utf-8');
23
31
  const cssOutputFile = join(stylesIndexesDirectory, 'react-example.css');
@@ -1,2 +1,2 @@
1
1
  import { Frontend } from '../../types';
2
- export declare const generateSveltePage: (frontends: Frontend[], editBasePath: string) => string;
2
+ export declare const generateSveltePage: (frontends: Frontend[], editBasePath: string, includeExamples: boolean) => string;
@@ -1,5 +1,26 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateSveltePage = (frontends, editBasePath) => {
2
+ export const generateSveltePage = (frontends, editBasePath, includeExamples) => {
3
+ if (!includeExamples) {
4
+ return `<script lang="ts">
5
+ type SvelteExampleProps = {
6
+ initialCount: number;
7
+ cssPath: string;
8
+ };
9
+
10
+ let { cssPath }: SvelteExampleProps = $props();
11
+ </script>
12
+
13
+ <svelte:head>
14
+ <meta charset="utf-8" />
15
+ <title>AbsoluteJS + Svelte</title>
16
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
17
+ <link rel="icon" href="/assets/ico/favicon.ico" />
18
+ <link rel="stylesheet" href={cssPath} type="text/css" />
19
+ </svelte:head>
20
+
21
+ <main></main>
22
+ `;
23
+ }
3
24
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
25
  return `<script lang="ts">
5
26
  type SvelteExampleProps = {
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldSvelte: ({ editBasePath, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldSvelte: ({ editBasePath, includeExamples, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
@@ -1,17 +1,23 @@
1
1
  import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateSveltePage } from './generateSveltePage';
4
- export const scaffoldSvelte = ({ editBasePath, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }) => {
4
+ export const scaffoldSvelte = ({ editBasePath, includeExamples, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory, stylesIndexesDirectory }) => {
5
+ const pagesDirectory = join(targetDirectory, 'pages');
6
+ const cssOutputFile = join(stylesIndexesDirectory, 'svelte-example.css');
7
+ const svelteCSS = `@import url('../reset.css');`;
8
+ if (!includeExamples) {
9
+ mkdirSync(pagesDirectory, { recursive: true });
10
+ writeFileSync(join(pagesDirectory, 'SvelteExample.svelte'), generateSveltePage(frontends, editBasePath, false), 'utf-8');
11
+ writeFileSync(cssOutputFile, svelteCSS, 'utf-8');
12
+ return;
13
+ }
5
14
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'svelte-logo.svg'), join(projectAssetsDirectory, 'svg', 'svelte-logo.svg'));
6
15
  cpSync(join(templatesDirectory, 'svelte'), targetDirectory, {
7
16
  recursive: true
8
17
  });
9
- const sveltePage = generateSveltePage(frontends, editBasePath);
10
- const pagesDirectory = join(targetDirectory, 'pages');
18
+ const sveltePage = generateSveltePage(frontends, editBasePath, true);
11
19
  mkdirSync(pagesDirectory, { recursive: true });
12
20
  const svelteFilePath = join(pagesDirectory, 'SvelteExample.svelte');
13
21
  writeFileSync(svelteFilePath, sveltePage, 'utf-8');
14
- const cssOutputFile = join(stylesIndexesDirectory, 'svelte-example.css');
15
- const svelteCSS = `@import url('../reset.css');`;
16
22
  writeFileSync(cssOutputFile, svelteCSS, 'utf-8');
17
23
  };
@@ -1,2 +1,2 @@
1
1
  import { Frontend } from '../../types';
2
- export declare const generateVuePage: (frontends: Frontend[], editBasePath: string) => string;
2
+ export declare const generateVuePage: (frontends: Frontend[], editBasePath: string, includeExamples: boolean) => string;
@@ -1,5 +1,27 @@
1
1
  import { formatNavLink } from '../../utils/formatNavLink';
2
- export const generateVuePage = (frontends, editBasePath) => {
2
+ export const generateVuePage = (frontends, editBasePath, includeExamples) => {
3
+ if (!includeExamples) {
4
+ return `<script setup lang="ts">
5
+ defineProps<{
6
+ initialCount: number;
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <main></main>
12
+ </template>
13
+
14
+ <style scoped>
15
+ :global(#root) {
16
+ display: flex;
17
+ flex-direction: column;
18
+ margin: 0 auto;
19
+ height: 100%;
20
+ width: 100%;
21
+ }
22
+ </style>
23
+ `;
24
+ }
3
25
  const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
26
  return `<script setup lang="ts">
5
27
  import CountButton from '../components/CountButton.vue';
@@ -1,4 +1,4 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
2
  type ScaffoldVueProps = Omit<ScaffoldFrontendProps, 'isSingleFrontend'>;
3
- export declare const scaffoldVue: ({ editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldVueProps) => void;
3
+ export declare const scaffoldVue: ({ editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, stylesIndexesDirectory }: ScaffoldVueProps) => void;
4
4
  export {};
@@ -2,17 +2,23 @@ import { cpSync, copyFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
4
  import { generateVuePage } from './generateVuePage';
5
- export const scaffoldVue = ({ editBasePath, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, stylesIndexesDirectory }) => {
5
+ export const scaffoldVue = ({ editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, stylesIndexesDirectory }) => {
6
+ const cssOutputFile = join(stylesIndexesDirectory, 'vue-example.css');
7
+ const pagesDirectory = join(targetDirectory, 'pages');
8
+ if (!includeExamples) {
9
+ mkdirSync(pagesDirectory, { recursive: true });
10
+ writeFileSync(join(pagesDirectory, 'VueExample.vue'), generateVuePage(frontends, editBasePath, false), 'utf-8');
11
+ writeFileSync(cssOutputFile, `@import url('../reset.css');`, 'utf-8');
12
+ return;
13
+ }
6
14
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'vue-logo.svg'), join(projectAssetsDirectory, 'svg', 'vue-logo.svg'));
7
15
  cpSync(join(templatesDirectory, 'vue'), targetDirectory, {
8
16
  recursive: true
9
17
  });
10
- const vuePage = generateVuePage(frontends, editBasePath);
11
- const pagesDirectory = join(targetDirectory, 'pages');
18
+ const vuePage = generateVuePage(frontends, editBasePath, true);
12
19
  mkdirSync(pagesDirectory, { recursive: true });
13
20
  const vueFilePath = join(pagesDirectory, 'VueExample.vue');
14
21
  writeFileSync(vueFilePath, vuePage, 'utf-8');
15
- const cssOutputFile = join(stylesIndexesDirectory, 'vue-example.css');
16
22
  const vueCSS = generateMarkupCSS('vue', '#42b883');
17
23
  writeFileSync(cssOutputFile, vueCSS, 'utf-8');
18
24
  };
@@ -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, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
13
+ export declare const getDebugMessage: ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
14
14
  export {};
package/dist/messages.js CHANGED
@@ -31,6 +31,7 @@ Options:
31
31
  ${cyan('--htmx-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the HTMX frontend
32
32
  ${cyan('--install')} Use the same package manager to install dependencies
33
33
  ${cyan('--lts')} Use LTS versions of required packages
34
+ ${cyan('--no-examples')} Generate a bare, bootable skeleton without example pages, components, or demo styles
34
35
  ${cyan('--orm')} ${dim(cyan('<orm>'))} ORM to configure: "drizzle" | "prisma" | 'none'
35
36
  ${cyan('--plugin')} ${dim(cyan('<plugin>'))} Elysia plugin(s) to include (repeatable); 'none' skips plugin setup
36
37
  ${cyan('--react')} Include a React frontend
@@ -48,7 +49,7 @@ export const getOutroMessage = ({ projectName, packageManager, installDependenci
48
49
  `${cyan('cd')} ${projectName}\n` +
49
50
  `${installDependenciesNow ? '' : `${cyan(`${packageManager} install`)}\n`}` +
50
51
  `${cyan(`${packageManager} dev`)}`; // TODO: Some package managers need run
51
- export const getDebugMessage = ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
52
+ export const getDebugMessage = ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
52
53
  const htmlScriptingValue = useHTMLScripts
53
54
  ? blueBright('TypeScript')
54
55
  : dim('None');
@@ -68,6 +69,7 @@ export const getDebugMessage = ({ response: { projectName, codeQualityTool, dire
68
69
  ['Tailwind Configuration', tailwindSection],
69
70
  [frontends.length === 1 ? 'Frontend' : 'Frontends', frontends.map((name) => frontendLabels[name]).join(', ')],
70
71
  ['HTML Scripting', frontends.includes('html') ? htmlScriptingValue : dim('None')],
72
+ ['Example Content', includeExamples ? green('Yes') : red('No')],
71
73
  ['Build Directory', buildDirectory],
72
74
  ['Assets Directory', assetsDirectory],
73
75
  ['Database Engine', databaseEngine && databaseEngine !== 'none' ? databaseEngine : dim('None')],
package/dist/prompt.js CHANGED
@@ -8,37 +8,41 @@ import { getFrontendDirectoryConfigurations } from './questions/frontendDirector
8
8
  import { getFrontends } from './questions/frontends';
9
9
  import { getGithubLink } from './questions/githubLink';
10
10
  import { getHtmlScriptingOption } from './questions/htmlScriptingOption';
11
+ import { getIncludeExamples } from './questions/includeExamples';
11
12
  import { getInitializeGit } from './questions/initializeGitNow';
12
13
  import { getInstallDependencies } from './questions/installDependenciesNow';
13
14
  import { getORM } from './questions/orm';
14
15
  import { getPlugins } from './questions/plugins';
15
16
  import { getProjectName } from './questions/projectName';
16
17
  import { getUseTailwind } from './questions/useTailwind';
18
+ import { orPrompt } from './utils/interactive';
17
19
  export const prompt = async (argumentConfiguration) => {
18
20
  // 1. Project name
19
- const projectName = argumentConfiguration.projectName ?? (await getProjectName());
21
+ const projectName = argumentConfiguration.projectName ?? (await orPrompt('a project name', getProjectName));
20
22
  // 2. Linting/formatting tool
21
- const codeQualityTool = argumentConfiguration.codeQualityTool ?? (await getCodeQualityTool());
23
+ const codeQualityTool = argumentConfiguration.codeQualityTool ?? (await orPrompt('--eslint+prettier/--biome', getCodeQualityTool));
22
24
  // 3. Tailwind support?
23
- const useTailwind = argumentConfiguration.useTailwind ?? (await getUseTailwind());
25
+ const useTailwind = argumentConfiguration.useTailwind ?? (await orPrompt('--tailwind/--no-tailwind', getUseTailwind));
24
26
  // 4. Frontend(s)
25
- const frontends = argumentConfiguration.frontends?.filter((frontend) => frontend !== undefined) ?? (await getFrontends());
27
+ const frontends = argumentConfiguration.frontends?.filter((frontend) => frontend !== undefined) ?? (await orPrompt('--react/--vue/--svelte/…', getFrontends));
26
28
  // 5. HTML scripting option (if HTML was selected)
27
29
  const useHTMLScripts = frontends.includes('html')
28
30
  ? (argumentConfiguration.useHTMLScripts ??
29
- (await getHtmlScriptingOption()))
31
+ (await orPrompt('--html-scripts/--no-html-scripts', getHtmlScriptingOption)))
30
32
  : false;
33
+ // 5b. Include example pages/components, or generate a bare skeleton
34
+ const includeExamples = argumentConfiguration.includeExamples ?? (await orPrompt('--examples/--no-examples', getIncludeExamples));
31
35
  // 6. Database engine
32
- const databaseEngine = argumentConfiguration.databaseEngine ?? (await getDatabaseEngine());
36
+ const databaseEngine = argumentConfiguration.databaseEngine ?? (await orPrompt('--db', getDatabaseEngine));
33
37
  // 7. Database host
34
38
  const databaseHost = argumentConfiguration.databaseHost ??
35
- (await getDatabaseHost(databaseEngine));
39
+ (await orPrompt('--db-host', () => getDatabaseHost(databaseEngine)));
36
40
  // 8. ORM choice
37
41
  const orm = databaseEngine !== undefined && databaseEngine !== 'none'
38
- ? (argumentConfiguration.orm ?? (await getORM(databaseEngine)))
42
+ ? (argumentConfiguration.orm ?? (await orPrompt('--orm', () => getORM(databaseEngine))))
39
43
  : undefined;
40
44
  // 9. Configuration type
41
- let directoryConfig = argumentConfiguration.directoryConfig ?? (await getConfigurationType());
45
+ let directoryConfig = argumentConfiguration.directoryConfig ?? (await orPrompt('--directory', getConfigurationType));
42
46
  // 10. Directory configurations
43
47
  const { buildDirectory, assetsDirectory, tailwind, databaseDirectory } = await getDirectoryConfiguration({
44
48
  argumentConfiguration,
@@ -52,11 +56,11 @@ export const prompt = async (argumentConfiguration) => {
52
56
  if (argumentConfiguration.frontendDirectories !== undefined)
53
57
  directoryConfig = 'custom';
54
58
  // 12. Auth provider
55
- const authOption = argumentConfiguration.authOption ?? (await getAuthOption());
59
+ const authOption = argumentConfiguration.authOption ?? (await orPrompt('--auth', getAuthOption));
56
60
  // 13. Additional plugins
57
- const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await getPlugins());
61
+ const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await orPrompt('--plugin', getPlugins));
58
62
  // 14. Initialize Git repository
59
- const initializeGitNow = argumentConfiguration.initializeGitNow ?? (await getInitializeGit());
63
+ const initializeGitNow = argumentConfiguration.initializeGitNow ?? (await orPrompt('--git/--no-git', getInitializeGit));
60
64
  // 14b. Optionally connect the new project to GitHub
61
65
  const resolveGithubLink = async () => {
62
66
  if (!initializeGitNow) {
@@ -73,12 +77,12 @@ export const prompt = async (argumentConfiguration) => {
73
77
  githubVisibility: argumentConfiguration.githubVisibility
74
78
  };
75
79
  }
76
- return getGithubLink(projectName);
80
+ return orPrompt('--repo/--repo-visibility', () => getGithubLink(projectName));
77
81
  };
78
82
  const { githubLink, githubRepoUrl, githubVisibility } = await resolveGithubLink();
79
83
  // 15. Install dependencies
80
84
  const installDependenciesNow = argumentConfiguration.installDependenciesNow ??
81
- (await getInstallDependencies());
85
+ (await orPrompt('--install/--no-install', getInstallDependencies));
82
86
  const values = {
83
87
  absProviders: argumentConfiguration.absProviders?.filter((provider) => provider !== undefined),
84
88
  assetsDirectory,
@@ -94,6 +98,7 @@ export const prompt = async (argumentConfiguration) => {
94
98
  githubLink,
95
99
  githubRepoUrl,
96
100
  githubVisibility,
101
+ includeExamples,
97
102
  initializeGitNow,
98
103
  installDependenciesNow,
99
104
  orm,
@@ -1,5 +1,7 @@
1
1
  import { text, isCancel } from '@clack/prompts';
2
2
  import { abort } from '../utils/abort';
3
+ import { orPrompt } from '../utils/interactive';
4
+ const orPromptText = (options) => orPrompt(`a value for "${String(options.message)}" (pass the matching --*-dir flag)`, () => text(options));
3
5
  export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind, databaseEngine, argumentConfiguration }) => {
4
6
  if (directoryConfig === 'default') {
5
7
  return {
@@ -20,7 +22,7 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
20
22
  }
21
23
  // Build directory
22
24
  const buildDirectory = argumentConfiguration.buildDirectory ??
23
- (await text({
25
+ (await orPromptText({
24
26
  message: 'Build directory:',
25
27
  placeholder: 'build'
26
28
  }));
@@ -28,7 +30,7 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
28
30
  abort();
29
31
  // Assets directory
30
32
  const assetsDirectory = argumentConfiguration.assetsDirectory ??
31
- (await text({
33
+ (await orPromptText({
32
34
  message: 'Assets directory:',
33
35
  placeholder: 'src/backend/assets'
34
36
  }));
@@ -38,14 +40,14 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
38
40
  let tailwind;
39
41
  if (useTailwind) {
40
42
  const input = argumentConfiguration.tailwind?.input ??
41
- (await text({
43
+ (await orPromptText({
42
44
  message: 'Tailwind input CSS file:',
43
45
  placeholder: './src/frontend/styles/tailwind.css'
44
46
  }));
45
47
  if (isCancel(input))
46
48
  abort();
47
49
  const output = argumentConfiguration.tailwind?.output ??
48
- (await text({
50
+ (await orPromptText({
49
51
  message: 'Tailwind output CSS file:',
50
52
  placeholder: 'assets/css/tailwind.generated.css'
51
53
  }));
@@ -61,7 +63,7 @@ export const getDirectoryConfiguration = async ({ directoryConfig, useTailwind,
61
63
  if (databaseEngine !== undefined && databaseEngine !== 'none') {
62
64
  databaseDirectory =
63
65
  argumentConfiguration.databaseDirectory ??
64
- (await text({
66
+ (await orPromptText({
65
67
  message: 'Database directory:',
66
68
  placeholder: 'db'
67
69
  }));
@@ -1,13 +1,14 @@
1
1
  import { text, isCancel } from '@clack/prompts';
2
2
  import { frontendLabels } from '../data';
3
3
  import { abort } from '../utils/abort';
4
+ import { orPrompt } from '../utils/interactive';
4
5
  const getDirectoryForFrontend = async (directoryConfiguration, frontend, isSingleFrontend) => {
5
6
  if (directoryConfiguration !== 'custom')
6
7
  return isSingleFrontend ? '' : frontend;
7
- const response = await text({
8
+ const response = await orPrompt(`the ${frontendLabels[frontend]} directory (pass --<frontend>-dir)`, () => text({
8
9
  message: `${frontendLabels[frontend]} directory:`,
9
10
  placeholder: isSingleFrontend ? '' : frontend
10
- });
11
+ }));
11
12
  if (isCancel(response))
12
13
  abort();
13
14
  return response;
@@ -0,0 +1 @@
1
+ export declare const getIncludeExamples: () => Promise<boolean>;
@@ -0,0 +1,11 @@
1
+ import { confirm, isCancel } from '@clack/prompts';
2
+ import { abort } from '../utils/abort';
3
+ export const getIncludeExamples = async () => {
4
+ const includeExamples = await confirm({
5
+ initialValue: true,
6
+ message: 'Include example pages and components?'
7
+ });
8
+ if (isCancel(includeExamples))
9
+ abort();
10
+ return includeExamples;
11
+ };
@@ -5,7 +5,7 @@ type ScaffoldProps = {
5
5
  latest: boolean;
6
6
  envVariables: string[] | undefined;
7
7
  };
8
- export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<{
8
+ export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<{
9
9
  dockerFreshInstall: boolean;
10
10
  }>;
11
11
  export {};
package/dist/scaffold.js CHANGED
@@ -10,7 +10,7 @@ import { scaffoldConfigurationFiles } from './generators/configurations/scaffold
10
10
  import { scaffoldDatabase } from './generators/db/scaffoldDatabase';
11
11
  import { scaffoldBackend } from './generators/project/scaffoldBackend';
12
12
  import { scaffoldFrontends } from './generators/project/scaffoldFrontends';
13
- export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }) => {
13
+ export const scaffold = async ({ response: { projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, 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);
@@ -76,6 +76,7 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
76
76
  frontendDirectories,
77
77
  frontendDirectory,
78
78
  frontends,
79
+ includeExamples,
79
80
  projectAssetsDirectory,
80
81
  templatesDirectory,
81
82
  typesDirectory,
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export type ScaffoldFrontendProps = {
5
5
  assetsDirectory: string;
6
6
  authOption: AuthOption;
7
7
  editBasePath: string;
8
+ includeExamples: boolean;
8
9
  targetDirectory: string;
9
10
  templatesDirectory: string;
10
11
  projectAssetsDirectory: string;
@@ -50,6 +51,7 @@ export type CreateConfiguration = {
50
51
  databaseEngine: DatabaseEngine;
51
52
  frontendDirectories: FrontendDirectories;
52
53
  frontends: Frontend[];
54
+ includeExamples: boolean;
53
55
  useHTMLScripts: boolean;
54
56
  initializeGitNow: boolean;
55
57
  githubLink: GithubLinkOption;
@@ -1,5 +1,5 @@
1
1
  import os from 'os';
2
- import { env, platform } from 'process';
2
+ import { env, platform, stdin, stdout } from 'process';
3
3
  import { confirm, spinner } from '@clack/prompts';
4
4
  import { $ } from 'bun';
5
5
  import { dim, yellow } from 'picocolors';
@@ -109,9 +109,17 @@ const installLinuxSqlite = async () => {
109
109
  export const checkSqliteInstalled = async () => {
110
110
  if (await hasSqlite())
111
111
  return;
112
+ // Non-interactive contexts (the Studio container, CI, any piped stdin)
113
+ // must never block on a prompt — and sudo-based installs can't work
114
+ // there anyway. The CLI is optional (scaffolding seeds via bun:sqlite);
115
+ // mention it and move on.
116
+ if (!stdin.isTTY || !stdout.isTTY || env.CI) {
117
+ console.log(`${dim('│')}\n${yellow('▲')} sqlite3 CLI not found — optional; your database still works. Install it later for a db shell: ${SQLITE_URL}`);
118
+ return;
119
+ }
112
120
  const proceed = await confirm({
113
121
  initialValue: true,
114
- message: 'sqlite3 CLI is required for local SQLite databases. Install now?'
122
+ message: 'sqlite3 CLI is optional (handy as a db shell). Install now?'
115
123
  });
116
124
  if (!proceed)
117
125
  return;
@@ -0,0 +1,6 @@
1
+ /** True only when a human can actually answer a clack prompt. */
2
+ export declare const interactive: boolean;
3
+ /** Headless safety net: without a TTY, a clack prompt blocks forever (the
4
+ * Studio container spawns this CLI with stdin ignored). Any axis that would
5
+ * prompt in that state is a missing flag/default — fail loudly instead. */
6
+ export declare const orPrompt: <T>(flagHint: string, ask: () => Promise<T>) => Promise<T>;
@@ -0,0 +1,13 @@
1
+ import { exit, stdin, stdout } from 'process';
2
+ /** True only when a human can actually answer a clack prompt. */
3
+ export const interactive = stdin.isTTY === true && stdout.isTTY === true;
4
+ /** Headless safety net: without a TTY, a clack prompt blocks forever (the
5
+ * Studio container spawns this CLI with stdin ignored). Any axis that would
6
+ * prompt in that state is a missing flag/default — fail loudly instead. */
7
+ export const orPrompt = (flagHint, ask) => {
8
+ if (!interactive) {
9
+ console.error(`create-absolutejs: cannot prompt for ${flagHint} without a TTY. Pass the flag explicitly (or use --skip for defaults).`);
10
+ exit(1);
11
+ }
12
+ return ask();
13
+ };
@@ -25,6 +25,7 @@ export const parseCommandLineOptions = () => {
25
25
  directory: { type: 'string' },
26
26
  env: { multiple: true, type: 'string' },
27
27
  'eslint+prettier': { type: 'boolean' },
28
+ examples: { type: 'boolean' },
28
29
  git: { type: 'boolean' },
29
30
  github: { type: 'string' },
30
31
  help: { default: false, short: 'h', type: 'boolean' },
@@ -118,12 +119,19 @@ export const parseCommandLineOptions = () => {
118
119
  else if (useBiome) {
119
120
  codeQualityTool = 'biome';
120
121
  }
122
+ else if (values.skip) {
123
+ // --skip must default EVERY prompted axis — a missing default here
124
+ // hangs headless callers (the Studio container) on a clack prompt.
125
+ codeQualityTool = 'eslint+prettier';
126
+ }
121
127
  else {
122
128
  codeQualityTool = undefined;
123
129
  }
124
130
  const directoryConfig = values.directory !== undefined && isDirectoryConfig(values.directory)
125
131
  ? values.directory
126
- : undefined;
132
+ : // --skip defaults every prompted axis (headless callers hang on
133
+ // missing ones).
134
+ (values.skip ? 'default' : undefined);
127
135
  if (values.directory !== undefined && directoryConfig === undefined) {
128
136
  errors.push(`Invalid directory configuration: "${values.directory}". Expected: [ ${availableDirectoryConfigurations.join(', ')} ]`);
129
137
  }
@@ -236,6 +244,7 @@ export const parseCommandLineOptions = () => {
236
244
  validEnv.push(entry);
237
245
  }
238
246
  values.env = validEnv.length ? validEnv : undefined;
247
+ const includeExamples = values.examples ?? (values.skip ? true : undefined);
239
248
  const repoVisibility = values['repo-visibility'];
240
249
  const githubVisibility = repoVisibility === 'public' || repoVisibility === 'private'
241
250
  ? repoVisibility
@@ -257,6 +266,7 @@ export const parseCommandLineOptions = () => {
257
266
  : undefined,
258
267
  githubRepoUrl: normalizeRepoInput(values.repo)?.httpsUrl,
259
268
  githubVisibility,
269
+ includeExamples,
260
270
  initializeGitNow: values.git,
261
271
  installDependenciesNow: values.install,
262
272
  orm,
package/package.json CHANGED
@@ -52,5 +52,5 @@
52
52
  "typecheck": "bun run tsc --noEmit"
53
53
  },
54
54
  "type": "module",
55
- "version": "0.13.7"
55
+ "version": "0.13.9"
56
56
  }