Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# Legacy Script Engine Scaffold

A utility for assisting in the development of Legacy Script Engine plugins, supporting a native development experience on the Node.js platform.

> Only TypeScript projects are supported at the moment.

## 📦 Prepare

It is a non-intrusive tool, meaning it does not require any mandatory files to be kept in your project. However, it is recommended to add it as a development dependency to your environment for convenient usage:

```bash
npm install legacy-script-engine-scaffold --save-dev
```

## 🚀 Usage

Generate manifest.json for the Legacy Script Engine plugin:

```bash
npx lses manifest
```

Package the Legacy Script Engine plugin:

```bash
npx lses pack
```
24 changes: 20 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
{
"name": "legacy-script-engine-scaffold",
"version": "0.0.0",
"description": "A utility for assisting in the development of Legacy Script Engine plugins, supporting a native development experience on the Node.js platform.",
"version": "0.1.0",
"description": "A utility for assisting in the development of Legacy Script Engine plugins.",
"bugs": "https://github.com/leoweyr/LSEScaffold/issues",
"bin": {
"lses": "dist/cli/index.js"
},
"files": [
"dist"
],
"scripts": {
"clean": "tsc --build --clean",
"build": "npm run clean & tsc"
"compile": "tsc",
"build": "npm run clean && npm run compile",
"package": "npm run build && npm pack",
"deploy": "npm run package && npm publish"
},
"keywords": [
"levilamina",
"minecraft",
"bedrock-dedicated-server",
"utility",
"scaffold",
"cli",
"npx"
],
"author": "leoweyr <[email protected]>",
"license": "MIT",
"devDependencies": {
"@types/node": "^22.10.7",
"@types/archiver": "^6.0.3"
},
"dependencies": {
"typescript": "^5.7.3"
"typescript": "^5.7.3",
"archiver": "^7.0.1",
"commander": "^13.1.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 4 additions & 0 deletions src/cli/CliLoggableError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface CliLoggableError {
getMessage(): string;
getSuggestion(): Array<string>;
}
41 changes: 41 additions & 0 deletions src/cli/CliLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CliLoggableError } from "./CliLoggableError";


export class CliLogger {
private static TOOL_NAME: string = "legacy-script-engine-scaffold";

private readonly methodName: string;

public constructor(methodName: string) {
this.methodName = methodName;
}

public success(msg: string): void {
console.log(
`✅ ${CliLogger.TOOL_NAME}::${this.methodName}: ${msg}`
)
}

public error(error: CliLoggableError): void {
let suggestionString: string = "";

if (error.getSuggestion().length === 1) {
suggestionString += `Suggestion: ${error.getSuggestion()[0]}`;
} else {
suggestionString += "Suggestions:\n";

let solutionIndex: number = 1;

for (const solution of error.getSuggestion()) {
suggestionString += ` ${solutionIndex}. ${solution}\n`;
solutionIndex++;
}

suggestionString = suggestionString.slice(0, -2); // Remove the last newline.
}

console.error(
`❌ ${CliLogger.TOOL_NAME}::${this.methodName}: ${error.constructor.name} - ${error.getMessage()}\n ${suggestionString}`
);
}
}
55 changes: 55 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env node


import { program } from "commander";

import { CliLogger } from "./CliLogger";
import { TypeScriptProject } from "../project/TypeScriptProject";
import { CliLoggableError } from "./CliLoggableError";
import { Packager } from "../packager/Packager";


program
.name("lses")
.version("0.1.0")
.description("A utility for assisting in the development of Legacy Script Engine plugins.");

program
.command("manifest")
.description("generate manifest.json for the Legacy Script Engine plugin")
.action((): void => {
const logger: CliLogger = new CliLogger("manifest");

try {
const project: TypeScriptProject = TypeScriptProject.getInstance();

const successMessage: string = project.getManifest().generate();
logger.success(successMessage);
} catch (error) {
logger.error(error as CliLoggableError);
}
});

program
.command("pack")
.description("package the Legacy Script Engine plugin")
.action(async (): Promise<void> => {
const logger = new CliLogger("pack");

try {
const project: TypeScriptProject = TypeScriptProject.getInstance();
const packager: Packager = new Packager(project);

const successMessage: string = await packager.package();
logger.success(successMessage);
} catch (error) {
logger.error(error as CliLoggableError);
}
});

program.on("command:*", (): void => {
console.error(`Error: Invalid command lses ${program.args.join(" ")}`);
program.help();
});

program.parse(process.argv);
58 changes: 58 additions & 0 deletions src/nodejs/NodeJsConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as Path from "path";
import * as File from "fs";

import { Project } from "../project/Project";
import { NodeJsConfigurationFileNotFoundError } from "./NodeJsConfigurationFileNotFoundError";
import { NodeJsConfigurationMissingError } from "./NodeJsConfigurationMissingError";


export class NodeJsConfiguration {
private readonly filePath: string;

public constructor(project: Project) {
const projectPath: string = project.getPath();

this.filePath = Path.join(projectPath, "package.json");

if (!File.existsSync(this.filePath)) {
throw new NodeJsConfigurationFileNotFoundError(projectPath);
}
}

public getPath(): string {
return this.filePath;
}

public getName(): string {
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
const name: string = configuration.name;

if (!name) {
throw new NodeJsConfigurationMissingError(this.filePath, "name");
}

return name;
}

public getVersion(): string {
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
const version: string = configuration.version;

if (!version) {
throw new NodeJsConfigurationMissingError(this.filePath, "version");
}

return version;
}

public getMainEntry(): string {
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
const mainEntry: string = configuration.main;

if (!mainEntry) {
throw new NodeJsConfigurationMissingError(this.filePath, "main");
}

return mainEntry;
}
}
25 changes: 25 additions & 0 deletions src/nodejs/NodeJsConfigurationFileNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CliLoggableError } from "../cli/CliLoggableError";


export class NodeJsConfigurationFileNotFoundError extends Error implements CliLoggableError {
private readonly msg: string;

public constructor(fileDirectory: string) {
const message: string = `Could not find package.json in ${fileDirectory}.`;

super(message);

this.msg = message;
}

public getMessage(): string {
return this.msg;
}

public getSuggestion(): Array<string> {
const suggestion: Array<string> = new Array<string>();
suggestion.push("Try `npm init` to initialize the project.");

return suggestion;
}
}
27 changes: 27 additions & 0 deletions src/nodejs/NodeJsConfigurationMissingError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { CliLoggableError } from "../cli/CliLoggableError";


export class NodeJsConfigurationMissingError extends Error implements CliLoggableError {
private readonly msg: string;
private readonly missingProperty: string;

public constructor(filePath: string, missingProperty: string) {
const message: string = `${filePath} is missing the required property \`${missingProperty}\`.`;

super(message);

this.msg = message;
this.missingProperty = missingProperty;
}

public getMessage(): string {
return this.msg;
}

public getSuggestion(): Array<string> {
const suggestion: Array<string> = new Array<string>();
suggestion.push(`Try checking if package.json includes the \`${this.missingProperty}\` configuration.`);

return suggestion;
}
}
73 changes: 73 additions & 0 deletions src/nodejs/TypeScriptConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as Path from "path";
import * as File from "fs";

import ts, { ParsedCommandLine, Program } from "typescript";

import { Project } from "../project/Project";
import { TypeScriptConfigurationParseError } from "./TypeScriptConfigurationParseError";
import { TypeScriptConfigurationFileNotFoundError } from "./TypeScriptConfigurationFileNotFoundError";
import { TypeScriptConfigurationMissingError } from "./TypeScriptConfigurationMissingError";


export class TypeScriptConfiguration {
private static parseFile(filePath: string): ParsedCommandLine {
const file: any = ts.readConfigFile(filePath, ts.sys.readFile);

if (file.error) {
throw new TypeScriptConfigurationParseError(filePath);
}

const parsedCommandLine: ParsedCommandLine = ts.parseJsonConfigFileContent(
file.config,
ts.sys,
Path.dirname(filePath)
);

if (parsedCommandLine.errors.length > 0) {
throw new TypeScriptConfigurationParseError(filePath, parsedCommandLine.errors);
}

return parsedCommandLine;
}

private readonly filePath: string;

public constructor(project: Project) {
this.filePath = Path.join(project.getPath(), "tsconfig.json");

if (!File.existsSync(this.filePath)) {
throw new TypeScriptConfigurationFileNotFoundError(project.getPath());
}
}

public getEmittedDirectory(): string {
const parsedCommandLine: ParsedCommandLine = TypeScriptConfiguration.parseFile(this.filePath);
const emittedDirectory: string | undefined = parsedCommandLine.options.outDir;

if (!emittedDirectory) {
throw new TypeScriptConfigurationMissingError(this.filePath, "outDir");
}

return emittedDirectory;
}

public getCompiledDirectoryStructure(): Array<string> {
const parsedCommandLine: ParsedCommandLine = TypeScriptConfiguration.parseFile(this.filePath);

const program: Program = ts.createProgram({
rootNames: parsedCommandLine.fileNames,
options: parsedCommandLine.options,
});

const emittedFilePaths: Array<string> = new Array<string>();
const emittedDirectory: string = this.getEmittedDirectory();

program.emit(undefined, (fileName: string): void => {
emittedFilePaths.push(fileName);
});

return emittedFilePaths.map((filePath: string): string => {
return Path.relative(emittedDirectory, filePath).replace(/\\/g, "/")
});
}
}
25 changes: 25 additions & 0 deletions src/nodejs/TypeScriptConfigurationFileNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CliLoggableError } from "../cli/CliLoggableError";


export class TypeScriptConfigurationFileNotFoundError extends Error implements CliLoggableError {
private readonly msg: string;

public constructor(fileDirectory: string) {
const message: string = `Could not find tsconfig.json in ${fileDirectory}.`;

super(message);

this.msg = message;
}

public getMessage(): string {
return this.msg;
}

public getSuggestion(): Array<string> {
const suggestion: Array<string> = new Array<string>();
suggestion.push("Try `npx tsc --init` to initialize the TypeScript project.");

return suggestion;
}
}
Loading