Link requirements.txt dependencies to PyPI in the source viewer
What does this MR do and why?
requirements.txt is rendered by the legacy backend highlighter today because requirements_txt is listed in LEGACY_FILE_TYPES, so the server-side DependencyLinker still produces its links. This MR adds a Highlight.js dependency linker for the requirements_txt file type and removes it from LEGACY_FILE_TYPES, so the file is rendered by the frontend source viewer with the same links kept.
The linker is registered in link_dependencies.js alongside the existing dependency linkers (package_json, gemfile, podspec_json, composer_json, go_sum, and so on). It handles the two shapes pip accepts:
- A requirement line links the package name to
https://pypi.org/project/<name>/. The name is matched as a PEP 508 name, taken only up to the character that ends it: a version specifier, extras bracket, direct-reference@, environment marker or whitespace. SoDjango==3.2,requests>=2.0,<3,numpy [security] >= 1.0,pytest-cov~=4.0; python_version >= "3.8", a bareflask, andrequests @ https://example.com/requests.whlall link just the name, leaving the rest of the line as it was. - A line that is only a URL, which pip installs directly, links the URL to itself.
Everything else is deliberately left alone, matching the Ruby linker: comments (#), pip options (-e, --index-url), local paths (./vendor/package, vendor/requests.whl) and VCS references (git+https://…). Because the package name must be followed by a terminator, URL and VCS schemes and relative paths never match as package names.
requirements.txt is highlighted as plaintext, so each line arrives as a single <span class="">…</span> with operators already HTML-escaped (>=, <, &, "); the regexes are written against that exact shape. The URL match runs to whitespace or the closing tag rather than stopping at an ampersand, so a query string whose separators arrive escaped as & stays whole, and the URL is unescaped before the link is built because createLink escapes what it is given.
References
- Resolves #367653
- Sibling changes restoring linkers in the new viewer: !255262 (merged) (Podfile), !255264 (merged) (podspec) and !255265 (merged) (Cartfile)
- Reuses
createLinkfromapp/assets/javascripts/highlight_js/plugins/utils/dependency_linker_util.js, so links carry the sametargetandrelattributes as every other linker.
Differences
Before: requirements.txt was rendered by the legacy backend highlighter (it was in LEGACY_FILE_TYPES). Moving it to the frontend viewer without a linker would have dropped the package links.
After: The frontend viewer renders requirements.txt and keeps the links. Django links to https://pypi.org/project/Django/ and requests to https://pypi.org/project/requests/, opening in a new tab, with the version specifiers, extras and markers left as plain text after the link. URL lines are clickable links to their URL. Comments, pip options, local paths and VCS references are unchanged.
How to set up and validate locally
- In a project, add a file named
requirements.txtwith the following content: - Open the file in the repository view.
Django,requests,numpy,pytest-cov,flaskand therequestsinrequests @ …should each link to their PyPI project page; thehttps://example.com/pkg.tar.gz?a=1&b=2line should link to itself with the whole query string intact; the comment, the-eand--index-urllines, the local paths and thegit+line should not be links.
Specs:
yarn jest spec/frontend/highlight_js/plugins/utils/requirements_txt_linker_spec.js
spec/frontend/highlight_js/plugins/link_dependencies_spec.js
requirements_txt_linker_spec.js covers pinned, ranged, extras, marker and bare requirements; the direct-reference @ and relative-wheel cases; direct URL lines including an escaped query string; and that comments, options, paths and VCS references are untouched. link_dependencies_spec.js asserts the requirements_txt file type is routed to the new linker.
MR acceptance checklist
- The change is limited to the frontend source viewer; no backend, database, or API changes.
- Links are built with the shared
createLinkhelper, so they carrytarget="_blank"andrel="nofollow noreferrer noopener"like every other dependency linker. - Only the package name at the start of a requirement line and standalone URL lines are linked; comments, pip options, local paths, VCS references, version specifiers, extras and markers are not rewritten.
- Unit specs added for the linker and for its registration in
link_dependencies.js; the full source viewer plugin suite passes locally. -
Changelog: changedtrailer is on the commit. - No documentation change required — this restores existing behaviour in the new viewer rather than introducing a new feature.
- No feature flag needed; the change is small, additive, and isolated to one file type.