mordorjs 0.0.1 → 0.0.2
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/README.md +53 -3
- package/mordor.js +117 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,10 +40,60 @@ If no file is provided, it reads from **stdin** (piped text) or defaults to the
|
|
|
40
40
|
| Option | Long Form | Description |
|
|
41
41
|
| :--- | :--- | :--- |
|
|
42
42
|
| `-w <num>` | `--wide <num>` | **Wrap into Columns:** Re-wraps the text into exactly `<num>` vertical strips. Words are preserved and distributed evenly. |
|
|
43
|
-
| `-s` | | **
|
|
43
|
+
| `-s` | | **Separator:** Adds a space between each column strip for better readability. |
|
|
44
44
|
| `-r [num]` | `--random [num]` | **Randomize:** Adds random vertical spacing (0 to `num`) before the first word and between every word in a strip. Default `num` is 3. |
|
|
45
|
-
| `-b64` | `--base64` | **Base64 Mode:** Encodes input to Base64 first. Uses "virtual words" (2
|
|
46
|
-
| `-c` | `--copy` | **Clipboard:** Automatically copies the final
|
|
45
|
+
| `-b64` | `--base64` | **Base64 Mode:** Encodes input to Base64 first. Uses "virtual words" (2–7 chars) to allow wrapping and randomization. |
|
|
46
|
+
| `-c` | `--copy` | **Clipboard:** Automatically copies the final output to your system clipboard. |
|
|
47
|
+
| `-mjs` | `--mordorjs` | **MordorJS Mode:** Converts a `.js` file into a self-executing 1W vertical program (`.cjs`). |
|
|
48
|
+
| `-pro` | `--project` | **Project Mode:** Packages an entire folder tree into a single `mordor-project` file (skips `node_modules`, hidden files, lock files; images truncated to 1KB). |
|
|
49
|
+
| `-h` | `--help` | **Help:** Show options in mordor-code format (`-w 31 -r 3 -s`), prefixed with `==== M\|\|D\|\|JS ====`. |
|
|
50
|
+
| `-ai-h` | `--ai-help` | **AI Help:** Show options in plain readable text, prefixed with `==== M\|\|D\|\|JS ====`. |
|
|
51
|
+
|
|
52
|
+
## mordor-code
|
|
53
|
+
|
|
54
|
+
Transform any JS file into a self-executing 1-character-wide vertical program:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
mordorjs myfile.js -mjs > myfile.cjs
|
|
58
|
+
node myfile.cjs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The generated file runs identically to the original. Shebang lines and `require()` calls are handled automatically.
|
|
62
|
+
|
|
63
|
+
## mordor-project
|
|
64
|
+
|
|
65
|
+
Package an entire project folder into a single portable text file:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
mordorjs ./my-project -pro -w 31 -s > my-project.mordor-project.txt
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Each file in the folder becomes one entry: a header line followed by the file's content in wide mordor format.
|
|
72
|
+
|
|
73
|
+
**Automatically skipped:**
|
|
74
|
+
- Hidden files and directories (`.git`, `.env`, etc.)
|
|
75
|
+
- `node_modules`, `dist`, `build`, `coverage`, `.next`, `.nuxt`
|
|
76
|
+
- Lock files (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, etc.)
|
|
77
|
+
|
|
78
|
+
**Images** (`.jpg`, `.jpeg`, `.png`): only the first 1KB is included as base64.
|
|
79
|
+
**Binary files**: base64-encoded automatically based on extension.
|
|
80
|
+
**Text files**: included as-is, rendered in the same wide mordor format as the other CLI flags produce.
|
|
81
|
+
|
|
82
|
+
Format spec:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
entry :=
|
|
86
|
+
header newline payload
|
|
87
|
+
|
|
88
|
+
header :=
|
|
89
|
+
/^_{4,}\s(.+)\s_{4,}$/
|
|
90
|
+
|
|
91
|
+
header content :=
|
|
92
|
+
<reversed-relative-path>
|
|
93
|
+
|
|
94
|
+
payload :=
|
|
95
|
+
all following lines until next header or EOF
|
|
96
|
+
```
|
|
47
97
|
|
|
48
98
|
## License
|
|
49
99
|
|
package/mordor.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
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
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
6
7
|
const { spawnSync } = require('child_process');
|
|
7
8
|
|
|
8
9
|
/** @returns {string} Verticalized `code-begin.js` (base64-embedded), opens the T template literal and defines single-char variable bindings. */
|
|
@@ -169,7 +170,78 @@ function generateMordorJS(jsCode) {
|
|
|
169
170
|
return prefix + startVert + verticalizedCode + endVert;
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
/**
|
|
173
|
+
/** @param {string} rootDir - Recursively walks a directory tree, returns sorted absolute file paths. Skips `node_modules`, hidden files/dirs, and lock files. @returns {string[]} */
|
|
174
|
+
function walkProjectTree(rootDir) {
|
|
175
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', '.svn', '.hg', '.cache', '.next', '.nuxt', 'dist', 'build', 'coverage']);
|
|
176
|
+
const SKIP_FILES = new Set(['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', 'composer.lock', 'Gemfile.lock', 'poetry.lock']);
|
|
177
|
+
const results = [];
|
|
178
|
+
function walk(dir) {
|
|
179
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
180
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
181
|
+
for (const entry of entries) {
|
|
182
|
+
if (entry.name.startsWith('.')) continue;
|
|
183
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
184
|
+
if (SKIP_FILES.has(entry.name)) continue;
|
|
185
|
+
const fullPath = path.join(dir, entry.name);
|
|
186
|
+
if (entry.isDirectory() || (entry.isSymbolicLink() && fs.statSync(fullPath).isDirectory())) {
|
|
187
|
+
walk(fullPath);
|
|
188
|
+
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
189
|
+
results.push(fullPath);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
walk(rootDir);
|
|
194
|
+
return results;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const BINARY_EXTENSIONS = new Set([
|
|
198
|
+
'.png','.jpg','.jpeg','.gif','.ico','.webp','.bmp','.tiff',
|
|
199
|
+
'.pdf','.zip','.tar','.gz','.bz2','.7z','.rar',
|
|
200
|
+
'.woff','.woff2','.ttf','.otf','.eot',
|
|
201
|
+
'.mp3','.mp4','.avi','.mov','.wav','.ogg','.webm',
|
|
202
|
+
'.exe','.dll','.so','.dylib','.bin','.class','.pyc',
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
/** @param {string} filePath - Returns true if the file extension is a known binary type. @returns {boolean} */
|
|
206
|
+
function isBinaryExtension(filePath) {
|
|
207
|
+
return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png']);
|
|
211
|
+
const IMAGE_PREVIEW_BYTES = 1024;
|
|
212
|
+
|
|
213
|
+
/** @param {string} filePath - Returns true if the file is an image that should be truncated to 1KB preview. @returns {boolean} */
|
|
214
|
+
function isPreviewImage(filePath) {
|
|
215
|
+
return IMAGE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** @param {string} reversedPath @param {number} targetWidth - Generates a mordor-project header padded to targetWidth with underscores. @returns {string} */
|
|
219
|
+
function makeProjectHeader(reversedPath, targetWidth = 61) {
|
|
220
|
+
const available = targetWidth - reversedPath.length - 2;
|
|
221
|
+
const left = Math.max(4, Math.ceil(available / 2));
|
|
222
|
+
const right = Math.max(4, available - left);
|
|
223
|
+
return '_'.repeat(left) + ' ' + reversedPath + ' ' + '_'.repeat(right);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const HELP_HEADER = '==== M||D||JS ====\n';
|
|
227
|
+
const HELP_BODY = `
|
|
228
|
+
Usage: mordorjs [options] [file]
|
|
229
|
+
|
|
230
|
+
Options:
|
|
231
|
+
-w, --wide <num> Wrap output into N vertical columns
|
|
232
|
+
-s Add separator spaces between columns
|
|
233
|
+
-r, --random [num] Add random spacing between words (default max: 3)
|
|
234
|
+
-b64, --base64 Base64-encode input before rendering
|
|
235
|
+
-c, --copy Copy output to system clipboard
|
|
236
|
+
-mjs, --mordorjs Convert JS file to self-executing 1W .cjs program
|
|
237
|
+
-pro, --project Package entire folder tree into a mordor-project file
|
|
238
|
+
(skips node_modules, hidden files, lock files;
|
|
239
|
+
images truncated to 1KB preview)
|
|
240
|
+
-h, --help Show this help in mordor-code format (-w 31 -r 3 -s)
|
|
241
|
+
-ai-h, --ai-help Show this help in plain text
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
/** CLI entry point — parses argv and dispatches to `verticalize`, `generateMordorJS`, or `buildMordorProject`. */
|
|
173
245
|
function main() {
|
|
174
246
|
const args = process.argv.slice(2);
|
|
175
247
|
let stripCount = null;
|
|
@@ -178,10 +250,17 @@ function main() {
|
|
|
178
250
|
let randomMax = null;
|
|
179
251
|
let isBase64 = false;
|
|
180
252
|
let isMordorJS = false;
|
|
253
|
+
let isProject = false;
|
|
181
254
|
let filePath = null;
|
|
182
255
|
|
|
183
256
|
for (let i = 0; i < args.length; i++) {
|
|
184
|
-
if (args[i] === '-
|
|
257
|
+
if (args[i] === '-h' || args[i] === '--help') {
|
|
258
|
+
process.stdout.write(HELP_HEADER + verticalize(HELP_BODY, 31, true, 3));
|
|
259
|
+
process.exit(0);
|
|
260
|
+
} else if (args[i] === '-ai-h' || args[i] === '--ai-help') {
|
|
261
|
+
process.stdout.write(HELP_HEADER + HELP_BODY);
|
|
262
|
+
process.exit(0);
|
|
263
|
+
} else if (args[i] === '-w' || args[i] === '--wide') {
|
|
185
264
|
stripCount = parseInt(args[i + 1], 10);
|
|
186
265
|
i++;
|
|
187
266
|
} else if (args[i] === '-s') {
|
|
@@ -192,6 +271,8 @@ function main() {
|
|
|
192
271
|
isBase64 = true;
|
|
193
272
|
} else if (args[i] === '-mjs' || args[i] === '--mordorjs') {
|
|
194
273
|
isMordorJS = true;
|
|
274
|
+
} else if (args[i] === '-pro' || args[i] === '--project') {
|
|
275
|
+
isProject = true;
|
|
195
276
|
} else if (args[i] === '-r' || args[i] === '--random') {
|
|
196
277
|
const nextArg = args[i + 1];
|
|
197
278
|
if (nextArg && !isNaN(parseInt(nextArg, 10)) && !nextArg.startsWith('-')) {
|
|
@@ -218,7 +299,40 @@ function main() {
|
|
|
218
299
|
}
|
|
219
300
|
};
|
|
220
301
|
|
|
221
|
-
if (
|
|
302
|
+
if (isProject) {
|
|
303
|
+
const rootDir = filePath || '.';
|
|
304
|
+
try {
|
|
305
|
+
const absRoot = path.resolve(rootDir);
|
|
306
|
+
const files = walkProjectTree(absRoot);
|
|
307
|
+
const strips = stripCount || 31;
|
|
308
|
+
const targetWidth = strips * 2 - 1;
|
|
309
|
+
let clipboard = shouldCopy ? '' : null;
|
|
310
|
+
for (const fp of files) {
|
|
311
|
+
const relativePath = path.relative(absRoot, fp).replace(/\\/g, '/');
|
|
312
|
+
const reversedPath = relativePath.split('').reverse().join('');
|
|
313
|
+
const header = makeProjectHeader(reversedPath, targetWidth) + '\n';
|
|
314
|
+
|
|
315
|
+
let content;
|
|
316
|
+
if (isPreviewImage(fp)) {
|
|
317
|
+
const buf = fs.readFileSync(fp).slice(0, IMAGE_PREVIEW_BYTES);
|
|
318
|
+
content = virtualizeWords(buf.toString('base64'));
|
|
319
|
+
} else if (isBinaryExtension(fp)) {
|
|
320
|
+
const b64 = fs.readFileSync(fp).toString('base64');
|
|
321
|
+
content = virtualizeWords(b64);
|
|
322
|
+
} else {
|
|
323
|
+
content = fs.readFileSync(fp, 'utf8');
|
|
324
|
+
}
|
|
325
|
+
const payload = verticalize(content, strips, addSeparator, randomMax, false);
|
|
326
|
+
const entry = header + payload;
|
|
327
|
+
process.stdout.write(entry);
|
|
328
|
+
if (clipboard !== null) clipboard += entry;
|
|
329
|
+
}
|
|
330
|
+
if (clipboard) copyToClipboard(clipboard);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
console.error(`Error building project: ${err.message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
} else if (filePath) {
|
|
222
336
|
try {
|
|
223
337
|
const data = fs.readFileSync(filePath, 'utf8');
|
|
224
338
|
processResult(data);
|