Skip to content

Commit b58634c

Browse files
authored
release: v0.1.0 (#1)
1 parent 80f9789 commit b58634c

20 files changed

+737
-4
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: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
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]>",
1929
"license": "MIT",
30+
"devDependencies": {
31+
"@types/node": "^22.10.7",
32+
"@types/archiver": "^6.0.3"
33+
},
2034
"dependencies": {
21-
"typescript": "^5.7.3"
35+
"typescript": "^5.7.3",
36+
"archiver": "^7.0.1",
37+
"commander": "^13.1.0"
2238
},
2339
"repository": {
2440
"type": "git",

src/cli/CliLoggableError.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface CliLoggableError {
2+
getMessage(): string;
3+
getSuggestion(): Array<string>;
4+
}

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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as Path from "path";
2+
import * as File from "fs";
3+
4+
import { Project } from "../project/Project";
5+
import { NodeJsConfigurationFileNotFoundError } from "./NodeJsConfigurationFileNotFoundError";
6+
import { NodeJsConfigurationMissingError } from "./NodeJsConfigurationMissingError";
7+
8+
9+
export class NodeJsConfiguration {
10+
private readonly filePath: string;
11+
12+
public constructor(project: Project) {
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+
}
20+
}
21+
22+
public getPath(): string {
23+
return this.filePath;
24+
}
25+
26+
public getName(): string {
27+
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+
}
33+
34+
return name;
35+
}
36+
37+
public getVersion(): string {
38+
const configuration: any = JSON.parse(File.readFileSync(this.filePath, "utf-8"));
39+
const version: string = configuration.version;
40+
41+
if (!version) {
42+
throw new NodeJsConfigurationMissingError(this.filePath, "version");
43+
}
44+
45+
return version;
46+
}
47+
48+
public getMainEntry(): string {
49+
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+
}
55+
56+
return mainEntry;
57+
}
58+
}
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+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import * as Path from "path";
2+
import * as File from "fs";
3+
4+
import ts, { ParsedCommandLine, Program } from "typescript";
5+
6+
import { Project } from "../project/Project";
7+
import { TypeScriptConfigurationParseError } from "./TypeScriptConfigurationParseError";
8+
import { TypeScriptConfigurationFileNotFoundError } from "./TypeScriptConfigurationFileNotFoundError";
9+
import { TypeScriptConfigurationMissingError } from "./TypeScriptConfigurationMissingError";
10+
11+
12+
export class TypeScriptConfiguration {
13+
private static parseFile(filePath: string): ParsedCommandLine {
14+
const file: any = ts.readConfigFile(filePath, ts.sys.readFile);
15+
16+
if (file.error) {
17+
throw new TypeScriptConfigurationParseError(filePath);
18+
}
19+
20+
const parsedCommandLine: ParsedCommandLine = ts.parseJsonConfigFileContent(
21+
file.config,
22+
ts.sys,
23+
Path.dirname(filePath)
24+
);
25+
26+
if (parsedCommandLine.errors.length > 0) {
27+
throw new TypeScriptConfigurationParseError(filePath, parsedCommandLine.errors);
28+
}
29+
30+
return parsedCommandLine;
31+
}
32+
33+
private readonly filePath: string;
34+
35+
public constructor(project: Project) {
36+
this.filePath = Path.join(project.getPath(), "tsconfig.json");
37+
38+
if (!File.existsSync(this.filePath)) {
39+
throw new TypeScriptConfigurationFileNotFoundError(project.getPath());
40+
}
41+
}
42+
43+
public getEmittedDirectory(): string {
44+
const parsedCommandLine: ParsedCommandLine = TypeScriptConfiguration.parseFile(this.filePath);
45+
const emittedDirectory: string | undefined = parsedCommandLine.options.outDir;
46+
47+
if (!emittedDirectory) {
48+
throw new TypeScriptConfigurationMissingError(this.filePath, "outDir");
49+
}
50+
51+
return emittedDirectory;
52+
}
53+
54+
public getCompiledDirectoryStructure(): Array<string> {
55+
const parsedCommandLine: ParsedCommandLine = TypeScriptConfiguration.parseFile(this.filePath);
56+
57+
const program: Program = ts.createProgram({
58+
rootNames: parsedCommandLine.fileNames,
59+
options: parsedCommandLine.options,
60+
});
61+
62+
const emittedFilePaths: Array<string> = new Array<string>();
63+
const emittedDirectory: string = this.getEmittedDirectory();
64+
65+
program.emit(undefined, (fileName: string): void => {
66+
emittedFilePaths.push(fileName);
67+
});
68+
69+
return emittedFilePaths.map((filePath: string): string => {
70+
return Path.relative(emittedDirectory, filePath).replace(/\\/g, "/")
71+
});
72+
}
73+
}
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+
}

0 commit comments

Comments
 (0)