Skip to content

Commit a2f3c77

Browse files
committed
rework directory structure
1 parent 37757ca commit a2f3c77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1719
-1513
lines changed

build.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
const { cwd } = require("process");
2+
const ts = require("typescript");
3+
const {
4+
cp,
5+
readdirSync,
6+
exists,
7+
existsSync,
8+
readFileSync,
9+
writeFile,
10+
writeFileSync,
11+
mkdirSync,
12+
realpathSync,
13+
} = require("node:fs");
14+
const nodesSourceDir = cwd() + "/src/nodes";
15+
const nodesTargetDir = cwd() + "/dist/nodes";
16+
const buildTsConfig = JSON.parse(readFileSync("build.tsconfig.json").toString());
17+
18+
function makeDir(dir) {
19+
if (!existsSync(dir)) {
20+
console.log(dir);
21+
const parentDir = realpathSync(`${dir}../`);
22+
console.log(parentDir);
23+
if (!existsSync(parentDir)) {
24+
makeDir(parentDir);
25+
}
26+
27+
mkdirSync(dir);
28+
}
29+
}
30+
31+
/**
32+
* @param node String
33+
* @param sourcePath String
34+
* @param targetPath String
35+
*/
36+
function buildForm(node, sourcePath, targetPath) {
37+
/**
38+
* @type {string[]}
39+
*/
40+
const html = [];
41+
const form = readFileSync(`${sourcePath}/form.html`).toString();
42+
const docs = existsSync(`${sourcePath}/docs.html`) ? readFileSync(`${sourcePath}/docs.html`).toString() : undefined;
43+
44+
const formLines = form.split("\n");
45+
html.push(`<script type="text/html" data-template-name="${node}">`);
46+
formLines.forEach((line) => {
47+
if (line.length > 0) {
48+
html.push(` ${line}`);
49+
}
50+
});
51+
html.push("</script>");
52+
53+
if (docs) {
54+
const docsLines = docs.split("\n");
55+
html.push(`<script type="text/html" data-help-name="${node}">`);
56+
docsLines.forEach((line) => {
57+
if (line.length > 0) {
58+
html.push(` ${line}`);
59+
}
60+
});
61+
html.push("</script>");
62+
}
63+
64+
const initTS = readFileSync(`${sourcePath}/init.ts`).toString();
65+
let initJS = ts
66+
.transpileModule(initTS, buildTsConfig)
67+
.outputText.replace(/export \{(?:[^\}]+)?\};/gim, "")
68+
.replace(/RED\.nodes\.registerType\("([^\"]+)"/i, `RED.nodes.registerType("${node}"`);
69+
70+
const initJSLines = initJS.split("\n");
71+
html.push('<script type="text/javascript">');
72+
initJSLines.forEach((line) => {
73+
if (line.length > 0) {
74+
html.push(` ${line}`);
75+
}
76+
});
77+
html.push("</script>");
78+
79+
html.push("");
80+
81+
mkdirSync(`${targetPath}/`, {
82+
recursive: true,
83+
});
84+
85+
writeFileSync(`${targetPath}/${node}.html`, html.join("\n"));
86+
}
87+
88+
/**
89+
* @param node String
90+
* @param sourcePath String
91+
* @param targetPath String
92+
*/
93+
function copyLocales(node, sourcePath, targetPath) {
94+
if (!existsSync(`${sourcePath}/locales`)) {
95+
return;
96+
}
97+
98+
readdirSync(`${sourcePath}/locales`, {
99+
recursive: false,
100+
})
101+
.filter((language) => {
102+
return language.match(/^[a-z]{2,2}(?:-[A-Z]{2,2})?\.json$/);
103+
})
104+
.forEach((language) => {
105+
const languageCode = language.match(/^([a-z]{2,2}(?:-[A-Z]{2,2})?)\.json$/i)[1];
106+
const content = readFileSync(`${sourcePath}/locales/${language}`).toString();
107+
108+
/**
109+
* @type {string[]}
110+
*/
111+
const json = [];
112+
113+
const contentLines = content.split("\n");
114+
const lastElement = json.push("{");
115+
json.push(` "${node}": ${contentLines[0]}`);
116+
contentLines.splice(1).forEach((line) => {
117+
if (line.length > 0) {
118+
json.push(` ${line}`);
119+
}
120+
});
121+
json.push("}");
122+
123+
mkdirSync(`${targetPath}/locales/${languageCode}`, {
124+
recursive: true,
125+
});
126+
127+
writeFileSync(`${targetPath}/locales/${languageCode}/${node}.json`, json.join("\n"));
128+
});
129+
}
130+
131+
readdirSync(nodesSourceDir, {
132+
recursive: false,
133+
})
134+
.filter((node) => {
135+
const path = `${nodesSourceDir}/${node}`;
136+
137+
return existsSync(`${path}/form.html`) && existsSync(`${path}/${node}.ts`);
138+
})
139+
.forEach((node) => {
140+
const sourcePath = `${nodesSourceDir}/${node}`;
141+
const targetPath = `${nodesTargetDir}/${node}`;
142+
143+
buildForm(node, sourcePath, targetPath);
144+
145+
copyLocales(node, sourcePath, targetPath);
146+
});

build.tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "./tsconfig",
3+
"compilerOptions": {
4+
"module": "ES2022",
5+
"target": "ES2022",
6+
"outDir": "tmp/",
7+
"noEmit": true
8+
},
9+
"include": [
10+
"src/**/init.ts"
11+
],
12+
"exclude": []
13+
}

src/sacn/locales/de/sacn-in.json renamed to dist/nodes/sacn-in/locales/de/sacn-in.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
"output_changes": "Änderungen"
1414
}
1515
}
16-
}
16+
}

src/sacn/locales/en-US/sacn-in.json renamed to dist/nodes/sacn-in/locales/en-US/sacn-in.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
"output_changes": "changes"
1414
}
1515
}
16-
}
16+
}

dist/nodes/sacn-in/sacn-in.html

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<script type="text/html" data-template-name="sacn-in">
2+
<div class="form-row">
3+
<label for="node-input-name">
4+
<i class="fa fa-tag"></i>
5+
<span data-i18n="node-red:common.label.name"></span>
6+
</label>
7+
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name" style="width:70%;"/>
8+
</div>
9+
10+
<div class="form-row">
11+
<label for="node-input-universe">
12+
<i class="fa fa-hashtag"></i>
13+
<span data-i18n="sacn-in.label.universe"></span>
14+
</label>
15+
<input type="text" id="node-input-universe" min="1" max="63999" step="1" style="width:70%;"/>
16+
</div>
17+
<div class="form-row">
18+
<label for="node-input-mode">
19+
<i class="fa fa-compress"></i>
20+
<span data-i18n="sacn-in.label.mode"></span>
21+
</label>
22+
<select id="node-input-mode" style="width:70%;">
23+
<option value="passthrough" selected data-i18n="sacn-in.label.mode_direct"></option>
24+
<option value="htp" data-i18n="sacn-in.label.mode_htp"></option>
25+
<option value="ltp" data-i18n="sacn-in.label.mode_ltp"></option>
26+
</select>
27+
</div>
28+
<div class="form-row">
29+
<label for="node-input-output">
30+
<i class="fa fa-compress"></i>
31+
<span data-i18n="sacn-in.label.output"></span>
32+
</label>
33+
<select id="node-input-output" style="width:70%;">
34+
<option value="full" selected data-i18n="sacn-in.label.output_full"></option>
35+
<option value="changes" data-i18n="sacn-in.label.output_changes"></option>
36+
</select>
37+
</div>
38+
<div class="form-row">
39+
<label for="node-input-interface">
40+
<i class="fa fa-plug"></i>
41+
<span data-i18n="sacn-in.label.interface"></span>
42+
</label>
43+
<input type="text" id="node-input-interface" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" style="width:70%;"/>
44+
</div>
45+
<div class="form-row">
46+
<label for="node-input-port">
47+
<span data-i18n="sacn-in.label.port"></span>
48+
</label>
49+
<input type="number" id="node-input-port" style="width:70%;"/>
50+
</div>
51+
</script>
52+
<script type="text/javascript">
53+
RED.nodes.registerType("sacn-in", {
54+
category: "sACN",
55+
color: "#dcc515",
56+
defaults: {
57+
name: {
58+
value: "Scene-Controller",
59+
},
60+
universe: {
61+
value: 1,
62+
required: true,
63+
},
64+
port: {
65+
value: undefined,
66+
required: false,
67+
},
68+
interface: {
69+
value: "",
70+
required: false,
71+
},
72+
mode: {
73+
value: "htp",
74+
required: true,
75+
},
76+
output: {
77+
value: "full",
78+
required: true,
79+
},
80+
},
81+
inputs: 0,
82+
outputs: 1,
83+
paletteLabel: "sACN in",
84+
icon: "font-awesome/fa-lightbulb-o",
85+
label: function () {
86+
return this.name || "sACN";
87+
},
88+
labelStyle: function () {
89+
return this.name ? "node_label_italic" : "";
90+
},
91+
});
92+
</script>

dist/nodes/sacn-in/sacn-in.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
const sacn_1 = require("sacn");
4+
class NodeHandler {
5+
node;
6+
config;
7+
data = new Map();
8+
sACN;
9+
constructor(node, config) {
10+
this.node = node;
11+
this.config = config;
12+
const options = {
13+
universes: [config.universe],
14+
reuseAddr: config.reuseAddress !== undefined ? config.reuseAddress : true,
15+
};
16+
if (config.interface !== undefined && config.interface.length > 7) {
17+
options.iface = config.interface;
18+
}
19+
if (config.port !== undefined && config.port > 0) {
20+
options.port = config.port;
21+
}
22+
else {
23+
options.port = 5568;
24+
}
25+
switch (config.mode) {
26+
case "passthrough":
27+
this.sACN = new sacn_1.Receiver(options);
28+
break;
29+
case "htp":
30+
case "ltp":
31+
options.mode = config.mode.toUpperCase();
32+
this.sACN = new sacn_1.unstable_MergingReceiver(options);
33+
break;
34+
default:
35+
throw new Error("[node-red-sacn] None or invalid mode selected.");
36+
}
37+
this.node.on("close", () => {
38+
this.sACN.close();
39+
this.data = new Map();
40+
});
41+
if (config.mode === "passthrough") {
42+
this.sACN.on("packet", (packet) => {
43+
this.node.send({
44+
universe: packet.universe,
45+
payload: this.parsePayload(packet.payload, packet.universe),
46+
sequence: packet.sequence,
47+
source: packet.sourceAddress,
48+
priority: packet.priority,
49+
});
50+
});
51+
}
52+
else if (config.mode === "ltp") {
53+
sACN.on("changed", (data) => {
54+
this.node.send({
55+
universe: data.universe,
56+
payload: parsePayload(data.payload, data.universe),
57+
});
58+
});
59+
}
60+
else if (config.mode === "htp") {
61+
sACN.on("changed", (data) => {
62+
this.node.send({
63+
universe: data.universe,
64+
payload: parsePayload(data.payload, data.universe),
65+
});
66+
});
67+
}
68+
}
69+
getNulledUniverse() {
70+
const universe = {};
71+
for (let ch = 1; ch <= 512; ch++) {
72+
universe[ch] = 0;
73+
}
74+
return universe;
75+
}
76+
getReference(universe) {
77+
if (this.config.output === "changes") {
78+
return {};
79+
}
80+
return this.data?.get(universe) ?? this.getNulledUniverse();
81+
}
82+
parsePayload(payload, universe) {
83+
const processedPayload = this.getReference(universe);
84+
Object.keys(payload).forEach((key) => {
85+
const ch = parseInt(key, 10);
86+
processedPayload[ch + 1] = payload[ch];
87+
});
88+
if (this.config.output !== "changes") {
89+
this.data?.set(universe, processedPayload);
90+
}
91+
return processedPayload;
92+
}
93+
}
94+
exports.default = (RED) => {
95+
RED.nodes.registerType("sacn-in", function (config) {
96+
RED.nodes.createNode(this, config);
97+
new NodeHandler(this, config);
98+
});
99+
};

src/sacn/locales/de/sacn-out.json renamed to dist/nodes/sacn-out/locales/de/sacn-out.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
"universe": "Universum"
1616
}
1717
}
18-
}
18+
}

src/sacn/locales/en-US/sacn-out.json renamed to dist/nodes/sacn-out/locales/en-US/sacn-out.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
"universe": "universe"
1616
}
1717
}
18-
}
18+
}

0 commit comments

Comments
 (0)