| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- import { RuleParams, RuleOnError } from "markdownlint"
- import * as yaml from "js-yaml"
- export const enforceHeaderStructure = {
- names: ["enforce-header-structure"],
- description: "Proposal header structure should follow template",
- tags: ["structure"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const category: string = frontMatter.category
- if (!category) return
- if (["Meta"].includes(category)) return
- const filtered = params.tokens.filter(function filterToken(token) {
- return (
- token.type === "heading_open" &&
- (token.tag === "h1" || token.tag === "h2")
- )
- })
- let index = 0
- let tempHeadings = expectedHeadings;
- while (index < filtered.length) {
- let token = filtered[index]
- tempHeadings = tempHeadings.filter(item => item !== token.line)
- if (index >= filtered.length) {
- onError({
- lineNumber: 1,
- detail: `Expected heading \`${tempHeadings[0]}\` and none exists. Please follow the structure outlined in the Proposal Template.`,
- })
- return
- } else {
- index++
- }
- }
- tempHeadings.forEach(item => {
- onError({
- lineNumber: 1,
- detail: `Expected heading \`${item}\` and none exists. Please follow the structure outlined in the Proposal Template.`,
- })
- })
- if (tempHeadings.length >= 0) {
- return
- }
- },
- }
- const expectedHeadings = [
- "## Summary",
- "## Motivation",
- "## Alternatives Considered",
- "## New Terminology",
- "## Detailed Design",
- "## Impact",
- "## Security Considerations",
- ]
- export const enforceMetadataStructure = {
- names: ["enforce-front-matter-structure"],
- description:
- "Proposal front matter should be YAML following template structure",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) {
- onError({
- lineNumber: 1,
- detail: `Missing front matter metadata formatted as YAML`,
- })
- return
- }
- Object.keys(requiredMetadata).forEach((meta) => {
- if (!frontMatter[meta]) {
- onError({
- lineNumber: 1,
- detail: `Front matter metadata either doesn't contain \`${meta}\` or isn't formatted correctly`,
- })
- }
- })
- Object.keys(frontMatter).forEach((key) => {
- if (!(requiredMetadata as any)[key] && !optionalMetadata.includes(key)) {
- onError({
- lineNumber: 1,
- detail: `Front matter contains invalid metadata \`${key}\``,
- })
- }
- })
- },
- }
- const requiredMetadata = {
- simd: {},
- title: {},
- authors: {},
- category: {},
- type: {},
- status: {},
- created: {},
- }
- const optionalMetadata = [
- "feature",
- "supersedes",
- "superseded-by",
- "extends",
- "development",
- ]
- export const metadataSimdIsValid = {
- names: ["front-matter-has-simd"],
- description: "Metadata `simd` is a 4 digit numerical string",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const simd: string = frontMatter.simd
- if (!simd) return
- if (isNaN(Number(simd))) {
- onError({
- lineNumber: 1,
- detail: "Front matter `simd` must be a numerical string",
- })
- }
- if (simd.length !== 4) {
- onError({
- lineNumber: 1,
- detail: "Front matter `simd` must be 4 digits",
- })
- }
- },
- }
- export const metadataTitleIsValid = {
- names: ["front-matter-has-title"],
- description:
- "Proposal front matter should include a title no longer than 45 characters",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const title: string = frontMatter.title
- if (!title) return
- if (title.length > 45) {
- onError({
- lineNumber: 1,
- detail: "Metadata `title` should be no longer than 45 characters",
- })
- }
- },
- }
- export const metadataAuthorsIsValid = {
- names: ["front-matter-has-authors"],
- description: "Proposal front matter should include authors",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const authors: string = frontMatter.authors
- if (!authors) return
- if (authors.length == 0) {
- onError({
- lineNumber: 1,
- detail: "Metadata `authors` exists but doesn't include any values",
- })
- }
- },
- }
- export const metadataCategoryIsValid = {
- names: ["front-matter-has-valid-category"],
- description: "Proposal front matter should have a valid category",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const category: string = frontMatter.category
- if (!category) return
- if (!["Meta", "Standard"].includes(category)) {
- onError({
- lineNumber: 1,
- detail: `\`${category}\` is not supported as a value for category`,
- })
- }
- },
- }
- export const metadataTypeIsValid = {
- names: ["front-matter-has-valid-type"],
- description: "Proposal front matter should have a valid type",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const type: string = frontMatter.type
- if (!type) return
- const validTypes = ["Core", "Networking", "Interface", "Meta"]
- if (!validTypes.some((validType) => type.includes(validType))) {
- onError({
- lineNumber: 1,
- detail: `\`${type}\` is not supported as a value for type. Valid values for type are: ${validTypes.join(
- ", "
- )}`,
- })
- }
- },
- }
- export const metadataStatusIsValid = {
- names: ["front-matter-has-valid-status"],
- description: "Proposal front matter should have a valid status",
- tags: ["front-matter"],
- function: function rule(params: RuleParams, onError: RuleOnError) {
- const string = params.frontMatterLines
- .join("\n")
- .trim()
- .replace(/^-*$/gm, "")
- const frontMatter: any = yaml.load(string)
- if (!frontMatter) return
- const status: string = frontMatter.status
- if (!status) return
- const validStatus = [
- "Idea",
- "Review",
- "Accepted",
- "Stagnant",
- "Withdrawn",
- "Implemented",
- "Activated",
- "Living"
- ]
- if (!validStatus.includes(status)) {
- onError({
- lineNumber: 1,
- detail: `\`${status}\` is not supported as a value for status. Valid values for status are: ${validStatus.join(
- ", "
- )}`,
- })
- }
- },
- }
|