@clarify-labs/cli 0.8.2 → 0.9.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.
@@ -1801,6 +1801,126 @@ function resolveProjectConfig(config = {}) {
1801
1801
  tabs: config.tabs
1802
1802
  };
1803
1803
  }
1804
+ /** Serialize a ContentRoute into the minimal shape exposed to tooling. */
1805
+ function toDevRouteEntry(route) {
1806
+ return {
1807
+ path: route.path,
1808
+ filePath: route.filePath,
1809
+ locale: route.locale,
1810
+ basePath: route.basePath,
1811
+ kind: route.kind,
1812
+ title: route.title
1813
+ };
1814
+ }
1815
+ /**
1816
+ * Resolve a single file path to its preview route.
1817
+ *
1818
+ * Selection order:
1819
+ * 1. Routes whose `filePath` exactly matches.
1820
+ * 2. Among matches, prefer the one whose `locale` matches the file's locale
1821
+ * directory (e.g. `source/zh-CN/x.mdx` → locale `zh-CN`).
1822
+ * 3. If no locale match, fall back to the default locale (if i18n is enabled)
1823
+ * or the first match.
1824
+ */
1825
+ function resolveRouteForFile(filePath, routes, defaultLocale) {
1826
+ const matches = routes.filter((r) => r.filePath === filePath);
1827
+ if (matches.length === 0) return void 0;
1828
+ if (matches.length === 1) return matches[0];
1829
+ const fileLocale = inferLocaleFromPath(filePath);
1830
+ if (fileLocale) {
1831
+ const localeMatch = matches.find((r) => r.locale === fileLocale);
1832
+ if (localeMatch) return localeMatch;
1833
+ }
1834
+ if (defaultLocale) {
1835
+ const defaultMatch = matches.find((r) => r.locale === defaultLocale);
1836
+ if (defaultMatch) return defaultMatch;
1837
+ }
1838
+ return matches[0];
1839
+ }
1840
+ /**
1841
+ * Derive a best-effort preview route for a file that is not in the route
1842
+ * manifest (e.g. a draft or newly created file). The path is computed from the
1843
+ * file's location relative to the content root, following the same convention
1844
+ * as route discovery (`source/{locale}/path/index.mdx` → `/{locale}/path`).
1845
+ *
1846
+ * Returns null when the file is outside the content root or is not a
1847
+ * recognized content file type.
1848
+ */
1849
+ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
1850
+ const ref = (0, node_path.relative)(contentRoot, filePath);
1851
+ if (!ref || ref.startsWith("..")) return null;
1852
+ const isMdx = /\.mdx?$/.test(filePath);
1853
+ const isOpenApi = /\.openapi\.(json|ya?ml)$/.test(filePath);
1854
+ if (!isMdx && !isOpenApi) return null;
1855
+ const segments = ref.split("/");
1856
+ const fileLocale = inferLocaleFromPath(filePath);
1857
+ const localeSegmentIndex = fileLocale ? segments.indexOf(fileLocale) : -1;
1858
+ const basePath = basePathFromRef(localeSegmentIndex >= 0 ? segments.slice(localeSegmentIndex + 1).join("/") : ref);
1859
+ const path = fileLocale && defaultLocale ? localizedRoutePath(basePath, fileLocale, {
1860
+ defaultLocale,
1861
+ missing: "fallback",
1862
+ locales: []
1863
+ }) : basePath;
1864
+ const title = segments[segments.length - 1]?.replace(/\.(mdx?|openapi\.(json|ya?ml))$/, "") || "Untitled";
1865
+ return {
1866
+ path: normalizePath(path),
1867
+ filePath,
1868
+ locale: fileLocale,
1869
+ basePath: normalizePath(basePath),
1870
+ kind: isMdx ? "mdx" : "openapi",
1871
+ title,
1872
+ inferred: true
1873
+ };
1874
+ }
1875
+ /** Best-effort locale inference from a file path segment like `.../zh-CN/x.mdx`. */
1876
+ function inferLocaleFromPath(filePath) {
1877
+ const segments = filePath.split("/");
1878
+ for (const segment of segments) if (/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/.test(segment)) return segment;
1879
+ }
1880
+ /** Read and parse the JSON body of an IncomingMessage. */
1881
+ function readJsonBody(req) {
1882
+ return new Promise((resolveBody, reject) => {
1883
+ const chunks = [];
1884
+ req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
1885
+ req.on("error", reject);
1886
+ req.on("end", () => {
1887
+ const raw = Buffer.concat(chunks).toString("utf-8").trim();
1888
+ if (!raw) return resolveBody({});
1889
+ try {
1890
+ resolveBody(JSON.parse(raw));
1891
+ } catch (err) {
1892
+ reject(err);
1893
+ }
1894
+ });
1895
+ });
1896
+ }
1897
+ /**
1898
+ * Handle a request to the dev route endpoint. Pure function — does not touch
1899
+ * the network directly, only reads the request body and writes to `res`.
1900
+ */
1901
+ async function handleDevRouteRequest(req, res, routes, projectConfig, contentRoot) {
1902
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
1903
+ let body;
1904
+ try {
1905
+ body = await readJsonBody(req);
1906
+ } catch {
1907
+ res.statusCode = 400;
1908
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
1909
+ return;
1910
+ }
1911
+ const file = typeof body === "object" && body !== null ? body.file : void 0;
1912
+ if (typeof file === "string" && file.length > 0) {
1913
+ const route = resolveRouteForFile(file, routes, projectConfig.i18n?.defaultLocale);
1914
+ if (route) {
1915
+ res.end(JSON.stringify(toDevRouteEntry(route)));
1916
+ return;
1917
+ }
1918
+ const inferred = inferRouteForFile(file, contentRoot, projectConfig.i18n?.defaultLocale);
1919
+ res.end(JSON.stringify(inferred));
1920
+ return;
1921
+ }
1922
+ res.end(JSON.stringify(routes.map(toDevRouteEntry)));
1923
+ }
1804
1924
  //#endregion
1805
1925
  //#region source/core/env-types.ts
1806
1926
  var GENERATED_FILE_NAME = "clarify-env.d.ts";
@@ -1860,6 +1980,84 @@ function resolveBuildOptions(options = {}) {
1860
1980
  };
1861
1981
  }
1862
1982
  //#endregion
1983
+ //#region source/core/user-config.ts
1984
+ function defineConfig(config) {
1985
+ validateProjectConfig(config);
1986
+ return config;
1987
+ }
1988
+ /**
1989
+ * Config filenames Clarify CLI looks for at the project root, in resolution
1990
+ * order. Exported so other modules (e.g. the dev `project-info` endpoint) can
1991
+ * surface the same list to tooling without duplicating it.
1992
+ */
1993
+ var CONFIG_FILENAMES = [
1994
+ "clarify.ts",
1995
+ "clarify.js",
1996
+ "clarify.json"
1997
+ ];
1998
+ function findClarifyConfigFile(root) {
1999
+ for (const filename of CONFIG_FILENAMES) {
2000
+ const filePath = (0, node_path.resolve)(root, filename);
2001
+ if ((0, node_fs.existsSync)(filePath)) return filePath;
2002
+ }
2003
+ }
2004
+ function assertConfigObject(config, configFile) {
2005
+ if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
2006
+ return config;
2007
+ }
2008
+ function loadJsonConfig(configFile) {
2009
+ const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
2010
+ const projectConfig = validateProjectConfig(config);
2011
+ const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
2012
+ return {
2013
+ ...projectConfig,
2014
+ ...ssg ? { ssg } : {}
2015
+ };
2016
+ }
2017
+ async function loadClarifyConfig(root, env) {
2018
+ const configFile = findClarifyConfigFile(root);
2019
+ if (!configFile) return {};
2020
+ if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
2021
+ return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
2022
+ }
2023
+ /**
2024
+ * File extensions Clarify treats as content files. Kept in sync with the
2025
+ * patterns used by route discovery in `parsers/routes.ts` and the OpenAPI
2026
+ * page generator. Exposed to tooling so the extension's "is this a content
2027
+ * file?" check stays accurate without duplicating the regex.
2028
+ */
2029
+ var CONTENT_FILE_EXTENSIONS = [
2030
+ ".md",
2031
+ ".mdx",
2032
+ ".openapi.json",
2033
+ ".openapi.yaml",
2034
+ ".openapi.yml"
2035
+ ];
2036
+ /**
2037
+ * Build the project-info response payload from the resolved plugin state.
2038
+ */
2039
+ function buildProjectInfo(projectRoot, contentRoot, projectConfig) {
2040
+ const response = {
2041
+ configFilenames: CONFIG_FILENAMES,
2042
+ contentFileExtensions: CONTENT_FILE_EXTENSIONS,
2043
+ contentRoot: (contentRoot.startsWith(projectRoot) ? contentRoot.slice(projectRoot.length).replace(/^[/\\]+/, "") : contentRoot) || "source",
2044
+ projectRoot
2045
+ };
2046
+ if (projectConfig.i18n) response.i18n = {
2047
+ defaultLocale: projectConfig.i18n.defaultLocale,
2048
+ locales: projectConfig.i18n.locales.map((l) => l.code)
2049
+ };
2050
+ return response;
2051
+ }
2052
+ /**
2053
+ * Handle a request to the project-info endpoint. Responds to any method
2054
+ * (GET is typical) with the JSON payload — there is no request body to parse.
2055
+ */
2056
+ function handleProjectInfoRequest(_req, res, projectRoot, contentRoot, projectConfig) {
2057
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2058
+ res.end(JSON.stringify(buildProjectInfo(projectRoot, contentRoot, projectConfig)));
2059
+ }
2060
+ //#endregion
1863
2061
  //#region source/core/site.ts
1864
2062
  async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
1865
2063
  return (await runHooks(plugins, "routes:discover", {
@@ -2133,42 +2331,6 @@ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundle
2133
2331
  }
2134
2332
  }
2135
2333
  //#endregion
2136
- //#region source/core/user-config.ts
2137
- function defineConfig(config) {
2138
- validateProjectConfig(config);
2139
- return config;
2140
- }
2141
- var CONFIG_FILENAMES = [
2142
- "clarify.ts",
2143
- "clarify.js",
2144
- "clarify.json"
2145
- ];
2146
- function findClarifyConfigFile(root) {
2147
- for (const filename of CONFIG_FILENAMES) {
2148
- const filePath = (0, node_path.resolve)(root, filename);
2149
- if ((0, node_fs.existsSync)(filePath)) return filePath;
2150
- }
2151
- }
2152
- function assertConfigObject(config, configFile) {
2153
- if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
2154
- return config;
2155
- }
2156
- function loadJsonConfig(configFile) {
2157
- const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
2158
- const projectConfig = validateProjectConfig(config);
2159
- const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
2160
- return {
2161
- ...projectConfig,
2162
- ...ssg ? { ssg } : {}
2163
- };
2164
- }
2165
- async function loadClarifyConfig(root, env) {
2166
- const configFile = findClarifyConfigFile(root);
2167
- if (!configFile) return {};
2168
- if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
2169
- return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
2170
- }
2171
- //#endregion
2172
2334
  //#region source/core/plugin.ts
2173
2335
  function loadVirtualModule(id, modules) {
2174
2336
  const bareId = stripVirtualPrefix(id);
@@ -2316,6 +2478,11 @@ function clarifyPlugin(options = {}) {
2316
2478
  async configureServer(server) {
2317
2479
  server.watcher.add(contentRoot);
2318
2480
  if (configFilePath) server.watcher.add(configFilePath);
2481
+ server.middlewares.use((req, res, next) => {
2482
+ if (req.url === "/dev/query-preview-route" && req.method === "POST") return handleDevRouteRequest(req, res, routes, projectConfig, contentRoot);
2483
+ if (req.url === "/dev/project-info" && (req.method === "GET" || req.method === "HEAD")) return handleProjectInfoRequest(req, res, root, contentRoot, projectConfig);
2484
+ next();
2485
+ });
2319
2486
  const handleContentTreeChange = async (filePath) => {
2320
2487
  const relativeContentFile = (0, node_path.relative)(contentRoot, (0, node_path.isAbsolute)(filePath) ? filePath : (0, node_path.join)(root, filePath));
2321
2488
  if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile))) return;
@@ -2434,4 +2601,4 @@ Object.defineProperty(exports, "resolveClarifySite", {
2434
2601
  }
2435
2602
  });
2436
2603
 
2437
- //# sourceMappingURL=plugin-DyceHdXd.cjs.map
2604
+ //# sourceMappingURL=plugin-JvxU6ZjU.cjs.map