@kosdev-code/kos-codegen-core 0.1.0-next.901 → 0.1.0-next.904

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/index.mjs CHANGED
@@ -1866,36 +1866,44 @@ function generateModel(params) {
1866
1866
  }
1867
1867
  }
1868
1868
  const DECLARATION_MARKER = "@kosModel";
1869
- function findModelDeclarationFile(codegenFs, searchRoot, typeIds) {
1870
- const wanted = new Set(typeIds.filter(Boolean));
1871
- if (wanted.size === 0) return null;
1872
- const matches = [];
1869
+ function collectModelDeclarations(codegenFs, searchRoot) {
1870
+ const found = [];
1873
1871
  for (const filePath of codegenFs.listFiles(searchRoot)) {
1874
1872
  if (!filePath.endsWith(".ts") || filePath.endsWith(".d.ts")) continue;
1875
1873
  const content = codegenFs.read(filePath);
1876
1874
  if (!content || !content.includes(DECLARATION_MARKER)) continue;
1877
- const declared = readDeclaredModelType(codegenFs, filePath);
1878
- if (declared && wanted.has(declared)) matches.push(filePath);
1875
+ const declared = readModelDeclaration(codegenFs, filePath);
1876
+ if (declared) found.push(declared);
1879
1877
  }
1878
+ return found;
1879
+ }
1880
+ function findModelDeclarationFile(codegenFs, searchRoot, identifiers) {
1881
+ const wanted = new Set(identifiers.filter(Boolean));
1882
+ if (wanted.size === 0) return null;
1883
+ const matches = collectModelDeclarations(codegenFs, searchRoot).filter(
1884
+ (d) => d.typeId && wanted.has(d.typeId) || d.className && wanted.has(d.className)
1885
+ ).map((d) => d.filePath);
1880
1886
  if (matches.length === 0) return null;
1881
1887
  if (matches.length > 1) {
1882
1888
  throw new Error(
1883
- `Model type '${[...wanted].join("' / '")}' is declared in more than one file:
1889
+ `Model '${[...wanted].join(
1890
+ "' / '"
1891
+ )}' is declared in more than one file:
1884
1892
  ` + matches.sort().map((c) => ` - ${c}`).join("\n") + `
1885
1893
  Pass modelPath to pick one.`
1886
1894
  );
1887
1895
  }
1888
1896
  return matches[0];
1889
1897
  }
1890
- function readDeclaredModelType(codegenFs, filePath) {
1898
+ function readModelDeclaration(codegenFs, filePath) {
1891
1899
  try {
1892
1900
  return readSourceFile(codegenFs, filePath, (sf) => {
1893
1901
  const cls = sf.getClasses().find((c) => c.getDecorator("kosModel"));
1894
1902
  if (!cls) return void 0;
1895
1903
  const config = decoratorConfigText(sf, cls, "kosModel");
1896
1904
  const inner = config?.startsWith("{") ? modelTypeIdProperty(config) : config;
1897
- if (!inner) return void 0;
1898
- return resolveToStringLiteral(inner.trim(), sf.getFullText());
1905
+ const typeId = inner ? resolveToStringLiteral(inner.trim(), sf.getFullText()) : void 0;
1906
+ return { filePath, typeId, className: cls.getName() };
1899
1907
  });
1900
1908
  } catch {
1901
1909
  return void 0;
@@ -1910,7 +1918,9 @@ function resolveToStringLiteral(expression, fileText) {
1910
1918
  if (literal) return literal[1];
1911
1919
  if (!/^[A-Za-z_$][\w$]*$/.test(expression)) return void 0;
1912
1920
  const declared = fileText.match(
1913
- new RegExp(`\\b(?:const|let|var)\\s+${expression}\\s*(?::[^=]+)?=\\s*["'\`]([^"'\`]+)["'\`]`)
1921
+ new RegExp(
1922
+ `\\b(?:const|let|var)\\s+${expression}\\s*(?::[^=]+)?=\\s*["'\`]([^"'\`]+)["'\`]`
1923
+ )
1914
1924
  );
1915
1925
  return declared ? declared[1] : void 0;
1916
1926
  }
@@ -1965,7 +1975,82 @@ function resolveModelFilePath(codegenFs, query, projects) {
1965
1975
  if (byDeclaration) {
1966
1976
  return { modelFilePath: byDeclaration, internal, sourceRoot };
1967
1977
  }
1968
- return { modelFilePath, internal, sourceRoot };
1978
+ throw new ModelFileNotFoundError(
1979
+ query.modelName,
1980
+ query.modelProject,
1981
+ modelFilePath,
1982
+ collectModelDeclarations(codegenFs, searchRoot)
1983
+ );
1984
+ }
1985
+ class ModelFileNotFoundError extends Error {
1986
+ constructor(modelName, modelProject, conventionalPath, candidates) {
1987
+ super(
1988
+ buildNotFoundMessage(
1989
+ modelName,
1990
+ modelProject,
1991
+ conventionalPath,
1992
+ candidates
1993
+ )
1994
+ );
1995
+ this.modelName = modelName;
1996
+ this.modelProject = modelProject;
1997
+ this.conventionalPath = conventionalPath;
1998
+ this.candidates = candidates;
1999
+ this.name = "ModelFileNotFoundError";
2000
+ }
2001
+ modelName;
2002
+ modelProject;
2003
+ conventionalPath;
2004
+ candidates;
2005
+ }
2006
+ function buildNotFoundMessage(modelName, modelProject, conventionalPath, candidates) {
2007
+ const lines = [
2008
+ `Model '${modelName}' not found in project '${modelProject}'.`,
2009
+ `Looked for ${conventionalPath}, any '*-model.ts' under the model folder, and any '@kosModel' class named or typed '${modelName}'.`
2010
+ ];
2011
+ if (candidates.length === 0) {
2012
+ lines.push(
2013
+ `This project declares no '@kosModel' classes under its model folder.`
2014
+ );
2015
+ return lines.join("\n");
2016
+ }
2017
+ const ranked = [...candidates].sort(
2018
+ (a, b) => closeness(modelName, b) - closeness(modelName, a)
2019
+ );
2020
+ const shown = ranked.slice(0, NOT_FOUND_SUGGESTIONS);
2021
+ lines.push(
2022
+ ``,
2023
+ `Closest of the ${candidates.length} models this project declares:`
2024
+ );
2025
+ for (const c of shown) {
2026
+ const names = [c.typeId, c.className].filter(Boolean).join(" | ");
2027
+ lines.push(` ${names || "(name unresolved)"}
2028
+ ${c.filePath}`);
2029
+ }
2030
+ if (ranked.length > shown.length) {
2031
+ lines.push(` …and ${ranked.length - shown.length} more.`);
2032
+ }
2033
+ lines.push(``, `Or pass modelPath to name the file directly.`);
2034
+ return lines.join("\n");
2035
+ }
2036
+ const NOT_FOUND_SUGGESTIONS = 8;
2037
+ function closeness(wanted, candidate) {
2038
+ const norm = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
2039
+ const target = norm(wanted);
2040
+ let best = 0;
2041
+ for (const name of [candidate.typeId, candidate.className]) {
2042
+ if (!name) continue;
2043
+ const n = norm(name);
2044
+ if (n === target) return 1e3;
2045
+ if (n.includes(target) || target.includes(n)) {
2046
+ best = Math.max(best, 500 + Math.min(n.length, target.length));
2047
+ continue;
2048
+ }
2049
+ let i = 0;
2050
+ while (i < n.length && i < target.length && n[i] === target[i]) i++;
2051
+ best = Math.max(best, i);
2052
+ }
2053
+ return best;
1969
2054
  }
1970
2055
  function findModelFileByName(codegenFs, searchRoot, modelNameDashCase) {
1971
2056
  const fileName = `${modelNameDashCase}-model.ts`;
@@ -2062,7 +2147,9 @@ function generateViewModel(codegenFs, templateDir, options, cwd, projects) {
2062
2147
  logger.info(
2063
2148
  `Generating ViewModel ${normalized.nameDashCase} in ${projectRoot}`
2064
2149
  );
2065
- const constructorParams = resolved.map(({ name, type }) => ({ name, type }));
2150
+ const constructorParams = resolved.map(({ name, type }) => {
2151
+ return { name, type };
2152
+ });
2066
2153
  generateFilesFromTemplates(codegenFs, templateDir, viewModelFolder, {
2067
2154
  ...normalized,
2068
2155
  internal,
@@ -2122,7 +2209,9 @@ function groupImports(resolved) {
2122
2209
  types.push(type);
2123
2210
  }
2124
2211
  }
2125
- return [...byModule].map(([module, types]) => ({ module, types }));
2212
+ return [...byModule].map(([module, types]) => {
2213
+ return { module, types };
2214
+ });
2126
2215
  }
2127
2216
  function modelBaseName(modelFilePath, modelName) {
2128
2217
  const base = path.basename(modelFilePath);
@@ -4530,6 +4619,7 @@ export {
4530
4619
  KOS_JSON_PATHS,
4531
4620
  KosConfigBuilder,
4532
4621
  LOCALIZED_PLUGIN_TYPES,
4622
+ ModelFileNotFoundError,
4533
4623
  PLUGIN_TYPES,
4534
4624
  PluginHandlerFactory,
4535
4625
  TrackingFileSystem,