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