@clarify-labs/cli 0.8.2 → 0.9.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.
@@ -1772,6 +1772,126 @@ function resolveProjectConfig(config = {}) {
1772
1772
  tabs: config.tabs
1773
1773
  };
1774
1774
  }
1775
+ /** Serialize a ContentRoute into the minimal shape exposed to tooling. */
1776
+ function toDevRouteEntry(route) {
1777
+ return {
1778
+ path: route.path,
1779
+ filePath: route.filePath,
1780
+ locale: route.locale,
1781
+ basePath: route.basePath,
1782
+ kind: route.kind,
1783
+ title: route.title
1784
+ };
1785
+ }
1786
+ /**
1787
+ * Resolve a single file path to its preview route.
1788
+ *
1789
+ * Selection order:
1790
+ * 1. Routes whose `filePath` exactly matches.
1791
+ * 2. Among matches, prefer the one whose `locale` matches the file's locale
1792
+ * directory (e.g. `source/zh-CN/x.mdx` → locale `zh-CN`).
1793
+ * 3. If no locale match, fall back to the default locale (if i18n is enabled)
1794
+ * or the first match.
1795
+ */
1796
+ function resolveRouteForFile(filePath, routes, defaultLocale) {
1797
+ const matches = routes.filter((r) => r.filePath === filePath);
1798
+ if (matches.length === 0) return void 0;
1799
+ if (matches.length === 1) return matches[0];
1800
+ const fileLocale = inferLocaleFromPath(filePath);
1801
+ if (fileLocale) {
1802
+ const localeMatch = matches.find((r) => r.locale === fileLocale);
1803
+ if (localeMatch) return localeMatch;
1804
+ }
1805
+ if (defaultLocale) {
1806
+ const defaultMatch = matches.find((r) => r.locale === defaultLocale);
1807
+ if (defaultMatch) return defaultMatch;
1808
+ }
1809
+ return matches[0];
1810
+ }
1811
+ /**
1812
+ * Derive a best-effort preview route for a file that is not in the route
1813
+ * manifest (e.g. a draft or newly created file). The path is computed from the
1814
+ * file's location relative to the content root, following the same convention
1815
+ * as route discovery (`source/{locale}/path/index.mdx` → `/{locale}/path`).
1816
+ *
1817
+ * Returns null when the file is outside the content root or is not a
1818
+ * recognized content file type.
1819
+ */
1820
+ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
1821
+ const ref = relative(contentRoot, filePath);
1822
+ if (!ref || ref.startsWith("..")) return null;
1823
+ const isMdx = /\.mdx?$/.test(filePath);
1824
+ const isOpenApi = /\.openapi\.(json|ya?ml)$/.test(filePath);
1825
+ if (!isMdx && !isOpenApi) return null;
1826
+ const segments = ref.split("/");
1827
+ const fileLocale = inferLocaleFromPath(filePath);
1828
+ const localeSegmentIndex = fileLocale ? segments.indexOf(fileLocale) : -1;
1829
+ const basePath = basePathFromRef(localeSegmentIndex >= 0 ? segments.slice(localeSegmentIndex + 1).join("/") : ref);
1830
+ const path = fileLocale && defaultLocale ? localizedRoutePath(basePath, fileLocale, {
1831
+ defaultLocale,
1832
+ missing: "fallback",
1833
+ locales: []
1834
+ }) : basePath;
1835
+ const title = segments[segments.length - 1]?.replace(/\.(mdx?|openapi\.(json|ya?ml))$/, "") || "Untitled";
1836
+ return {
1837
+ path: normalizePath(path),
1838
+ filePath,
1839
+ locale: fileLocale,
1840
+ basePath: normalizePath(basePath),
1841
+ kind: isMdx ? "mdx" : "openapi",
1842
+ title,
1843
+ inferred: true
1844
+ };
1845
+ }
1846
+ /** Best-effort locale inference from a file path segment like `.../zh-CN/x.mdx`. */
1847
+ function inferLocaleFromPath(filePath) {
1848
+ const segments = filePath.split("/");
1849
+ for (const segment of segments) if (/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/.test(segment)) return segment;
1850
+ }
1851
+ /** Read and parse the JSON body of an IncomingMessage. */
1852
+ function readJsonBody(req) {
1853
+ return new Promise((resolveBody, reject) => {
1854
+ const chunks = [];
1855
+ req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
1856
+ req.on("error", reject);
1857
+ req.on("end", () => {
1858
+ const raw = Buffer.concat(chunks).toString("utf-8").trim();
1859
+ if (!raw) return resolveBody({});
1860
+ try {
1861
+ resolveBody(JSON.parse(raw));
1862
+ } catch (err) {
1863
+ reject(err);
1864
+ }
1865
+ });
1866
+ });
1867
+ }
1868
+ /**
1869
+ * Handle a request to the dev route endpoint. Pure function — does not touch
1870
+ * the network directly, only reads the request body and writes to `res`.
1871
+ */
1872
+ async function handleDevRouteRequest(req, res, routes, projectConfig, contentRoot) {
1873
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
1874
+ let body;
1875
+ try {
1876
+ body = await readJsonBody(req);
1877
+ } catch {
1878
+ res.statusCode = 400;
1879
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
1880
+ return;
1881
+ }
1882
+ const file = typeof body === "object" && body !== null ? body.file : void 0;
1883
+ if (typeof file === "string" && file.length > 0) {
1884
+ const route = resolveRouteForFile(file, routes, projectConfig.i18n?.defaultLocale);
1885
+ if (route) {
1886
+ res.end(JSON.stringify(toDevRouteEntry(route)));
1887
+ return;
1888
+ }
1889
+ const inferred = inferRouteForFile(file, contentRoot, projectConfig.i18n?.defaultLocale);
1890
+ res.end(JSON.stringify(inferred));
1891
+ return;
1892
+ }
1893
+ res.end(JSON.stringify(routes.map(toDevRouteEntry)));
1894
+ }
1775
1895
  //#endregion
1776
1896
  //#region source/core/env-types.ts
1777
1897
  var GENERATED_FILE_NAME = "clarify-env.d.ts";
@@ -1831,6 +1951,84 @@ function resolveBuildOptions(options = {}) {
1831
1951
  };
1832
1952
  }
1833
1953
  //#endregion
1954
+ //#region source/core/user-config.ts
1955
+ function defineConfig(config) {
1956
+ validateProjectConfig(config);
1957
+ return config;
1958
+ }
1959
+ /**
1960
+ * Config filenames Clarify CLI looks for at the project root, in resolution
1961
+ * order. Exported so other modules (e.g. the dev `project-info` endpoint) can
1962
+ * surface the same list to tooling without duplicating it.
1963
+ */
1964
+ var CONFIG_FILENAMES = [
1965
+ "clarify.ts",
1966
+ "clarify.js",
1967
+ "clarify.json"
1968
+ ];
1969
+ function findClarifyConfigFile(root) {
1970
+ for (const filename of CONFIG_FILENAMES) {
1971
+ const filePath = resolve(root, filename);
1972
+ if (existsSync(filePath)) return filePath;
1973
+ }
1974
+ }
1975
+ function assertConfigObject(config, configFile) {
1976
+ if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
1977
+ return config;
1978
+ }
1979
+ function loadJsonConfig(configFile) {
1980
+ const config = assertConfigObject(JSON.parse(readFileSync(configFile, "utf-8")), configFile);
1981
+ const projectConfig = validateProjectConfig(config);
1982
+ const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
1983
+ return {
1984
+ ...projectConfig,
1985
+ ...ssg ? { ssg } : {}
1986
+ };
1987
+ }
1988
+ async function loadClarifyConfig(root, env) {
1989
+ const configFile = findClarifyConfigFile(root);
1990
+ if (!configFile) return {};
1991
+ if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
1992
+ return assertConfigObject((await loadConfigFromFile(env, configFile, root, "silent"))?.config, configFile);
1993
+ }
1994
+ /**
1995
+ * File extensions Clarify treats as content files. Kept in sync with the
1996
+ * patterns used by route discovery in `parsers/routes.ts` and the OpenAPI
1997
+ * page generator. Exposed to tooling so the extension's "is this a content
1998
+ * file?" check stays accurate without duplicating the regex.
1999
+ */
2000
+ var CONTENT_FILE_EXTENSIONS = [
2001
+ ".md",
2002
+ ".mdx",
2003
+ ".openapi.json",
2004
+ ".openapi.yaml",
2005
+ ".openapi.yml"
2006
+ ];
2007
+ /**
2008
+ * Build the project-info response payload from the resolved plugin state.
2009
+ */
2010
+ function buildProjectInfo(projectRoot, contentRoot, projectConfig) {
2011
+ const response = {
2012
+ configFilenames: CONFIG_FILENAMES,
2013
+ contentFileExtensions: CONTENT_FILE_EXTENSIONS,
2014
+ contentRoot: (contentRoot.startsWith(projectRoot) ? contentRoot.slice(projectRoot.length).replace(/^[/\\]+/, "") : contentRoot) || "source",
2015
+ projectRoot
2016
+ };
2017
+ if (projectConfig.i18n) response.i18n = {
2018
+ defaultLocale: projectConfig.i18n.defaultLocale,
2019
+ locales: projectConfig.i18n.locales.map((l) => l.code)
2020
+ };
2021
+ return response;
2022
+ }
2023
+ /**
2024
+ * Handle a request to the project-info endpoint. Responds to any method
2025
+ * (GET is typical) with the JSON payload — there is no request body to parse.
2026
+ */
2027
+ function handleProjectInfoRequest(_req, res, projectRoot, contentRoot, projectConfig) {
2028
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2029
+ res.end(JSON.stringify(buildProjectInfo(projectRoot, contentRoot, projectConfig)));
2030
+ }
2031
+ //#endregion
1834
2032
  //#region source/core/site.ts
1835
2033
  async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
1836
2034
  return (await runHooks(plugins, "routes:discover", {
@@ -2104,42 +2302,6 @@ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundle
2104
2302
  }
2105
2303
  }
2106
2304
  //#endregion
2107
- //#region source/core/user-config.ts
2108
- function defineConfig(config) {
2109
- validateProjectConfig(config);
2110
- return config;
2111
- }
2112
- var CONFIG_FILENAMES = [
2113
- "clarify.ts",
2114
- "clarify.js",
2115
- "clarify.json"
2116
- ];
2117
- function findClarifyConfigFile(root) {
2118
- for (const filename of CONFIG_FILENAMES) {
2119
- const filePath = resolve(root, filename);
2120
- if (existsSync(filePath)) return filePath;
2121
- }
2122
- }
2123
- function assertConfigObject(config, configFile) {
2124
- if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
2125
- return config;
2126
- }
2127
- function loadJsonConfig(configFile) {
2128
- const config = assertConfigObject(JSON.parse(readFileSync(configFile, "utf-8")), configFile);
2129
- const projectConfig = validateProjectConfig(config);
2130
- const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
2131
- return {
2132
- ...projectConfig,
2133
- ...ssg ? { ssg } : {}
2134
- };
2135
- }
2136
- async function loadClarifyConfig(root, env) {
2137
- const configFile = findClarifyConfigFile(root);
2138
- if (!configFile) return {};
2139
- if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
2140
- return assertConfigObject((await loadConfigFromFile(env, configFile, root, "silent"))?.config, configFile);
2141
- }
2142
- //#endregion
2143
2305
  //#region source/core/plugin.ts
2144
2306
  function loadVirtualModule(id, modules) {
2145
2307
  const bareId = stripVirtualPrefix(id);
@@ -2287,6 +2449,11 @@ function clarifyPlugin(options = {}) {
2287
2449
  async configureServer(server) {
2288
2450
  server.watcher.add(contentRoot);
2289
2451
  if (configFilePath) server.watcher.add(configFilePath);
2452
+ server.middlewares.use((req, res, next) => {
2453
+ if (req.url === "/dev/query-preview-route" && req.method === "POST") return handleDevRouteRequest(req, res, routes, projectConfig, contentRoot);
2454
+ if (req.url === "/dev/project-info" && (req.method === "GET" || req.method === "HEAD")) return handleProjectInfoRequest(req, res, root, contentRoot, projectConfig);
2455
+ next();
2456
+ });
2290
2457
  const handleContentTreeChange = async (filePath) => {
2291
2458
  const relativeContentFile = relative(contentRoot, isAbsolute(filePath) ? filePath : join(root, filePath));
2292
2459
  if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !isAbsolute(relativeContentFile))) return;
@@ -2368,6 +2535,6 @@ function clarifyPlugin(options = {}) {
2368
2535
  ].flat().filter(Boolean);
2369
2536
  }
2370
2537
  //#endregion
2371
- export { resolveClarifySite as a, createClarifyRuntimeAliases as i, defineConfig as n, createClarifyTempDir as o, loadClarifyConfig as r, clarifyPlugin as t };
2538
+ export { loadClarifyConfig as a, defineConfig as i, createClarifyRuntimeAliases as n, createClarifyTempDir as o, resolveClarifySite as r, clarifyPlugin as t };
2372
2539
 
2373
- //# sourceMappingURL=plugin-D5EF3pYl.js.map
2540
+ //# sourceMappingURL=plugin-_mqQSBqE.js.map