fix: Improve @n8n/node-cli cross-platform compatibility and dev experience (no-changelog) (#19368)

This commit is contained in:
Elias Meire
2025-09-11 08:47:26 +02:00
committed by GitHub
parent 306bdb9a2a
commit e404e55ce5
9 changed files with 120 additions and 75 deletions

View File

@@ -7,19 +7,19 @@ A powerful scaffolding tool to quickly create custom n8n community nodes with be
Create a new n8n node in seconds: Create a new n8n node in seconds:
```bash ```bash
pnpm create @n8n/node npm create @n8n/node@latest # or pnpm/yarn/...
``` ```
Follow the interactive prompts to configure your node, or specify options directly: Follow the interactive prompts to configure your node, or specify options directly:
```bash ```bash
pnpm create @n8n/node my-awesome-node --template declarative/custom npm create @n8n/node my-awesome-node --template declarative/custom
``` ```
## 📋 Command Line Options ## 📋 Command Line Options
```bash ```bash
pnpm create @n8n/node [NAME] [OPTIONS] npm create @n8n/node [NAME] [OPTIONS]
``` ```
### Options ### Options
@@ -41,7 +41,7 @@ pnpm create @n8n/node [NAME] [OPTIONS]
The CLI will guide you through setting up your node: The CLI will guide you through setting up your node:
``` ```
$ pnpm create @n8n/node $ npm create @n8n/node
┌ @n8n/create-node ┌ @n8n/create-node
◇ What is your node called? ◇ What is your node called?
@@ -65,7 +65,7 @@ $ pnpm create @n8n/node
◇ Next Steps ─────────────────────────────────────────────────────────────────────╮ ◇ Next Steps ─────────────────────────────────────────────────────────────────────╮
│ │ │ │
│ cd ./my-awesome-api-node && pnpm run dev │ │ cd ./my-awesome-api-node && npm run dev │
│ │ │ │
│ 📚 Documentation: https://docs.n8n.io/integrations/creating-nodes/ │ │ 📚 Documentation: https://docs.n8n.io/integrations/creating-nodes/ │
│ 💬 Community: https://community.n8n.io │ │ 💬 Community: https://community.n8n.io │
@@ -86,7 +86,7 @@ cd ./my-awesome-api-node
### 2. Start development server ### 2. Start development server
```bash ```bash
pnpm dev npm run dev
``` ```
This command: This command:
@@ -109,28 +109,28 @@ Your generated project comes with these convenient npm scripts:
### Development ### Development
```bash ```bash
pnpm dev npm run dev
# Runs: n8n-node dev # Runs: n8n-node dev
``` ```
### Building ### Building
```bash ```bash
pnpm build npm run build
# Runs: n8n-node build # Runs: n8n-node build
``` ```
### Linting ### Linting
```bash ```bash
pnpm lint npm run lint
# Runs: n8n-node lint # Runs: n8n-node lint
pnpm lint:fix npm run lint:fix
# Runs: n8n-node lint --fix # Runs: n8n-node lint --fix
``` ```
### Publishing ### Publishing
```bash ```bash
pnpm run release npm run release
# Runs: n8n-node release # Runs: n8n-node release
``` ```
@@ -139,7 +139,7 @@ pnpm run release
### Build for production ### Build for production
```bash ```bash
pnpm build npm run build
``` ```
Generates: Generates:
@@ -151,7 +151,7 @@ Generates:
### Quality checks ### Quality checks
```bash ```bash
pnpm lint npm run lint
``` ```
Validates: Validates:
@@ -163,13 +163,13 @@ Validates:
Fix issues automatically: Fix issues automatically:
```bash ```bash
pnpm lint:fix npm run lint:fix
``` ```
### Publish your node ### Publish your node
```bash ```bash
pnpm run release npm run release
``` ```
Runs [release-it](https://github.com/release-it/release-it) to handle the complete release process: Runs [release-it](https://github.com/release-it/release-it) to handle the complete release process:
@@ -238,28 +238,28 @@ Choose the right template for your use case:
```bash ```bash
# Clear n8n node cli cache and restart # Clear n8n node cli cache and restart
rm -rf ~/.n8n-node-cli/.n8n/custom rm -rf ~/.n8n-node-cli/.n8n/custom
pnpm dev npm run dev
``` ```
**TypeScript errors:** **TypeScript errors:**
```bash ```bash
# Reinstall dependencies # Reinstall dependencies
rm -rf node_modules pnpm-lock.yaml rm -rf node_modules npm-lock.yaml
pnpm install npm install
``` ```
**Build failures:** **Build failures:**
```bash ```bash
# Check for linting issues first # Check for linting issues first
pnpm lint --fix npm run lint --fix
pnpm build npm run build
``` ```
**Development server issues:** **Development server issues:**
```bash ```bash
# Clear cache and restart development server # Clear cache and restart development server
rm -rf ~/.n8n-node-cli/.n8n/custom rm -rf ~/.n8n-node-cli/.n8n/custom
pnpm dev npm run dev
``` ```
## 🔧 Advanced Usage ## 🔧 Advanced Usage
@@ -269,7 +269,7 @@ pnpm dev
If you prefer to use your own n8n installation: If you prefer to use your own n8n installation:
```bash ```bash
pnpm dev --external-n8n npm run dev --external-n8n
``` ```
### Custom User Folder ### Custom User Folder
@@ -277,7 +277,7 @@ pnpm dev --external-n8n
Specify a custom location for n8n user data: Specify a custom location for n8n user data:
```bash ```bash
pnpm dev --custom-user-folder /path/to/custom/folder npm run dev --custom-user-folder /path/to/custom/folder
``` ```
## 📚 Resources ## 📚 Resources

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env node
const { spawnSync } = require('child_process');
const n8nNodeBin = require.resolve('.bin/n8n-node');
const result = spawnSync(n8nNodeBin, ['new', ...process.argv.slice(2)], {
stdio: 'inherit',
});
process.exit(result.status ?? 1);

View File

@@ -1,9 +0,0 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
const result = spawnSync('n8n-node', ['new', ...process.argv.slice(2)], {
stdio: 'inherit',
});
process.exit(result.status ?? 1);

View File

@@ -1,16 +1,16 @@
{ {
"name": "@n8n/create-node", "name": "@n8n/create-node",
"version": "0.5.0", "version": "0.6.0",
"description": "Official CLI to create new community nodes for n8n", "description": "Official CLI to create new community nodes for n8n",
"bin": { "bin": {
"create-n8n-node": "bin/create.js" "create-node": "bin/create-node.cjs"
}, },
"files": [ "files": [
"bin", "bin",
"dist" "dist"
], ],
"scripts": { "scripts": {
"start": "node bin/create.js" "start": "node bin/create-node.cjs"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -18,5 +18,8 @@
}, },
"dependencies": { "dependencies": {
"@n8n/node-cli": "workspace:*" "@n8n/node-cli": "workspace:*"
},
"devDependencies": {
"@n8n/typescript-config": "workspace:*"
} }
} }

View File

@@ -7,45 +7,45 @@ Official CLI for developing community nodes for n8n.
**To create a new node**, run: **To create a new node**, run:
```bash ```bash
pnpm create @n8n/node npm create @n8n/node@latest # or pnpm/yarn/...
``` ```
This will generate a project with `pnpm` scripts that use this CLI under the hood. This will generate a project with `npm` scripts that use this CLI under the hood.
## 📦 Generated Project Commands ## 📦 Generated Project Commands
After creating your node with `pnpm create @n8n/node`, you'll use these commands in your project: After creating your node with `npm create @n8n/node`, you'll use these commands in your project:
### Development ### Development
```bash ```bash
pnpm dev npm run dev
# Runs: n8n-node dev # Runs: n8n-node dev
``` ```
### Building ### Building
```bash ```bash
pnpm build npm run build
# Runs: n8n-node build # Runs: n8n-node build
``` ```
### Linting ### Linting
```bash ```bash
pnpm lint npm run lint
# Runs: n8n-node lint # Runs: n8n-node lint
pnpm lint:fix npm run lint:fix
# Runs: n8n-node lint --fix # Runs: n8n-node lint --fix
``` ```
### Publishing ### Publishing
```bash ```bash
pnpm run release npm run release
# Runs: n8n-node release # Runs: n8n-node release
``` ```
## 🛠️ CLI Reference ## 🛠️ CLI Reference
> **Note:** These commands are typically wrapped by `pnpm` scripts in generated projects. > **Note:** These commands are typically wrapped by `npm` scripts in generated projects.
```bash ```bash
n8n-node [COMMAND] [OPTIONS] n8n-node [COMMAND] [OPTIONS]
@@ -76,7 +76,7 @@ n8n-node new n8n-nodes-my-app --force
n8n-node new n8n-nodes-my-app --template declarative/custom n8n-node new n8n-nodes-my-app --template declarative/custom
``` ```
> **Note:** This command is used internally by `pnpm create @n8n/node` to provide the interactive scaffolding experience. > **Note:** This command is used internally by `npm create @n8n/node` to provide the interactive scaffolding experience.
#### `n8n-node dev` #### `n8n-node dev`
@@ -172,13 +172,13 @@ The recommended workflow using the scaffolding tool:
1. **Create your node**: 1. **Create your node**:
```bash ```bash
pnpm create @n8n/node my-awesome-node npm create @n8n/node my-awesome-node
cd my-awesome-node cd my-awesome-node
``` ```
2. **Start development**: 2. **Start development**:
```bash ```bash
pnpm dev npm run dev
``` ```
- Starts n8n on `http://localhost:5678` - Starts n8n on `http://localhost:5678`
- Links your node automatically - Links your node automatically
@@ -188,17 +188,17 @@ The recommended workflow using the scaffolding tool:
4. **Lint your code**: 4. **Lint your code**:
```bash ```bash
pnpm lint npm run lint
``` ```
5. **Build for production**: 5. **Build for production**:
```bash ```bash
pnpm build npm run build
``` ```
6. **Publish**: 6. **Publish**:
```bash ```bash
pnpm run release npm run release
``` ```
## 📁 Project Structure ## 📁 Project Structure
@@ -244,16 +244,16 @@ The CLI reads configuration from your `package.json`:
rm -rf ~/.n8n-node-cli/.n8n/custom rm -rf ~/.n8n-node-cli/.n8n/custom
# Restart development server # Restart development server
pnpm dev npm run dev
``` ```
### Build failures ### Build failures
```bash ```bash
# Run linting first # Run linting first
pnpm lint npm run lint
# Clean build # Clean build
pnpm build npm run build
``` ```
## 📚 Resources ## 📚 Resources

View File

@@ -1,6 +1,6 @@
{ {
"name": "@n8n/node-cli", "name": "@n8n/node-cli",
"version": "0.5.0", "version": "0.6.0",
"description": "Official CLI for developing community nodes for n8n", "description": "Official CLI for developing community nodes for n8n",
"bin": { "bin": {
"n8n-node": "bin/n8n-node.mjs" "n8n-node": "bin/n8n-node.mjs"

View File

@@ -1,4 +1,4 @@
import { intro, outro, spinner } from '@clack/prompts'; import { intro, outro, spinner, log } from '@clack/prompts';
import { Command, Flags } from '@oclif/core'; import { Command, Flags } from '@oclif/core';
import os from 'node:os'; import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
@@ -18,7 +18,7 @@ export default class Dev extends Command {
static override examples = [ static override examples = [
'<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --external-n8n', '<%= config.bin %> <%= command.id %> --external-n8n',
'<%= config.bin %> <%= command.id %> --custom-nodes-dir /opt/n8n-extensions', '<%= config.bin %> <%= command.id %> --custom-user-folder /Users/test',
]; ];
static override flags = { static override flags = {
'external-n8n': Flags.boolean({ 'external-n8n': Flags.boolean({
@@ -29,7 +29,7 @@ export default class Dev extends Command {
'custom-user-folder': Flags.directory({ 'custom-user-folder': Flags.directory({
default: path.join(os.homedir(), '.n8n-node-cli'), default: path.join(os.homedir(), '.n8n-node-cli'),
description: description:
'Folder to use to store user-specific n8n data. By default it will use ~/.n8n-node-cli. You probably want to enable this option if you run n8n with a custom N8N_CUSTOM_EXTENSIONS env variable.', 'Folder to use to store user-specific n8n data. By default it will use ~/.n8n-node-cli.',
}), }),
}; };
@@ -67,25 +67,52 @@ export default class Dev extends Command {
linkingSpinner.stop('Linked custom node to n8n'); linkingSpinner.stop('Linked custom node to n8n');
outro('✓ Setup complete');
if (!flags['external-n8n']) { if (!flags['external-n8n']) {
// Run n8n with hot reload enabled let setupComplete = false;
runPersistentCommand('npx', ['-y', '--quiet', 'n8n'], { const npxN8nSpinner = spinner();
cwd: n8nUserFolder, npxN8nSpinner.start('Starting n8n dev server');
env: { log.warn(picocolors.dim('First run may take a few minutes while dependencies are installed'));
...process.env,
N8N_DEV_RELOAD: 'true', // Run n8n with hot reload enabled, always attempt to use latest n8n
N8N_RUNNERS_ENABLED: 'true', // TODO: Use n8n@latest. Currently using n8n@next because of broken hot reloading before n8n@1.111.0
DB_SQLITE_POOL_SIZE: '10', await Promise.race([
N8N_USER_FOLDER: n8nUserFolder, new Promise<void>((resolve) => {
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: 'false', runPersistentCommand('npx', ['-y', '--quiet', '--prefer-online', 'n8n@next'], {
}, cwd: n8nUserFolder,
name: 'n8n', env: {
color: picocolors.green, ...process.env,
}); N8N_DEV_RELOAD: 'true',
N8N_RUNNERS_ENABLED: 'true',
DB_SQLITE_POOL_SIZE: '10',
N8N_USER_FOLDER: n8nUserFolder,
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: 'false',
},
name: 'n8n',
color: picocolors.green,
allowOutput: (line) => {
if (line.includes('Initializing n8n process')) {
resolve();
}
return setupComplete;
},
});
}),
new Promise<void>((_, reject) => {
setTimeout(() => {
const error = new Error('n8n startup timeout after 120 seconds');
onCancel(error.message);
reject(error);
}, 120_000);
}),
]);
setupComplete = true;
npxN8nSpinner.stop('Started n8n dev server');
} }
outro('✓ Setup complete');
// Run `tsc --watch` in background // Run `tsc --watch` in background
runPersistentCommand(packageManager, ['exec', '--', 'tsc', '--watch'], { runPersistentCommand(packageManager, ['exec', '--', 'tsc', '--watch'], {
name: 'build', name: 'build',

View File

@@ -25,8 +25,14 @@ export function commands() {
function runPersistentCommand( function runPersistentCommand(
cmd: string, cmd: string,
args: string[], args: string[],
opts: { cwd?: string; env?: NodeJS.ProcessEnv; name?: string; color?: Formatter } = {}, opts: {
): void { cwd?: string;
env?: NodeJS.ProcessEnv;
name?: string;
color?: Formatter;
allowOutput?: (line: string) => boolean;
} = {},
) {
const child = spawn(cmd, args, { const child = spawn(cmd, args, {
cwd: opts.cwd, cwd: opts.cwd,
env: { ...process.env, ...opts.env }, env: { ...process.env, ...opts.env },
@@ -46,6 +52,7 @@ export function commands() {
} }
const log = (text: string) => { const log = (text: string) => {
if (opts.allowOutput && !opts.allowOutput(text)) return;
if (opts.name) { if (opts.name) {
const rawPrefix = `[${opts.name}]`; const rawPrefix = `[${opts.name}]`;
const prefix = opts.color ? opts.color(rawPrefix) : rawPrefix; const prefix = opts.color ? opts.color(rawPrefix) : rawPrefix;
@@ -71,6 +78,8 @@ export function commands() {
console.log(`${opts.name ?? cmd} exited with code ${code}`); console.log(`${opts.name ?? cmd} exited with code ${code}`);
process.exit(code); process.exit(code);
}); });
return child;
} }
return { return {

4
pnpm-lock.yaml generated
View File

@@ -647,6 +647,10 @@ importers:
'@n8n/node-cli': '@n8n/node-cli':
specifier: workspace:* specifier: workspace:*
version: link:../node-cli version: link:../node-cli
devDependencies:
'@n8n/typescript-config':
specifier: workspace:*
version: link:../typescript-config
packages/@n8n/db: packages/@n8n/db:
dependencies: dependencies: