create-n8n 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/dist/index.js ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile, readdir } from "node:fs/promises";
3
+ import { resolve, join, basename } from "node:path";
4
+ import { exec } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ import pc from "picocolors";
7
+ import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
8
+ const execAsync = promisify(exec);
9
+ const ASCII_ART = [
10
+ " ██████╗ █████╗ ██████╗ ",
11
+ " ██╔══██╗██╔══██╗██╔══██╗",
12
+ " ██║ ██║╚█████╔╝██║ ██║",
13
+ " ██║ ██║██╔══██╗██║ ██║",
14
+ " ██║ ██║╚█████╔╝██║ ██║",
15
+ " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
16
+ ];
17
+ function showBranding() {
18
+ process.stdout.write("\n");
19
+ const color = "\x1b[38;2;234;75;113m"; // #EA4B71
20
+ const reset = "\x1b[0m";
21
+ process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
22
+ }
23
+ const PACKAGE_JSON_TEMPLATE = (name) => `{
24
+ "name": "${name}",
25
+ "version": "1.0.0",
26
+ "description": "An n8n workflow project powered by flat8n",
27
+ "scripts": {
28
+ "dev": "flat8n",
29
+ "build": "flat8n link --entry workflows/main.json --out dist/main.json",
30
+ "start": "npx n8n start"
31
+ },
32
+ "dependencies": {},
33
+ "devDependencies": {
34
+ "@flat8n/cli": "latest"
35
+ }
36
+ }
37
+ `;
38
+ const GITIGNORE_TEMPLATE = `node_modules/
39
+ dist/
40
+ .env
41
+ .n8n/
42
+ `;
43
+ const ENV_TEMPLATE = `# The port your local n8n dev server runs on
44
+ N8N_PORT=5678
45
+
46
+ # The host your local n8n dev server binds to
47
+ N8N_HOST=localhost
48
+
49
+ # Security key for n8n encryption (change this in production)
50
+ N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
51
+ `;
52
+ const CONFIG_TEMPLATE = `{
53
+ "$schema": "https://flat8n.com/schema.json",
54
+ "workflowsDir": "workflows",
55
+ "outDir": "dist"
56
+ }
57
+ `;
58
+ const README_TEMPLATE = (name) => `# ${name}
59
+
60
+ [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
61
+ [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
62
+
63
+ A modern n8n workflow project, scaffolded with \`create-n8n\`.
64
+
65
+ ## Development
66
+
67
+ Run the dev server (automatically starts a local n8n instance and watches your files for changes):
68
+
69
+ \`\`\`bash
70
+ npm run dev
71
+ \`\`\`
72
+
73
+ ## Build
74
+
75
+ Compile your modular workflows into a single deployable monolithic JSON workflow:
76
+
77
+ \`\`\`bash
78
+ npm run build
79
+ \`\`\`
80
+
81
+ ## Architecture
82
+
83
+ Your source workflows are in the \`workflows/\` directory.
84
+ Shared or reusable logic goes in \`workflows/subworkflows/\`.
85
+ `;
86
+ const MAIN_JSON_TEMPLATE = `{
87
+ "name": "Main Entrypoint",
88
+ "nodes": [
89
+ {
90
+ "parameters": {
91
+ "httpMethod": "POST",
92
+ "path": "test",
93
+ "options": {}
94
+ },
95
+ "id": "1",
96
+ "name": "Webhook",
97
+ "type": "n8n-nodes-base.webhook",
98
+ "typeVersion": 1,
99
+ "position": [0, 0],
100
+ "webhookId": "test-webhook"
101
+ },
102
+ {
103
+ "parameters": {
104
+ "workflowId": "child"
105
+ },
106
+ "id": "2",
107
+ "name": "Execute Subworkflow",
108
+ "type": "n8n-nodes-base.executeWorkflow",
109
+ "typeVersion": 1,
110
+ "position": [200, 0]
111
+ }
112
+ ],
113
+ "connections": {
114
+ "Webhook": {
115
+ "main": [
116
+ [
117
+ {
118
+ "node": "Execute Subworkflow",
119
+ "type": "main",
120
+ "index": 0
121
+ }
122
+ ]
123
+ ]
124
+ }
125
+ }
126
+ }
127
+ `;
128
+ const CHILD_JSON_TEMPLATE = `{
129
+ "name": "Child Module",
130
+ "id": "child",
131
+ "nodes": [
132
+ {
133
+ "parameters": {},
134
+ "id": "1",
135
+ "name": "Execute Workflow Trigger",
136
+ "type": "n8n-nodes-base.executeWorkflowTrigger",
137
+ "typeVersion": 1,
138
+ "position": [0, 0]
139
+ },
140
+ {
141
+ "parameters": {
142
+ "jsCode": "return $input.all();"
143
+ },
144
+ "id": "2",
145
+ "name": "Process Logic",
146
+ "type": "n8n-nodes-base.code",
147
+ "typeVersion": 2,
148
+ "position": [200, 0]
149
+ }
150
+ ],
151
+ "connections": {
152
+ "Execute Workflow Trigger": {
153
+ "main": [
154
+ [
155
+ {
156
+ "node": "Process Logic",
157
+ "type": "main",
158
+ "index": 0
159
+ }
160
+ ]
161
+ ]
162
+ }
163
+ }
164
+ }
165
+ `;
166
+ async function main() {
167
+ console.clear();
168
+ showBranding();
169
+ intro(pc.bgCyan(pc.black(" create-n8n ")));
170
+ const cwd = process.cwd();
171
+ const cwdFiles = await readdir(cwd);
172
+ const allowedFiles = new Set([".git", ".DS_Store", ".vscode", ".idea", "Thumbs.db"]);
173
+ const isCwdEmpty = cwdFiles.every((f) => allowedFiles.has(f));
174
+ let targetDir = ".";
175
+ if (!isCwdEmpty) {
176
+ const dirPrompt = await text({
177
+ message: "Project name (creates a new folder, or '.' for current directory)",
178
+ placeholder: "my-n8n-app",
179
+ defaultValue: "my-n8n-app",
180
+ validate: (val) => {
181
+ if (!val || val.length === 0)
182
+ return "Project name is required";
183
+ if (!/^[a-z0-9-_./]+$/.test(val))
184
+ return "Invalid path format";
185
+ return;
186
+ }
187
+ });
188
+ if (isCancel(dirPrompt) || typeof dirPrompt !== "string") {
189
+ cancel("Initialization cancelled.");
190
+ return process.exit(1);
191
+ }
192
+ targetDir = dirPrompt;
193
+ if (targetDir === ".") {
194
+ const confirmOverwrite = await confirm({
195
+ message: "Current directory is not empty. Continue and potentially overwrite files?",
196
+ initialValue: false
197
+ });
198
+ if (isCancel(confirmOverwrite) || !confirmOverwrite) {
199
+ cancel("Initialization cancelled.");
200
+ return process.exit(1);
201
+ }
202
+ }
203
+ }
204
+ const projectDir = resolve(cwd, targetDir);
205
+ const projectName = targetDir === "." ? basename(cwd) : basename(projectDir);
206
+ const initGit = await confirm({
207
+ message: "Initialize a new Git repository?",
208
+ initialValue: true
209
+ });
210
+ if (isCancel(initGit)) {
211
+ cancel("Initialization cancelled.");
212
+ return process.exit(1);
213
+ }
214
+ const s = spinner();
215
+ s.start(`Creating project in ${projectDir}...`);
216
+ try {
217
+ // 1. Create Directories
218
+ await mkdir(projectDir, { recursive: true });
219
+ await mkdir(join(projectDir, "workflows"), { recursive: true });
220
+ await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
221
+ // 2. Write Config / Root Files
222
+ await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
223
+ await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
224
+ await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
225
+ await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
226
+ await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
227
+ // 3. Write Workflows
228
+ await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
229
+ await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
230
+ s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
231
+ if (initGit) {
232
+ s.start("Initializing Git repository...");
233
+ try {
234
+ await execAsync("git init", { cwd: projectDir });
235
+ s.stop(pc.green("✔ Git repository initialized"));
236
+ }
237
+ catch (err) {
238
+ s.stop(pc.red("✖ Failed to initialize Git repository"));
239
+ }
240
+ }
241
+ const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
242
+ log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
243
+ outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
244
+ process.exit(0);
245
+ }
246
+ catch (error) {
247
+ s.stop(pc.red("✖ Failed to create project"));
248
+ log.error(error.message);
249
+ process.exit(1);
250
+ }
251
+ }
252
+ main().catch(console.error);
package/index.ts ADDED
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile, readdir } from "node:fs/promises";
3
+ import { resolve, join, basename } from "node:path";
4
+ import { exec } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ import pc from "picocolors";
7
+ import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
8
+
9
+ const execAsync = promisify(exec);
10
+
11
+ const ASCII_ART = [
12
+ " ██████╗ █████╗ ██████╗ ",
13
+ " ██╔══██╗██╔══██╗██╔══██╗",
14
+ " ██║ ██║╚█████╔╝██║ ██║",
15
+ " ██║ ██║██╔══██╗██║ ██║",
16
+ " ██║ ██║╚█████╔╝██║ ██║",
17
+ " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
18
+ ];
19
+
20
+ function showBranding(): void {
21
+ process.stdout.write("\n");
22
+ const color = "\x1b[38;2;234;75;113m"; // #EA4B71
23
+ const reset = "\x1b[0m";
24
+ process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
25
+ }
26
+
27
+ const PACKAGE_JSON_TEMPLATE = (name: string) => `{
28
+ "name": "${name}",
29
+ "version": "1.0.0",
30
+ "description": "An n8n workflow project powered by flat8n",
31
+ "scripts": {
32
+ "dev": "flat8n",
33
+ "build": "flat8n link --entry workflows/main.json --out dist/main.json",
34
+ "start": "npx n8n start"
35
+ },
36
+ "dependencies": {},
37
+ "devDependencies": {
38
+ "@flat8n/cli": "latest"
39
+ }
40
+ }
41
+ `;
42
+
43
+ const GITIGNORE_TEMPLATE = `node_modules/
44
+ dist/
45
+ .env
46
+ .n8n/
47
+ `;
48
+
49
+ const ENV_TEMPLATE = `# The port your local n8n dev server runs on
50
+ N8N_PORT=5678
51
+
52
+ # The host your local n8n dev server binds to
53
+ N8N_HOST=localhost
54
+
55
+ # Security key for n8n encryption (change this in production)
56
+ N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
57
+ `;
58
+
59
+ const CONFIG_TEMPLATE = `{
60
+ "$schema": "https://flat8n.com/schema.json",
61
+ "workflowsDir": "workflows",
62
+ "outDir": "dist"
63
+ }
64
+ `;
65
+
66
+ const README_TEMPLATE = (name: string) => `# ${name}
67
+
68
+ [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
69
+ [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
70
+
71
+ A modern n8n workflow project, scaffolded with \`create-n8n\`.
72
+
73
+ ## Development
74
+
75
+ Run the dev server (automatically starts a local n8n instance and watches your files for changes):
76
+
77
+ \`\`\`bash
78
+ npm run dev
79
+ \`\`\`
80
+
81
+ ## Build
82
+
83
+ Compile your modular workflows into a single deployable monolithic JSON workflow:
84
+
85
+ \`\`\`bash
86
+ npm run build
87
+ \`\`\`
88
+
89
+ ## Architecture
90
+
91
+ Your source workflows are in the \`workflows/\` directory.
92
+ Shared or reusable logic goes in \`workflows/subworkflows/\`.
93
+ `;
94
+
95
+ const MAIN_JSON_TEMPLATE = `{
96
+ "name": "Main Entrypoint",
97
+ "nodes": [
98
+ {
99
+ "parameters": {
100
+ "httpMethod": "POST",
101
+ "path": "test",
102
+ "options": {}
103
+ },
104
+ "id": "1",
105
+ "name": "Webhook",
106
+ "type": "n8n-nodes-base.webhook",
107
+ "typeVersion": 1,
108
+ "position": [0, 0],
109
+ "webhookId": "test-webhook"
110
+ },
111
+ {
112
+ "parameters": {
113
+ "workflowId": "child"
114
+ },
115
+ "id": "2",
116
+ "name": "Execute Subworkflow",
117
+ "type": "n8n-nodes-base.executeWorkflow",
118
+ "typeVersion": 1,
119
+ "position": [200, 0]
120
+ }
121
+ ],
122
+ "connections": {
123
+ "Webhook": {
124
+ "main": [
125
+ [
126
+ {
127
+ "node": "Execute Subworkflow",
128
+ "type": "main",
129
+ "index": 0
130
+ }
131
+ ]
132
+ ]
133
+ }
134
+ }
135
+ }
136
+ `;
137
+
138
+ const CHILD_JSON_TEMPLATE = `{
139
+ "name": "Child Module",
140
+ "id": "child",
141
+ "nodes": [
142
+ {
143
+ "parameters": {},
144
+ "id": "1",
145
+ "name": "Execute Workflow Trigger",
146
+ "type": "n8n-nodes-base.executeWorkflowTrigger",
147
+ "typeVersion": 1,
148
+ "position": [0, 0]
149
+ },
150
+ {
151
+ "parameters": {
152
+ "jsCode": "return $input.all();"
153
+ },
154
+ "id": "2",
155
+ "name": "Process Logic",
156
+ "type": "n8n-nodes-base.code",
157
+ "typeVersion": 2,
158
+ "position": [200, 0]
159
+ }
160
+ ],
161
+ "connections": {
162
+ "Execute Workflow Trigger": {
163
+ "main": [
164
+ [
165
+ {
166
+ "node": "Process Logic",
167
+ "type": "main",
168
+ "index": 0
169
+ }
170
+ ]
171
+ ]
172
+ }
173
+ }
174
+ }
175
+ `;
176
+
177
+ async function main() {
178
+ console.clear();
179
+ showBranding();
180
+ intro(pc.bgCyan(pc.black(" create-n8n ")));
181
+
182
+ const cwd = process.cwd();
183
+
184
+ const cwdFiles = await readdir(cwd);
185
+ const allowedFiles = new Set([".git", ".DS_Store", ".vscode", ".idea", "Thumbs.db"]);
186
+ const isCwdEmpty = cwdFiles.every((f: string) => allowedFiles.has(f));
187
+
188
+ let targetDir = ".";
189
+
190
+ if (!isCwdEmpty) {
191
+ const dirPrompt = await text({
192
+ message: "Project name (creates a new folder, or '.' for current directory)",
193
+ placeholder: "my-n8n-app",
194
+ defaultValue: "my-n8n-app",
195
+ validate: (val) => {
196
+ if (!val || val.length === 0) return "Project name is required";
197
+ if (!/^[a-z0-9-_./]+$/.test(val)) return "Invalid path format";
198
+ return;
199
+ }
200
+ });
201
+
202
+ if (isCancel(dirPrompt) || typeof dirPrompt !== "string") {
203
+ cancel("Initialization cancelled.");
204
+ return process.exit(1);
205
+ }
206
+ targetDir = dirPrompt;
207
+
208
+ if (targetDir === ".") {
209
+ const confirmOverwrite = await confirm({
210
+ message: "Current directory is not empty. Continue and potentially overwrite files?",
211
+ initialValue: false
212
+ });
213
+ if (isCancel(confirmOverwrite) || !confirmOverwrite) {
214
+ cancel("Initialization cancelled.");
215
+ return process.exit(1);
216
+ }
217
+ }
218
+ }
219
+
220
+ const projectDir = resolve(cwd, targetDir);
221
+ const projectName = targetDir === "." ? basename(cwd) : basename(projectDir);
222
+
223
+ const initGit = await confirm({
224
+ message: "Initialize a new Git repository?",
225
+ initialValue: true
226
+ });
227
+
228
+ if (isCancel(initGit)) {
229
+ cancel("Initialization cancelled.");
230
+ return process.exit(1);
231
+ }
232
+
233
+ const s = spinner();
234
+ s.start(`Creating project in ${projectDir}...`);
235
+
236
+ try {
237
+ // 1. Create Directories
238
+ await mkdir(projectDir, { recursive: true });
239
+ await mkdir(join(projectDir, "workflows"), { recursive: true });
240
+ await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
241
+
242
+ // 2. Write Config / Root Files
243
+ await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
244
+ await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
245
+ await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
246
+ await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
247
+ await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
248
+
249
+ // 3. Write Workflows
250
+ await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
251
+ await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
252
+
253
+ s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
254
+
255
+ if (initGit) {
256
+ s.start("Initializing Git repository...");
257
+ try {
258
+ await execAsync("git init", { cwd: projectDir });
259
+ s.stop(pc.green("✔ Git repository initialized"));
260
+ } catch (err) {
261
+ s.stop(pc.red("✖ Failed to initialize Git repository"));
262
+ }
263
+ }
264
+
265
+ const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
266
+ log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
267
+ outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
268
+ process.exit(0);
269
+
270
+ } catch (error: any) {
271
+ s.stop(pc.red("✖ Failed to create project"));
272
+ log.error(error.message);
273
+ process.exit(1);
274
+ }
275
+ }
276
+
277
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "create-n8n",
3
+ "version": "1.0.0",
4
+ "description": "Scaffolding tool for n8n workflow projects",
5
+ "main": "./dist/index.js",
6
+ "author": "JVON DEV <https://github.com/jvondev>",
7
+ "type": "module",
8
+ "bin": {
9
+ "create-n8n": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepack": "npm run build"
14
+ },
15
+ "dependencies": {
16
+ "@clack/prompts": "^1.7.0",
17
+ "picocolors": "^1.1.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.0.0",
21
+ "typescript": "^5.0.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ }
26
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "esModuleInterop": true,
7
+ "forceConsistentCasingInFileNames": true,
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "outDir": "./dist",
11
+ "types": ["node"]
12
+ },
13
+ "include": ["index.ts"]
14
+ }