CEM Validator
This tool validates your Custom Elements Manifest to ensure your web components are properly documented and configured for optimal compatibility with IDEs, documentation tools, and component catalogs.
What Does It Validate?
The validator checks two main areas:
- Package.json Configuration - Ensures your package is properly configured with correct entry points, module types, and custom elements manifest references
- Custom Elements Manifest - Validates that your manifest has proper schema versions, module paths, type definitions, and exports for each component
Installation
To install the package, use the following command:
npm install -D @wc-toolkit/cem-validatorUsage
This package can be used in two ways:
- As a standalone script - Run validation programmatically in your build process
- As a CEM Analyzer plugin - Automatically validate during manifest generation
Script Usage
Run the validator as part of your build process or as a standalone validation step:
import { validateCem, type CemValidatorOptions } from "@wc-toolkit/cem-validator";import manifest from "./custom-elements.json" with { type: 'json' };
const options: CemValidatorOptions = { packageJsonPath: "./package.json", cemFileName: "custom-elements.json", logErrors: false, // Set to true to log errors instead of throwing an exception exclude: ["MyInternalComponent"], // Skip validation for specific components rules: { // Override default severity levels for validation packageJson: { packageType: "off", customElementsProperty: "error", }, manifest: { tagName: "error", exportTypes: "warning", } }};
try { validateCem(manifest, options); console.log("✅ Manifest validation passed!");} catch (error) { console.error("❌ Manifest validation failed:", error); process.exit(1);}CEM Analyzer Plugin
Integrate validation directly into your manifest generation workflow:
import { cemValidatorPlugin } from "@wc-toolkit/cem-validator";
export default { plugins: [ cemValidatorPlugin({ logErrors: true, // Log errors without stopping the build exclude: ["BaseComponent", "InternalMixin"], // Skip base classes rules: { // Override default severity levels for validation packageJson: { exports: "off", customElementsProperty: "error", }, manifest: { schemaVersion: "error", tagName: "error", modulePath: "error", } } }) ],};Then run the analyzer:
npx @custom-elements-manifest/analyzer analyzeConfiguration
Options
packageJsonPath
- Type:
string - Default:
"./package.json" - Description: Path to your package.json file. Used to validate package configuration.
{ packageJsonPath: "./package.json"}cemFileName
- Type:
string - Default:
"custom-elements.json" - Description: Name of your Custom Elements Manifest file. Used to verify the manifest is properly referenced in package.json.
{ cemFileName: "custom-elements.json"}logErrors
- Type:
boolean - Default:
false - Description: When
true, validation errors are logged to the console instead of throwing exceptions. Useful for CI/CD pipelines where you want to see all errors without stopping the process.
{ logErrors: true // Logs errors but doesn't throw}exclude
- Type:
string[] - Default:
[] - Description: Array of component class names to exclude from validation. Useful for base classes, mixins, or internal components that don’t need full validation.
{ exclude: [ "MyBaseElement", // Skip base classes "InternalMixin", // Skip mixins "DeprecatedComponent" // Skip deprecated components ]}Example: If you have a base component that other components extend:
export class MyBaseElement extends HTMLElement { // Base functionality}
// my-button.tsexport class MyButton extends MyBaseElement { // This will be validated}// Validation config{ exclude: ["MyBaseElement"] // Only validate MyButton, skip MyBaseElement}debug
- Type:
boolean - Default:
false - Description: Enables verbose logging during the validation process. Helpful for troubleshooting validation issues.
{ debug: true // Logs detailed validation steps}skip
- Type:
boolean - Default:
false - Description: Completely disables the validator. Useful for conditional execution in different environments.
{ skip: process.env.NODE_ENV === "development" // Skip validation in dev}rules
- Type:
Rules - Description: Configure which validations to run and their severity levels. Each rule can be set to
"off","warning", or"error".
Rules Configuration
Rules are organized into two categories: packageJson and manifest.
Package.json Rules
These rules validate your package.json configuration:
packageType
- Default:
"error" - Description: Ensures the
typefield is set to"module"for ESM packages.
{ rules: { packageJson: { packageType: "error" // Requires "type": "module" in package.json } }}main
- Default:
"error" - Description: Validates the
mainfield points to a valid entry point file.
{ rules: { packageJson: { main: "error" // Requires valid "main": "./dist/index.js" } }}module
- Default:
"error" - Description: Validates the
modulefield points to a valid ESM entry point.
{ rules: { packageJson: { module: "error" // Requires valid "module": "./dist/index.js" } }}types
- Default:
"error" - Description: Validates the
typesfield points to a valid TypeScript declaration file.
{ rules: { packageJson: { types: "error" // Requires valid "types": "./dist/index.d.ts" } }}exports
- Default:
"error" - Description: Validates the package has an
exportsfield properly configured for modern package resolution.
{ rules: { packageJson: { exports: "error" // Requires "exports" field in package.json } }}customElementsProperty
- Default:
"error" - Description: Validates the
customElementsfield in package.json points to your manifest file.
{ rules: { packageJson: { customElementsProperty: "error" // Requires "customElements": "./custom-elements.json" } }}publishedCem
- Default:
"error" - Description: Validates that the Custom Elements Manifest is included in the published package (not excluded by
.npmignoreorfilesfield).
{ rules: { packageJson: { publishedCem: "error" // Ensures manifest is published with package } }}Manifest Rules
These rules validate your Custom Elements Manifest:
schemaVersion
- Default:
"error" - Description: Validates the manifest uses the latest schema version.
{ rules: { manifest: { schemaVersion: "error" // Ensures latest schema version } }}modulePath
- Default:
"error" - Description: Validates each component has a valid module path that exists.
{ rules: { manifest: { modulePath: "error" // Ensures "./src/my-button.ts" exists } }}definitionPath
- Default:
"error" - Description: Validates each component has a valid path to its definition/implementation.
{ rules: { manifest: { definitionPath: "error" // Ensures component definition is valid } }}typeDefinitionPath
- Default:
"error" - Description: Validates each component has a valid TypeScript declaration file path.
{ rules: { manifest: { typeDefinitionPath: "error" // Ensures "./dist/my-button.d.ts" exists } }}exportTypes
- Default:
"error" - Description: Validates that all necessary types are exported from the module.
{ rules: { manifest: { exportTypes: "warning" // Warns if types aren't exported } }}tagName
- Default:
"error" - Description: Validates each custom element has a tag name defined.
{ rules: { manifest: { tagName: "error" // Requires <my-button> tag name } }}Complete Configuration Example
Here’s a comprehensive example showing all options:
import { validateCem, type CemValidatorOptions } from "@wc-toolkit/cem-validator";import manifest from "./custom-elements.json" with { type: 'json' };
const options: CemValidatorOptions = { // File paths packageJsonPath: "./package.json", cemFileName: "custom-elements.json",
// Behavior logErrors: false, debug: false, skip: false,
// Exclude specific components exclude: [ "BaseElement", "InternalMixin" ],
// Rule configuration rules: { packageJson: { packageType: "error", main: "off", module: "error", types: "error", exports: "error", customElementsProperty: "error", publishedCem: "error" }, manifest: { schemaVersion: "error", modulePath: "error", definitionPath: "error", typeDefinitionPath: "error", exportTypes: "warning", tagName: "error" } }};
validateCem(manifest, options);Severity Levels
Each rule supports three severity levels:
"off"- Disables the rule completely"warning"- Logs a warning but doesn’t fail validation"error"- Fails validation and throws an error (or logs iflogErrors: true)
{ rules: { manifest: { tagName: "error", // Fails validation exportTypes: "warning", // Shows warning only modulePath: "off" // Skips this check } }}Type Definitions
type CemValidatorOptions = { /** The path to the `package.json` file */ packageJsonPath?: string; /** Custom Elements Manifest file name */ cemFileName?: string; /** This will log errors rather throw an exception */ logErrors?: boolean; /** List of classes to exclude from validation */ exclude?: string[]; /** Enables logging during the component loading process */ debug?: boolean; /** Prevents plugin from executing */ skip?: boolean; /** Rule configurations */ rules?: Rules;};
/** The severity level for each rule */type Severity = "off" | "warning" | "error";
type Rules = { /** Checks if the package.json file is appropriately configured */ packageJson?: { /** Is `type` property set to "module" */ packageType?: Severity; /** Is `main` property set with a valid file path */ main?: Severity; /** Is `module` property set with a valid file path */ module?: Severity; /** Is `types` property set with a valid file path */ types?: Severity; /** Does the package have a `exports` property configured */ exports?: Severity; /** Is the `customElements` property properly configured */ customElementsProperty?: Severity; /** Is the Custom Elements Manifest included in the published package */ publishedCem?: Severity; }; /** Checks if the `customElementsManifest` is valid */ manifest?: { /** Is the manifest using the latest schema version */ schemaVersion?: Severity; /** Does the component have a valid module path */ modulePath?: Severity; /** Does the component have a valid definition path */ definitionPath?: Severity; /** Does the element have a valid type definition path */ typeDefinitionPath?: Severity; /** Does the component export all necessary types */ exportTypes?: Severity; /** Does the component have a tag name defined */ tagName?: Severity; };};