@titanpl/packet 2.0.1 → 2.0.3
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 -11
- package/index.js +83 -83
- package/js/titan/builder.js +50 -50
- package/js/titan/bundle.js +164 -164
- package/js/titan/dev.js +53 -19
- package/js/titan/error-box.js +277 -277
- package/package.json +1 -1
- package/ts/titan/bundle.js +227 -227
- package/ts/titan/dev.js +53 -20
- package/ts/titan/error-box.js +277 -277
package/package.json
CHANGED
package/ts/titan/bundle.js
CHANGED
|
@@ -1,227 +1,227 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundle.js (TypeScript Version)
|
|
3
|
-
* Handles esbuild bundling with comprehensive error reporting and TypeScript type checking
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import esbuild from 'esbuild';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
import { createRequire } from 'module';
|
|
11
|
-
import ts from 'typescript';
|
|
12
|
-
import { renderErrorBox, parseEsbuildError } from './error-box.js';
|
|
13
|
-
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
const __dirname = path.dirname(__filename);
|
|
16
|
-
|
|
17
|
-
// Required for resolving node_modules inside ESM
|
|
18
|
-
const require = createRequire(import.meta.url);
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Titan Node Builtin Rewrite Map
|
|
22
|
-
*/
|
|
23
|
-
const NODE_BUILTIN_MAP = {
|
|
24
|
-
"fs": "@titanpl/node/fs",
|
|
25
|
-
"node:fs": "@titanpl/node/fs",
|
|
26
|
-
"path": "@titanpl/node/path",
|
|
27
|
-
"node:path": "@titanpl/node/path",
|
|
28
|
-
"os": "@titanpl/node/os",
|
|
29
|
-
"node:os": "@titanpl/node/os",
|
|
30
|
-
"crypto": "@titanpl/node/crypto",
|
|
31
|
-
"node:crypto": "@titanpl/node/crypto",
|
|
32
|
-
"process": "@titanpl/node/process",
|
|
33
|
-
"util": "@titanpl/node/util",
|
|
34
|
-
"node:util": "@titanpl/node/util",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const titanNodeCompatPlugin = {
|
|
38
|
-
name: "titan-node-compat",
|
|
39
|
-
setup(build) {
|
|
40
|
-
build.onResolve({ filter: /.*/ }, args => {
|
|
41
|
-
if (NODE_BUILTIN_MAP[args.path]) {
|
|
42
|
-
try {
|
|
43
|
-
const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
|
|
44
|
-
return { path: resolved };
|
|
45
|
-
} catch (e) {
|
|
46
|
-
throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
function getTitanVersion() {
|
|
54
|
-
try {
|
|
55
|
-
const pkgPath = require.resolve("@titanpl/cli/package.json");
|
|
56
|
-
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
57
|
-
} catch (e) {
|
|
58
|
-
return "1.0.0";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export class BundleError extends Error {
|
|
63
|
-
constructor(message, errors = [], warnings = []) {
|
|
64
|
-
super(message);
|
|
65
|
-
this.name = 'BundleError';
|
|
66
|
-
this.errors = errors;
|
|
67
|
-
this.warnings = warnings;
|
|
68
|
-
this.isBundleError = true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Run TypeScript type checking
|
|
74
|
-
*/
|
|
75
|
-
async function checkTypes(root) {
|
|
76
|
-
const tsconfigPath = path.join(root, 'tsconfig.json');
|
|
77
|
-
if (!fs.existsSync(tsconfigPath)) return;
|
|
78
|
-
|
|
79
|
-
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
80
|
-
if (configFile.error) {
|
|
81
|
-
throw new BundleError("Failed to load tsconfig.json", [configFile.error]);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const parsedConfig = ts.parseJsonConfigFileContent(
|
|
85
|
-
configFile.config,
|
|
86
|
-
ts.sys,
|
|
87
|
-
root
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
|
|
91
|
-
const diagnostics = ts.getPreEmitDiagnostics(program);
|
|
92
|
-
|
|
93
|
-
if (diagnostics.length > 0) {
|
|
94
|
-
const errors = diagnostics.map(d => {
|
|
95
|
-
const message = ts.flattenDiagnosticMessageText(d.messageText, '\n');
|
|
96
|
-
if (d.file) {
|
|
97
|
-
const { line, character } = d.file.getLineAndCharacterOfPosition(d.start);
|
|
98
|
-
return {
|
|
99
|
-
title: 'TypeScript Error',
|
|
100
|
-
message: message,
|
|
101
|
-
file: d.file.fileName,
|
|
102
|
-
line: line + 1,
|
|
103
|
-
column: character + 1,
|
|
104
|
-
location: `at ${d.file.fileName}:${line + 1}:${character + 1}`,
|
|
105
|
-
codeFrame: `${line + 1} | ${d.file.text.split('\n')[line]}\n${' '.repeat(String(line + 1).length)} | ${' '.repeat(character)}^`
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
return { title: 'TypeScript Error', message: message };
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
throw new BundleError(`TypeScript checking failed with ${diagnostics.length} error(s)`, errors);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function validateEntryPoint(entryPoint) {
|
|
116
|
-
const absPath = path.resolve(entryPoint);
|
|
117
|
-
if (!fs.existsSync(absPath)) {
|
|
118
|
-
throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Bundles a single file
|
|
124
|
-
*/
|
|
125
|
-
export async function bundleFile(options) {
|
|
126
|
-
const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
|
|
127
|
-
|
|
128
|
-
await validateEntryPoint(entryPoint);
|
|
129
|
-
|
|
130
|
-
const outDir = path.dirname(outfile);
|
|
131
|
-
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const result = await esbuild.build({
|
|
135
|
-
entryPoints: [entryPoint],
|
|
136
|
-
bundle: true,
|
|
137
|
-
outfile,
|
|
138
|
-
format,
|
|
139
|
-
globalName,
|
|
140
|
-
platform: 'node',
|
|
141
|
-
target,
|
|
142
|
-
logLevel: 'silent',
|
|
143
|
-
plugins: [titanNodeCompatPlugin],
|
|
144
|
-
banner: { js: "var Titan = t;" },
|
|
145
|
-
footer: options.footer || {}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (result.errors?.length) {
|
|
149
|
-
throw new BundleError(`Build failed`, result.errors);
|
|
150
|
-
}
|
|
151
|
-
} catch (err) {
|
|
152
|
-
if (err.errors) throw new BundleError(`Build failed`, err.errors);
|
|
153
|
-
throw err;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Main TS Bundler
|
|
159
|
-
*/
|
|
160
|
-
export async function bundle(options = {}) {
|
|
161
|
-
const root = options.root || process.cwd();
|
|
162
|
-
const outDir = options.outDir || path.join(root, 'dist');
|
|
163
|
-
const titanVersion = getTitanVersion();
|
|
164
|
-
|
|
165
|
-
// 1. Mandatory Type Check for TS apps
|
|
166
|
-
try {
|
|
167
|
-
await checkTypes(root);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error();
|
|
170
|
-
if (error.isBundleError && error.errors?.length) {
|
|
171
|
-
for (let i = 0; i < error.errors.length; i++) {
|
|
172
|
-
const errorInfo = error.errors[i];
|
|
173
|
-
errorInfo.titanVersion = titanVersion;
|
|
174
|
-
console.error(renderErrorBox(errorInfo));
|
|
175
|
-
console.error();
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
console.error(renderErrorBox({ title: 'TypeScript Error', message: error.message, titanVersion }));
|
|
179
|
-
}
|
|
180
|
-
throw new Error('__TITAN_BUNDLE_FAILED__');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 2. Bundle Actions
|
|
184
|
-
const actionsDir = path.join(root, 'app', 'actions');
|
|
185
|
-
const bundleDir = path.join(outDir, 'actions');
|
|
186
|
-
|
|
187
|
-
if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
|
|
188
|
-
fs.mkdirSync(bundleDir, { recursive: true });
|
|
189
|
-
|
|
190
|
-
if (!fs.existsSync(actionsDir)) return;
|
|
191
|
-
|
|
192
|
-
const files = fs.readdirSync(actionsDir).filter(f => (f.endsWith('.ts') || f.endsWith('.js')) && !f.endsWith('.d.ts'));
|
|
193
|
-
|
|
194
|
-
for (const file of files) {
|
|
195
|
-
const actionName = path.basename(file, path.extname(file));
|
|
196
|
-
const entryPoint = path.join(actionsDir, file);
|
|
197
|
-
const outfile = path.join(bundleDir, actionName + ".jsbundle");
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
await bundleFile({
|
|
201
|
-
entryPoint,
|
|
202
|
-
outfile,
|
|
203
|
-
footer: {
|
|
204
|
-
js: `
|
|
205
|
-
(function () {
|
|
206
|
-
const fn = __titan_exports["${actionName}"] || __titan_exports.default;
|
|
207
|
-
if (typeof fn !== "function") throw new Error("[TitanPL] Action '${actionName}' not found or not a function");
|
|
208
|
-
globalThis["${actionName}"] = globalThis.defineAction(fn);
|
|
209
|
-
})();`
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error();
|
|
214
|
-
if (error.isBundleError && error.errors?.length) {
|
|
215
|
-
for (const err of error.errors) {
|
|
216
|
-
const errorInfo = parseEsbuildError(err);
|
|
217
|
-
errorInfo.titanVersion = titanVersion;
|
|
218
|
-
console.error(renderErrorBox(errorInfo));
|
|
219
|
-
console.error();
|
|
220
|
-
}
|
|
221
|
-
} else {
|
|
222
|
-
console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
|
|
223
|
-
}
|
|
224
|
-
throw new Error('__TITAN_BUNDLE_FAILED__');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Bundle.js (TypeScript Version)
|
|
3
|
+
* Handles esbuild bundling with comprehensive error reporting and TypeScript type checking
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import esbuild from 'esbuild';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { createRequire } from 'module';
|
|
11
|
+
import ts from 'typescript';
|
|
12
|
+
import { renderErrorBox, parseEsbuildError } from './error-box.js';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
|
|
17
|
+
// Required for resolving node_modules inside ESM
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Titan Node Builtin Rewrite Map
|
|
22
|
+
*/
|
|
23
|
+
const NODE_BUILTIN_MAP = {
|
|
24
|
+
"fs": "@titanpl/node/fs",
|
|
25
|
+
"node:fs": "@titanpl/node/fs",
|
|
26
|
+
"path": "@titanpl/node/path",
|
|
27
|
+
"node:path": "@titanpl/node/path",
|
|
28
|
+
"os": "@titanpl/node/os",
|
|
29
|
+
"node:os": "@titanpl/node/os",
|
|
30
|
+
"crypto": "@titanpl/node/crypto",
|
|
31
|
+
"node:crypto": "@titanpl/node/crypto",
|
|
32
|
+
"process": "@titanpl/node/process",
|
|
33
|
+
"util": "@titanpl/node/util",
|
|
34
|
+
"node:util": "@titanpl/node/util",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const titanNodeCompatPlugin = {
|
|
38
|
+
name: "titan-node-compat",
|
|
39
|
+
setup(build) {
|
|
40
|
+
build.onResolve({ filter: /.*/ }, args => {
|
|
41
|
+
if (NODE_BUILTIN_MAP[args.path]) {
|
|
42
|
+
try {
|
|
43
|
+
const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
|
|
44
|
+
return { path: resolved };
|
|
45
|
+
} catch (e) {
|
|
46
|
+
throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function getTitanVersion() {
|
|
54
|
+
try {
|
|
55
|
+
const pkgPath = require.resolve("@titanpl/cli/package.json");
|
|
56
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return "1.0.0";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class BundleError extends Error {
|
|
63
|
+
constructor(message, errors = [], warnings = []) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = 'BundleError';
|
|
66
|
+
this.errors = errors;
|
|
67
|
+
this.warnings = warnings;
|
|
68
|
+
this.isBundleError = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Run TypeScript type checking
|
|
74
|
+
*/
|
|
75
|
+
async function checkTypes(root) {
|
|
76
|
+
const tsconfigPath = path.join(root, 'tsconfig.json');
|
|
77
|
+
if (!fs.existsSync(tsconfigPath)) return;
|
|
78
|
+
|
|
79
|
+
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
80
|
+
if (configFile.error) {
|
|
81
|
+
throw new BundleError("Failed to load tsconfig.json", [configFile.error]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const parsedConfig = ts.parseJsonConfigFileContent(
|
|
85
|
+
configFile.config,
|
|
86
|
+
ts.sys,
|
|
87
|
+
root
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
|
|
91
|
+
const diagnostics = ts.getPreEmitDiagnostics(program);
|
|
92
|
+
|
|
93
|
+
if (diagnostics.length > 0) {
|
|
94
|
+
const errors = diagnostics.map(d => {
|
|
95
|
+
const message = ts.flattenDiagnosticMessageText(d.messageText, '\n');
|
|
96
|
+
if (d.file) {
|
|
97
|
+
const { line, character } = d.file.getLineAndCharacterOfPosition(d.start);
|
|
98
|
+
return {
|
|
99
|
+
title: 'TypeScript Error',
|
|
100
|
+
message: message,
|
|
101
|
+
file: d.file.fileName,
|
|
102
|
+
line: line + 1,
|
|
103
|
+
column: character + 1,
|
|
104
|
+
location: `at ${d.file.fileName}:${line + 1}:${character + 1}`,
|
|
105
|
+
codeFrame: `${line + 1} | ${d.file.text.split('\n')[line]}\n${' '.repeat(String(line + 1).length)} | ${' '.repeat(character)}^`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { title: 'TypeScript Error', message: message };
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
throw new BundleError(`TypeScript checking failed with ${diagnostics.length} error(s)`, errors);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function validateEntryPoint(entryPoint) {
|
|
116
|
+
const absPath = path.resolve(entryPoint);
|
|
117
|
+
if (!fs.existsSync(absPath)) {
|
|
118
|
+
throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Bundles a single file
|
|
124
|
+
*/
|
|
125
|
+
export async function bundleFile(options) {
|
|
126
|
+
const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
|
|
127
|
+
|
|
128
|
+
await validateEntryPoint(entryPoint);
|
|
129
|
+
|
|
130
|
+
const outDir = path.dirname(outfile);
|
|
131
|
+
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = await esbuild.build({
|
|
135
|
+
entryPoints: [entryPoint],
|
|
136
|
+
bundle: true,
|
|
137
|
+
outfile,
|
|
138
|
+
format,
|
|
139
|
+
globalName,
|
|
140
|
+
platform: 'node',
|
|
141
|
+
target,
|
|
142
|
+
logLevel: 'silent',
|
|
143
|
+
plugins: [titanNodeCompatPlugin],
|
|
144
|
+
banner: { js: "var Titan = t;" },
|
|
145
|
+
footer: options.footer || {}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (result.errors?.length) {
|
|
149
|
+
throw new BundleError(`Build failed`, result.errors);
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
if (err.errors) throw new BundleError(`Build failed`, err.errors);
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Main TS Bundler
|
|
159
|
+
*/
|
|
160
|
+
export async function bundle(options = {}) {
|
|
161
|
+
const root = options.root || process.cwd();
|
|
162
|
+
const outDir = options.outDir || path.join(root, 'dist');
|
|
163
|
+
const titanVersion = getTitanVersion();
|
|
164
|
+
|
|
165
|
+
// 1. Mandatory Type Check for TS apps
|
|
166
|
+
try {
|
|
167
|
+
await checkTypes(root);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error();
|
|
170
|
+
if (error.isBundleError && error.errors?.length) {
|
|
171
|
+
for (let i = 0; i < error.errors.length; i++) {
|
|
172
|
+
const errorInfo = error.errors[i];
|
|
173
|
+
errorInfo.titanVersion = titanVersion;
|
|
174
|
+
console.error(renderErrorBox(errorInfo));
|
|
175
|
+
console.error();
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
console.error(renderErrorBox({ title: 'TypeScript Error', message: error.message, titanVersion }));
|
|
179
|
+
}
|
|
180
|
+
throw new Error('__TITAN_BUNDLE_FAILED__');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 2. Bundle Actions
|
|
184
|
+
const actionsDir = path.join(root, 'app', 'actions');
|
|
185
|
+
const bundleDir = path.join(outDir, 'actions');
|
|
186
|
+
|
|
187
|
+
if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
|
|
188
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
189
|
+
|
|
190
|
+
if (!fs.existsSync(actionsDir)) return;
|
|
191
|
+
|
|
192
|
+
const files = fs.readdirSync(actionsDir).filter(f => (f.endsWith('.ts') || f.endsWith('.js')) && !f.endsWith('.d.ts'));
|
|
193
|
+
|
|
194
|
+
for (const file of files) {
|
|
195
|
+
const actionName = path.basename(file, path.extname(file));
|
|
196
|
+
const entryPoint = path.join(actionsDir, file);
|
|
197
|
+
const outfile = path.join(bundleDir, actionName + ".jsbundle");
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
await bundleFile({
|
|
201
|
+
entryPoint,
|
|
202
|
+
outfile,
|
|
203
|
+
footer: {
|
|
204
|
+
js: `
|
|
205
|
+
(function () {
|
|
206
|
+
const fn = __titan_exports["${actionName}"] || __titan_exports.default;
|
|
207
|
+
if (typeof fn !== "function") throw new Error("[TitanPL] Action '${actionName}' not found or not a function");
|
|
208
|
+
globalThis["${actionName}"] = globalThis.defineAction(fn);
|
|
209
|
+
})();`
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error();
|
|
214
|
+
if (error.isBundleError && error.errors?.length) {
|
|
215
|
+
for (const err of error.errors) {
|
|
216
|
+
const errorInfo = parseEsbuildError(err);
|
|
217
|
+
errorInfo.titanVersion = titanVersion;
|
|
218
|
+
console.error(renderErrorBox(errorInfo));
|
|
219
|
+
console.error();
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
|
|
223
|
+
}
|
|
224
|
+
throw new Error('__TITAN_BUNDLE_FAILED__');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
package/ts/titan/dev.js
CHANGED
|
@@ -38,33 +38,53 @@ function getEngineBinaryPath(root) {
|
|
|
38
38
|
// 1. Monorepo search (dev environment)
|
|
39
39
|
let current = root;
|
|
40
40
|
for (let i = 0; i < 5; i++) {
|
|
41
|
-
const
|
|
42
|
-
if (fs.existsSync(
|
|
41
|
+
const potentialRelease = path.join(current, 'engine', 'target', 'release', binName);
|
|
42
|
+
if (fs.existsSync(potentialRelease)) return potentialRelease;
|
|
43
|
+
const potentialDebug = path.join(current, 'engine', 'target', 'debug', binName);
|
|
44
|
+
if (fs.existsSync(potentialDebug)) return potentialDebug;
|
|
45
|
+
|
|
46
|
+
// Check sibling monorepo folder for test-apps
|
|
47
|
+
const siblingRelease = path.join(current, 'titanpl', 'engine', 'target', 'release', binName);
|
|
48
|
+
if (fs.existsSync(siblingRelease)) return siblingRelease;
|
|
49
|
+
const siblingDebug = path.join(current, 'titanpl', 'engine', 'target', 'debug', binName);
|
|
50
|
+
if (fs.existsSync(siblingDebug)) return siblingDebug;
|
|
51
|
+
const siblingPkg = path.join(current, 'titanpl', 'packages', pkgName.replace('@titanpl/', ''), 'bin', binName);
|
|
52
|
+
if (fs.existsSync(siblingPkg)) return siblingPkg;
|
|
53
|
+
|
|
43
54
|
current = path.dirname(current);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
// 2. Search relative to @titanpl/cli (where optionalDependencies are installed)
|
|
47
|
-
// This is the primary path for globally-installed CLI users.
|
|
48
58
|
try {
|
|
49
|
-
const
|
|
50
|
-
const cliPkgPath =
|
|
59
|
+
const req = createRequire(import.meta.url);
|
|
60
|
+
const cliPkgPath = req.resolve('@titanpl/cli/package.json');
|
|
51
61
|
const cliDir = path.dirname(cliPkgPath);
|
|
52
|
-
// Check cli's own node_modules (global install sibling)
|
|
53
62
|
const cliNodeModulesBin = path.join(cliDir, 'node_modules', pkgName, 'bin', binName);
|
|
54
63
|
if (fs.existsSync(cliNodeModulesBin)) return cliNodeModulesBin;
|
|
55
|
-
|
|
56
|
-
const
|
|
64
|
+
|
|
65
|
+
const nodeModulesDir = path.dirname(path.dirname(cliDir));
|
|
66
|
+
const parentNodeModulesBin = path.join(nodeModulesDir, pkgName, 'bin', binName);
|
|
57
67
|
if (fs.existsSync(parentNodeModulesBin)) return parentNodeModulesBin;
|
|
58
68
|
} catch (e) { }
|
|
59
69
|
|
|
60
|
-
// 3. Search in the project's own node_modules
|
|
70
|
+
// 3. Search in the project's own node_modules directly
|
|
61
71
|
try {
|
|
62
|
-
const
|
|
63
|
-
const pkgPath =
|
|
72
|
+
const req = createRequire(import.meta.url);
|
|
73
|
+
const pkgPath = req.resolve(`${pkgName}/package.json`);
|
|
64
74
|
const binPath = path.join(path.dirname(pkgPath), 'bin', binName);
|
|
65
75
|
if (fs.existsSync(binPath)) return binPath;
|
|
66
76
|
} catch (e) { }
|
|
67
77
|
|
|
78
|
+
// Walk upwards from current dir searching for node_modules/@titanpl/engine-...
|
|
79
|
+
let searchDir = process.cwd();
|
|
80
|
+
for (let i = 0; i < 5; i++) {
|
|
81
|
+
const nmBin = path.join(searchDir, 'node_modules', pkgName, 'bin', binName);
|
|
82
|
+
if (fs.existsSync(nmBin)) return nmBin;
|
|
83
|
+
const parent = path.dirname(searchDir);
|
|
84
|
+
if (parent === searchDir) break;
|
|
85
|
+
searchDir = parent;
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
// 4. Fallback: check common global npm paths
|
|
69
89
|
const globalSearchRoots = [
|
|
70
90
|
process.env.npm_config_prefix,
|
|
@@ -76,8 +96,16 @@ function getEngineBinaryPath(root) {
|
|
|
76
96
|
for (const gRoot of globalSearchRoots) {
|
|
77
97
|
const gBin = path.join(gRoot, 'node_modules', pkgName, 'bin', binName);
|
|
78
98
|
if (fs.existsSync(gBin)) return gBin;
|
|
99
|
+
const libNodeModulesBin = path.join(gRoot, 'lib', 'node_modules', pkgName, 'bin', binName);
|
|
100
|
+
if (fs.existsSync(libNodeModulesBin)) return libNodeModulesBin;
|
|
79
101
|
}
|
|
80
102
|
|
|
103
|
+
try {
|
|
104
|
+
const globalModules = execSync('npm root -g').toString().trim();
|
|
105
|
+
const globalBin = path.join(globalModules, pkgName, 'bin', binName);
|
|
106
|
+
if (fs.existsSync(globalBin)) return globalBin;
|
|
107
|
+
} catch (e) { }
|
|
108
|
+
|
|
81
109
|
return null;
|
|
82
110
|
}
|
|
83
111
|
|
|
@@ -87,23 +115,28 @@ async function killServer() {
|
|
|
87
115
|
if (!serverProcess) return;
|
|
88
116
|
|
|
89
117
|
return new Promise((resolve) => {
|
|
90
|
-
|
|
118
|
+
if (serverProcess.killed || serverProcess.exitCode !== null) {
|
|
91
119
|
serverProcess = null;
|
|
92
120
|
resolve();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let isDone = false;
|
|
125
|
+
const onExit = () => {
|
|
126
|
+
if (isDone) return;
|
|
127
|
+
isDone = true;
|
|
128
|
+
serverProcess = null;
|
|
129
|
+
setTimeout(resolve, 300); // Grace period for OS socket release
|
|
93
130
|
};
|
|
94
131
|
|
|
95
132
|
serverProcess.on('exit', onExit);
|
|
133
|
+
serverProcess.on('error', onExit);
|
|
96
134
|
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
execSync(`taskkill /pid ${serverProcess.pid} /f /t`, { stdio: 'ignore' });
|
|
100
|
-
} catch (e) { }
|
|
101
|
-
} else {
|
|
135
|
+
try {
|
|
102
136
|
serverProcess.kill('SIGKILL');
|
|
103
|
-
}
|
|
137
|
+
} catch (e) { }
|
|
104
138
|
|
|
105
|
-
//
|
|
106
|
-
setTimeout(onExit, 500);
|
|
139
|
+
setTimeout(onExit, 800); // Fallback
|
|
107
140
|
});
|
|
108
141
|
}
|
|
109
142
|
|