Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ A pnpm workspace. Every package in `packages/` is published to npm under its own
- `src/index.ts` is the parse plugin. It resolves every `:icon` in one call and writes `src`, `ratio`, `mask`, and `label` on the node, and drops one it can't draw. `name` stays, so a renderer that maps `icon` to Nuxt UI's component still works.
- An icon is a `.ci` span with its image in `--ci`: a mask over `currentColor` when the SVG uses it, a background image otherwise. The renderers set the style as an object, because `@comark/react` splits a style string at every colon and would cut the data URI.
- Plugins that draw icons (tabs) take an `Icons` and draw with `iconHtml()`/`iconStyles()` from `/html` and the `Icon` component from `/vue`, `/react`, and `/svelte`.
- `packages/lint` is `lint-comark`, a library, CLI, and Vite Doctor extension that lints Comark documents.
- `packages/lint` is `comark-linter`, a library, CLI, and Vite Doctor extension that lints Comark documents. npm refuses `comark-lint` as too close to `remark-lint`, so only the package is `comark-linter`: its command, config file, and messages stay `comark-lint`.
- `src/index.ts` is `lint()`. Comark gives nodes no positions, so block components take their line from markdown-exit tokens (plus the stripped frontmatter's lines) and everything else is found by searching the source with fenced code blanked out.
- `src/config.ts` loads the other packages in this repo through optional dynamic imports; they are optional peer dependencies. Add a new package there.
- `src/doctor.ts` is `lint-comark/doctor`. Vite Doctor has no Markdown visitor, so its Rules are manifest Rules that glob the Markdown in `onProjectEnd`. The root `doctor.config.ts` lints `docs/pages` with it as part of `pnpm run doctor`.
- `src/doctor.ts` is `comark-linter/doctor`. Vite Doctor has no Markdown visitor, so its Rules are manifest Rules that glob the Markdown in `onProjectEnd`. The root `doctor.config.ts` lints `docs/pages` with it as part of `pnpm run doctor`.
- `packages/tabs` is `comark-tabs`. Its parse plugin turns `::code-group` into `::tabs` and gives each `tabs-item` its label and position, and the `tabs` node every label and a `uid` from a hash of the source, so no renderer splits children and the HTML renderer's ids differ between documents on one page. Its script is built like carousel's.
- `packages/zoom` is `comark-zoom`.
- `src/index.ts` is the parse plugin. It marks content images and adds one `zoom-runtime` node to a document with anything to zoom.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
| [`comark-embed`](./packages/embed) | YouTube, Vimeo, Loom, Spotify, CodePen, StackBlitz, and Figma players from a link, with no network fetch. |
| [`comark-github-callouts`](./packages/github-callouts) | GitHub's `> [!NOTE]` alerts as callouts with an icon and a title. |
| [`comark-icon`](./packages/icon) | Iconify icons in Markdown as `:icon{name="lucide:apple"}`, drawn as CSS with no script, and the resolver other plugins draw icons with. |
| [`lint-comark`](./packages/lint) | Finds unclosed blocks, unknown components, broken anchors, and plugin issues in Comark documents, from a script, the command line, or Vite Doctor. |
| [`comark-linter`](./packages/lint) | Finds unclosed blocks, unknown components, broken anchors, and plugin issues in Comark documents, from a script, the command line, or Vite Doctor. |
| [`comark-include`](./packages/include) | Markdown partials and code snippets from files, by lines or by region. |
| [`comark-tabs`](./packages/tabs) | Tabs and code groups that switch together across the page, and read without a script. |
| [`comark-zoom`](./packages/zoom) | Zoom images and any block like medium-zoom, with Escape, Back, and click-outside to close. |
Expand Down
4 changes: 3 additions & 1 deletion docs/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ const packages = (
},
] satisfies { slug: string; group: string; icon: IconName; blurb: string; credits?: string }[]
).filter((pkg) => existsSync(new URL(`pages/${pkg.slug}/index.md`, root)));
// npm refuses `comark-lint` as too close to `remark-lint`.
const npmNames: Record<string, string> = { lint: "comark-linter" };
const fileOf = (slug: string) =>
existsSync(new URL(`pages/${slug || "index"}.md`, root))
? `${slug || "index"}.md`
Expand Down Expand Up @@ -223,7 +225,7 @@ const sections: Section[] = [
...packages.map((pkg) => ({
...pkg,
label: pkg.slug,
npm: `comark-${pkg.slug}`,
npm: npmNames[pkg.slug] ?? `comark-${pkg.slug}`,
// A package's pages, in this order when it has them.
lists: [
{
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/charts/guides.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ It reports unknown props and types, columns that `x` or `y` name but the table d

Annotations are checked too: a missing or unknown `at`, a `series` the chart doesn't draw, and the mark, color, and side, as comark-annotate checks them on screenshots.

Rendering never throws. A cell that isn't a number is left out, a mark it can't place is left out, a type that doesn't fit falls back to the default, a pie or a waterfall draws its first column, and a chart with nothing to draw shows its content as it is. [lint-comark](/lint/) reports the same issues with their line numbers.
Rendering never throws. A cell that isn't a number is left out, a mark it can't place is left out, a type that doesn't fit falls back to the default, a pie or a waterfall draws its first column, and a chart with nothing to draw shows its content as it is. [comark-linter](/lint/) reports the same issues with their line numbers.

## Theming

Expand Down
4 changes: 2 additions & 2 deletions docs/pages/guide/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ The text then always matches the renderer the app installed: an app on an older

## Check the result

An agent should check a document before it hands it over. lint-comark lists every problem, each with its line:
An agent should check a document before it hands it over. comark-linter lists every problem, each with its line:

```sh
npx lint-comark report.md
npx comark-linter report.md
```

Marks need a look as well as a check: a box can sit inside the image and still miss its target. `comark-annotate render` writes a standalone page next to the file, which the agent opens in a browser and screenshots to confirm each mark sits on its target:
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/guide/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ code, github-callouts, and zoom have nothing to report: every code block gets it

## Line numbers

`meta.issues` can't say where a problem is, because Comark gives nodes no source positions. [lint-comark](/lint/) finds the line of each issue and adds what Comark forgives silently: blocks never closed, misspelled components, broken anchors, and unsafe URLs. Run it in CI and in [Vite Doctor](/lint/vite-doctor/):
`meta.issues` can't say where a problem is, because Comark gives nodes no source positions. [comark-linter](/lint/) finds the line of each issue and adds what Comark forgives silently: blocks never closed, misspelled components, broken anchors, and unsafe URLs. Run it in CI and in [Vite Doctor](/lint/getting-started/#vite-doctor):

```sh
pnpm lint-comark "docs/**/*.md"
Expand Down
31 changes: 25 additions & 6 deletions docs/pages/lint/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Getting started
description: Run lint-comark from the command line, a script, or Vite Doctor.
install: pnpm add -D lint-comark
description: Run comark-linter from the command line, a script, or Vite Doctor.
install: pnpm add -D comark-linter
---

## Command line
Expand All @@ -11,13 +11,15 @@ pnpm lint-comark "docs/**/*.md"
pnpm lint-comark review.md --format json
```

The package is `comark-linter`, since npm refuses `comark-lint` as too close to `remark-lint`. Its command and config file are `comark-lint`.

It checks each file with the plugins and components of every comark-plugins package installed: annotate, carousel, github-callouts, and zoom. It prints one `file:line:column rule message` line per problem and exits with 1 if there are any, or 2 if a file can't be read.

Components of your own go in a config, passed with `--config`:

```ts
// lint-comark.config.ts
import { defineConfig } from "lint-comark";
// comark-lint.config.ts
import { defineConfig } from "comark-linter";
import mermaid from "./mermaid.ts";

export default defineConfig({
Expand All @@ -31,7 +33,7 @@ Its `plugins` and `components` are added to the installed packages'. `components
## Library

```ts
import { lint } from "lint-comark";
import { lint } from "comark-linter";
import annotate, { components } from "comark-annotate/html";

const diagnostics = await lint(markdown, { plugins: [annotate()], components });
Expand All @@ -42,4 +44,21 @@ const diagnostics = await lint(markdown, { plugins: [annotate()], components });

## Vite Doctor

`lint-comark/doctor` runs the same rules inside [Vite Doctor](/lint/vite-doctor/), each with its own code.
`comark-linter/doctor` is a [Vite Doctor](https://github.com/onmax/vite-doctor) extension. Vite Doctor loads extensions from an executable config, so register it in `doctor.config.ts`:

```ts
// doctor.config.ts
import comark from "comark-linter/doctor";
import { defineDoctorConfig } from "vite-doctor/config";

export default defineDoctorConfig({
extends: ["auto"],
extensions: [comark({ components: ["tabs"] })],
});
```

```sh
pnpm vite-doctor . --config doctor.config.ts
```

It lints `**/*.{md,mdc}` except `node_modules` and `dist`; set `include` and `exclude` to change that. Each rule reports with its own code, `COMARK0001` to `COMARK0007`, and the `comark/recommended` preset turns them all on. It runs whenever the extension is registered. It needs `vite-doctor` 0.1 or later.
4 changes: 2 additions & 2 deletions docs/pages/lint/index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: lint-comark
title: comark-linter
description: Finds unclosed blocks, unknown components, broken anchors, and plugin issues in Comark documents.
tagline: Check Comark documents before they publish, from a script, the command line, or Vite Doctor. Every message names the block and says how to fix it.
install: pnpm add -D lint-comark
install: pnpm add -D comark-linter
---

Comark forgives a lot: it closes a `::` block you forgot to close, reads a missing `}` as the end of the line, and renders a misspelled component as an empty element. lint-comark reports each of these, with its line, so whoever wrote the document, often an agent, can fix it.
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/lint/vite-doctor.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
---
title: Vite Doctor
description: Lint a project's Markdown as part of Vite Doctor, with one code per rule.
install: pnpm add -D lint-comark vite-doctor
install: pnpm add -D comark-linter vite-doctor
---

## Setup

`lint-comark/doctor` is a [Vite Doctor](https://github.com/onmax/vite-doctor) extension. Vite Doctor loads extensions from an executable config, so register it in `doctor.config.ts`:
`comark-linter/doctor` is a [Vite Doctor](https://github.com/onmax/vite-doctor) extension. Vite Doctor loads extensions from an executable config, so register it in `doctor.config.ts`:

```ts
// doctor.config.ts
import comark from "lint-comark/doctor";
import comark from "comark-linter/doctor";
import { defineDoctorConfig } from "vite-doctor/config";

export default defineDoctorConfig({
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/recipes/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Register the plugins in one renderer, as [Renderers](/guide/renderers/#several-p

## Check it

Run [lint-comark](/lint/) on the file before you post it, and open the rendered page to see that each mark sits on its target. [Agents](/guide/agents/#check-the-result) shows both commands.
Run [comark-linter](/lint/) on the file before you post it, and open the rendered page to see that each mark sits on its target. [Agents](/guide/agents/#check-the-result) shows both commands.

## Share it

Expand Down
2 changes: 1 addition & 1 deletion docs/play.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// The playground: renders the editor's Markdown in the browser with every plugin that runs there. comark-include
// reads files and lint-comark is a checker, so they're left out, and so is Shiki, which would triple the bundle.
// reads files and comark-linter is a checker, so they're left out, and so is Shiki, which would triple the bundle.
import { renderHtmlFromDocument } from "@comark/html";
import { parseMarkdown } from "comark";
import annotate, { components as annotateComponents } from "../packages/annotate/src/html.ts";
Expand Down
4 changes: 2 additions & 2 deletions doctor.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import comark from "lint-comark/doctor";
import comark from "comark-linter/doctor";
import { defineDoctorConfig } from "vite-doctor/config";

// The packages run their own Doctor configs; this one only lints the docs pages with lint-comark.
// The packages run their own Doctor configs; this one only lints the docs pages with comark-linter.
export default defineDoctorConfig({
extends: ["comark/recommended"],
extensions: [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"@playwright/test": "catalog:",
"@types/node": "catalog:",
"comark": "catalog:",
"comark-linter": "workspace:*",
"geist": "catalog:",
"lint-comark": "workspace:*",
"pkg-pr-new": "catalog:",
"publint": "catalog:",
"shiki": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion packages/charts/skills/comark-charts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ The cache lands, and builds drop below 600 ms.

## Check it

In a project with comark-charts installed, run `npx lint-comark report.md`. It prints one line per problem, each naming the chart by its position (`` `::chart` 2 ``) and saying how to fix it, and exits with 1 if there are any. Fix every one. In code, the same sentences are in `meta.issues` after `parseMarkdown(markdown, { plugins: [charts()] })`.
In a project with comark-charts installed, run `npx comark-linter report.md`. It prints one line per problem, each naming the chart by its position (`` `::chart` 2 ``) and saying how to fix it, and exits with 1 if there are any. Fix every one. In code, the same sentences are in `meta.issues` after `parseMarkdown(markdown, { plugins: [charts()] })`.

When you can render the page, open it and confirm the chart says what your text says: the right type, every series named, and the numbers in the expected order.
2 changes: 1 addition & 1 deletion packages/icon/test/icon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ describe("comark-icon", () => {
);
const markdown =
'A :icon{name="lucide:aple"} B :icon{name="apple"} C :icon{name="gone:x"} D :icon{name="down:x"} E :icon{name="lucide:apple:extra"} F :icon';
expect(stripStyles(await render(markdown))).toBe("<p>A B C D E F</p>");
expect(await issues(markdown)).toEqual([
'`:icon` 1 ("lucide:aple"): The `lucide` set has no icon `aple`. Find its name at https://icones.js.org/collection/lucide.',
'`:icon` 2 ("apple"): "apple" isn\'t an icon name. Write it as `set:name`, such as `lucide:apple`, or give an image URL.',
Expand All @@ -141,7 +142,6 @@ describe("comark-icon", () => {
'`:icon` 5 ("lucide:apple:extra"): "lucide:apple:extra" isn\'t an icon name. Write it as `set:name`, such as `lucide:apple`, or give an image URL.',
'`:icon` 6: `name` is required. Write `:icon{name="lucide:apple"}`.',
]);
expect(stripStyles(await render(markdown))).toBe("<p>A B C D E F</p>");
});

it("adds its issues to the ones other plugins found", async () => {
Expand Down
22 changes: 12 additions & 10 deletions packages/lint/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
# lint-comark
# comark-linter

[![npm version](https://npmx.dev/api/registry/badge/version/lint-comark)](https://npmx.dev/package/lint-comark)
[![npm downloads](https://npmx.dev/api/registry/badge/downloads-month/lint-comark)](https://npmx.dev/package/lint-comark)
[![install size](https://npmx.dev/api/registry/badge/size/lint-comark)](https://npmx.dev/package/lint-comark)
[![license](https://npmx.dev/api/registry/badge/license/lint-comark)](https://npmx.dev/package/lint-comark)
[![npm version](https://npmx.dev/api/registry/badge/version/comark-linter)](https://npmx.dev/package/comark-linter)
[![npm downloads](https://npmx.dev/api/registry/badge/downloads-month/comark-linter)](https://npmx.dev/package/comark-linter)
[![install size](https://npmx.dev/api/registry/badge/size/comark-linter)](https://npmx.dev/package/comark-linter)
[![license](https://npmx.dev/api/registry/badge/license/comark-linter)](https://npmx.dev/package/comark-linter)
[![docs](https://img.shields.io/badge/docs-comark.onmax.me-14b8a6?labelColor=0a0a0a)](https://comark.onmax.me/lint/)

Finds unclosed blocks, unknown components, broken anchors, and plugin issues in [Comark](https://comark.dev) documents. A library, a CLI, and a [Vite Doctor](https://github.com/onmax/vite-doctor) extension. Every message names the block and says how to fix it.

```sh
pnpm add -D lint-comark
pnpm lint-comark "docs/**/*.md"
pnpm add -D comark-linter
pnpm comark-lint "docs/**/*.md"
```

The package is `comark-linter`, since npm refuses `comark-lint` as too close to `remark-lint`. Its command, config file, and messages are `comark-lint`.

```txt
docs/review.md:8:1 comark/unclosed-block `::compare` opened on line 8 is never closed. Add a line with `::` after its last child.
```

The CLI checks each file with every comark-plugins package installed (annotate, carousel, charts, github-callouts, zoom), prints `file:line:column rule message`, or JSON with `--format json`, and exits with 1 on any problem. Add your own plugins and component names with `--config lint-comark.config.ts`, which exports `defineConfig({ plugins, components })`.
The CLI checks each file with every comark-plugins package installed (annotate, carousel, charts, github-callouts, zoom), prints `file:line:column rule message`, or JSON with `--format json`, and exits with 1 on any problem. Add your own plugins and component names with `--config comark-linter.config.ts`, which exports `defineConfig({ plugins, components })`.

```ts
import { lint } from "lint-comark";
import { lint } from "comark-linter";
import annotate, { components } from "comark-annotate/html";

const diagnostics = await lint(markdown, { plugins: [annotate()], components });
Expand All @@ -31,7 +33,7 @@ const diagnostics = await lint(markdown, { plugins: [annotate()], components });
For Vite Doctor 0.1 or later, register the extension in `doctor.config.ts` and run `vite-doctor . --config doctor.config.ts`:

```ts
import comark from "lint-comark/doctor";
import comark from "comark-linter/doctor";
import { defineDoctorConfig } from "vite-doctor/config";

export default defineDoctorConfig({ extends: ["auto"], extensions: [comark()] });
Expand Down
4 changes: 2 additions & 2 deletions packages/lint/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "lint-comark",
"name": "comark-linter",
"version": "0.2.0",
"description": "Finds unclosed blocks, unknown components, broken anchors, and plugin issues in Comark documents. A library, a CLI, and a Vite Doctor extension.",
"keywords": [
Expand Down Expand Up @@ -39,7 +39,7 @@
"build": "tsdown",
"dev": "tsdown --watch",
"doctor": "vite-doctor . --no-cache --max-warnings 0 --format text",
"prepack": "pnpm --filter lint-comark... build"
"prepack": "pnpm --filter comark-linter... build"
},
"devDependencies": {
"comark": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading