npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@hashicorp/platform-content-conformance

v0.0.12

Published

A system to execute checks against HashiCorp's various types of content. Very similar to [ESLint](https://eslint.org/), but tailored to checking content (MDX) and data (JSON, YAML) instead of JavaScript source.

Downloads

2,483

Readme

@hashicorp/platform-content-conformance

A system to execute checks against HashiCorp's various types of content. Very similar to ESLint, but tailored to checking content (MDX) and data (JSON, YAML) instead of JavaScript source.

Installation

$ npm install @hashicorp/platform-content-conformance

Usage

CLI

$ hc-content [options] [path/to/file] [path/to/file]

Options:
  --cwd
  --config ./path/to/conformance.config.mjs

JavaScript

The package exposes its underlying functionality through the ContentConformanceRunner class.

import { ContentConformanceRunner } from '@hashicorp/platform-content-conformance'

async function main() {
  const runner = new ContentConformanceRunner()

  await runner.init()

  await runner.run()

  console.log(runner.report())
}

main()

Options

  • cwd (string) - Specify the working directory where the content checks will be run. Defaults to process.cwd().
  • config (string) - Specify a path to a custom configuration file. If not provided, defaults to {cwd}/content-conformance.config.mjs.
  • files (string[]) - Specify a list of filenames to match against when running checks. Acts as a filter. If not specified, all files matching the patterns provided in your configuration will be checked.
  • reporter (string) - Specify the reporter type to use when calling runner.report(). Can be text, json, or markdown. Defaults to text.

Configuration

On its own, the content conformance system does not know where the files to be checked are located, or what rules should be used. To configure the checker, we create a content-conformance.config.mjs file located in your project's root directory.

export default {
  root: '.',
  contentFileGlobPattern: 'content/**/*.mdx',
  rules: {
    'no-h1': 'error',
  },
}

Configuring rules

Rules can be specified with differing severity to control the check's failure state. The valid values are: error, warn, or off. By default, errors will cause a run to fail, while warnings will not. Rules can also be turned off. Individual rules may also accept a configuration object. This configuration object is made available via the rule context that is passed into the executor functions.

export default {
  root: '.',
  contentFileGlobPattern: 'content/**/*.mdx',
  partialsDirectories: ['content/partials'],
  rules: {
    'with-config': [
      'error',
      { message: 'this will be available as context.config.message' },
    ],
  },
}

Presets

This package includes a number of configuration presets that can be used by specifying a preset configuration property:

export default {
  preset: 'base-mdx',
  rules: {
    'my-rule': 'off',
  },
}

When using a preset, any properties defined in the consuming configuration will take precedence over the preset. Individual rule configuration from a preset is completely overwritten if present in the consuming configuration. In the above case, if base-mdx contains configuration for the my-rule rule, it would be replaced with off.

Available presets can be found in the ./src/configs directory.

Rules

The core value of the conformance checker lies in the rules that you configure. The core package ships with a number of rules and presets that can be used, and custom rules can also be created in your project. A rule is a JavaScript module that exports a specific object format.

// ./rules/my-rule.js

/** @type {import('@hashicorp/platform-content-conformance').Rule} */
export default {
  id: 'my-rule',
  type: 'content',
  description: 'This rule performs an important check.',
  executor: {
    async contentFile(file, context) {},
    async dataFile(file, context) {},
  },
}

Rule Properties

  • id (string) A unique identifier for your rule. Convention is to use the ID as the rule's filename.
  • type (string) The type of rule. Can be content, data, or structure.
  • description (string) A short description of what the rule does.
  • executor (object) An object containing file "executor" functions. These functions contain the logic for the rule.
    • contentFile (function) Called when a ContentFile is visited.
    • dataFile (function) Called when a DataFile is visited.

Writing an executor

A rule executor contains the logic for the conformance rule. Each executor type is run once for each file of the provided type (content, or data), or once for the entire project if the type is structure.

Each executor has access to two arguments:

  • file (ContentFile | DataFile) The file being checked by the rule.
  • context (object) An object containing global information and a report() method (see ConformanceRuleContext in types.ts).

context.report()

An executor should contain logic related to the rule to determine if the given file is in violation. If a violation is detected, the executor should call context.report():

{
  async contentFile(file, context) {
    if (hasViolation) {
      context.report('This rule was broken', file)
    }
  }
}

The context.report() method accepts three arguments:

  • message (string) The message that will be used in generated output.
  • file (object) The file that the report is associated with.
  • node (object) The node that the violation is associated with. Allows positional information to be displayed in the report. (optional)

Note An individual rule does not have a concept of severity in its implementation. That is handled in the configuration file.

ContentFile visitors

When defining a contentFile executor, the file argument exposes a visit() method. This allows the rule to interact with the Abstract Syntax Tree (AST) generated from the source file. It allows checks to be written against specific parts of a content file with minimal boilerplate or extra processing. Usage of file.visit() is very similar to unist-util-visit.

{
  async contentFile(file, context) {
    file.visit(['link'], node => {
      const hasViolation = node.url.includes('example.com')
      if (hasViolation) {
        context.report(`Link to example.com detected: ${node.url}`, file, node)
      }
    })
  }
}

ContentFile frontmatter

The frontmatter of a content file can be accessed using the file.frontmatter() method:

{
  async contentFile(file, context) {
    console.log(file.frontmatter())
  }
}

Internally, the underlying ContentFile class ensures that the source file is only parsed into its AST representation once.

DataFile contents

To access the contents of a data file, use the .contents() method:

{
  async dataFile(file, context) {
    console.log(file.contents())
  }
}

Using remark-lint rules

remark-lint rules are supported by default. To use a remark-lint rule, add it to your configuration and ensure the package is installed as a development dependency in your project.

export default {
  root: '.',
  contentFileGlobPattern: 'content/**/*.mdx',
  rules: {
    'remark-lint-definition-case': 'error',
  },
}

Rule Messages

Writing meaningful, actionable rule messages is important to ensure contributors can address issues on their own. Messages passed to context.report() should be written in an active voice. For example:

❌ 'Expected frontmatter to contain: description'
✅ 'Document does not have a `description` key in its frontmatter. Add a `description` key at the top of the document.'

Internals

At its core, the content conformance checker handles loading a number of different file types and running rules against those files. Rule violations are tracked by file, and after execution rules can be passed to a reporter method to present the results.

Files that are eligible to be checked are represented as VFile[https://github.com/vfile/vfile] instances. VFile has a notion of messages attached to a file, along with a number of custom reporters to format and output a file's messages. We take advantage fo this by logging rule violations as a VFileMessage on the relevant file.

Runner

The runner is responsible for handling options, loading configuration, loading rules, and setting up the environment for the engine to execute. The runner can be called directly from JavaScript, or invoked via the CLI. After calling the engine to execute the checks, the runner is responsible for reporting the results using its configured reporter.

Engine

The engine is responsible for reading files and executing rules against the files.

File

All files that are checked are represented by VFile instances, with some additional information. The ContentFile primitive represents files that contain documentation content. Currently, our content files are authored with MDX, with support for YAML frontmatter, out-of-the-box. The DataFile primitive represents files that contain data used to render our pages. Currently, JSON and YAML files are supported.

Rule

This package ships with a number of rules, and they can also be defined within consuming projects. Rules can define a number of executor functions that will be run on the different file types. See Rules for more information.