Skip to content

Commit 169dbba

Browse files
committed
feat(templates): automate JSDoc typing for linked config nodes
1 parent 81eaa0a commit 169dbba

2 files changed

Lines changed: 30 additions & 12 deletions

File tree

packages/node-red-builder/src/cli/utils.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,23 @@ export async function linkPackage() {
129129
} catch {}
130130
}
131131

132+
/**
133+
* Computes the class name for a node based on its name and type.
134+
*
135+
* @param {string} nodeName
136+
* @param {'node'|'config'} type
137+
* @returns {string}
138+
*/
139+
function getNodeClassName(nodeName, type) {
140+
const pascalName = toPascalCase(nodeName);
141+
let suffix = 'Node';
142+
if (type === 'config') {
143+
if (!pascalName.endsWith('Config'))
144+
suffix = 'Config' + suffix;
145+
}
146+
return pascalName + suffix;
147+
}
148+
132149
/**
133150
* Generates a new node (or config node) from templates.
134151
*
@@ -137,25 +154,23 @@ export async function linkPackage() {
137154
* @param {string} options.prefix - The project prefix (e.g. "custom").
138155
* @param {string} options.color - The node color.
139156
* @param {'node'|'config'} [options.type='node'] - Type of node to generate.
140-
* @param {string} [options.linkedConfigNode] - Name of the config node to link to (if any).
157+
* @param {string} [options.configNode] - Name of the config node to link to (if any).
141158
*/
142-
export async function generateNode({ nodeName, prefix, color, type = 'node', linkedConfigNode }) {
159+
export async function generateNode({ nodeName, prefix, color, type = 'node', configNode }) {
143160
const isConfigNode = type === 'config';
144-
const pascalName = toPascalCase(nodeName);
145-
146-
let suffix = 'Node';
147-
if (isConfigNode) {
148-
if (!pascalName.endsWith('Config'))
149-
suffix = 'Config' + suffix;
150-
}
151-
const nodeClass = pascalName + suffix;
161+
const nodeClass = getNodeClassName(nodeName, type);
152162

153163
let configEntry = '';
154164
let configRow = '';
165+
let configImport = '';
155166
let propsType = "Omit<BaseNodeProps, 'config'>";
167+
let baseNodeExtends = '/** @extends {BaseNode<NodeProps>} */';
156168

157-
if (linkedConfigNode) {
158-
configEntry = `config: { value: '', type: '${prefix}-${linkedConfigNode}', required: true },`;
169+
if (configNode) {
170+
const configNodeClass = getNodeClassName(configNode, 'config');
171+
baseNodeExtends = `/** @extends {BaseNode<NodeProps, ${configNodeClass}>} */`;
172+
configImport = `/** @import { ${configNodeClass} } from '../${configNode}/runtime.js' */`;
173+
configEntry = `config: { value: '', type: '${prefix}-${configNode}', required: true },`;
159174
configRow = [
160175
'<div class="form-row">',
161176
'\t<label for="node-input-config">',
@@ -173,8 +188,10 @@ export async function generateNode({ nodeName, prefix, color, type = 'node', lin
173188
'__NODE_NAME__': nodeName,
174189
'__NODE_CLASS__': nodeClass,
175190
'__COLOR__': color,
191+
'// __CONFIG_IMPORT__': configImport,
176192
'// __CONFIG_ENTRY__': configEntry,
177193
'<!-- __CONFIG_ROW__ -->': configRow,
194+
'/** @extends {BaseNode<NodeProps>} */': baseNodeExtends,
178195
"Omit<BaseNodeProps, 'config'>": propsType
179196
};
180197

packages/node-red-builder/templates/src/nodes/node/runtime.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BaseNode, registerNode } from 'node-red-builder';
22
/** @import { NodeMessage, NodeAPI } from 'node-red' */
33
/** @import { NodeProps as BaseNodeProps } from 'node-red-builder' */
4+
// __CONFIG_IMPORT__
45

56
/** @typedef {(typeof ACTION)[keyof typeof ACTION]} Action */
67
export const ACTION = /** @type {const} */ ({

0 commit comments

Comments
 (0)