Ok, so, i’m stuck on the Stage #MG5 where we hae to find executables in the $PATH.
Problem is, everything works on my machine, on Windows and Linux, but it doesn’t work in the codecrafters test environment.
I tested almost everything i could,i logged to see if there was problem with me loading the commands and nothing appeared off to me, maybe it comes from the accessSync or anything, i don’t know.
If someone could help me thank you.
My code:
Main.ts
import readline from "readline";
import fs from "fs";
import { Engine } from "./engine.ts";
import { Arg } from "./args/arg.ts";
import { ExitCommand } from "./commands/ExitCommand.ts";
import { EchoCommand } from "./commands/EchoCommand.ts";
import { TypeCommand } from "./commands/TypeCommand.ts";
import { NotFoundCommand } from "./commands/NotFoundCommand.ts";
import { UserCommand } from "./commands/UserCommand.ts";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const engine = new Engine();
engine.registerCommand(new ExitCommand(() => rl.close()));
engine.registerCommand(new EchoCommand(engine, (value: string) => console.log(value)));
engine.registerCommand(new TypeCommand(engine))
let envPATH = process.env["PATH"]!;
// console.log(envPATH)
let PATH: Array<string> = [];
let separator: "/" | "\\";
switch(process.platform) {
case "win32":
PATH = envPATH.split(";").filter(dir => dir.trim() != "")
separator = "\\"
break;
case "linux":
PATH = envPATH.split(":").filter(dir => dir.trim() != "")
separator = "/"
break;
}
PATH.forEach(path => {
if(fs.existsSync(path)) {
const files = fs.readdirSync(path, { withFileTypes: true });
files.forEach(file => {
if(file.isFile()) {
const full_path = `${path}${separator}${file.name}`
const [name, ..._ext] = file.name.split(".");
try {
fs.accessSync(full_path, fs.constants.X_OK)
if(!engine.find(name)) {
engine.registerCommand(new UserCommand(name, full_path))
}
} catch(e) {}
}
})
}
})
rl.setPrompt("$ ");
rl.prompt();
// TODO: Uncomment the code below to pass the first stage
rl.on("line", async (input) => {
const [command, ...argums] = input.split(" ");
const args = argums.map((arg) => new Arg(arg));
engine.currentCommand = command;
engine.setArgs(args);
let current = engine.find(command);
if(current) current.execute();
else console.log("" + new NotFoundCommand(command));
rl.prompt();
});
rl.on("close", () => {
process.exit();
})
Engine.ts (i will probably change it because it doesn’t look like an engine lol)
import { Arg } from "./args/arg.ts";
import { Command } from "./commands/command.ts";
export class Engine {
private commands: Array<Command> = [];
private args: Array<Arg> = [];
public currentCommand: string = "";
registerCommand(command: Command) {
this.commands.push(command);
};
getCommands(): Array<Command> {
return this.commands;
}
find(key: string) {
return this.commands.find((command) => command.name === key);
}
getArgs() {
return this.args;
}
setArgs(args: Array<Arg>) {
this.args = args;
}
}
UserCommand.ts
import { Command } from "./command.ts";
import { CommandType } from "./command_type.ts";
export class UserCommand extends Command {
constructor(name: string, private path: string) {
super(name, CommandType.User)
}
public execute(): void {};
public toString(): string {
return `${this.name} is ${this.path.replace("C:", "").replace(".exe", "").replaceAll("\\", "/")}`
}
}
TypeCommand.ts
import { Arg } from "../args/arg.ts";
import { Engine } from "../engine.ts";
import { Command } from "./command.ts";
import { CommandType } from "./command_type.ts";
import { TypeNotFoundCommand } from "./TypeNotFoundCommand.ts";
export class TypeCommand extends Command {
constructor(engine: Engine) {
super("type", CommandType.BuiltIn, engine)
}
public execute(): void {
const engine = this.engine!;
const engineArgs = engine.getArgs().map((arg: Arg) => arg.value).join("")
let command = engine.find(engineArgs);
if(!command) {
command = new TypeNotFoundCommand(engineArgs);
}
console.log("" + command)
}
}
command.ts
import { Engine } from "../engine.ts";
import { CommandType } from "./command_type.ts";
export abstract class Command {
public constructor(public name: string, public type: CommandType, protected engine?: Engine) {};
public abstract execute(): void;
public toString(): string {
switch(this.type) {
case CommandType.BuiltIn:
return `${this.name} is a shell builtin`;
case CommandType.User:
return `${this.name} is user-created`;
case CommandType.TypeNotFound:
return `${this.name}: not found`;
case CommandType.NotFound:
return `${this.name}: command not found`;
}
}
}
command_type.ts
export enum CommandType {
BuiltIn,
User,
NotFound,
TypeNotFound
}
I thank everyone who will help me