@cms0/cms0 0.0.1
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/bin/cms0.js +30 -0
- package/dist/cjs/cli.cjs +7 -0
- package/dist/cjs/config.cjs +6 -0
- package/dist/cjs/generated/schema-descriptor.cjs +523 -0
- package/dist/cjs/index.cjs +48 -0
- package/dist/cjs/libs/cli/build.cjs +14 -0
- package/dist/cjs/libs/cli/cli.cjs +64 -0
- package/dist/cjs/libs/cli/config-loader.cjs +211 -0
- package/dist/cjs/libs/cli/descriptor-builder-alt.cjs +221 -0
- package/dist/cjs/libs/cli/descriptor-builder.cjs +232 -0
- package/dist/cjs/libs/cli/descriptor-writer.cjs +23 -0
- package/dist/cjs/libs/cli/index.cjs +10 -0
- package/dist/cjs/libs/cli/paths.cjs +28 -0
- package/dist/cjs/libs/cli/publisher.cjs +103 -0
- package/dist/cjs/libs/cli/types.cjs +3 -0
- package/dist/cjs/libs/cli/watcher.cjs +34 -0
- package/dist/cjs/schema-descriptors.cjs +5 -0
- package/dist/cjs/utils/index.cjs +2 -0
- package/dist/esm/cli.js +5 -0
- package/dist/esm/config.js +3 -0
- package/dist/esm/generated/schema-descriptor.js +520 -0
- package/dist/esm/index.js +45 -0
- package/dist/esm/libs/cli/build.js +12 -0
- package/dist/esm/libs/cli/cli.js +62 -0
- package/dist/esm/libs/cli/config-loader.js +168 -0
- package/dist/esm/libs/cli/descriptor-builder-alt.js +216 -0
- package/dist/esm/libs/cli/descriptor-builder.js +227 -0
- package/dist/esm/libs/cli/descriptor-writer.js +18 -0
- package/dist/esm/libs/cli/index.js +4 -0
- package/dist/esm/libs/cli/paths.js +21 -0
- package/dist/esm/libs/cli/publisher.js +101 -0
- package/dist/esm/libs/cli/types.js +2 -0
- package/dist/esm/libs/cli/watcher.js +29 -0
- package/dist/esm/schema-descriptors.js +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/config.d.ts +22 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/generated/schema-descriptor.d.ts +520 -0
- package/dist/types/generated/schema-descriptor.d.ts.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/libs/cli/build.d.ts +5 -0
- package/dist/types/libs/cli/build.d.ts.map +1 -0
- package/dist/types/libs/cli/cli.d.ts +3 -0
- package/dist/types/libs/cli/cli.d.ts.map +1 -0
- package/dist/types/libs/cli/config-loader.d.ts +12 -0
- package/dist/types/libs/cli/config-loader.d.ts.map +1 -0
- package/dist/types/libs/cli/descriptor-builder-alt.d.ts +5 -0
- package/dist/types/libs/cli/descriptor-builder-alt.d.ts.map +1 -0
- package/dist/types/libs/cli/descriptor-builder.d.ts +5 -0
- package/dist/types/libs/cli/descriptor-builder.d.ts.map +1 -0
- package/dist/types/libs/cli/descriptor-writer.d.ts +4 -0
- package/dist/types/libs/cli/descriptor-writer.d.ts.map +1 -0
- package/dist/types/libs/cli/index.d.ts +4 -0
- package/dist/types/libs/cli/index.d.ts.map +1 -0
- package/dist/types/libs/cli/paths.d.ts +4 -0
- package/dist/types/libs/cli/paths.d.ts.map +1 -0
- package/dist/types/libs/cli/publisher.d.ts +5 -0
- package/dist/types/libs/cli/publisher.d.ts.map +1 -0
- package/dist/types/libs/cli/types.d.ts +8 -0
- package/dist/types/libs/cli/types.d.ts.map +1 -0
- package/dist/types/libs/cli/watcher.d.ts +5 -0
- package/dist/types/libs/cli/watcher.d.ts.map +1 -0
- package/dist/types/schema-descriptors.d.ts +2 -0
- package/dist/types/schema-descriptors.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runFromCli = runFromCli;
|
|
4
|
+
// CLI entry point: parse arguments, load config, and trigger build/watch modes.
|
|
5
|
+
const url_1 = require("url");
|
|
6
|
+
const build_js_1 = require("./build.cjs");
|
|
7
|
+
const config_loader_js_1 = require("./config-loader.cjs");
|
|
8
|
+
const watcher_js_1 = require("./watcher.cjs");
|
|
9
|
+
async function runFromCli() {
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
let mode = "build";
|
|
12
|
+
let configArg;
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === "watch" || arg === "build" || arg === "dev") {
|
|
16
|
+
mode = arg;
|
|
17
|
+
}
|
|
18
|
+
else if (arg === "--config") {
|
|
19
|
+
configArg = args[i + 1];
|
|
20
|
+
i++;
|
|
21
|
+
}
|
|
22
|
+
else if (arg && arg.startsWith("--config=")) {
|
|
23
|
+
configArg = arg.split("=")[1];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const loaded = await (0, config_loader_js_1.loadUserConfig)(configArg);
|
|
27
|
+
if (!loaded)
|
|
28
|
+
return;
|
|
29
|
+
const resolved = (0, config_loader_js_1.resolvePaths)(loaded.path, loaded.config);
|
|
30
|
+
console.log("resolved: ", resolved);
|
|
31
|
+
if (mode === "watch") {
|
|
32
|
+
(0, watcher_js_1.startWatcher)(resolved);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (mode === "dev") {
|
|
36
|
+
(0, watcher_js_1.startWatcher)(resolved);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
(0, build_js_1.buildOnce)(resolved);
|
|
40
|
+
}
|
|
41
|
+
// ESM/CJS-safe main check without import.meta syntax in the source so CJS builds succeed.
|
|
42
|
+
const isDirectRun = (() => {
|
|
43
|
+
const argv1 = process.argv[1];
|
|
44
|
+
if (!argv1)
|
|
45
|
+
return false;
|
|
46
|
+
if (typeof module !== "undefined" && typeof require !== "undefined") {
|
|
47
|
+
if (require.main === module)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// Use Function to access import.meta.url only when supported to avoid CJS parse errors.
|
|
51
|
+
const metaUrl = (() => {
|
|
52
|
+
try {
|
|
53
|
+
// eslint-disable-next-line no-new-func
|
|
54
|
+
return Function("return import.meta.url")();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
return metaUrl ? metaUrl === (0, url_1.pathToFileURL)(argv1).href : false;
|
|
61
|
+
})();
|
|
62
|
+
if (isDirectRun) {
|
|
63
|
+
runFromCli();
|
|
64
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.DEFAULT_CONFIG_BASENAMES = void 0;
|
|
40
|
+
exports.findConfigPath = findConfigPath;
|
|
41
|
+
exports.loadUserConfig = loadUserConfig;
|
|
42
|
+
exports.resolvePaths = resolvePaths;
|
|
43
|
+
exports.findTsConfig = findTsConfig;
|
|
44
|
+
// Load cms0.config files and resolve relevant paths for the CLI.
|
|
45
|
+
const fs_1 = __importDefault(require("fs"));
|
|
46
|
+
const path_1 = __importDefault(require("path"));
|
|
47
|
+
const url_1 = require("url");
|
|
48
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
49
|
+
const DEFAULT_CONFIG_BASENAMES = [
|
|
50
|
+
"cms0.config.ts",
|
|
51
|
+
"cms0.config.js",
|
|
52
|
+
"cms0.config.mjs",
|
|
53
|
+
"cms0.config.cjs",
|
|
54
|
+
"cms0.config.json",
|
|
55
|
+
];
|
|
56
|
+
exports.DEFAULT_CONFIG_BASENAMES = DEFAULT_CONFIG_BASENAMES;
|
|
57
|
+
function findConfigPath(provided) {
|
|
58
|
+
if (provided) {
|
|
59
|
+
const resolved = path_1.default.isAbsolute(provided)
|
|
60
|
+
? provided
|
|
61
|
+
: path_1.default.resolve(process.cwd(), provided);
|
|
62
|
+
return fs_1.default.existsSync(resolved) ? resolved : undefined;
|
|
63
|
+
}
|
|
64
|
+
let dir = process.cwd();
|
|
65
|
+
while (true) {
|
|
66
|
+
for (const name of DEFAULT_CONFIG_BASENAMES) {
|
|
67
|
+
const candidate = path_1.default.join(dir, name);
|
|
68
|
+
if (fs_1.default.existsSync(candidate))
|
|
69
|
+
return candidate;
|
|
70
|
+
}
|
|
71
|
+
const parent = path_1.default.dirname(dir);
|
|
72
|
+
if (parent === dir)
|
|
73
|
+
break;
|
|
74
|
+
dir = parent;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function findNearestPackageJson(startPath) {
|
|
79
|
+
let dir = path_1.default.dirname(startPath);
|
|
80
|
+
while (true) {
|
|
81
|
+
const candidate = path_1.default.join(dir, "package.json");
|
|
82
|
+
if (fs_1.default.existsSync(candidate))
|
|
83
|
+
return candidate;
|
|
84
|
+
const parent = path_1.default.dirname(dir);
|
|
85
|
+
if (parent === dir)
|
|
86
|
+
break;
|
|
87
|
+
dir = parent;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
function readPackageType(pkgJsonPath) {
|
|
92
|
+
try {
|
|
93
|
+
const raw = fs_1.default.readFileSync(pkgJsonPath, "utf8");
|
|
94
|
+
const parsed = JSON.parse(raw);
|
|
95
|
+
return parsed?.type === "module"
|
|
96
|
+
? "module"
|
|
97
|
+
: parsed?.type === "commonjs"
|
|
98
|
+
? "commonjs"
|
|
99
|
+
: undefined;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function loadUserConfig(configPath) {
|
|
106
|
+
const resolvedPath = findConfigPath(configPath);
|
|
107
|
+
if (!resolvedPath) {
|
|
108
|
+
console.warn("cms0: no cms0.config.* file found; provide --config");
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const ext = path_1.default.extname(resolvedPath).toLowerCase();
|
|
112
|
+
if (ext === ".json") {
|
|
113
|
+
const json = fs_1.default.readFileSync(resolvedPath, "utf8");
|
|
114
|
+
return { path: resolvedPath, config: JSON.parse(json) };
|
|
115
|
+
}
|
|
116
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts" || ext === ".tsx") {
|
|
117
|
+
// If the enclosing package is ESM, use dynamic import instead of require.
|
|
118
|
+
const pkgJsonPath = findNearestPackageJson(resolvedPath);
|
|
119
|
+
const pkgType = pkgJsonPath ? readPackageType(pkgJsonPath) : undefined;
|
|
120
|
+
const isEsmPkg = pkgType === "module";
|
|
121
|
+
if (isEsmPkg) {
|
|
122
|
+
const compiledHref = await transpileTsModuleToTemp(resolvedPath);
|
|
123
|
+
const imported = (await Promise.resolve(`${compiledHref}`).then(s => __importStar(require(s))));
|
|
124
|
+
const config = imported?.default ?? imported?.config ?? imported ?? {};
|
|
125
|
+
cleanupTempModule(compiledHref);
|
|
126
|
+
return { path: resolvedPath, config };
|
|
127
|
+
}
|
|
128
|
+
const previous = process.env.TS_NODE_COMPILER_OPTIONS;
|
|
129
|
+
// register ts-node on demand so TypeScript configs can be required
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
131
|
+
require("ts-node/register/transpile-only");
|
|
132
|
+
try {
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
134
|
+
const required = require(resolvedPath);
|
|
135
|
+
const config = required?.default ?? required?.config ?? required ?? {};
|
|
136
|
+
return { path: resolvedPath, config };
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
if (previous === undefined) {
|
|
140
|
+
delete process.env.TS_NODE_COMPILER_OPTIONS;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
process.env.TS_NODE_COMPILER_OPTIONS = previous;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const imported = (await Promise.resolve(`${(0, url_1.pathToFileURL)(resolvedPath).href}`).then(s => __importStar(require(s))));
|
|
148
|
+
const config = imported?.default ?? imported?.config ?? imported ?? {};
|
|
149
|
+
return { path: resolvedPath, config };
|
|
150
|
+
}
|
|
151
|
+
function resolvePaths(cfgPath, config) {
|
|
152
|
+
if (!config.entry) {
|
|
153
|
+
throw new Error("cms0: config.entry is required");
|
|
154
|
+
}
|
|
155
|
+
const baseDir = path_1.default.dirname(cfgPath);
|
|
156
|
+
const entryFile = path_1.default.resolve(baseDir, config.entry);
|
|
157
|
+
const tsconfigPath = config.tsconfig
|
|
158
|
+
? path_1.default.resolve(baseDir, config.tsconfig)
|
|
159
|
+
: undefined;
|
|
160
|
+
const apiBaseUrl = config.api?.baseUrl;
|
|
161
|
+
const apiKey = config.api?.key;
|
|
162
|
+
return {
|
|
163
|
+
configPath: cfgPath,
|
|
164
|
+
entryFile,
|
|
165
|
+
tsconfigPath,
|
|
166
|
+
apiBaseUrl,
|
|
167
|
+
apiKey,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function findTsConfig(entryFile) {
|
|
171
|
+
let dir = path_1.default.dirname(path_1.default.resolve(entryFile));
|
|
172
|
+
while (true) {
|
|
173
|
+
const candidate = path_1.default.join(dir, "tsconfig.json");
|
|
174
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
175
|
+
return candidate;
|
|
176
|
+
}
|
|
177
|
+
const parent = path_1.default.dirname(dir);
|
|
178
|
+
if (parent === dir)
|
|
179
|
+
break;
|
|
180
|
+
dir = parent;
|
|
181
|
+
}
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
async function transpileTsModuleToTemp(tsPath) {
|
|
185
|
+
const ts = await Promise.resolve().then(() => __importStar(require("typescript")));
|
|
186
|
+
const source = fs_1.default.readFileSync(tsPath, "utf8");
|
|
187
|
+
const transpiled = ts.transpileModule(source, {
|
|
188
|
+
compilerOptions: {
|
|
189
|
+
module: ts.ModuleKind.ESNext,
|
|
190
|
+
target: ts.ScriptTarget.ES2022,
|
|
191
|
+
esModuleInterop: true,
|
|
192
|
+
},
|
|
193
|
+
fileName: tsPath,
|
|
194
|
+
});
|
|
195
|
+
const hash = crypto_1.default
|
|
196
|
+
.createHash("sha1")
|
|
197
|
+
.update(tsPath + source + Date.now().toString())
|
|
198
|
+
.digest("hex");
|
|
199
|
+
const outFile = path_1.default.join(path_1.default.dirname(tsPath), `.cms0-config.${hash}.mjs`);
|
|
200
|
+
fs_1.default.writeFileSync(outFile, transpiled.outputText, "utf8");
|
|
201
|
+
return (0, url_1.pathToFileURL)(outFile).href;
|
|
202
|
+
}
|
|
203
|
+
function cleanupTempModule(tempHref) {
|
|
204
|
+
try {
|
|
205
|
+
const filePath = (0, url_1.fileURLToPath)(tempHref);
|
|
206
|
+
fs_1.default.unlinkSync(filePath);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// ignore cleanup failures
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildDescriptorAlt = buildDescriptorAlt;
|
|
7
|
+
// Fresh descriptor builder using tree-based traversal from cms0<T>.
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const ts_morph_1 = require("ts-morph");
|
|
11
|
+
const config_loader_js_1 = require("./config-loader.cjs");
|
|
12
|
+
function unwrapOptional(type) {
|
|
13
|
+
let optional = false;
|
|
14
|
+
let nullable = false;
|
|
15
|
+
if (type.isUnion()) {
|
|
16
|
+
const unionTypes = type.getUnionTypes();
|
|
17
|
+
const filtered = unionTypes.filter((t) => {
|
|
18
|
+
if (t.isNull()) {
|
|
19
|
+
nullable = true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (t.isUndefined()) {
|
|
23
|
+
optional = true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
if (filtered.length === 1) {
|
|
29
|
+
return { base: filtered[0], optional, nullable };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { base: type, optional, nullable };
|
|
33
|
+
}
|
|
34
|
+
function collectModelNames(sourceFiles) {
|
|
35
|
+
const names = new Set();
|
|
36
|
+
sourceFiles.forEach((sf) => {
|
|
37
|
+
sf.getTypeAliases().forEach((alias) => {
|
|
38
|
+
if (!alias.hasExportKeyword() && !alias.isDefaultExport())
|
|
39
|
+
return;
|
|
40
|
+
if (alias.getType().isObject())
|
|
41
|
+
names.add(alias.getName());
|
|
42
|
+
});
|
|
43
|
+
sf.getInterfaces().forEach((iface) => {
|
|
44
|
+
if (!iface.hasExportKeyword() && !iface.isDefaultExport())
|
|
45
|
+
return;
|
|
46
|
+
if (iface.getType().isObject())
|
|
47
|
+
names.add(iface.getName());
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return names;
|
|
51
|
+
}
|
|
52
|
+
function collectReferencedModelNames(type, names, visited, ctx) {
|
|
53
|
+
const { base } = unwrapOptional(type);
|
|
54
|
+
const key = `${ctx}:${base.getText()}`;
|
|
55
|
+
if (visited.has(key))
|
|
56
|
+
return;
|
|
57
|
+
visited.add(key);
|
|
58
|
+
if (base.isUnion()) {
|
|
59
|
+
base.getUnionTypes().forEach((t, idx) => collectReferencedModelNames(t, names, visited, `${ctx}|${idx}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (base.isArray()) {
|
|
63
|
+
const elem = base.getArrayElementTypeOrThrow();
|
|
64
|
+
collectReferencedModelNames(elem, names, visited, `${ctx}[]`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const aliasSymbol = base.getAliasSymbol();
|
|
68
|
+
const symbol = base.getSymbol();
|
|
69
|
+
const name = aliasSymbol?.getName() ?? symbol?.getName();
|
|
70
|
+
if (name && base.isObject()) {
|
|
71
|
+
names.add(name);
|
|
72
|
+
}
|
|
73
|
+
if (base.isObject()) {
|
|
74
|
+
base.getProperties().forEach((prop) => {
|
|
75
|
+
const decl = prop.getDeclarations()[0];
|
|
76
|
+
if (!decl)
|
|
77
|
+
return;
|
|
78
|
+
collectReferencedModelNames(decl.getType(), names, visited, `${ctx}.${prop.getName()}`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function buildProperties(type, modelNames, warnings, ctx) {
|
|
83
|
+
const props = {};
|
|
84
|
+
type.getProperties().forEach((prop) => {
|
|
85
|
+
const decl = prop.getDeclarations()[0];
|
|
86
|
+
if (!decl)
|
|
87
|
+
return;
|
|
88
|
+
const analyzed = unwrapOptional(decl.getType());
|
|
89
|
+
props[prop.getName()] = descriptorFromType(analyzed.base, modelNames, warnings, `${ctx}.${prop.getName()}`, { optional: prop.isOptional() || analyzed.optional, nullable: analyzed.nullable });
|
|
90
|
+
});
|
|
91
|
+
return props;
|
|
92
|
+
}
|
|
93
|
+
function descriptorFromType(type, modelNames, warnings, ctx, opts) {
|
|
94
|
+
const { base, optional, nullable } = unwrapOptional(type);
|
|
95
|
+
const isOptional = !!opts?.optional || optional;
|
|
96
|
+
const isNullable = !!opts?.nullable || nullable;
|
|
97
|
+
if (base.isString()) {
|
|
98
|
+
return { kind: "primitive", type: "string", optional: isOptional, nullable: isNullable };
|
|
99
|
+
}
|
|
100
|
+
if (base.isNumber()) {
|
|
101
|
+
return { kind: "primitive", type: "number", optional: isOptional, nullable: isNullable };
|
|
102
|
+
}
|
|
103
|
+
if (base.isBoolean()) {
|
|
104
|
+
return { kind: "primitive", type: "boolean", optional: isOptional, nullable: isNullable };
|
|
105
|
+
}
|
|
106
|
+
if (base.isArray()) {
|
|
107
|
+
const elem = base.getArrayElementTypeOrThrow();
|
|
108
|
+
return {
|
|
109
|
+
type: "array",
|
|
110
|
+
items: descriptorFromType(elem, modelNames, warnings, `${ctx}[]`),
|
|
111
|
+
optional: isOptional,
|
|
112
|
+
nullable: isNullable,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const aliasSymbol = base.getAliasSymbol();
|
|
116
|
+
const symbol = base.getSymbol();
|
|
117
|
+
const name = aliasSymbol?.getName() ?? symbol?.getName();
|
|
118
|
+
if (name && modelNames.has(name)) {
|
|
119
|
+
return { kind: "modelRef", model: name, optional: isOptional, nullable: isNullable };
|
|
120
|
+
}
|
|
121
|
+
if (base.isObject()) {
|
|
122
|
+
return {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: buildProperties(base, modelNames, warnings, ctx),
|
|
125
|
+
optional: isOptional,
|
|
126
|
+
nullable: isNullable,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
warnings.add(`cms0: unsupported type at ${ctx}; falling back to string`);
|
|
130
|
+
return { kind: "primitive", type: "string", optional: isOptional, nullable: isNullable };
|
|
131
|
+
}
|
|
132
|
+
function collectModels(sourceFiles, modelNames, modelMap, warnings) {
|
|
133
|
+
const handleShape = (name, type, ctx) => {
|
|
134
|
+
if (modelMap[name])
|
|
135
|
+
return;
|
|
136
|
+
if (!modelNames.has(name))
|
|
137
|
+
return;
|
|
138
|
+
if (!type.isObject())
|
|
139
|
+
return;
|
|
140
|
+
modelMap[name] = { kind: "model", properties: buildProperties(type, modelNames, warnings, ctx) };
|
|
141
|
+
};
|
|
142
|
+
sourceFiles.forEach((sf) => {
|
|
143
|
+
sf.getTypeAliases().forEach((alias) => {
|
|
144
|
+
if (!alias.hasExportKeyword() && !alias.isDefaultExport())
|
|
145
|
+
return;
|
|
146
|
+
handleShape(alias.getName(), alias.getType(), `model ${alias.getName()}`);
|
|
147
|
+
});
|
|
148
|
+
sf.getInterfaces().forEach((iface) => {
|
|
149
|
+
if (!iface.hasExportKeyword() && !iface.isDefaultExport())
|
|
150
|
+
return;
|
|
151
|
+
handleShape(iface.getName(), iface.getType(), `model ${iface.getName()}`);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function findRootType(sourceFiles) {
|
|
156
|
+
for (const sf of sourceFiles) {
|
|
157
|
+
const invoc = sf
|
|
158
|
+
?.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)
|
|
159
|
+
.find((call) => call.getExpression().getText() === "cms0");
|
|
160
|
+
if (invoc) {
|
|
161
|
+
const typeArgs = invoc.getTypeArguments();
|
|
162
|
+
if (typeArgs.length > 0)
|
|
163
|
+
return typeArgs[0].getType();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
function buildDescriptorAlt(resolved) {
|
|
169
|
+
const tsconfig = resolved.tsconfigPath ?? (0, config_loader_js_1.findTsConfig)(resolved.entryFile);
|
|
170
|
+
const project = tsconfig ? new ts_morph_1.Project({ tsConfigFilePath: tsconfig }) : new ts_morph_1.Project();
|
|
171
|
+
const warnings = new Set();
|
|
172
|
+
if (!project.getSourceFile(resolved.entryFile) && fs_1.default.existsSync(resolved.entryFile)) {
|
|
173
|
+
project.addSourceFileAtPath(resolved.entryFile);
|
|
174
|
+
}
|
|
175
|
+
const sourceFiles = project.getSourceFiles().filter((sf) => {
|
|
176
|
+
const filePath = path_1.default.resolve(sf.getFilePath());
|
|
177
|
+
if (filePath.includes("node_modules"))
|
|
178
|
+
return false;
|
|
179
|
+
if (filePath.endsWith(".d.ts"))
|
|
180
|
+
return false;
|
|
181
|
+
const rel = path_1.default.relative(path_1.default.dirname(resolved.entryFile), filePath);
|
|
182
|
+
return rel && !rel.startsWith("..") && !path_1.default.isAbsolute(rel);
|
|
183
|
+
});
|
|
184
|
+
const rootType = findRootType(sourceFiles);
|
|
185
|
+
if (!rootType)
|
|
186
|
+
throw new Error("Could not locate cms0<T>() invocation in project sources.");
|
|
187
|
+
// Restrict models to only those referenced by the root type tree and exported in scope.
|
|
188
|
+
const exportedModelNames = collectModelNames(sourceFiles);
|
|
189
|
+
const referencedModelNames = new Set();
|
|
190
|
+
collectReferencedModelNames(rootType, referencedModelNames, new Set(), "root");
|
|
191
|
+
const modelNames = new Set(Array.from(referencedModelNames).filter((n) => exportedModelNames.has(n)));
|
|
192
|
+
const modelMap = {};
|
|
193
|
+
collectModels(sourceFiles, modelNames, modelMap, warnings);
|
|
194
|
+
const roots = {};
|
|
195
|
+
for (const prop of rootType.getProperties()) {
|
|
196
|
+
const name = prop.getName();
|
|
197
|
+
const decl = prop.getDeclarations()[0];
|
|
198
|
+
if (!decl)
|
|
199
|
+
continue;
|
|
200
|
+
const analyzed = unwrapOptional(decl.getType());
|
|
201
|
+
const propType = analyzed.base;
|
|
202
|
+
const propOptional = prop.isOptional() || analyzed.optional;
|
|
203
|
+
const propNullable = analyzed.nullable;
|
|
204
|
+
if (propType.isArray()) {
|
|
205
|
+
const elem = propType.getArrayElementTypeOrThrow();
|
|
206
|
+
const itemDesc = descriptorFromType(elem, modelNames, warnings, `root ${name}[]`, { optional: false, nullable: false });
|
|
207
|
+
roots[name] = {
|
|
208
|
+
type: "array",
|
|
209
|
+
items: itemDesc,
|
|
210
|
+
optional: propOptional,
|
|
211
|
+
nullable: propNullable,
|
|
212
|
+
};
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const fieldDesc = descriptorFromType(propType, modelNames, warnings, `root ${name}`, { optional: propOptional, nullable: propNullable });
|
|
216
|
+
roots[name] = fieldDesc;
|
|
217
|
+
}
|
|
218
|
+
if (warnings.size)
|
|
219
|
+
warnings.forEach((w) => console.warn(w));
|
|
220
|
+
return { models: modelMap, roots };
|
|
221
|
+
}
|