Skip to content
Open
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,22 @@ Used to build your nanoforge project.
- `-c, --config [config]` path to the config file.
- `--client-outDir [clientDirectory]` specifies the client directory.
- `--server-outDir [serverDirectory]` specifies the server directory.
- `--watch` build app in watching mode. (default : `false`)

### `dev`

Used to run your nanoforge project in dev mode.

- `-d, --directory [directory]` specify the directory of the nanoforge project to build.
- `--generate` generate app files from config, like generate command in dev mode. (default : `false`)

### `generate`

Used to generate nanoforge project files from config

- `-d, --directory [directory]` specify the directory of the nanoforge project to build.
- `-c, --config [config]` path to the config file.
- `--watch` generate app in watching mode. (default : `false`)

### `install` or `add`

Expand Down Expand Up @@ -95,6 +104,7 @@ Used to start your nanoforge project
- `-p, --client-port [clientPort]` specify the port of the loader (the website to load the game)
- `--game-exposure-port [gameExposurePort]` specify the port of the game exposure
- `--server-port [serverPort]` specify the port of the server
- `--watch` run app in watching mode. (default : `false`)

## Contributing

Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
"@angular-devkit/schematics": "^21.0.1",
"@angular-devkit/schematics-cli": "^21.0.1",
"@inquirer/prompts": "^7.9.0",
"@nanoforge-dev/loader-client": "^1.0.1",
"@nanoforge-dev/loader-server": "^1.0.1",
"@nanoforge-dev/loader-client": "^1.1.0",
"@nanoforge-dev/loader-server": "^1.1.0",
"@nanoforge-dev/schematics": "^1.0.2",
"ansis": "^4.2.0",
"chokidar": "^4.0.3",
Expand Down Expand Up @@ -78,7 +78,6 @@
"husky": "^9.1.7",
"lint-staged": "^16.2.6",
"prettier": "^3.6.2",
"taze": "^19.9.0",
"tsup": "^8.5.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.2"
Expand Down
1,330 changes: 619 additions & 711 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

68 changes: 50 additions & 18 deletions src/action/actions/build.action.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import * as ansis from "ansis";
import * as process from "node:process";
import { watch } from "chokidar";
import * as console from "node:console";
import { dirname, join } from "path";

import { BuildConfig } from "@lib/config";
import { Input, getDirectoryInput } from "@lib/input";
import { getWatchInput } from "@lib/input";
import { PackageManager, PackageManagerFactory } from "@lib/package-manager";
import { Messages } from "@lib/ui";

import { getCwd } from "@utils/path";

import { getConfig } from "~/action/common/config";

import { AbstractAction } from "../abstract.action";
Expand All @@ -24,27 +29,34 @@ export class BuildAction extends AbstractAction {
try {
const directory = getDirectoryInput(options);
const config = await getConfig(options, directory);
const watch = getWatchInput(options);

const client = getPart(
config.client.build,
options.get("clientDirectory")?.value as string | undefined,
"client",
);
let res = await buildPart("Client", client, directory);
let res = await buildPart("Client", client, directory, { watch });

if (config.server.enable) {
const server = getPart(
config.server.build,
options.get("serverDirectory")?.value as string | undefined,
"server",
);
res = (await buildPart("Server", server, directory)) ? res : false;
res = (await buildPart("Server", server, directory, { watch })) ? res : false;
}

console.info();

if (watch) {
console.info(Messages.BUILD_WATCH_START);
console.info();
return;
}

if (!res) console.info(Messages.BUILD_FAILED);
else console.info(Messages.BUILD_SUCCESS);

process.exit(0);
} catch (e) {
console.error(e);
Expand All @@ -65,21 +77,41 @@ const getPart = (
};
};

const buildPart = async (name: string, part: BuildPart, directory: string) => {
const buildPart = async (
name: string,
part: BuildPart,
directory: string,
options?: { watch?: boolean },
) => {
const packageManagerName = PackageManager.BUN;

try {
const packageManager = PackageManagerFactory.create(packageManagerName);
return await packageManager.build(name, directory, part.entry, part.output, [
"--asset-naming",
"[name].[ext]",
"--target",
part.target === "client" ? "browser" : "node",
]);
} catch (error: any) {
if (error && error.message) {
console.error(ansis.red(error.message));
const packageManager = PackageManagerFactory.create(packageManagerName);

const build = async (watch = false) => {
try {
return await packageManager.build(
name,
directory,
part.entry,
part.output,
[
"--asset-naming",
"[name].[ext]",
"--target",
part.target === "client" ? "browser" : "node",
],
watch,
);
} catch (error: any) {
if (error && error.message) {
console.error(ansis.red(error.message));
}
return false;
}
return false;
}
};

if (options?.watch)
watch(dirname(join(getCwd(directory), part.entry))).on("change", () => build(true));

return await build();
};
50 changes: 50 additions & 0 deletions src/action/actions/dev.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as ansis from "ansis";

import { Input, getDevGenerateInput, getDirectoryInput } from "@lib/input";
import { PackageManager, PackageManagerFactory } from "@lib/package-manager";
import { Messages } from "@lib/ui";

import { AbstractAction } from "../abstract.action";

export class DevAction extends AbstractAction {
public async handle(_args: Input, options: Input) {
console.info(Messages.DEV_START);
console.info();

try {
const directory = getDirectoryInput(options);
const generate = getDevGenerateInput(options);

await Promise.all([
generate ? runAction("generate", [], directory, false) : undefined,
runAction("build", [], directory, false),
runAction("start", [], directory, true),
]);

console.info(Messages.DEV_SUCCESS);
process.exit(0);
} catch (e) {
console.error(Messages.DEV_FAILED);
console.error(e);
process.exit(1);
}
}
}

const runAction = async (
command: string,
params: string[],
directory: string,
stdout: boolean = false,
) => {
const packageManagerName = PackageManager.BUN;

try {
const packageManager = PackageManagerFactory.create(packageManagerName);
await packageManager.runDev(directory, "nf", {}, [command, ...params, "--watch"], !stdout);
} catch (error: any) {
if (error && error.message) {
console.error(ansis.red(error.message));
}
}
};
62 changes: 42 additions & 20 deletions src/action/actions/generate.action.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as console from "node:console";
import { join } from "path";

import { Config } from "@lib/config";
import { Input, getDirectoryInput } from "@lib/input";
import { Input, getDirectoryInput, getWatchInput } from "@lib/input";
import { AbstractCollection, Collection, CollectionFactory } from "@lib/schematics";
import { Messages } from "@lib/ui";

import { getCwd } from "@utils/path";

import { getConfig } from "~/action/common/config";

import { AbstractAction } from "../abstract.action";
Expand All @@ -20,22 +23,31 @@ interface GenerateOptions {

export class GenerateAction extends AbstractAction {
public async handle(_args: Input, options: Input) {
console.info(Messages.NEW_START);
console.info(Messages.GENERATE_START);
console.info();

try {
const directory = getDirectoryInput(options);

const config = await getConfig(options, directory);
const watch = getWatchInput(options);

const values = await getSchemaValues(config);

await generateFiles(values, directory);
await generateFiles(values, directory, watch);

console.info();
console.info(Messages.NEW_SUCCESS);

if (watch) {
console.info(Messages.GENERATE_WATCH_START);
console.info();
return;
}

console.info(Messages.GENERATE_SUCCESS);
process.exit(0);
} catch (e) {
console.error(Messages.NEW_FAILED);
console.error(Messages.GENERATE_FAILED);
console.error(e);
process.exit(1);
}
Expand All @@ -52,29 +64,39 @@ const getSchemaValues = async (config: Config): Promise<GenerateOptions> => {
};
};

const generateFiles = async (values: GenerateOptions, directory: string) => {
console.info();
const generateFiles = async (values: GenerateOptions, directory: string, watch?: boolean) => {
const collection: AbstractCollection = CollectionFactory.create(Collection.NANOFORGE, directory);

console.info();
console.info(Messages.SCHEMATICS_START);
console.info();

await executeSchematic("Client main file", collection, "part-main", {
name: values.name,
part: "client",
directory: values.directory,
language: values.language,
initFunctions: values.initFunctions,
});

if (values.server) {
await executeSchematic("Server main file", collection, "part-main", {
await executeSchematic(
"Client main file",
collection,
"part-main",
{
name: values.name,
part: "server",
part: "client",
directory: values.directory,
language: values.language,
initFunctions: values.initFunctions,
});
},
watch ? join(getCwd(directory), values.directory, ".nanoforge", "client.save.json") : undefined,
);

if (values.server) {
await executeSchematic(
"Server main file",
collection,
"part-main",
{
name: values.name,
part: "server",
directory: values.directory,
language: values.language,
initFunctions: values.initFunctions,
},
join(getCwd(directory), values.directory, ".nanoforge", "server.save.json"),
);
}
};
43 changes: 34 additions & 9 deletions src/action/actions/start.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as ansis from "ansis";
import * as console from "node:console";
import { join } from "path";

import { Input, getDirectoryInput, getStringInputWithDefault } from "@lib/input";
import { Input, getDirectoryInput, getStringInputWithDefault, getWatchInput } from "@lib/input";
import { PackageManager, PackageManagerFactory } from "@lib/package-manager";
import { Messages } from "@lib/ui";

Expand All @@ -26,9 +26,14 @@ export class StartAction extends AbstractAction {

const clientPort = getStringInputWithDefault(options, "clientPort", config.client.port);

const watch = getWatchInput(options);

await Promise.all([
config.server.enable ? this.startServer(directory, serverDir) : undefined,
this.startClient(clientPort, directory, clientDir),
config.server.enable ? this.startServer(directory, serverDir, watch) : undefined,
this.startClient(clientPort, directory, clientDir, {
watch,
serverGameDir: config.server.enable ? serverDir : undefined,
}),
]);
process.exit(0);
} catch (e) {
Expand All @@ -37,20 +42,40 @@ export class StartAction extends AbstractAction {
}
}

private async startClient(port: string, directory: string, gameDir: string): Promise<void> {
private async startClient(
port: string,
directory: string,
gameDir: string,
options?: {
watch?: boolean;
serverGameDir?: string;
},
): Promise<void> {
const path = getModulePath("@nanoforge-dev/loader-client/package.json", true);

return runPart("Client", path, {
const params: any = {
PORT: port,
GAME_DIR: getCwd(join(directory, gameDir)),
});
};
if (options?.watch) {
params["WATCH"] = "true";
if (options?.serverGameDir) {
params["WATCH_SERVER_GAME_DIR"] = getCwd(join(directory, options.serverGameDir));
}
}

return runPart("Client", path, params);
}

private startServer(directory: string, gameDir: string): Promise<void> {
private startServer(directory: string, gameDir: string, watch: boolean): Promise<void> {
const path = getModulePath("@nanoforge-dev/loader-server/package.json", true);
return runPart("Server", path, {

const params: any = {
GAME_DIR: getCwd(join(directory, gameDir)),
});
};
if (watch) params["WATCH"] = "true";

return runPart("Server", path, params);
}
}

Expand Down
Loading