@titanpl/packet 1.0.0 → 1.4.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/README.md +11 -0
- package/index.js +30 -9
- package/js/titan/builder.js +7 -30
- package/js/titan/bundle.js +25 -122
- package/js/titan/dev.js +113 -26
- package/js/titan/error-box.js +2 -2
- package/package.json +7 -2
- package/ts/titan/builder.js +66 -113
- package/ts/titan/bundle.js +227 -123
- package/ts/titan/dev.js +102 -381
- package/ts/titan/error-box.js +277 -0
- package/js/titan/titan.js +0 -129
- package/ts/titan/titan.d.ts +0 -17
- package/ts/titan/titan.js +0 -124
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @titanpl/packet
|
|
2
|
+
|
|
3
|
+
The internal bundler and module packer for Titan Planet.
|
|
4
|
+
|
|
5
|
+
## What it works (What it does)
|
|
6
|
+
This library parses all your app routes, components, middleware, and logic paths, then packages them into a static `build` output layer consisting of metadata files and JavaScript assets.
|
|
7
|
+
|
|
8
|
+
## How it works
|
|
9
|
+
This package is triggered whenever you run `titan init` or `titan build` from the `@titanpl/cli`. It hooks into tools like `esbuild` to optimize your scripts efficiently. It strips unnecessary pieces out so that only explicit route actions make their way aggressively over to the Titan runtime engine.
|
|
10
|
+
|
|
11
|
+
**Important Note:** Currently, Titan Planet and its entire package ecosystem are only for Windows. The Linux version is in development (dev only) for the new architecture and will be launched later.
|
package/index.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Get project type (js or ts)
|
|
6
|
+
*/
|
|
7
|
+
function getProjectType(root) {
|
|
8
|
+
try {
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
10
|
+
if (pkg.titan && pkg.titan.template && pkg.titan.template.includes("ts")) return "ts";
|
|
11
|
+
} catch (e) { }
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(path.join(root, "tsconfig.json"))) return "ts";
|
|
14
|
+
return "js";
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Ensure dist directory exists
|
|
@@ -27,6 +36,10 @@ function ensureDist(root) {
|
|
|
27
36
|
* Production build
|
|
28
37
|
*/
|
|
29
38
|
export async function build(root = process.cwd()) {
|
|
39
|
+
const type = getProjectType(root);
|
|
40
|
+
const { bundle: bundleActions } = await import(`./${type}/titan/bundle.js`);
|
|
41
|
+
const { buildMetadata } = await import(`./${type}/titan/builder.js`);
|
|
42
|
+
|
|
30
43
|
const dist = ensureDist(root);
|
|
31
44
|
|
|
32
45
|
await buildMetadata(root, dist);
|
|
@@ -39,10 +52,13 @@ export async function build(root = process.cwd()) {
|
|
|
39
52
|
}
|
|
40
53
|
|
|
41
54
|
/**
|
|
42
|
-
* Dev mode build
|
|
55
|
+
* Dev mode build
|
|
43
56
|
*/
|
|
44
57
|
export async function dev(options = {}) {
|
|
45
58
|
const root = typeof options === 'string' ? options : (options.root || process.cwd());
|
|
59
|
+
const type = getProjectType(root);
|
|
60
|
+
const { dev: devServer } = await import(`./${type}/titan/dev.js`);
|
|
61
|
+
|
|
46
62
|
const dist = ensureDist(root);
|
|
47
63
|
|
|
48
64
|
await devServer({
|
|
@@ -55,9 +71,14 @@ export async function dev(options = {}) {
|
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
/**
|
|
58
|
-
* Direct export of
|
|
74
|
+
* Direct export of current project tools
|
|
59
75
|
*/
|
|
60
|
-
export {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
76
|
+
export async function getTools(root = process.cwd()) {
|
|
77
|
+
const type = getProjectType(root);
|
|
78
|
+
const bundleModule = await import(`./${type}/titan/bundle.js`);
|
|
79
|
+
const builderModule = await import(`./${type}/titan/builder.js`);
|
|
80
|
+
return {
|
|
81
|
+
bundleActions: bundleModule.bundle,
|
|
82
|
+
buildMetadata: builderModule.buildMetadata
|
|
83
|
+
};
|
|
84
|
+
}
|
package/js/titan/builder.js
CHANGED
|
@@ -3,40 +3,17 @@ import path from "path";
|
|
|
3
3
|
import { pathToFileURL } from "url";
|
|
4
4
|
|
|
5
5
|
export async function buildMetadata(root, dist) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const appDir = path.join(root, "app");
|
|
7
|
+
const appFile = "app.js";
|
|
8
|
+
const appPath = path.join(appDir, appFile);
|
|
8
9
|
|
|
9
|
-
if (!fs.existsSync(
|
|
10
|
-
console.error(
|
|
10
|
+
if (!fs.existsSync(appPath)) {
|
|
11
|
+
console.error(`\x1b[31m❌ app/${appFile} not found.\x1b[0m`);
|
|
11
12
|
process.exit(1);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
// Load the app file to populate globalThis.__TITAN_ROUTES_MAP__
|
|
15
15
|
try {
|
|
16
|
-
|
|
17
|
-
if (fs.existsSync(appTsPath)) {
|
|
18
|
-
const dotTitan = path.join(root, ".titan");
|
|
19
|
-
const compiledApp = path.join(dotTitan, "app.js");
|
|
20
|
-
|
|
21
|
-
if (!fs.existsSync(dotTitan)) {
|
|
22
|
-
fs.mkdirSync(dotTitan, { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const esbuild = await import("esbuild");
|
|
26
|
-
await esbuild.build({
|
|
27
|
-
entryPoints: [appTsPath],
|
|
28
|
-
outfile: compiledApp,
|
|
29
|
-
bundle: true,
|
|
30
|
-
platform: "node",
|
|
31
|
-
format: "esm",
|
|
32
|
-
packages: "external",
|
|
33
|
-
logLevel: "silent"
|
|
34
|
-
});
|
|
35
|
-
appUrl = pathToFileURL(compiledApp).href;
|
|
36
|
-
} else {
|
|
37
|
-
appUrl = pathToFileURL(appJsPath).href;
|
|
38
|
-
}
|
|
39
|
-
|
|
16
|
+
const appUrl = pathToFileURL(appPath).href;
|
|
40
17
|
const cacheBuster = `?t=${Date.now()}`;
|
|
41
18
|
await import(appUrl + cacheBuster);
|
|
42
19
|
|
|
@@ -67,7 +44,7 @@ export async function buildMetadata(root, dist) {
|
|
|
67
44
|
);
|
|
68
45
|
|
|
69
46
|
} catch (err) {
|
|
70
|
-
console.error("❌ Failed to parse routes from app.js", err);
|
|
47
|
+
console.error("\x1b[31m❌ Failed to parse routes from app.js\x1b[0m", err);
|
|
71
48
|
process.exit(1);
|
|
72
49
|
}
|
|
73
50
|
}
|
package/js/titan/bundle.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bundle.js
|
|
3
|
-
* Handles esbuild bundling with
|
|
4
|
-
* RULE: This file handles ALL esbuild errors and prints error boxes directly
|
|
2
|
+
* Bundle.js (JavaScript Version)
|
|
3
|
+
* Handles esbuild bundling with error reporting
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import esbuild from 'esbuild';
|
|
@@ -19,32 +18,21 @@ const require = createRequire(import.meta.url);
|
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Titan Node Builtin Rewrite Map
|
|
22
|
-
* Rewrites Node builtins to @titanpl/node shims
|
|
23
21
|
*/
|
|
24
22
|
const NODE_BUILTIN_MAP = {
|
|
25
23
|
"fs": "@titanpl/node/fs",
|
|
26
24
|
"node:fs": "@titanpl/node/fs",
|
|
27
|
-
|
|
28
25
|
"path": "@titanpl/node/path",
|
|
29
26
|
"node:path": "@titanpl/node/path",
|
|
30
|
-
|
|
31
27
|
"os": "@titanpl/node/os",
|
|
32
28
|
"node:os": "@titanpl/node/os",
|
|
33
|
-
|
|
34
29
|
"crypto": "@titanpl/node/crypto",
|
|
35
30
|
"node:crypto": "@titanpl/node/crypto",
|
|
36
|
-
|
|
37
31
|
"process": "@titanpl/node/process",
|
|
38
|
-
|
|
39
32
|
"util": "@titanpl/node/util",
|
|
40
33
|
"node:util": "@titanpl/node/util",
|
|
41
34
|
};
|
|
42
35
|
|
|
43
|
-
/**
|
|
44
|
-
* Titan Node Compatibility Plugin
|
|
45
|
-
* Rewrites require/import of Node builtins
|
|
46
|
-
* Returns absolute paths (required by esbuild)
|
|
47
|
-
*/
|
|
48
36
|
const titanNodeCompatPlugin = {
|
|
49
37
|
name: "titan-node-compat",
|
|
50
38
|
setup(build) {
|
|
@@ -54,30 +42,22 @@ const titanNodeCompatPlugin = {
|
|
|
54
42
|
const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
|
|
55
43
|
return { path: resolved };
|
|
56
44
|
} catch (e) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`[Titan] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`
|
|
59
|
-
);
|
|
45
|
+
throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
|
|
60
46
|
}
|
|
61
47
|
}
|
|
62
48
|
});
|
|
63
49
|
}
|
|
64
50
|
};
|
|
65
51
|
|
|
66
|
-
/**
|
|
67
|
-
* Get Titan version for error branding
|
|
68
|
-
*/
|
|
69
52
|
function getTitanVersion() {
|
|
70
53
|
try {
|
|
71
|
-
const pkgPath = require.resolve("@
|
|
54
|
+
const pkgPath = require.resolve("@titanpl/cli/package.json");
|
|
72
55
|
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
73
56
|
} catch (e) {
|
|
74
|
-
return "
|
|
57
|
+
return "1.0.0";
|
|
75
58
|
}
|
|
76
59
|
}
|
|
77
60
|
|
|
78
|
-
/**
|
|
79
|
-
* Custom error class for bundle errors
|
|
80
|
-
*/
|
|
81
61
|
export class BundleError extends Error {
|
|
82
62
|
constructor(message, errors = [], warnings = []) {
|
|
83
63
|
super(message);
|
|
@@ -88,26 +68,10 @@ export class BundleError extends Error {
|
|
|
88
68
|
}
|
|
89
69
|
}
|
|
90
70
|
|
|
91
|
-
/**
|
|
92
|
-
* Validate entry file exists
|
|
93
|
-
*/
|
|
94
71
|
async function validateEntryPoint(entryPoint) {
|
|
95
72
|
const absPath = path.resolve(entryPoint);
|
|
96
|
-
|
|
97
73
|
if (!fs.existsSync(absPath)) {
|
|
98
|
-
throw new BundleError(
|
|
99
|
-
`Entry point does not exist: ${entryPoint}`,
|
|
100
|
-
[{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
await fs.promises.access(absPath, fs.constants.R_OK);
|
|
106
|
-
} catch {
|
|
107
|
-
throw new BundleError(
|
|
108
|
-
`Entry point is not readable: ${entryPoint}`,
|
|
109
|
-
[{ text: `Cannot read file: ${absPath}`, location: { file: entryPoint } }]
|
|
110
|
-
);
|
|
74
|
+
throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
|
|
111
75
|
}
|
|
112
76
|
}
|
|
113
77
|
|
|
@@ -115,23 +79,12 @@ async function validateEntryPoint(entryPoint) {
|
|
|
115
79
|
* Bundles a single file
|
|
116
80
|
*/
|
|
117
81
|
export async function bundleFile(options) {
|
|
118
|
-
const {
|
|
119
|
-
entryPoint,
|
|
120
|
-
outfile,
|
|
121
|
-
format = 'iife',
|
|
122
|
-
minify = false,
|
|
123
|
-
sourcemap = false,
|
|
124
|
-
platform = 'neutral',
|
|
125
|
-
globalName = '__titan_exports',
|
|
126
|
-
target = 'es2020',
|
|
127
|
-
banner = {},
|
|
128
|
-
footer = {}
|
|
129
|
-
} = options;
|
|
82
|
+
const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
|
|
130
83
|
|
|
131
84
|
await validateEntryPoint(entryPoint);
|
|
132
85
|
|
|
133
86
|
const outDir = path.dirname(outfile);
|
|
134
|
-
|
|
87
|
+
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
135
88
|
|
|
136
89
|
try {
|
|
137
90
|
const result = await esbuild.build({
|
|
@@ -140,63 +93,40 @@ export async function bundleFile(options) {
|
|
|
140
93
|
outfile,
|
|
141
94
|
format,
|
|
142
95
|
globalName,
|
|
143
|
-
platform,
|
|
96
|
+
platform: 'node',
|
|
144
97
|
target,
|
|
145
|
-
banner,
|
|
146
|
-
footer,
|
|
147
|
-
minify,
|
|
148
|
-
sourcemap,
|
|
149
98
|
logLevel: 'silent',
|
|
150
|
-
logLimit: 0,
|
|
151
|
-
write: true,
|
|
152
|
-
metafile: false,
|
|
153
99
|
plugins: [titanNodeCompatPlugin],
|
|
100
|
+
banner: { js: "var Titan = t;" },
|
|
101
|
+
footer: options.footer || {}
|
|
154
102
|
});
|
|
155
103
|
|
|
156
104
|
if (result.errors?.length) {
|
|
157
|
-
throw new BundleError(
|
|
158
|
-
`Build failed with ${result.errors.length} error(s)`,
|
|
159
|
-
result.errors,
|
|
160
|
-
result.warnings || []
|
|
161
|
-
);
|
|
105
|
+
throw new BundleError(`Build failed`, result.errors);
|
|
162
106
|
}
|
|
163
|
-
|
|
164
107
|
} catch (err) {
|
|
165
|
-
if (err.errors
|
|
166
|
-
|
|
167
|
-
`Build failed with ${err.errors.length} error(s)`,
|
|
168
|
-
err.errors,
|
|
169
|
-
err.warnings || []
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
throw new BundleError(
|
|
174
|
-
`Unexpected build error: ${err.message}`,
|
|
175
|
-
[{ text: err.message, location: { file: entryPoint } }]
|
|
176
|
-
);
|
|
108
|
+
if (err.errors) throw new BundleError(`Build failed`, err.errors);
|
|
109
|
+
throw err;
|
|
177
110
|
}
|
|
178
111
|
}
|
|
179
112
|
|
|
180
113
|
/**
|
|
181
|
-
* Main
|
|
114
|
+
* Main JS Bundler
|
|
182
115
|
*/
|
|
183
116
|
export async function bundle(options = {}) {
|
|
184
117
|
const root = options.root || process.cwd();
|
|
185
118
|
const outDir = options.outDir || path.join(root, 'dist');
|
|
119
|
+
const titanVersion = getTitanVersion();
|
|
186
120
|
|
|
187
121
|
const actionsDir = path.join(root, 'app', 'actions');
|
|
188
122
|
const bundleDir = path.join(outDir, 'actions');
|
|
189
123
|
|
|
190
|
-
if (fs.existsSync(bundleDir)) {
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
await fs.promises.mkdir(bundleDir, { recursive: true });
|
|
124
|
+
if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
|
|
125
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
194
126
|
|
|
195
127
|
if (!fs.existsSync(actionsDir)) return;
|
|
196
128
|
|
|
197
|
-
const files = fs.readdirSync(actionsDir).filter(f =>
|
|
198
|
-
(f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts')
|
|
199
|
-
);
|
|
129
|
+
const files = fs.readdirSync(actionsDir).filter(f => f.endsWith('.js'));
|
|
200
130
|
|
|
201
131
|
for (const file of files) {
|
|
202
132
|
const actionName = path.basename(file, path.extname(file));
|
|
@@ -207,54 +137,27 @@ export async function bundle(options = {}) {
|
|
|
207
137
|
await bundleFile({
|
|
208
138
|
entryPoint,
|
|
209
139
|
outfile,
|
|
210
|
-
format: 'iife',
|
|
211
|
-
globalName: '__titan_exports',
|
|
212
|
-
platform: 'node',
|
|
213
|
-
target: 'es2020',
|
|
214
|
-
banner: { js: "var Titan = t;" },
|
|
215
140
|
footer: {
|
|
216
141
|
js: `
|
|
217
142
|
(function () {
|
|
218
|
-
const fn =
|
|
219
|
-
|
|
220
|
-
__titan_exports.default;
|
|
221
|
-
|
|
222
|
-
if (typeof fn !== "function") {
|
|
223
|
-
throw new Error("[Titan] Action '${actionName}' not found or not a function");
|
|
224
|
-
}
|
|
225
|
-
|
|
143
|
+
const fn = __titan_exports["${actionName}"] || __titan_exports.default;
|
|
144
|
+
if (typeof fn !== "function") throw new Error("[TitanPL] Action '${actionName}' not found or not a function");
|
|
226
145
|
globalThis["${actionName}"] = globalThis.defineAction(fn);
|
|
227
|
-
})()
|
|
228
|
-
`
|
|
146
|
+
})();`
|
|
229
147
|
}
|
|
230
148
|
});
|
|
231
|
-
|
|
232
149
|
} catch (error) {
|
|
233
|
-
|
|
234
150
|
console.error();
|
|
235
|
-
|
|
236
|
-
const titanVersion = getTitanVersion();
|
|
237
|
-
|
|
238
151
|
if (error.isBundleError && error.errors?.length) {
|
|
239
|
-
for (
|
|
240
|
-
const errorInfo = parseEsbuildError(
|
|
241
|
-
if (error.errors.length > 1) {
|
|
242
|
-
errorInfo.title = `Build Error ${i + 1}/${error.errors.length}`;
|
|
243
|
-
}
|
|
152
|
+
for (const err of error.errors) {
|
|
153
|
+
const errorInfo = parseEsbuildError(err);
|
|
244
154
|
errorInfo.titanVersion = titanVersion;
|
|
245
155
|
console.error(renderErrorBox(errorInfo));
|
|
246
156
|
console.error();
|
|
247
157
|
}
|
|
248
158
|
} else {
|
|
249
|
-
|
|
250
|
-
title: 'Build Error',
|
|
251
|
-
file: entryPoint,
|
|
252
|
-
message: error.message || 'Unknown error',
|
|
253
|
-
titanVersion
|
|
254
|
-
};
|
|
255
|
-
console.error(renderErrorBox(errorInfo));
|
|
159
|
+
console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
|
|
256
160
|
}
|
|
257
|
-
|
|
258
161
|
throw new Error('__TITAN_BUNDLE_FAILED__');
|
|
259
162
|
}
|
|
260
163
|
}
|
package/js/titan/dev.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
|
+
import { spawn, execSync } from "child_process";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import fs from "fs";
|
|
4
5
|
import { bundle } from "./bundle.js";
|
|
5
6
|
import { buildMetadata } from "./builder.js";
|
|
6
7
|
import { createRequire } from "module";
|
|
8
|
+
import os from "os";
|
|
7
9
|
|
|
8
10
|
const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
9
11
|
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
@@ -22,31 +24,94 @@ function getTitanVersion() {
|
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
27
|
+
function getEngineBinaryPath(root) {
|
|
28
|
+
const platform = os.platform();
|
|
29
|
+
const arch = os.arch();
|
|
30
|
+
const binName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
|
|
31
|
+
const pkgName = `@titanpl/engine-${platform}-${arch}`;
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// Monorepo search
|
|
34
|
+
let current = root;
|
|
35
|
+
for (let i = 0; i < 5; i++) {
|
|
36
|
+
const potential = path.join(current, 'engine', 'target', 'release', binName);
|
|
37
|
+
if (fs.existsSync(potential)) return potential;
|
|
38
|
+
current = path.dirname(current);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// Node modules search
|
|
42
|
+
try {
|
|
43
|
+
const require = createRequire(import.meta.url);
|
|
44
|
+
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
45
|
+
const binPath = path.join(path.dirname(pkgPath), 'bin', binName);
|
|
46
|
+
if (fs.existsSync(binPath)) return binPath;
|
|
47
|
+
} catch (e) { }
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let serverProcess = null;
|
|
53
|
+
|
|
54
|
+
async function killServer() {
|
|
55
|
+
if (!serverProcess) return;
|
|
56
|
+
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const onExit = () => {
|
|
59
|
+
serverProcess = null;
|
|
60
|
+
resolve();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
serverProcess.on('exit', onExit);
|
|
64
|
+
|
|
65
|
+
if (os.platform() === 'win32') {
|
|
66
|
+
try {
|
|
67
|
+
execSync(`taskkill /pid ${serverProcess.pid} /f /t`, { stdio: 'ignore' });
|
|
68
|
+
} catch (e) { }
|
|
69
|
+
} else {
|
|
70
|
+
serverProcess.kill('SIGKILL');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setTimeout(onExit, 500);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function startServer(root, outDir) {
|
|
78
|
+
const binaryPath = getEngineBinaryPath(root);
|
|
79
|
+
if (!binaryPath) {
|
|
80
|
+
console.error(red("[TitanPL] Error: Could not find engine binary. Ensure you have installed the correct engine package."));
|
|
81
|
+
return;
|
|
42
82
|
}
|
|
83
|
+
|
|
84
|
+
const distPath = path.resolve(root, "dist");
|
|
85
|
+
|
|
86
|
+
serverProcess = spawn(binaryPath, ['run', distPath, '--watch'], {
|
|
87
|
+
stdio: 'inherit',
|
|
88
|
+
env: {
|
|
89
|
+
...process.env,
|
|
90
|
+
TITAN_ENV: 'development',
|
|
91
|
+
Titan_Dev: '1'
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
serverProcess.on('error', (err) => {
|
|
96
|
+
if (err.code === 'ENOENT') {
|
|
97
|
+
console.error(red("[TitanPL] Failed to start engine: Binary not found."));
|
|
98
|
+
} else {
|
|
99
|
+
console.error(red(`[TitanPL] Engine error: ${err.message}`));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function dev(options) {
|
|
105
|
+
const root = options.root || process.cwd();
|
|
106
|
+
const outDir = options.outDir || path.join(root, "dist");
|
|
107
|
+
|
|
43
108
|
const version = getTitanVersion();
|
|
44
109
|
|
|
45
110
|
console.clear();
|
|
46
111
|
console.log("");
|
|
47
112
|
console.log(` ${bold(cyan("⏣ Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
|
|
48
113
|
console.log("");
|
|
49
|
-
console.log(` ${gray("Type: ")}
|
|
114
|
+
console.log(` ${gray("Type: ")} JS Actions`);
|
|
50
115
|
console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
|
|
51
116
|
|
|
52
117
|
if (fs.existsSync(path.join(root, ".env"))) {
|
|
@@ -54,9 +119,25 @@ export async function dev(options) {
|
|
|
54
119
|
}
|
|
55
120
|
console.log("");
|
|
56
121
|
|
|
122
|
+
const runBuildCycle = async () => {
|
|
123
|
+
try {
|
|
124
|
+
await killServer();
|
|
125
|
+
|
|
126
|
+
await buildMetadata(root, outDir);
|
|
127
|
+
await bundle({ root, outDir });
|
|
128
|
+
|
|
129
|
+
console.log(cyan(`\n[TitanPL] Starting Engine...`));
|
|
130
|
+
startServer(root, outDir);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err.message !== '__TITAN_BUNDLE_FAILED__') {
|
|
133
|
+
console.error(red(`[TitanPL] Build failed: ${err.message}`));
|
|
134
|
+
}
|
|
135
|
+
console.log(yellow("\n[TitanPL] Waiting for fixes before restarting orbit..."));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
57
139
|
// Initial build
|
|
58
|
-
await
|
|
59
|
-
await bundle({ root, outDir });
|
|
140
|
+
await runBuildCycle();
|
|
60
141
|
|
|
61
142
|
// Watch for changes inside app/
|
|
62
143
|
const appDir = path.join(root, "app");
|
|
@@ -64,21 +145,27 @@ export async function dev(options) {
|
|
|
64
145
|
|
|
65
146
|
const watcher = chokidar.watch([appDir, envFile], {
|
|
66
147
|
ignoreInitial: true,
|
|
67
|
-
awaitWriteFinish: { stabilityThreshold:
|
|
148
|
+
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
68
149
|
});
|
|
69
150
|
|
|
151
|
+
let buildTimer = null;
|
|
70
152
|
watcher.on("all", async (event, file) => {
|
|
71
153
|
if (!file) return;
|
|
72
154
|
const relPath = path.relative(root, file);
|
|
73
|
-
if (relPath.startsWith("dist") || relPath.startsWith("server")) return;
|
|
155
|
+
if (relPath.startsWith("dist") || relPath.startsWith(".titan") || relPath.startsWith("server") || relPath.startsWith("node_modules")) return;
|
|
74
156
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
157
|
+
if (buildTimer) clearTimeout(buildTimer);
|
|
158
|
+
buildTimer = setTimeout(() => {
|
|
159
|
+
console.clear();
|
|
160
|
+
console.log(cyan(`[TitanPL] File changed: ${relPath}. Rebuilding...`));
|
|
161
|
+
runBuildCycle();
|
|
162
|
+
}, 300); // Debounce
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Cleanup on exit
|
|
166
|
+
process.on('SIGINT', async () => {
|
|
167
|
+
await killServer();
|
|
168
|
+
process.exit(0);
|
|
82
169
|
});
|
|
83
170
|
|
|
84
171
|
return watcher;
|
package/js/titan/error-box.js
CHANGED
|
@@ -25,7 +25,7 @@ const red = (text) => `\x1b[31m${text}\x1b[0m`;
|
|
|
25
25
|
function getTitanVersion() {
|
|
26
26
|
try {
|
|
27
27
|
const require = createRequire(import.meta.url);
|
|
28
|
-
const pkgPath = require.resolve("
|
|
28
|
+
const pkgPath = require.resolve("titanpl/package.json");
|
|
29
29
|
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
30
30
|
} catch (e) {
|
|
31
31
|
try {
|
|
@@ -34,7 +34,7 @@ function getTitanVersion() {
|
|
|
34
34
|
const pkgPath = path.join(cur, "package.json");
|
|
35
35
|
if (fs.existsSync(pkgPath)) {
|
|
36
36
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
37
|
-
if (pkg.name === "
|
|
37
|
+
if (pkg.name === "titanpl") return pkg.version;
|
|
38
38
|
}
|
|
39
39
|
cur = path.join(cur, "..");
|
|
40
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titanpl/packet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "The bundler for TitanPl servers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bundler",
|
|
@@ -14,5 +14,10 @@
|
|
|
14
14
|
"main": "index.js",
|
|
15
15
|
"scripts": {
|
|
16
16
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"esbuild": "^0.27.2",
|
|
20
|
+
"chokidar": "^5.0.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
17
22
|
}
|
|
18
|
-
}
|
|
23
|
+
}
|