Skip to content

Commit

Permalink
Add astro/no-set-html-directive rule (#5)
Browse files Browse the repository at this point in the history
* Add astro/no-set-html-directive rule

* Upadte site

* update

* update doc
  • Loading branch information
ota-meshi committed May 23, 2022
1 parent fcb3355 commit 6ec7b69
Show file tree
Hide file tree
Showing 36 changed files with 505 additions and 134 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
/node_modules
!/.vscode
!/.github
/tests/fixtures/rules/*/invalid/template-attr-*.astro
/tests/fixtures/rules/*/invalid/template-attr-*.astro
/docs-build/src/md
/docs-build/src/pages
/docs-build/README.md
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,13 @@ The `--fix` option on the [command line](https://eslint.org/docs/user-guide/comm

<!--RULES_TABLE_START-->

*No rules have been provided yet.*
## Security Vulnerability

These rules relate to security vulnerabilities in Astro component code:

| Rule ID | Description | |
|:--------|:------------|:---|
| [astro/no-set-html-directive](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-set-html-directive/) | disallow use of `set:html` to prevent XSS attack | |

<!--RULES_TABLE_END-->
<!--RULES_SECTION_END-->
Expand Down
13 changes: 12 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineConfig } from "astro/config"
import svelte from "@astrojs/svelte"
import emoji from "remark-emoji"
import gfm from "remark-gfm"
import replaceLink from "./docs-build/remark-replace-link.mjs"
import "./docs-build/setup-docs.mjs"
import path from "path"
Expand All @@ -17,7 +18,17 @@ export default defineConfig({
root: dirname,
integrations: [svelte()],
markdown: {
remarkPlugins: [emoji, replaceLink],
remarkPlugins: [
emoji,
gfm,
[
replaceLink,
{
srcDir: "./docs-build/src",
base: "/eslint-plugin-astro",
},
],
],
},
vite: {
server: {
Expand Down
33 changes: 29 additions & 4 deletions docs-build/remark-replace-link.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
import { visit } from "unist-util-visit"
import path from "path"

export default () => {
return (tree) => {
visit(tree, "link", (node) => {
node.url = node.url.replace(/\.md$/u, "/")
export default (options = {}) => {
const base =
// eslint-disable-next-line no-process-env -- ignore
(process?.env?.NODE_ENV === "production" ? options.base : "") || ""
return (tree, file) => {
const srcDir = path.resolve(
file.cwd,
options.srcDir ? options.srcDir : "./src",
)
const pagesDir = path.join(srcDir, "pages")
let markdownPath
visit(tree, "html", (node, _i, parent) => {
if (node.value.startsWith("<!-- markdownPath:")) {
markdownPath = node.value.slice(18, -3).trim()
parent.children.splice(parent.children.indexOf(node), 1)
}
})
if (markdownPath) {
visit(tree, "link", (node) => {
if (node.url.startsWith(".")) {
const linkPath = path.resolve(path.dirname(markdownPath), node.url)
const relativeLinkPath = path.relative(pagesDir, linkPath)
const absoluteLinkPath = path.join(base, relativeLinkPath)
node.url = `/${absoluteLinkPath
.replace(/^\//u, "")
.replace(/\.md$/u, "/")}`
}
})
}
return tree
}
}
63 changes: 22 additions & 41 deletions docs-build/setup-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from "fs"
import path from "path"
import { URL } from "url"
import "./build-system/build.js"
import { load, dump } from "js-yaml"

const dirname = path.dirname(new URL(import.meta.url).pathname)
setupDocs()
Expand All @@ -16,50 +17,30 @@ function setupDocs() {
}
const docsDir = path.resolve(dirname, "../docs")
for (const md of listup(docsDir, ".md")) {
const from = (
md.endsWith("README.md") ? md.replace(/README.md$/u, "index.md") : md
).replace(/\.md$/u, ".astro")
const to = path.resolve(buildDocsDir, path.relative(docsDir, from))
const to = path.resolve(
buildDocsDir,
path.relative(
docsDir,
md.endsWith("README.md") ? md.replace(/README.md$/u, "index.md") : md,
),
)
mkDirs(path.dirname(to))

if (md.endsWith("/playground.md")) {
fs.writeFileSync(
to,
`---
import ESLintPlayground from '../components/ESLintPlaygroundWrap.astro'
import MainLayout from '../layouts/MainLayout.astro'
const content = {
title: 'Playground',
astro: { headers: [] }
}
---
<MainLayout {content} hiddenRight>
<ESLintPlayground />
</MainLayout>
`,
"utf8",
)
continue
}
const content = fs.readFileSync(md, "utf8")
const frontmatter = /^---\n([\s\S]*?)\n---\n/u.exec(content)?.[1]
const data = frontmatter ? load(frontmatter) : {}
data.layout = `./${path.relative(path.dirname(to), mainLayoutPath)}`
data.markdownPath = `${to}`
const newFrontmatter = `---
${dump(data)}---
fs.writeFileSync(
to,
`---
import * as all from '${path.relative(path.dirname(to), md)}'
import MainLayout from '${path.relative(path.dirname(to), mainLayoutPath)}'
const content = await all.getHeaders().then(headers=>{
return {
astro: {headers}
}
})
const Md = all.Content
---
<MainLayout {content}>
<Md />
</MainLayout>
`,
"utf8",
)
<!-- markdownPath: ${to} -->
`
const pageContent = frontmatter
? content.replace(/^---\n[\s\S]*?\n---\n/u, newFrontmatter)
: `${newFrontmatter}\n${content}`

fs.writeFileSync(to, pageContent, "utf8")
}
}

Expand Down
94 changes: 94 additions & 0 deletions docs-build/src/components/ESLintCodeBlock.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<script>
import { onMount } from "svelte"
import ESLintEditor from "./eslint/ESLintEditor.svelte"
import { loadMonacoEditor } from "./eslint/scripts/monaco-loader.mjs"
import {
createLinter,
preprocess,
postprocess,
} from "./eslint/scripts/linter.mjs"
const linter = loadMonacoEditor()
.then(async () => {
const parser = await import("@typescript-eslint/parser")
if (typeof window !== "undefined") {
window.require.define("@typescript-eslint/parser", parser)
}
return window.waitSetupForAstroCompilerWasm
})
.then(() => {
return createLinter()
})
let code = ""
export let rules = {}
export let fix = false
let time = ""
let options = {
filename: "example.astro",
preprocess,
postprocess,
}
let showDiff = fix
function onLintedResult(evt) {
time = `${evt.detail.time}ms`
}
let slotRoot
onMount(() => {
code = slotRoot.textContent.trim()
})
$: blockHeight = `${Math.max(
120,
20 * (1 + code.split("\n").length) + 100,
)}px`
</script>

<div class="eslint-code-block-default-content" bind:this={slotRoot}>
<slot />
</div>

<div class="eslint-code-block-root" style:height={blockHeight}>
<ESLintEditor
{linter}
bind:code
config={{
parser: "astro-auto-eslint-parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
parser: "@typescript-eslint/parser",
},
rules,
env: {
browser: true,
es2021: true,
},
}}
{options}
on:result={onLintedResult}
showDiff={showDiff && fix}
/>
<div class="eslint-code-block-tools">
{#if fix}
<label>
<input bind:checked={showDiff} type="checkbox" />
Show Diff
</label>
{/if}
<span style:margin-left="16px">{time}</span>
</div>
</div>

<style>
.eslint-code-block-default-content {
display: none;
}
.eslint-code-block-root {
height: 300px;
}
.eslint-code-block-tools {
text-align: end;
}
</style>
6 changes: 6 additions & 0 deletions docs-build/src/components/ESLintCodeBlockWrap.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
import ESLintCodeBlock from './ESLintCodeBlock.svelte'
---
<ESLintCodeBlock client:only="svelte" >
<slot></slot>
</ESLintCodeBlock>
2 changes: 1 addition & 1 deletion docs-build/src/components/ESLintPlaygroundWrap.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
import ESLintPlayground from './ESLintPlayground.svelte'
---
<ESLintPlayground client:only />
<ESLintPlayground client:only="svelte" />
10 changes: 2 additions & 8 deletions docs-build/src/components/LeftSidebar/LeftSidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,7 @@ const sidebarSections = SIDEBAR.en.reduce((col, item, i) => {
<li>
<div class="nav-group">
<h2 class="nav-group-title">
{section.link
? <a href={`${baseUrl}${section.link}`} aria-current={`${currentPageMatch === section.link ? 'page' : 'false'}`} >
{section.text}
</a>
: section.text
}
{section.text}
</h2>
<ul>
{section.children.map((child) => (
Expand Down Expand Up @@ -97,8 +92,7 @@ const sidebarSections = SIDEBAR.en.reduce((col, item, i) => {
margin: 1px;
padding: 0.3rem 1rem;
}
.nav-link a,
.nav-group-title a
.nav-link a
{
font: inherit;
color: inherit;
Expand Down
13 changes: 8 additions & 5 deletions docs-build/src/components/PageContent/PageContent.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
import MoreMenu from '../RightSidebar/MoreMenu.astro';
import TableOfContents from '../RightSidebar/TableOfContents.svelte';
const { content, githubEditUrl, hiddenRight } = Astro.props;
const { content, githubEditUrl, hiddenRight, hiddenTOC } = Astro.props;
const title = content.title;
const headers = content.astro.headers;
---

<article id="article" class:list={["content", { 'hidden-right': hiddenRight}]}>
<article id="article" class:list={["content", { 'hidden-right': hiddenRight, hiddenTOC}]}>
<section class="main-section">
<h1 class="content-title" id="overview">{title}</h1>
<nav class="block sm:hidden">
<TableOfContents client:media="(max-width: 50em)" {headers} />
</nav>
{
!hiddenTOC &&
<nav class="block sm:hidden">
<TableOfContents client:media="(max-width: 50em)" {headers} />
</nav>
}
<slot />
</section>
<nav class="block sm:hidden">
Expand Down
4 changes: 2 additions & 2 deletions docs-build/src/components/RightSidebar/RightSidebar.astro
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
import TableOfContents from './TableOfContents.svelte';
import MoreMenu from './MoreMenu.astro';
const { content, githubEditUrl } = Astro.props;
const { content, githubEditUrl, hiddenTOC } = Astro.props;
const headers = content.astro.headers;
---

<nav class="sidebar-nav" aria-labelledby="grid-right">
<div class="sidebar-nav-inner">
<TableOfContents client:media="(min-width: 50em)" {headers} />
{!hiddenTOC && <TableOfContents client:media="(min-width: 50em)" {headers} />}
<MoreMenu editHref={githubEditUrl} />
</div>
</nav>
Expand Down
31 changes: 29 additions & 2 deletions docs-build/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
import { rules } from "../../src/utils/rules"

const categories = [
"Possible Errors",
"Security Vulnerability",
"Best Practices",
"Stylistic Issues",
"Extension Rules",
"System",
] as const

export const SITE = {
title: "eslint-plugin-astro",
description: "ESLint plugin for Astro component.",
Expand Down Expand Up @@ -33,7 +44,23 @@ export const SIDEBAR = {
{ text: "Introduction", link: "" },
{ text: "User Guide", link: "user-guide/" },
{ text: "Demo", link: "playground/" },

{ text: "Rules", header: true, link: "rules/" },
{ text: "Rules", link: "rules/" },
...categories.flatMap((category) => {
const categoryRules = rules.filter(
(rule) => rule.meta.docs.category === category,
)
if (!categoryRules.length) {
return []
}
return [
{ text: category, header: true },
...categoryRules.map((rule) => {
return {
text: rule.meta.docs.ruleId,
link: `rules/${rule.meta.docs.ruleName}/`,
}
}),
]
}),
],
}

0 comments on commit 6ec7b69

Please sign in to comment.