From: Stas Vilchik Date: Mon, 28 May 2018 10:05:03 +0000 (+0200) Subject: get rid of gray-matter (#280) X-Git-Tag: 7.5~1133 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9819fb14345f1e57c9c1e8f59ec6cda6fe367750;p=sonarqube.git get rid of gray-matter (#280) --- diff --git a/server/sonar-web/config/documentation-loader/fetch-matter.js b/server/sonar-web/config/documentation-loader/fetch-matter.js index 424d491d948..806332edb2a 100644 --- a/server/sonar-web/config/documentation-loader/fetch-matter.js +++ b/server/sonar-web/config/documentation-loader/fetch-matter.js @@ -19,7 +19,7 @@ */ const fs = require('fs'); const path = require('path'); -const matter = require('gray-matter'); +const { getFrontMatter } = require('../../src/main/js/helpers/markdown'); const compare = (a, b) => { if (a.order === b.order) return a.title.localeCompare(b.title); @@ -32,7 +32,7 @@ module.exports = (root, files) => { return files .map(file => { const content = fs.readFileSync(root + '/' + file, 'utf8'); - const headerData = matter(content).data; + const headerData = getFrontMatter(content); return { name: path.basename(file).slice(0, -3), relativeName: file.slice(0, -3), diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 283b02f948d..fbf2659e4b7 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -16,7 +16,6 @@ "d3-shape": "1.2.0", "date-fns": "1.29.0", "formik": "0.11.11", - "gray-matter": "4.0.1", "history": "3.3.0", "intl-relativeformat": "2.1.0", "keymaster": "1.6.2", diff --git a/server/sonar-web/src/main/js/@types/gray-matter.d.ts b/server/sonar-web/src/main/js/@types/gray-matter.d.ts deleted file mode 100644 index ee5cb17e576..00000000000 --- a/server/sonar-web/src/main/js/@types/gray-matter.d.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -/** - * Takes a string or object with `content` property, extracts - * and parses front-matter from the string, then returns an object - * with `data`, `content` and other [useful properties](#returned-object). - * - * ```js - * var matter = require('gray-matter'); - * console.log(matter('---\ntitle: Home\n---\nOther stuff')); - * //=> { data: { title: 'Home'}, content: 'Other stuff' } - * ``` - * @param {Object|String} `input` String, or object with `content` string - * @param {Object} `options` - * @return {Object} - * @api public - */ -declare function matter>( - input: I | { content: I }, - options?: O -): matter.GrayMatterFile; - -declare namespace matter { - type Input = string | Buffer; - interface GrayMatterOption> { - parser?: () => void; - // eslint-disable-next-line no-eval - eval?: boolean; - excerpt?: boolean | ((input: I, options: O) => string); - excerpt_separator?: string; - engines?: { - [index: string]: - | ((input: string) => object) - | { parse: (input: string) => object; stringify?: (data: object) => string }; - }; - language?: string; - delimiters?: string | [string, string]; - } - interface GrayMatterFile { - data: { [key: string]: any }; - content: string; - excerpt?: string; - orig: Buffer | I; - language: string; - matter: string; - stringify(lang: string): string; - } - - /** - * Stringify an object to YAML or the specified language, and - * append it to the given string. By default, only YAML and JSON - * can be stringified. See the [engines](#engines) section to learn - * how to stringify other languages. - * - * ```js - * console.log(matter.stringify('foo bar baz', {title: 'Home'})); - * // results in: - * // --- - * // title: Home - * // --- - * // foo bar baz - * ``` - * @param {String|Object} `file` The content string to append to stringified front-matter, or a file object with `file.content` string. - * @param {Object} `data` Front matter to stringify. - * @param {Object} `options` [Options](#options) to pass to gray-matter and [js-yaml]. - * @return {String} Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string. - */ - export function stringify>( - file: string | { content: string }, - data: object, - options?: GrayMatterOption - ): string; - - /** - * Synchronously read a file from the file system and parse - * front matter. Returns the same object as the [main function](#matter). - * - * ```js - * var file = matter.read('./content/blog-post.md'); - * ``` - * @param {String} `filepath` file path of the file to read. - * @param {Object} `options` [Options](#options) to pass to gray-matter. - * @return {Object} Returns [an object](#returned-object) with `data` and `content` - */ - export function read>( - fp: string, - options?: GrayMatterOption - ): matter.GrayMatterFile; - - /** - * Returns true if the given `string` has front matter. - * @param {String} `string` - * @param {Object} `options` - * @return {Boolean} True if front matter exists. - */ - export function test>( - str: string, - options?: GrayMatterOption - ): boolean; - - /** - * Detect the language to use, if one is defined after the - * first front-matter delimiter. - * @param {String} `string` - * @param {Object} `options` - * @return {Object} Object with `raw` (actual language string), and `name`, the language with whitespace trimmed - */ - export function language>( - str: string, - options?: GrayMatterOption - ): { name: string; raw: string }; -} - -export = matter; diff --git a/server/sonar-web/src/main/js/apps/documentation/components/App.tsx b/server/sonar-web/src/main/js/apps/documentation/components/App.tsx index 05ee09a677f..cec2a0e8251 100644 --- a/server/sonar-web/src/main/js/apps/documentation/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/documentation/components/App.tsx @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import * as matter from 'gray-matter'; import Helmet from 'react-helmet'; import { Link } from 'react-router'; import * as PropTypes from 'prop-types'; @@ -28,6 +27,7 @@ import ScreenPositionHelper from '../../../components/common/ScreenPositionHelpe import DocMarkdownBlock from '../../../components/docs/DocMarkdownBlock'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; +import { getFrontMatter } from '../../../helpers/markdown'; import '../styles.css'; interface Props { @@ -71,8 +71,8 @@ export default class App extends React.PureComponent { import(`Docs/pages/${path === '' ? 'index' : path}.md`).then( ({ default: content }) => { if (this.mounted) { - const parsed = matter(content || ''); - if (parsed.data.scope === 'sonarcloud' && !this.context.onSonarCloud) { + const { scope } = getFrontMatter(content || ''); + if (scope === 'sonarcloud' && !this.context.onSonarCloud) { this.setState({ loading: false, notFound: true }); } else { this.setState({ content, loading: false, notFound: false }); @@ -108,7 +108,7 @@ export default class App extends React.PureComponent { } render() { - const pageTitle = matter(this.state.content || '').data.title; + const pageTitle = getFrontMatter(this.state.content || '').title; const mainTitle = translate('documentation.page'); const isIndex = !this.props.params.splat || this.props.params.splat === ''; return ( diff --git a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx index 8d3ae04e71d..384f0339d78 100644 --- a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx @@ -21,11 +21,11 @@ import * as React from 'react'; import * as classNames from 'classnames'; import remark from 'remark'; import reactRenderer from 'remark-react'; -import * as matter from 'gray-matter'; import * as PropTypes from 'prop-types'; import DocLink from './DocLink'; import DocParagraph from './DocParagraph'; import DocImg from './DocImg'; +import { separateFrontMatter } from '../../helpers/markdown'; interface Props { className?: string; @@ -40,10 +40,10 @@ export default class DocMarkdownBlock extends React.PureComponent { render() { const { className, content, displayH1 } = this.props; - const parsed = matter(content || ''); + const parsed = separateFrontMatter(content || ''); return (
- {displayH1 &&

{parsed.data.title}

} + {displayH1 &&

{parsed.frontmatter.title}

} { remark() // .use(remarkInclude) diff --git a/server/sonar-web/src/main/js/helpers/__tests__/markdown-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/markdown-test.ts new file mode 100644 index 00000000000..2e1db47c92e --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/markdown-test.ts @@ -0,0 +1,107 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { getFrontMatter, separateFrontMatter } from '../markdown'; + +it('returns parsed frontmatter of one item', () => { + expect( + getFrontMatter(` + --- + title: Foo + --- + + some content here + `) + ).toEqual({ title: 'Foo' }); +}); + +it('returns parsed frontmatter of two items', () => { + expect( + getFrontMatter(` + --- + title: Foo + scope: sonarcloud + --- + + some content here + `) + ).toEqual({ title: 'Foo', scope: 'sonarcloud' }); +}); + +it('returns empty object when frontmatter is missing', () => { + expect( + getFrontMatter(` + some content here + `) + ).toEqual({}); +}); + +it('returns empty object when frontmatter is unfinished', () => { + expect( + getFrontMatter(` + --- + title: Foo + + some content here + `) + ).toEqual({}); +}); + +it('ignores frontmatter in wrong format', () => { + expect( + getFrontMatter(` + --- + title: Foo + scope: sonarcloud: sonarqube + --- + + some content here + `) + ).toEqual({ title: 'Foo' }); +}); + +it('returns parsed frontmatter and the rest of the content', () => { + expect( + separateFrontMatter(` +--- +title: Foo +--- + +some content here`) + ).toEqual({ content: '\nsome content here', frontmatter: { title: 'Foo' } }); +}); + +it('returns empty object and content when frontmatter is missing', () => { + expect(separateFrontMatter('some content here')).toEqual({ + content: 'some content here', + frontmatter: {} + }); +}); + +it('returns full content when frontmatter has bad formatting', () => { + const content = ` + ---- + title: Foo + scope: sonarcloud + --- + + some content here`; + + expect(separateFrontMatter(content)).toEqual({ content, frontmatter: {} }); +}); diff --git a/server/sonar-web/src/main/js/helpers/markdown.d.ts b/server/sonar-web/src/main/js/helpers/markdown.d.ts new file mode 100644 index 00000000000..83116b7b6eb --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/markdown.d.ts @@ -0,0 +1,26 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +interface FrontMatter { + [x: string]: string; +} + +export function getFrontMatter(content: string): FrontMatter; + +export function separateFrontMatter(content: string): { content: string; frontmatter: FrontMatter }; diff --git a/server/sonar-web/src/main/js/helpers/markdown.js b/server/sonar-web/src/main/js/helpers/markdown.js new file mode 100644 index 00000000000..b13db15e2fe --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/markdown.js @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// keep this file in JavaScript, because it is used by a webpack loader +module.exports = { getFrontMatter, separateFrontMatter }; + +function getFrontMatter(content) { + const lines = content.split('\n'); + const position = getFrontMatterPosition(lines); + return position ? parseFrontMatter(lines.slice(position.firstLine + 1, position.lastLine)) : {}; +} + +function separateFrontMatter(content) { + const lines = content.split('\n'); + const position = getFrontMatterPosition(lines); + if (position) { + const frontmatter = parseFrontMatter(lines.slice(position.firstLine + 1, position.lastLine)); + const content = lines.slice(position.lastLine + 1).join('\n'); + return { frontmatter, content }; + } else { + return { frontmatter: {}, content }; + } +} + +function getFrontMatterPosition(lines) { + let firstLine; + let lastLine; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.trim() === '---') { + if (firstLine === undefined) { + firstLine = i; + } else { + lastLine = i; + break; + } + } + } + return lastLine !== undefined ? { firstLine, lastLine } : undefined; +} + +function parseFrontMatter(lines) { + const data = {}; + for (let i = 0; i < lines.length; i++) { + const tokens = lines[i].split(':').map(x => x.trim()); + if (tokens.length === 2) { + data[tokens[0]] = tokens[1]; + } + } + return data; +} diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index ca97ba90123..3fa52d493cc 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -3608,15 +3608,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -gray-matter@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.1.tgz#375263c194f0d9755578c277e41b1c1dfdf22c7d" - dependencies: - js-yaml "^3.11.0" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" - growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -4803,7 +4794,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.11.0, js-yaml@^3.4.3, js-yaml@^3.7.0, js-yaml@^3.9.1: +js-yaml@^3.4.3, js-yaml@^3.7.0, js-yaml@^3.9.1: version "3.11.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" dependencies: @@ -7331,13 +7322,6 @@ schema-utils@^0.4.0, schema-utils@^0.4.4, schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -7811,10 +7795,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - strip-bom@3.0.0, strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"