Skip to content

Commit

Permalink
Use vitepress & twoslash
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Mar 31, 2024
1 parent 2efd5d6 commit 55b4f2d
Show file tree
Hide file tree
Showing 42 changed files with 370 additions and 1,163 deletions.
5 changes: 3 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
node_modules
/test-file
/tests/fixtures
!/docs/.vuepress
/docs/.vuepress/dist
!/docs/.vitepress
/docs/.vitepress/dist
/docs/.vitepress/cache
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ dist
# vuepress build output
.vuepress/dist

# vitepress build output
/docs/.vitepress/dist
/docs/.vitepress/cache

# Serverless directories
.serverless/

Expand All @@ -108,3 +112,5 @@ dist
/index.html
/alias-package


!docs/.vitepress/twoslash-stylelint/dist
5 changes: 3 additions & 2 deletions .stylelintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
!/docs/.vuepress
/docs/.vuepress/dist
!/docs/.vitepress
/docs/.vitepress/dist
/docs/.vitepress/cache
/.nyc_output
/coverage
node_modules
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h1 align="center">stylelint-stylus</h1>

<p align="center"><a href="https://stylelint.io/" alt="Stylelint">Stylelint</a> plugin for <a href="https://stylus-lang.com/" alt="Stylus">Stylus</a>.
<p align="center"><a href="https://stylelint.io/" alt="Stylelint">Stylelint</a> plugin for <a href="https://stylus-lang.com/" alt="Stylus">Stylus</a>.</p>

<p align="center"><b><i>This plugin is still in an experimental state</i></b></p>

Expand Down Expand Up @@ -34,7 +34,7 @@ This plugin allows us to check the [Stylus] with [Stylelint].

[Stylelint editor integrations](https://stylelint.io/user-guide/integrations/editor) are useful to check your code in real-time.

You can check on the [Online DEMO](https://stylus.github.io/stylelint-stylus/playground/).
You can check on the [Online DEMO](https://stylelint.io/demo/#N4Igxg9gJgpiBcID0SAEAVATgT1QZQBdsAbAVwGcBCAHQDsAjaXYO1VAMwloPlQEYALAAcAHkj4A6AKyoAEjGIA3GAQCWYAIYAaVBsyqNxHeQ21yAWnIx97ANytUAYmIQA5hFQtabNo0yxMc0wNKFUKXilRe29UAF86eNo6FFQAMWIYEVV6DNRybG4NETpGKGwHTm5+YTFJGXklFXVtXX1DY1MLKxs6B3pSAgIuPoGh2gl+weGY1VohAYBtIiEYAF5qEEmxjYBdB1n5giXsFfWQclJ6AFtVAl2HXwh-ayCQsPJUSOKk2hSAWVUWTMJSeAVeoQoAApFIYAJQOcwAdxg9AA1rdzH4wcEIeReDDiAirhAAF6Y0EvHHvfGGPoUwJU8KoAkOEqjLieOnPBlvKFfWHRRIgLQgdiqDIAOQ0VzgiEy0qEGQk5CIxGF4C4YtcCBAXjYG0yBBgtCg5A2vAWD1QGxVJAUswIllVFCQKtMUD0UA2Dj2tFi6sgtC1qSeVw0BB1ACtyFx1bAhOQdXrredVfbuOaU8RwzAVRstKzU3biA6nSQKJmNtmjXmQAkQLEgA).

## :cd: Installation

Expand Down Expand Up @@ -207,9 +207,8 @@ These rules relate to style guidelines.

## License

See the [LICENSE] file for license rights and limitations (MIT).
See the [LICENSE](./LICENSE) file for license rights and limitations (MIT).

[license]: ./LICENSE
[stylelint]: https://stylelint.io/
[stylus]: https://stylus-lang.com/
[vscode extension]: https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint
Expand Down
140 changes: 140 additions & 0 deletions docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { defineConfig } from "vitepress"
import path from "path"
import { fileURLToPath } from "url"
import { transformerTwoslash } from "@shikijs/vitepress-twoslash"
import { createTwoslasher as createTwoslasherStylelint } from "./twoslash-stylelint/index.mjs"

const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

function ruleToSidebarItem({ ruleName, fileName }) {
return {
text: ruleName,
link: `/rules/${fileName}`,
}
}

export default async () => {
const categoriesPath = path.join(dirname, "../../scripts/lib/categories.js")
const { categories, uncategorizedRules, deprecatedRules } = await import(
categoriesPath
).then((m) => m.default || m)

const extraCategories = []
if (uncategorizedRules.length > 0) {
extraCategories.push({
text: "Uncategorized",
collapsed: false,
items: uncategorizedRules.map(ruleToSidebarItem),
})
}
if (deprecatedRules.length > 0) {
extraCategories.push({
text: "Deprecated",
collapsed: false,
items: deprecatedRules.map(ruleToSidebarItem),
})
}

const configExtractor = /\/\*\s*stylelint rules config:(.*?)\*\//u

const pluginPath = path.join(dirname, "../../lib/index.js")
return defineConfig({
base: "/stylelint-stylus/",
title: "stylelint-stylus",
outDir: path.join(dirname, "./dist/stylelint-stylus"),
description: "Stylelint plugin for Stylus",
head: [],
lastUpdated: true,
markdown: {
codeTransformers: [
transformerTwoslash({
explicitTrigger: false, // Required for v-menu to work.
langs: ["stylus", "styl"],
filter(lang, code) {
if (
lang.startsWith("stylus") ||
lang.startsWith("styl")
) {
return configExtractor.test(code)
}
return false
},
errorRendering: "hover",
twoslasher: (code, ...args) => {
const config = configExtractor.exec(code)[1]

const twoslasher = createTwoslasherStylelint({
stylelintConfig: {
plugins: [pluginPath],
extends: ["stylelint-config-html"],
overrides: [
{
files: [
"*.stylus",
"*.styl",
"**/*.stylus",
"**/*.styl",
],
customSyntax: "postcss-styl",
rules: JSON.parse(config),
},
],
},
})
return twoslasher(code, ...args)
},
}),
],
},
themeConfig: {
siteTitle: "stylelint-stylus",
search: {
provider: "local",
options: {
detailedView: true,
},
},
editLink: {
pattern:
"https://github.com/stylus/stylelint-stylus/edit/main/docs/:path",
},
nav: [
{ text: "User Guide", link: "/" },
{
text: "Playground",
link: "https://stylelint.io/demo/#N4Igxg9gJgpiBcID0SAEAVATgT1QZQBdsAbAVwGcBCAHQDsAjaXYO1VAMwloPlQEYALAAcAHkj4A6AKyoAEjGIA3GAQCWYAIYAaVBsyqNxHeQ21yAWnIx97ANytUAYmIQA5hFQtabNo0yxMc0wNKFUKXilRe29UAF86eNo6FFQAMWIYEVV6DNRybG4NETpGKGwHTm5+YTFJGXklFXVtXX1DY1MLKxs6B3pSAgIuPoGh2gl+weGY1VohAYBtIiEYAF5qEEmxjYBdB1n5giXsFfWQclJ6AFtVAl2HXwh-ayCQsPJUSOKk2hSAWVUWTMJSeAVeoQoAApFIYAJQOcwAdxg9AA1rdzH4wcEIeReDDiAirhAAF6Y0EvHHvfGGPoUwJU8KoAkOEqjLieOnPBlvKFfWHRRIgLQgdiqDIAOQ0VzgiEy0qEGQk5CIxGF4C4YtcCBAXjYG0yBBgtCg5A2vAWD1QGxVJAUswIllVFCQKtMUD0UA2Dj2tFi6sgtC1qSeVw0BB1ACtyFx1bAhOQdXrredVfbuOaU8RwzAVRstKzU3biA6nSQKJmNtmjXmQAkQLEgA",
},
],
socialLinks: [
{
icon: "github",
link: "https://github.com/stylus/stylelint-stylus",
},
],
sidebar: {
"/": [
{
text: "Guide",
items: [{ text: "User Guide", link: "/" }],
},
{
text: "Rules",
items: [
...categories
.map(({ title, rules: catRules }) => ({
text: title.replace(/ \(.+?\)/u, ""),
collapsed: false,
items: catRules.map(ruleToSidebarItem),
}))
.filter((menu) => Boolean(menu.items.length)),

// Rules in no category.
...extraCategories,
],
},
],
},
},
})
}
11 changes: 11 additions & 0 deletions docs/.vitepress/stylelint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {

Check failure on line 1 in docs/.vitepress/stylelint.config.js

View workflow job for this annotation

GitHub Actions / lint

Use the global form of 'use strict'
extends: ["stylelint-config-standard-vue"],
rules: {
"no-descending-specificity": null,
"selector-class-pattern": null,
"value-keyword-case": null,

// Conflict with Prettier
// indentation: null,
},
};

Check failure on line 11 in docs/.vitepress/stylelint.config.js

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
13 changes: 13 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Theme, EnhanceAppContext } from "vitepress";
import DefaultTheme from "vitepress/theme";
import TwoslashFloatingVue from "@shikijs/vitepress-twoslash/client";
import "@shikijs/vitepress-twoslash/style.css";
import "./style.css";

const theme: Theme = {
extends: DefaultTheme,
enhanceApp({ app }: EnhanceAppContext) {
app.use(TwoslashFloatingVue as never);
},
};
export default theme;
13 changes: 13 additions & 0 deletions docs/.vitepress/theme/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
a > img {
display: inline-block;
}

a.title {
white-space: pre-wrap;
}

.twoslash-error-hover > * {
min-width: 4px;
min-height: 16px;
display: inline-block;
}
33 changes: 33 additions & 0 deletions docs/.vitepress/twoslash-stylelint/index.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as stylelint from 'stylelint';
import { TwoslashGenericFunction } from 'twoslash-protocol';

interface CreateTwoslashStylelintOptions {
/**
* Flat configs for Stylelint
*/
stylelintConfig: stylelint.Config;
/**
* Custom code transform before sending to Stylelint for verification
*
* This does not affect the code rendering
*/
stylelintCodePreprocess?: (code: string) => string;
/**
* The current working directory for Stylelint
*/
cwd?: string;
/**
* Include the parsed docs in the result
*
* @default true
*/
includeDocs?: boolean;
/**
* Merge error messages that has same range
* @default true
*/
mergeMessages?: boolean;
}
declare function createTwoslasher(options: CreateTwoslashStylelintOptions): TwoslashGenericFunction;

export { type CreateTwoslashStylelintOptions, createTwoslasher };
81 changes: 81 additions & 0 deletions docs/.vitepress/twoslash-stylelint/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { fileURLToPath } from "node:url"
import * as path from "node:path"
import {
createPositionConverter,
resolveNodePositions,
} from "twoslash-protocol"
import { createSyncFn } from "synckit"

function createTwoslasher(options) {
const { includeDocs = true, mergeMessages = true } = options
const workerPath = path.join(
fileURLToPath(import.meta.url),
"../stylelint-worker.mjs",
)
const lint = createSyncFn(workerPath)
return (code, file) => {
const filename = file?.includes(".") ? file : `index.${file ?? "css"}`
const linterResult = lint({
config: options.stylelintConfig,
codeFilename: filename,
code: options.stylelintCodePreprocess?.(code) || code,
})
const result = linterResult.results[0]
const pc = createPositionConverter(code)
const raws = result.warnings.map((message) => {
const start = pc.posToIndex(message.line - 1, message.column - 1)
const end =
message.endLine != null && message.endColumn != null
? pc.posToIndex(message.endLine - 1, message.endColumn - 1)
: start + 1
let text = message.text
if (message.rule) {
const link =
includeDocs &&
linterResult.ruleMetadata?.[message.rule]?.url
text += link
? ` ([${message.rule}](${link}))`
: ` (${message.rule})`
}
return {
type: "error",
id: message.rule || "",
code: 0,
text,
start,
length: end - start,
level: message.severity,
filename,
}
})
let merged = []
if (mergeMessages) {
for (const current of raws) {
const existing = merged.find(
(r) =>
r.start === current.start &&
r.length === current.length,
)
if (existing) {
existing.text += `
${current.text}`
continue
}
merged.push(current)
}
} else {
merged = raws
}
const nodes = resolveNodePositions(merged, code).filter(
(i) => i.line < pc.lines.length,
)
const results = {
code,
nodes,
}
return results
}
}

export { createTwoslasher }
17 changes: 17 additions & 0 deletions docs/.vitepress/twoslash-stylelint/stylelint-worker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { runAsWorker } from "synckit"

runAsWorker(lint)

async function lint(options) {
const stylelint = await import("stylelint").then((m) => m.default || m)
const result = await stylelint.lint(options)
// Returns only cloneable values for subsequent use.
return {
results: result.results.map((r) => {
return {
warnings: r.warnings,
}
}),
ruleMetadata: result.ruleMetadata,
}
}
12 changes: 0 additions & 12 deletions docs/.vuepress/.eslintrc.js

This file was deleted.

Loading

0 comments on commit 55b4f2d

Please sign in to comment.