mordorjs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +76 -0
- package/bin/mordorjs.cjs +10556 -0
- package/mordor.js +253 -0
- package/package.json +50 -0
package/mordor.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/** MordorJS - the world's first real 1W (1-character-wide) programming language. Transforms any JS file into a vertical, executable format where each character occupies its own line. */
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
/** @returns {string} Verticalized `code-begin.js` (base64-embedded), opens the T template literal and defines single-char variable bindings. */
|
|
9
|
+
function codeBegin() {
|
|
10
|
+
return Buffer.from('WwosCmMKLAosCmUKLAosCmYKLAosCmgKLAosCmkKLAosCmoKLAosCmwKLAosCm0KLAosCm4KLAosCm8KLAosCnAKLAosCnIKLAosCnMKLAosCnQKLAosCnUKLAosCmEKLAosCmQKLAosCmcKLAosClMKLAosCkMKLAosClgKLAosClkKLAosCloKLAosCkQKXQo9CmAKYwplCmYKaAppCmoKbAptCm4KbwpwCnIKcwp0CnUKYQpkCmcKUwpDCigKKQogCi4KYAo7CkEKPQo5CisKMQo7Ck8KPQpzCisKbworCnIKKwp0CjsKSwo9CmMKKwpvCisKbgorCnMKKwp0CisKcgorCnUKKwpjCisKdAorCm8KKwpyCjsKUAo9CnMKKwpwCisKbAorCmkKKwp0CjsKSgo9CmoKKwpvCisKaQorCm4KOwpGCj0KWwpdClsKTwpdClsKSwpdCjsKUgo9CnIKKwplCisKdAorCnUKKwpyCisKbgorCloKKwpTCisKdAorCnIKKwppCisKbgorCmcKKwpECisKZgorCnIKKwpvCisKbQorCkMKKwpoCisKYQorCnIKKwpDCisKbworCmQKKwplCisKWAo7CkUKPQpbCl0KKwpbCl0KOwpOCj0KYApgCjsKdwo9CkYKKApSCisKKApBCioKMwotCjEKKQorClkKKQooCikKOwpCCj0KRgooClIKKwpBCioKMworClkKKQooCikKOwpRCj0KRgooClIKKwooCkEKKgozCisKMQopCisKWQopCigKKQo7ClQKPQpgCg==','base64').toString();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @returns {string} Verticalized `code-end.js` (base64-embedded), closes T, decodes the source and evaluates it via `new Function`. */
|
|
14
|
+
function codeEnd() {
|
|
15
|
+
return Buffer.from('YAo7CkcKPQpGCigKUgorCkEKKwpZCikKKAopCjsKTQo9ClQKWwpQCl0KKApHCikKWwpKCl0KKApFCikKOwpWCj0KRgooClIKKwooCkEKKgo5CisKNgopCisKWQopCigKKQo7ClcKPQpGCigKUgorCigKQQoqCjkKKwoyCikKKwpZCikKKAopCjsKTQo9Ck0KWwpQCl0KKApRCikKWwpKCl0KKApWCikKWwpQCl0KKAp3CikKWwpKCl0KKApXCikKWwpQCl0KKApCCikKWwpKCl0KKApHCikKOwpGCigKTQopCigKKQo7Cg==','base64').toString();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Encoding sentinel characters (U+001D, U+001E, U+001F) used to safely represent `\`, `\n`, and `` ` `` inside the 1W template literal. */
|
|
19
|
+
const [ALIAS_BACKSLASH, ALIAS_NEWLINE, ALIAS_BACKTICK] = `\x1d\x1e\x1f`;
|
|
20
|
+
|
|
21
|
+
/** @param {string} text - Copies text to the system clipboard (macOS/Windows/Linux). */
|
|
22
|
+
function copyToClipboard(text) {
|
|
23
|
+
try {
|
|
24
|
+
if (process.platform === 'darwin') {
|
|
25
|
+
spawnSync('pbcopy', { input: text });
|
|
26
|
+
} else if (process.platform === 'win32') {
|
|
27
|
+
spawnSync('clip', { input: text });
|
|
28
|
+
} else {
|
|
29
|
+
spawnSync('xclip', ['-selection', 'clipboard'], { input: text });
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error(`Warning: Failed to copy to clipboard: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @param {string} str - Splits a string into pseudo-words of random length (2–7 chars) joined by spaces. @returns {string} */
|
|
37
|
+
function virtualizeWords(str) {
|
|
38
|
+
let words = [];
|
|
39
|
+
let i = 0;
|
|
40
|
+
while (i < str.length) {
|
|
41
|
+
const len = Math.floor(Math.random() * 6) + 2;
|
|
42
|
+
words.push(str.substring(i, i + len));
|
|
43
|
+
i += len;
|
|
44
|
+
}
|
|
45
|
+
return words.join(' ');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @param {string} text @param {number} targetStripCount - Distributes words into N balanced vertical strips. @returns {string[]} */
|
|
49
|
+
function wrapToStrips(text, targetStripCount) {
|
|
50
|
+
const words = text.replace(/\t/g, ' ').split(/\s+/).filter(w => w.length > 0);
|
|
51
|
+
|
|
52
|
+
if (words.length === 0) return Array(targetStripCount).fill('');
|
|
53
|
+
if (targetStripCount <= 1) return [words.join(' ')];
|
|
54
|
+
|
|
55
|
+
const totalContentLength = words.reduce((acc, w) => acc + w.length, 0) + words.length - 1;
|
|
56
|
+
|
|
57
|
+
const strips = [];
|
|
58
|
+
let wordIndex = 0;
|
|
59
|
+
let remainingContentLength = totalContentLength;
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < targetStripCount; i++) {
|
|
62
|
+
const remainingStrips = targetStripCount - i;
|
|
63
|
+
const targetForThisStrip = remainingContentLength / remainingStrips;
|
|
64
|
+
|
|
65
|
+
let currentStripWords = [];
|
|
66
|
+
let currentStripLength = 0;
|
|
67
|
+
|
|
68
|
+
while (wordIndex < words.length) {
|
|
69
|
+
const word = words[wordIndex];
|
|
70
|
+
const wordLenWithSpace = (currentStripWords.length > 0 ? 1 : 0) + word.length;
|
|
71
|
+
|
|
72
|
+
if (remainingStrips === 1 ||
|
|
73
|
+
currentStripLength === 0 ||
|
|
74
|
+
Math.abs(currentStripLength + wordLenWithSpace - targetForThisStrip) < Math.abs(currentStripLength - targetForThisStrip)) {
|
|
75
|
+
|
|
76
|
+
currentStripWords.push(word);
|
|
77
|
+
currentStripLength += wordLenWithSpace;
|
|
78
|
+
wordIndex++;
|
|
79
|
+
remainingContentLength -= wordLenWithSpace;
|
|
80
|
+
} else {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
strips.push(currentStripWords.join(' '));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return strips;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** @param {string} strip @param {number} randomMax - Applies random leading and inter-word spacing up to `randomMax` extra spaces. @returns {string} */
|
|
91
|
+
function applyRandomSpacing(strip, randomMax) {
|
|
92
|
+
const words = strip.split(' ').filter(w => w.length > 0);
|
|
93
|
+
if (words.length === 0) return strip;
|
|
94
|
+
|
|
95
|
+
let result = ' '.repeat(Math.floor(Math.random() * (randomMax + 1)));
|
|
96
|
+
for (let i = 0; i < words.length; i++) {
|
|
97
|
+
result += words[i];
|
|
98
|
+
if (i < words.length - 1) {
|
|
99
|
+
result += ' '.repeat(1 + Math.floor(Math.random() * (randomMax + 1)));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @param {string} text @param {number|null} stripCount @param {boolean|null} addSeparator @param {number|null} randomMax @param {boolean|null} isBase64 - Renders text in vertical (1-char-per-row) or multi-strip format. @returns {string} */
|
|
106
|
+
function verticalize(text, stripCount=null, addSeparator=null, randomMax=null, isBase64=null) {
|
|
107
|
+
const cleanText = text.replace(/\t/g, ' ');
|
|
108
|
+
let processedText = cleanText;
|
|
109
|
+
|
|
110
|
+
if (isBase64) {
|
|
111
|
+
processedText = Buffer.from(cleanText).toString('base64');
|
|
112
|
+
processedText = virtualizeWords(processedText);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let strips = stripCount ? wrapToStrips(processedText, stripCount) : processedText.split('\n');
|
|
116
|
+
|
|
117
|
+
if (randomMax !== null) {
|
|
118
|
+
strips = strips.map(strip => applyRandomSpacing(strip, randomMax));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const maxLength = Math.max(...strips.map(s => s.length));
|
|
122
|
+
let output = '';
|
|
123
|
+
for (let i = 0; i < maxLength; i++) {
|
|
124
|
+
let row = '';
|
|
125
|
+
for (let j = 0; j < strips.length; j++) {
|
|
126
|
+
row += (strips[j][i] || ' ');
|
|
127
|
+
if (addSeparator && j < strips.length - 1) row += ' ';
|
|
128
|
+
}
|
|
129
|
+
output += row.trimEnd() + '\n';
|
|
130
|
+
}
|
|
131
|
+
return output;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @param {string} text - Puts each character on its own line (strips all existing newlines first). @returns {string} */
|
|
135
|
+
function verticalizeRaw(text) {
|
|
136
|
+
return text
|
|
137
|
+
.replace(/\t/g, ' ')
|
|
138
|
+
.replace(/\r/g, '')
|
|
139
|
+
.replace(/\n/g, '')
|
|
140
|
+
.split('')
|
|
141
|
+
.join('\n') + '\n';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @param {string} jsCode - Encodes and verticalizes JS source into a self-executing 1W MordorJS file. Detects shebang and `require` usage to build the minimal non-1W prefix. @returns {string} */
|
|
145
|
+
function generateMordorJS(jsCode) {
|
|
146
|
+
const startVert = codeBegin();
|
|
147
|
+
const endVert = codeEnd();
|
|
148
|
+
|
|
149
|
+
const hasShebang = jsCode.startsWith('#!');
|
|
150
|
+
const strippedCode = jsCode
|
|
151
|
+
.replace(/^#!.*\n/, '')
|
|
152
|
+
.replace(/\r/g, '');
|
|
153
|
+
|
|
154
|
+
const hasRequire = /\brequire\s*\(/.test(strippedCode);
|
|
155
|
+
|
|
156
|
+
const escapedCode = strippedCode
|
|
157
|
+
.replace(/\\/g, '\x1d')
|
|
158
|
+
.replace(/\n/g, '\x1e')
|
|
159
|
+
.replace(/`/g, '\x1f')
|
|
160
|
+
;
|
|
161
|
+
|
|
162
|
+
const verticalizedCode = verticalizeRaw(escapedCode);
|
|
163
|
+
|
|
164
|
+
const prefix = [
|
|
165
|
+
hasShebang ? '#!/usr/bin/env node' : '',
|
|
166
|
+
hasRequire ? 'global.require=require;' : '',
|
|
167
|
+
].filter(Boolean).join('\n') + (hasShebang || hasRequire ? '\n' : '');
|
|
168
|
+
|
|
169
|
+
return prefix + startVert + verticalizedCode + endVert;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** CLI entry point — parses argv and dispatches to `verticalize` or `generateMordorJS`. */
|
|
173
|
+
function main() {
|
|
174
|
+
const args = process.argv.slice(2);
|
|
175
|
+
let stripCount = null;
|
|
176
|
+
let addSeparator = false;
|
|
177
|
+
let shouldCopy = false;
|
|
178
|
+
let randomMax = null;
|
|
179
|
+
let isBase64 = false;
|
|
180
|
+
let isMordorJS = false;
|
|
181
|
+
let filePath = null;
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < args.length; i++) {
|
|
184
|
+
if (args[i] === '-w' || args[i] === '--wide') {
|
|
185
|
+
stripCount = parseInt(args[i + 1], 10);
|
|
186
|
+
i++;
|
|
187
|
+
} else if (args[i] === '-s') {
|
|
188
|
+
addSeparator = true;
|
|
189
|
+
} else if (args[i] === '-c' || args[i] === '--copy') {
|
|
190
|
+
shouldCopy = true;
|
|
191
|
+
} else if (args[i] === '-b64' || args[i] === '--base64') {
|
|
192
|
+
isBase64 = true;
|
|
193
|
+
} else if (args[i] === '-mjs' || args[i] === '--mordorjs') {
|
|
194
|
+
isMordorJS = true;
|
|
195
|
+
} else if (args[i] === '-r' || args[i] === '--random') {
|
|
196
|
+
const nextArg = args[i + 1];
|
|
197
|
+
if (nextArg && !isNaN(parseInt(nextArg, 10)) && !nextArg.startsWith('-')) {
|
|
198
|
+
randomMax = parseInt(nextArg, 10);
|
|
199
|
+
i++;
|
|
200
|
+
} else {
|
|
201
|
+
randomMax = 3;
|
|
202
|
+
}
|
|
203
|
+
} else if (!filePath && !args[i].startsWith('-')) {
|
|
204
|
+
filePath = args[i];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const processResult = (text) => {
|
|
209
|
+
let result;
|
|
210
|
+
if (isMordorJS) {
|
|
211
|
+
result = generateMordorJS(text);
|
|
212
|
+
} else {
|
|
213
|
+
result = verticalize(text, stripCount, addSeparator, randomMax, isBase64);
|
|
214
|
+
}
|
|
215
|
+
process.stdout.write(result);
|
|
216
|
+
if (shouldCopy) {
|
|
217
|
+
copyToClipboard(result);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (filePath) {
|
|
222
|
+
try {
|
|
223
|
+
const data = fs.readFileSync(filePath, 'utf8');
|
|
224
|
+
processResult(data);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error(`Error reading file: ${err.message}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
} else if (!process.stdin.isTTY) {
|
|
230
|
+
let data = '';
|
|
231
|
+
process.stdin.on('data', chunk => {
|
|
232
|
+
data += chunk;
|
|
233
|
+
});
|
|
234
|
+
process.stdin.on('end', () => {
|
|
235
|
+
processResult(data);
|
|
236
|
+
});
|
|
237
|
+
} else {
|
|
238
|
+
console.error("Warning: no input provided. Pass a file or pipe text via stdin.");
|
|
239
|
+
console.log("Usage: node mordor.js [-w|--wide number] [-s] [-c|--copy] [-r|--random [number]] [-b64|--base64] [-mjs] [file]");
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function verticalizeRaw(text) {
|
|
245
|
+
return text
|
|
246
|
+
.replace(/\t/g, ' ')
|
|
247
|
+
.replace(/\r/g, '')
|
|
248
|
+
.replace(/\n/g, '')
|
|
249
|
+
.split('')
|
|
250
|
+
.join('\n') + '\n';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mordorjs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "is a toolkit for first real 1D programming language and so much more",
|
|
5
|
+
"main": "mordor.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mordorjs": "bin/mordorjs.cjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"mordor.js",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "mkdir -p bin && node mordor.js mordor.js --mordorjs > bin/mordorjs.cjs && chmod +x bin/mordorjs.cjs",
|
|
17
|
+
"test": "node mordor.js mordor.js -w 61 -s -c -r 4"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Pengeszikra/mordorjs.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"vertical",
|
|
25
|
+
"obfuscation",
|
|
26
|
+
"mordor",
|
|
27
|
+
"column",
|
|
28
|
+
"cli",
|
|
29
|
+
"javascript",
|
|
30
|
+
"1d",
|
|
31
|
+
"one-dimensional",
|
|
32
|
+
"esolang",
|
|
33
|
+
"SAURON",
|
|
34
|
+
"Survivor",
|
|
35
|
+
"Agentic",
|
|
36
|
+
"Unit",
|
|
37
|
+
"Resistance",
|
|
38
|
+
"Operations",
|
|
39
|
+
"Network"
|
|
40
|
+
],
|
|
41
|
+
"author": "Peter Vivo",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/Pengeszikra/mordorjs/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/Pengeszikra/mordorjs#readme",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|