radiant-docs-validator 0.1.10 → 0.1.12

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 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",
@@ -2933,6 +2969,13 @@ function createComponentValidationPlugin(args) {
2933
2969
  linkIndex: args.linkIndex,
2934
2970
  expectedTarget: "asset"
2935
2971
  });
2972
+ },
2973
+ validateIconHref: (href, context) => {
2974
+ validateComponentIcon(href, [
2975
+ context.sourceFile,
2976
+ context.componentName,
2977
+ context.propName
2978
+ ]);
2936
2979
  }
2937
2980
  });
2938
2981
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "radiant-docs-validator",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Shared validation for Radiant documentation repositories",
5
5
  "type": "module",
6
6
  "scripts": {