radiant-docs-validator 0.1.9 → 0.1.11
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/index.d.ts +7 -1
- package/dist/index.js +81 -18
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -205,8 +205,14 @@ type RadiantComponentValidationOptions = {
|
|
|
205
205
|
sourceFile: string;
|
|
206
206
|
validateLinkHref?: (href: string) => void;
|
|
207
207
|
validateAssetHref?: (href: string) => void;
|
|
208
|
+
validateIconHref?: (href: string, context: IconValidationContext) => void;
|
|
209
|
+
};
|
|
210
|
+
type IconValidationContext = {
|
|
211
|
+
componentName: string;
|
|
212
|
+
propName: string;
|
|
213
|
+
sourceFile: string;
|
|
208
214
|
};
|
|
209
215
|
declare function validateRadiantComponentProps(componentName: string, props: Record<string, unknown>, options: RadiantComponentValidationOptions): void;
|
|
210
216
|
declare function validateRadiantComponentNode(node: MdxJsxElementNode, options: RadiantComponentValidationOptions): void;
|
|
211
217
|
|
|
212
|
-
export { type AssistantButtonConfig, type AssistantButtonSize, type AssistantConfig, type AssistantIcon, type AssistantNavbarButtonConfig, BASE_COLOR_OPTIONS, type BaseColorByMode, type BaseColorOption, type CardButtonTheme, type CardCoverTheme, type CardTheme, type CodeSyntaxThemeConfig, type CodeTheme, DEFAULT_THEME_COLOR_DARK, DEFAULT_THEME_COLOR_LIGHT, type DocsConfig, type DocsHrefResolution, type DocsTheme, type DocsValidatorOptions, type Footer, type FooterLink, type HiddenPageRoute, type Logo, type LogoVariant, type NavGroup, type NavMenu, type NavMenuItem, type NavOpenApi, type NavOpenApiPage, type NavOpenApiPageRef, type NavPage, type NavTag, type NavbarItem, type NavigationItem, PUBLISHABLE_STATIC_ASSET_EXTENSIONS, type RadiantComponentValidationOptions, type SocialPlatform, type TagTheme, type ThemeColorByMode, configureDocsValidator, getConfig, isPublishableStaticAssetPath, loadOpenApiSpec, resolveDocsHref, resolveDocsPageHref, validateMdxContent, validateRadiantComponentNode, validateRadiantComponentProps };
|
|
218
|
+
export { type AssistantButtonConfig, type AssistantButtonSize, type AssistantConfig, type AssistantIcon, type AssistantNavbarButtonConfig, BASE_COLOR_OPTIONS, type BaseColorByMode, type BaseColorOption, type CardButtonTheme, type CardCoverTheme, type CardTheme, type CodeSyntaxThemeConfig, type CodeTheme, DEFAULT_THEME_COLOR_DARK, DEFAULT_THEME_COLOR_LIGHT, type DocsConfig, type DocsHrefResolution, type DocsTheme, type DocsValidatorOptions, type Footer, type FooterLink, type HiddenPageRoute, type IconValidationContext, type Logo, type LogoVariant, type NavGroup, type NavMenu, type NavMenuItem, type NavOpenApi, type NavOpenApiPage, type NavOpenApiPageRef, type NavPage, type NavTag, type NavbarItem, type NavigationItem, PUBLISHABLE_STATIC_ASSET_EXTENSIONS, type RadiantComponentValidationOptions, type SocialPlatform, type TagTheme, type ThemeColorByMode, configureDocsValidator, getConfig, isPublishableStaticAssetPath, loadOpenApiSpec, resolveDocsHref, resolveDocsPageHref, validateMdxContent, validateRadiantComponentNode, validateRadiantComponentProps };
|
package/dist/index.js
CHANGED
|
@@ -239,6 +239,21 @@ function assertHexColor(componentName, propName, value, sourceFile) {
|
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
+
function validateIconProp(componentName, propName, value, options) {
|
|
243
|
+
if (typeof value !== "string") return;
|
|
244
|
+
if (value.trim().length === 0) {
|
|
245
|
+
componentError(
|
|
246
|
+
componentName,
|
|
247
|
+
`Invalid prop "${propName}": expected an Iconify name like "lucide:settings", an HTTP(S) URL, or a docs-root absolute local path like "/icons/settings.svg"`,
|
|
248
|
+
options.sourceFile
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
options.validateIconHref?.(value, {
|
|
252
|
+
componentName,
|
|
253
|
+
propName,
|
|
254
|
+
sourceFile: options.sourceFile
|
|
255
|
+
});
|
|
256
|
+
}
|
|
242
257
|
function validateCard(props, options) {
|
|
243
258
|
const componentName = "Card";
|
|
244
259
|
assertNoUnknownProps(
|
|
@@ -251,6 +266,7 @@ function validateCard(props, options) {
|
|
|
251
266
|
assertType(componentName, "title", props.title, ["string"], options.sourceFile);
|
|
252
267
|
assertType(componentName, "href", props.href, ["string"], options.sourceFile);
|
|
253
268
|
assertType(componentName, "icon", props.icon, ["string"], options.sourceFile);
|
|
269
|
+
validateIconProp(componentName, "icon", props.icon, options);
|
|
254
270
|
if (typeof props.href === "string") {
|
|
255
271
|
options.validateLinkHref?.(props.href);
|
|
256
272
|
}
|
|
@@ -268,6 +284,7 @@ function validateCard(props, options) {
|
|
|
268
284
|
assertType("Card.cover", "colors", props.cover.colors, ["array"], options.sourceFile);
|
|
269
285
|
assertType("Card.cover", "patternSeed", props.cover.patternSeed, ["string"], options.sourceFile);
|
|
270
286
|
assertType("Card.cover", "colorSeed", props.cover.colorSeed, ["string"], options.sourceFile);
|
|
287
|
+
validateIconProp("Card.cover", "icon", props.cover.icon, options);
|
|
271
288
|
if (Array.isArray(props.cover.colors)) {
|
|
272
289
|
if (props.cover.colors.length < 1 || props.cover.colors.length > 4) {
|
|
273
290
|
componentError(
|
|
@@ -408,12 +425,14 @@ function validateRadiantComponentProps(componentName, props, options) {
|
|
|
408
425
|
assertRequired("Tab", "label", props.label, options.sourceFile);
|
|
409
426
|
assertType("Tab", "label", props.label, ["string"], options.sourceFile);
|
|
410
427
|
assertType("Tab", "icon", props.icon, ["string"], options.sourceFile);
|
|
428
|
+
validateIconProp("Tab", "icon", props.icon, options);
|
|
411
429
|
return;
|
|
412
430
|
}
|
|
413
431
|
if (componentName === "Accordion") {
|
|
414
432
|
assertRequired("Accordion", "title", props.title, options.sourceFile);
|
|
415
433
|
assertType("Accordion", "title", props.title, ["string"], options.sourceFile);
|
|
416
434
|
assertType("Accordion", "icon", props.icon, ["string"], options.sourceFile);
|
|
435
|
+
validateIconProp("Accordion", "icon", props.icon, options);
|
|
417
436
|
assertType("Accordion", "defaultOpen", props.defaultOpen, ["boolean"], options.sourceFile);
|
|
418
437
|
assertEnum(
|
|
419
438
|
"Accordion",
|
|
@@ -434,6 +453,7 @@ function validateRadiantComponentProps(componentName, props, options) {
|
|
|
434
453
|
);
|
|
435
454
|
assertType("Callout", "title", props.title, ["string", "boolean"], options.sourceFile);
|
|
436
455
|
assertType("Callout", "icon", props.icon, ["string", "boolean"], options.sourceFile);
|
|
456
|
+
validateIconProp("Callout", "icon", props.icon, options);
|
|
437
457
|
assertType("Callout", "accent", props.accent, ["boolean"], options.sourceFile);
|
|
438
458
|
assertType("Callout", "color", props.color, ["string"], options.sourceFile);
|
|
439
459
|
if (props.title === true) {
|
|
@@ -725,6 +745,22 @@ function validateIcon(icon, currentPath) {
|
|
|
725
745
|
);
|
|
726
746
|
}
|
|
727
747
|
}
|
|
748
|
+
function validateComponentIcon(icon, currentPath) {
|
|
749
|
+
const trimmedIcon = icon.trim();
|
|
750
|
+
if (trimmedIcon !== icon) {
|
|
751
|
+
throwConfigError(
|
|
752
|
+
"Component icon cannot include leading or trailing whitespace.",
|
|
753
|
+
currentPath
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
if (!isUrl(icon) && !icon.startsWith("/") && !icon.includes(":")) {
|
|
757
|
+
throwConfigError(
|
|
758
|
+
`Invalid component icon "${icon}". Component icons must use an Iconify name like "lucide:settings", an HTTP(S) URL, or a docs-root absolute local path like "/icons/settings.svg". Bare icon names are ambiguous.`,
|
|
759
|
+
currentPath
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
validateIcon(icon, currentPath);
|
|
763
|
+
}
|
|
728
764
|
var AVAILABLE_COMPONENTS = [
|
|
729
765
|
"Callout",
|
|
730
766
|
"Tabs",
|
|
@@ -1309,39 +1345,59 @@ async function validateNavigationNode(item, currentPath, groupDepth = 0) {
|
|
|
1309
1345
|
return;
|
|
1310
1346
|
}
|
|
1311
1347
|
}
|
|
1312
|
-
function
|
|
1348
|
+
function getFirstRouteFromPageItems(items) {
|
|
1313
1349
|
for (const item of items) {
|
|
1314
1350
|
if (typeof item === "string") {
|
|
1315
|
-
return
|
|
1351
|
+
return {
|
|
1352
|
+
type: "mdx",
|
|
1353
|
+
filePath: item
|
|
1354
|
+
};
|
|
1316
1355
|
}
|
|
1317
1356
|
if ("page" in item) {
|
|
1318
|
-
return
|
|
1357
|
+
return {
|
|
1358
|
+
type: "mdx",
|
|
1359
|
+
filePath: item.page
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
if ("openapi" in item) {
|
|
1363
|
+
return {
|
|
1364
|
+
type: "openapi"
|
|
1365
|
+
};
|
|
1319
1366
|
}
|
|
1320
1367
|
if ("group" in item) {
|
|
1321
|
-
const
|
|
1322
|
-
if (
|
|
1323
|
-
return
|
|
1368
|
+
const nestedRoute = getFirstRouteFromPageItems(item.pages);
|
|
1369
|
+
if (nestedRoute) {
|
|
1370
|
+
return nestedRoute;
|
|
1324
1371
|
}
|
|
1325
1372
|
}
|
|
1326
1373
|
}
|
|
1327
1374
|
return void 0;
|
|
1328
1375
|
}
|
|
1329
|
-
function
|
|
1376
|
+
function getFirstRouteFromNavigation(navigation) {
|
|
1330
1377
|
if (navigation.pages) {
|
|
1331
|
-
return
|
|
1378
|
+
return getFirstRouteFromPageItems(navigation.pages);
|
|
1332
1379
|
}
|
|
1333
1380
|
if (navigation.menu) {
|
|
1334
1381
|
for (const menuItem of navigation.menu.items) {
|
|
1335
1382
|
const submenuPages = menuItem.submenu.pages;
|
|
1336
|
-
if (
|
|
1337
|
-
|
|
1383
|
+
if (submenuPages) {
|
|
1384
|
+
const firstRoute = getFirstRouteFromPageItems(submenuPages);
|
|
1385
|
+
if (firstRoute) {
|
|
1386
|
+
return firstRoute;
|
|
1387
|
+
}
|
|
1338
1388
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1389
|
+
if (menuItem.submenu.openapi) {
|
|
1390
|
+
return {
|
|
1391
|
+
type: "openapi"
|
|
1392
|
+
};
|
|
1342
1393
|
}
|
|
1343
1394
|
}
|
|
1344
1395
|
}
|
|
1396
|
+
if (navigation.openapi) {
|
|
1397
|
+
return {
|
|
1398
|
+
type: "openapi"
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1345
1401
|
return void 0;
|
|
1346
1402
|
}
|
|
1347
1403
|
async function validateNavOpenApi(navOpenApi, currentPath) {
|
|
@@ -2526,12 +2582,12 @@ async function validateConfig(config) {
|
|
|
2526
2582
|
await validateNavigation(config.navigation);
|
|
2527
2583
|
config.home = validateHome(config.home);
|
|
2528
2584
|
if (config.home === void 0) {
|
|
2529
|
-
const
|
|
2530
|
-
if (
|
|
2531
|
-
config.home =
|
|
2532
|
-
} else if (!
|
|
2585
|
+
const fallbackRoute = getFirstRouteFromNavigation(config.navigation);
|
|
2586
|
+
if (fallbackRoute?.type === "mdx") {
|
|
2587
|
+
config.home = fallbackRoute.filePath;
|
|
2588
|
+
} else if (!fallbackRoute) {
|
|
2533
2589
|
throwConfigError(
|
|
2534
|
-
"Home is undefined and no
|
|
2590
|
+
"Home is undefined and no navigation route exists to use as fallback.",
|
|
2535
2591
|
["home"]
|
|
2536
2592
|
);
|
|
2537
2593
|
}
|
|
@@ -2913,6 +2969,13 @@ function createComponentValidationPlugin(args) {
|
|
|
2913
2969
|
linkIndex: args.linkIndex,
|
|
2914
2970
|
expectedTarget: "asset"
|
|
2915
2971
|
});
|
|
2972
|
+
},
|
|
2973
|
+
validateIconHref: (href, context) => {
|
|
2974
|
+
validateComponentIcon(href, [
|
|
2975
|
+
context.sourceFile,
|
|
2976
|
+
context.componentName,
|
|
2977
|
+
context.propName
|
|
2978
|
+
]);
|
|
2916
2979
|
}
|
|
2917
2980
|
});
|
|
2918
2981
|
});
|