Repository navigation
Expand file tree
/
Copy pathmordor.js
More file actions
executable file
·367 lines (320 loc) · 15.3 KB
/
Copy pathmordor.js
File metadata and controls
executable file
·367 lines (320 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#!/usr/bin/env node
/** 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. */
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
/** @returns {string} Verticalized `code-begin.js` (base64-embedded), opens the T template literal and defines single-char variable bindings. */
function codeBegin() {
return Buffer.from('WwosCmMKLAosCmUKLAosCmYKLAosCmgKLAosCmkKLAosCmoKLAosCmwKLAosCm0KLAosCm4KLAosCm8KLAosCnAKLAosCnIKLAosCnMKLAosCnQKLAosCnUKLAosCmEKLAosCmQKLAosCmcKLAosClMKLAosCkMKLAosClgKLAosClkKLAosCloKLAosCkQKXQo9CmAKYwplCmYKaAppCmoKbAptCm4KbwpwCnIKcwp0CnUKYQpkCmcKUwpDCigKKQogCi4KYAo7CkEKPQo5CisKMQo7Ck8KPQpzCisKbworCnIKKwp0CjsKSwo9CmMKKwpvCisKbgorCnMKKwp0CisKcgorCnUKKwpjCisKdAorCm8KKwpyCjsKUAo9CnMKKwpwCisKbAorCmkKKwp0CjsKSgo9CmoKKwpvCisKaQorCm4KOwpGCj0KWwpdClsKTwpdClsKSwpdCjsKUgo9CnIKKwplCisKdAorCnUKKwpyCisKbgorCloKKwpTCisKdAorCnIKKwppCisKbgorCmcKKwpECisKZgorCnIKKwpvCisKbQorCkMKKwpoCisKYQorCnIKKwpDCisKbworCmQKKwplCisKWAo7CkUKPQpbCl0KKwpbCl0KOwpOCj0KYApgCjsKdwo9CkYKKApSCisKKApBCioKMwotCjEKKQorClkKKQooCikKOwpCCj0KRgooClIKKwpBCioKMworClkKKQooCikKOwpRCj0KRgooClIKKwooCkEKKgozCisKMQopCisKWQopCigKKQo7ClQKPQpgCg==','base64').toString();
}
/** @returns {string} Verticalized `code-end.js` (base64-embedded), closes T, decodes the source and evaluates it via `new Function`. */
function codeEnd() {
return Buffer.from('YAo7CkcKPQpGCigKUgorCkEKKwpZCikKKAopCjsKTQo9ClQKWwpQCl0KKApHCikKWwpKCl0KKApFCikKOwpWCj0KRgooClIKKwooCkEKKgo5CisKNgopCisKWQopCigKKQo7ClcKPQpGCigKUgorCigKQQoqCjkKKwoyCikKKwpZCikKKAopCjsKTQo9Ck0KWwpQCl0KKApRCikKWwpKCl0KKApWCikKWwpQCl0KKAp3CikKWwpKCl0KKApXCikKWwpQCl0KKApCCikKWwpKCl0KKApHCikKOwpGCigKTQopCigKKQo7Cg==','base64').toString();
}
/** Encoding sentinel characters (U+001D, U+001E, U+001F) used to safely represent `\`, `\n`, and `` ` `` inside the 1W template literal. */
const [ALIAS_BACKSLASH, ALIAS_NEWLINE, ALIAS_BACKTICK] = `\x1d\x1e\x1f`;
/** @param {string} text - Copies text to the system clipboard (macOS/Windows/Linux). */
function copyToClipboard(text) {
try {
if (process.platform === 'darwin') {
spawnSync('pbcopy', { input: text });
} else if (process.platform === 'win32') {
spawnSync('clip', { input: text });
} else {
spawnSync('xclip', ['-selection', 'clipboard'], { input: text });
}
} catch (err) {
console.error(`Warning: Failed to copy to clipboard: ${err.message}`);
}
}
/** @param {string} str - Splits a string into pseudo-words of random length (2–7 chars) joined by spaces. @returns {string} */
function virtualizeWords(str) {
let words = [];
let i = 0;
while (i < str.length) {
const len = Math.floor(Math.random() * 6) + 2;
words.push(str.substring(i, i + len));
i += len;
}
return words.join(' ');
}
/** @param {string} text @param {number} targetStripCount - Distributes words into N balanced vertical strips. @returns {string[]} */
function wrapToStrips(text, targetStripCount) {
const words = text.replace(/\t/g, ' ').split(/\s+/).filter(w => w.length > 0);
if (words.length === 0) return Array(targetStripCount).fill('');
if (targetStripCount <= 1) return [words.join(' ')];
const totalContentLength = words.reduce((acc, w) => acc + w.length, 0) + words.length - 1;
const strips = [];
let wordIndex = 0;
let remainingContentLength = totalContentLength;
for (let i = 0; i < targetStripCount; i++) {
const remainingStrips = targetStripCount - i;
const targetForThisStrip = remainingContentLength / remainingStrips;
let currentStripWords = [];
let currentStripLength = 0;
while (wordIndex < words.length) {
const word = words[wordIndex];
const wordLenWithSpace = (currentStripWords.length > 0 ? 1 : 0) + word.length;
if (remainingStrips === 1 ||
currentStripLength === 0 ||
Math.abs(currentStripLength + wordLenWithSpace - targetForThisStrip) < Math.abs(currentStripLength - targetForThisStrip)) {
currentStripWords.push(word);
currentStripLength += wordLenWithSpace;
wordIndex++;
remainingContentLength -= wordLenWithSpace;
} else {
break;
}
}
strips.push(currentStripWords.join(' '));
}
return strips;
}
/** @param {string} strip @param {number} randomMax - Applies random leading and inter-word spacing up to `randomMax` extra spaces. @returns {string} */
function applyRandomSpacing(strip, randomMax) {
const words = strip.split(' ').filter(w => w.length > 0);
if (words.length === 0) return strip;
let result = ' '.repeat(Math.floor(Math.random() * (randomMax + 1)));
for (let i = 0; i < words.length; i++) {
result += words[i];
if (i < words.length - 1) {
result += ' '.repeat(1 + Math.floor(Math.random() * (randomMax + 1)));
}
}
return result;
}
/** @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} */
function verticalize(text, stripCount=null, addSeparator=null, randomMax=null, isBase64=null) {
const cleanText = text.replace(/\t/g, ' ');
let processedText = cleanText;
if (isBase64) {
processedText = Buffer.from(cleanText).toString('base64');
processedText = virtualizeWords(processedText);
}
let strips = stripCount ? wrapToStrips(processedText, stripCount) : processedText.split('\n');
if (randomMax !== null) {
strips = strips.map(strip => applyRandomSpacing(strip, randomMax));
}
const maxLength = Math.max(...strips.map(s => s.length));
let output = '';
for (let i = 0; i < maxLength; i++) {
let row = '';
for (let j = 0; j < strips.length; j++) {
row += (strips[j][i] || ' ');
if (addSeparator && j < strips.length - 1) row += ' ';
}
output += row.trimEnd() + '\n';
}
return output;
}
/** @param {string} text - Puts each character on its own line (strips all existing newlines first). @returns {string} */
function verticalizeRaw(text) {
return text
.replace(/\t/g, ' ')
.replace(/\r/g, '')
.replace(/\n/g, '')
.split('')
.join('\n') + '\n';
}
/** @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} */
function generateMordorJS(jsCode) {
const startVert = codeBegin();
const endVert = codeEnd();
const hasShebang = jsCode.startsWith('#!');
const strippedCode = jsCode
.replace(/^#!.*\n/, '')
.replace(/\r/g, '');
const hasRequire = /\brequire\s*\(/.test(strippedCode);
const escapedCode = strippedCode
.replace(/\\/g, '\x1d')
.replace(/\n/g, '\x1e')
.replace(/`/g, '\x1f')
;
const verticalizedCode = verticalizeRaw(escapedCode);
const prefix = [
hasShebang ? '#!/usr/bin/env node' : '',
hasRequire ? 'global.require=require;' : '',
].filter(Boolean).join('\n') + (hasShebang || hasRequire ? '\n' : '');
return prefix + startVert + verticalizedCode + endVert;
}
/** @param {string} rootDir - Recursively walks a directory tree, returns sorted absolute file paths. Skips `node_modules`, hidden files/dirs, and lock files. @returns {string[]} */
function walkProjectTree(rootDir) {
const SKIP_DIRS = new Set(['node_modules', '.git', '.svn', '.hg', '.cache', '.next', '.nuxt', 'dist', 'build', 'coverage']);
const SKIP_FILES = new Set(['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', 'composer.lock', 'Gemfile.lock', 'poetry.lock']);
const results = [];
function walk(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
entries.sort((a, b) => a.name.localeCompare(b.name));
for (const entry of entries) {
if (entry.name.startsWith('.')) continue;
if (SKIP_DIRS.has(entry.name)) continue;
if (SKIP_FILES.has(entry.name)) continue;
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() || (entry.isSymbolicLink() && fs.statSync(fullPath).isDirectory())) {
walk(fullPath);
} else if (entry.isFile() || entry.isSymbolicLink()) {
results.push(fullPath);
}
}
}
walk(rootDir);
return results;
}
const BINARY_EXTENSIONS = new Set([
'.png','.jpg','.jpeg','.gif','.ico','.webp','.bmp','.tiff',
'.pdf','.zip','.tar','.gz','.bz2','.7z','.rar',
'.woff','.woff2','.ttf','.otf','.eot',
'.mp3','.mp4','.avi','.mov','.wav','.ogg','.webm',
'.exe','.dll','.so','.dylib','.bin','.class','.pyc',
]);
/** @param {string} filePath - Returns true if the file extension is a known binary type. @returns {boolean} */
function isBinaryExtension(filePath) {
return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
}
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png']);
const IMAGE_PREVIEW_BYTES = 1024;
/** @param {string} filePath - Returns true if the file is an image that should be truncated to 1KB preview. @returns {boolean} */
function isPreviewImage(filePath) {
return IMAGE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
}
/** @param {string} reversedPath @param {number} targetWidth - Generates a mordor-project header padded to targetWidth with underscores. @returns {string} */
function makeProjectHeader(reversedPath, targetWidth = 61) {
const available = targetWidth - reversedPath.length - 2;
const left = Math.max(4, Math.ceil(available / 2));
const right = Math.max(4, available - left);
return '_'.repeat(left) + ' ' + reversedPath + ' ' + '_'.repeat(right);
}
const HELP_HEADER = '==== M||D||JS ====\n';
const HELP_BODY = `
Usage: mordorjs [options] [file]
Options:
-w, --wide <num> Wrap output into N vertical columns
-s Add separator spaces between columns
-r, --random [num] Add random spacing between words (default max: 3)
-b64, --base64 Base64-encode input before rendering
-c, --copy Copy output to system clipboard
-mjs, --mordorjs Convert JS file to self-executing 1W .cjs program
-pro, --project Package entire folder tree into a mordor-project file
(skips node_modules, hidden files, lock files;
images truncated to 1KB preview)
-h, --help Show this help in mordor-code format (-w 31 -r 3 -s)
-ai-h, --ai-help Show this help in plain text
`;
/** CLI entry point — parses argv and dispatches to `verticalize`, `generateMordorJS`, or `buildMordorProject`. */
function main() {
const args = process.argv.slice(2);
let stripCount = null;
let addSeparator = false;
let shouldCopy = false;
let randomMax = null;
let isBase64 = false;
let isMordorJS = false;
let isProject = false;
let filePath = null;
for (let i = 0; i < args.length; i++) {
if (args[i] === '-h' || args[i] === '--help') {
process.stdout.write(HELP_HEADER + verticalize(HELP_BODY, 31, true, 3));
process.exit(0);
} else if (args[i] === '-ai-h' || args[i] === '--ai-help') {
process.stdout.write(HELP_HEADER + HELP_BODY);
process.exit(0);
} else if (args[i] === '-w' || args[i] === '--wide') {
stripCount = parseInt(args[i + 1], 10);
i++;
} else if (args[i] === '-s') {
addSeparator = true;
} else if (args[i] === '-c' || args[i] === '--copy') {
shouldCopy = true;
} else if (args[i] === '-b64' || args[i] === '--base64') {
isBase64 = true;
} else if (args[i] === '-mjs' || args[i] === '--mordorjs') {
isMordorJS = true;
} else if (args[i] === '-pro' || args[i] === '--project') {
isProject = true;
} else if (args[i] === '-r' || args[i] === '--random') {
const nextArg = args[i + 1];
if (nextArg && !isNaN(parseInt(nextArg, 10)) && !nextArg.startsWith('-')) {
randomMax = parseInt(nextArg, 10);
i++;
} else {
randomMax = 3;
}
} else if (!filePath && !args[i].startsWith('-')) {
filePath = args[i];
}
}
const processResult = (text) => {
let result;
if (isMordorJS) {
result = generateMordorJS(text);
} else {
result = verticalize(text, stripCount, addSeparator, randomMax, isBase64);
}
process.stdout.write(result);
if (shouldCopy) {
copyToClipboard(result);
}
};
if (isProject) {
const rootDir = filePath || '.';
try {
const absRoot = path.resolve(rootDir);
const files = walkProjectTree(absRoot);
const strips = stripCount || 31;
const targetWidth = strips * 2 - 1;
let clipboard = shouldCopy ? '' : null;
for (const fp of files) {
const relativePath = path.relative(absRoot, fp).replace(/\\/g, '/');
const reversedPath = relativePath.split('').reverse().join('');
const header = makeProjectHeader(reversedPath, targetWidth) + '\n';
let content;
if (isPreviewImage(fp)) {
const buf = fs.readFileSync(fp).slice(0, IMAGE_PREVIEW_BYTES);
content = virtualizeWords(buf.toString('base64'));
} else if (isBinaryExtension(fp)) {
const b64 = fs.readFileSync(fp).toString('base64');
content = virtualizeWords(b64);
} else {
content = fs.readFileSync(fp, 'utf8');
}
const payload = verticalize(content, strips, addSeparator, randomMax, false);
const entry = header + payload;
process.stdout.write(entry);
if (clipboard !== null) clipboard += entry;
}
if (clipboard) copyToClipboard(clipboard);
} catch (err) {
console.error(`Error building project: ${err.message}`);
process.exit(1);
}
} else if (filePath) {
try {
const data = fs.readFileSync(filePath, 'utf8');
processResult(data);
} catch (err) {
console.error(`Error reading file: ${err.message}`);
process.exit(1);
}
} else if (!process.stdin.isTTY) {
let data = '';
process.stdin.on('data', chunk => {
data += chunk;
});
process.stdin.on('end', () => {
processResult(data);
});
} else {
console.error("Warning: no input provided. Pass a file or pipe text via stdin.");
console.log("Usage: node mordor.js [-w|--wide number] [-s] [-c|--copy] [-r|--random [number]] [-b64|--base64] [-mjs] [file]");
process.exit(1);
}
}
function verticalizeRaw(text) {
return text
.replace(/\t/g, ' ')
.replace(/\r/g, '')
.replace(/\n/g, '')
.split('')
.join('\n') + '\n';
}
main();