Repository navigation
feat: ship a comark-linter config for sites #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0219d15
51b7a1d
65f403c
00f631c
2c31a4e
32d70ed
6c2afba
094d485
a411f43
1d0110e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| // `::landing-hero` is Nuxt UI's page hero. | ||
| export { default } from '@nuxt/ui/components/PageHero.vue' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,9 @@ | ||
| <script setup lang="ts"> | ||
| import LandingHero from '@nuxt/ui/components/PageHero.vue' | ||
| import LandingStack from '../components/landing/LandingStack.vue' | ||
| import LandingTabs from '../components/landing/LandingTabs.vue' | ||
| import LandingFeatures from '../components/landing/LandingFeatures.vue' | ||
| import LandingFeatureCard from '../components/landing/LandingFeatureCard.vue' | ||
| import LandingFaq from '../components/landing/LandingFaq.vue' | ||
| import LandingCta from '../components/landing/LandingCta.vue' | ||
| import LandingHeroDemo from '../components/landing/LandingHeroDemo.vue' | ||
| import Button from '@nuxt/ui/components/Button.vue' | ||
|
|
||
| const landingComponents = { | ||
| LandingHero, | ||
| LandingStack, | ||
| LandingTabs, | ||
| LandingFeatures, | ||
| LandingFeatureCard, | ||
| LandingFaq, | ||
| LandingCta, | ||
| LandingHeroDemo, | ||
| Button | ||
| ...byFileName(import.meta.glob('../components/content/landing/*.{vue,ts}', { eager: true, import: 'default' })), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Button, | ||
| } | ||
|
|
||
| const content = useDocsContent() | ||
|
|
@@ -35,7 +20,7 @@ | |
| return p ? { ...p, nodes: prefixTreeLinks(p.nodes, content.value.routeBase) } : p | ||
| }) | ||
|
|
||
| const fm = computed<Record<string, any>>(() => page.value?.data ?? {}) | ||
|
|
||
| // Keep branch/commit previews out of search, same as DocsPage. | ||
| useRobotsRule(computed(() => (content.value.mode === 'prod' ? 'index, follow' : 'noindex, nofollow'))) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import type { Component } from 'vue' | ||
|
|
||
| /** | ||
| * Names the layer's Markdown components after their file: `content/CodeExplorer.vue` renders | ||
| * `::code-explorer`. Each page globs its own folder (docs pages `content/`, the landing page | ||
| * `content/landing/`) and passes the result to `MarkdownDocument`, so none is global and neither | ||
| * page loads the other's, such as the landing demo's Markdown parser. `lint.mjs` reads both folders. | ||
| */ | ||
| export function byFileName(modules: Record<string, unknown>): Record<string, Component> { | ||
| return Object.fromEntries( | ||
| Object.entries(modules).map(([path, component]) => [path.slice(path.lastIndexOf('/') + 1).replace(/\.\w+$/, ''), component as Component]), | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // @ts-check | ||
| // A comark-linter config for sites extending this layer. Run `comark-lint` from the site's root: | ||
| // comark-lint "content/**/*.md" | ||
| // next to a `comark-lint.config.ts` that re-exports it: `export { default } from 'comark-docs/lint.mjs'`. | ||
| // Plain JavaScript, since Node won't strip types from a file in node_modules. | ||
| import { readdirSync } from 'node:fs' | ||
| import { basename, dirname } from 'node:path' | ||
| import { fileURLToPath } from 'node:url' | ||
| import { comarkContent } from 'comark-content' | ||
| import fs from 'comark-content/sources/fs' | ||
| import emoji from 'comark/plugins/emoji' | ||
|
|
||
| const kebab = (/** @type {string} */ name) => name.replace(/[A-Z]/g, (c, i) => (i ? '-' : '') + c.toLowerCase()) | ||
|
|
||
| /** The kebab-case names of the components in a folder and its subfolders. */ | ||
| function componentsIn(/** @type {string} */ dir) { | ||
| return readdirSync(dir, { recursive: true, encoding: 'utf8' }) | ||
| .map(file => basename(file).match(/^(\w+)\.(vue|ts)$/)?.[1]) | ||
| .filter(name => name !== undefined) | ||
| .map(name => kebab(name)) | ||
| } | ||
|
|
||
| const nuxtUi = dirname(fileURLToPath(import.meta.resolve('@nuxt/ui/components/Button.vue'))) | ||
|
|
||
| // What the pages pass to `MarkdownDocument`: Nuxt UI's prose components and the layer's | ||
| // `app/components/content/`, plus every Nuxt UI component with the `u-` prefix, such as `::u-button`. | ||
| const components = [ | ||
| ...componentsIn(`${nuxtUi}/prose`), | ||
| ...componentsIn(fileURLToPath(new URL('app/components/content', import.meta.url))), | ||
| ...readdirSync(nuxtUi).filter(file => file.endsWith('.vue')).map(file => `u-${kebab(basename(file, '.vue'))}`), | ||
| ] | ||
|
|
||
| // Each page's URL, mapped to its file the way comark-content derives the URL from it. | ||
| const pages = new Map() | ||
| for (const item of await comarkContent({ source: fs('content') }).list()) { | ||
| pages.set(item.path, `content/${item.meta.stem}${item.meta.extension}`) | ||
| } | ||
|
|
||
| /** @type {import('comark-linter').Config} */ | ||
| export default { | ||
| components, | ||
| // The layer parses `:smile:` shortcodes as emoji, not as inline components. | ||
| plugins: [emoji()], | ||
| links: { | ||
| // Guides link a local dev server on purpose, for readers running it. | ||
| skip: ['localhost'], | ||
| routes: path => pages.get(path.replace(/\/$/, '') || '/'), | ||
| }, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| // Lints this playground with the config the layer ships to sites. | ||
| export { default } from '../lint.mjs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: comarkdown/comark-docs
Length of output: 2048
Script executed:
Repository: comarkdown/comark-docs
Length of output: 11356
Script executed:
Repository: comarkdown/comark-docs
Length of output: 7552
Script executed:
Repository: comarkdown/comark-docs
Length of output: 3132
Script executed:
Repository: comarkdown/comark-docs
Length of output: 1990
Script executed:
Repository: comarkdown/comark-docs
Length of output: 525
Script executed:
Repository: comarkdown/comark-docs
Length of output: 9125
Script executed:
Repository: comarkdown/comark-docs
Length of output: 9758
Script executed:
Repository: comarkdown/comark-docs
Length of output: 4552
Script executed:
Repository: comarkdown/comark-docs
Length of output: 2503
Script executed:
Repository: comarkdown/comark-docs
Length of output: 4173
Script executed:
Repository: comarkdown/comark-docs
Length of output: 5650
Script executed:
Repository: comarkdown/comark-docs
Length of output: 547
Scope the content linter's component list to the page that renders it.
lint.mjscollects every component underapp/components/content/, includinglanding/. The docs route renders only the top-levelcontent/*.vueandcontent/*.tscomponents. A docs page that uses::landing-hero-demo, such asplayground/content/2.writing/4.landing-page.md, passes lint. At render time the docs page emits an unresolvedlanding-hero-demoelement, and the demo does not appear. Limit the landing-only names to the landing page, or remove the landing-only block from docs content.