@nanoforge-dev/schematics 2.2.1-beta.88f48da → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +472 -0
- package/README.md +12 -15
- package/dist/collection.json +8 -23
- package/dist/index.cjs +21 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +19 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -4
- package/dist/index.js.map +1 -1
- package/dist/libs/component/component.factory.d.ts +1 -3
- package/dist/libs/component/component.factory.d.ts.map +1 -1
- package/dist/libs/component/component.factory.js +2 -1
- package/dist/libs/component/component.factory.js.map +1 -1
- package/dist/libs/project/files/js/.dockerignore +5 -0
- package/dist/libs/project/files/js/Dockerfile +36 -0
- package/dist/libs/project/files/js/README.md +53 -0
- package/dist/libs/project/files/js/assets/__dot__gitkeep +0 -0
- package/dist/libs/project/files/js/jsconfig.json +36 -0
- package/dist/libs/project/files/js/nanoforge.config.js +10 -0
- package/dist/libs/project/files/js/package.json.template +24 -0
- package/dist/libs/project/files/js/pnpm-workspace.yaml.template +3 -0
- package/dist/libs/project/files/js/src/components/drawable-circle-2d.component.js +404 -0
- package/dist/libs/project/files/js/src/components/position-2d.component.js +42 -0
- package/dist/libs/project/files/js/src/main.js +43 -0
- package/dist/libs/project/files/js/src/systems/draw-2d.system.js +33 -0
- package/dist/libs/project/files/js/src/systems/move-2d.system.js +34 -0
- package/dist/libs/project/files/js/src/systems/position-sync.system.js +35 -0
- package/dist/libs/project/files/ts/.dockerignore +5 -0
- package/dist/libs/project/files/ts/Dockerfile +36 -0
- package/dist/libs/project/files/ts/README.md +53 -0
- package/dist/libs/project/files/ts/assets/__dot__gitkeep +0 -0
- package/dist/libs/project/files/ts/nanoforge.config.ts +5 -0
- package/dist/libs/project/files/ts/package.json.template +24 -0
- package/dist/libs/project/files/ts/pnpm-workspace.yaml.template +3 -0
- package/dist/libs/project/files/ts/src/components/drawable-circle-2d.component.ts +367 -0
- package/dist/libs/project/files/ts/src/components/position-2d.component.ts +35 -0
- package/dist/libs/project/files/ts/src/main.ts +39 -0
- package/dist/libs/project/files/ts/src/systems/draw-2d.system.ts +25 -0
- package/dist/libs/project/files/ts/src/systems/move-2d.system.ts +26 -0
- package/dist/libs/project/files/ts/src/systems/position-sync.system.ts +27 -0
- package/dist/libs/project/files/ts/tsconfig.json +42 -0
- package/dist/libs/project/project.factory.d.ts +64 -0
- package/dist/libs/project/project.factory.d.ts.map +1 -0
- package/dist/libs/project/project.factory.js +122 -0
- package/dist/libs/project/project.factory.js.map +1 -0
- package/dist/libs/project/schema.json +83 -0
- package/dist/libs/system/system.factory.d.ts +1 -3
- package/dist/libs/system/system.factory.d.ts.map +1 -1
- package/dist/libs/system/system.factory.js +2 -1
- package/dist/libs/system/system.factory.js.map +1 -1
- package/dist/libs/workspace/files/js/.dockerignore +5 -0
- package/dist/libs/workspace/files/js/Dockerfile +36 -0
- package/dist/libs/workspace/files/js/README.md +51 -0
- package/dist/libs/workspace/files/js/__dot__gitignore +137 -0
- package/dist/libs/{application → workspace}/files/js/jsconfig.json +1 -2
- package/dist/libs/workspace/files/js/nanoforge.config.js +6 -0
- package/dist/libs/workspace/files/js/package.json.template +19 -0
- package/dist/libs/workspace/files/js/pnpm-workspace.yaml.template +6 -0
- package/dist/libs/workspace/files/ts/.dockerignore +5 -0
- package/dist/libs/workspace/files/ts/Dockerfile +36 -0
- package/dist/libs/workspace/files/ts/README.md +51 -0
- package/dist/libs/workspace/files/ts/__dot__gitignore +134 -0
- package/dist/libs/workspace/files/ts/nanoforge.config.ts +6 -0
- package/dist/libs/workspace/files/ts/package.json.template +20 -0
- package/dist/libs/workspace/files/ts/pnpm-workspace.yaml.template +6 -0
- package/dist/libs/{application → workspace}/files/ts/tsconfig.json +1 -2
- package/dist/libs/workspace/schema.json +45 -0
- package/dist/libs/workspace/workspace.factory.d.ts +42 -0
- package/dist/libs/workspace/workspace.factory.d.ts.map +1 -0
- package/dist/libs/workspace/workspace.factory.js +116 -0
- package/dist/libs/workspace/workspace.factory.js.map +1 -0
- package/package.json +23 -21
- package/dist/libs/application/application.factory.d.ts +0 -59
- package/dist/libs/application/application.factory.d.ts.map +0 -1
- package/dist/libs/application/application.factory.js +0 -57
- package/dist/libs/application/application.factory.js.map +0 -1
- package/dist/libs/application/files/js/.prettierignore +0 -8
- package/dist/libs/application/files/js/README.md +0 -166
- package/dist/libs/application/files/js/eslint.config.js +0 -11
- package/dist/libs/application/files/js/package.json.template +0 -48
- package/dist/libs/application/files/js/pnpm-workspace.yaml.template +0 -3
- package/dist/libs/application/files/js/prettier.config.js +0 -6
- package/dist/libs/application/files/ts/.prettierignore +0 -8
- package/dist/libs/application/files/ts/README.md +0 -166
- package/dist/libs/application/files/ts/eslint.config.js +0 -15
- package/dist/libs/application/files/ts/package.json.template +0 -52
- package/dist/libs/application/files/ts/pnpm-workspace.yaml.template +0 -3
- package/dist/libs/application/files/ts/prettier.config.js +0 -6
- package/dist/libs/application/schema.json +0 -70
- package/dist/libs/configuration/configuration.factory.d.ts +0 -31
- package/dist/libs/configuration/configuration.factory.d.ts.map +0 -1
- package/dist/libs/configuration/configuration.factory.js +0 -97
- package/dist/libs/configuration/configuration.factory.js.map +0 -1
- package/dist/libs/configuration/files/nanoforge.config.json +0 -1
- package/dist/libs/configuration/schema.json +0 -34
- package/dist/libs/docker/docker.factory.d.ts +0 -19
- package/dist/libs/docker/docker.factory.d.ts.map +0 -1
- package/dist/libs/docker/docker.factory.js +0 -20
- package/dist/libs/docker/docker.factory.js.map +0 -1
- package/dist/libs/docker/files/.dockerignore +0 -1
- package/dist/libs/docker/files/Dockerfile +0 -25
- package/dist/libs/docker/schema.json +0 -19
- package/dist/libs/part-base/files/js/__nanoforgeFolder__/client.save.json +0 -77
- package/dist/libs/part-base/files/js/__nanoforgeFolder__/server.save.json +0 -49
- package/dist/libs/part-base/files/js/__part__/components/example.component.js +0 -72
- package/dist/libs/part-base/files/js/__part__/init/after-init.js +0 -12
- package/dist/libs/part-base/files/js/__part__/init/after-registry-init.js +0 -14
- package/dist/libs/part-base/files/js/__part__/init/after-run.js +0 -12
- package/dist/libs/part-base/files/js/__part__/init/before-init.js +0 -13
- package/dist/libs/part-base/files/js/__part__/init/before-registry-init.js +0 -14
- package/dist/libs/part-base/files/js/__part__/init/before-run.js +0 -12
- package/dist/libs/part-base/files/js/__part__/systems/example.system.js +0 -44
- package/dist/libs/part-base/files/ts/__nanoforgeFolder__/client.save.json +0 -77
- package/dist/libs/part-base/files/ts/__nanoforgeFolder__/server.save.json +0 -49
- package/dist/libs/part-base/files/ts/__part__/components/example.component.ts +0 -55
- package/dist/libs/part-base/files/ts/__part__/init/after-init.ts +0 -5
- package/dist/libs/part-base/files/ts/__part__/init/after-registry-init.ts +0 -6
- package/dist/libs/part-base/files/ts/__part__/init/after-run.ts +0 -5
- package/dist/libs/part-base/files/ts/__part__/init/before-init.ts +0 -5
- package/dist/libs/part-base/files/ts/__part__/init/before-registry-init.ts +0 -6
- package/dist/libs/part-base/files/ts/__part__/init/before-run.ts +0 -5
- package/dist/libs/part-base/files/ts/__part__/systems/example.system.ts +0 -31
- package/dist/libs/part-base/part-base.factory.d.ts +0 -31
- package/dist/libs/part-base/part-base.factory.d.ts.map +0 -1
- package/dist/libs/part-base/part-base.factory.js +0 -33
- package/dist/libs/part-base/part-base.factory.js.map +0 -1
- package/dist/libs/part-base/schema.json +0 -35
- package/dist/libs/part-main/part-main.factory.d.ts +0 -39
- package/dist/libs/part-main/part-main.factory.d.ts.map +0 -1
- package/dist/libs/part-main/part-main.factory.js +0 -252
- package/dist/libs/part-main/part-main.factory.js.map +0 -1
- package/dist/libs/part-main/schema.json +0 -43
- /package/dist/libs/{application → project}/files/js/__dot__gitignore +0 -0
- /package/dist/libs/{application → project}/files/ts/__dot__gitignore +0 -0
- /package/dist/libs/{application → workspace}/files/js/__dot__env +0 -0
- /package/dist/libs/{application → workspace}/files/ts/__dot__env +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { join, normalize, strings } from "@angular-devkit/core";
|
|
2
|
+
import { apply, filter, mergeWith, move, renameTemplateFiles, template, url } from "@angular-devkit/schematics";
|
|
3
|
+
//#region src/utils/formatting.ts
|
|
4
|
+
const toWords = (str) => {
|
|
5
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[-_]+/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
6
|
+
};
|
|
7
|
+
const toKebabCase = (str) => {
|
|
8
|
+
return toWords(str).map((word) => word.toLowerCase()).join("-");
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/utils/registry.ts
|
|
12
|
+
const REGISTRY_URL = "https://registry.npmjs.org";
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
14
|
+
const RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
15
|
+
const compareVersions = (a, b) => {
|
|
16
|
+
const partsA = a.split(".").map(Number);
|
|
17
|
+
const partsB = b.split(".").map(Number);
|
|
18
|
+
for (let i = 0; i < 3; i++) {
|
|
19
|
+
const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);
|
|
20
|
+
if (diff !== 0) return diff;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the newest published version of an npm package that is at least
|
|
26
|
+
* `minAgeHours` old, so freshly (and potentially maliciously) published
|
|
27
|
+
* versions aren't picked up immediately. Falls back to `fallback` on any
|
|
28
|
+
* network error, timeout, missing package, or when no version satisfies the
|
|
29
|
+
* constraints, so callers never have to handle a rejected promise.
|
|
30
|
+
*/
|
|
31
|
+
const fetchTrustedVersion = async (packageName, fallback, options = {}) => {
|
|
32
|
+
const { minAgeHours = 48, major, timeoutMs = DEFAULT_TIMEOUT_MS } = options;
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`${REGISTRY_URL}/${encodeURIComponent(packageName)}`, { signal: controller.signal });
|
|
37
|
+
if (!response.ok) return fallback;
|
|
38
|
+
const time = (await response.json()).time;
|
|
39
|
+
if (!time) return fallback;
|
|
40
|
+
const cutoff = Date.now() - minAgeHours * 60 * 60 * 1e3;
|
|
41
|
+
const eligible = Object.entries(time).filter(([version]) => RELEASE_VERSION_PATTERN.test(version)).filter(([version]) => major === void 0 || Number(version.split(".")[0]) === major).filter(([, publishedAt]) => new Date(publishedAt).getTime() <= cutoff).map(([version]) => version).sort(compareVersions);
|
|
42
|
+
return eligible.at(-1) ? `^${eligible.at(-1)}` : fallback;
|
|
43
|
+
} catch {
|
|
44
|
+
return fallback;
|
|
45
|
+
} finally {
|
|
46
|
+
clearTimeout(timeout);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/libs/project/project.factory.ts
|
|
51
|
+
const resolveDepVersions = async (part) => {
|
|
52
|
+
const isClient = part === "client";
|
|
53
|
+
const [nanoforge, ecs, network, graphics2d, input] = await Promise.all([
|
|
54
|
+
fetchTrustedVersion("nanoforge", "^2"),
|
|
55
|
+
fetchTrustedVersion("@nanoforge-dev/ecs", "^2"),
|
|
56
|
+
fetchTrustedVersion("@nanoforge-dev/network", "^2"),
|
|
57
|
+
isClient ? fetchTrustedVersion("@nanoforge-dev/graphics-2d", "^2") : "^2",
|
|
58
|
+
isClient ? fetchTrustedVersion("@nanoforge-dev/input", "^2") : "^2"
|
|
59
|
+
]);
|
|
60
|
+
return {
|
|
61
|
+
ecs,
|
|
62
|
+
graphics2d,
|
|
63
|
+
input,
|
|
64
|
+
network,
|
|
65
|
+
nanoforge
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const transform = async (schema) => {
|
|
69
|
+
const packageManager = schema.packageManager ?? "npm";
|
|
70
|
+
const workspace = schema.workspace ?? true;
|
|
71
|
+
const depVersions = await resolveDepVersions(schema.part);
|
|
72
|
+
const name = schema.name ?? schema.part;
|
|
73
|
+
const packageName = schema.workspaceName ? `${toKebabCase(schema.workspaceName)}-${toKebabCase(name)}` : toKebabCase(schema.name ?? "nanoforge-app");
|
|
74
|
+
return {
|
|
75
|
+
part: schema.part,
|
|
76
|
+
appClass: schema.part === "client" ? "NanoforgeClient" : "NanoforgeServer",
|
|
77
|
+
packageName,
|
|
78
|
+
language: schema.language ?? "ts",
|
|
79
|
+
strict: schema.strict ?? true,
|
|
80
|
+
packageManager,
|
|
81
|
+
workspace,
|
|
82
|
+
ecsVersion: depVersions.ecs,
|
|
83
|
+
graphics2dVersion: depVersions.graphics2d,
|
|
84
|
+
inputVersion: depVersions.input,
|
|
85
|
+
networkVersion: depVersions.network,
|
|
86
|
+
nanoforgeVersion: depVersions.nanoforge,
|
|
87
|
+
hasServer: schema.hasServer,
|
|
88
|
+
docker: schema.docker ?? false,
|
|
89
|
+
editor: schema.editor ?? false,
|
|
90
|
+
allowBuilds: workspace ? [] : ["bun"]
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
const generate = (options, path) => {
|
|
94
|
+
return apply(url(join("./files", options.language)), [
|
|
95
|
+
template({
|
|
96
|
+
dot: ".",
|
|
97
|
+
...strings,
|
|
98
|
+
...options
|
|
99
|
+
}),
|
|
100
|
+
filter((path) => options.docker && !options.workspace || !["Dockerfile", ".dockerignore"].includes(path.split("/").at(-1) ?? "")),
|
|
101
|
+
filter((path) => options.packageManager === "pnpm" && options.allowBuilds.length > 0 || !(path.split("/").at(-1) ?? "").startsWith("pnpm-workspace.yaml")),
|
|
102
|
+
filter((path) => {
|
|
103
|
+
const filename = path.split("/").at(-1) ?? "";
|
|
104
|
+
if (filename.startsWith("drawable-circle-2d.component") || filename.startsWith("draw-2d.system")) return options.part === "client";
|
|
105
|
+
if (filename.startsWith("move-2d.system")) return options.part === "server";
|
|
106
|
+
if (filename.startsWith("position-sync.system")) return options.part === "client" && options.hasServer;
|
|
107
|
+
return true;
|
|
108
|
+
}),
|
|
109
|
+
renameTemplateFiles(),
|
|
110
|
+
move(normalize(path))
|
|
111
|
+
]);
|
|
112
|
+
};
|
|
113
|
+
const main = (schema) => {
|
|
114
|
+
return async () => {
|
|
115
|
+
const options = await transform(schema);
|
|
116
|
+
return mergeWith(generate(options, schema.directory));
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { main };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=project.factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.factory.js","names":[],"sources":["../../../src/utils/formatting.ts","../../../src/defaults.ts","../../../src/utils/registry.ts","../../../src/libs/project/project.factory.ts"],"sourcesContent":["const toWords = (str: string): string[] => {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .replace(/[-_]+/g, \" \")\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n};\n\nexport const toKebabCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.toLowerCase())\n .join(\"-\");\n};\n\nexport const toPascalCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n};\n\nexport const toCamelCase = (str: string): string => {\n const words = toWords(str);\n return words\n .map((word, i) =>\n i === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\"\");\n};\n","export const DEFAULT_APP_NAME = \"nanoforge-app\";\nexport const DEFAULT_LANGUAGE = \"ts\";\nexport const DEFAULT_PACKAGE_MANAGER = \"npm\";\nexport const DEFAULT_NANOFORGE_DEPENDENCY_VERSION = \"^2\";\nexport const DEFAULT_ENGINE_VERSION = \"^2\";\n\n/** Fallback used when the registry lookup for `@nanoforge-dev/cli` fails. */\nexport const DEFAULT_CLI_DEPENDENCY_VERSION = \"latest\";\n\n/**\n * Fallback used when the registry lookup for `typescript` fails. A specific\n * version, not a range — non-nanoforge packages are pinned exactly.\n */\nexport const DEFAULT_TYPESCRIPT_VERSION = \"6.0.3\";\n\n/**\n * Mirrors pnpm's own `minimumReleaseAge` supply-chain protection: when\n * resolving a pinned dependency version at generation time, only consider\n * versions published at least this long ago, so a freshly (and potentially\n * maliciously) published release isn't picked up immediately.\n */\nexport const MIN_RELEASE_AGE_HOURS = 48;\n","import { MIN_RELEASE_AGE_HOURS } from \"~/defaults\";\n\nconst REGISTRY_URL = \"https://registry.npmjs.org\";\nconst DEFAULT_TIMEOUT_MS = 3000;\n\nconst RELEASE_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+$/;\n\nconst compareVersions = (a: string, b: string): number => {\n const partsA = a.split(\".\").map(Number);\n const partsB = b.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) {\n const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);\n if (diff !== 0) {\n return diff;\n }\n }\n return 0;\n};\n\nexport interface FetchTrustedVersionOptions {\n /**\n * Only consider versions published at least this many hours ago, mirroring\n * pnpm's `minimumReleaseAge` supply-chain protection for a freshly\n * published (and potentially compromised) release.\n * @default MIN_RELEASE_AGE_HOURS\n */\n minAgeHours?: number;\n\n /**\n * Restrict candidates to this major version (e.g. `6` only considers\n * `6.x.x`). Useful to avoid silently jumping a generated project onto a\n * new major release.\n */\n major?: number;\n\n /** Network timeout in milliseconds. @default 3000 */\n timeoutMs?: number;\n}\n\n/**\n * Resolves the newest published version of an npm package that is at least\n * `minAgeHours` old, so freshly (and potentially maliciously) published\n * versions aren't picked up immediately. Falls back to `fallback` on any\n * network error, timeout, missing package, or when no version satisfies the\n * constraints, so callers never have to handle a rejected promise.\n */\nexport const fetchTrustedVersion = async (\n packageName: string,\n fallback: string,\n options: FetchTrustedVersionOptions = {},\n): Promise<string> => {\n const { minAgeHours = MIN_RELEASE_AGE_HOURS, major, timeoutMs = DEFAULT_TIMEOUT_MS } = options;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(`${REGISTRY_URL}/${encodeURIComponent(packageName)}`, {\n signal: controller.signal,\n });\n if (!response.ok) {\n return fallback;\n }\n\n const data = (await response.json()) as { time?: Record<string, string> };\n const time = data.time;\n if (!time) {\n return fallback;\n }\n\n const cutoff = Date.now() - minAgeHours * 60 * 60 * 1000;\n const eligible = Object.entries(time)\n .filter(([version]) => RELEASE_VERSION_PATTERN.test(version))\n .filter(([version]) => major === undefined || Number(version.split(\".\")[0]) === major)\n .filter(([, publishedAt]) => new Date(publishedAt).getTime() <= cutoff)\n .map(([version]) => version)\n .sort(compareVersions);\n\n return eligible.at(-1) ? `^${eligible.at(-1)}` : fallback;\n } catch {\n return fallback;\n } finally {\n clearTimeout(timeout);\n }\n};\n\n/**\n * Resolves several packages' trusted versions in parallel. Keys of `specs`\n * are the package name to query the registry for; values are the fallback\n * to use if that lookup fails. Returns a map from the *same* keys to the\n * resolved (or fallback) version — typed so callers don't get `| undefined`\n * back for a key they just passed in.\n */\nexport const fetchTrustedVersions = async <\n T extends Record<string, { fallback: string; major?: number }>,\n>(\n specs: T,\n): Promise<{ [K in keyof T]: string }> => {\n const entries = await Promise.all(\n Object.entries(specs).map(async ([packageName, { fallback, major }]) => {\n const version = await fetchTrustedVersion(packageName, fallback, { major });\n return [packageName, version] as const;\n }),\n );\n return Object.fromEntries(entries) as { [K in keyof T]: string };\n};\n","import { type Path, join, normalize, strings } from \"@angular-devkit/core\";\nimport {\n type Rule,\n type Source,\n apply,\n filter,\n mergeWith,\n move,\n renameTemplateFiles,\n template,\n url,\n} from \"@angular-devkit/schematics\";\n\nimport { toKebabCase } from \"@utils/formatting\";\nimport { fetchTrustedVersion } from \"@utils/registry\";\n\nimport {\n DEFAULT_APP_NAME,\n DEFAULT_LANGUAGE,\n DEFAULT_NANOFORGE_DEPENDENCY_VERSION,\n DEFAULT_PACKAGE_MANAGER,\n} from \"~/defaults\";\n\nimport { type ProjectOptions } from \"./project.options\";\nimport { type ProjectSchema } from \"./project.schema\";\n\ninterface DepVersions {\n ecs: string;\n graphics2d: string;\n input: string;\n network: string;\n nanoforge: string;\n}\n\nconst resolveDepVersions = async (part: ProjectSchema[\"part\"]): Promise<DepVersions> => {\n const isClient = part === \"client\";\n\n const [nanoforge, ecs, network, graphics2d, input] = await Promise.all([\n fetchTrustedVersion(\"nanoforge\", DEFAULT_NANOFORGE_DEPENDENCY_VERSION),\n fetchTrustedVersion(\"@nanoforge-dev/ecs\", DEFAULT_NANOFORGE_DEPENDENCY_VERSION),\n fetchTrustedVersion(\"@nanoforge-dev/network\", DEFAULT_NANOFORGE_DEPENDENCY_VERSION),\n isClient\n ? fetchTrustedVersion(\"@nanoforge-dev/graphics-2d\", DEFAULT_NANOFORGE_DEPENDENCY_VERSION)\n : DEFAULT_NANOFORGE_DEPENDENCY_VERSION,\n isClient\n ? fetchTrustedVersion(\"@nanoforge-dev/input\", DEFAULT_NANOFORGE_DEPENDENCY_VERSION)\n : DEFAULT_NANOFORGE_DEPENDENCY_VERSION,\n ]);\n\n return {\n ecs,\n graphics2d,\n input,\n network,\n nanoforge,\n };\n};\n\nconst transform = async (schema: ProjectSchema): Promise<ProjectOptions> => {\n const packageManager = schema.packageManager ?? DEFAULT_PACKAGE_MANAGER;\n const workspace = schema.workspace ?? true;\n const depVersions = await resolveDepVersions(schema.part);\n\n const name = schema.name ?? schema.part;\n const packageName = schema.workspaceName\n ? `${toKebabCase(schema.workspaceName)}-${toKebabCase(name)}`\n : toKebabCase(schema.name ?? DEFAULT_APP_NAME);\n\n return {\n part: schema.part,\n appClass: schema.part === \"client\" ? \"NanoforgeClient\" : \"NanoforgeServer\",\n packageName,\n language: schema.language ?? DEFAULT_LANGUAGE,\n strict: schema.strict ?? true,\n packageManager,\n workspace,\n ecsVersion: depVersions.ecs,\n graphics2dVersion: depVersions.graphics2d,\n inputVersion: depVersions.input,\n networkVersion: depVersions.network,\n nanoforgeVersion: depVersions.nanoforge,\n hasServer: schema.hasServer,\n docker: schema.docker ?? false,\n editor: schema.editor ?? false,\n allowBuilds: workspace ? [] : [\"bun\"],\n };\n};\n\nconst generate = (options: ProjectOptions, path: string): Source => {\n return apply(url(join(\"./files\" as Path, options.language)), [\n template({\n dot: \".\",\n ...strings,\n ...options,\n }),\n filter(\n (path) =>\n (options.docker && !options.workspace) ||\n ![\"Dockerfile\", \".dockerignore\"].includes(path.split(\"/\").at(-1) ?? \"\"),\n ),\n filter(\n (path) =>\n (options.packageManager === \"pnpm\" && options.allowBuilds.length > 0) ||\n !(path.split(\"/\").at(-1) ?? \"\").startsWith(\"pnpm-workspace.yaml\"),\n ),\n filter((path) => {\n const filename = path.split(\"/\").at(-1) ?? \"\";\n if (\n filename.startsWith(\"drawable-circle-2d.component\") ||\n filename.startsWith(\"draw-2d.system\")\n ) {\n return options.part === \"client\";\n }\n if (filename.startsWith(\"move-2d.system\")) {\n return options.part === \"server\";\n }\n if (filename.startsWith(\"position-sync.system\")) {\n return options.part === \"client\" && options.hasServer;\n }\n return true;\n }),\n renameTemplateFiles(),\n move(normalize(path)),\n ]);\n};\n\nexport const main = (schema: ProjectSchema): Rule => {\n return async () => {\n const options = await transform(schema);\n\n return mergeWith(generate(options, schema.directory));\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "SchematicsNanoForgeProject",
|
|
4
|
+
"title": "NanoForge Project Options Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"part": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["client", "server"],
|
|
10
|
+
"description": "The type of project to generate"
|
|
11
|
+
},
|
|
12
|
+
"workspaceName": {
|
|
13
|
+
"type": ["string", "null"],
|
|
14
|
+
"description": "Name of the parent workspace, used to derive the project package name. Optional - when omitted, `name` is used as the whole package name instead of being appended to the workspace name.",
|
|
15
|
+
"default": null
|
|
16
|
+
},
|
|
17
|
+
"name": {
|
|
18
|
+
"type": ["string", "null"],
|
|
19
|
+
"description": "Name used to derive the project package name. When `workspaceName` is set, the package name is `${workspaceName}-${name}` (replacing the previous `${workspaceName}-${part}` suffix). When `workspaceName` is absent, `name` becomes the whole package name. Defaults to `part`.",
|
|
20
|
+
"default": null
|
|
21
|
+
},
|
|
22
|
+
"directory": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Full destination path for this project (e.g. \"apps/client\", \"packages/player-client\"). The caller is responsible for including any part-specific segment - it is not appended automatically.",
|
|
25
|
+
"default": "apps"
|
|
26
|
+
},
|
|
27
|
+
"language": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": ["ts", "js"],
|
|
30
|
+
"description": "NanoForge project language",
|
|
31
|
+
"default": "ts"
|
|
32
|
+
},
|
|
33
|
+
"strict": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"description": "With strict mode",
|
|
36
|
+
"default": true
|
|
37
|
+
},
|
|
38
|
+
"packageManager": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"enum": ["npm", "yarn", "pnpm", "bun"],
|
|
41
|
+
"description": "The used package manager",
|
|
42
|
+
"default": "npm"
|
|
43
|
+
},
|
|
44
|
+
"workspace": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"description": "Whether this project is generated inside an existing NanoForge workspace",
|
|
47
|
+
"default": true
|
|
48
|
+
},
|
|
49
|
+
"docker": {
|
|
50
|
+
"type": "boolean",
|
|
51
|
+
"description": "Generate a Dockerfile for the project. Ignored when `workspace` is true - a project inside a workspace is built and run by the workspace's own Dockerfile instead.",
|
|
52
|
+
"default": false
|
|
53
|
+
},
|
|
54
|
+
"initFunctions": {
|
|
55
|
+
"type": "boolean",
|
|
56
|
+
"description": "Add init functions to the project",
|
|
57
|
+
"default": false
|
|
58
|
+
},
|
|
59
|
+
"hasServer": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"description": "Only meaningful when part is \"client\": whether the client syncs its entity position from a companion server instead of owning it locally. A server project always simulates and broadcasts its own position, regardless of this flag.",
|
|
62
|
+
"default": false
|
|
63
|
+
},
|
|
64
|
+
"editor": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"description": "Add editor dependencies",
|
|
67
|
+
"default": false
|
|
68
|
+
},
|
|
69
|
+
"libs": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": { "type": "string" },
|
|
72
|
+
"description": "Relative paths to shared libs this project depends on",
|
|
73
|
+
"default": []
|
|
74
|
+
},
|
|
75
|
+
"allowBuilds": {
|
|
76
|
+
"type": "array",
|
|
77
|
+
"items": { "type": "string" },
|
|
78
|
+
"description": "Package names allowed to run install/build scripts. Only applies to a standalone project (workspace: false); ignored otherwise since the workspace root already handles it.",
|
|
79
|
+
"default": []
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"required": ["part"]
|
|
83
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Rule } from "@angular-devkit/schematics";
|
|
2
|
-
|
|
3
2
|
//#region src/libs/system/system.schema.d.ts
|
|
4
3
|
interface SystemSchema {
|
|
5
4
|
/**
|
|
@@ -21,7 +20,6 @@ interface SystemSchema {
|
|
|
21
20
|
}
|
|
22
21
|
//#endregion
|
|
23
22
|
//#region src/libs/system/system.factory.d.ts
|
|
24
|
-
declare const main: (schema: SystemSchema) => Rule;
|
|
23
|
+
export declare const main: (schema: SystemSchema) => Rule;
|
|
25
24
|
//#endregion
|
|
26
|
-
export { main };
|
|
27
25
|
//# sourceMappingURL=system.factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.factory.d.ts","names":[
|
|
1
|
+
{"version":3,"file":"system.factory.d.ts","names":[],"sources":["../../../src/libs/system/system.schema.d.ts","../../../src/libs/system/system.factory.ts"],"mappings":";;UAAiB;;;;EAIf;;;;EAKA;;;;EAKA;;;;EAKA;;;;qBCiBW,OAAQ,QAAQ,iBAAe"}
|
|
@@ -27,7 +27,8 @@ const generate = (options, path, language) => {
|
|
|
27
27
|
return apply(url(join("./files", language)), rules);
|
|
28
28
|
};
|
|
29
29
|
const main = (schema) => {
|
|
30
|
-
|
|
30
|
+
const options = transform(schema);
|
|
31
|
+
return mergeWith(generate(options, schema.directory, schema.language));
|
|
31
32
|
};
|
|
32
33
|
//#endregion
|
|
33
34
|
export { main };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.factory.js","names":[],"sources":["../../../src/utils/formatting.ts","../../../src/libs/system/system.factory.ts"],"sourcesContent":["const toWords = (str: string): string[] => {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .replace(/[-_]+/g, \" \")\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n};\n\nexport const toKebabCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.toLowerCase())\n .join(\"-\");\n};\n\nexport const toPascalCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n};\n\nexport const toCamelCase = (str: string): string => {\n const words = toWords(str);\n return words\n .map((word, i) =>\n i === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\"\");\n};\n","import { type Path, join, normalize, strings } from \"@angular-devkit/core\";\nimport {\n type Rule,\n type Source,\n apply,\n mergeWith,\n move,\n template,\n url,\n} from \"@angular-devkit/schematics\";\n\nimport { toCamelCase, toKebabCase } from \"@utils/formatting\";\n\nimport { type SystemOptions } from \"./system.options\";\nimport { type SystemSchema } from \"./system.schema\";\n\nconst transform = (schema: SystemSchema): SystemOptions => {\n return {\n part: schema.part,\n functionName: toCamelCase(schema.name),\n fileName: toKebabCase(schema.name),\n };\n};\n\nconst generate = (options: SystemOptions, path: string, language: \"ts\" | \"js\"): Source => {\n const rules = [\n template({\n ...strings,\n ...options,\n }),\n move(normalize(path)),\n ];\n\n return apply(url(join(\"./files\" as Path, language)), rules);\n};\n\nexport const main = (schema: SystemSchema): Rule => {\n const options = transform(schema);\n\n return mergeWith(generate(options, schema.directory, schema.language));\n};\n"],"mappings":";;;AAAA,MAAM,WAAW,QAA0B;CACzC,OAAO,IACJ,QAAQ,mBAAmB,OAAO,CAAC,CACnC,QAAQ,yBAAyB,OAAO,CAAC,CACzC,QAAQ,UAAU,GAAG,CAAC,CACtB,KAAK,CAAC,CACN,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO;AACnB;AAEA,MAAa,eAAe,QAAwB;CAClD,OAAO,QAAQ,GAAG,CAAC,CAChB,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC,CACjC,KAAK,GAAG;AACb;AAQA,MAAa,eAAe,QAAwB;CAElD,OADc,QAAQ,GACf,CAAA,CACJ,KAAK,MAAM,MACV,MAAM,IAAI,KAAK,YAAY,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAC1F,CAAC,CACA,KAAK,EAAE;AACZ;;;ACbA,MAAM,aAAa,WAAwC;CACzD,OAAO;EACL,MAAM,OAAO;EACb,cAAc,YAAY,OAAO,IAAI;EACrC,UAAU,YAAY,OAAO,IAAI;CACnC;AACF;AAEA,MAAM,YAAY,SAAwB,MAAc,aAAkC;CACxF,MAAM,QAAQ,CACZ,SAAS;EACP,GAAG;EACH,GAAG;CACL,CAAC,GACD,KAAK,UAAU,IAAI,CAAC,CACtB;CAEA,OAAO,MAAM,IAAI,KAAK,WAAmB,QAAQ,CAAC,GAAG,KAAK;AAC5D;AAEA,MAAa,QAAQ,WAA+B;
|
|
1
|
+
{"version":3,"file":"system.factory.js","names":[],"sources":["../../../src/utils/formatting.ts","../../../src/libs/system/system.factory.ts"],"sourcesContent":["const toWords = (str: string): string[] => {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .replace(/[-_]+/g, \" \")\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n};\n\nexport const toKebabCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.toLowerCase())\n .join(\"-\");\n};\n\nexport const toPascalCase = (str: string): string => {\n return toWords(str)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n};\n\nexport const toCamelCase = (str: string): string => {\n const words = toWords(str);\n return words\n .map((word, i) =>\n i === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\"\");\n};\n","import { type Path, join, normalize, strings } from \"@angular-devkit/core\";\nimport {\n type Rule,\n type Source,\n apply,\n mergeWith,\n move,\n template,\n url,\n} from \"@angular-devkit/schematics\";\n\nimport { toCamelCase, toKebabCase } from \"@utils/formatting\";\n\nimport { type SystemOptions } from \"./system.options\";\nimport { type SystemSchema } from \"./system.schema\";\n\nconst transform = (schema: SystemSchema): SystemOptions => {\n return {\n part: schema.part,\n functionName: toCamelCase(schema.name),\n fileName: toKebabCase(schema.name),\n };\n};\n\nconst generate = (options: SystemOptions, path: string, language: \"ts\" | \"js\"): Source => {\n const rules = [\n template({\n ...strings,\n ...options,\n }),\n move(normalize(path)),\n ];\n\n return apply(url(join(\"./files\" as Path, language)), rules);\n};\n\nexport const main = (schema: SystemSchema): Rule => {\n const options = transform(schema);\n\n return mergeWith(generate(options, schema.directory, schema.language));\n};\n"],"mappings":";;;AAAA,MAAM,WAAW,QAA0B;CACzC,OAAO,IACJ,QAAQ,mBAAmB,OAAO,CAAC,CACnC,QAAQ,yBAAyB,OAAO,CAAC,CACzC,QAAQ,UAAU,GAAG,CAAC,CACtB,KAAK,CAAC,CACN,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO;AACnB;AAEA,MAAa,eAAe,QAAwB;CAClD,OAAO,QAAQ,GAAG,CAAC,CAChB,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC,CACjC,KAAK,GAAG;AACb;AAQA,MAAa,eAAe,QAAwB;CAElD,OADc,QAAQ,GACf,CAAA,CACJ,KAAK,MAAM,MACV,MAAM,IAAI,KAAK,YAAY,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAC1F,CAAC,CACA,KAAK,EAAE;AACZ;;;ACbA,MAAM,aAAa,WAAwC;CACzD,OAAO;EACL,MAAM,OAAO;EACb,cAAc,YAAY,OAAO,IAAI;EACrC,UAAU,YAAY,OAAO,IAAI;CACnC;AACF;AAEA,MAAM,YAAY,SAAwB,MAAc,aAAkC;CACxF,MAAM,QAAQ,CACZ,SAAS;EACP,GAAG;EACH,GAAG;CACL,CAAC,GACD,KAAK,UAAU,IAAI,CAAC,CACtB;CAEA,OAAO,MAAM,IAAI,KAAK,WAAmB,QAAQ,CAAC,GAAG,KAAK;AAC5D;AAEA,MAAa,QAAQ,WAA+B;CAClD,MAAM,UAAU,UAAU,MAAM;CAEhC,OAAO,UAAU,SAAS,SAAS,OAAO,WAAW,OAAO,QAAQ,CAAC;AACvE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<% if (packageManager === "bun") { %>FROM oven/bun:1.3 AS base
|
|
2
|
+
<% } else if (packageManager === "pnpm") { %>FROM ghcr.io/pnpm/pnpm:12 AS base
|
|
3
|
+
RUN pnpm runtime set node 26 -g
|
|
4
|
+
<% } else { %>FROM node:26-slim AS base
|
|
5
|
+
<% } %><% if (packageManager === "yarn") { %>
|
|
6
|
+
RUN npm install -g yarn
|
|
7
|
+
<% } %>
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
FROM base AS deps
|
|
11
|
+
<% if (packageManager === "pnpm") { %>
|
|
12
|
+
COPY pnpm-lock.yaml ./
|
|
13
|
+
RUN pnpm fetch
|
|
14
|
+
|
|
15
|
+
FROM deps AS build
|
|
16
|
+
COPY . .
|
|
17
|
+
RUN pnpm install --frozen-lockfile --offline
|
|
18
|
+
<% } else if (packageManager === "npm") { %>
|
|
19
|
+
FROM deps AS build
|
|
20
|
+
COPY . .
|
|
21
|
+
RUN npm ci
|
|
22
|
+
<% } else { %>
|
|
23
|
+
FROM deps AS build
|
|
24
|
+
COPY . .
|
|
25
|
+
RUN <%= packageManager %> install --frozen-lockfile
|
|
26
|
+
<% } %>
|
|
27
|
+
RUN <%= packageManager %> run build
|
|
28
|
+
|
|
29
|
+
<% if (packageManager === "bun") { %>FROM oven/bun:1.3-slim<% } else { %>FROM base<% } %>
|
|
30
|
+
WORKDIR /app
|
|
31
|
+
|
|
32
|
+
COPY --from=build /app .
|
|
33
|
+
<% if (packageManager !== "pnpm") { %>
|
|
34
|
+
USER <% if (packageManager === "bun") { %>bun<% } else { %>node<% } %>
|
|
35
|
+
<% } %>EXPOSE 3000
|
|
36
|
+
CMD ["<%= packageManager %>", "run", "start"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# <%= name %>
|
|
2
|
+
|
|
3
|
+
A [NanoForge](https://nanoforge.eu) game workspace.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js and <%= packageManager %>
|
|
8
|
+
- The [NanoForge CLI](https://www.npmjs.com/package/@nanoforge-dev/cli): `npm install -g @nanoforge-dev/cli`
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
<%= packageManager %> install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Structure
|
|
17
|
+
|
|
18
|
+
This is the root of your NanoForge monorepo. `nanoforge.config.js` lists the app packages, and each
|
|
19
|
+
client or server app lives under `apps/<part>` (generated separately with the `project` schematic).
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
nf dev -r
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`-r` runs the command across every app in the workspace. `nf dev` serves the game and watches your
|
|
28
|
+
files, so changes are picked up as you work. Target a single app instead with `-d apps/<part>`.
|
|
29
|
+
|
|
30
|
+
## Build & start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
nf build -r
|
|
34
|
+
nf start -r
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`nf build` compiles every app for production, and `nf start` serves the build.
|
|
38
|
+
|
|
39
|
+
## CLI reference
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
| ---------- | ------------------------------------- |
|
|
43
|
+
| `nf dev` | Develop with hot reload |
|
|
44
|
+
| `nf build` | Build for production |
|
|
45
|
+
| `nf start` | Run a built app |
|
|
46
|
+
| `-r` | Apply the command to every app |
|
|
47
|
+
|
|
48
|
+
## More info
|
|
49
|
+
|
|
50
|
+
- [Documentation](https://docs.nanoforge.eu) - CLI commands, configuration, and guides
|
|
51
|
+
- [nanoforge.eu](https://nanoforge.eu) - learn more about NanoForge
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
### VisualStudioCode template
|
|
2
|
+
.vscode/*
|
|
3
|
+
!.vscode/settings.json
|
|
4
|
+
!.vscode/tasks.json
|
|
5
|
+
!.vscode/launch.json
|
|
6
|
+
!.vscode/extensions.json
|
|
7
|
+
!.vscode/*.code-snippets
|
|
8
|
+
|
|
9
|
+
# Local History for Visual Studio Code
|
|
10
|
+
.history/
|
|
11
|
+
|
|
12
|
+
# Built Visual Studio Code Extensions
|
|
13
|
+
*.vsix
|
|
14
|
+
|
|
15
|
+
### JetBrains template
|
|
16
|
+
|
|
17
|
+
# User-specific stuff
|
|
18
|
+
.idea/**/workspace.xml
|
|
19
|
+
.idea/**/tasks.xml
|
|
20
|
+
.idea/**/usage.statistics.xml
|
|
21
|
+
.idea/**/dictionaries
|
|
22
|
+
.idea/**/shelf
|
|
23
|
+
|
|
24
|
+
# Generated files
|
|
25
|
+
.idea/**/contentModel.xml
|
|
26
|
+
|
|
27
|
+
# Sensitive or high-churn files
|
|
28
|
+
.idea/**/dataSources/
|
|
29
|
+
.idea/**/dataSources.ids
|
|
30
|
+
.idea/**/dataSources.local.xml
|
|
31
|
+
.idea/**/sqlDataSources.xml
|
|
32
|
+
.idea/**/dynamic.xml
|
|
33
|
+
.idea/**/uiDesigner.xml
|
|
34
|
+
.idea/**/dbnavigator.xml
|
|
35
|
+
|
|
36
|
+
# File-based project format
|
|
37
|
+
*.iws
|
|
38
|
+
|
|
39
|
+
# Editor-based Rest Client
|
|
40
|
+
.idea/httpRequests
|
|
41
|
+
|
|
42
|
+
### Node template
|
|
43
|
+
# Logs
|
|
44
|
+
logs
|
|
45
|
+
*.log
|
|
46
|
+
npm-debug.log*
|
|
47
|
+
yarn-debug.log*
|
|
48
|
+
yarn-error.log*
|
|
49
|
+
lerna-debug.log*
|
|
50
|
+
.pnpm-debug.log*
|
|
51
|
+
|
|
52
|
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
53
|
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
54
|
+
|
|
55
|
+
# Runtime data
|
|
56
|
+
pids
|
|
57
|
+
*.pid
|
|
58
|
+
*.seed
|
|
59
|
+
*.pid.lock
|
|
60
|
+
|
|
61
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
62
|
+
lib-cov
|
|
63
|
+
|
|
64
|
+
# Coverage directory used by tools like istanbul
|
|
65
|
+
coverage
|
|
66
|
+
*.lcov
|
|
67
|
+
|
|
68
|
+
# nyc test coverage
|
|
69
|
+
.nyc_output
|
|
70
|
+
|
|
71
|
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
72
|
+
.grunt
|
|
73
|
+
|
|
74
|
+
# Bower dependency directory (https://bower.io/)
|
|
75
|
+
bower_components
|
|
76
|
+
|
|
77
|
+
# node-waf configuration
|
|
78
|
+
.lock-wscript
|
|
79
|
+
|
|
80
|
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
81
|
+
build/Release
|
|
82
|
+
|
|
83
|
+
# Dependency directories
|
|
84
|
+
node_modules/
|
|
85
|
+
jspm_packages/
|
|
86
|
+
|
|
87
|
+
# Snowpack dependency directory (https://snowpack.dev/)
|
|
88
|
+
web_modules/
|
|
89
|
+
|
|
90
|
+
# TypeScript cache
|
|
91
|
+
*.tsbuildinfo
|
|
92
|
+
|
|
93
|
+
# Optional npm cache directory
|
|
94
|
+
.npm
|
|
95
|
+
|
|
96
|
+
# Optional eslint cache
|
|
97
|
+
.eslintcache
|
|
98
|
+
|
|
99
|
+
# Optional stylelint cache
|
|
100
|
+
.stylelintcache
|
|
101
|
+
|
|
102
|
+
# Microbundle cache
|
|
103
|
+
.rpt2_cache/
|
|
104
|
+
.rts2_cache_cjs/
|
|
105
|
+
.rts2_cache_es/
|
|
106
|
+
.rts2_cache_umd/
|
|
107
|
+
|
|
108
|
+
# Optional REPL history
|
|
109
|
+
.node_repl_history
|
|
110
|
+
|
|
111
|
+
# Output of 'npm pack'
|
|
112
|
+
*.tgz
|
|
113
|
+
|
|
114
|
+
# Yarn Integrity file
|
|
115
|
+
.yarn-integrity
|
|
116
|
+
|
|
117
|
+
# dotenv environment variable files
|
|
118
|
+
.env
|
|
119
|
+
.env.development.local
|
|
120
|
+
.env.test.local
|
|
121
|
+
.env.production.local
|
|
122
|
+
.env.local
|
|
123
|
+
|
|
124
|
+
# yarn v2
|
|
125
|
+
.yarn/cache
|
|
126
|
+
.yarn/unplugged
|
|
127
|
+
.yarn/build-state.yml
|
|
128
|
+
.yarn/install-state.gz
|
|
129
|
+
.pnp.*
|
|
130
|
+
|
|
131
|
+
# Turborepo
|
|
132
|
+
.turbo/
|
|
133
|
+
|
|
134
|
+
### NanoForge template
|
|
135
|
+
# Build/compilation output
|
|
136
|
+
.nanoforge/
|
|
137
|
+
dist/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "<%= name %>",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "nf dev -r",
|
|
6
|
+
"build": "nf build -r",
|
|
7
|
+
"start": "nf start -r"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@nanoforge-dev/cli": "<%= cliVersion %>",
|
|
11
|
+
"nanoforge": "<%= engineVersion %>"
|
|
12
|
+
},<% if (packageManager !== "pnpm") { %>
|
|
13
|
+
"workspaces": ["apps/*"],<% } %><% if (packageManager === "npm" && allowBuilds.length > 0) { %>
|
|
14
|
+
"allowScripts": {<% allowBuilds.forEach(function (pkg, i) { %>
|
|
15
|
+
"<%= pkg %>": true<% if (i < allowBuilds.length - 1) { %>,<% } %><% }); %>
|
|
16
|
+
},<% } %><% if (packageManager === "bun" && allowBuilds.length > 0) { %>
|
|
17
|
+
"trustedDependencies": [<% allowBuilds.forEach(function (pkg, i) { %>"<%= pkg %>"<% if (i < allowBuilds.length - 1) { %>, <% } %><% }); %>],<% } %>
|
|
18
|
+
"private": true
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<% if (packageManager === "bun") { %>FROM oven/bun:1.3 AS base
|
|
2
|
+
<% } else if (packageManager === "pnpm") { %>FROM ghcr.io/pnpm/pnpm:12 AS base
|
|
3
|
+
RUN pnpm runtime set node 26 -g
|
|
4
|
+
<% } else { %>FROM node:26-slim AS base
|
|
5
|
+
<% } %><% if (packageManager === "yarn") { %>
|
|
6
|
+
RUN npm install -g yarn
|
|
7
|
+
<% } %>
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
FROM base AS deps
|
|
11
|
+
<% if (packageManager === "pnpm") { %>
|
|
12
|
+
COPY pnpm-lock.yaml ./
|
|
13
|
+
RUN pnpm fetch
|
|
14
|
+
|
|
15
|
+
FROM deps AS build
|
|
16
|
+
COPY . .
|
|
17
|
+
RUN pnpm install --frozen-lockfile --offline
|
|
18
|
+
<% } else if (packageManager === "npm") { %>
|
|
19
|
+
FROM deps AS build
|
|
20
|
+
COPY . .
|
|
21
|
+
RUN npm ci
|
|
22
|
+
<% } else { %>
|
|
23
|
+
FROM deps AS build
|
|
24
|
+
COPY . .
|
|
25
|
+
RUN <%= packageManager %> install --frozen-lockfile
|
|
26
|
+
<% } %>
|
|
27
|
+
RUN <%= packageManager %> run build
|
|
28
|
+
|
|
29
|
+
<% if (packageManager === "bun") { %>FROM oven/bun:1.3-slim<% } else { %>FROM base<% } %>
|
|
30
|
+
WORKDIR /app
|
|
31
|
+
|
|
32
|
+
COPY --from=build /app .
|
|
33
|
+
<% if (packageManager !== "pnpm") { %>
|
|
34
|
+
USER <% if (packageManager === "bun") { %>bun<% } else { %>node<% } %>
|
|
35
|
+
<% } %>EXPOSE 3000
|
|
36
|
+
CMD ["<%= packageManager %>", "run", "start"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# <%= name %>
|
|
2
|
+
|
|
3
|
+
A [NanoForge](https://nanoforge.eu) game workspace.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js and <%= packageManager %>
|
|
8
|
+
- The [NanoForge CLI](https://www.npmjs.com/package/@nanoforge-dev/cli): `npm install -g @nanoforge-dev/cli`
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
<%= packageManager %> install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Structure
|
|
17
|
+
|
|
18
|
+
This is the root of your NanoForge monorepo. `nanoforge.config.ts` lists the app packages, and each
|
|
19
|
+
client or server app lives under `apps/<part>` (generated separately with the `project` schematic).
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
nf dev -r
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`-r` runs the command across every app in the workspace. `nf dev` serves the game and watches your
|
|
28
|
+
files, so changes are picked up as you work. Target a single app instead with `-d apps/<part>`.
|
|
29
|
+
|
|
30
|
+
## Build & start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
nf build -r
|
|
34
|
+
nf start -r
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`nf build` compiles every app for production, and `nf start` serves the build.
|
|
38
|
+
|
|
39
|
+
## CLI reference
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
| ---------- | ------------------------------------- |
|
|
43
|
+
| `nf dev` | Develop with hot reload |
|
|
44
|
+
| `nf build` | Build for production |
|
|
45
|
+
| `nf start` | Run a built app |
|
|
46
|
+
| `-r` | Apply the command to every app |
|
|
47
|
+
|
|
48
|
+
## More info
|
|
49
|
+
|
|
50
|
+
- [Documentation](https://docs.nanoforge.eu) - CLI commands, configuration, and guides
|
|
51
|
+
- [nanoforge.eu](https://nanoforge.eu) - learn more about NanoForge
|