diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 18:09:14 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 18:09:14 +0300 |
commit | a88c31d0ea1a79ca93fad357c3eb536b5e013e44 (patch) | |
tree | b38023b2be7478fa706c7a95d08cdfad13f8d8c1 /app/assets/javascripts/vue_shared/components/source_viewer | |
parent | affec3ced2d85697d9a21d83e811285b030705f2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/source_viewer')
6 files changed, 94 insertions, 2 deletions
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js index 94ebbccb014..3ac35abcf3a 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js @@ -145,3 +145,5 @@ export const BIDI_CHAR_TOOLTIP = __( export const HLJS_COMMENT_SELECTOR = 'hljs-comment'; export const HLJS_ON_AFTER_HIGHLIGHT = 'after:highlight'; + +export const NPM_URL = 'https://npmjs.com/package'; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js index c9f7e5508be..5d24a3d110b 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js @@ -1,5 +1,6 @@ import { HLJS_ON_AFTER_HIGHLIGHT } from '../constants'; import wrapComments from './wrap_comments'; +import linkDependencies from './link_dependencies'; /** * Registers our plugins for Highlight.js @@ -8,6 +9,9 @@ import wrapComments from './wrap_comments'; * * @param {Object} hljs - the Highlight.js instance. */ -export const registerPlugins = (hljs) => { +export const registerPlugins = (hljs, fileType, rawContent) => { hljs.addPlugin({ [HLJS_ON_AFTER_HIGHLIGHT]: wrapComments }); + hljs.addPlugin({ + [HLJS_ON_AFTER_HIGHLIGHT]: (result) => linkDependencies(result, fileType, rawContent), + }); }; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js new file mode 100644 index 00000000000..5b7650c56ae --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js @@ -0,0 +1,25 @@ +import packageJsonLinker from './utils/package_json_linker'; + +const DEPENDENCY_LINKERS = { + package_json: packageJsonLinker, +}; + +/** + * Highlight.js plugin for generating links to dependencies when viewing dependency files. + * + * Plugin API: https://github.com/highlightjs/highlight.js/blob/main/docs/plugin-api.rst + * + * @param {Object} result - an object that represents the highlighted result from Highlight.js + * @param {String} fileType - a string containing the file type + * @param {String} rawContent - raw (non-highlighted) file content + */ +export default (result, fileType, rawContent) => { + if (DEPENDENCY_LINKERS[fileType]) { + try { + // eslint-disable-next-line no-param-reassign + result.value = DEPENDENCY_LINKERS[fileType](result, rawContent); + } catch (e) { + // Shallowed (do nothing), in this case the original unlinked dependencies will be rendered. + } + } +}; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util.js new file mode 100644 index 00000000000..56ad55ef553 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util.js @@ -0,0 +1,15 @@ +import { escape } from 'lodash'; +import { setAttributes } from '~/lib/utils/dom_utils'; + +export const createLink = (href, innerText) => { + // eslint-disable-next-line @gitlab/require-i18n-strings + const rel = 'nofollow noreferrer noopener'; + const link = document.createElement('a'); + + setAttributes(link, { href: escape(href), rel }); + link.innerText = escape(innerText); + + return link.outerHTML; +}; + +export const generateHLJSOpenTag = (type) => `<span class="hljs-${escape(type)}">"`; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/package_json_linker.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/package_json_linker.js new file mode 100644 index 00000000000..d013d077ba3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/package_json_linker.js @@ -0,0 +1,46 @@ +import { joinPaths } from '~/lib/utils/url_utility'; +import { NPM_URL } from '../../constants'; +import { createLink, generateHLJSOpenTag } from './dependency_linker_util'; + +const attrOpenTag = generateHLJSOpenTag('attr'); +const stringOpenTag = generateHLJSOpenTag('string'); +const closeTag = '"</span>'; +const DEPENDENCY_REGEX = new RegExp( + /* + * Detects dependencies inside of content that is highlighted by Highlight.js + * Example: <span class="hljs-attr">"@babel/core"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^7.18.5"</span> + * Group 1: @babel/core + * Group 2: ^7.18.5 + */ + `${attrOpenTag}(.*)${closeTag}.*${stringOpenTag}(.*[0-9].*)(${closeTag})`, + 'gm', +); + +const handleReplace = (original, packageName, version, dependenciesToLink) => { + const href = joinPaths(NPM_URL, packageName); + const packageLink = createLink(href, packageName); + const versionLink = createLink(href, version); + const closeAndOpenTag = `${closeTag}: ${attrOpenTag}`; + const dependencyToLink = dependenciesToLink[packageName]; + + if (dependencyToLink && dependencyToLink === version) { + return `${attrOpenTag}${packageLink}${closeAndOpenTag}${versionLink}${closeTag}`; + } + + return original; +}; + +export default (result, raw) => { + const { dependencies, devDependencies, peerDependencies, optionalDependencies } = JSON.parse(raw); + + const dependenciesToLink = { + ...dependencies, + ...devDependencies, + ...peerDependencies, + ...optionalDependencies, + }; + + return result.value.replace(DEPENDENCY_REGEX, (original, packageName, version) => + handleReplace(original, packageName, version, dependenciesToLink), + ); +}; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue index 4ef37b1d059..ccc8b44942a 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue @@ -141,7 +141,7 @@ export default { let detectedLanguage = language; let highlightedContent; if (this.hljs) { - registerPlugins(this.hljs); + registerPlugins(this.hljs, this.blob.fileType, this.content); if (!detectedLanguage) { const hljsHighlightAuto = this.hljs.highlightAuto(content); highlightedContent = hljsHighlightAuto.value; |