create-absolutejs 0.13.8 → 0.13.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -95,7 +95,9 @@ export default defineConfig([
95
95
  security: fixupPluginRules(securityPlugin)
96
96
  },
97
97
  rules: {
98
+ 'absolute/button-icon-is-hidden': 'error',
98
99
  'absolute/explicit-object-types': 'error',
100
+ 'absolute/icon-button-has-accessible-name': 'error',
99
101
  'absolute/localize-react-props': 'error',
100
102
  'absolute/max-depth-extended': ['error', 1],
101
103
  'absolute/max-jsxnesting': ['error', 5],
@@ -214,6 +216,12 @@ export default defineConfig([
214
216
  'promise/param-names': 'error'
215
217
  }
216
218
  },
219
+ {
220
+ files: ['**/*.{html,vue,svelte,gjs,gts}'],
221
+ ignores: ['example/build/**'],
222
+ plugins: { absolute: fixupPluginRules(absolutePlugin) },
223
+ processor: 'absolute/template-source'
224
+ },
217
225
  ${reactBlock} {
218
226
  files: [
219
227
  'example/server.ts',
@@ -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' &&
package/dist/prompt.js CHANGED
@@ -15,33 +15,34 @@ import { getORM } from './questions/orm';
15
15
  import { getPlugins } from './questions/plugins';
16
16
  import { getProjectName } from './questions/projectName';
17
17
  import { getUseTailwind } from './questions/useTailwind';
18
+ import { orPrompt } from './utils/interactive';
18
19
  export const prompt = async (argumentConfiguration) => {
19
20
  // 1. Project name
20
- const projectName = argumentConfiguration.projectName ?? (await getProjectName());
21
+ const projectName = argumentConfiguration.projectName ?? (await orPrompt('a project name', getProjectName));
21
22
  // 2. Linting/formatting tool
22
- const codeQualityTool = argumentConfiguration.codeQualityTool ?? (await getCodeQualityTool());
23
+ const codeQualityTool = argumentConfiguration.codeQualityTool ?? (await orPrompt('--eslint+prettier/--biome', getCodeQualityTool));
23
24
  // 3. Tailwind support?
24
- const useTailwind = argumentConfiguration.useTailwind ?? (await getUseTailwind());
25
+ const useTailwind = argumentConfiguration.useTailwind ?? (await orPrompt('--tailwind/--no-tailwind', getUseTailwind));
25
26
  // 4. Frontend(s)
26
- 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));
27
28
  // 5. HTML scripting option (if HTML was selected)
28
29
  const useHTMLScripts = frontends.includes('html')
29
30
  ? (argumentConfiguration.useHTMLScripts ??
30
- (await getHtmlScriptingOption()))
31
+ (await orPrompt('--html-scripts/--no-html-scripts', getHtmlScriptingOption)))
31
32
  : false;
32
33
  // 5b. Include example pages/components, or generate a bare skeleton
33
- const includeExamples = argumentConfiguration.includeExamples ?? (await getIncludeExamples());
34
+ const includeExamples = argumentConfiguration.includeExamples ?? (await orPrompt('--examples/--no-examples', getIncludeExamples));
34
35
  // 6. Database engine
35
- const databaseEngine = argumentConfiguration.databaseEngine ?? (await getDatabaseEngine());
36
+ const databaseEngine = argumentConfiguration.databaseEngine ?? (await orPrompt('--db', getDatabaseEngine));
36
37
  // 7. Database host
37
38
  const databaseHost = argumentConfiguration.databaseHost ??
38
- (await getDatabaseHost(databaseEngine));
39
+ (await orPrompt('--db-host', () => getDatabaseHost(databaseEngine)));
39
40
  // 8. ORM choice
40
41
  const orm = databaseEngine !== undefined && databaseEngine !== 'none'
41
- ? (argumentConfiguration.orm ?? (await getORM(databaseEngine)))
42
+ ? (argumentConfiguration.orm ?? (await orPrompt('--orm', () => getORM(databaseEngine))))
42
43
  : undefined;
43
44
  // 9. Configuration type
44
- let directoryConfig = argumentConfiguration.directoryConfig ?? (await getConfigurationType());
45
+ let directoryConfig = argumentConfiguration.directoryConfig ?? (await orPrompt('--directory', getConfigurationType));
45
46
  // 10. Directory configurations
46
47
  const { buildDirectory, assetsDirectory, tailwind, databaseDirectory } = await getDirectoryConfiguration({
47
48
  argumentConfiguration,
@@ -55,11 +56,11 @@ export const prompt = async (argumentConfiguration) => {
55
56
  if (argumentConfiguration.frontendDirectories !== undefined)
56
57
  directoryConfig = 'custom';
57
58
  // 12. Auth provider
58
- const authOption = argumentConfiguration.authOption ?? (await getAuthOption());
59
+ const authOption = argumentConfiguration.authOption ?? (await orPrompt('--auth', getAuthOption));
59
60
  // 13. Additional plugins
60
- const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await getPlugins());
61
+ const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await orPrompt('--plugin', getPlugins));
61
62
  // 14. Initialize Git repository
62
- const initializeGitNow = argumentConfiguration.initializeGitNow ?? (await getInitializeGit());
63
+ const initializeGitNow = argumentConfiguration.initializeGitNow ?? (await orPrompt('--git/--no-git', getInitializeGit));
63
64
  // 14b. Optionally connect the new project to GitHub
64
65
  const resolveGithubLink = async () => {
65
66
  if (!initializeGitNow) {
@@ -76,12 +77,12 @@ export const prompt = async (argumentConfiguration) => {
76
77
  githubVisibility: argumentConfiguration.githubVisibility
77
78
  };
78
79
  }
79
- return getGithubLink(projectName);
80
+ return orPrompt('--repo/--repo-visibility', () => getGithubLink(projectName));
80
81
  };
81
82
  const { githubLink, githubRepoUrl, githubVisibility } = await resolveGithubLink();
82
83
  // 15. Install dependencies
83
84
  const installDependenciesNow = argumentConfiguration.installDependenciesNow ??
84
- (await getInstallDependencies());
85
+ (await orPrompt('--install/--no-install', getInstallDependencies));
85
86
  const values = {
86
87
  absProviders: argumentConfiguration.absProviders?.filter((provider) => provider !== undefined),
87
88
  assetsDirectory,
@@ -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;
@@ -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
+ };
@@ -119,12 +119,19 @@ export const parseCommandLineOptions = () => {
119
119
  else if (useBiome) {
120
120
  codeQualityTool = 'biome';
121
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
+ }
122
127
  else {
123
128
  codeQualityTool = undefined;
124
129
  }
125
130
  const directoryConfig = values.directory !== undefined && isDirectoryConfig(values.directory)
126
131
  ? values.directory
127
- : undefined;
132
+ : // --skip defaults every prompted axis (headless callers hang on
133
+ // missing ones).
134
+ (values.skip ? 'default' : undefined);
128
135
  if (values.directory !== undefined && directoryConfig === undefined) {
129
136
  errors.push(`Invalid directory configuration: "${values.directory}". Expected: [ ${availableDirectoryConfigurations.join(', ')} ]`);
130
137
  }
@@ -34,7 +34,7 @@ export declare const versions: {
34
34
  readonly 'elysia-rate-limit': "4.5.0";
35
35
  readonly 'elysia-scoped-state': "0.1.1";
36
36
  readonly eslint: "10.0.0";
37
- readonly 'eslint-plugin-absolute': "0.2.0";
37
+ readonly 'eslint-plugin-absolute': "0.11.12";
38
38
  readonly 'eslint-plugin-import': "2.32.0";
39
39
  readonly 'eslint-plugin-jsx-a11y': "6.10.2";
40
40
  readonly 'eslint-plugin-promise': "7.2.1";
package/dist/versions.js CHANGED
@@ -43,7 +43,7 @@ export const versions = {
43
43
  'elysia-rate-limit': '4.5.0',
44
44
  'elysia-scoped-state': '0.1.1',
45
45
  eslint: '10.0.0',
46
- 'eslint-plugin-absolute': '0.2.0',
46
+ 'eslint-plugin-absolute': '0.11.12',
47
47
  'eslint-plugin-import': '2.32.0',
48
48
  /* ── ESLint React ─────────────────────────────────────── */
49
49
  'eslint-plugin-jsx-a11y': '6.10.2',
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "drizzle-kit": "0.31.8",
20
20
  "drizzle-orm": "0.45.1",
21
21
  "eslint": "10.0.1",
22
- "eslint-plugin-absolute": "0.2.1",
22
+ "eslint-plugin-absolute": "0.11.12",
23
23
  "eslint-plugin-import": "2.32.0",
24
24
  "eslint-plugin-promise": "7.2.1",
25
25
  "eslint-plugin-security": "4.0.0",
@@ -52,5 +52,5 @@
52
52
  "typecheck": "bun run tsc --noEmit"
53
53
  },
54
54
  "type": "module",
55
- "version": "0.13.8"
55
+ "version": "0.13.10"
56
56
  }