veryfront 0.1.82 → 0.1.84
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/esm/cli/commands/knowledge/command.d.ts +10 -13
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command.js +294 -110
- package/esm/cli/commands/knowledge/parser-source.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/parser-source.js +52 -0
- package/esm/cli/commands/knowledge/result.d.ts +54 -0
- package/esm/cli/commands/knowledge/result.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/result.js +22 -0
- package/esm/cli/commands/knowledge/source-policy.d.ts +11 -0
- package/esm/cli/commands/knowledge/source-policy.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/source-policy.js +135 -0
- package/esm/deno.js +1 -1
- package/esm/src/jobs/index.d.ts +1 -1
- package/esm/src/jobs/index.d.ts.map +1 -1
- package/esm/src/jobs/index.js +1 -1
- package/esm/src/jobs/schemas.d.ts +1104 -185
- package/esm/src/jobs/schemas.d.ts.map +1 -1
- package/esm/src/jobs/schemas.js +81 -8
- package/esm/src/platform/adapters/base.d.ts +4 -1
- package/esm/src/platform/adapters/base.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/github/adapter.d.ts +2 -1
- package/esm/src/platform/adapters/fs/github/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/github/adapter.js +2 -2
- package/esm/src/platform/adapters/fs/github/stat-operations.d.ts +2 -1
- package/esm/src/platform/adapters/fs/github/stat-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/github/stat-operations.js +2 -2
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +2 -2
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +17 -2
- package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.d.ts +2 -2
- package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.js +2 -2
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts +1 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/stat-operations.d.ts +6 -2
- package/esm/src/platform/adapters/fs/veryfront/stat-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/stat-operations.js +131 -21
- package/esm/src/platform/adapters/fs/veryfront/types.d.ts +2 -1
- package/esm/src/platform/adapters/fs/veryfront/types.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/wrapper.d.ts +2 -2
- package/esm/src/platform/adapters/fs/wrapper.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/wrapper.js +2 -2
- package/esm/src/proxy/error-response.d.ts +7 -0
- package/esm/src/proxy/error-response.d.ts.map +1 -0
- package/esm/src/proxy/error-response.js +26 -0
- package/esm/src/proxy/handler.d.ts.map +1 -1
- package/esm/src/proxy/handler.js +25 -0
- package/esm/src/proxy/main.js +2 -23
- package/esm/src/rendering/app-route-resolver.js +0 -9
- package/esm/src/rendering/orchestrator/file-resolver/index.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/file-resolver/index.js +17 -0
- package/esm/src/rendering/page-resolution/page-resolver.d.ts.map +1 -1
- package/esm/src/rendering/page-resolution/page-resolver.js +19 -6
- package/esm/src/rendering/router-detection.d.ts +1 -0
- package/esm/src/rendering/router-detection.d.ts.map +1 -1
- package/esm/src/rendering/router-detection.js +3 -0
- package/esm/src/transforms/esm/http-cache-helpers.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache-helpers.js +7 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +11 -8
- package/esm/src/transforms/shared/package-specifier.d.ts +7 -0
- package/esm/src/transforms/shared/package-specifier.d.ts.map +1 -0
- package/esm/src/transforms/shared/package-specifier.js +19 -0
- package/esm/src/types/entities/getEntityInfo.d.ts.map +1 -1
- package/esm/src/types/entities/getEntityInfo.js +59 -42
- package/package.json +1 -1
- package/src/cli/commands/knowledge/command.ts +375 -139
- package/src/cli/commands/knowledge/parser-source.ts +52 -0
- package/src/cli/commands/knowledge/result.ts +88 -0
- package/src/cli/commands/knowledge/source-policy.ts +164 -0
- package/src/deno.js +1 -1
- package/src/src/jobs/index.ts +16 -0
- package/src/src/jobs/schemas.ts +105 -8
- package/src/src/platform/adapters/base.ts +5 -1
- package/src/src/platform/adapters/fs/github/adapter.ts +3 -2
- package/src/src/platform/adapters/fs/github/stat-operations.ts +3 -2
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +24 -3
- package/src/src/platform/adapters/fs/veryfront/multi-project-adapter.ts +6 -3
- package/src/src/platform/adapters/fs/veryfront/read-operations.ts +1 -0
- package/src/src/platform/adapters/fs/veryfront/stat-operations.ts +161 -25
- package/src/src/platform/adapters/fs/veryfront/types.ts +2 -1
- package/src/src/platform/adapters/fs/wrapper.ts +10 -3
- package/src/src/proxy/error-response.ts +33 -0
- package/src/src/proxy/handler.ts +43 -0
- package/src/src/proxy/main.ts +2 -27
- package/src/src/rendering/app-route-resolver.ts +0 -8
- package/src/src/rendering/orchestrator/file-resolver/index.ts +19 -0
- package/src/src/rendering/page-resolution/page-resolver.ts +26 -17
- package/src/src/rendering/router-detection.ts +7 -0
- package/src/src/transforms/esm/http-cache-helpers.ts +12 -1
- package/src/src/transforms/import-rewriter/strategies/bare-strategy.ts +11 -12
- package/src/src/transforms/shared/package-specifier.ts +29 -0
- package/src/src/types/entities/getEntityInfo.ts +78 -59
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { rendererLogger } from "../../../utils/index.js";
|
|
8
8
|
import { buildEsmShUrl, TAILWIND_VERSION } from "../url-builder.js";
|
|
9
|
+
import { parseBarePackageSpecifier } from "../../shared/package-specifier.js";
|
|
9
10
|
const logger = rendererLogger.component("esm");
|
|
10
11
|
const unversionedImportsWarned = new Set();
|
|
11
12
|
function hasVersionSpecifier(specifier) {
|
|
12
13
|
return /@[\d^~x][\d.x^~-]*(?=\/|$)/.test(specifier);
|
|
13
14
|
}
|
|
14
|
-
function normalizeVersionedSpecifier(specifier) {
|
|
15
|
-
return specifier.replace(/@[\d^~x][\d.x^~-]*(?=\/|$)/, "");
|
|
16
|
-
}
|
|
17
15
|
function warnUnversionedImport(specifier, projectId) {
|
|
18
16
|
const key = `${projectId}:${specifier}`;
|
|
19
17
|
if (unversionedImportsWarned.has(key))
|
|
@@ -53,15 +51,20 @@ export class BareStrategy {
|
|
|
53
51
|
rewrite(info, ctx) {
|
|
54
52
|
if (ctx.target === "ssr")
|
|
55
53
|
return { specifier: null };
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
const parsed = parseBarePackageSpecifier(info.specifier);
|
|
55
|
+
if (parsed == null) {
|
|
56
|
+
return { specifier: null };
|
|
57
|
+
}
|
|
58
|
+
const packageName = parsed.packageName;
|
|
59
|
+
let version = parsed.version ?? undefined;
|
|
60
|
+
const subpath = parsed.subpath ?? undefined;
|
|
61
|
+
if (packageName === "tailwindcss") {
|
|
62
|
+
version = TAILWIND_VERSION;
|
|
60
63
|
}
|
|
61
64
|
else if (!hasVersionSpecifier(info.specifier)) {
|
|
62
65
|
warnUnversionedImport(info.specifier, ctx.projectId);
|
|
63
66
|
}
|
|
64
|
-
const url = buildEsmShUrl(
|
|
67
|
+
const url = buildEsmShUrl(packageName, version, subpath, {
|
|
65
68
|
external: ["react"],
|
|
66
69
|
});
|
|
67
70
|
return { specifier: url };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ParsedBarePackageSpecifier {
|
|
2
|
+
packageName: string;
|
|
3
|
+
version: string | null;
|
|
4
|
+
subpath: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseBarePackageSpecifier(specifier: string): ParsedBarePackageSpecifier | null;
|
|
7
|
+
//# sourceMappingURL=package-specifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-specifier.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/shared/package-specifier.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,GAChB,0BAA0B,GAAG,IAAI,CAoBnC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function parseBarePackageSpecifier(specifier) {
|
|
2
|
+
const scopedMatch = specifier.match(/^(@[^/]+\/[^/@]+)(?:@([^/]+))?(\/.*)?$/);
|
|
3
|
+
if (scopedMatch?.[1]) {
|
|
4
|
+
return {
|
|
5
|
+
packageName: scopedMatch[1],
|
|
6
|
+
version: scopedMatch[2] ?? null,
|
|
7
|
+
subpath: scopedMatch[3] ?? null,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const unscopedMatch = specifier.match(/^([^/@][^/@]*?)(?:@([^/]+))?(\/.*)?$/);
|
|
11
|
+
if (unscopedMatch?.[1]) {
|
|
12
|
+
return {
|
|
13
|
+
packageName: unscopedMatch[1],
|
|
14
|
+
version: unscopedMatch[2] ?? null,
|
|
15
|
+
subpath: unscopedMatch[3] ?? null,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getEntityInfo.d.ts","sourceRoot":"","sources":["../../../../src/src/types/entities/getEntityInfo.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAe,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAWtE,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"getEntityInfo.d.ts","sourceRoot":"","sources":["../../../../src/src/types/entities/getEntityInfo.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAe,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAWtE,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmJ5B;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAwM5B;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAyE5B"}
|
|
@@ -29,37 +29,42 @@ export async function getEntityInfo(filePath, adapter) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
|
+
const shouldReadDirectly = adapter
|
|
33
|
+
? isExtendedFSAdapter(adapter.fs) && adapter.fs.isVeryfrontAdapter()
|
|
34
|
+
: false;
|
|
35
|
+
let content;
|
|
32
36
|
if (adapter) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
if (!shouldReadDirectly) {
|
|
38
|
+
try {
|
|
39
|
+
const stat = await withFallback(() => adapter.fs.stat(normalizedPath), async () => {
|
|
40
|
+
const exists = await fs.exists(filePath);
|
|
41
|
+
if (!exists) {
|
|
42
|
+
throw toError(createError({
|
|
43
|
+
type: "file",
|
|
44
|
+
message: "File not found",
|
|
45
|
+
context: { path: filePath, operation: "read" },
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
return await fs.stat(filePath);
|
|
49
|
+
}, { operationName: "stat:getEntityInfo", logError: false });
|
|
50
|
+
if (!stat.isFile)
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
entityInfoScope.runSync(() => {
|
|
55
|
+
throw error;
|
|
56
|
+
}, { path: filePath, details: { reason: "stat-failed" } }, undefined);
|
|
46
57
|
return null;
|
|
58
|
+
}
|
|
47
59
|
}
|
|
48
|
-
|
|
49
|
-
entityInfoScope.runSync(() => {
|
|
50
|
-
throw error;
|
|
51
|
-
}, { path: filePath, details: { reason: "stat-failed" } }, undefined);
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
60
|
+
content = await withFallback(() => adapter.fs.readFile(normalizedPath), () => fs.readTextFile(filePath), { operationName: "readFile:getEntityInfo", logError: false });
|
|
54
61
|
}
|
|
55
62
|
else {
|
|
56
63
|
const exists = await fs.exists(filePath);
|
|
57
64
|
if (!exists)
|
|
58
65
|
return null;
|
|
66
|
+
content = await fs.readTextFile(filePath);
|
|
59
67
|
}
|
|
60
|
-
const content = adapter
|
|
61
|
-
? await withFallback(() => adapter.fs.readFile(normalizedPath), () => fs.readTextFile(filePath), { operationName: "readFile:getEntityInfo", logError: false })
|
|
62
|
-
: await fs.readTextFile(filePath);
|
|
63
68
|
const ext = pathHelper.extname(filePath).toLowerCase();
|
|
64
69
|
let frontmatter = {};
|
|
65
70
|
let body = content;
|
|
@@ -120,23 +125,27 @@ export async function getEntityInfo(filePath, adapter) {
|
|
|
120
125
|
}
|
|
121
126
|
export async function getEntityBySlug(projectDir, slug, adapter) {
|
|
122
127
|
return await withSpan("types.getEntityBySlug", async () => {
|
|
123
|
-
const
|
|
128
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
129
|
+
const isVeryfrontRoute = normalizedSlug.startsWith(".veryfront/") ||
|
|
130
|
+
normalizedSlug === ".veryfront";
|
|
124
131
|
const resolveFile = adapter?.fs.resolveFile;
|
|
125
132
|
logger.debug("START", {
|
|
126
133
|
slug,
|
|
134
|
+
normalizedSlug,
|
|
127
135
|
projectDir,
|
|
128
136
|
isVeryfrontRoute,
|
|
129
137
|
hasResolveFile: !!resolveFile,
|
|
130
138
|
});
|
|
131
139
|
if (resolveFile) {
|
|
132
|
-
const basePaths = [pathHelper.join(projectDir, "pages",
|
|
140
|
+
const basePaths = [pathHelper.join(projectDir, "pages", normalizedSlug)];
|
|
133
141
|
if (isVeryfrontRoute)
|
|
134
|
-
basePaths.unshift(pathHelper.join(projectDir,
|
|
135
|
-
if (
|
|
142
|
+
basePaths.unshift(pathHelper.join(projectDir, normalizedSlug));
|
|
143
|
+
if (normalizedSlug === "index" || normalizedSlug === "") {
|
|
136
144
|
basePaths.unshift(pathHelper.join(projectDir, "pages", "index"));
|
|
137
145
|
}
|
|
138
146
|
logger.debug("Checking paths (resolveFile branch)", {
|
|
139
147
|
slug,
|
|
148
|
+
normalizedSlug,
|
|
140
149
|
basePaths,
|
|
141
150
|
});
|
|
142
151
|
const pathResults = await parallelMap(basePaths, async (basePath) => {
|
|
@@ -153,12 +162,13 @@ export async function getEntityBySlug(projectDir, slug, adapter) {
|
|
|
153
162
|
if (info?.entity.isPage) {
|
|
154
163
|
logger.debug("Found page via resolveFile", {
|
|
155
164
|
slug,
|
|
165
|
+
normalizedSlug,
|
|
156
166
|
path: info.entity.path,
|
|
157
167
|
});
|
|
158
168
|
return info;
|
|
159
169
|
}
|
|
160
170
|
}
|
|
161
|
-
const slugParts =
|
|
171
|
+
const slugParts = normalizedSlug === "" ? [] : normalizedSlug.split("/");
|
|
162
172
|
for (let depth = slugParts.length - 1; depth >= 0; depth--) {
|
|
163
173
|
const parentPath = slugParts.slice(0, depth).join("/");
|
|
164
174
|
const pagesDir = parentPath
|
|
@@ -194,25 +204,25 @@ export async function getEntityBySlug(projectDir, slug, adapter) {
|
|
|
194
204
|
/* expected: directory may not exist or readDir may fail */
|
|
195
205
|
}
|
|
196
206
|
}
|
|
197
|
-
logger.debug("No page found via resolveFile branch", { slug });
|
|
207
|
+
logger.debug("No page found via resolveFile branch", { slug, normalizedSlug });
|
|
198
208
|
return null;
|
|
199
209
|
}
|
|
200
210
|
const possiblePaths = [
|
|
201
|
-
pathHelper.join(projectDir, "pages", `${
|
|
202
|
-
pathHelper.join(projectDir, "pages", `${
|
|
203
|
-
pathHelper.join(projectDir, "pages", `${
|
|
204
|
-
pathHelper.join(projectDir, "pages", `${
|
|
205
|
-
pathHelper.join(projectDir, "pages", `${
|
|
206
|
-
pathHelper.join(projectDir, "pages", `${
|
|
207
|
-
pathHelper.join(projectDir, "pages", `${
|
|
208
|
-
pathHelper.join(projectDir, "pages", `${
|
|
209
|
-
pathHelper.join(projectDir, "pages", `${
|
|
210
|
-
pathHelper.join(projectDir, "pages", `${
|
|
211
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}.mdx`),
|
|
212
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}.md`),
|
|
213
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}.tsx`),
|
|
214
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}.jsx`),
|
|
215
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}.ts`),
|
|
216
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}/index.mdx`),
|
|
217
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}/index.md`),
|
|
218
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}/index.tsx`),
|
|
219
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}/index.jsx`),
|
|
220
|
+
pathHelper.join(projectDir, "pages", `${normalizedSlug}/index.ts`),
|
|
211
221
|
];
|
|
212
222
|
if (isVeryfrontRoute) {
|
|
213
|
-
possiblePaths.unshift(pathHelper.join(projectDir, `${
|
|
223
|
+
possiblePaths.unshift(pathHelper.join(projectDir, `${normalizedSlug}.mdx`), pathHelper.join(projectDir, `${normalizedSlug}.md`), pathHelper.join(projectDir, `${normalizedSlug}.tsx`), pathHelper.join(projectDir, `${normalizedSlug}.ts`));
|
|
214
224
|
}
|
|
215
|
-
if (
|
|
225
|
+
if (normalizedSlug === "index" || normalizedSlug === "") {
|
|
216
226
|
possiblePaths.unshift(pathHelper.join(projectDir, "pages", "index.mdx"), pathHelper.join(projectDir, "pages", "index.md"), pathHelper.join(projectDir, "pages", "index.tsx"), pathHelper.join(projectDir, "pages", "index.ts"));
|
|
217
227
|
}
|
|
218
228
|
const pathResults = await parallelMap(possiblePaths, async (p) => {
|
|
@@ -222,7 +232,7 @@ export async function getEntityBySlug(projectDir, slug, adapter) {
|
|
|
222
232
|
if (info?.entity.isPage)
|
|
223
233
|
return info;
|
|
224
234
|
}
|
|
225
|
-
const slugParts =
|
|
235
|
+
const slugParts = normalizedSlug === "" ? [] : normalizedSlug.split("/");
|
|
226
236
|
for (let depth = slugParts.length - 1; depth >= 0; depth--) {
|
|
227
237
|
const parentPath = slugParts.slice(0, depth).join("/");
|
|
228
238
|
const pagesDir = parentPath
|
|
@@ -267,7 +277,11 @@ export async function getEntityBySlug(projectDir, slug, adapter) {
|
|
|
267
277
|
}
|
|
268
278
|
}
|
|
269
279
|
return null;
|
|
270
|
-
}, {
|
|
280
|
+
}, {
|
|
281
|
+
"entity.slug": slug,
|
|
282
|
+
"entity.normalized_slug": normalizeSlug(slug),
|
|
283
|
+
"entity.projectDir": projectDir,
|
|
284
|
+
});
|
|
271
285
|
}
|
|
272
286
|
export async function getLayoutEntity(projectDir, layoutName, adapter) {
|
|
273
287
|
return await withSpan("types.getLayoutEntity", async () => {
|
|
@@ -345,3 +359,6 @@ function getSlugFromPath(filePath) {
|
|
|
345
359
|
const parentDir = parts[parts.length - 2];
|
|
346
360
|
return parentDir === "pages" ? "" : parentDir ?? "";
|
|
347
361
|
}
|
|
362
|
+
function normalizeSlug(slug) {
|
|
363
|
+
return slug === "/" ? "" : slug.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
364
|
+
}
|