]> source.dussan.org Git - sonarqube.git/commitdiff
get rid of gray-matter (#280)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 28 May 2018 10:05:03 +0000 (12:05 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 28 May 2018 18:20:43 +0000 (20:20 +0200)
server/sonar-web/config/documentation-loader/fetch-matter.js
server/sonar-web/package.json
server/sonar-web/src/main/js/@types/gray-matter.d.ts [deleted file]
server/sonar-web/src/main/js/apps/documentation/components/App.tsx
server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx
server/sonar-web/src/main/js/helpers/__tests__/markdown-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/markdown.d.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/markdown.js [new file with mode: 0644]
server/sonar-web/yarn.lock

index 424d491d94866ddc2339ee20fd0a056b333aa388..806332edb2a9a5e6ea26f9da643cbd7188de3c2d 100644 (file)
@@ -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),
index 283b02f948d59ac2c9da291c92d51d58221dab08..fbf2659e4b763356ed03aa6cb45de2ba2727089d 100644 (file)
@@ -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 (file)
index ee5cb17..0000000
+++ /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<I extends matter.Input, O extends matter.GrayMatterOption<I, O>>(
-  input: I | { content: I },
-  options?: O
-): matter.GrayMatterFile<I>;
-
-declare namespace matter {
-  type Input = string | Buffer;
-  interface GrayMatterOption<I extends Input, O extends GrayMatterOption<I, O>> {
-    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<I extends Input> {
-    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<O extends GrayMatterOption<string, O>>(
-    file: string | { content: string },
-    data: object,
-    options?: GrayMatterOption<string, O>
-  ): 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<O extends GrayMatterOption<string, O>>(
-    fp: string,
-    options?: GrayMatterOption<string, O>
-  ): matter.GrayMatterFile<string>;
-
-  /**
-   * 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<O extends matter.GrayMatterOption<string, O>>(
-    str: string,
-    options?: GrayMatterOption<string, O>
-  ): 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<O extends matter.GrayMatterOption<string, O>>(
-    str: string,
-    options?: GrayMatterOption<string, O>
-  ): { name: string; raw: string };
-}
-
-export = matter;
index 05ee09a677f28ce3723227ace92868daa70709a5..cec2a0e8251c2985e6363436a8cad5dffa681597 100644 (file)
@@ -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<Props, State> {
     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<Props, State> {
   }
 
   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 (
index 8d3ae04e71d82e673a9a8251a1dc0c0bc6c3a0a0..384f0339d78b37a729ddd5e01b9a245e1834abf4 100644 (file)
@@ -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<Props> {
 
   render() {
     const { className, content, displayH1 } = this.props;
-    const parsed = matter(content || '');
+    const parsed = separateFrontMatter(content || '');
     return (
       <div className={classNames('markdown', className)}>
-        {displayH1 && <h1>{parsed.data.title}</h1>}
+        {displayH1 && <h1>{parsed.frontmatter.title}</h1>}
         {
           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 (file)
index 0000000..2e1db47
--- /dev/null
@@ -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 (file)
index 0000000..83116b7
--- /dev/null
@@ -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 (file)
index 0000000..b13db15
--- /dev/null
@@ -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;
+}
index ca97ba90123934a70dd3b065f1c8c03bd5b5c094..3fa52d493cc3a48aa4e1a7a1d520b818a55f74b8 100644 (file)
@@ -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"