create-lafkn 1.0.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/README.md +85 -0
- package/dist/index.js +176 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# π Lafkn
|
|
2
|
+
|
|
3
|
+
A modern serverless framework scaffolding tool for AWS CDK projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create lafkn@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx create-lafkn
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Simply run the command and follow the interactive prompts:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm create lafkn
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The CLI will guide you through:
|
|
26
|
+
|
|
27
|
+
1. **Project name**: Choose a name for your project
|
|
28
|
+
2. **Directory selection**: If your current directory matches the project name, you'll be asked if you want to use it
|
|
29
|
+
3. **Service selection**: Choose which AWS services to include in your project
|
|
30
|
+
4. **Dependency installation**: Optionally install dependencies with your preferred package manager
|
|
31
|
+
|
|
32
|
+
## Available Services
|
|
33
|
+
|
|
34
|
+
Lafkn supports the following AWS services out of the box:
|
|
35
|
+
|
|
36
|
+
- **API Gateway** - RESTful API endpoints
|
|
37
|
+
- **Authentication** - User authentication and authorization
|
|
38
|
+
- **S3 Bucket** - Object storage
|
|
39
|
+
- **DynamoDB** - NoSQL database
|
|
40
|
+
- **EventBridge** - Event bus for event-driven architecture
|
|
41
|
+
- **SQS Queue** - Message queuing service
|
|
42
|
+
- **EventBridge Schedule** - Scheduled task execution
|
|
43
|
+
- **Step Functions** - State machine workflows
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
After scaffolding, your project will have the following structure:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
my-lafkn-app/
|
|
51
|
+
βββ src/
|
|
52
|
+
β βββ index.ts
|
|
53
|
+
βββ package.json
|
|
54
|
+
βββ tsconfig.json
|
|
55
|
+
βββ cdktf.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `package.json` will include only the services you selected during setup.
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
Once your project is created:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cd my-lafkn-app
|
|
66
|
+
npm install # if you didn't install during setup
|
|
67
|
+
npm run build
|
|
68
|
+
npm run cdktf:synth
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Available Scripts
|
|
72
|
+
|
|
73
|
+
- `npm run build` - Compile TypeScript to JavaScript
|
|
74
|
+
- `npm run cdktf:deploy` - Build and deploy your infrastructure
|
|
75
|
+
- `npm run cdktf:destroy` - Destroy your infrastructure
|
|
76
|
+
- `npm run cdktf:synth` - Synthesize CloudFormation templates
|
|
77
|
+
- `npm run clean` - Clean build artifacts
|
|
78
|
+
|
|
79
|
+
## Support
|
|
80
|
+
|
|
81
|
+
For issues and questions, please open an issue on GitHub.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
Built with β€οΈ using [Eta](https://eta.js.org/) templates and [Inquirer](https://github.com/SBoudrias/Inquirer.js)
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import { exec } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { input, checkbox, confirm, select } from "@inquirer/prompts";
|
|
9
|
+
import { Eta } from "eta";
|
|
10
|
+
const execPromise = promisify(exec);
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const TEMPLATE_DIR = path.join(__dirname, "../template");
|
|
14
|
+
const processDirectory = async (src, dest, context) => {
|
|
15
|
+
await fs.mkdir(dest, { recursive: true });
|
|
16
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const srcPath = path.join(src, entry.name);
|
|
19
|
+
const destPath = path.join(dest, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
await processDirectory(srcPath, destPath, context);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (entry.name.endsWith(".eta")) {
|
|
25
|
+
const content = await fs.readFile(srcPath, "utf-8");
|
|
26
|
+
const realDestPath = destPath.replace(/\.eta$/, "");
|
|
27
|
+
const eta = new Eta();
|
|
28
|
+
console.log(context);
|
|
29
|
+
const rendered = eta.renderString(content, context);
|
|
30
|
+
await fs.writeFile(realDestPath, rendered);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
await fs.copyFile(srcPath, destPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const checkDirectoryEmpty = async (dir) => {
|
|
39
|
+
try {
|
|
40
|
+
const files = await fs.readdir(dir);
|
|
41
|
+
return files.length === 0;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const main = async () => {
|
|
48
|
+
try {
|
|
49
|
+
console.log("π Welcome to Lafkn π");
|
|
50
|
+
const appName = await input({
|
|
51
|
+
message: "Project name:",
|
|
52
|
+
default: "my-lafkn-app",
|
|
53
|
+
validate: (value) => {
|
|
54
|
+
if (!value || value.trim().length === 0) {
|
|
55
|
+
return "Project name cannot be empty";
|
|
56
|
+
}
|
|
57
|
+
if (!/^[a-z0-9-_]+$/i.test(value)) {
|
|
58
|
+
return "Project name can only contain letters, numbers, hyphens and underscores";
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const currentDirName = path.basename(process.cwd());
|
|
64
|
+
let useCurrentDir = false;
|
|
65
|
+
if (currentDirName === appName) {
|
|
66
|
+
useCurrentDir = await confirm({
|
|
67
|
+
message: `Current directory is already named "${appName}". Create project here?`,
|
|
68
|
+
default: true,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const targetDir = useCurrentDir
|
|
72
|
+
? process.cwd()
|
|
73
|
+
: path.join(process.cwd(), appName);
|
|
74
|
+
let dirExists = false;
|
|
75
|
+
try {
|
|
76
|
+
await fs.access(targetDir);
|
|
77
|
+
dirExists = true;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
dirExists = false;
|
|
81
|
+
}
|
|
82
|
+
if (!dirExists) {
|
|
83
|
+
console.log(`\nπ Creating directory: ${targetDir}`);
|
|
84
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const isEmpty = await checkDirectoryEmpty(targetDir);
|
|
88
|
+
if (!isEmpty) {
|
|
89
|
+
const overwrite = await confirm({
|
|
90
|
+
message: "β οΈ Directory is not empty. Continue anyway?",
|
|
91
|
+
default: false,
|
|
92
|
+
});
|
|
93
|
+
if (!overwrite) {
|
|
94
|
+
console.log("\nβ Project creation cancelled");
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const services = await checkbox({
|
|
100
|
+
message: "Select services to include:",
|
|
101
|
+
choices: [
|
|
102
|
+
{ name: "API Gateway", value: "api" },
|
|
103
|
+
{ name: "Cognito Authentication", value: "auth" },
|
|
104
|
+
{ name: "S3 Bucket", value: "bucket" },
|
|
105
|
+
{ name: "DynamoDB", value: "dynamo" },
|
|
106
|
+
{ name: "EventBridge Events", value: "event" },
|
|
107
|
+
{ name: "SQS Queue", value: "queue" },
|
|
108
|
+
{ name: "EventBridge Schedule", value: "schedule" },
|
|
109
|
+
{ name: "Step Functions", value: "state-machine" },
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
const shouldInstall = await confirm({
|
|
113
|
+
message: "Install dependencies?",
|
|
114
|
+
default: true,
|
|
115
|
+
});
|
|
116
|
+
let packageManager = "npm";
|
|
117
|
+
if (shouldInstall) {
|
|
118
|
+
packageManager = await select({
|
|
119
|
+
message: "Select package manager:",
|
|
120
|
+
choices: [
|
|
121
|
+
{ name: "npm", value: "npm" },
|
|
122
|
+
{ name: "yarn", value: "yarn" },
|
|
123
|
+
{ name: "pnpm", value: "pnpm" },
|
|
124
|
+
],
|
|
125
|
+
default: "npm",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
console.log(`\nπ Creating project in ${targetDir}...`);
|
|
129
|
+
try {
|
|
130
|
+
await fs.access(TEMPLATE_DIR);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
console.error(`β Template directory not found at ${TEMPLATE_DIR}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const uuid = crypto.randomUUID();
|
|
137
|
+
await processDirectory(TEMPLATE_DIR, targetDir, {
|
|
138
|
+
appName,
|
|
139
|
+
services,
|
|
140
|
+
uuid,
|
|
141
|
+
});
|
|
142
|
+
console.log("β
Project created successfully!");
|
|
143
|
+
if (shouldInstall) {
|
|
144
|
+
console.log(`\nπ¦ Installing dependencies with ${packageManager}...`);
|
|
145
|
+
const installCommand = packageManager === "yarn"
|
|
146
|
+
? "yarn install"
|
|
147
|
+
: `${packageManager} install`;
|
|
148
|
+
try {
|
|
149
|
+
await execPromise(installCommand, { cwd: targetDir });
|
|
150
|
+
console.log("β
Dependencies installed successfully!");
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error("β Failed to install dependencies:", error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
console.log("\nπ All done! Happy coding with Lafkn!\n");
|
|
157
|
+
console.log("Next steps:");
|
|
158
|
+
if (!useCurrentDir) {
|
|
159
|
+
console.log(` cd ${appName}`);
|
|
160
|
+
}
|
|
161
|
+
if (!shouldInstall) {
|
|
162
|
+
console.log(` ${packageManager} install`);
|
|
163
|
+
}
|
|
164
|
+
console.log(` ${packageManager === "npm" ? "npm run" : packageManager} dev\n`);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
168
|
+
console.log("\nπ Project creation cancelled");
|
|
169
|
+
process.exit(0);
|
|
170
|
+
}
|
|
171
|
+
console.error("β An error occurred:", error);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
main();
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-lafkn",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a simple lafkn project",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"aws",
|
|
7
|
+
"lafkn",
|
|
8
|
+
"serverless",
|
|
9
|
+
"cdk"
|
|
10
|
+
],
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"author": "AnΓbal Jorquera<ajorquera.cornejo@gmail.com>",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"bin": "./dist/index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"lib"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@inquirer/prompts": "8.1.0",
|
|
21
|
+
"eta": "4.5.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "25.0.3",
|
|
25
|
+
"typescript": "5.9.3"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"release": "pnpm run build && pnpm -r publish --access public --no-git-checks"
|
|
30
|
+
}
|
|
31
|
+
}
|