Skip to content
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

Feature: Adds realtime linting for markdown previewer. #4520

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
84 changes: 84 additions & 0 deletions .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// https://github.com/DavidAnson/markdownlint

{
"config": {
"default": true,
// heading-style
// Enforces ATX style headings, e.g. ### A level 3 heading
"MD003": {
"style": "atx"
},
// ul-style
// Enforces dash syntax for unordered lists
"MD004": {
"style": "dash"
},
// line-length
// Avoids errors related to line lengths in the MD files
"MD013": {
"code_blocks": false,
"headings": false,
"tables": false,
// Arbitrary length - Revisit to possibly set a standard
"line_length": 9999
},
// no-trailing-punctuation
// Prevents most punctuation in headings, except for exclamation and question marks
"MD026": {
"punctuation": ".:,;"
},
// ol-prefix
// Enforces lazy numbering for ordered lists
"MD029": {
"style": "one"
},
// no-inline-html
// Only allows specified HTML to be inline
"MD033": {
"allowed_elements": [
"p",
"a",
"div",
"span",
"kbd",
"script",
"iframe",
"details",
"summary",
"pre"
]
},
// first-line-heading/first-line-h1
// Enforces the first heading on a lesson page to be an H3, since the page template renders an H1 and H2 automatically
"MD041": {
"level": 3
},
// proper-names
// Enforces proper spelling of the names array, except for code blocks and HTML elements
"MD044": {
"code_blocks": false,
"html_elements": false,
"names": ["CSS", "HTML", "JavaScript"]
},
// code-block-style
// Prevents indented code blocks from being used
"MD046": {
"style": "fenced"
},
// code-fence-style
// Enforces backticks for codeblocks
"MD048": {
"style": "backtick"
},
// emphasis-style
// Enforces asterisk syntax instead of underscore syntax
"MD049": {
"style": "asterisk"
},
// strong-style
// Enforces asterisk syntax instead of underscore syntax
"MD050": {
"style": "asterisk"
}
}
}
13 changes: 13 additions & 0 deletions app/components/markdown/preview_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
<%= render ContentContainerComponent.new(classes: 'pt-6') do %>
<% if markdown.present? %>
<%= sanitize MarkdownConverter.new(markdown).as_html, tags: allowed_tags, attributes: allowed_attributes %>

<% if lint.any? %>
<div class="lesson-note lesson-note--critical" markdown="1">
<h4>The linter has detected issues with this preview</h4>
<p>Below are a list of potential issues with your preview. If you intend to create a pull request with this markdown, these warnings may appear later on in the pull request!</p>
</div>

<ul class="list-none">
<% for err in @lint_errors %>
<li class="m-0"><%= inline_svg_tag 'icons/flag.svg', class: '-ml-0.5 mr-2 h-4 w-4 text-orange-500 dark:text-orange-400 inline', aria: true, title: 'Warning' %><%= err %></li>
<% end %>
</ul>
<% end %>
<% else %>
<p>Nothing to preview yet!</p>
<% end %>
Expand Down
14 changes: 14 additions & 0 deletions app/components/markdown/preview_component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
class Markdown::PreviewComponent < ApplicationComponent
def initialize(markdown: '')
@markdown = markdown
@lint_errors = []
end

def lint
File.write('markdown_to_lint.md', @markdown)
results = `markdownlint-cli2 markdown_to_lint.md 2>&1`
File.delete('markdown_to_lint.md')

@lint_errors = results.split("\n").drop(4)
@lint_errors.each_with_index do |err, i|
@lint_errors[i] = err.split.drop(2).join(' ')
end

@lint_errors
end

def allowed_tags
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"eslint": "eslint --max-warnings 0 './app/javascript/**/*.js'",
"build:css": "npm-run-all --parallel \"build:css:* {@}\" --",
"build:css:application": "postcss ./app/assets/stylesheets/application.postcss.css -o ./app/assets/builds/application.css",
"build:css:active_admin": "sass ./app/assets/stylesheets/active_admin.scss ./app/assets/builds/active_admin.css --no-source-map --load-path=node_modules --style=compressed"
"build:css:active_admin": "sass ./app/assets/stylesheets/active_admin.scss ./app/assets/builds/active_admin.css --no-source-map --load-path=node_modules --style=compressed",
"postinstall": "npm install -g markdownlint-cli2"
},
"dependencies": {
"@activeadmin/activeadmin": "^3.2.0",
Expand Down Expand Up @@ -40,6 +41,7 @@
"hint.css": "^3.0.0",
"js-base64": "^3.7.5",
"lodash": "^4.17.21",
"markdownlint-cli2": "^0.13.0",
"mermaid": "^9.4.3",
"mini-css-extract-plugin": "^2.8.1",
"npm-run-all": "^4.1.5",
Expand Down
44 changes: 44 additions & 0 deletions spec/services/markdown/preview_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require 'rails_helper'
require './lib/kramdown/document_sections'

RSpec.describe Markdown::PreviewComponent do
describe '#lint' do
it 'lints the markdown' do
sample_markdown = <<~MARKDOWN
### First section header
some content

### Second section header
some content

### Third section header
some content
MARKDOWN

expect(described_class.new(markdown: sample_markdown).lint.any?).to be true
end

context 'when no issues present' do
it 'lints the markdown and returns an empty array' do
sample_markdown = <<~MARKDOWN
### First section header

some content
MARKDOWN

expect(described_class.new(markdown: sample_markdown).lint.any?).to be false
end
end

context 'when no empty line is present under the header' do
it 'detects it in the linter' do
sample_markdown = <<~MARKDOWN
### First section header
some content
MARKDOWN

expect(described_class.new(markdown: sample_markdown).lint.first).to include 'surrounded by blank lines'
end
end
end
end
77 changes: 73 additions & 4 deletions yarn.lock

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