Ar1: Initial project files

This commit is contained in:
Annie 2025-03-18 19:15:47 +03:00
parent 44f036243e
commit 0b02e33995
7 changed files with 403 additions and 0 deletions

17
package.json Normal file
View 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
View 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: {}

View file

@ -0,0 +1,3 @@
export default function(shctx) {
console.log(shctx.fqa.path.parsed ?? 'Path isnt parsed or Context is not accessible');
};

View file

@ -0,0 +1,3 @@
export default function() {
process.exit(0)
};

165
src/index.js Normal file
View 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
View 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
View 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;