*/
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);
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),
"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",
+++ /dev/null
-/*
- * 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;
* 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';
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 {
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 });
}
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 (
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;
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)
--- /dev/null
+/*
+ * 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: {} });
+});
--- /dev/null
+/*
+ * 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 };
--- /dev/null
+/*
+ * 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;
+}
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"
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:
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"
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"