@arkstack/console 0.5.2 → 0.5.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 +2 -0
- package/dist/BuildInterfaces-CPmSl41C.js +288 -0
- package/dist/{app-CNmh_S9c.js → app-DnaQWE9q.js} +7 -6
- package/dist/app.d.ts +10 -0
- package/dist/app.js +1 -1
- package/dist/index.d.ts +113 -1
- package/dist/index.js +306 -35
- package/dist/prepare.js +14 -2
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
# @arkstack/console
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@arkstack/console)
|
|
4
|
+
|
|
3
5
|
Console module for Arkstack, providing the command-line runtime and console integration layer.
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Arkstack } from "@arkstack/contract";
|
|
4
|
+
import { Node, Project } from "ts-morph";
|
|
5
|
+
|
|
6
|
+
//#region src/prepare/TSConfig.ts
|
|
7
|
+
const TSConfig = {
|
|
8
|
+
compilerOptions: {
|
|
9
|
+
module: "es2022",
|
|
10
|
+
target: "es2022",
|
|
11
|
+
lib: ["es2022", "DOM"],
|
|
12
|
+
moduleResolution: "bundler",
|
|
13
|
+
esModuleInterop: true,
|
|
14
|
+
forceConsistentCasingInFileNames: true,
|
|
15
|
+
strict: true,
|
|
16
|
+
skipLibCheck: true,
|
|
17
|
+
skipDefaultLibCheck: true,
|
|
18
|
+
strictFunctionTypes: true,
|
|
19
|
+
strictPropertyInitialization: true,
|
|
20
|
+
strictBindCallApply: true,
|
|
21
|
+
noImplicitAny: true,
|
|
22
|
+
removeComments: true,
|
|
23
|
+
experimentalDecorators: true,
|
|
24
|
+
emitDecoratorMetadata: true,
|
|
25
|
+
paths: {
|
|
26
|
+
"@": ["../src"],
|
|
27
|
+
"src/*": ["../src/*"],
|
|
28
|
+
"@app/*": ["../src/app/*"],
|
|
29
|
+
"@core/*": ["../src/core/*"],
|
|
30
|
+
"@controllers/*": ["../src/app/http/controllers/*"],
|
|
31
|
+
"@models/*": ["../src/app/models/*"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
include: [
|
|
35
|
+
"./*.d.ts",
|
|
36
|
+
"../src",
|
|
37
|
+
"../tests"
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
const BaseTCConfig = { extends: "./.arkstack/tsconfig.json" };
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/prepare/BuildInterfaces.ts
|
|
44
|
+
var BuildInterfaces = class BuildInterfaces {
|
|
45
|
+
static project;
|
|
46
|
+
static checker;
|
|
47
|
+
/**
|
|
48
|
+
* Generate configuration interfaces
|
|
49
|
+
*
|
|
50
|
+
* @param configDir
|
|
51
|
+
*/
|
|
52
|
+
static configs(configDir) {
|
|
53
|
+
configDir ??= path.join(Arkstack.rootDir(), "src/config");
|
|
54
|
+
const declaration = this.generateConfig(configDir);
|
|
55
|
+
writeFileSync(path.join(Arkstack.rootDir(), ".arkstack/ark.d.ts"), declaration, "utf8");
|
|
56
|
+
}
|
|
57
|
+
static tsconfig() {
|
|
58
|
+
const configs = {
|
|
59
|
+
".arkstack/tsconfig.json": JSON.stringify(TSConfig, void 0, 2),
|
|
60
|
+
"tsconfig.json": JSON.stringify(BaseTCConfig, void 0, 2)
|
|
61
|
+
};
|
|
62
|
+
for (const [file, config] of Object.entries(configs)) writeFileSync(path.join(Arkstack.rootDir(), file), config, "utf8");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generate an `EnvRegistry` augmentation from the application's `.env`
|
|
66
|
+
* schema, giving `env()` precise return types for app-specific variables.
|
|
67
|
+
*
|
|
68
|
+
* Mirrors {@link configs}: the declaration is appended to
|
|
69
|
+
* `.arkstack/ark.d.ts`. Keys already typed by the framework's own
|
|
70
|
+
* `EnvRegistry` are skipped so declaration merging never conflicts.
|
|
71
|
+
*
|
|
72
|
+
* @param envFile Explicit `.env` path; defaults to `.env.example` then `.env`.
|
|
73
|
+
*/
|
|
74
|
+
static env(envFile) {
|
|
75
|
+
const root = Arkstack.rootDir();
|
|
76
|
+
const file = envFile ?? BuildInterfaces.resolveEnvFile(root);
|
|
77
|
+
if (!file) return;
|
|
78
|
+
const declaration = BuildInterfaces.envRegistryFromEnv(readFileSync(file, "utf8"), [...BuildInterfaces.frameworkEnvKeys()]);
|
|
79
|
+
if (!declaration) return;
|
|
80
|
+
const target = path.join(root, ".arkstack/ark.d.ts");
|
|
81
|
+
let content = `${existsSync(target) ? readFileSync(target, "utf8") : ""}\n${declaration}\n`;
|
|
82
|
+
if (!/^\s*export\s|\bimport\s/m.test(content)) content += "\nexport {}\n";
|
|
83
|
+
writeFileSync(target, content, "utf8");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Render an `EnvRegistry` augmentation for the variables declared in the
|
|
87
|
+
* given `.env` contents. Pure (no filesystem) for testability.
|
|
88
|
+
*
|
|
89
|
+
* @param contents Raw `.env` file contents.
|
|
90
|
+
* @param skip Variable names to omit (e.g. framework-owned keys).
|
|
91
|
+
* @returns The `declare module` block, or `''` when nothing to emit.
|
|
92
|
+
*/
|
|
93
|
+
static envRegistryFromEnv(contents, skip = []) {
|
|
94
|
+
const skipped = new Set(skip);
|
|
95
|
+
const properties = Object.entries(BuildInterfaces.parseEnvFile(contents)).filter(([key]) => !skipped.has(key)).map(([key, value]) => ` ${key}: ${BuildInterfaces.inferEnvType(value)}`);
|
|
96
|
+
if (!properties.length) return "";
|
|
97
|
+
return [
|
|
98
|
+
"declare module '@arkstack/common' {",
|
|
99
|
+
" interface EnvRegistry {",
|
|
100
|
+
...properties,
|
|
101
|
+
" }",
|
|
102
|
+
"}"
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Prefer `.env.example` (the documented schema), then `.env`.
|
|
107
|
+
*
|
|
108
|
+
* @param root
|
|
109
|
+
* @returns
|
|
110
|
+
*/
|
|
111
|
+
static resolveEnvFile(root) {
|
|
112
|
+
for (const name of [".env.example", ".env"]) {
|
|
113
|
+
const file = path.join(root, name);
|
|
114
|
+
if (existsSync(file)) return file;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parse `KEY=VALUE` lines, skipping blanks, comments and invalid names.
|
|
119
|
+
*
|
|
120
|
+
* @param contents
|
|
121
|
+
* @returns
|
|
122
|
+
*/
|
|
123
|
+
static parseEnvFile(contents) {
|
|
124
|
+
const entries = {};
|
|
125
|
+
for (const raw of contents.split(/\r?\n/)) {
|
|
126
|
+
const line = raw.trim();
|
|
127
|
+
if (!line || line.startsWith("#")) continue;
|
|
128
|
+
const eq = line.indexOf("=");
|
|
129
|
+
if (eq === -1) continue;
|
|
130
|
+
const key = line.slice(0, eq).trim();
|
|
131
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
132
|
+
let value = line.slice(eq + 1).trim();
|
|
133
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
134
|
+
entries[key] = value;
|
|
135
|
+
}
|
|
136
|
+
return entries;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Infer a TS type from a value, mirroring env()'s runtime coercion.
|
|
140
|
+
*
|
|
141
|
+
* @param value
|
|
142
|
+
* @returns
|
|
143
|
+
*/
|
|
144
|
+
static inferEnvType(value) {
|
|
145
|
+
if (value === "") return "string";
|
|
146
|
+
if ([
|
|
147
|
+
"true",
|
|
148
|
+
"false",
|
|
149
|
+
"on",
|
|
150
|
+
"off"
|
|
151
|
+
].includes(value)) return "boolean";
|
|
152
|
+
if (!Number.isNaN(Number(value))) return "number";
|
|
153
|
+
return "string";
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Resolve the framework's own `EnvRegistry` keys to skip during generation.
|
|
157
|
+
*
|
|
158
|
+
* @returns
|
|
159
|
+
*/
|
|
160
|
+
static frameworkEnvKeys() {
|
|
161
|
+
try {
|
|
162
|
+
const keys = new Project({
|
|
163
|
+
tsConfigFilePath: path.join(Arkstack.rootDir(), "tsconfig.json"),
|
|
164
|
+
skipAddingFilesFromTsConfig: true
|
|
165
|
+
}).createSourceFile("__ark_env_probe__.ts", "import type { EnvRegistry } from '@arkstack/common'\ndeclare const value: EnvRegistry\n", { overwrite: true }).getVariableDeclarationOrThrow("value").getType().getProperties().map((prop) => prop.getName());
|
|
166
|
+
return new Set(keys);
|
|
167
|
+
} catch {
|
|
168
|
+
return /* @__PURE__ */ new Set();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
static generateConfig(configDir = path.join(process.cwd(), "src/config")) {
|
|
172
|
+
BuildInterfaces.project = new Project({
|
|
173
|
+
tsConfigFilePath: path.join(process.cwd(), "tsconfig.json"),
|
|
174
|
+
skipAddingFilesFromTsConfig: true
|
|
175
|
+
});
|
|
176
|
+
BuildInterfaces.checker = BuildInterfaces.project.getTypeChecker();
|
|
177
|
+
const files = readdirSync(configDir).filter((f) => f.endsWith(".ts"));
|
|
178
|
+
const imports = /* @__PURE__ */ new Map();
|
|
179
|
+
const properties = [];
|
|
180
|
+
for (const file of files) {
|
|
181
|
+
const configName = path.basename(file, ".ts");
|
|
182
|
+
const sourceFile = BuildInterfaces.project.addSourceFileAtPath(path.join(configDir, file));
|
|
183
|
+
const exportAssignment = sourceFile.getExportAssignment((e) => !e.isExportEquals());
|
|
184
|
+
if (!exportAssignment) continue;
|
|
185
|
+
const expr = exportAssignment.getExpression();
|
|
186
|
+
const annotatedType = BuildInterfaces.resolveReturnTypeAnnotation(sourceFile, expr, imports);
|
|
187
|
+
if (annotatedType) {
|
|
188
|
+
properties.push(` ${configName}: ${annotatedType}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const type = BuildInterfaces.checker.getTypeAtLocation(expr);
|
|
192
|
+
const callSignatures = type.getCallSignatures();
|
|
193
|
+
const resolvedType = callSignatures.length ? callSignatures[0].getReturnType() : type;
|
|
194
|
+
const typeStr = BuildInterfaces.resolveType(resolvedType, 2);
|
|
195
|
+
properties.push(` ${configName}: ${typeStr}`);
|
|
196
|
+
}
|
|
197
|
+
return [
|
|
198
|
+
...BuildInterfaces.renderImports(imports),
|
|
199
|
+
"declare module '@arkstack/common' {",
|
|
200
|
+
" interface ConfigRegistry {",
|
|
201
|
+
...properties,
|
|
202
|
+
" }",
|
|
203
|
+
"}",
|
|
204
|
+
"",
|
|
205
|
+
"export {}"
|
|
206
|
+
].join("\n");
|
|
207
|
+
}
|
|
208
|
+
static resolveReturnTypeAnnotation(sourceFile, expr, imports) {
|
|
209
|
+
if (!Node.isArrowFunction(expr) && !Node.isFunctionExpression(expr)) return;
|
|
210
|
+
const returnType = expr.getReturnTypeNode();
|
|
211
|
+
if (!returnType) return;
|
|
212
|
+
const referencedNames = /* @__PURE__ */ new Set();
|
|
213
|
+
if (Node.isTypeReference(returnType)) referencedNames.add(returnType.getTypeName().getText());
|
|
214
|
+
returnType.forEachDescendant((node) => {
|
|
215
|
+
if (Node.isTypeReference(node)) referencedNames.add(node.getTypeName().getText());
|
|
216
|
+
});
|
|
217
|
+
for (const name of referencedNames) {
|
|
218
|
+
const imported = BuildInterfaces.findNamedTypeImport(sourceFile, name);
|
|
219
|
+
if (imported) {
|
|
220
|
+
const specifiers = imports.get(imported.moduleSpecifier) ?? /* @__PURE__ */ new Set();
|
|
221
|
+
specifiers.add(imported.specifier);
|
|
222
|
+
imports.set(imported.moduleSpecifier, specifiers);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return returnType.getText(!!sourceFile);
|
|
226
|
+
}
|
|
227
|
+
static findNamedTypeImport(sourceFile, name) {
|
|
228
|
+
for (const declaration of sourceFile.getImportDeclarations()) for (const namedImport of declaration.getNamedImports()) {
|
|
229
|
+
const alias = namedImport.getAliasNode()?.getText();
|
|
230
|
+
const importedName = namedImport.getName();
|
|
231
|
+
if ((alias ?? importedName) !== name) continue;
|
|
232
|
+
return {
|
|
233
|
+
moduleSpecifier: declaration.getModuleSpecifierValue(),
|
|
234
|
+
specifier: alias ? `${importedName} as ${alias}` : importedName
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
static renderImports(imports) {
|
|
239
|
+
return [...imports.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([moduleSpecifier, specifiers]) => {
|
|
240
|
+
return `import type { ${[...specifiers].sort().join(", ")} } from '${moduleSpecifier}'`;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* ts-morph resolves computed keys like path.join(...) as { [x: number]: any }
|
|
245
|
+
* detect these by checking the type text for an index signature pattern
|
|
246
|
+
*
|
|
247
|
+
* @param type
|
|
248
|
+
* @returns
|
|
249
|
+
*/
|
|
250
|
+
static isDynamicMap(type) {
|
|
251
|
+
return /^\{ \[x: (string|number)\]:/.test(type.getText());
|
|
252
|
+
}
|
|
253
|
+
static resolveType(type, indent) {
|
|
254
|
+
const pad = " ".repeat(indent);
|
|
255
|
+
const innerPad = " ".repeat(indent + 1);
|
|
256
|
+
if (type.isString()) return "string";
|
|
257
|
+
if (type.isNumber()) return "number";
|
|
258
|
+
if (type.isBoolean()) return "boolean";
|
|
259
|
+
if (type.isNull()) return "null";
|
|
260
|
+
if (type.isUndefined()) return "undefined";
|
|
261
|
+
if (type.isAny() || type.isUnknown()) return "any";
|
|
262
|
+
if (type.isStringLiteral()) return `'${type.getLiteralValue()}'`;
|
|
263
|
+
if (type.isNumberLiteral()) return String(type.getLiteralValue());
|
|
264
|
+
if (type.isBooleanLiteral()) return String(type.getLiteralValue());
|
|
265
|
+
if (type.isUnion()) return type.getUnionTypes().map((t) => BuildInterfaces.resolveType(t, indent)).join(" | ");
|
|
266
|
+
if (type.isArray()) {
|
|
267
|
+
const elementType = type.getArrayElementTypeOrThrow();
|
|
268
|
+
return `${BuildInterfaces.resolveType(elementType, indent)}[]`;
|
|
269
|
+
}
|
|
270
|
+
if (type.getCallSignatures().length) return "Function";
|
|
271
|
+
if (type.isObject()) {
|
|
272
|
+
if (BuildInterfaces.isDynamicMap(type)) return "Record<string, string>";
|
|
273
|
+
const props = type.getProperties();
|
|
274
|
+
if (!props.length) return "Record<string, any>";
|
|
275
|
+
return `{\n${props.map((prop) => {
|
|
276
|
+
const decl = prop.getDeclarations()[0];
|
|
277
|
+
if (!decl) return `${innerPad}${prop.getName()}: any`;
|
|
278
|
+
const propType = BuildInterfaces.checker.getTypeOfSymbolAtLocation(prop, decl);
|
|
279
|
+
const optional = prop.isOptional() ? "?" : "";
|
|
280
|
+
return `${innerPad}${prop.getName()}${optional}: ${BuildInterfaces.resolveType(propType, indent + 1)}`;
|
|
281
|
+
}).join("\n")}\n${pad}}`;
|
|
282
|
+
}
|
|
283
|
+
return type.getText();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
//#endregion
|
|
288
|
+
export { BaseTCConfig as n, TSConfig as r, BuildInterfaces as t };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import path, { isAbsolute, join } from "node:path";
|
|
3
|
+
import { Arkstack } from "@arkstack/contract";
|
|
2
4
|
import { CliApp } from "resora";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
5
|
|
|
5
6
|
//#region dist/config.js
|
|
6
7
|
const defaultConfig = (app) => {
|
|
@@ -22,11 +23,11 @@ const defaultConfig = (app) => {
|
|
|
22
23
|
//#region dist/app.js
|
|
23
24
|
const resolveStubsDir = (config, options, core) => {
|
|
24
25
|
const configuredDir = config?.localStubsDir;
|
|
25
|
-
if (configuredDir) return isAbsolute(configuredDir) ? configuredDir : join(
|
|
26
|
+
if (configuredDir) return isAbsolute(configuredDir) ? configuredDir : join(Arkstack.rootDir(), configuredDir);
|
|
26
27
|
if (!options?.stubsDir) {
|
|
27
28
|
const driver = core?.getDriver().name ?? "h3";
|
|
28
|
-
let stubsDir = path.resolve(
|
|
29
|
-
if (!existsSync(stubsDir)) stubsDir = path.resolve(
|
|
29
|
+
let stubsDir = path.resolve(Arkstack.rootDir(), `node_modules/@arkstack/driver-${driver}/stubs`);
|
|
30
|
+
if (!existsSync(stubsDir)) stubsDir = path.resolve(Arkstack.rootDir(), "stubs");
|
|
30
31
|
return stubsDir;
|
|
31
32
|
}
|
|
32
33
|
return options?.stubsDir;
|
|
@@ -44,7 +45,7 @@ var ArkstackConsoleApp = class extends CliApp {
|
|
|
44
45
|
this.mergeConfig();
|
|
45
46
|
const normalized = name.endsWith("Controller") ? name.replace(/controller/i, "") : name;
|
|
46
47
|
let controllerName = normalized.endsWith("Controller") ? normalized : `${normalized}Controller`;
|
|
47
|
-
const outputPath = join(path.resolve(
|
|
48
|
+
const outputPath = join(path.resolve(Arkstack.rootDir(), "src", "app/http/controllers"), `${controllerName}.${opts?.ext ?? "ts"}`);
|
|
48
49
|
const stubsDir = resolveStubsDir(this.config, this.options, this.core);
|
|
49
50
|
if (!stubsDir) {
|
|
50
51
|
console.error("Error: stubsDir is not configured. Set stubsDir in resora.config.js.");
|
|
@@ -72,7 +73,7 @@ var ArkstackConsoleApp = class extends CliApp {
|
|
|
72
73
|
* @returns
|
|
73
74
|
*/
|
|
74
75
|
normalizePath = (p) => {
|
|
75
|
-
return p.replace(
|
|
76
|
+
return p.replace(Arkstack.rootDir(), "");
|
|
76
77
|
};
|
|
77
78
|
/**
|
|
78
79
|
* Recursively merge defaultConfig with config from resora.config.js, giving
|
package/dist/app.d.ts
CHANGED
|
@@ -21,7 +21,17 @@ declare class ArkstackConsoleApp<TCore extends Core> extends CliApp {
|
|
|
21
21
|
private readonly options;
|
|
22
22
|
constructor(core: TCore, options: ConsoleAppOptions);
|
|
23
23
|
makeController: (name: string, opts: any) => string;
|
|
24
|
+
/**
|
|
25
|
+
* Normalize a file path by removing the current working directory from it.
|
|
26
|
+
*
|
|
27
|
+
* @param p
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
24
30
|
normalizePath: (p: string) => string;
|
|
31
|
+
/**
|
|
32
|
+
* Recursively merge defaultConfig with config from resora.config.js, giving
|
|
33
|
+
* precedence to resora.
|
|
34
|
+
*/
|
|
25
35
|
mergeConfig: () => void;
|
|
26
36
|
}
|
|
27
37
|
//#endregion
|
package/dist/app.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,119 @@
|
|
|
1
|
+
//#region src/prepare/BuildInterfaces.d.ts
|
|
2
|
+
declare class BuildInterfaces {
|
|
3
|
+
private static project;
|
|
4
|
+
private static checker;
|
|
5
|
+
/**
|
|
6
|
+
* Generate configuration interfaces
|
|
7
|
+
*
|
|
8
|
+
* @param configDir
|
|
9
|
+
*/
|
|
10
|
+
static configs(configDir?: string): void;
|
|
11
|
+
static tsconfig(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Generate an `EnvRegistry` augmentation from the application's `.env`
|
|
14
|
+
* schema, giving `env()` precise return types for app-specific variables.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors {@link configs}: the declaration is appended to
|
|
17
|
+
* `.arkstack/ark.d.ts`. Keys already typed by the framework's own
|
|
18
|
+
* `EnvRegistry` are skipped so declaration merging never conflicts.
|
|
19
|
+
*
|
|
20
|
+
* @param envFile Explicit `.env` path; defaults to `.env.example` then `.env`.
|
|
21
|
+
*/
|
|
22
|
+
static env(envFile?: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Render an `EnvRegistry` augmentation for the variables declared in the
|
|
25
|
+
* given `.env` contents. Pure (no filesystem) for testability.
|
|
26
|
+
*
|
|
27
|
+
* @param contents Raw `.env` file contents.
|
|
28
|
+
* @param skip Variable names to omit (e.g. framework-owned keys).
|
|
29
|
+
* @returns The `declare module` block, or `''` when nothing to emit.
|
|
30
|
+
*/
|
|
31
|
+
static envRegistryFromEnv(contents: string, skip?: string[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Prefer `.env.example` (the documented schema), then `.env`.
|
|
34
|
+
*
|
|
35
|
+
* @param root
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
private static resolveEnvFile;
|
|
39
|
+
/**
|
|
40
|
+
* Parse `KEY=VALUE` lines, skipping blanks, comments and invalid names.
|
|
41
|
+
*
|
|
42
|
+
* @param contents
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
private static parseEnvFile;
|
|
46
|
+
/**
|
|
47
|
+
* Infer a TS type from a value, mirroring env()'s runtime coercion.
|
|
48
|
+
*
|
|
49
|
+
* @param value
|
|
50
|
+
* @returns
|
|
51
|
+
*/
|
|
52
|
+
private static inferEnvType;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the framework's own `EnvRegistry` keys to skip during generation.
|
|
55
|
+
*
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
private static frameworkEnvKeys;
|
|
59
|
+
private static generateConfig;
|
|
60
|
+
private static resolveReturnTypeAnnotation;
|
|
61
|
+
private static findNamedTypeImport;
|
|
62
|
+
private static renderImports;
|
|
63
|
+
/**
|
|
64
|
+
* ts-morph resolves computed keys like path.join(...) as { [x: number]: any }
|
|
65
|
+
* detect these by checking the type text for an index signature pattern
|
|
66
|
+
*
|
|
67
|
+
* @param type
|
|
68
|
+
* @returns
|
|
69
|
+
*/
|
|
70
|
+
private static isDynamicMap;
|
|
71
|
+
private static resolveType;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/prepare/TSConfig.d.ts
|
|
75
|
+
declare const TSConfig: {
|
|
76
|
+
compilerOptions: {
|
|
77
|
+
module: string;
|
|
78
|
+
target: string;
|
|
79
|
+
lib: string[];
|
|
80
|
+
moduleResolution: string;
|
|
81
|
+
esModuleInterop: boolean;
|
|
82
|
+
forceConsistentCasingInFileNames: boolean;
|
|
83
|
+
strict: boolean;
|
|
84
|
+
skipLibCheck: boolean;
|
|
85
|
+
skipDefaultLibCheck: boolean;
|
|
86
|
+
strictFunctionTypes: boolean;
|
|
87
|
+
strictPropertyInitialization: boolean;
|
|
88
|
+
strictBindCallApply: boolean;
|
|
89
|
+
noImplicitAny: boolean;
|
|
90
|
+
removeComments: boolean;
|
|
91
|
+
experimentalDecorators: boolean;
|
|
92
|
+
emitDecoratorMetadata: boolean;
|
|
93
|
+
paths: {
|
|
94
|
+
'@': string[];
|
|
95
|
+
'src/*': string[];
|
|
96
|
+
'@app/*': string[];
|
|
97
|
+
'@core/*': string[];
|
|
98
|
+
'@controllers/*': string[];
|
|
99
|
+
'@models/*': string[];
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
include: string[];
|
|
103
|
+
};
|
|
104
|
+
declare const BaseTCConfig: {
|
|
105
|
+
extends: string;
|
|
106
|
+
};
|
|
107
|
+
//#endregion
|
|
1
108
|
//#region src/index.d.ts
|
|
2
109
|
interface RunConsoleOptions {
|
|
3
110
|
logo?: string;
|
|
4
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Runs the console kernel, initializing the application and registering commands.
|
|
114
|
+
*
|
|
115
|
+
* @param options
|
|
116
|
+
*/
|
|
5
117
|
declare const runConsoleKernel: (options?: RunConsoleOptions) => Promise<void>;
|
|
6
118
|
//#endregion
|
|
7
|
-
export { RunConsoleOptions, runConsoleKernel };
|
|
119
|
+
export { BaseTCConfig, BuildInterfaces, RunConsoleOptions, TSConfig, runConsoleKernel };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as ArkstackConsoleApp } from "./app-
|
|
3
|
-
import {
|
|
2
|
+
import { t as ArkstackConsoleApp } from "./app-DnaQWE9q.js";
|
|
3
|
+
import { n as BaseTCConfig, r as TSConfig, t as BuildInterfaces } from "./BuildInterfaces-CPmSl41C.js";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { Publisher, abort, abortIf, assertFound, config, discoverCommands, env, importFile, initializeGlobalContext, loadPrototypes, outputDir, rebuildOutput } from "@arkstack/common";
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
4
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import path, { join } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
+
import path, { dirname, join } from "node:path";
|
|
9
|
+
import { Arkstack } from "@arkstack/contract";
|
|
10
|
+
import { MakeResource, applyRuntimeConfig, getDefaultConfig } from "resora";
|
|
8
11
|
import { Command, Kernel } from "@h3ravel/musket";
|
|
9
12
|
import { spawn } from "node:child_process";
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
10
14
|
import { resolve } from "path";
|
|
11
15
|
import { writeFile } from "fs/promises";
|
|
12
16
|
import { CliApp as CliApp$1 } from "arkormx";
|
|
@@ -15,14 +19,19 @@ import { str } from "@h3ravel/support";
|
|
|
15
19
|
|
|
16
20
|
//#region dist/commands/BuildCommand.js
|
|
17
21
|
var BuildCommand = class extends Command {
|
|
18
|
-
signature =
|
|
22
|
+
signature = `build
|
|
23
|
+
{--d|dev : Run the build in dev mode, the dev server will not be fired}
|
|
24
|
+
`;
|
|
19
25
|
description = "Build the application for production";
|
|
20
26
|
async handle() {
|
|
21
27
|
await new Promise((resolve, reject) => {
|
|
22
28
|
const child = spawn(process.platform === "win32" ? "pnpm.cmd" : "pnpm", ["exec", "tsdown"], {
|
|
23
|
-
cwd:
|
|
29
|
+
cwd: Arkstack.rootDir(),
|
|
24
30
|
stdio: "inherit",
|
|
25
|
-
env: Object.assign(process.env, {
|
|
31
|
+
env: Object.assign(process.env, {
|
|
32
|
+
NODE_ENV: this.option("dev") ? "development" : "production",
|
|
33
|
+
CLI_BUILD: this.option("dev") ? "true" : void 0
|
|
34
|
+
})
|
|
26
35
|
});
|
|
27
36
|
child.on("error", (error) => {
|
|
28
37
|
reject(error);
|
|
@@ -40,20 +49,32 @@ var BuildCommand = class extends Command {
|
|
|
40
49
|
|
|
41
50
|
//#endregion
|
|
42
51
|
//#region dist/commands/DevCommand.js
|
|
43
|
-
var DevCommand = class extends Command {
|
|
44
|
-
signature =
|
|
52
|
+
var DevCommand = class DevCommand extends Command {
|
|
53
|
+
signature = `dev
|
|
54
|
+
{--t|tunnel : Tunnel the dev server through Ngrok}
|
|
55
|
+
{--host : Expose the dev server on the local network}
|
|
56
|
+
{--s|secure : Serve the dev server over HTTPS with a self-signed certificate}
|
|
57
|
+
`;
|
|
45
58
|
description = "Run the development server";
|
|
46
59
|
async handle() {
|
|
60
|
+
const vars = DevCommand.devServerEnv(this.options());
|
|
61
|
+
const rootDir = Arkstack.rootDir();
|
|
62
|
+
const bin = DevCommand.resolveTsdownBin(rootDir);
|
|
63
|
+
const [command, args] = bin ? [process.execPath, [
|
|
64
|
+
bin,
|
|
65
|
+
"--log-level",
|
|
66
|
+
"silent"
|
|
67
|
+
]] : [process.platform === "win32" ? "pnpm.cmd" : "pnpm", [
|
|
68
|
+
"exec",
|
|
69
|
+
"tsdown",
|
|
70
|
+
"--log-level",
|
|
71
|
+
"silent"
|
|
72
|
+
]];
|
|
47
73
|
await new Promise((resolve, reject) => {
|
|
48
|
-
const child = spawn(
|
|
49
|
-
|
|
50
|
-
"tsdown",
|
|
51
|
-
"--log-level",
|
|
52
|
-
"silent"
|
|
53
|
-
], {
|
|
54
|
-
cwd: process.cwd(),
|
|
74
|
+
const child = spawn(command, args, {
|
|
75
|
+
cwd: rootDir,
|
|
55
76
|
stdio: "inherit",
|
|
56
|
-
env: Object.assign(process.env,
|
|
77
|
+
env: Object.assign(process.env, vars)
|
|
57
78
|
});
|
|
58
79
|
child.on("error", (error) => {
|
|
59
80
|
reject(error);
|
|
@@ -67,6 +88,111 @@ var DevCommand = class extends Command {
|
|
|
67
88
|
});
|
|
68
89
|
});
|
|
69
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolve the absolute path to tsdown's executable so it can be run directly
|
|
93
|
+
* with `node`, skipping the `pnpm exec` wrapper process. Returns `undefined`
|
|
94
|
+
* when tsdown cannot be resolved from the app root, letting the caller fall
|
|
95
|
+
* back to `pnpm exec`.
|
|
96
|
+
*
|
|
97
|
+
* @param rootDir The application root to resolve tsdown from.
|
|
98
|
+
*/
|
|
99
|
+
static resolveTsdownBin(rootDir) {
|
|
100
|
+
try {
|
|
101
|
+
const pkgPath = createRequire(join(rootDir, "noop.js")).resolve("tsdown/package.json");
|
|
102
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
103
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.tsdown;
|
|
104
|
+
return bin ? join(dirname(pkgPath), bin) : void 0;
|
|
105
|
+
} catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Map `dev` command flags to the environment variables the running server reads.
|
|
111
|
+
*
|
|
112
|
+
* The dev server binds `127.0.0.1` by default so it is local-only; `--host`
|
|
113
|
+
* switches it to `0.0.0.0` to expose it on the local network. `--secure` flags
|
|
114
|
+
* the driver to serve HTTPS, and `--tunnel` enables the Ngrok tunnel.
|
|
115
|
+
*
|
|
116
|
+
* @param options
|
|
117
|
+
* @returns
|
|
118
|
+
*/
|
|
119
|
+
static devServerEnv(options) {
|
|
120
|
+
const vars = {
|
|
121
|
+
NODE_ENV: "development",
|
|
122
|
+
APP_HOST: options.host ? "0.0.0.0" : "127.0.0.1"
|
|
123
|
+
};
|
|
124
|
+
if (options.tunnel) vars.TUNNEL = "true";
|
|
125
|
+
if (options.secure) vars.APP_SECURE = "true";
|
|
126
|
+
return vars;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region dist/commands/KeyGenerateCommand.js
|
|
132
|
+
/**
|
|
133
|
+
* Generate and set the application key (APP_KEY).
|
|
134
|
+
*
|
|
135
|
+
* APP_KEY is the unified secret used for signing JWTs and encrypting values
|
|
136
|
+
* across the framework (exposed as `config('app.key')`).
|
|
137
|
+
*/
|
|
138
|
+
var KeyGenerateCommand = class KeyGenerateCommand extends Command {
|
|
139
|
+
signature = `key:generate
|
|
140
|
+
{--show : Display the generated key instead of writing it to the .env file.}
|
|
141
|
+
{--force : Overwrite the existing APP_KEY without confirmation.}
|
|
142
|
+
`;
|
|
143
|
+
description = "Set the application key (APP_KEY).";
|
|
144
|
+
async handle() {
|
|
145
|
+
const key = this.generateKey();
|
|
146
|
+
const ignore = !this.option("interaction");
|
|
147
|
+
if (this.option("show")) {
|
|
148
|
+
this.line(key);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const envPath = join(Arkstack.rootDir(), ".env");
|
|
152
|
+
if (!existsSync(envPath)) {
|
|
153
|
+
this.error("No .env file found. Copy .env.example to .env before running key:generate.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const contents = readFileSync(envPath, "utf-8");
|
|
157
|
+
if (KeyGenerateCommand.hasEnvValue(contents, "APP_KEY") && !this.option("force") || ignore) {
|
|
158
|
+
if (!(!ignore ? await this.confirm("An application key already exists. Overwrite it?", false) : false) || ignore) {
|
|
159
|
+
this.info(`Application key generation ${ignore ? "skipped" : "aborted"}.`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
writeFileSync(envPath, KeyGenerateCommand.upsertEnvKey(contents, "APP_KEY", key));
|
|
164
|
+
this.success("Application key set successfully.");
|
|
165
|
+
}
|
|
166
|
+
generateKey() {
|
|
167
|
+
return randomBytes(32).toString("base64url");
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Whether the env file defines a non-empty value for `name`.
|
|
171
|
+
*
|
|
172
|
+
* An empty assignment (`APP_KEY=`), whitespace, or empty quotes (`APP_KEY=""`)
|
|
173
|
+
* all count as "not set" so a placeholder line is never mistaken for a real key.
|
|
174
|
+
*
|
|
175
|
+
* @param contents The raw `.env` contents.
|
|
176
|
+
* @param name The variable name.
|
|
177
|
+
*/
|
|
178
|
+
static hasEnvValue = (contents, name) => {
|
|
179
|
+
const value = contents.match(new RegExp(`^${name}=(.*)$`, "m"))?.[1]?.trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
180
|
+
return Boolean(value);
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Return `contents` with `name` set to `value`, replacing the line in place if
|
|
184
|
+
* it exists or appending it otherwise.
|
|
185
|
+
*
|
|
186
|
+
* @param contents The raw `.env` contents.
|
|
187
|
+
* @param name The variable name.
|
|
188
|
+
* @param value The value to set.
|
|
189
|
+
*/
|
|
190
|
+
static upsertEnvKey = (contents, name, value) => {
|
|
191
|
+
const line = `${name}=${value}`;
|
|
192
|
+
const pattern = new RegExp(`^${name}=.*$`, "m");
|
|
193
|
+
if (pattern.test(contents)) return contents.replace(pattern, () => line);
|
|
194
|
+
return `${contents.replace(/\s*$/, "")}\n${line}\n`;
|
|
195
|
+
};
|
|
70
196
|
};
|
|
71
197
|
|
|
72
198
|
//#endregion
|
|
@@ -80,7 +206,7 @@ var MakeCommand = class extends Command {
|
|
|
80
206
|
const name = String(this.argument("name")).replace(/\s+/g, "").replace(/\.js$/, "").trim();
|
|
81
207
|
if (!name) return void this.error("Command name is required");
|
|
82
208
|
const stubContent = this.stub(name);
|
|
83
|
-
const filePath = resolve(
|
|
209
|
+
const filePath = resolve(Arkstack.rootDir(), "src", `app/console/commands/${name}.js`);
|
|
84
210
|
await writeFile(filePath, stubContent, { flag: "wx" });
|
|
85
211
|
this.success(`Command ${name} created successfully at ${filePath}`);
|
|
86
212
|
}
|
|
@@ -179,7 +305,129 @@ var MakeFullResource = class extends Command {
|
|
|
179
305
|
|
|
180
306
|
//#endregion
|
|
181
307
|
//#region dist/commands/MakeResource.js
|
|
182
|
-
var MakeResource$1 = class extends MakeResource {
|
|
308
|
+
var MakeResource$1 = class extends MakeResource {
|
|
309
|
+
async handle() {
|
|
310
|
+
try {
|
|
311
|
+
applyRuntimeConfig({
|
|
312
|
+
...getDefaultConfig(),
|
|
313
|
+
...config("resources", {})
|
|
314
|
+
});
|
|
315
|
+
} catch {}
|
|
316
|
+
return super.handle();
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region dist/commands/PublishCommand.js
|
|
322
|
+
/** Suffix that marks a file as a publishable stub; stripped on publish. */
|
|
323
|
+
const STUB_SUFFIX = ".stub";
|
|
324
|
+
/**
|
|
325
|
+
* Strip a trailing `.stub` suffix from a path.
|
|
326
|
+
*
|
|
327
|
+
* Stubs are shipped as `<name>.<ext>.stub` so they are ignored by linting,
|
|
328
|
+
* type-checking and test discovery in the source repo, then restored to their
|
|
329
|
+
* real extension when published into an application.
|
|
330
|
+
*
|
|
331
|
+
* @param path
|
|
332
|
+
*/
|
|
333
|
+
const stripStubSuffix = (path) => path.endsWith(STUB_SUFFIX) ? path.slice(0, -5) : path;
|
|
334
|
+
/**
|
|
335
|
+
* Publish artifacts (migrations, stubs, assets, …) that installed packages
|
|
336
|
+
* register via `publishes()` into the consuming application.
|
|
337
|
+
*/
|
|
338
|
+
var PublishCommand = class extends Command {
|
|
339
|
+
signature = `publish
|
|
340
|
+
{--package= : Only publish artifacts registered by this package (e.g. @arkstack/cache).}
|
|
341
|
+
{--tag= : Only publish artifacts registered under this tag.}
|
|
342
|
+
{--force : Overwrite files that already exist at the destination.}
|
|
343
|
+
{--list : List the publishable artifacts without copying anything.}
|
|
344
|
+
`;
|
|
345
|
+
description = "Publish package artifacts into your application.";
|
|
346
|
+
async handle() {
|
|
347
|
+
await this.loadPackageSetups();
|
|
348
|
+
const filter = {
|
|
349
|
+
package: this.option("package"),
|
|
350
|
+
tag: this.option("tag")
|
|
351
|
+
};
|
|
352
|
+
const groups = Publisher.publishables(filter);
|
|
353
|
+
if (groups.length < 1) {
|
|
354
|
+
this.warn(`No publishable artifacts found${this.describeFilter(filter)}.`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (this.option("list")) {
|
|
358
|
+
this.listGroups(groups);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
let published = 0;
|
|
362
|
+
let skipped = 0;
|
|
363
|
+
for (const group of groups) for (const entry of group.entries) {
|
|
364
|
+
if (!existsSync(entry.from)) {
|
|
365
|
+
this.warn(`[${group.package}] Source not found, skipping: ${entry.from}`);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const to = stripStubSuffix(entry.to);
|
|
369
|
+
const dest = join(Arkstack.rootDir(), to);
|
|
370
|
+
if (existsSync(dest) && !this.option("force")) {
|
|
371
|
+
this.warn(`Exists, skipped (use --force): ${to}`);
|
|
372
|
+
skipped++;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
376
|
+
cpSync(entry.from, dest, { recursive: true });
|
|
377
|
+
if (statSync(dest).isDirectory()) this.stripStubsInTree(dest);
|
|
378
|
+
this.success(`Published [${group.package}] -> ${to}`);
|
|
379
|
+
published++;
|
|
380
|
+
}
|
|
381
|
+
this.info(`Done. ${published} published, ${skipped} skipped.`);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Recursively rename `*.stub` files within a published directory to their
|
|
385
|
+
* real extension.
|
|
386
|
+
*
|
|
387
|
+
* @param dir
|
|
388
|
+
*/
|
|
389
|
+
stripStubsInTree(dir) {
|
|
390
|
+
for (const item of readdirSync(dir, {
|
|
391
|
+
recursive: true,
|
|
392
|
+
withFileTypes: true
|
|
393
|
+
})) if (item.isFile() && item.name.endsWith(STUB_SUFFIX)) {
|
|
394
|
+
const current = join(item.parentPath, item.name);
|
|
395
|
+
renameSync(current, stripStubSuffix(current));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Print the publishable artifacts grouped by package without copying.
|
|
400
|
+
*
|
|
401
|
+
* @param groups
|
|
402
|
+
*/
|
|
403
|
+
listGroups(groups) {
|
|
404
|
+
this.info("Publishable artifacts:");
|
|
405
|
+
for (const group of groups) {
|
|
406
|
+
this.line(` ${group.package}${group.tag ? ` (tag: ${group.tag})` : ""}`);
|
|
407
|
+
for (const entry of group.entries) this.line(` - ${stripStubSuffix(entry.to)}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Import the `setup` module of every installed `@arkstack/*` package so its
|
|
412
|
+
* `publishes()` registrations run. Errors (missing build, side effects) are
|
|
413
|
+
* ignored so one bad package cannot break publishing.
|
|
414
|
+
*/
|
|
415
|
+
async loadPackageSetups() {
|
|
416
|
+
const scope = join(Arkstack.rootDir(), "node_modules", "@arkstack");
|
|
417
|
+
if (!existsSync(scope)) return;
|
|
418
|
+
for (const pkg of readdirSync(scope)) {
|
|
419
|
+
const setup = join(scope, pkg, "dist", "setup.js");
|
|
420
|
+
if (!existsSync(setup)) continue;
|
|
421
|
+
try {
|
|
422
|
+
await import(pathToFileURL(setup).href);
|
|
423
|
+
} catch {}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
describeFilter(filter) {
|
|
427
|
+
const parts = [filter.package ? `package "${filter.package}"` : "", filter.tag ? `tag "${filter.tag}"` : ""].filter(Boolean);
|
|
428
|
+
return parts.length ? ` for ${parts.join(" and ")}` : "";
|
|
429
|
+
}
|
|
430
|
+
};
|
|
183
431
|
|
|
184
432
|
//#endregion
|
|
185
433
|
//#region dist/commands/RouteList.js
|
|
@@ -253,14 +501,37 @@ var logo_default = String.raw`
|
|
|
253
501
|
|
|
254
502
|
//#endregion
|
|
255
503
|
//#region dist/index.js
|
|
256
|
-
/**
|
|
257
|
-
*
|
|
504
|
+
/**
|
|
505
|
+
* A missing-module error from importing a stale/incomplete build artifact.
|
|
258
506
|
*
|
|
507
|
+
* @param error
|
|
259
508
|
* @returns
|
|
260
509
|
*/
|
|
510
|
+
const isMissingModuleError = (error) => {
|
|
511
|
+
const code = error?.code;
|
|
512
|
+
return code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Loads the core application instance by importing the built bootstrap file.
|
|
516
|
+
*
|
|
517
|
+
* The kernel boots this for every command — including `build` — before the build
|
|
518
|
+
* can run, so a stale or incomplete build artifact (source changed since the last
|
|
519
|
+
* build: a module moved, renamed, or added) would otherwise wedge startup with
|
|
520
|
+
* `Cannot find module '<outDir>/...'` and the build could never self-heal. When
|
|
521
|
+
* that happens and source is present, regenerate the output once and retry.
|
|
522
|
+
*
|
|
523
|
+
* @returns
|
|
524
|
+
*/
|
|
261
525
|
const loadCoreApp = async () => {
|
|
262
|
-
const dist = path.relative(
|
|
263
|
-
|
|
526
|
+
const dist = path.relative(Arkstack.rootDir(), outputDir());
|
|
527
|
+
const bootstrapPath = join(Arkstack.rootDir(), `${dist}/core/bootstrap.js`);
|
|
528
|
+
try {
|
|
529
|
+
return (await importFile(bootstrapPath)).app;
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (!isMissingModuleError(error) || !existsSync(join(Arkstack.rootDir(), "src"))) throw error;
|
|
532
|
+
await rebuildOutput();
|
|
533
|
+
return (await importFile(bootstrapPath)).app;
|
|
534
|
+
}
|
|
264
535
|
};
|
|
265
536
|
/**
|
|
266
537
|
* Runs the console kernel, initializing the application and registering commands.
|
|
@@ -270,13 +541,17 @@ const loadCoreApp = async () => {
|
|
|
270
541
|
const runConsoleKernel = async (options = {}) => {
|
|
271
542
|
loadPrototypes();
|
|
272
543
|
const app = await loadCoreApp();
|
|
273
|
-
const dist = path.relative(process.cwd(), outputDir());
|
|
274
544
|
const stubsDir = process.env.ARKSTACK_STUBS_DIR;
|
|
275
545
|
globalThis.app = () => app;
|
|
276
546
|
globalThis.env = env;
|
|
277
547
|
globalThis.config = config;
|
|
278
548
|
globalThis.str = str;
|
|
549
|
+
globalThis.abort = abort;
|
|
550
|
+
globalThis.abortIf = abortIf;
|
|
551
|
+
globalThis.assertFound = assertFound;
|
|
279
552
|
globalThis.arkctx = { runtime: "CLI" };
|
|
553
|
+
await initializeGlobalContext();
|
|
554
|
+
const userCommands = await discoverCommands();
|
|
280
555
|
await Kernel.init(await new ArkstackConsoleApp(app, { stubsDir }).loadConfig(), {
|
|
281
556
|
logo: options.logo ?? logo_default,
|
|
282
557
|
name: "Cmd",
|
|
@@ -287,16 +562,12 @@ const runConsoleKernel = async (options = {}) => {
|
|
|
287
562
|
MakeFullResource,
|
|
288
563
|
DevCommand,
|
|
289
564
|
BuildCommand,
|
|
290
|
-
MakeCommand
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
join(process.cwd(), "src", "app/console/commands/*.js"),
|
|
295
|
-
join(process.cwd(), "src", "app/console/commands/*.mjs"),
|
|
296
|
-
join(process.cwd(), dist, "app/console/commands/*.js"),
|
|
297
|
-
join(process.cwd(), dist, "app/console/commands/*.mjs"),
|
|
298
|
-
join(process.cwd(), "node_modules", "@arkstack/*", "dist", "commands", "*.js")
|
|
565
|
+
MakeCommand,
|
|
566
|
+
KeyGenerateCommand,
|
|
567
|
+
PublishCommand,
|
|
568
|
+
...userCommands
|
|
299
569
|
],
|
|
570
|
+
discoveryPaths: [join(Arkstack.rootDir(), "node_modules", "@arkstack/*", "dist", "commands", "*.js")],
|
|
300
571
|
exceptionHandler(exception) {
|
|
301
572
|
throw exception;
|
|
302
573
|
}
|
|
@@ -320,4 +591,4 @@ const isEntrypointExecution = () => {
|
|
|
320
591
|
if (isEntrypointExecution()) await runConsoleKernel();
|
|
321
592
|
|
|
322
593
|
//#endregion
|
|
323
|
-
export { runConsoleKernel };
|
|
594
|
+
export { BaseTCConfig, BuildInterfaces, TSConfig, runConsoleKernel };
|
package/dist/prepare.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as BuildInterfaces } from "./BuildInterfaces-CPmSl41C.js";
|
|
3
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { Arkstack } from "@arkstack/contract";
|
|
1
6
|
import { spawn } from "node:child_process";
|
|
2
7
|
import chalk from "chalk";
|
|
3
8
|
|
|
4
9
|
//#region dist/prepare.js
|
|
10
|
+
if (!existsSync(path.join(Arkstack.rootDir(), ".arkstack/build"))) mkdirSync(path.join(Arkstack.rootDir(), ".arkstack/build"), { recursive: true });
|
|
11
|
+
if (!process.env.NODE_CI) {
|
|
12
|
+
BuildInterfaces.configs();
|
|
13
|
+
BuildInterfaces.env();
|
|
14
|
+
}
|
|
15
|
+
BuildInterfaces.tsconfig();
|
|
16
|
+
const LOG_LEVEL = parseInt(process.env.VERBOSITY ?? "0") > 0 ? [] : ["--log-level=silent"];
|
|
5
17
|
const NODE_ENV = process.env.NODE_ENV || "development";
|
|
6
18
|
const child = spawn(process.platform === "win32" ? "pnpm.cmd" : "pnpm", [
|
|
7
19
|
"exec",
|
|
8
20
|
"tsdown",
|
|
9
|
-
|
|
21
|
+
...LOG_LEVEL
|
|
10
22
|
], {
|
|
11
|
-
cwd:
|
|
23
|
+
cwd: Arkstack.rootDir(),
|
|
12
24
|
stdio: "inherit",
|
|
13
25
|
env: Object.assign({}, process.env, {
|
|
14
26
|
NODE_ENV,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/console",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Console module for Arkstack, providing the command-line runtime and console integration layer.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net/guide/cli",
|
|
@@ -42,16 +42,17 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
45
|
+
"arkormx": "^2.10.1",
|
|
46
|
+
"clear-router": "^2.9.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@h3ravel/musket": "^
|
|
50
|
-
"@h3ravel/support": "^
|
|
49
|
+
"@h3ravel/musket": "^2.2.1",
|
|
50
|
+
"@h3ravel/support": "^2.2.0",
|
|
51
51
|
"chalk": "^5.6.2",
|
|
52
|
-
"resora": "^1.
|
|
53
|
-
"
|
|
54
|
-
"@arkstack/common": "^0.5.
|
|
52
|
+
"resora": "^1.3.27",
|
|
53
|
+
"ts-morph": "^28.0.0",
|
|
54
|
+
"@arkstack/common": "^0.5.3",
|
|
55
|
+
"@arkstack/contract": "^0.5.3"
|
|
55
56
|
},
|
|
56
57
|
"scripts": {
|
|
57
58
|
"build": "tsdown",
|