uidex 0.8.0 → 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.
- package/dist/cli/cli.cjs +88 -13
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/scan/index.cjs +88 -13
- package/dist/scan/index.cjs.map +1 -1
- package/dist/scan/index.d.cts +31 -4
- package/dist/scan/index.d.ts +31 -4
- package/dist/scan/index.js +88 -13
- package/dist/scan/index.js.map +1 -1
- package/package.json +17 -17
package/dist/scan/index.d.cts
CHANGED
|
@@ -153,7 +153,18 @@ interface SourceConfig {
|
|
|
153
153
|
interface ConventionsConfig {
|
|
154
154
|
primitives?: string[] | false;
|
|
155
155
|
features?: string | false;
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* How Page entities are derived from route files.
|
|
158
|
+
* - `"auto"` (default): infer the route root from the `app`/`pages`/
|
|
159
|
+
* `routes` directory-name heuristic.
|
|
160
|
+
* - `{ routesDir }`: the route root is exactly this dir (relative to the
|
|
161
|
+
* config dir), detected with the TanStack / React Router `routes/`-style
|
|
162
|
+
* convention. Use for a custom TanStack `routesDirectory`.
|
|
163
|
+
* - `false`: do not derive pages from routes.
|
|
164
|
+
*/
|
|
165
|
+
pages?: "auto" | false | {
|
|
166
|
+
routesDir: string;
|
|
167
|
+
};
|
|
157
168
|
flows?: string[] | false;
|
|
158
169
|
regions?: "landmarks" | false;
|
|
159
170
|
}
|
|
@@ -611,10 +622,26 @@ declare function emit(opts: EmitOptions): EmitResult;
|
|
|
611
622
|
* Detect framework-aware routes:
|
|
612
623
|
* - Next.js App Router: `**\/app/**\/page.tsx` -> `/segments`
|
|
613
624
|
* - Next.js Pages Router: `**\/pages/**\/*.tsx` -> `/segments` (excludes _app, _document, api/)
|
|
614
|
-
* - Vite +
|
|
615
|
-
*
|
|
625
|
+
* - Vite + TanStack Router / React Router: `**\/routes/**\/*.tsx` -> `/segments`.
|
|
626
|
+
* Honors TanStack conventions: pathless layouts (`_layout`) and route
|
|
627
|
+
* groups (`(group)`) drop from the path, the folder route file
|
|
628
|
+
* (`route.tsx`) collapses like `index`, `-`-prefixed and `api/` files are
|
|
629
|
+
* excluded, and flat dot-notation filenames (`a.b.$c.tsx`) split into
|
|
630
|
+
* nested segments. Dynamic segments are normalized to the canonical
|
|
631
|
+
* `[param]` form used everywhere else in the pipeline: `$id` -> `[id]`,
|
|
632
|
+
* the splat `$` -> `[...splat]`, and `[.]`-escaped literals are unwrapped.
|
|
616
633
|
*/
|
|
617
|
-
|
|
634
|
+
interface DetectRoutesOptions {
|
|
635
|
+
/**
|
|
636
|
+
* Explicit routes root, as it appears in `displayPath` (relative to the
|
|
637
|
+
* config dir). When set, only files under this dir are routes and the
|
|
638
|
+
* TanStack / React Router `routes/`-style convention is applied — overriding
|
|
639
|
+
* the `app`/`pages`/`routes` directory-name heuristic. Use for a custom
|
|
640
|
+
* TanStack `routesDirectory`.
|
|
641
|
+
*/
|
|
642
|
+
routesDir?: string;
|
|
643
|
+
}
|
|
644
|
+
declare function detectRoutes(files: ScannedFile[], options?: DetectRoutesOptions): DetectedRoute[];
|
|
618
645
|
declare function pathToId(routePath: string): string;
|
|
619
646
|
|
|
620
647
|
interface GitResolveOptions {
|
package/dist/scan/index.d.ts
CHANGED
|
@@ -153,7 +153,18 @@ interface SourceConfig {
|
|
|
153
153
|
interface ConventionsConfig {
|
|
154
154
|
primitives?: string[] | false;
|
|
155
155
|
features?: string | false;
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* How Page entities are derived from route files.
|
|
158
|
+
* - `"auto"` (default): infer the route root from the `app`/`pages`/
|
|
159
|
+
* `routes` directory-name heuristic.
|
|
160
|
+
* - `{ routesDir }`: the route root is exactly this dir (relative to the
|
|
161
|
+
* config dir), detected with the TanStack / React Router `routes/`-style
|
|
162
|
+
* convention. Use for a custom TanStack `routesDirectory`.
|
|
163
|
+
* - `false`: do not derive pages from routes.
|
|
164
|
+
*/
|
|
165
|
+
pages?: "auto" | false | {
|
|
166
|
+
routesDir: string;
|
|
167
|
+
};
|
|
157
168
|
flows?: string[] | false;
|
|
158
169
|
regions?: "landmarks" | false;
|
|
159
170
|
}
|
|
@@ -611,10 +622,26 @@ declare function emit(opts: EmitOptions): EmitResult;
|
|
|
611
622
|
* Detect framework-aware routes:
|
|
612
623
|
* - Next.js App Router: `**\/app/**\/page.tsx` -> `/segments`
|
|
613
624
|
* - Next.js Pages Router: `**\/pages/**\/*.tsx` -> `/segments` (excludes _app, _document, api/)
|
|
614
|
-
* - Vite +
|
|
615
|
-
*
|
|
625
|
+
* - Vite + TanStack Router / React Router: `**\/routes/**\/*.tsx` -> `/segments`.
|
|
626
|
+
* Honors TanStack conventions: pathless layouts (`_layout`) and route
|
|
627
|
+
* groups (`(group)`) drop from the path, the folder route file
|
|
628
|
+
* (`route.tsx`) collapses like `index`, `-`-prefixed and `api/` files are
|
|
629
|
+
* excluded, and flat dot-notation filenames (`a.b.$c.tsx`) split into
|
|
630
|
+
* nested segments. Dynamic segments are normalized to the canonical
|
|
631
|
+
* `[param]` form used everywhere else in the pipeline: `$id` -> `[id]`,
|
|
632
|
+
* the splat `$` -> `[...splat]`, and `[.]`-escaped literals are unwrapped.
|
|
616
633
|
*/
|
|
617
|
-
|
|
634
|
+
interface DetectRoutesOptions {
|
|
635
|
+
/**
|
|
636
|
+
* Explicit routes root, as it appears in `displayPath` (relative to the
|
|
637
|
+
* config dir). When set, only files under this dir are routes and the
|
|
638
|
+
* TanStack / React Router `routes/`-style convention is applied — overriding
|
|
639
|
+
* the `app`/`pages`/`routes` directory-name heuristic. Use for a custom
|
|
640
|
+
* TanStack `routesDirectory`.
|
|
641
|
+
*/
|
|
642
|
+
routesDir?: string;
|
|
643
|
+
}
|
|
644
|
+
declare function detectRoutes(files: ScannedFile[], options?: DetectRoutesOptions): DetectedRoute[];
|
|
618
645
|
declare function pathToId(routePath: string): string;
|
|
619
646
|
|
|
620
647
|
interface GitResolveOptions {
|
package/dist/scan/index.js
CHANGED
|
@@ -135,7 +135,13 @@ function validateConfig(raw) {
|
|
|
135
135
|
fail(`conventions.features must be a string or false`);
|
|
136
136
|
}
|
|
137
137
|
if (c.pages !== void 0 && c.pages !== false && c.pages !== "auto") {
|
|
138
|
-
|
|
138
|
+
const p2 = c.pages;
|
|
139
|
+
const routesDir = typeof p2 === "object" && p2 !== null && !Array.isArray(p2) ? p2.routesDir : void 0;
|
|
140
|
+
if (typeof p2 !== "object" || p2 === null || Array.isArray(p2) || Object.keys(p2).some((k) => k !== "routesDir") || typeof routesDir !== "string" || routesDir.length === 0) {
|
|
141
|
+
fail(
|
|
142
|
+
`conventions.pages must be "auto", false, or { routesDir: string }`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
139
145
|
}
|
|
140
146
|
if (c.flows !== void 0 && c.flows !== false) {
|
|
141
147
|
assertStringArray(c.flows, "conventions.flows");
|
|
@@ -1820,13 +1826,26 @@ import * as path4 from "path";
|
|
|
1820
1826
|
var PAGE_BASENAME = /^page\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
1821
1827
|
var PAGES_ROUTER_BASENAME = /\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
1822
1828
|
var ROUTE_BASENAME = /^route\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
1823
|
-
function detectRoutes(files) {
|
|
1829
|
+
function detectRoutes(files, options = {}) {
|
|
1824
1830
|
const out2 = [];
|
|
1825
1831
|
const seen = /* @__PURE__ */ new Set();
|
|
1832
|
+
const routesDir = options.routesDir ? options.routesDir.replace(/^\.?\/+/, "").replace(/\/+$/, "") : null;
|
|
1833
|
+
const routesDirParts = routesDir ? routesDir.split("/") : null;
|
|
1826
1834
|
for (const f of files) {
|
|
1827
1835
|
const rel = f.displayPath;
|
|
1828
1836
|
const parts = rel.split("/");
|
|
1829
1837
|
const base = parts[parts.length - 1];
|
|
1838
|
+
if (routesDirParts) {
|
|
1839
|
+
const end = dirEndIndex(parts, routesDirParts);
|
|
1840
|
+
if (end === -1 || end > parts.length - 1) continue;
|
|
1841
|
+
if (!PAGES_ROUTER_BASENAME.test(base)) continue;
|
|
1842
|
+
const routePath = routesStylePath(
|
|
1843
|
+
parts.slice(end, parts.length - 1),
|
|
1844
|
+
base
|
|
1845
|
+
);
|
|
1846
|
+
if (routePath !== null) push(out2, seen, routePath, f.displayPath);
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1830
1849
|
const appIdx = parts.indexOf("app");
|
|
1831
1850
|
if (appIdx !== -1 && PAGE_BASENAME.test(base)) {
|
|
1832
1851
|
const routeSegments = parts.slice(appIdx + 1, parts.length - 1);
|
|
@@ -1853,15 +1872,11 @@ function detectRoutes(files) {
|
|
|
1853
1872
|
}
|
|
1854
1873
|
const routesIdx = parts.indexOf("routes");
|
|
1855
1874
|
if (routesIdx !== -1 && PAGES_ROUTER_BASENAME.test(base)) {
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
base.replace(/\.[^.]+$/, "")
|
|
1862
|
-
].filter((s) => s !== "index" && s !== "__root");
|
|
1863
|
-
const routePath = formatNextAppPath(normalized);
|
|
1864
|
-
push(out2, seen, routePath, f.displayPath);
|
|
1875
|
+
const routePath = routesStylePath(
|
|
1876
|
+
parts.slice(routesIdx + 1, parts.length - 1),
|
|
1877
|
+
base
|
|
1878
|
+
);
|
|
1879
|
+
if (routePath !== null) push(out2, seen, routePath, f.displayPath);
|
|
1865
1880
|
continue;
|
|
1866
1881
|
}
|
|
1867
1882
|
}
|
|
@@ -1877,6 +1892,63 @@ function formatNextAppPath(segments) {
|
|
|
1877
1892
|
if (kept.length === 0) return "/";
|
|
1878
1893
|
return "/" + kept.join("/");
|
|
1879
1894
|
}
|
|
1895
|
+
function dirEndIndex(parts, dirParts) {
|
|
1896
|
+
if (dirParts.length === 0) return 0;
|
|
1897
|
+
for (let i = 0; i + dirParts.length <= parts.length; i++) {
|
|
1898
|
+
let match = true;
|
|
1899
|
+
for (let j = 0; j < dirParts.length; j++) {
|
|
1900
|
+
if (parts[i + j] !== dirParts[j]) {
|
|
1901
|
+
match = false;
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
if (match) return i + dirParts.length;
|
|
1906
|
+
}
|
|
1907
|
+
return -1;
|
|
1908
|
+
}
|
|
1909
|
+
function routesStylePath(dirSegs, base) {
|
|
1910
|
+
const fileSegs = splitFlatSegments(base.replace(/\.[^.]+$/, ""));
|
|
1911
|
+
const leaf = fileSegs[fileSegs.length - 1];
|
|
1912
|
+
if (leaf.startsWith("_")) return null;
|
|
1913
|
+
const rawSegs = [...dirSegs, ...fileSegs];
|
|
1914
|
+
if (rawSegs.some((s) => s.startsWith("-"))) return null;
|
|
1915
|
+
if (rawSegs[0] === "api") return null;
|
|
1916
|
+
return formatTanStackPath(rawSegs);
|
|
1917
|
+
}
|
|
1918
|
+
function splitFlatSegments(name) {
|
|
1919
|
+
const segs = [];
|
|
1920
|
+
let cur = "";
|
|
1921
|
+
let depth = 0;
|
|
1922
|
+
for (const ch of name) {
|
|
1923
|
+
if (ch === "[") depth++;
|
|
1924
|
+
else if (ch === "]") depth = Math.max(0, depth - 1);
|
|
1925
|
+
if (ch === "." && depth === 0) {
|
|
1926
|
+
segs.push(cur);
|
|
1927
|
+
cur = "";
|
|
1928
|
+
continue;
|
|
1929
|
+
}
|
|
1930
|
+
cur += ch;
|
|
1931
|
+
}
|
|
1932
|
+
segs.push(cur);
|
|
1933
|
+
return segs;
|
|
1934
|
+
}
|
|
1935
|
+
function formatTanStackPath(segments) {
|
|
1936
|
+
const kept = [];
|
|
1937
|
+
for (const seg of segments) {
|
|
1938
|
+
if (!seg) continue;
|
|
1939
|
+
if (seg === "index" || seg === "route" || seg === "__root") continue;
|
|
1940
|
+
if (seg.startsWith("_")) continue;
|
|
1941
|
+
if (seg.startsWith("(") && seg.endsWith(")")) continue;
|
|
1942
|
+
kept.push(normalizeTanStackSegment(seg.replace(/_$/, "")));
|
|
1943
|
+
}
|
|
1944
|
+
if (kept.length === 0) return "/";
|
|
1945
|
+
return "/" + kept.join("/");
|
|
1946
|
+
}
|
|
1947
|
+
function normalizeTanStackSegment(seg) {
|
|
1948
|
+
if (seg === "$") return "[...splat]";
|
|
1949
|
+
if (seg.startsWith("$")) return `[${seg.slice(1)}]`;
|
|
1950
|
+
return seg.replace(/\[([^\]]*)\]/g, "$1");
|
|
1951
|
+
}
|
|
1880
1952
|
function pathToId(routePath) {
|
|
1881
1953
|
if (routePath === "/") return "root";
|
|
1882
1954
|
return routePath.replace(/^\/+/, "").replace(/\[\.{3}([^\]]+)\]/g, "$1").replace(/\[([^\]]+)\]/g, "$1").replace(/\//g, "-").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -1972,9 +2044,12 @@ function resolve2(ctx) {
|
|
|
1972
2044
|
function metaWithComposes(kind, id, base) {
|
|
1973
2045
|
const composes = directChildren.get(`${kind}:${id}`);
|
|
1974
2046
|
if (!composes || composes.length === 0) return base;
|
|
1975
|
-
return { ...base
|
|
2047
|
+
return { ...base, composes };
|
|
1976
2048
|
}
|
|
1977
|
-
const routes = conventions.pages ===
|
|
2049
|
+
const routes = conventions.pages === false ? [] : detectRoutes(
|
|
2050
|
+
ctx.extracted.map((e) => e.file),
|
|
2051
|
+
conventions.pages === "auto" ? {} : { routesDir: conventions.pages.routesDir }
|
|
2052
|
+
);
|
|
1978
2053
|
const handledPageFiles = /* @__PURE__ */ new Set();
|
|
1979
2054
|
for (const route of routes) {
|
|
1980
2055
|
const routeDir = path4.posix.dirname(route.file);
|