mirror of
https://github.com/nushell/nushell.git
synced 2025-04-18 02:08:20 +02:00
create nu_plugin_node_example.js (#15482)
example like [nu_plugin_python_example.py](https://github.com/nushell/nushell/blob/main/crates/nu_plugin_python/nu_plugin_python_example.py)
This commit is contained in:
parent
c8c018452f
commit
017daeed18
248
crates/nu_plugin_javascript/nu_plugin_node_example.js
Normal file
248
crates/nu_plugin_javascript/nu_plugin_node_example.js
Normal file
@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Node.js Nushell Plugin Example
|
||||
* Communicates with Nushell via JSON-encoded messages over stdin/stdout
|
||||
*
|
||||
* Register command: `plugin add -s 'C:\Program Files\nodejs\node.exe' ./nu_plugin_node_example.js`
|
||||
* `plugin use ./nu_plugin_node_example.js`
|
||||
* Usage: node_example 2 "3"
|
||||
*/
|
||||
|
||||
// Configuration constants
|
||||
const NUSHELL_VERSION = '0.103.1';
|
||||
const PLUGIN_VERSION = '0.1.1';
|
||||
|
||||
// Core protocol functions
|
||||
|
||||
/**
|
||||
* Writes a structured response to stdout
|
||||
* @param {number} id - Call identifier
|
||||
* @param {object} response - Response payload
|
||||
*/
|
||||
function writeResponse (id, response) {
|
||||
const message = JSON.stringify({ CallResponse: [id, response] });
|
||||
process.stdout.write(`${message}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an error response
|
||||
* @param {number} id - Call identifier
|
||||
* @param {string} text - Error description
|
||||
* @param {object|null} span - Error location metadata
|
||||
*/
|
||||
function writeError (id, text, span = null) {
|
||||
const error = span ? {
|
||||
Error: {
|
||||
msg: 'Plugin execution error',
|
||||
labels: [{ text, span }]
|
||||
}
|
||||
} : {
|
||||
Error: {
|
||||
msg: 'Plugin configuration error',
|
||||
help: text
|
||||
}
|
||||
};
|
||||
writeResponse(id, error);
|
||||
}
|
||||
|
||||
// Plugin capability definitions
|
||||
|
||||
/**
|
||||
* Generates plugin signature metadata
|
||||
* @returns {object} Structured plugin capabilities
|
||||
*/
|
||||
function getPluginSignature () {
|
||||
return {
|
||||
Signature: [{
|
||||
sig: {
|
||||
name: 'node_example',
|
||||
description: 'Demonstration plugin for Node.js',
|
||||
extra_description: '',
|
||||
required_positional: [
|
||||
{
|
||||
name: 'a',
|
||||
desc: 'Required integer parameter',
|
||||
shape: 'Int'
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
desc: 'Required string parameter',
|
||||
shape: 'String'
|
||||
}
|
||||
],
|
||||
optional_positional: [{
|
||||
name: 'opt',
|
||||
desc: 'Optional numeric parameter',
|
||||
shape: 'Int'
|
||||
}],
|
||||
rest_positional: {
|
||||
name: 'rest',
|
||||
desc: 'Variable-length string parameters',
|
||||
shape: 'String'
|
||||
},
|
||||
named: [
|
||||
{
|
||||
long: 'help',
|
||||
short: 'h',
|
||||
arg: null,
|
||||
required: false,
|
||||
desc: 'Display help information'
|
||||
},
|
||||
{
|
||||
long: 'flag',
|
||||
short: 'f',
|
||||
arg: null,
|
||||
required: false,
|
||||
desc: 'Example boolean flag'
|
||||
},
|
||||
{
|
||||
long: 'named',
|
||||
short: 'n',
|
||||
arg: 'String',
|
||||
required: false,
|
||||
desc: 'Example named parameter'
|
||||
}
|
||||
],
|
||||
input_output_types: [['Any', 'Any']],
|
||||
allow_variants_without_examples: true,
|
||||
search_terms: ['nodejs', 'example'],
|
||||
is_filter: false,
|
||||
creates_scope: false,
|
||||
allows_unknown_args: false,
|
||||
category: 'Experimental'
|
||||
},
|
||||
examples: []
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes execution calls from Nushell
|
||||
* @param {number} id - Call identifier
|
||||
* @param {object} callData - Execution context metadata
|
||||
*/
|
||||
function processExecutionCall (id, callData) {
|
||||
const span = callData.call.head;
|
||||
|
||||
// Generate sample tabular data
|
||||
const tableData = Array.from({ length: 10 }, (_, index) => ({
|
||||
Record: {
|
||||
val: {
|
||||
one: { Int: { val: index * 1, span } },
|
||||
two: { Int: { val: index * 2, span } },
|
||||
three: { Int: { val: index * 3, span } }
|
||||
},
|
||||
span
|
||||
}
|
||||
}));
|
||||
|
||||
writeResponse(id, {
|
||||
PipelineData: {
|
||||
Value: [{
|
||||
List: {
|
||||
vals: tableData,
|
||||
span
|
||||
}
|
||||
}, null]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Protocol handling
|
||||
|
||||
/**
|
||||
* Handles different types of input messages
|
||||
* @param {object} input - Parsed JSON message from Nushell
|
||||
*/
|
||||
function handleInputMessage (input) {
|
||||
if (input.Hello) {
|
||||
handleHelloMessage(input.Hello);
|
||||
} else if (input === 'Goodbye') {
|
||||
process.exit(0);
|
||||
} else if (input.Call) {
|
||||
handleCallMessage(...input.Call);
|
||||
} else if (input.Signal) {
|
||||
handleSignal(input.Signal);
|
||||
}
|
||||
}
|
||||
|
||||
function handleHelloMessage ({ version }) {
|
||||
if (version !== NUSHELL_VERSION) {
|
||||
process.stderr.write(`Version mismatch: Expected ${NUSHELL_VERSION}, got ${version}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCallMessage (id, call) {
|
||||
try {
|
||||
if (call === 'Metadata') {
|
||||
writeResponse(id, { Metadata: { version: PLUGIN_VERSION } });
|
||||
} else if (call === 'Signature') {
|
||||
writeResponse(id, getPluginSignature());
|
||||
} else if (call.Run) {
|
||||
processExecutionCall(id, call.Run);
|
||||
} else {
|
||||
writeError(id, `Unsupported operation: ${JSON.stringify(call)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
writeError(id, `Processing error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSignal (signal) {
|
||||
if (signal !== 'Reset') {
|
||||
process.stderr.write(`Unhandled signal: ${signal}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// Stream processing setup
|
||||
|
||||
/**
|
||||
* Initializes plugin communication protocol
|
||||
*/
|
||||
function initializePlugin () {
|
||||
// Set up JSON encoding
|
||||
process.stdout.write('\x04json\n');
|
||||
|
||||
// Send handshake message
|
||||
process.stdout.write(JSON.stringify({
|
||||
Hello: {
|
||||
protocol: 'nu-plugin',
|
||||
version: NUSHELL_VERSION,
|
||||
features: []
|
||||
}
|
||||
}) + '\n');
|
||||
|
||||
// Configure input processing
|
||||
let buffer = '';
|
||||
process.stdin.setEncoding('utf8')
|
||||
.on('data', chunk => {
|
||||
buffer += chunk;
|
||||
const messages = buffer.split('\n');
|
||||
buffer = messages.pop() || ''; // Preserve incomplete line
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.trim()) {
|
||||
try {
|
||||
handleInputMessage(JSON.parse(message));
|
||||
} catch (error) {
|
||||
process.stderr.write(`Parse error: ${error.message}\n`);
|
||||
process.stderr.write(`Received: ${message}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('error', error => {
|
||||
process.stderr.write(`Input error: ${error.message}\n`);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
// Main execution
|
||||
if (process.argv.includes('--stdio')) {
|
||||
initializePlugin();
|
||||
} else {
|
||||
console.log('This plugin is intended to be run from within Nushell');
|
||||
process.exit(2);
|
||||
}
|
Loading…
Reference in New Issue
Block a user