Skip to content

Commit 2933e01

Browse files
committed
feat(cli): add support for manifest and packaging of Legacy Script Engine plugins
1 parent 58be2a4 commit 2933e01

17 files changed

+397
-18
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
11
# Legacy Script Engine Scaffold
22

33
A utility for assisting in the development of Legacy Script Engine plugins, supporting a native development experience on the Node.js platform.
4+
5+
> Only TypeScript projects are supported at the moment.
6+
7+
## 📦 Prepare
8+
9+
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:
10+
11+
```bash
12+
npm install legacy-script-engine-scaffold --save-dev
13+
```
14+
15+
## 🚀 Usage
16+
17+
Generate manifest.json for the Legacy Script Engine plugin:
18+
19+
```bash
20+
npx lses manifest
21+
```
22+
23+
Package the Legacy Script Engine plugin:
24+
25+
```bash
26+
npx lses pack
27+
```

package.json

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
{
22
"name": "legacy-script-engine-scaffold",
3-
"version": "0.0.0",
4-
"description": "A utility for assisting in the development of Legacy Script Engine plugins, supporting a native development experience on the Node.js platform.",
3+
"version": "0.1.0",
4+
"description": "A utility for assisting in the development of Legacy Script Engine plugins.",
55
"bugs": "https://github.com/leoweyr/LSEScaffold/issues",
6+
"bin": {
7+
"lses": "dist/cli/index.js"
8+
},
9+
"files": [
10+
"dist"
11+
],
612
"scripts": {
713
"clean": "tsc --build --clean",
8-
"build": "npm run clean & tsc"
14+
"compile": "tsc",
15+
"build": "npm run clean && npm run compile",
16+
"package": "npm run build && npm pack",
17+
"deploy": "npm run package && npm publish"
918
},
1019
"keywords": [
1120
"levilamina",
1221
"minecraft",
1322
"bedrock-dedicated-server",
1423
"utility",
1524
"scaffold",
25+
"cli",
1626
"npx"
1727
],
1828
"author": "leoweyr <[email protected]>",
@@ -23,7 +33,8 @@
2333
},
2434
"dependencies": {
2535
"typescript": "^5.7.3",
26-
"archiver": "^7.0.1"
36+
"archiver": "^7.0.1",
37+
"commander": "^13.1.0"
2738
},
2839
"repository": {
2940
"type": "git",

src/cli/CliLogger.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { CliLoggableError } from "./CliLoggableError";
2+
3+
4+
export class CliLogger {
5+
private static TOOL_NAME: string = "legacy-script-engine-scaffold";
6+
7+
private readonly methodName: string;
8+
9+
public constructor(methodName: string) {
10+
this.methodName = methodName;
11+
}
12+
13+
public success(msg: string): void {
14+
console.log(
15+
`✅ ${CliLogger.TOOL_NAME}::${this.methodName}: ${msg}`
16+
)
17+
}
18+
19+
public error(error: CliLoggableError): void {
20+
let suggestionString: string = "";
21+
22+
if (error.getSuggestion().length === 1) {
23+
suggestionString += `Suggestion: ${error.getSuggestion()[0]}`;
24+
} else {
25+
suggestionString += "Suggestions:\n";
26+
27+
let solutionIndex: number = 1;
28+
29+
for (const solution of error.getSuggestion()) {
30+
suggestionString += ` ${solutionIndex}. ${solution}\n`;
31+
solutionIndex++;
32+
}
33+
34+
suggestionString = suggestionString.slice(0, -2); // Remove the last newline.
35+
}
36+
37+
console.error(
38+
`❌ ${CliLogger.TOOL_NAME}::${this.methodName}: ${error.constructor.name} - ${error.getMessage()}\n ${suggestionString}`
39+
);
40+
}
41+
}

src/cli/index.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env node
2+
3+
4+
import { program } from "commander";
5+
6+
import { CliLogger } from "./CliLogger";
7+
import { TypeScriptProject } from "../project/TypeScriptProject";
8+
import { CliLoggableError } from "./CliLoggableError";
9+
import { Packager } from "../packager/Packager";
10+
11+
12+
program
13+
.name("lses")
14+
.version("0.1.0")
15+
.description("A utility for assisting in the development of Legacy Script Engine plugins.");
16+
17+
program
18+
.command("manifest")
19+
.description("generate manifest.json for the Legacy Script Engine plugin")
20+
.action((): void => {
21+
const logger: CliLogger = new CliLogger("manifest");
22+
23+
try {
24+
const project: TypeScriptProject = TypeScriptProject.getInstance();
25+
26+
const successMessage: string = project.getManifest().generate();
27+
logger.success(successMessage);
28+
} catch (error) {
29+
logger.error(error as CliLoggableError);
30+
}
31+
});
32+
33+
program
34+
.command("pack")
35+
.description("package the Legacy Script Engine plugin")
36+
.action(async (): Promise<void> => {
37+
const logger = new CliLogger("pack");
38+
39+
try {
40+
const project: TypeScriptProject = TypeScriptProject.getInstance();
41+
const packager: Packager = new Packager(project);
42+
43+
const successMessage: string = await packager.package();
44+
logger.success(successMessage);
45+
} catch (error) {
46+
logger.error(error as CliLoggableError);
47+
}
48+
});
49+
50+
program.on("command:*", (): void => {
51+
console.error(`Error: Invalid command lses ${program.args.join(" ")}`);
52+
program.help();
53+
});
54+
55+
program.parse(process.argv);

src/nodejs/NodeJsConfiguration.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ import * as Path from "path";
22
import * as File from "fs";
33

44
import { Project } from "../project/Project";
5+
import { NodeJsConfigurationFileNotFoundError } from "./NodeJsConfigurationFileNotFoundError";
6+
import { NodeJsConfigurationMissingError } from "./NodeJsConfigurationMissingError";
57

68

79
export class NodeJsConfiguration {
810
private readonly filePath: string;
911

1012
public constructor(project: Project) {
11-
this.filePath = Path.join(project.getPath(), "package.json");
13+
const projectPath: string = project.getPath();
14+
15+
this.filePath = Path.join(projectPath, "package.json");
16+
17+
if (!File.existsSync(this.filePath)) {
18+
throw new NodeJsConfigurationFileNotFoundError(projectPath);
19+
}
1220
}
1321

1422
public getPath(): string {
@@ -17,19 +25,34 @@ export class NodeJsConfiguration {
1725

1826
public getName(): string {
1927
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
28+
const name: string = configuration.name;
29+
30+
if (!name) {
31+
throw new NodeJsConfigurationMissingError(this.filePath, "name");
32+
}
2033

21-
return configuration.name;
34+
return name;
2235
}
2336

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

27-
return configuration.version;
41+
if (!version) {
42+
throw new NodeJsConfigurationMissingError(this.filePath, "version");
43+
}
44+
45+
return version;
2846
}
2947

3048
public getMainEntry(): string {
3149
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
50+
const mainEntry: string = configuration.main;
51+
52+
if (!mainEntry) {
53+
throw new NodeJsConfigurationMissingError(this.filePath, "main");
54+
}
3255

33-
return configuration.main;
56+
return mainEntry;
3457
}
3558
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { CliLoggableError } from "../cli/CliLoggableError";
2+
3+
4+
export class NodeJsConfigurationFileNotFoundError extends Error implements CliLoggableError {
5+
private readonly msg: string;
6+
7+
public constructor(fileDirectory: string) {
8+
const message: string = `Could not find package.json in ${fileDirectory}.`;
9+
10+
super(message);
11+
12+
this.msg = message;
13+
}
14+
15+
public getMessage(): string {
16+
return this.msg;
17+
}
18+
19+
public getSuggestion(): Array<string> {
20+
const suggestion: Array<string> = new Array<string>();
21+
suggestion.push("Try `npm init` to initialize the project.");
22+
23+
return suggestion;
24+
}
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CliLoggableError } from "../cli/CliLoggableError";
2+
3+
4+
export class NodeJsConfigurationMissingError extends Error implements CliLoggableError {
5+
private readonly msg: string;
6+
private readonly missingProperty: string;
7+
8+
public constructor(filePath: string, missingProperty: string) {
9+
const message: string = `${filePath} is missing the required property \`${missingProperty}\`.`;
10+
11+
super(message);
12+
13+
this.msg = message;
14+
this.missingProperty = missingProperty;
15+
}
16+
17+
public getMessage(): string {
18+
return this.msg;
19+
}
20+
21+
public getSuggestion(): Array<string> {
22+
const suggestion: Array<string> = new Array<string>();
23+
suggestion.push(`Try checking if package.json includes the \`${this.missingProperty}\` configuration.`);
24+
25+
return suggestion;
26+
}
27+
}

src/nodejs/TypeScriptConfiguration.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import * as Path from "path";
2+
import * as File from "fs";
23

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

56
import { Project } from "../project/Project";
7+
import { TypeScriptConfigurationParseError } from "./TypeScriptConfigurationParseError";
8+
import { TypeScriptConfigurationFileNotFoundError } from "./TypeScriptConfigurationFileNotFoundError";
9+
import { TypeScriptConfigurationMissingError } from "./TypeScriptConfigurationMissingError";
610

711

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

1216
if (file.error) {
13-
throw new Error("Error reading tsconfig.json.");
17+
throw new TypeScriptConfigurationParseError(filePath);
1418
}
1519

1620
const parsedCommandLine: ParsedCommandLine = ts.parseJsonConfigFileContent(
@@ -20,7 +24,7 @@ export class TypeScriptConfiguration {
2024
);
2125

2226
if (parsedCommandLine.errors.length > 0) {
23-
throw new Error(`Error parsing tsconfig.json: ${JSON.stringify(parsedCommandLine.errors)}.`);
27+
throw new TypeScriptConfigurationParseError(filePath, parsedCommandLine.errors);
2428
}
2529

2630
return parsedCommandLine;
@@ -30,14 +34,18 @@ export class TypeScriptConfiguration {
3034

3135
public constructor(project: Project) {
3236
this.filePath = Path.join(project.getPath(), "tsconfig.json");
37+
38+
if (!File.existsSync(this.filePath)) {
39+
throw new TypeScriptConfigurationFileNotFoundError(project.getPath());
40+
}
3341
}
3442

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

3947
if (!emittedDirectory) {
40-
throw new Error("No `outDir` configuration in tsconfig.json.");
48+
throw new TypeScriptConfigurationMissingError(this.filePath, "outDir");
4149
}
4250

4351
return emittedDirectory;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { CliLoggableError } from "../cli/CliLoggableError";
2+
3+
4+
export class TypeScriptConfigurationFileNotFoundError extends Error implements CliLoggableError {
5+
private readonly msg: string;
6+
7+
public constructor(fileDirectory: string) {
8+
const message: string = `Could not find tsconfig.json in ${fileDirectory}.`;
9+
10+
super(message);
11+
12+
this.msg = message;
13+
}
14+
15+
public getMessage(): string {
16+
return this.msg;
17+
}
18+
19+
public getSuggestion(): Array<string> {
20+
const suggestion: Array<string> = new Array<string>();
21+
suggestion.push("Try `npx tsc --init` to initialize the TypeScript project.");
22+
23+
return suggestion;
24+
}
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CliLoggableError } from "../cli/CliLoggableError";
2+
3+
4+
export class TypeScriptConfigurationMissingError extends Error implements CliLoggableError {
5+
private readonly msg: string;
6+
private readonly missingProperty: string;
7+
8+
public constructor(filePath: string, missingProperty: string) {
9+
const message: string = `${filePath} is missing the required property \`${missingProperty}\`.`;
10+
11+
super(message);
12+
13+
this.msg = message;
14+
this.missingProperty = missingProperty;
15+
}
16+
17+
public getMessage(): string {
18+
return this.msg;
19+
}
20+
21+
public getSuggestion(): Array<string> {
22+
const suggestion: Array<string> = new Array<string>();
23+
suggestion.push(`Try checking if tsconfig.json includes the \`${this.missingProperty}\` configuration.`);
24+
25+
return suggestion;
26+
}
27+
}

0 commit comments

Comments
 (0)