Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

[docs] adds optional capability to provide code examples in rules' metadata #3602

Merged
merged 12 commits into from
Apr 19, 2018
238 changes: 238 additions & 0 deletions docs/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
GEM
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be checked in to the repo? I'm not familiar with gem/gemfile

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@suchanlee It looks like the bundler docs suggest checking in the lock file. But full disclosure, I'm no Ruby expert.

remote: https://rubygems.org/
specs:
activesupport (4.2.9)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.7.1)
ruby-enum (~> 0.5)
concurrent-ruby (1.0.5)
ethon (0.11.0)
ffi (>= 1.3.0)
execjs (2.7.0)
faraday (0.13.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.18)
forwardable-extended (2.6.0)
gemoji (3.0.0)
github-pages (172)
activesupport (= 4.2.9)
github-pages-health-check (= 1.3.5)
jekyll (= 3.6.2)
jekyll-avatar (= 0.5.0)
jekyll-coffeescript (= 1.0.2)
jekyll-commonmark-ghpages (= 0.1.3)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.9.2)
jekyll-gist (= 1.4.1)
jekyll-github-metadata (= 2.9.3)
jekyll-mentions (= 1.2.0)
jekyll-optional-front-matter (= 0.3.0)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.2.0)
jekyll-redirect-from (= 0.12.1)
jekyll-relative-links (= 0.5.2)
jekyll-remote-theme (= 0.2.3)
jekyll-sass-converter (= 1.5.0)
jekyll-seo-tag (= 2.3.0)
jekyll-sitemap (= 1.1.1)
jekyll-swiss (= 0.4.0)
jekyll-theme-architect (= 0.1.0)
jekyll-theme-cayman (= 0.1.0)
jekyll-theme-dinky (= 0.1.0)
jekyll-theme-hacker (= 0.1.0)
jekyll-theme-leap-day (= 0.1.0)
jekyll-theme-merlot (= 0.1.0)
jekyll-theme-midnight (= 0.1.0)
jekyll-theme-minimal (= 0.1.0)
jekyll-theme-modernist (= 0.1.0)
jekyll-theme-primer (= 0.5.2)
jekyll-theme-slate (= 0.1.0)
jekyll-theme-tactile (= 0.1.0)
jekyll-theme-time-machine (= 0.1.0)
jekyll-titles-from-headings (= 0.5.0)
jemoji (= 0.8.1)
kramdown (= 1.14.0)
liquid (= 4.0.0)
listen (= 3.0.6)
mercenary (~> 0.3)
minima (= 2.1.1)
rouge (= 2.2.1)
terminal-table (~> 1.4)
github-pages-health-check (1.3.5)
addressable (~> 2.3)
net-dns (~> 0.8)
octokit (~> 4.0)
public_suffix (~> 2.0)
typhoeus (~> 0.7)
html-pipeline (2.7.1)
activesupport (>= 2)
nokogiri (>= 1.4)
i18n (0.9.1)
concurrent-ruby (~> 1.0)
jekyll (3.6.2)
addressable (~> 2.4)
colorator (~> 1.0)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 3)
safe_yaml (~> 1.0)
jekyll-avatar (0.5.0)
jekyll (~> 3.0)
jekyll-coffeescript (1.0.2)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.1.0)
commonmarker (~> 0.14)
jekyll (>= 3.0, < 4.0)
jekyll-commonmark-ghpages (0.1.3)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1)
rouge (~> 2)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.9.2)
jekyll (~> 3.3)
jekyll-gist (1.4.1)
octokit (~> 4.2)
jekyll-github-metadata (2.9.3)
jekyll (~> 3.1)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.2.0)
activesupport (~> 4.0)
html-pipeline (~> 2.3)
jekyll (~> 3.0)
jekyll-optional-front-matter (0.3.0)
jekyll (~> 3.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.2.0)
jekyll (~> 3.0)
jekyll-redirect-from (0.12.1)
jekyll (~> 3.3)
jekyll-relative-links (0.5.2)
jekyll (~> 3.3)
jekyll-remote-theme (0.2.3)
jekyll (~> 3.5)
rubyzip (>= 1.2.1, < 3.0)
typhoeus (>= 0.7, < 2.0)
jekyll-sass-converter (1.5.0)
sass (~> 3.4)
jekyll-seo-tag (2.3.0)
jekyll (~> 3.3)
jekyll-sitemap (1.1.1)
jekyll (~> 3.3)
jekyll-swiss (0.4.0)
jekyll-theme-architect (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.2)
jekyll (~> 3.5)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.2)
jekyll-theme-slate (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.0)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.0)
jekyll (~> 3.3)
jekyll-watch (1.5.1)
listen (~> 3.0)
jemoji (0.8.1)
activesupport (~> 4.0, >= 4.2.9)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (>= 3.0)
kramdown (1.14.0)
liquid (4.0.0)
listen (3.0.6)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9.7)
mercenary (0.3.6)
mini_portile2 (2.3.0)
minima (2.1.1)
jekyll (~> 3.3)
minitest (5.10.3)
multipart-post (2.0.0)
net-dns (0.8.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
octokit (4.8.0)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.1)
forwardable-extended (~> 2.6)
public_suffix (2.0.5)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rouge (2.2.1)
ruby-enum (0.7.1)
i18n
rubyzip (1.2.1)
safe_yaml (1.0.4)
sass (3.5.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (0.8.0)
ethon (>= 0.8.0)
tzinfo (1.2.4)
thread_safe (~> 0.1)
unicode-display_width (1.3.0)

PLATFORMS
ruby

DEPENDENCIES
github-pages

BUNDLED WITH
1.16.1
23 changes: 22 additions & 1 deletion docs/_layouts/rule.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{% if page.descriptionDetails %}
{{page.descriptionDetails | markdownify}}
{% endif %}

{% if page.rationale %}
<h5>Rationale</h5>
{{page.rationale | markdownify}}
Expand All @@ -28,7 +29,7 @@ <h5>Notes: </h5>
<h3>Config</h3>
{{page.optionsDescription | markdownify}}

<h5>Examples</h5>
<h5>Config examples</h5>
{% for example in page.optionExamples %}
<pre>
"{{page.ruleName}}": {{example}}
Expand All @@ -39,3 +40,23 @@ <h5>Schema</h5>
<pre>
{{page.optionsJSON}}
</pre>

{% if page.codeExamples %}
<h3>Code examples: </h3>
{% for codeExample in page.codeExamples %}
<div class="wrapper__code-example">
<h5>{{ codeExample.description }}</h5>
{{ codeExample.config | markdownify }}
<h6 class="feature">Passes</h6>
<div class="code-example">
{{ codeExample.pass | markdownify }}
</div>
{% if codeExample.fail %}
<h6 class="heading-fail">Fails</h6>
<div class="code-example code-example-fail">
{{ codeExample.fail | markdownify }}
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
27 changes: 27 additions & 0 deletions docs/_sass/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,30 @@ figcaption {
}
}
}

.wrapper__code-example {
margin-bottom: 64px;

&:last-of-type {
margin-bottom: 24px;
}

h6.heading-fail {
// Strong red
color: #d14;
}

.code-example {
// Light green
background-color: #f1fff1;

&.code-example-fail {
// Light red
background-color: #fff5f5;
}

pre, .highlight {
background: transparent;
}
}
}
23 changes: 17 additions & 6 deletions scripts/buildDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@

import * as fs from "fs";
import * as glob from "glob";
import stringify = require("json-stringify-pretty-compact");
import * as yaml from "js-yaml";
import stringify = require("json-stringify-pretty-compact");
import * as path from "path";
import * as rimraf from "rimraf";

import {IFormatterMetadata} from "../lib/language/formatter/formatter";
import {IRuleMetadata} from "../lib/language/rule/rule";
import { IFormatterMetadata } from "../lib/language/formatter/formatter";
import { ICodeExample, IRuleMetadata } from "../lib/language/rule/rule";

type Metadata = IRuleMetadata | IFormatterMetadata;

interface Documented {
metadata: Metadata;
};
}

interface IDocumentation {
/**
Expand All @@ -71,7 +71,7 @@ interface IDocumentation {
/**
* Function to generate individual documentation pages.
*/
pageGenerator: (metadata: any) => string;
pageGenerator(metadata: any): string;

/**
* Documentation subdirectory to output to.
Expand Down Expand Up @@ -148,7 +148,7 @@ function buildSingleModuleDocumentation(documentation: IDocumentation, modulePat
// tslint:disable-next-line:no-var-requires
const module = require(modulePath);
const DocumentedItem = module[documentation.exportName] as Documented;
if (DocumentedItem != null && DocumentedItem.metadata != null) {
if (DocumentedItem !== null && DocumentedItem.metadata !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd prefer that it stay as != instead of !==. != null checks for both undefined and null whereas with !== we specify for null, which may not be the desired behavior. But happy to revisit if there's a good reason to change to !==!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this was changed as a result of the doc-building process (a pass with --fix maybe?). Anyway, I would never make these sorts of changes deliberately (in a PR).

// Build the module's page.
const { metadata } = DocumentedItem;
const fileData = documentation.pageGenerator(metadata);
Expand Down Expand Up @@ -195,6 +195,17 @@ function generateRuleFile(metadata: IRuleMetadata): string {
typeof example === "string" ? example : stringify(example));
}

if (metadata.codeExamples) {
metadata.codeExamples = metadata.codeExamples.map((example: ICodeExample) => {
example.pass = `\`\`\`ts\n${example.pass.trim()}\n\`\`\``;
if (example.fail) {
example.fail = `\`\`\`ts\n${example.fail.trim()}\n\`\`\``;
}
example.config = `\`\`\`json\n${example.config.trim()}\n\`\`\``;
return example;
});
}

const yamlData = generateJekyllData(metadata, "rule", "Rule", metadata.ruleName);
yamlData.optionsJSON = JSON.stringify(metadata.options, undefined, 2);
return `---\n${yaml.safeDump(yamlData, {lineWidth: 140} as any)}---`;
Expand Down
12 changes: 12 additions & 0 deletions src/language/rule/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,24 @@ export interface IRuleMetadata {
* Whether or not the rule use for TypeScript only. If `false`, this rule may be used with .js files.
*/
typescriptOnly: boolean;

/**
* Examples demonstrating what the lint rule will pass and fail
*/
codeExamples?: ICodeExample[];
}

export type RuleType = "functionality" | "maintainability" | "style" | "typescript";

export type RuleSeverity = "warning" | "error" | "off";

export interface ICodeExample {
config: string;
description: string;
pass: string;
fail?: string;
}

export interface IOptions {
ruleArguments: any[];
ruleSeverity: RuleSeverity;
Expand Down
Loading