Ar1: Initial project files
This commit is contained in:
parent
44f036243e
commit
0b02e33995
7 changed files with 403 additions and 0 deletions
17
package.json
Normal file
17
package.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "jshell",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "https://git.true1ann.me/true1ann/jshell/",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "true1ann",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.6.1",
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"readline-sync": "^1.4.10"
|
||||||
|
}
|
||||||
|
}
|
23
pnpm-lock.yaml
generated
Normal file
23
pnpm-lock.yaml
generated
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
readline-sync:
|
||||||
|
specifier: ^1.4.10
|
||||||
|
version: 1.4.10
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
readline-sync@1.4.10:
|
||||||
|
resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
readline-sync@1.4.10: {}
|
3
src/builtin_commands/envtest.js
Normal file
3
src/builtin_commands/envtest.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function(shctx) {
|
||||||
|
console.log(shctx.fqa.path.parsed ?? 'Path isnt parsed or Context is not accessible');
|
||||||
|
};
|
3
src/builtin_commands/nuke.js
Normal file
3
src/builtin_commands/nuke.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function() {
|
||||||
|
process.exit(0)
|
||||||
|
};
|
165
src/index.js
Normal file
165
src/index.js
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import htmlc from './lib/htmlColors.js';
|
||||||
|
import printc from './lib/printc.js';
|
||||||
|
import readline from 'readline';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import readlineSync from 'readline-sync';
|
||||||
|
|
||||||
|
let __temp;
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
let fqa = {
|
||||||
|
home: process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,
|
||||||
|
path: {
|
||||||
|
raw: process.env.PATH,
|
||||||
|
parsed: process.env.PATH ? process.env.PATH.split(':') : []
|
||||||
|
},
|
||||||
|
colors: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
__temp = {
|
||||||
|
term: {
|
||||||
|
warn: 3,
|
||||||
|
error: 1,
|
||||||
|
text: 4,
|
||||||
|
prompt: 15
|
||||||
|
},
|
||||||
|
html: {
|
||||||
|
warn: htmlc['Yellow'],
|
||||||
|
error: htmlc['Red'],
|
||||||
|
text: htmlc['White'],
|
||||||
|
prompt: htmlc['Gray'],
|
||||||
|
},
|
||||||
|
none: {
|
||||||
|
warn: -1,
|
||||||
|
error: -1,
|
||||||
|
text: -1,
|
||||||
|
prompt: -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const configPath = path.join(fqa.home, '.config', 'jshell', 'config.json');
|
||||||
|
const historyFile = process.env.JSHELL_HISTORY || `${fqa.home}/.history`;
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
process.stdout.write(printc('FF0000', `No config file was found in ${fqa.home}/.config/jshell/config.json.` + '\n'));
|
||||||
|
process.stdout.write('Do you want to generate a standart config now? (Y/n)' + '\n');
|
||||||
|
|
||||||
|
const userResponse = readlineSync.question('> ');
|
||||||
|
|
||||||
|
if (userResponse.toLowerCase() !== 'y' && userResponse !== '') {
|
||||||
|
process.stdout.write('JSH cannot continue without a config file.' + '\n');
|
||||||
|
process.stdout.write(printc('FF0000', 'JSH Fatal. exitting'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
||||||
|
const defaultConfig = {
|
||||||
|
colorMode: 'term',
|
||||||
|
prompt: 'jsh> ',
|
||||||
|
exitPhrase: 'exit',
|
||||||
|
customCommandPrefix: '.'
|
||||||
|
};
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
if (!(config.colorMode in __temp)) {
|
||||||
|
process.stdout.write(printc('FFFFFFF', 'Invalid colorMode in config. (term, html, none)'));
|
||||||
|
process.stdout.write(printc('FF0000', 'JSH Fatal. exitting'))
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fqa.colors = __temp[config.colorMode];
|
||||||
|
__temp = undefined;
|
||||||
|
|
||||||
|
const shctx = {
|
||||||
|
fqa: fqa,
|
||||||
|
config: config
|
||||||
|
}
|
||||||
|
|
||||||
|
readline.emitKeypressEvents(process.stdin);
|
||||||
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||||
|
let shv = {
|
||||||
|
line: '',
|
||||||
|
chars: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
process.stdin.on('keypress', async (char, key) => {
|
||||||
|
if (key.sequence === '\u0003') {
|
||||||
|
process.stdout.write(config.exitPhrase || 'exit');
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
if (key.name === 'return') {
|
||||||
|
process.stdout.write('\n');
|
||||||
|
if (shv.line == '' || shv.line.startsWith('//')) {
|
||||||
|
// skip
|
||||||
|
} else if (shv.line.startsWith('./')) {
|
||||||
|
// LOCAL command
|
||||||
|
process.stdout.write(printc(fqa.colors.warn, 'Executing as an LOCAL command' + '\n'));
|
||||||
|
} else if (shv.line.startsWith(config.customCommandPrefix || '.')) {
|
||||||
|
process.stdout.write(printc(fqa.colors.warn, 'Executing as an JSH command') + '\n');
|
||||||
|
// JSH command
|
||||||
|
const commandPath = path.join(__dirname, 'builtin_commands', `${shv.line.slice(config.customCommandPrefix.length)}`);
|
||||||
|
|
||||||
|
let commandFile = commandPath;
|
||||||
|
if (!fs.existsSync(commandFile)) {
|
||||||
|
commandFile = `${commandPath}.js`;
|
||||||
|
if (!fs.existsSync(commandFile)) {
|
||||||
|
commandFile = `${commandPath}.ts`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(commandFile)) {
|
||||||
|
await import(commandFile).then(async command => {
|
||||||
|
if (typeof command.default === 'function') {
|
||||||
|
await command.default(shctx);
|
||||||
|
} else {
|
||||||
|
process.stdout.write(printc(fqa.colors.error, 'Loaded module is not a function.' + '\n'));
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
process.stdout.write(printc(fqa.colors.error, 'Failed to execute JSH command:' + '\n'));
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
process.stdout.write(printc(fqa.colors.error, 'No such JSH command.' + '\n'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// UNIX command
|
||||||
|
process.stdout.write(printc(fqa.colors.warn, 'Executing as an UNIX command' + '\n'));
|
||||||
|
const command = shv.line.split(' ')[0]
|
||||||
|
let found = false;
|
||||||
|
for (const dir of fqa.path.parsed) {
|
||||||
|
const commandFile = path.join(dir, command);
|
||||||
|
if (fs.existsSync(commandFile) && fs.statSync(commandFile).isFile()) {
|
||||||
|
process.stdout.write(printc(fqa.colors.text, `Found executable: ${commandFile}` + '\n'));
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
process.stdout.write(printc(fqa.colors.error, 'No such command.') + '\n');
|
||||||
|
}
|
||||||
|
const args = shv.line.split(' ').slice(1);
|
||||||
|
process.stdout.write(printc(fqa.colors.text, `Args: ${JSON.stringify(args)}`) + '\n');
|
||||||
|
}
|
||||||
|
process.stdout.write(printc(fqa.colors.prompt, config.prompt));
|
||||||
|
shv.line = '';
|
||||||
|
shv.chars = [];
|
||||||
|
} else if (key.name === 'backspace') {
|
||||||
|
if (shv.line.length > 0) {
|
||||||
|
shv.line = shv.line.slice(0, -1);
|
||||||
|
shv.chars.pop();
|
||||||
|
process.stdout.clearLine();
|
||||||
|
process.stdout.cursorTo(0);
|
||||||
|
process.stdout.write(printc(fqa.colors.prompt, config.prompt) + printc(fqa.colors.text, shv.chars.map(c => c.render).join('')));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shv.line += char;
|
||||||
|
const r = printc(fqa.colors.text, char);
|
||||||
|
shv.chars.push({ render: r, rawKey: key, rawChar: char});
|
||||||
|
process.stdout.write(r);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
process.stdout.write(printc(fqa.colors.prompt, config.prompt));
|
147
src/lib/htmlColors.js
Normal file
147
src/lib/htmlColors.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// https://www.w3schools.com/colors/colors_names.asp
|
||||||
|
|
||||||
|
const htmlc = {
|
||||||
|
"AliceBlue": "F0F8FF",
|
||||||
|
"AntiqueWhite": "FAEBD7",
|
||||||
|
"Aqua": "00FFFF",
|
||||||
|
"Aquamarine": "7FFFD4",
|
||||||
|
"Azure": "F0FFFF",
|
||||||
|
"Beige": "F5F5DC",
|
||||||
|
"Bisque": "FFE4C4",
|
||||||
|
"Black": "000000",
|
||||||
|
"BlanchedAlmond": "FFEBCD",
|
||||||
|
"Blue": "0000FF",
|
||||||
|
"BlueViolet": "8A2BE2",
|
||||||
|
"Brown": "A52A2A",
|
||||||
|
"BurlyWood": "DEB887",
|
||||||
|
"CadetBlue": "5F9EA0",
|
||||||
|
"Chartreuse": "7FFF00",
|
||||||
|
"Chocolate": "D2691E",
|
||||||
|
"Coral": "FF7F50",
|
||||||
|
"CornflowerBlue": "6495ED",
|
||||||
|
"Cornsilk": "FFF8DC",
|
||||||
|
"Crimson": "DC143C",
|
||||||
|
"Cyan": "00FFFF",
|
||||||
|
"DarkBlue": "00008B",
|
||||||
|
"DarkCyan": "008B8B",
|
||||||
|
"DarkGoldenRod": "B8860B",
|
||||||
|
"DarkGray": "A9A9A9",
|
||||||
|
"DarkGreen": "006400",
|
||||||
|
"DarkKhaki": "BDB76B",
|
||||||
|
"DarkMagenta": "8B008B",
|
||||||
|
"DarkOliveGreen": "556B2F",
|
||||||
|
"DarkOrange": "FF8C00",
|
||||||
|
"DarkOrchid": "9932CC",
|
||||||
|
"DarkRed": "8B0000",
|
||||||
|
"DarkSalmon": "E9967A",
|
||||||
|
"DarkSeaGreen": "8FBC8F",
|
||||||
|
"DarkSlateBlue": "483D8B",
|
||||||
|
"DarkSlateGray": "2F4F4F",
|
||||||
|
"DarkTurquoise": "00CED1",
|
||||||
|
"DarkViolet": "9400D3",
|
||||||
|
"DeepPink": "FF1493",
|
||||||
|
"DeepSkyBlue": "00BFFF",
|
||||||
|
"DimGray": "696969",
|
||||||
|
"DodgerBlue": "1E90FF",
|
||||||
|
"FireBrick": "B22222",
|
||||||
|
"FloralWhite": "FFFAF0",
|
||||||
|
"ForestGreen": "228B22",
|
||||||
|
"Fuchsia": "FF00FF",
|
||||||
|
"Gainsboro": "DCDCDC",
|
||||||
|
"GhostWhite": "F8F8FF",
|
||||||
|
"Gold": "FFD700",
|
||||||
|
"GoldenRod": "DAA520",
|
||||||
|
"Gray": "808080",
|
||||||
|
"Green": "008000",
|
||||||
|
"GreenYellow": "ADFF2F",
|
||||||
|
"HoneyDew": "F0FFF0",
|
||||||
|
"HotPink": "FF69B4",
|
||||||
|
"IndianRed": "CD5C5C",
|
||||||
|
"Indigo": "4B0082",
|
||||||
|
"Ivory": "FFFFF0",
|
||||||
|
"Khaki": "F0E68C",
|
||||||
|
"Lavender": "E6E6FA",
|
||||||
|
"LavenderBlush": "FFF0F5",
|
||||||
|
"LawnGreen": "7CFC00",
|
||||||
|
"LemonChiffon": "FFFACD",
|
||||||
|
"LightBlue": "ADD8E6",
|
||||||
|
"LightCoral": "F08080",
|
||||||
|
"LightCyan": "E0FFFF",
|
||||||
|
"LightGoldenRodYellow": "FAFAD2",
|
||||||
|
"LightGray": "D3D3D3",
|
||||||
|
"LightGreen": "90EE90",
|
||||||
|
"LightPink": "FFB6C1",
|
||||||
|
"LightSalmon": "FFA07A",
|
||||||
|
"LightSeaGreen": "20B2AA",
|
||||||
|
"LightSkyBlue": "87CEFA",
|
||||||
|
"LightSlateGray": "778899",
|
||||||
|
"LightSteelBlue": "B0C4DE",
|
||||||
|
"LightYellow": "FFFFE0",
|
||||||
|
"Lime": "00FF00",
|
||||||
|
"LimeGreen": "32CD32",
|
||||||
|
"Linen": "FAF0E6",
|
||||||
|
"Magenta": "FF00FF",
|
||||||
|
"Maroon": "800000",
|
||||||
|
"MediumAquaMarine": "66CDAA",
|
||||||
|
"MediumBlue": "0000CD",
|
||||||
|
"MediumOrchid": "BA55D3",
|
||||||
|
"MediumPurple": "9370DB",
|
||||||
|
"MediumSeaGreen": "3CB371",
|
||||||
|
"MediumSlateBlue": "7B68EE",
|
||||||
|
"MediumSpringGreen": "00FA9A",
|
||||||
|
"MediumTurquoise": "48D1CC",
|
||||||
|
"MediumVioletRed": "C71585",
|
||||||
|
"MidnightBlue": "191970",
|
||||||
|
"MintCream": "F5FFFA",
|
||||||
|
"MistyRose": "FFE4E1",
|
||||||
|
"Moccasin": "FFE4B5",
|
||||||
|
"NavajoWhite": "FFDEAD",
|
||||||
|
"Navy": "000080",
|
||||||
|
"OldLace": "FDF5E6",
|
||||||
|
"Olive": "808000",
|
||||||
|
"OliveDrab": "6B8E23",
|
||||||
|
"Orange": "FFA500",
|
||||||
|
"OrangeRed": "FF4500",
|
||||||
|
"Orchid": "DA70D6",
|
||||||
|
"PaleGoldenRod": "EEE8AA",
|
||||||
|
"PaleGreen": "98FB98",
|
||||||
|
"PaleTurquoise": "AFEEEE",
|
||||||
|
"PaleVioletRed": "DB7093",
|
||||||
|
"PapayaWhip": "FFEFD5",
|
||||||
|
"PeachPuff": "FFDAB9",
|
||||||
|
"Peru": "CD853F",
|
||||||
|
"Pink": "FFC0CB",
|
||||||
|
"Plum": "DDA0DD",
|
||||||
|
"PowderBlue": "B0E0E6",
|
||||||
|
"Purple": "800080",
|
||||||
|
"RebeccaPurple": "663399",
|
||||||
|
"Red": "FF0000",
|
||||||
|
"RosyBrown": "BC8F8F",
|
||||||
|
"RoyalBlue": "4169E1",
|
||||||
|
"SaddleBrown": "8B4513",
|
||||||
|
"Salmon": "FA8072",
|
||||||
|
"SandyBrown": "F4A460",
|
||||||
|
"SeaGreen": "2E8B57",
|
||||||
|
"SeaShell": "FFF5EE",
|
||||||
|
"Sienna": "A0522D",
|
||||||
|
"Silver": "C0C0C0",
|
||||||
|
"SkyBlue": "87CEEB",
|
||||||
|
"SlateBlue": "6A5ACD",
|
||||||
|
"SlateGray": "708090",
|
||||||
|
"Snow": "FFFAFA",
|
||||||
|
"SpringGreen": "00FF7F",
|
||||||
|
"SteelBlue": "4682B4",
|
||||||
|
"Tan": "D2B48C",
|
||||||
|
"Teal": "008080",
|
||||||
|
"Thistle": "D8BFD8",
|
||||||
|
"Tomato": "FF6347",
|
||||||
|
"Turquoise": "40E0D0",
|
||||||
|
"Violet": "EE82EE",
|
||||||
|
"Wheat": "F5DEB3",
|
||||||
|
"White": "FFFFFF",
|
||||||
|
"WhiteSmoke": "F5F5F5",
|
||||||
|
"Yellow": "FFFF00",
|
||||||
|
"YellowGreen": "9ACD32"
|
||||||
|
};
|
||||||
|
|
||||||
|
export default htmlc;
|
45
src/lib/printc.js
Normal file
45
src/lib/printc.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
const colorMap = {
|
||||||
|
// standart
|
||||||
|
0: 30, // black
|
||||||
|
1: 31, // red
|
||||||
|
2: 32, // green
|
||||||
|
3: 33, // yellow
|
||||||
|
4: 34, // blue
|
||||||
|
5: 35, // magenta
|
||||||
|
6: 36, // cyan
|
||||||
|
7: 37, // white
|
||||||
|
// bright
|
||||||
|
8: 90, // black
|
||||||
|
9: 91, // red
|
||||||
|
10: 92, // green
|
||||||
|
11: 93, // yellow
|
||||||
|
12: 94, // blue
|
||||||
|
13: 95, // magenta
|
||||||
|
14: 96, // cyan
|
||||||
|
15: 97 // white
|
||||||
|
};
|
||||||
|
|
||||||
|
const hexToAnsi = (hex) => {
|
||||||
|
hex = hex.replace(/^#/, '');
|
||||||
|
if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
|
||||||
|
const r = parseInt(hex.slice(0, 2), 16);
|
||||||
|
const g = parseInt(hex.slice(2, 4), 16);
|
||||||
|
const b = parseInt(hex.slice(4, 6), 16);
|
||||||
|
return `\x1b[38;2;${r};${g};${b}m`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const printc = (COL, TX) => {
|
||||||
|
let colorCode;
|
||||||
|
if (colorMap[COL] !== undefined) {
|
||||||
|
colorCode = colorMap[COL];
|
||||||
|
} else if (/^#?[0-9A-Fa-f]{6}$/.test(COL)) {
|
||||||
|
return hexToAnsi(COL) + TX + '\x1b[0m';
|
||||||
|
} else if (COL == -1) {
|
||||||
|
return TX;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid color format');
|
||||||
|
}
|
||||||
|
return `\x1b[${colorCode}m${TX}\x1b[0m`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default printc;
|
Loading…
Add table
Reference in a new issue