@@ -301,34 +301,17 @@ Note that an iframe is **not** a self-closing tag. This means that the following | |||
``` | |||
#### Dynamic Plugin/Scanner Version Info | |||
#### Dynamic Scanner Version Info | |||
You can dynamically include a plugin/scanner version block to any page, using the following special tag: | |||
For static documentation, use: | |||
```html | |||
<!-- update_center:{PLUGIN/SCANNER}_KEY --> | |||
``` | |||
For embedded documentation, use: | |||
```html | |||
<update-center updatecenterkey="{PLUGIN/SCANNER}_KEY"></update-center> | |||
``` | |||
For example, for Sonar Java, use: | |||
You can dynamically include a scanner version block to any page, using the following special tag: | |||
```html | |||
<!-- update_center:java --> | |||
or | |||
<update-center updatecenterkey="java"></update-center> | |||
<update-center updatecenterkey="SCANNER_KEY"></update-center> | |||
``` | |||
For gradle's scanner, use: | |||
For example, for gradle's scanner, use: | |||
```html | |||
<!-- update_center:sonargradle --> | |||
or | |||
<update-center updatecenterkey="sonargradle"></update-center> | |||
``` | |||
@@ -15,12 +15,14 @@ | |||
"gatsby-remark-custom-blocks": "2.1.27", | |||
"gatsby-source-filesystem": "2.1.57", | |||
"gatsby-transformer-remark": "2.6.59", | |||
"hast-util-select": "4.0.0", | |||
"lodash": "4.17.15", | |||
"lunr": "2.3.8", | |||
"react": "16.13.0", | |||
"react-dom": "16.13.0", | |||
"react-helmet": "5.2.1", | |||
"react-typography": "0.16.19", | |||
"rehype-react": "6.1.0", | |||
"sonar-ui-common": "1.0.22", | |||
"typography": "0.16.19" | |||
}, | |||
@@ -33,6 +35,7 @@ | |||
"@types/react": "16.8.23", | |||
"@types/react-dom": "16.8.4", | |||
"@types/react-helmet": "5.0.15", | |||
"@types/rehype-react": "4.0.0", | |||
"@typescript-eslint/parser": "2.6.0", | |||
"babel-jest": "25.1.0", | |||
"enzyme": "3.11.0", |
@@ -0,0 +1,27 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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 { HtmlAST, HtmlASTNode } from '../types/hast'; | |||
declare module 'hast-util-select' { | |||
export function matches(selector: string, ode: HtmlASTNode): boolean; | |||
export function selectAll(selector: string, tree: HtmlAST): HtmlASTNode[]; | |||
export function select(selector: string, tree: HtmlAST): HtmlASTNode; | |||
} |
@@ -1,132 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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 * as React from 'react'; | |||
import { createPortal } from 'react-dom'; | |||
import MetaData from 'sonar-ui-common/components/ui/update-center/MetaData'; | |||
import { Dict } from '../@types/types'; | |||
interface Props { | |||
location: Pick<Location, 'pathname'>; | |||
} | |||
interface State { | |||
wrappers: Dict<HTMLDivElement>; | |||
} | |||
export default class UpdateCenterMetaDataInjector extends React.Component<Props, State> { | |||
state: State = { | |||
wrappers: {} | |||
}; | |||
componentDidMount() { | |||
this.searchForMetaData(); | |||
} | |||
componentDidUpdate({ location }: Props) { | |||
if (location.pathname !== this.props.location.pathname) { | |||
this.clearMetaData(); | |||
this.searchForMetaData(); | |||
} | |||
} | |||
componentWillUnmount() { | |||
this.clearMetaData(); | |||
} | |||
clearMetaData = () => { | |||
const { wrappers } = this.state; | |||
Object.keys(wrappers).forEach(key => { | |||
const node = wrappers[key]; | |||
const { parentNode } = node; | |||
if (parentNode) { | |||
parentNode.removeChild(node); | |||
} | |||
delete wrappers[key]; | |||
}); | |||
this.setState({ wrappers: {} }); | |||
}; | |||
searchForMetaData = () => { | |||
const pageContainer = document.querySelector('.page-container'); | |||
if (!pageContainer) { | |||
return; | |||
} | |||
// The following uses an older syntax for createNodeIterator() in order | |||
// to support IE11 | |||
// - IE doesn't support the new { acceptNode: (node: Node) => number } | |||
// format for the 3rd parameter, and instead expects to get it passed | |||
// the function directly. Modern browsers support both paradigms as a | |||
// fallback, so we fallback to the old one. | |||
// - IE11 requires the 4th argument. | |||
// @ts-ignore: tsc requires an additional comment at the function call. | |||
const iterator = document.createNodeIterator( | |||
pageContainer, | |||
NodeFilter.SHOW_COMMENT, | |||
// @ts-ignore: IE11 doesn't support the { acceptNode: () => number } format. | |||
(_: Node) => NodeFilter.FILTER_ACCEPT, | |||
// @ts-ignore: IE11 requires the 4th argument. | |||
false | |||
); | |||
const wrappers: Dict<HTMLDivElement> = {}; | |||
let node = iterator.nextNode(); | |||
while (node) { | |||
if (node.nodeValue && node.parentNode && /update_center\s*:/.test(node.nodeValue)) { | |||
let [, key] = node.nodeValue.split(':'); | |||
key = key.trim(); | |||
const wrapper = document.createElement('div'); | |||
wrappers[key] = wrapper; | |||
node.parentNode.insertBefore(wrapper, node); | |||
} | |||
node = iterator.nextNode(); | |||
} | |||
this.setState({ wrappers }); | |||
}; | |||
render() { | |||
const { wrappers } = this.state; | |||
const keys = Object.keys(wrappers); | |||
if (keys.length === 0) { | |||
return null; | |||
} | |||
return ( | |||
<div> | |||
{keys.map(key => { | |||
if (wrappers[key]) { | |||
return createPortal(<MetaData updateCenterKey={key} />, wrappers[key]); | |||
} else { | |||
return null; | |||
} | |||
})} | |||
</div> | |||
); | |||
} | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import UpdateCenterMetaDataInjector from '../UpdateCenterMetaDataInjector'; | |||
it('should render correctly', () => { | |||
(global as any).document.body.innerHTML = ` | |||
<div class="page-container"> | |||
<p>Lorem ipsum</p> | |||
<!-- update_center:java --> | |||
<p>Dolor sit amet</p> | |||
<!-- update_center : python --> | |||
<p>Foo Bar</p> | |||
<!--update_center : abap--> | |||
</div> | |||
`; | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
(global as any).document.body.innerHTML = ` | |||
<div class="page-container"> | |||
<p>Lorem ipsum</p> | |||
<!-- update_center:csharp --> | |||
<p>Foo Bar</p> | |||
</div> | |||
`; | |||
wrapper.setProps({ location: { pathname: 'foo2' } }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<UpdateCenterMetaDataInjector['props']> = {}) { | |||
return shallow<UpdateCenterMetaDataInjector>( | |||
<UpdateCenterMetaDataInjector location={{ pathname: 'foo' }} {...props} /> | |||
); | |||
} |
@@ -1,39 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div> | |||
<Portal | |||
containerInfo={<div />} | |||
> | |||
<MetaData | |||
updateCenterKey="java" | |||
/> | |||
</Portal> | |||
<Portal | |||
containerInfo={<div />} | |||
> | |||
<MetaData | |||
updateCenterKey="python" | |||
/> | |||
</Portal> | |||
<Portal | |||
containerInfo={<div />} | |||
> | |||
<MetaData | |||
updateCenterKey="abap" | |||
/> | |||
</Portal> | |||
</div> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<div> | |||
<Portal | |||
containerInfo={<div />} | |||
> | |||
<MetaData | |||
updateCenterKey="csharp" | |||
/> | |||
</Portal> | |||
</div> | |||
`; |
@@ -24,7 +24,6 @@ import Footer from '../components/Footer'; | |||
import HeaderListProvider from '../components/HeaderListProvider'; | |||
import HeadingsLink from '../components/HeadingsLink'; | |||
import Sidebar from '../components/Sidebar'; | |||
import UpdateCenterMetaDataInjector from '../components/UpdateCenterMetaDataInjector'; | |||
import './layout.css'; | |||
const version = process.env.GATSBY_DOCS_VERSION || '1.0'; | |||
@@ -95,7 +94,6 @@ export default function Layout({ children, location }: Props) { | |||
<div className="markdown-container">{children}</div> | |||
</div> | |||
<Footer /> | |||
<UpdateCenterMetaDataInjector location={location} /> | |||
</div> | |||
</div> | |||
)} |
@@ -5,9 +5,6 @@ url: /analysis/languages/abap/ | |||
_ABAP analysis is available starting in [Developer Edition](https://redirect.sonarsource.com/editions/developer.html)._ | |||
<!-- static --> | |||
<!-- update_center:abap --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -9,9 +9,6 @@ _Apex analysis is available starting in [Enterprise Edition](https://redirect.so | |||
<!-- /sonarqube --> | |||
<!-- static --> | |||
<!-- update_center:sonarapex --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -5,9 +5,6 @@ url: /analysis/languages/cfamily/ | |||
_C/C++/Objective-C analysis is available starting with [Developer Edition](https://redirect.sonarsource.com/editions/developer.html)._ | |||
<!-- static --> | |||
<!-- update_center:cpp --> | |||
<!-- /static --> | |||
C/C++/Objective-C analysis is officially registered as [CWE Compatible](https://cwe.mitre.org/compatible/). |
@@ -5,9 +5,6 @@ url: /analysis/languages/cobol/ | |||
_Cobol analysis is available starting in [Enterprise Edition](https://redirect.sonarsource.com/editions/enterprise.html)._ | |||
<!-- static --> | |||
<!-- update_center:cobol --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -3,9 +3,6 @@ title: C# | |||
url: /analysis/languages/csharp/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:csharp --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: CSS | |||
url: /analysis/languages/css/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:cssfamily --> | |||
<!-- /static --> | |||
## Prerequisites |
@@ -3,9 +3,6 @@ title: Flex | |||
url: /analysis/languages/flex/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:flex --> | |||
<!-- /static --> | |||
## Supported Versions | |||
* ActionScript 2 |
@@ -3,9 +3,6 @@ title: Go | |||
url: /analysis/languages/go/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:go --> | |||
<!-- /static --> | |||
@@ -3,9 +3,6 @@ title: HTML | |||
url: /analysis/languages/html/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:web --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: Java | |||
url: /analysis/languages/java/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:java --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: JavaScript | |||
url: /analysis/languages/javascript/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:javascript --> | |||
<!-- /static --> | |||
## Prerequisites |
@@ -3,9 +3,6 @@ title: Kotlin | |||
url: /analysis/languages/kotlin/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:kotlin --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: PHP | |||
url: /analysis/languages/php/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:php --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -5,9 +5,6 @@ url: /analysis/languages/pli/ | |||
_PL/I analysis is available starting in [Enterprise Edition](https://redirect.sonarsource.com/editions/enterprise.html)._ | |||
<!-- static --> | |||
<!-- update_center:pli --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -3,9 +3,6 @@ title: PL/SQL | |||
url: /analysis/languages/plsql/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:plsql --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: Python | |||
url: /analysis/languages/python/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:python --> | |||
<!-- /static --> | |||
## Supported Versions |
@@ -5,9 +5,6 @@ url: /analysis/languages/rpg/ | |||
_RPG analysis is available starting in [Enterprise Edition](https://redirect.sonarsource.com/editions/enterprise.html)._ | |||
<!-- static --> | |||
<!-- update_center:rpg --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -3,9 +3,6 @@ title: Ruby | |||
url: /analysis/languages/ruby/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:ruby --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: Scala | |||
url: /analysis/languages/scala/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:sonarscala --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: Swift | |||
url: /analysis/languages/swift/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:swift --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: T-SQL | |||
url: /analysis/languages/tsql/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:tsql --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -5,9 +5,6 @@ url: /analysis/languages/vb6/ | |||
_VB6 analysis is available starting in [Enterprise Edition](https://redirect.sonarsource.com/editions/enterprise.html)._ | |||
<!-- static --> | |||
<!-- update_center:vb --> | |||
<!-- /static --> | |||
## Language-Specific Properties | |||
@@ -3,9 +3,6 @@ title: VB.NET | |||
url: /analysis/languages/vbnet/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:vbnet --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: XML | |||
url: /analysis/languages/xml/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:xml --> | |||
<!-- /static --> | |||
## Language-Specific Properties |
@@ -3,9 +3,6 @@ title: SonarScanner for Ant | |||
url: /analysis/scan/sonarscanner-for-ant/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannerant --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannerant"></update-center> | |||
The SonarScanner for Ant provides a `task` to allow integration of SonarQube analysis into an Apache Ant build script. |
@@ -3,9 +3,6 @@ title: SonarScanner for Azure DevOps | |||
url: /analysis/scan/sonarscanner-for-azure-devops/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannerazure --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannerazure"></update-center> | |||
The [SonarScanner for Azure DevOps](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube) makes it easy to integrate analysis into your build pipeline. The extension allows the analysis of all languages supported by SonarQube. |
@@ -3,9 +3,6 @@ title: SonarScanner for Gradle | |||
url: /analysis/scan/sonarscanner-for-gradle/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannergradle --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannergradle"></update-center> | |||
The SonarScanner for Gradle provides an easy way to start SonarQube analysis of a Gradle project. |
@@ -3,9 +3,6 @@ title: SonarScanner for Jenkins | |||
url: /analysis/scan/sonarscanner-for-jenkins/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannerjenkins --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannerjenkins"></update-center> | |||
This plugin lets you centralize the configuration of SonarQube server connection details in Jenkins global configuration. |
@@ -3,9 +3,6 @@ title: SonarScanner for Maven | |||
url: /analysis/scan/sonarscanner-for-maven/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannermaven --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannermaven"></update-center> | |||
The SonarScanner for Maven is recommended as the default scanner for Maven projects. |
@@ -3,9 +3,6 @@ title: SonarScanner for MSBuild | |||
url: /analysis/scan/sonarscanner-for-msbuild/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannermsbuild --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannermsbuild"></update-center> | |||
The SonarScanner for MSBuild is the recommended way to launch an analysis for projects/solutions using MSBuild or dotnet command as a build tool. It is the result of a [collaboration between SonarSource and Microsoft](http://www.sonarqube.org/announcing-sonarqube-integration-with-msbuild-and-team-build/). |
@@ -3,9 +3,6 @@ title: SonarScanner | |||
url: /analysis/scan/sonarscanner/ | |||
--- | |||
<!-- static --> | |||
<!-- update_center:scannercli --> | |||
<!-- /static --> | |||
<update-center updatecenterkey="scannercli"></update-center> | |||
The SonarScanner is the scanner to use when there is no specific scanner for your build system. |
@@ -18,22 +18,61 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { graphql } from 'gatsby'; | |||
import { selectAll } from 'hast-util-select'; | |||
import * as React from 'react'; | |||
import Helmet from 'react-helmet'; | |||
import rehypeReact from 'rehype-react'; | |||
import MetaData from 'sonar-ui-common/components/ui/update-center/MetaData'; | |||
import { MarkdownHeading, MarkdownRemark, MarkdownRemarkConnection } from '../@types/graphql-types'; | |||
import HeaderList from '../components/HeaderList'; | |||
import { HtmlAST, HtmlASTNode } from '../types/hast'; | |||
interface Props { | |||
data: { | |||
allMarkdownRemark: Pick<MarkdownRemarkConnection, 'edges'>; | |||
markdownRemark: Pick<MarkdownRemark, 'html' | 'headings' | 'frontmatter'>; | |||
markdownRemark: Pick<MarkdownRemark, 'htmlAst' | 'headings' | 'frontmatter'>; | |||
}; | |||
location: Location; | |||
} | |||
export const query = graphql` | |||
query($slug: String!) { | |||
allMarkdownRemark { | |||
edges { | |||
node { | |||
html | |||
fields { | |||
slug | |||
} | |||
} | |||
} | |||
} | |||
markdownRemark(fields: { slug: { eq: $slug } }) { | |||
htmlAst | |||
headings { | |||
depth | |||
value | |||
} | |||
frontmatter { | |||
title | |||
} | |||
} | |||
} | |||
`; | |||
export default class Page extends React.PureComponent<Props> { | |||
baseUrl = ''; | |||
// @ts-ignore | |||
renderAst = new rehypeReact({ | |||
createElement: React.createElement, | |||
components: { | |||
'update-center': ({ updatecenterkey }: { updatecenterkey: string }) => ( | |||
<MetaData updateCenterKey={updatecenterkey} /> | |||
) | |||
} | |||
}).Compiler; | |||
componentDidMount() { | |||
if (window) { | |||
this.baseUrl = window.location.origin + '/'; | |||
@@ -73,14 +112,11 @@ export default class Page extends React.PureComponent<Props> { | |||
const mainTitle = 'SonarQube Docs'; | |||
const pageTitle = page.frontmatter && page.frontmatter.title; | |||
let htmlPageContent = page.html || ''; | |||
const realHeadingsList = removeExtraHeadings(htmlPageContent, page.headings || []); | |||
htmlPageContent = removeTableOfContents(htmlPageContent); | |||
htmlPageContent = createAnchorForHeadings(htmlPageContent, realHeadingsList); | |||
htmlPageContent = replaceDynamicLinks(htmlPageContent); | |||
htmlPageContent = replaceImageLinks(htmlPageContent); | |||
page.headings = filterHeaderList(page.htmlAst, page.headings); | |||
addSlugToHeader(page.htmlAst); | |||
makeExternalLinkOpenInNewTab(page.htmlAst); | |||
removeInAppLinks(page.htmlAst); | |||
addDocVersionToImagesLinks(page.htmlAst); | |||
return ( | |||
<> | |||
@@ -102,104 +138,67 @@ export default class Page extends React.PureComponent<Props> { | |||
})(window,document); | |||
`}</script> | |||
</Helmet> | |||
<HeaderList headers={realHeadingsList} /> | |||
<HeaderList headers={page.headings || []} /> | |||
<h1>{pageTitle || mainTitle}</h1> | |||
<div | |||
className="markdown-content" | |||
// Safe: comes from the backend | |||
dangerouslySetInnerHTML={{ __html: htmlPageContent }} | |||
/> | |||
<div className="markdown-content">{this.renderAst(page.htmlAst)}</div> | |||
</> | |||
); | |||
} | |||
} | |||
export const query = graphql` | |||
query($slug: String!) { | |||
allMarkdownRemark { | |||
edges { | |||
node { | |||
html | |||
fields { | |||
slug | |||
} | |||
} | |||
} | |||
} | |||
markdownRemark(fields: { slug: { eq: $slug } }) { | |||
html | |||
headings { | |||
depth | |||
value | |||
} | |||
frontmatter { | |||
title | |||
} | |||
} | |||
function filterHeaderList(hast: HtmlAST, headers: MarkdownHeading[] | null) { | |||
if (!headers) { | |||
return null; | |||
} | |||
`; | |||
function replaceImageLinks(content: string) { | |||
const version = process.env.GATSBY_DOCS_VERSION || ''; | |||
if (version !== '') { | |||
content = content.replace(/<img src="\/images\/(.*?)"/gim, `<img src="/${version}/images/$1"`); | |||
} | |||
return content; | |||
// Keep only first level h2 | |||
return headers.filter(header => | |||
hast.children.some( | |||
elt => | |||
elt.tagName === 'h2' && | |||
elt.children && | |||
elt.children.some(child => child.value === header.value) | |||
) | |||
); | |||
} | |||
function replaceDynamicLinks(content: string) { | |||
// Make outside link open in a new tab | |||
content = content.replace( | |||
/<a href="http(.*?)">(.*?)<\/a>/gim, | |||
'<a href="http$1" target="_blank">$2</a>' | |||
); | |||
function addSlugToHeader(hast: HtmlAST) { | |||
let counter = 1; | |||
// Render only the text part of links going inside the app | |||
return content.replace( | |||
/<a href="(.*)\/#(?:sonarqube|sonarcloud|sonarqube-admin)#.*?">(.*?)<\/a>/gim, | |||
'$2' | |||
); | |||
hast.children.forEach(elt => { | |||
if (elt.tagName === 'h2') { | |||
elt.properties = { ...elt.properties, id: `header-${counter}` }; | |||
counter++; | |||
} | |||
}); | |||
} | |||
/** | |||
* For the sidebar table of content, we do not want headers for sonarcloud, | |||
* collapsable container title, of table of contents headers. | |||
*/ | |||
function removeExtraHeadings(content: string, headings: MarkdownHeading[]) { | |||
return headings | |||
.filter( | |||
heading => | |||
content.indexOf( | |||
`<div class="custom-block collapse"><div class="custom-block-body"><h2>${heading.value}</h2>` | |||
) < 0 | |||
) | |||
.filter(heading => !heading.value || !heading.value.match(/Table of content/i)) | |||
.filter(heading => { | |||
const regex = new RegExp( | |||
`<!-- sonarcloud -->[\\s\\S]*<h2>${heading.value!.replace( | |||
/[.*+?^${}()|[\]\\]/g, | |||
'\\$&' | |||
)}<\\/h2>[\\s\\S]*<!-- /sonarcloud -->`, | |||
'gim' | |||
); | |||
return !content.match(regex); | |||
}); | |||
function makeExternalLinkOpenInNewTab(hast: HtmlAST) { | |||
selectAll('a[href^=http]', hast).forEach( | |||
(elt: HtmlASTNode) => (elt.properties = { ...elt.properties, target: '_blank' }) | |||
); | |||
} | |||
function createAnchorForHeadings(content: string, headings: MarkdownHeading[]) { | |||
let counter = 1; | |||
headings.forEach(h => { | |||
if (h.depth === 2) { | |||
content = content.replace( | |||
`<h${h.depth}>${h.value}</h${h.depth}>`, | |||
`<h${h.depth} id="header-${counter}">${h.value}</h${h.depth}>` | |||
); | |||
counter++; | |||
function removeInAppLinks(hast: HtmlAST) { | |||
const inAppLinksTags = ['/#sonarqube#/', '/#sonarcloud#/', '/#sonarqube-admin#/']; | |||
selectAll(inAppLinksTags.map(tag => `a[href*=${tag}]`).join(','), hast).forEach( | |||
(elt: HtmlASTNode) => { | |||
elt.tagName = 'span'; | |||
delete elt.properties?.href; | |||
} | |||
}); | |||
return content; | |||
); | |||
} | |||
function removeTableOfContents(content: string) { | |||
return content.replace(/<h[1-9]>Table Of Contents<\/h[1-9]>/i, ''); | |||
function addDocVersionToImagesLinks(hast: HtmlAST) { | |||
const version = process.env.GATSBY_DOCS_VERSION || ''; | |||
const imgPrefix = 'images'; | |||
if (version !== '') { | |||
selectAll(`img[src^=/${imgPrefix}/]`, hast).forEach((elt: HtmlASTNode) => { | |||
if (elt.properties?.src) { | |||
elt.properties.src = elt.properties.src.replace(imgPrefix, `${version}/${imgPrefix}`); | |||
} | |||
}); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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. | |||
*/ | |||
export interface HtmlAST { | |||
children: HtmlASTNode[]; | |||
} | |||
export interface HtmlASTNode { | |||
type: string; | |||
tagName?: string; | |||
properties?: { [key: string]: string }; | |||
value?: string; | |||
children?: HtmlASTNode[]; | |||
} |