@forinda/kickjs-cli 5.0.2 → 5.2.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/dist/builtins-BW3g09hP.mjs +8538 -0
- package/dist/builtins-C_VfEGdg.mjs +4182 -0
- package/dist/builtins-C_VfEGdg.mjs.map +1 -0
- package/dist/cli.mjs +21 -9299
- package/dist/config-DDrgs-I3.mjs +171 -0
- package/dist/config-DDrgs-I3.mjs.map +1 -0
- package/dist/config-DsQe2yzy.mjs +169 -0
- package/dist/generator-extension-DRNQpoZP.mjs +4380 -0
- package/dist/generator-extension-DRNQpoZP.mjs.map +1 -0
- package/dist/index.d.mts +513 -138
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -4159
- package/dist/plugin-6_YlK-JG.mjs +71 -0
- package/dist/plugin-6_YlK-JG.mjs.map +1 -0
- package/dist/plugin-CQ0yYXyr.mjs +80 -0
- package/dist/rolldown-runtime-CYBbkZNy.mjs +24 -0
- package/dist/run-plugins-B1R0HG0g.mjs +12 -0
- package/dist/typegen-CYCsmCRF.mjs +1351 -0
- package/dist/{typegen-D8MJ2hPX.mjs → typegen-DugZmi-0.mjs} +127 -224
- package/dist/typegen-DugZmi-0.mjs.map +1 -0
- package/dist/types-CGB8BiQh.mjs +25 -0
- package/dist/types-CGB8BiQh.mjs.map +1 -0
- package/package.json +9 -3
- package/dist/index.mjs.map +0 -1
- package/dist/typegen-D8MJ2hPX.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v5.0
|
|
2
|
+
* @forinda/kickjs-cli v5.2.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { t as __exportAll } from "./rolldown-runtime-CYBbkZNy.mjs";
|
|
12
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
13
13
|
import { statSync } from "node:fs";
|
|
14
|
+
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
14
15
|
import { globSync } from "glob";
|
|
16
|
+
import { groupAssetKeys } from "@forinda/kickjs";
|
|
15
17
|
//#region src/typegen/scanner.ts
|
|
16
18
|
/** Decorators that mark a class as DI-managed */
|
|
17
19
|
const DECORATOR_NAMES = [
|
|
@@ -729,12 +731,12 @@ function discoverAssets(assetMap, cwd) {
|
|
|
729
731
|
posix: true
|
|
730
732
|
});
|
|
731
733
|
matches.sort();
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const
|
|
734
|
+
const { pairs } = groupAssetKeys(namespace, matches, { strategy: entry.keys ?? "auto" });
|
|
735
|
+
for (const { key: logical } of pairs) {
|
|
736
|
+
const subKey = logical.slice(namespace.length + 1);
|
|
735
737
|
seen.set(logical, {
|
|
736
738
|
namespace,
|
|
737
|
-
key
|
|
739
|
+
key: subKey
|
|
738
740
|
});
|
|
739
741
|
}
|
|
740
742
|
}
|
|
@@ -798,7 +800,7 @@ export {}
|
|
|
798
800
|
}
|
|
799
801
|
const LEAF = Symbol("asset-leaf");
|
|
800
802
|
function renderTree(node, indent) {
|
|
801
|
-
const keys = Object.keys(node).
|
|
803
|
+
const keys = Object.keys(node).toSorted();
|
|
802
804
|
const lines = [];
|
|
803
805
|
for (const key of keys) {
|
|
804
806
|
const child = node[key];
|
|
@@ -822,10 +824,6 @@ function isDir(path) {
|
|
|
822
824
|
return false;
|
|
823
825
|
}
|
|
824
826
|
}
|
|
825
|
-
function stripExt(path) {
|
|
826
|
-
const ext = extname(path);
|
|
827
|
-
return ext ? path.slice(0, -ext.length) : path;
|
|
828
|
-
}
|
|
829
827
|
//#endregion
|
|
830
828
|
//#region src/typegen/generator.ts
|
|
831
829
|
/**
|
|
@@ -953,7 +951,7 @@ export type ${typeName} = never
|
|
|
953
951
|
`;
|
|
954
952
|
return `${HEADER}
|
|
955
953
|
export type ${typeName} =
|
|
956
|
-
${[...new Set(names)].
|
|
954
|
+
${[...new Set(names)].toSorted().map((n) => ` | '${n}'`).join("\n")}
|
|
957
955
|
`;
|
|
958
956
|
}
|
|
959
957
|
/** Render the barrel index that re-exports the union types */
|
|
@@ -969,203 +967,11 @@ export type { ModuleToken } from './modules'
|
|
|
969
967
|
// \`dependsOn: ['TenantAdapter']\`, \`assets.mails.welcome()\`, and
|
|
970
968
|
// \`@Value('PORT')\` to resolve.
|
|
971
969
|
import './registry'
|
|
972
|
-
import './
|
|
970
|
+
import './kick__routes'
|
|
973
971
|
import './plugins'
|
|
974
972
|
import './augmentations'
|
|
975
973
|
import './assets'
|
|
976
|
-
${includeEnv ? "import './
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Render the `query` field's TypeScript type for a single route.
|
|
980
|
-
*
|
|
981
|
-
* - When `@ApiQueryParams` is absent (`queryFilterable === null`), emits
|
|
982
|
-
* `unknown` so the user gets nothing extra.
|
|
983
|
-
* - When the decorator is present, emits an object literal whose keys
|
|
984
|
-
* are the standard query string keys (`filter`, `sort`, `q`, `page`,
|
|
985
|
-
* `limit`). `sort` is narrowed to a string-literal union of allowed
|
|
986
|
-
* field names with optional `-` direction prefix.
|
|
987
|
-
*/
|
|
988
|
-
function renderQueryShape(m) {
|
|
989
|
-
if (m.queryFilterable === null) return "unknown";
|
|
990
|
-
const sortable = m.querySortable ?? [];
|
|
991
|
-
return `{ filter?: string | string[]; sort?: ${sortable.length > 0 ? sortable.flatMap((f) => [`'${f}'`, `'-${f}'`]).join(" | ") : "string"}; q?: string; page?: string; limit?: string }`;
|
|
992
|
-
}
|
|
993
|
-
/** Render JSDoc lines summarising the @ApiQueryParams whitelist */
|
|
994
|
-
function renderQueryDocLines(m) {
|
|
995
|
-
const lines = [];
|
|
996
|
-
if (m.queryFilterable && m.queryFilterable.length > 0) lines.push(`Filterable: ${m.queryFilterable.join(", ")}`);
|
|
997
|
-
if (m.querySortable && m.querySortable.length > 0) lines.push(`Sortable: ${m.querySortable.join(", ")}`);
|
|
998
|
-
if (m.querySearchable && m.querySearchable.length > 0) lines.push(`Searchable: ${m.querySearchable.join(", ")}`);
|
|
999
|
-
return lines;
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Plan a schema import for hoisting at the top of `routes.ts`. Returns
|
|
1003
|
-
* the alias the in-namespace code should use, or `null` if the schema
|
|
1004
|
-
* cannot be referenced (no validator configured, or source unresolvable).
|
|
1005
|
-
*
|
|
1006
|
-
* Aliases are unique per (alias-counter) so two schemas named
|
|
1007
|
-
* `createTaskSchema` from different modules don't collide.
|
|
1008
|
-
*/
|
|
1009
|
-
function planSchemaImport(schema, routeFilePath, routesOutFile, schemaValidator, imports) {
|
|
1010
|
-
if (!schema || schemaValidator !== "zod") return null;
|
|
1011
|
-
if (schema.source === null) return null;
|
|
1012
|
-
const specifier = resolveSchemaImportSpecifier(schema.source, routeFilePath, routesOutFile);
|
|
1013
|
-
if (specifier === "unknown") return null;
|
|
1014
|
-
const key = `${specifier}::${schema.identifier}`;
|
|
1015
|
-
let alias = imports.get(key)?.specifier;
|
|
1016
|
-
if (!alias) {
|
|
1017
|
-
alias = `_S${imports.size}`;
|
|
1018
|
-
imports.set(key, {
|
|
1019
|
-
identifier: schema.identifier,
|
|
1020
|
-
specifier: alias
|
|
1021
|
-
});
|
|
1022
|
-
} else alias = imports.get(key).specifier;
|
|
1023
|
-
return alias;
|
|
1024
|
-
}
|
|
1025
|
-
/** Build the `import type { ... } from '...'` lines for hoisted schema imports */
|
|
1026
|
-
function renderSchemaImports(imports) {
|
|
1027
|
-
if (imports.size === 0) return "";
|
|
1028
|
-
const lines = [];
|
|
1029
|
-
for (const [key, value] of imports) {
|
|
1030
|
-
const [path] = key.split("::");
|
|
1031
|
-
lines.push(`import type { ${value.identifier} as ${value.specifier} } from '${path}'`);
|
|
1032
|
-
}
|
|
1033
|
-
return lines.join("\n") + "\n";
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Compute the import specifier the generated `routes.d.ts` should use to
|
|
1037
|
-
* reach a schema declared either in the controller file (empty string)
|
|
1038
|
-
* or imported from elsewhere (relative path or bare module name).
|
|
1039
|
-
*
|
|
1040
|
-
* - Bare module names (`zod`, `@scope/pkg`) are returned as-is.
|
|
1041
|
-
* - Relative paths (`./users.dto`, `../shared/schema`) are resolved
|
|
1042
|
-
* against the controller's file path, then re-relativised against the
|
|
1043
|
-
* directory containing `routes.d.ts`.
|
|
1044
|
-
* - Empty string (same-file schema) becomes a relative path from the
|
|
1045
|
-
* `routes.d.ts` directory back to the controller file.
|
|
1046
|
-
*/
|
|
1047
|
-
function resolveSchemaImportSpecifier(source, routeFilePath, routesOutFile) {
|
|
1048
|
-
if (source === null) return "unknown";
|
|
1049
|
-
const routesDir = dirname(routesOutFile);
|
|
1050
|
-
if (source === "") {
|
|
1051
|
-
let rel = relative(routesDir, routeFilePath).split(sep).join("/");
|
|
1052
|
-
rel = rel.replace(/\.(ts|tsx|mts|cts)$/i, "");
|
|
1053
|
-
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1054
|
-
return rel;
|
|
1055
|
-
}
|
|
1056
|
-
if (!source.startsWith(".") && !source.startsWith("/")) return source;
|
|
1057
|
-
let rel = relative(routesDir, resolve(dirname(routeFilePath), source)).split(sep).join("/");
|
|
1058
|
-
rel = rel.replace(/\.(ts|tsx|mts|cts)$/i, "");
|
|
1059
|
-
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1060
|
-
return rel;
|
|
1061
|
-
}
|
|
1062
|
-
/**
|
|
1063
|
-
* Render the `KickEnv` + `NodeJS.ProcessEnv` augmentation file from a
|
|
1064
|
-
* detected env schema. Mirrors the routes.ts pattern: emits as a `.ts`
|
|
1065
|
-
* file (not `.d.ts`) so the top-level `import type schema from '...'`
|
|
1066
|
-
* actually resolves under `moduleResolution: 'bundler'`.
|
|
1067
|
-
*
|
|
1068
|
-
* Returns `null` when no env file was discovered, so the caller can
|
|
1069
|
-
* skip writing the file altogether (rather than emitting an empty
|
|
1070
|
-
* augmentation that would shadow `KickEnv` to a useless `{}`).
|
|
1071
|
-
*/
|
|
1072
|
-
function renderEnv(env, envOutFile) {
|
|
1073
|
-
if (!env) return null;
|
|
1074
|
-
let rel = relative(dirname(envOutFile), env.filePath).split(sep).join("/");
|
|
1075
|
-
rel = rel.replace(/\.(ts|tsx|mts|cts)$/i, "");
|
|
1076
|
-
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1077
|
-
return `${HEADER}
|
|
1078
|
-
// Importing the schema as a type lets us infer its shape without
|
|
1079
|
-
// pulling in any runtime code. \`Awaited<>\` strips an accidental
|
|
1080
|
-
// Promise wrap on dynamic-imported defaults.
|
|
1081
|
-
import type _envSchema from '${rel}'
|
|
1082
|
-
|
|
1083
|
-
// Local type alias — interfaces can only \`extend\` an identifier,
|
|
1084
|
-
// not an inline import expression, so we resolve the schema's
|
|
1085
|
-
// inferred shape into a named type first.
|
|
1086
|
-
type _KickEnvShape = import('zod').infer<typeof _envSchema>
|
|
1087
|
-
|
|
1088
|
-
declare global {
|
|
1089
|
-
/**
|
|
1090
|
-
* Typed environment registry. Augmented from \`${env.relativePath}\`
|
|
1091
|
-
* so \`@Value('PORT')\`, \`Env<'PORT'>\`, and \`process.env.PORT\` are
|
|
1092
|
-
* all type-safe and autocomplete.
|
|
1093
|
-
*/
|
|
1094
|
-
interface KickEnv extends _KickEnvShape {}
|
|
1095
|
-
|
|
1096
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
1097
|
-
namespace NodeJS {
|
|
1098
|
-
/**
|
|
1099
|
-
* Narrow \`process.env\` so known keys exist as \`string\` (the raw
|
|
1100
|
-
* pre-Zod-coercion form). \`@Value\` and the \`ConfigService\` apply
|
|
1101
|
-
* the schema's transforms internally; access \`process.env\` directly
|
|
1102
|
-
* only when you need the raw string. Unknown keys still resolve to
|
|
1103
|
-
* \`string | undefined\` via the base @types/node declaration.
|
|
1104
|
-
*/
|
|
1105
|
-
interface ProcessEnv extends Record<keyof KickEnv, string> {}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
export {}
|
|
1110
|
-
`;
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Render the `KickRoutes` global namespace augmentation. Each interface
|
|
1114
|
-
* inside corresponds to a controller class; each property is a single
|
|
1115
|
-
* route method on that controller, conforming to `RouteShape`.
|
|
1116
|
-
*
|
|
1117
|
-
* Fills `params` from URL patterns, `query` from `@ApiQueryParams`, and
|
|
1118
|
-
* `body`/`query`/`params` (when schema-validated) from the configured
|
|
1119
|
-
* schema validator. `response` is emitted as `unknown`.
|
|
1120
|
-
*/
|
|
1121
|
-
function renderRoutes(routes, routesOutFile, schemaValidator) {
|
|
1122
|
-
if (routes.length === 0) return `${HEADER}
|
|
1123
|
-
// (no routes discovered yet — annotate a controller method with
|
|
1124
|
-
// @Get/@Post/@Put/@Delete/@Patch and re-run \`kick typegen\`)
|
|
1125
|
-
declare global {
|
|
1126
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
1127
|
-
namespace KickRoutes {}
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
export {}
|
|
1131
|
-
`;
|
|
1132
|
-
const byController = /* @__PURE__ */ new Map();
|
|
1133
|
-
for (const r of routes) {
|
|
1134
|
-
const arr = byController.get(r.controller) ?? [];
|
|
1135
|
-
arr.push(r);
|
|
1136
|
-
byController.set(r.controller, arr);
|
|
1137
|
-
}
|
|
1138
|
-
const schemaImports = /* @__PURE__ */ new Map();
|
|
1139
|
-
const renderField = (schema, routeFilePath) => {
|
|
1140
|
-
const alias = planSchemaImport(schema, routeFilePath, routesOutFile, schemaValidator, schemaImports);
|
|
1141
|
-
return alias ? `import('zod').infer<typeof ${alias}>` : null;
|
|
1142
|
-
};
|
|
1143
|
-
const interfaces = [];
|
|
1144
|
-
for (const [controller, methods] of byController) {
|
|
1145
|
-
const lines = [` interface ${controller} {`];
|
|
1146
|
-
for (const m of methods) {
|
|
1147
|
-
const urlParamsType = m.pathParams.length > 0 ? `{ ${m.pathParams.map((p) => `${p}: string`).join("; ")} }` : "{}";
|
|
1148
|
-
const bodySchemaType = renderField(m.bodySchema, m.filePath);
|
|
1149
|
-
const querySchemaType = renderField(m.querySchema, m.filePath);
|
|
1150
|
-
const paramsType = renderField(m.paramsSchema, m.filePath) ?? urlParamsType;
|
|
1151
|
-
const bodyType = bodySchemaType ?? "unknown";
|
|
1152
|
-
const queryType = querySchemaType ?? renderQueryShape(m);
|
|
1153
|
-
const docLines = renderQueryDocLines(m);
|
|
1154
|
-
lines.push(` /**`, ` * ${m.httpMethod} ${m.path}`, ...docLines.map((d) => ` * ${d}`), ` */`, ` ${m.method}: {`, ` params: ${paramsType}`, ` body: ${bodyType}`, ` query: ${queryType}`, ` response: unknown`, ` }`);
|
|
1155
|
-
}
|
|
1156
|
-
lines.push(" }");
|
|
1157
|
-
interfaces.push(lines.join("\n"));
|
|
1158
|
-
}
|
|
1159
|
-
return `${HEADER}${renderSchemaImports(schemaImports)}
|
|
1160
|
-
declare global {
|
|
1161
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
1162
|
-
namespace KickRoutes {
|
|
1163
|
-
${interfaces.join("\n")}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
export {}
|
|
1168
|
-
`;
|
|
974
|
+
${includeEnv ? "import './kick__env'\n" : ""}`;
|
|
1169
975
|
}
|
|
1170
976
|
/**
|
|
1171
977
|
* Render the `KickJsPluginRegistry` augmentation. Each entry maps the
|
|
@@ -1182,7 +988,7 @@ export {}
|
|
|
1182
988
|
function renderPlugins(items) {
|
|
1183
989
|
const byName = /* @__PURE__ */ new Map();
|
|
1184
990
|
for (const item of items) if (!byName.has(item.name)) byName.set(item.name, item);
|
|
1185
|
-
const entries = [...byName.values()].
|
|
991
|
+
const entries = [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map((item) => ` '${item.name}': '${item.kind}'`).join("\n");
|
|
1186
992
|
return `${HEADER}
|
|
1187
993
|
declare module '@forinda/kickjs' {
|
|
1188
994
|
/**
|
|
@@ -1225,7 +1031,7 @@ export {}
|
|
|
1225
1031
|
const byName = /* @__PURE__ */ new Map();
|
|
1226
1032
|
for (const item of items) if (!byName.has(item.name)) byName.set(item.name, item);
|
|
1227
1033
|
const blocks = [];
|
|
1228
|
-
for (const item of [...byName.values()].
|
|
1034
|
+
for (const item of [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
1229
1035
|
const docLines = [];
|
|
1230
1036
|
if (item.description) for (const line of item.description.split("\n")) docLines.push(` * ${line}`);
|
|
1231
1037
|
if (item.example) {
|
|
@@ -1254,14 +1060,12 @@ async function generateTypes(opts) {
|
|
|
1254
1060
|
const { classes, routes = [], tokens = [], injects = [], collisions = [], env = null, pluginsAndAdapters = [], augmentations = [], assets = {
|
|
1255
1061
|
entries: [],
|
|
1256
1062
|
count: 0
|
|
1257
|
-
}, outDir, allowDuplicates = false, schemaValidator = false } = opts;
|
|
1063
|
+
}, outDir, allowDuplicates = false, schemaValidator: _schemaValidator = false } = opts;
|
|
1258
1064
|
if (collisions.length > 0 && !allowDuplicates) throw new TokenCollisionError(collisions);
|
|
1259
1065
|
await mkdir(outDir, { recursive: true });
|
|
1260
1066
|
const registryFile = join(outDir, "registry.d.ts");
|
|
1261
1067
|
const servicesFile = join(outDir, "services.d.ts");
|
|
1262
1068
|
const modulesFile = join(outDir, "modules.d.ts");
|
|
1263
|
-
const routesFile = join(outDir, "routes.ts");
|
|
1264
|
-
const envFile = join(outDir, "env.ts");
|
|
1265
1069
|
const pluginsFile = join(outDir, "plugins.d.ts");
|
|
1266
1070
|
const augmentationsFile = join(outDir, "augmentations.d.ts");
|
|
1267
1071
|
const assetsFile = join(outDir, "assets.d.ts");
|
|
@@ -1279,16 +1083,13 @@ async function generateTypes(opts) {
|
|
|
1279
1083
|
const modules = classes.filter((c) => c.decorator === "Module").map((c) => c.className);
|
|
1280
1084
|
const servicesContent = renderUnion("ServiceToken", allServices, "(no tokens discovered — declare with createToken<T>() or `kick g service <name>`)");
|
|
1281
1085
|
const modulesContent = renderUnion("ModuleToken", modules, "(no @Module classes discovered — `kick g module <name>` to add one)");
|
|
1282
|
-
const routesContent = renderRoutes(routes, routesFile, schemaValidator);
|
|
1283
|
-
const envContent = renderEnv(env, envFile);
|
|
1284
1086
|
const pluginsContent = renderPlugins(pluginsAndAdapters);
|
|
1285
1087
|
const augmentationsContent = renderAugmentations(augmentations);
|
|
1286
1088
|
const assetsContent = renderAssetTypes(assets);
|
|
1287
|
-
const indexContent = renderIndex(
|
|
1089
|
+
const indexContent = renderIndex(env !== null);
|
|
1288
1090
|
await writeFile(registryFile, registryContent, "utf-8");
|
|
1289
1091
|
await writeFile(servicesFile, servicesContent, "utf-8");
|
|
1290
1092
|
await writeFile(modulesFile, modulesContent, "utf-8");
|
|
1291
|
-
await writeFile(routesFile, routesContent, "utf-8");
|
|
1292
1093
|
await writeFile(pluginsFile, pluginsContent, "utf-8");
|
|
1293
1094
|
await writeFile(augmentationsFile, augmentationsContent, "utf-8");
|
|
1294
1095
|
await writeFile(assetsFile, assetsContent, "utf-8");
|
|
@@ -1297,16 +1098,11 @@ async function generateTypes(opts) {
|
|
|
1297
1098
|
registryFile,
|
|
1298
1099
|
servicesFile,
|
|
1299
1100
|
modulesFile,
|
|
1300
|
-
routesFile,
|
|
1301
1101
|
pluginsFile,
|
|
1302
1102
|
augmentationsFile,
|
|
1303
1103
|
assetsFile,
|
|
1304
1104
|
indexFile
|
|
1305
1105
|
];
|
|
1306
|
-
if (envContent) {
|
|
1307
|
-
await writeFile(envFile, envContent, "utf-8");
|
|
1308
|
-
written.push(envFile);
|
|
1309
|
-
}
|
|
1310
1106
|
await writeFile(join(dirname(outDir), ".gitignore"), "# Auto-generated by kick typegen\n*\n", "utf-8");
|
|
1311
1107
|
const uniquePluginNames = new Set(pluginsAndAdapters.map((p) => p.name)).size;
|
|
1312
1108
|
const uniqueAugmentations = new Set(augmentations.map((a) => a.name)).size;
|
|
@@ -1318,7 +1114,7 @@ async function generateTypes(opts) {
|
|
|
1318
1114
|
pluginEntries: uniquePluginNames,
|
|
1319
1115
|
augmentationEntries: uniqueAugmentations,
|
|
1320
1116
|
assetEntries: assets.count,
|
|
1321
|
-
envWritten:
|
|
1117
|
+
envWritten: env !== null,
|
|
1322
1118
|
written,
|
|
1323
1119
|
resolvedCollisions: collisions.length
|
|
1324
1120
|
};
|
|
@@ -1374,6 +1170,10 @@ function suggestRename(name) {
|
|
|
1374
1170
|
*
|
|
1375
1171
|
* @module @forinda/kickjs-cli/typegen
|
|
1376
1172
|
*/
|
|
1173
|
+
var typegen_exports = /* @__PURE__ */ __exportAll({
|
|
1174
|
+
runTypegen: () => runTypegen,
|
|
1175
|
+
watchTypegen: () => watchTypegen
|
|
1176
|
+
});
|
|
1377
1177
|
/** Resolve options to absolute paths */
|
|
1378
1178
|
function resolveOptions(opts) {
|
|
1379
1179
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -1419,6 +1219,20 @@ async function runTypegen(opts = {}) {
|
|
|
1419
1219
|
allowDuplicates,
|
|
1420
1220
|
schemaValidator
|
|
1421
1221
|
});
|
|
1222
|
+
if (opts.runPlugins !== false) try {
|
|
1223
|
+
const { runAllPluginTypegens } = await import("./run-plugins-B1R0HG0g.mjs");
|
|
1224
|
+
const { loadKickConfig } = await import("./config-DDrgs-I3.mjs").then((n) => n.n);
|
|
1225
|
+
await runAllPluginTypegens({
|
|
1226
|
+
cwd,
|
|
1227
|
+
config: await loadKickConfig(cwd),
|
|
1228
|
+
silent: true
|
|
1229
|
+
});
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
if (!silent) {
|
|
1232
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1233
|
+
console.warn(` kick typegen: plugin pipeline failed (${msg}) — continuing`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1422
1236
|
const tokenWarnings = validateTokenConventions(scan.tokens);
|
|
1423
1237
|
const elapsed = Date.now() - start;
|
|
1424
1238
|
if (!silent) {
|
|
@@ -1444,7 +1258,96 @@ async function runTypegen(opts = {}) {
|
|
|
1444
1258
|
tokenWarnings
|
|
1445
1259
|
};
|
|
1446
1260
|
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Watch mode for `kick typegen --watch`.
|
|
1263
|
+
*
|
|
1264
|
+
* Uses Node's built-in `fs.watch` (recursive, available on Linux 22+ and
|
|
1265
|
+
* macOS 19+). Falls back gracefully if recursive watch is not supported.
|
|
1266
|
+
*
|
|
1267
|
+
* Debounces re-runs by 100ms so a bulk file change (e.g. `kick g module`
|
|
1268
|
+
* creating 5 files at once) emits one regen, not five.
|
|
1269
|
+
*
|
|
1270
|
+
* In watch mode collisions are reported but never thrown — the watcher
|
|
1271
|
+
* keeps running so the user can fix the rename and the next scan
|
|
1272
|
+
* recovers automatically.
|
|
1273
|
+
*
|
|
1274
|
+
* Returns a `stop()` function that closes the watcher.
|
|
1275
|
+
*/
|
|
1276
|
+
async function watchTypegen(opts = {}) {
|
|
1277
|
+
const resolved = resolveOptions(opts);
|
|
1278
|
+
const { srcDir, silent, cwd } = resolved;
|
|
1279
|
+
const runOpts = {
|
|
1280
|
+
...resolved,
|
|
1281
|
+
allowDuplicates: true,
|
|
1282
|
+
runPlugins: false
|
|
1283
|
+
};
|
|
1284
|
+
const forcePolling = process.env.KICKJS_WATCH_POLLING === "1" || process.env.KICKJS_WATCH_POLLING === "true";
|
|
1285
|
+
const [{ runAllPluginTypegens }, { loadKickConfig }] = await Promise.all([import("./run-plugins-B1R0HG0g.mjs"), import("./config-DDrgs-I3.mjs").then((n) => n.n)]);
|
|
1286
|
+
const pluginConfig = await loadKickConfig(cwd);
|
|
1287
|
+
const runPlugins = () => runAllPluginTypegens({
|
|
1288
|
+
cwd,
|
|
1289
|
+
config: pluginConfig,
|
|
1290
|
+
silent: true
|
|
1291
|
+
}).catch(() => {});
|
|
1292
|
+
await safeRun(runOpts, silent);
|
|
1293
|
+
await runPlugins();
|
|
1294
|
+
const { watch } = await import("node:fs");
|
|
1295
|
+
let timer = null;
|
|
1296
|
+
const trigger = (filename) => {
|
|
1297
|
+
if (!filename) return;
|
|
1298
|
+
if (!/\.(ts|tsx|mts|cts)$/.test(filename)) return;
|
|
1299
|
+
if (filename.includes(".kickjs")) return;
|
|
1300
|
+
if (filename.endsWith(".d.ts")) return;
|
|
1301
|
+
if (timer) clearTimeout(timer);
|
|
1302
|
+
timer = setTimeout(() => {
|
|
1303
|
+
safeRun(runOpts, silent);
|
|
1304
|
+
runPlugins();
|
|
1305
|
+
}, 100);
|
|
1306
|
+
};
|
|
1307
|
+
if (forcePolling) {
|
|
1308
|
+
if (!silent) console.log(" kick typegen: polling mode (KICKJS_WATCH_POLLING)");
|
|
1309
|
+
const interval = setInterval(() => {
|
|
1310
|
+
safeRun({
|
|
1311
|
+
...runOpts,
|
|
1312
|
+
silent: true
|
|
1313
|
+
}, true);
|
|
1314
|
+
}, 2e3);
|
|
1315
|
+
return () => clearInterval(interval);
|
|
1316
|
+
}
|
|
1317
|
+
let watcher;
|
|
1318
|
+
try {
|
|
1319
|
+
watcher = watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
1320
|
+
trigger(filename);
|
|
1321
|
+
});
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
if (!silent) console.warn(` kick typegen: watch mode unavailable (${err?.message ?? err}). Falling back to polling.`);
|
|
1324
|
+
const interval = setInterval(() => {
|
|
1325
|
+
safeRun({
|
|
1326
|
+
...runOpts,
|
|
1327
|
+
silent: true
|
|
1328
|
+
}, true);
|
|
1329
|
+
}, 2e3);
|
|
1330
|
+
return () => clearInterval(interval);
|
|
1331
|
+
}
|
|
1332
|
+
return () => {
|
|
1333
|
+
if (timer) clearTimeout(timer);
|
|
1334
|
+
watcher.close();
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
/** Run typegen swallowing errors so the watcher loop never dies */
|
|
1338
|
+
async function safeRun(opts, silent) {
|
|
1339
|
+
try {
|
|
1340
|
+
await runTypegen(opts);
|
|
1341
|
+
} catch (err) {
|
|
1342
|
+
if (silent) return;
|
|
1343
|
+
if (err instanceof TokenCollisionError) console.error("\n" + err.message + "\n");
|
|
1344
|
+
else {
|
|
1345
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1346
|
+
console.error(` kick typegen failed: ${msg}`);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1447
1350
|
//#endregion
|
|
1448
|
-
export { runTypegen };
|
|
1351
|
+
export { discoverAssets as a, TokenCollisionError as i, typegen_exports as n, renderAssetTypes as o, watchTypegen as r, scanProject as s, runTypegen as t };
|
|
1449
1352
|
|
|
1450
|
-
//# sourceMappingURL=typegen-
|
|
1353
|
+
//# sourceMappingURL=typegen-DugZmi-0.mjs.map
|