anhedral 0.1.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.
- package/LICENSE +22 -0
- package/README.md +65 -0
- package/anhedral.svg +4 -0
- package/bin/anhedral.js +2 -0
- package/dist/branding.d.ts +3 -0
- package/dist/branding.js +9 -0
- package/dist/commands.d.ts +12 -0
- package/dist/commands.js +74 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +157 -0
- package/dist/scaffold.d.ts +25 -0
- package/dist/scaffold.js +629 -0
- package/dist/templates/backend.d.ts +2 -0
- package/dist/templates/backend.js +2337 -0
- package/dist/templates/default-web.d.ts +2 -0
- package/dist/templates/default-web.js +1368 -0
- package/dist/templates/extension.d.ts +2 -0
- package/dist/templates/extension.js +457 -0
- package/dist/templates/frontend.d.ts +2 -0
- package/dist/templates/frontend.js +1605 -0
- package/dist/toolchain.d.ts +14 -0
- package/dist/toolchain.js +34 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.js +99 -0
- package/package.json +60 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const TOOLCHAIN_CHANNELS: readonly ["latest", "stable"];
|
|
2
|
+
export type ToolchainChannel = (typeof TOOLCHAIN_CHANNELS)[number];
|
|
3
|
+
export type ToolchainSpec = {
|
|
4
|
+
verifiedAt: string | null;
|
|
5
|
+
viteCreate: string;
|
|
6
|
+
shadcn: string;
|
|
7
|
+
reactNativeReusables: string;
|
|
8
|
+
wxt: string;
|
|
9
|
+
tauriCli: string;
|
|
10
|
+
tauriApi: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function resolveToolchainChannel(value: string | undefined): ToolchainChannel;
|
|
13
|
+
export declare function resolveToolchain(channel: ToolchainChannel): ToolchainSpec;
|
|
14
|
+
export declare function toolPackageRef(pkg: string, versionTag: string): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const TOOLCHAIN_CHANNELS = ['latest', 'stable'];
|
|
2
|
+
const TOOLCHAIN_SPECS = {
|
|
3
|
+
latest: {
|
|
4
|
+
verifiedAt: null,
|
|
5
|
+
viteCreate: 'latest',
|
|
6
|
+
shadcn: 'latest',
|
|
7
|
+
reactNativeReusables: 'latest',
|
|
8
|
+
wxt: 'latest',
|
|
9
|
+
tauriCli: 'latest',
|
|
10
|
+
tauriApi: 'latest',
|
|
11
|
+
},
|
|
12
|
+
stable: {
|
|
13
|
+
verifiedAt: '2026-03-22',
|
|
14
|
+
viteCreate: '7.1.4',
|
|
15
|
+
shadcn: '4.1.0',
|
|
16
|
+
reactNativeReusables: '0.5.0',
|
|
17
|
+
wxt: '0.20.20',
|
|
18
|
+
tauriCli: '2.8.4',
|
|
19
|
+
tauriApi: '2.8.0',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export function resolveToolchainChannel(value) {
|
|
23
|
+
if (!value)
|
|
24
|
+
return 'stable';
|
|
25
|
+
if (value === 'latest' || value === 'stable')
|
|
26
|
+
return value;
|
|
27
|
+
throw new Error(`--toolchain must be one of: ${TOOLCHAIN_CHANNELS.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
export function resolveToolchain(channel) {
|
|
30
|
+
return TOOLCHAIN_SPECS[channel];
|
|
31
|
+
}
|
|
32
|
+
export function toolPackageRef(pkg, versionTag) {
|
|
33
|
+
return `${pkg}@${versionTag}`;
|
|
34
|
+
}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function writeFile(filePath: string, content: string): void;
|
|
2
|
+
export declare function appendGitignore(root: string, lines: string[]): void;
|
|
3
|
+
export declare function exec(cmd: string, cwd: string): void;
|
|
4
|
+
export declare function execWithInput(cmd: string, cwd: string, stdinInput: string): void;
|
|
5
|
+
export declare function liftNestedProject(root: string, nestedName: string): void;
|
|
6
|
+
export declare function execExpect(cmd: string, cwd: string, prompts: [waitFor: string, answer: string][]): void;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function writeFile(filePath, content) {
|
|
5
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
6
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
7
|
+
}
|
|
8
|
+
export function appendGitignore(root, lines) {
|
|
9
|
+
const filePath = path.join(root, '.gitignore');
|
|
10
|
+
const existing = existsSync(filePath)
|
|
11
|
+
? readFileSync(filePath, 'utf-8').split('\n').map((line) => line.trim()).filter(Boolean)
|
|
12
|
+
: [];
|
|
13
|
+
const merged = [...new Set([...existing, ...lines])];
|
|
14
|
+
writeFile(filePath, merged.join('\n') + '\n');
|
|
15
|
+
}
|
|
16
|
+
export function exec(cmd, cwd) {
|
|
17
|
+
console.log(` $ ${cmd}`);
|
|
18
|
+
execSync(cmd, { cwd, stdio: 'inherit' });
|
|
19
|
+
}
|
|
20
|
+
export function execWithInput(cmd, cwd, stdinInput) {
|
|
21
|
+
console.log(` $ ${cmd}`);
|
|
22
|
+
execSync(cmd, { cwd, stdio: ['pipe', 'inherit', 'inherit'], input: stdinInput });
|
|
23
|
+
}
|
|
24
|
+
function moveDirectoryContents(sourceDir, targetDir) {
|
|
25
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
26
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
27
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
28
|
+
if (existsSync(targetPath)) {
|
|
29
|
+
if (!entry.isDirectory() || !statSync(targetPath).isDirectory()) {
|
|
30
|
+
throw new Error(`Cannot merge scaffold output because ${targetPath} already exists.`);
|
|
31
|
+
}
|
|
32
|
+
moveDirectoryContents(sourcePath, targetPath);
|
|
33
|
+
rmSync(sourcePath, { recursive: true, force: true });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
renameSync(sourcePath, targetPath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function liftNestedProject(root, nestedName) {
|
|
40
|
+
const nestedRoot = path.join(root, nestedName);
|
|
41
|
+
if (!existsSync(nestedRoot)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const selfNamedChild = path.join(nestedRoot, nestedName);
|
|
45
|
+
const tempSelfNamedChild = path.join(root, `.${nestedName}.anhedral-tmp`);
|
|
46
|
+
const hasSelfNamedChild = existsSync(selfNamedChild);
|
|
47
|
+
if (hasSelfNamedChild) {
|
|
48
|
+
if (existsSync(tempSelfNamedChild)) {
|
|
49
|
+
throw new Error(`Cannot lift scaffold output because ${tempSelfNamedChild} already exists.`);
|
|
50
|
+
}
|
|
51
|
+
renameSync(selfNamedChild, tempSelfNamedChild);
|
|
52
|
+
}
|
|
53
|
+
moveDirectoryContents(nestedRoot, root);
|
|
54
|
+
rmSync(nestedRoot, { recursive: true, force: true });
|
|
55
|
+
if (hasSelfNamedChild) {
|
|
56
|
+
renameSync(tempSelfNamedChild, path.join(root, nestedName));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function bashSingleQuoteEscape(value) {
|
|
60
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
61
|
+
}
|
|
62
|
+
function tclDoubleQuoteEscape(value) {
|
|
63
|
+
return value
|
|
64
|
+
.replace(/\\/g, '\\\\')
|
|
65
|
+
.replace(/"/g, '\\"')
|
|
66
|
+
.replace(/\[/g, '\\[')
|
|
67
|
+
.replace(/\]/g, '\\]')
|
|
68
|
+
.replace(/\$/g, '\\$')
|
|
69
|
+
.replace(/\r/g, '\\r')
|
|
70
|
+
.replace(/\n/g, '\\n');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Escape a string for use inside a Tcl double-quoted `send` argument.
|
|
74
|
+
* Converts JS escape sequences to their Tcl equivalents.
|
|
75
|
+
*/
|
|
76
|
+
function tclSendEscape(s) {
|
|
77
|
+
return tclDoubleQuoteEscape(s).replace(/\x1b/g, '\\033');
|
|
78
|
+
}
|
|
79
|
+
export function execExpect(cmd, cwd, prompts) {
|
|
80
|
+
console.log(` $ ${cmd}`);
|
|
81
|
+
const shellCommand = `cd ${bashSingleQuoteEscape(cwd)} && ${cmd}`;
|
|
82
|
+
const sendLines = prompts
|
|
83
|
+
.map(([wait, answer]) => `expect "${wait}"\nsend "${tclSendEscape(answer)}\\r"`)
|
|
84
|
+
.join('\n');
|
|
85
|
+
const expectScript = [
|
|
86
|
+
`set timeout 120`,
|
|
87
|
+
`set shell_command "${tclDoubleQuoteEscape(shellCommand)}"`,
|
|
88
|
+
`spawn bash -lc $shell_command`,
|
|
89
|
+
sendLines,
|
|
90
|
+
`expect eof`,
|
|
91
|
+
`catch wait result`,
|
|
92
|
+
`exit [lindex $result 3]`,
|
|
93
|
+
].join('\n');
|
|
94
|
+
execSync(`expect << 'EXPECT_EOF'\n${expectScript}\nEXPECT_EOF`, {
|
|
95
|
+
cwd,
|
|
96
|
+
stdio: 'inherit',
|
|
97
|
+
shell: '/bin/bash',
|
|
98
|
+
});
|
|
99
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "anhedral",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Opinionated init CLI for Next, Next + Fastify, Expo + Fastify, and backend starters with Neon, Drizzle, R2, auth, and billing integrations.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"anhedral": "./bin/anhedral.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"dist",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md",
|
|
14
|
+
"anhedral.svg"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/anhedral/anhedral-init.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/anhedral/anhedral-init#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/anhedral/anhedral-init/issues"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20.0.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"scaffold": "node dist/index.js init",
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"prepare": "pnpm build",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "pnpm test:cli",
|
|
33
|
+
"pretest": "pnpm build",
|
|
34
|
+
"prepublishOnly": "pnpm test:all",
|
|
35
|
+
"test:cli": "node scripts/test-cli.js",
|
|
36
|
+
"test:cli:validation": "node scripts/test-cli-validation.js",
|
|
37
|
+
"test:cli:scaffold": "node scripts/test-cli-scaffold-headless.js",
|
|
38
|
+
"test:e2e": "node scripts/test-e2e-public-stacks.js",
|
|
39
|
+
"test:all": "pnpm test && pnpm test:e2e"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"cli",
|
|
43
|
+
"scaffold",
|
|
44
|
+
"fastify",
|
|
45
|
+
"expo",
|
|
46
|
+
"clerk",
|
|
47
|
+
"drizzle",
|
|
48
|
+
"neondb",
|
|
49
|
+
"revenuecat",
|
|
50
|
+
"better-auth",
|
|
51
|
+
"shadcn"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"packageManager": "pnpm@10.15.1",
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^24.5.2",
|
|
57
|
+
"tsx": "^4.20.5",
|
|
58
|
+
"typescript": "^5.9.2"
|
|
59
|
+
}
|
|
60
|
+
}
|