feat: Add @n8n/node-cli package with an empty create command (#17620)

This commit is contained in:
Elias Meire
2025-07-31 00:09:49 +02:00
committed by GitHub
parent 53594a90e4
commit 79c6b60fcb
13 changed files with 268 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# @n8n/create-node
Scaffold a new community n8n node
## Usage
```bash
npm create @n8n/node
# or
pnpm create @n8n/node
# or
yarn create @n8n/node
```

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { createRequire } from 'node:module';
import path from 'node:path';
const require = createRequire(import.meta.url);
const cliBin = require.resolve('@n8n/node-cli/bin/n8n-node.js');
const result = spawnSync('node', [cliBin, 'create', ...process.argv.slice(2)], {
stdio: 'inherit',
});
process.exit(result.status ?? 1);

View File

@@ -0,0 +1,25 @@
{
"private": true,
"type": "module",
"name": "@n8n/create-node",
"version": "0.1.0",
"description": "Official CLI to create new community nodes for n8n",
"bin": {
"create-n8n-node": "./bin/create.js"
},
"files": [
"bin",
"dist"
],
"scripts": {
"publish:dry": "pnpm run build && pnpm pub --dry-run",
"start": "./bin/create.js"
},
"repository": {
"type": "git",
"url": "https://github.com/n8n-io/n8n"
},
"dependencies": {
"@n8n/node-cli": "workspace:*"
}
}

View File

@@ -0,0 +1,11 @@
{
"extends": "@n8n/typescript-config/modern/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"outDir": "dist",
"types": ["vite/client", "vitest/globals"],
"isolatedModules": true
},
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,35 @@
# @n8n/node-cli
Official CLI for developing community nodes for [n8n](https://n8n.io).
## Features
- 🔧 Scaffold new nodes
- More coming soon
## Installation
Run directly via `npx`:
```bash
npx n8n-node create
```
Or install globally:
```bash
npm install -g @n8n/node-cli
n8n-node create
```
## Commands
## Create a node
```bash
n8n-node create # Scaffold a new node
```
## Related
`@n8n/create-node`: Lightweight wrapper to support `npm create @n8n/node`

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env node
import { execute } from '@oclif/core';
await execute({ dir: import.meta.url });

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'eslint/config';
import { nodeConfig } from '@n8n/eslint-config/node';
export default defineConfig(nodeConfig, {
files: ['./src/commands/*.ts'],
rules: { 'import-x/no-default-export': 'off' },
});

View File

@@ -0,0 +1,46 @@
{
"private": true,
"type": "module",
"name": "@n8n/node-cli",
"version": "0.1.0",
"description": "Official CLI for developing community nodes for n8n",
"bin": {
"n8n-node": "./bin/n8n-node.js"
},
"files": [
"bin",
"dist"
],
"scripts": {
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"dev": "tsc -w",
"format": "biome format --write src",
"format:check": "biome ci src",
"lint": "eslint src --quiet",
"lintfix": "eslint src --fix",
"build": "tsc",
"publish:dry": "pnpm run build && pnpm pub --dry-run",
"test": "vitest run",
"test:dev": "vitest --silent=false",
"start": "./bin/n8n-node.js"
},
"repository": {
"type": "git",
"url": "https://github.com/n8n-io/n8n"
},
"oclif": {
"bin": "n8n-node",
"commands": "./dist/commands",
"topicSeparator": " "
},
"dependencies": {
"@oclif/core": "^4.5.2",
"prompts": "^2.4.2"
},
"devDependencies": {
"@n8n/typescript-config": "workspace:*",
"@n8n/vitest-config": "workspace:*",
"@oclif/test": "^4.1.13"
}
}

View File

@@ -0,0 +1,8 @@
import { runCommand } from '@oclif/test';
describe('n8n-node create', () => {
it('should print correct output', async () => {
const { stdout } = await runCommand('create -f', { root: import.meta.dirname });
expect(stdout).toEqual('hello from commands/create.ts (force=true)\n');
});
});

View File

@@ -0,0 +1,17 @@
import { Command, Flags } from '@oclif/core';
export default class Create extends Command {
static override description = 'Create a new n8n community node';
static override examples = ['<%= config.bin %> <%= command.id %>'];
static override flags = {
// flag with no value (-f, --force)
force: Flags.boolean({ char: 'f' }),
};
async run(): Promise<void> {
const { flags } = await this.parse(Create);
const force = flags.force;
this.log(`hello from commands/create.ts (force=${force})`);
}
}

View File

@@ -0,0 +1,12 @@
{
"extends": "@n8n/typescript-config/modern/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"outDir": "dist",
"types": ["vite/client", "vitest/globals"],
"isolatedModules": true
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}

View File

@@ -0,0 +1,4 @@
import { vitestConfig } from '@n8n/vitest-config/node';
import { defineConfig, mergeConfig } from 'vitest/config';
export default defineConfig({ test: { globals: true, disableConsoleIntercept: true } });

70
pnpm-lock.yaml generated
View File

@@ -635,6 +635,12 @@ importers:
specifier: workspace:*
version: link:../typescript-config
packages/@n8n/create-node:
dependencies:
'@n8n/node-cli':
specifier: workspace:*
version: link:../node-cli
packages/@n8n/db:
dependencies:
'@n8n/api-types':
@@ -912,6 +918,25 @@ importers:
specifier: 'catalog:'
version: 3.25.67
packages/@n8n/node-cli:
dependencies:
'@oclif/core':
specifier: ^4.5.2
version: 4.5.2
prompts:
specifier: ^2.4.2
version: 2.4.2
devDependencies:
'@n8n/typescript-config':
specifier: workspace:*
version: link:../typescript-config
'@n8n/vitest-config':
specifier: workspace:*
version: link:../vitest-config
'@oclif/test':
specifier: ^4.1.13
version: 4.1.13(@oclif/core@4.5.2)
packages/@n8n/nodes-langchain:
dependencies:
'@aws-sdk/client-sso-oidc':
@@ -5734,6 +5759,16 @@ packages:
resolution: {integrity: sha512-sU4Dx+RXCWAkrMw8tQFYAL6VfcHYKLPxVC9iKfgTXr4aDhcCssDwrbgpx0Di1dnNxvQlDGUhuCEInZuIY/nNfw==}
engines: {node: '>=18.0.0'}
'@oclif/core@4.5.2':
resolution: {integrity: sha512-eQcKyrEcDYeZJKu4vUWiu0ii/1Gfev6GF4FsLSgNez5/+aQyAUCjg3ZWlurf491WiYZTXCWyKAxyPWk8DKv2MA==}
engines: {node: '>=18.0.0'}
'@oclif/test@4.1.13':
resolution: {integrity: sha512-pulrTiJRhoAKizFf6y5WeHvM2JyoRiZKV0H8qqYEoE0UHDKqInNmfGJyp8Ip6lTVQeMv1U8YCAXOS/HiWPVWeg==}
engines: {node: '>=18.0.0'}
peerDependencies:
'@oclif/core': '>= 3.0.0'
'@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
@@ -8047,6 +8082,10 @@ packages:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
ansis@3.17.0:
resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==}
engines: {node: '>=14'}
ansis@3.2.0:
resolution: {integrity: sha512-Yk3BkHH9U7oPyCN3gL5Tc7CpahG/+UFv/6UG03C311Vy9lzRmA5uoxDTpU9CO3rGHL6KzJz/pdDeXZCZ5Mu/Sg==}
engines: {node: '>=15'}
@@ -19843,6 +19882,35 @@ snapshots:
wordwrap: 1.0.0
wrap-ansi: 7.0.0
'@oclif/core@4.5.2':
dependencies:
ansi-escapes: 4.3.2
ansis: 3.17.0
clean-stack: 3.0.1
cli-spinners: 2.9.2
debug: 4.4.1(supports-color@8.1.1)
ejs: 3.1.10
get-package-type: 0.1.0
indent-string: 4.0.0
is-wsl: 2.2.0
lilconfig: 3.1.3
minimatch: 9.0.5
semver: 7.7.2
string-width: 4.2.3
supports-color: 8.1.1
tinyglobby: 0.2.14
widest-line: 3.1.0
wordwrap: 1.0.0
wrap-ansi: 7.0.0
'@oclif/test@4.1.13(@oclif/core@4.5.2)':
dependencies:
'@oclif/core': 4.5.2
ansis: 3.17.0
debug: 4.4.1(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
'@one-ini/wasm@0.1.1': {}
'@open-draft/deferred-promise@2.2.0': {}
@@ -22732,6 +22800,8 @@ snapshots:
ansi-styles@6.2.1: {}
ansis@3.17.0: {}
ansis@3.2.0: {}
any-promise@1.3.0: {}