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.
- package/dist/generators/configurations/generateEslintConfig.js +8 -0
- package/dist/generators/db/scaffoldDatabase.js +8 -2
- package/dist/prompt.js +16 -15
- package/dist/questions/directoryConfiguration.js +7 -5
- package/dist/questions/frontendDirectoryConfigurations.js +3 -2
- package/dist/utils/checkSqliteInstalled.js +10 -2
- package/dist/utils/interactive.d.ts +6 -0
- package/dist/utils/interactive.js +13 -0
- package/dist/utils/parseCommandLineOptions.js +8 -1
- package/dist/versions.d.ts +1 -1
- package/dist/versions.js +1 -1
- package/package.json +2 -2
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
:
|
|
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
|
}
|
package/dist/versions.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
55
|
+
"version": "0.13.10"
|
|
56
56
|
}
|