1
0
mirror of synced 2026-01-05 03:06:35 -05:00

Merge pull request #8439 from github/repo-sync

repo sync
This commit is contained in:
Octomerger Bot
2021-07-23 02:26:22 +10:00
committed by GitHub
50 changed files with 261 additions and 1735 deletions

3
.gitignore vendored
View File

@@ -7,14 +7,13 @@ npm-debug.log
coverage/
.linkinator
/assets/images/early-access
/assets/fonts/inter
/content/early-access
/data/early-access
dist
.next
.eslintcache
# blc: broken link checker
blc_output.log
blc_output_internal.log
/dist/
broken_links.md

View File

@@ -46,7 +46,6 @@ COPY lib ./lib
# one part of the build relies on this content file to pull all-products
COPY content/index.md ./content/index.md
COPY webpack.config.js ./webpack.config.js
COPY next.config.js ./next.config.js
COPY tsconfig.json ./tsconfig.json
@@ -73,7 +72,6 @@ USER node
COPY --chown=node:node --from=prod_deps /usr/src/docs/node_modules /usr/src/docs/node_modules
# Copy our front-end code
COPY --chown=node:node --from=builder /usr/src/docs/dist /usr/src/docs/dist
COPY --chown=node:node --from=builder /usr/src/docs/.next /usr/src/docs/.next
# We should always be running in production mode
@@ -87,7 +85,6 @@ COPY --chown=node:node assets ./assets
COPY --chown=node:node content ./content
COPY --chown=node:node data ./data
COPY --chown=node:node includes ./includes
COPY --chown=node:node layouts ./layouts
COPY --chown=node:node lib ./lib
COPY --chown=node:node middleware ./middleware
COPY --chown=node:node translations ./translations

View File

@@ -74,8 +74,9 @@ The recommended formats explicitly define which versions are used for all direct
| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
| Composer | PHP | `composer.lock` | `composer.json`, `composer.lock` |
| `dotnet` CLI | .NET languages (C#, C++, F#, VB) | `.csproj`, `.vbproj`, `.nuspec`, `.vcxproj`, `.fsproj` | `.csproj`, `.vbproj`, `.nuspec`, `.vcxproj`, `.fsproj`, `packages.config` |
| Maven | Java, Scala | `pom.xml` | `pom.xml` |
| `dotnet` CLI | .NET languages (C#, C++, F#, VB) | `.csproj`, `.vbproj`, `.nuspec`, `.vcxproj`, `.fsproj` | `.csproj`, `.vbproj`, `.nuspec`, `.vcxproj`, `.fsproj`, `packages.config` |{% ifversion fpt or ghes > 3.1 %}
| Go modules | Go | `go.mod` | `go.mod` |
{% endif %}| Maven | Java, Scala | `pom.xml` | `pom.xml` |
| npm | JavaScript | `package-lock.json` | `package-lock.json`, `package.json`|
| Python PIP | Python | `requirements.txt`, `pipfile.lock` | `requirements.txt`, `pipfile`, `pipfile.lock`, `setup.py`* |
| RubyGems | Ruby | `Gemfile.lock` | `Gemfile.lock`, `Gemfile`, `*.gemspec` |

View File

@@ -1 +1 @@
| Go {% ifversion fpt %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} | {% octicon "check" aria-label="The check icon" %}<br> Go modules | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% elsif ghes > 2.21 %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% elsif ghae %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% endif %}
| Go {% ifversion fpt %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %}<br>Go modules | {% octicon "check" aria-label="The check icon" %}<br> Go modules | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% elsif ghes > 3.1 %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %}<br>Go modules | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% elsif ghes > 2.21 %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% elsif ghae %}| {% octicon "check" aria-label="The check icon" %} | {% octicon "check" aria-label="The check icon" %} | {% octicon "x" aria-label="The X icon" %} |{% endif %}

View File

@@ -1 +0,0 @@
<a class="f6 no-underline color-text-tertiary pt-1" href="/{{currentLanguage}}/{{enterpriseServerVersions[0]}}/admin/all-releases">See all Enterprise releases</a>

View File

@@ -1,8 +0,0 @@
{% if currentVersion != 'homepage' %}
<li title="Home">
<a href="/{{currentLanguage}}" class="f6 pl-4 pr-5 ml-n1 pb-1 color-text-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="octicon" viewBox="0 0 16 16" width="16" height="16"> <path fill-rule="evenodd" clip-rule="evenodd" d="M7.78033 12.5303C7.48744 12.8232 7.01256 12.8232 6.71967 12.5303L2.46967 8.28033C2.17678 7.98744 2.17678 7.51256 2.46967 7.21967L6.71967 2.96967C7.01256 2.67678 7.48744 2.67678 7.78033 2.96967C8.07322 3.26256 8.07322 3.73744 7.78033 4.03033L4.81066 7L12.25 7C12.6642 7 13 7.33579 13 7.75C13 8.16421 12.6642 8.5 12.25 8.5L4.81066 8.5L7.78033 11.4697C8.07322 11.7626 8.07322 12.2374 7.78033 12.5303Z"></path></svg>
All products
</a>
</li>
{% endif %}

View File

@@ -1,16 +0,0 @@
{% unless enterpriseServerReleases.isOldestReleaseDeprecated and currentVersion contains enterpriseServerReleases.oldestSupported %}
<div class="f5 contribution">
<h2 class="f4">{% data ui.contribution_cta.title %}</h2>
<p class="color-text-secondary f6">{% data ui.contribution_cta.body %}</p>
{% if page.relativePath %}
{% assign contribution_href = "https://github.com/github/docs/edit/main/content/" | append: page.relativePath %}
{% else %}
{% assign contribution_href = "https://github.com/github/docs" %}
{% endif %}
<a class="btn btn-outline" href="{{ contribution_href }}">
{% octicon "git-pull-request" height="16" %}
{% data ui.contribution_cta.button %}
</a>
<p class="color-text-secondary f6 mt-2">{% data ui.contribution_cta.or %} <a href="https://github.com/github/docs/blob/main/CONTRIBUTING.md" target="_blank">{% data ui.contribution_cta.to_guidelines %}</a></p>
</div>
{% endunless %}

View File

@@ -1,12 +0,0 @@
{% if enterpriseServerReleases.deprecated contains currentVersion %}
{% assign deprecatedDate = enterpriseServerReleases.dates[currentVersion].deprecationDate %}
<div class="deprecation-banner border rounded-1 mb-2 color-bg-warning p-3 color-border-warning f5">
<p>
<b>
{% data reusables.enterprise_deprecation.version_was_deprecated %}
<span data-date="{{ deprecatedDate }}" data-format="%B %d, %Y" title="{{ deprecatedDate }}">{{ deprecatedDate }}</span>.
</b>
The documentation page you are looking for may have moved. Try going to the <a href="/{{ currentLanguage }}/enterprise/{{ currentVersion}}">GitHub Enterprise Server {{ currentVersion }} documentation homepage</a> or searching by keyword.
</p>
</div>
{% endif %}

View File

@@ -1,33 +0,0 @@
<head>
{% comment %} For human readers {% endcomment %}
<meta charset="utf-8" />
<title>{% if error == '404' %}{% data ui.errors.oops %}{% elsif page.documentType == 'homepage' and currentVersion == 'free-pro-team@latest' %}GitHub Documentation{% elsif page.fullTitle %}{{ page.fullTitle }}{% else %}GitHub Documentation{% endif %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="alternate icon" type="image/png" href="/assets/images/site/favicon.png">
<link rel="icon" type="image/svg+xml" href="/assets/images/site/favicon.svg">
<link rel="stylesheet" href="{{ builtAssets.main.css }}">
{% comment %} For Google and Bots {% endcomment %}
{% if page.intro %}
<meta name="description" content="{{ page.introPlainText }}">
{% endif %}
{% if page.topics %}
<meta name="keywords" content="{{ page.topics | join: ',' }}">
{% endif %}
{% if page.hidden %}
<meta name="robots" content="noindex" />
{% endif %}
{% for languageVariant in page.languageVariants %}
<link
rel="alternate"
hreflang="{{ languageVariant.hreflang }}"
href="https://docs.github.com{{ languageVariant.href }}"
/>
{% endfor %}
<meta name="google-site-verification" content="OgdQc0GZfjDI52wDv1bkMT-SLpBUo_h5nn9mI9L22xQ" />
<meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY" />
{% comment %} For our JS {% endcomment %}
<meta name="csrf-token" content="$CSRFTOKEN$">
<meta name="site.data.ui.search.placeholder" content="{% data ui.search.placeholder %}">
</head>

View File

@@ -1,69 +0,0 @@
<!-- START TRANSLATIONS NOTICES -->
{% if currentLanguage != 'en' %}
{% assign translation_notification_type = "true" %}
<!-- Site policy translations notice -->
{% if page.relativePath contains '/site-policy/' %}
{% assign translation_notification = site.data.reusables.policies.translation %}
<!-- Completed translations notice -->
{% elsif languages[currentLanguage].wip != true %}
{% assign translation_notification = site.data.ui.header.notices.localization_complete %}
<!-- In-progress translations notice -->
{% elsif languages[currentLanguage].wip %}
{% assign translation_notification = site.data.ui.header.notices.localization_in_progress %}
{% endif %}
{% else %}
<!-- User language redirect notice -->
{% if languages[userLanguage].wip == false and userLanguage != 'en' %}
{% assign user_language_redirect_notification_type = true %}
{% endif %}
{% endif %}
<!-- END TRANSLATIONS NOTICES -->
<!-- START RELEASE NOTICES -->
<!-- Custom GitHub AE notice -->
{% if currentVersion == "github-ae@latest" %}
{% assign release_notification_type = "true" %}
{% assign release_notification = site.data.ui.header.notices.ghae_silent_launch %}
<!-- Release candidate notice -->
{% elsif currentVersion == site.data.variables.release_candidate.version %}
{% assign release_notification_type = "true" %}
{% assign release_notification = allVersions[currentVersion].versionTitle | append: site.data.ui.header.notices.release_candidate %}
{% endif %}
<!-- END RELEASE NOTICES -->
<!-- ONEOFF EARLY ACCESS NOTICE -->
{% if page.hidden and page.relativePath contains "early-access/" %}
{% assign early_access_notification_type = true %}
{% assign early_access_notification = site.data.ui.header.notices.early_access %}
{% endif %}
<!-- END ONEOFF EARLY ACCESS NOTICE -->
{% if translation_notification_type %}
<div class="header-notifications text-center f5 color-bg-info color-text-primary py-4 px-6 translation_notice{% if release_notification_type %} border-bottom color-border-tertiary{% endif %}">
{{ translation_notification }}
</div>
{% endif %}
{% if user_language_redirect_notification_type %}
<div class="header-notifications text-center f5 color-bg-info color-text-primary py-4 px-6 translation_notice">
This article is also available in your language of choice. Click <a href="/{{userLanguage}}{{currentPathWithoutLanguage}}">here</a>
</div>
{% endif %}
{% if release_notification_type %}
<div class="header-notifications text-center f5 color-bg-info color-text-primary py-4 px-6 release_notice{% if early_access_notification_type %} border-bottom color-border-tertiary{% endif %}">
{{ release_notification }}
</div>
{% endif %}
{% if early_access_notification_type %}
<div class="header-notifications text-center f5 color-bg-danger color-text-primary py-4 px-6 early_access">
{{ early_access_notification }}
</div>
{% endif %}

View File

@@ -1,26 +0,0 @@
<!-- Versions picker that only appears in the header on homepage or product landing -->
{% if page.relativePath == 'index.md' or page.layout == 'product-landing' or page.layout == 'product-sublanding' or page.layout == 'release-notes' %}
{% if page.permalinks and page.permalinks.length > 1 %}
<div class="d-md-inline-block">
<div class="border-top border-md-top-0 py-2 py-md-0 d-md-inline-block">
<details class="dropdown-withArrow position-relative details details-reset mr-md-3 close-when-clicked-outside">
<summary class="py-2 color-text-primary" role="button" aria-label="Toggle versions list">
<div class="d-flex flex-items-center flex-justify-between">
{{ allVersions[currentVersion].versionTitle }}
<svg class="arrow ml-md-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg>
</div>
</summary>
<div id="versions-selector" class="position-md-absolute nav-desktop-langDropdown p-md-4 right-md-n4 top-md-6" style="z-index: 6;">
{% for permalink in page.permalinks %}
<a
href="{{ permalink.href }}"
class="d-block py-2 no-underline {% if currentPath == permalink.href %}active{% else %}Link--primary{% endif %}"
style="white-space: nowrap"
>{{ allVersions[permalink.pageVersion].versionTitle }}</a>
{% endfor %}
{% include all-enterprise-releases-link %}
</div>
</details>
</div>
{% endif %}
{% endif %}

View File

@@ -1,104 +0,0 @@
<div class="border-bottom color-border-secondary no-print">
{% unless error == '404' %}
{% include header-notification %}
{% endunless %}
<header class="container-xl px-3 px-md-6 pt-3 pb-2 position-relative d-flex flex-justify-between width-full {% if error == '404' %} d-md-none {% endif %}">
<div class="d-flex flex-items-center d-lg-none" style="z-index: 3;" id="github-logo-mobile" role="banner">
<a href="/{{ currentLanguage }}" aria-hidden="true" tabindex="-1">
{% octicon "mark-github" height="32" class="color-icon-primary" %}
</a>
<a href="/{{ currentLanguage }}" class="h4-mktg color-text-primary no-underline no-wrap pl-2">{% data ui.header.github_docs %}</a>
</div>
<div class="width-full">
<div class="d-inline-block width-full d-md-flex" style="z-index: 1;">
<button class="nav-mobile-burgerIcon float-right mt-1 border-0 d-md-none" type="button" aria-label="Toggle navigation">
<!-- Hamburger icon added with css -->
</button>
<div style="z-index: 2;" class="nav-mobile-dropdown width-full">
<div class="d-md-flex flex-justify-between flex-items-center">
<div class="py-2 py-md-0 d-md-inline-block">
<h4 class="text-mono f5 text-normal color-text-secondary d-md-none">{% data ui.homepage.explore_by_product %}</h4>
<details class="dropdown-withArrow position-relative details details-reset d-md-none close-when-clicked-outside">
<summary class="nav-desktop-productDropdownButton color-text-link py-2" role="button" aria-label="Toggle products list">
<div id="current-product" class="d-flex flex-items-center flex-justify-between" style="padding-top: 2px;">
<!-- Product switcher - GitHub.com, Enterprise Server, etc -->
<!-- 404 and 500 error layouts are not real pages so we need to hardcode the name for those -->
{{ productMap[currentProduct].name }}
<svg class="arrow ml-md-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg>
</div>
</summary>
<!-- Mobile-only product dropdown -->
<div id="homepages" class="position-md-absolute nav-desktop-productDropdown p-md-4 left-md-n4 top-md-6" style="z-index: 6;">
{% for product in activeProducts %}
<a href="{% unless product.external %}/{{ currentLanguage }}{% endunless %}{{ product.href }}"
{% if product.external %}target="_blank"{% endif %} class="d-block py-2
{% if product.id == currentProduct %}color-text-link text-underline active{% elsif product.id == currentProduct.id %}color-text-link text-underline active{% else %}Link--primary no-underline{% endif %}">
{{ product.name }}
{% if product.external %}
<span class="ml-1"><svg width="9" height="10" viewBox="0 0 9 10" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke="currentColor" d="M.646 8.789l8-8M8.5 9V1M1 .643h8"/></svg></span>
{% endif %}
</a>
{% endfor %}
</div>
</details>
</div>
<!-- Versions picker that only appears in the header on landing pages -->
{% include header-version-switcher %}
<div class="d-md-inline-block">
<!-- Language picker - 'English', 'Japanese', etc -->
{% if page.languageVariants.length > 0 and error != '404' and !page.hidden %}
<div class="border-top border-md-top-0 py-2 py-md-0 d-md-inline-block">
<details class="dropdown-withArrow position-relative details details-reset mr-md-3 close-when-clicked-outside">
<summary class="py-2 color-text-primary" role="button" aria-label="Toggle languages list">
<div class="d-flex flex-items-center flex-justify-between">
{% if languages[page.languageCode].nativeName %}
{{ languages[page.languageCode].nativeName }} ({{ languages[page.languageCode].name }})
{% else %}
{{ languages[page.languageCode].name }}
{% endif %}
<svg class="arrow ml-md-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg>
</div>
</summary>
<div id="languages-selector" class="position-md-absolute nav-desktop-langDropdown p-md-4 right-md-n4 top-md-6" style="z-index: 6;">
{% for languageVariant in page.languageVariants %}
{% unless languages[languageVariant.code].wip %}
<a
href="{{ languageVariant.href }}"
class="d-block py-2 no-underline {% if currentPath == languageVariant.href %}active {% else %}Link--primary{% endif %}"
style="white-space: nowrap"
>
{% if languages[languageVariant.code].nativeName %}
{{ languages[languageVariant.code].nativeName }} ({{ languageVariant.name }})
{% else %}
{{ languageVariant.name }}
{% endif %}
</a>
{% endunless %}
{% endfor %}
</div>
</details>
</div>
{% endif %}
<!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not -->
{% if page.relativePath != 'index.md' and error != '404' %}
<div class="pt-3 pt-md-0 d-md-inline-block ml-md-3 border-top border-md-top-0">
{% include search-form %}
<div id="search-results-container"></div>
<div class="search-overlay-desktop"></div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</header>
</div>

View File

@@ -1,4 +0,0 @@
<a class="link-with-intro Bump-link--hover no-underline d-block offset-lg-2 col-lg-8 mb-5" href="{{ fullPath }}">
<h4 class="link-with-intro-title h4-mktg">{{ title }}<span class="Bump-link-symbol"></span></h4>
<p class="link-with-intro-intro f5">{{ intro }}</p>
</a>

View File

@@ -11,4 +11,4 @@
</div>
{% endif %}
</a>
</div>
</div>

View File

@@ -1 +1 @@
- <a class="article-link link Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>
- <a class="article-link link Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>

View File

@@ -1,4 +1,4 @@
<a class="link-with-intro Bump-link--hover no-underline" href="{{ fullPath }}">
<h2 class="link-with-intro-title f4">{{ title }}<span class="Bump-link-symbol"></span></h2>
</a>
{% if intro %}<p class="link-with-intro-intro">{{ intro }}</p>{% endif %}
{% if intro %}<p class="link-with-intro-intro">{{ intro }}</p>{% endif %}

View File

@@ -1 +1 @@
<a class="link-title Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>
<a class="link-title Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>

View File

@@ -1 +1 @@
- <a class="topic-link link Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>
- <a class="topic-link link Bump-link--hover no-underline" href="{{ fullPath }}">{{ title }}</a>

View File

@@ -1,2 +0,0 @@
<script id="expose" type="application/json">{{ expose }}</script>
<script src="{{ builtAssets.main.js }}"></script>

View File

@@ -1,3 +0,0 @@
<button class="arrow-for-scrolling-top tooltipped tooltipped-n tooltipped-no-delay" aria-label="{% data ui.scroll_button.scroll_to_top %}" id="js-scroll-top">
{% octicon "chevron-up" %}
</button>

View File

@@ -1,10 +0,0 @@
<!--
This form is used in two places:
- On the homepage, front and center
- On all other pages, in the header
-->
<div id="search-input-container" aria-hidden="true">
<!-- will add a search input here -->
</div>

View File

@@ -1,19 +0,0 @@
{% if currentVersion != 'free-pro-team@latest' %}
{% include all-products-link %}
{% endif %}
{% for product in activeProducts %}
{% if currentVersion == 'free-pro-team@latest' or product.external or product.versions contains currentVersion %}
<li
title="{{product.name}}{% if product.external %} (External Site){% endif %}"
class="sidebar-product"
>
<a href="{% unless product.external %}/{{currentLanguage}}{% endunless %}{% if product.versions contains currentVersion and currentVersion != 'free-pro-team@latest' %}/{{currentVersion}}/{{product.id}}{% else %}{{product.href}}{% endif %}" {% if product.external %}target="_blank"{% endif %} class="f4 pl-4 pr-5 py-2 color-text-primary">
{{ product.name }}
{% if product.external %}
<span class="ml-1"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="octicon" width="16" height="16"> <path fill-rule="evenodd" clip-rule="evenodd" d="M14.75 1C14.8163 1 14.8799 1.02634 14.9268 1.07322C14.9737 1.12011 15 1.1837 15 1.25V5.396C15.0001 5.44551 14.9855 5.49393 14.958 5.53513C14.9305 5.57632 14.8915 5.60843 14.8457 5.62739C14.8 5.64635 14.7497 5.6513 14.7011 5.64162C14.6525 5.63194 14.608 5.60805 14.573 5.573L13.03 4.03L8.53 8.53C8.38783 8.66248 8.19978 8.7346 8.00548 8.73118C7.81118 8.72775 7.62579 8.64903 7.48838 8.51162C7.35097 8.37421 7.27225 8.18882 7.26883 7.99452C7.2654 7.80022 7.33752 7.61217 7.47 7.47L11.97 2.97L10.427 1.427C10.3919 1.39204 10.3681 1.34745 10.3584 1.2989C10.3487 1.25034 10.3536 1.20001 10.3726 1.15427C10.3916 1.10853 10.4237 1.06945 10.4649 1.04199C10.5061 1.01453 10.5545 0.999912 10.604 1H14.75ZM2.75 2C2.28587 2 1.84075 2.18437 1.51256 2.51256C1.18437 2.84075 1 3.28587 1 3.75V13.25C1 14.216 1.784 15 2.75 15H12.25C12.7141 15 13.1592 14.8156 13.4874 14.4874C13.8156 14.1592 14 13.7141 14 13.25V9.75C14 9.55109 13.921 9.36032 13.7803 9.21967C13.6397 9.07902 13.4489 9 13.25 9C13.0511 9 12.8603 9.07902 12.7197 9.21967C12.579 9.36032 12.5 9.55109 12.5 9.75V13.25C12.5 13.3163 12.4737 13.3799 12.4268 13.4268C12.3799 13.4737 12.3163 13.5 12.25 13.5H2.75C2.6837 13.5 2.62011 13.4737 2.57322 13.4268C2.52634 13.3799 2.5 13.3163 2.5 13.25V3.75C2.5 3.6837 2.52634 3.62011 2.57322 3.57322C2.62011 3.52634 2.6837 3.5 2.75 3.5H6.25C6.44891 3.5 6.63968 3.42098 6.78033 3.28033C6.92098 3.13968 7 2.94891 7 2.75C7 2.55109 6.92098 2.36032 6.78033 2.21967C6.63968 2.07902 6.44891 2 6.25 2H2.75Z"></path></svg></span>
{% endif %}
</a>
</li>
{% endif %}
{% endfor %}

View File

@@ -1,76 +0,0 @@
<!--
Styling note:
Categories, Maptopics, and Articles list items get a class of `active` when they correspond to content
hierarchy of the current page. If an item's URL is also the same as the current URL, the item
also gets an `is-current-page` class.
-->
{% include all-products-link %}
{% unless currentProductTree.page.hidden %}
{% if currentProductTree.renderedShortTitle %}{% assign productTitle = currentProductTree.renderedShortTitle %}{% else %}{% assign productTitle = currentProductTree.renderedFullTitle %}{% endif %}
<li title="" class="sidebar-product mb-2">
<a href="{{currentProductTree.href}}" class="pl-4 pr-5 pb-1 f4 color-text-primary">{{ productTitle }}</a>
</li>
<li>
<ul class="sidebar-categories list-style-none">
{% for childPage in currentProductTree.childPages %}
{% unless childPage.page.hidden %}
{% if childPage.page.documentType == "article" %}{% assign standaloneCategory = true %}{% else %}{% assign standaloneCategory = false %}{% endif %}
<li class="sidebar-category py-1 {% if currentPath contains childPage.href %}active {% if currentPath == childPage.href %}is-current-page {% endif %}{% endif %}{% if standaloneCategory %}standalone-category{% endif %}">
{% if childPage.renderedShortTitle %}{% assign childTitle = childPage.renderedShortTitle %}{% else %}{% assign childTitle = childPage.renderedFullTitle %}{% endif %}
{% if standaloneCategory %}
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">{{ childTitle }}</a>
{% else %}
<details class="dropdown-withArrow details details-reset" {% if currentPath contains childPage.href or forloop.index < 4 %}open{% endif %}>
<summary>
<div class="d-flex flex-justify-between">
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">{{ childTitle }}</a>
{% if forloop.index < 4 %}
<svg xmlns="http://www.w3.org/2000/svg" class="octicon flex-shrink-0 arrow mr-3" style="margin-top:7px" viewBox="0 0 16 16" width="16" height="16"> <path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></svg>
{% endif %}
</div>
</summary>
{% endif %}
<!-- some categories have maptopics with child articles -->
{% if currentPath contains childPage.href or forloop.index < 4 %}
{% if childPage.childPages[0].page.documentType == "mapTopic" %}
<ul class="sidebar-topics list-style-none position-relative">
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-maptopic {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-2 color-text-primary">{{ grandchildTitle }} </a>
<ul class="sidebar-articles my-2">
{% for greatgrandchildPage in grandchildPage.childPages %}
{% if greatgrandchildPage.renderedShortTitle %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedShortTitle %}{% else %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains greatgrandchildPage.href %}active {% if currentPath == greatgrandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{greatgrandchildPage.href}}" class="pl-6 pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %} color-text-primary">{{ greatgrandchildTitle }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
<!-- some categories have no maptopics, only articles -->
{% elsif childPage.childPages[0].page.documentType == "article" %}
<ul class="sidebar-articles list-style-none">
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-6 pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %} color-text-primary">{{ grandchildTitle }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</details>
</li>
{% endunless %}
{% endfor %}
</ul>
</li>
{% endunless %}

View File

@@ -1,22 +0,0 @@
<!-- product > category > maptopic > article -->
<div class="sidebar d-none d-lg-block color-bg-tertiary">
<div class="d-flex flex-items-center p-4 position-sticky top-0 color-bg-tertiary" style="z-index: 3;" id="github-logo" role="banner">
<a href="/{{ currentLanguage }}" class="color-text-primary" aria-hidden="true" tabindex="-1">
{% octicon "mark-github" height="32" %}
</a>
<a href="/{{ currentLanguage }}" class="h4-mktg color-text-primary no-underline no-wrap pl-2 flex-auto">{% data ui.header.github_docs %}</a>
</div>
<nav>
{% if error == '404' or page.relativePath == 'index.md' %}
<ul class="sidebar-products mt-4">
{% include sidebar-homepage %}
</ul>
{% else %}
<ul class="sidebar-products">
{% include sidebar-product %}
</ul>
{% endif %}
</nav>
</div>

View File

@@ -1,22 +0,0 @@
<footer class="py-6 text-small">
<div class="container-xl d-flex px-3 px-md-6">
<ul class="d-flex list-style-none flex-wrap flex-justify-center flex-xl-justify-start">
<li class="d-flex mr-xl-3 color-text-secondary">
{% octicon "mark-github" height="20" class="mr-2 mr-xl-3" %}
<span>&copy; {{ "now" | date: "%Y" }} GitHub, Inc.</span>
</li>
<li class="ml-3"><a href="/github/site-policy/github-terms-of-service">{% data ui.footer.terms %} </a></li>
<li class="ml-3"><a href="/github/site-policy/github-privacy-statement">{% data ui.footer.privacy %} </a></li>
<li class="ml-3"><a href="https://github.com/security">{% data ui.footer.product.links.security %}</a></li>
<li class="ml-3"><a href="https://www.githubstatus.com/">{% data ui.footer.support.links.status %}</a></li>
<li class="ml-3"><a href="/">{% data ui.footer.support.links.help %}</a></li>
<li class="ml-3"><a href="https://support.github.com">{% data ui.footer.support.links.contact_github %}</a></li>
<li class="ml-3"><a href="https://github.com/pricing">{% data ui.footer.product.links.pricing %}</a></li>
<li class="ml-3"><a href="/developers">{% data ui.footer.platform.links.developer_api %}</a></li>
<li class="ml-3"><a href="https://services.github.com/">{% data ui.footer.support.links.training %}</a></li>
<li class="ml-3"><a href="https://github.com/about">{% data ui.footer.company.links.about %}</a></li>
</ul>
</div>
</footer>
{% include scripts %}

View File

@@ -1,16 +0,0 @@
<section class="mt-lg-9 py-7 px-3 px-md-6 no-print color-bg-tertiary">
<div class="container-xl gutter-lg-spacious d-flex flex-wrap">
<div class="col-12 col-lg-6 col-xl-4 mb-6 mb-xl-0">
{% include survey %}
</div>
<div class="col-12 col-lg-6 col-xl-4 mb-6 mb-xl-0">
{% include contribution %}
</div>
<div class="col-12 col-lg-6 col-xl-4 mb-6 mb-xl-0">
{% include support %}
</div>
<div class="col-12 col-lg-6 col-xl-4 mb-6 mb-xl-0">
{% include toggle-images %}
</div>
</div>
</section>

View File

@@ -1,15 +0,0 @@
<!-- Contact support banner -->
<div>
<h3 class="mb-2 f4">
{% data ui.support.still_need_help %}
</h3>
{% if currentVersion contains 'enterprise' %}{% assign isEnterprise = true %}{% else %}{% assign isEnterprise = false %}{% endif %}
<a id="ask-community" href="https://github.community" class="btn btn-outline mr-4 mt-2">
{% octicon "people" width="16" %}
{% data ui.support.ask_community %}
</a>
<a id="contact-us" href="{% unless isEnterprise %}https://support.github.com/contact{% else %}https://enterprise.github.com/support{% endunless %}" class="btn btn-outline mt-2">
{% octicon "comment-discussion" width="16" %}
{% data ui.support.contact_support %}
</a>
</div>

View File

@@ -1,107 +0,0 @@
{% unless enterpriseServerReleases.isOldestReleaseDeprecated and currentVersion contains enterpriseServerReleases.oldestSupported %}
<form class="js-survey f5">
<h2
data-help-start
data-help-yes
data-help-no
class="mb-1 f4"
>
{% data ui.survey.able_to_find %}
<a
class="f6 text-normal ml-3 color-text-link"
href="/github/site-policy/github-privacy-statement"
target="_blank"
>{% data ui.survey.privacy_policy %}</a>
</h2>
<p
class="radio-group"
data-help-start
data-help-yes
data-help-no
>
<input
hidden
id="survey-yes"
type="radio"
name="survey-vote"
value="Yes"
aria-label="{% data ui.survey.yes %}"
/>
<label class="btn x-radio-label" for="survey-yes">
{% octicon "thumbsup" height="24" %}
</label>
<input
hidden
id="survey-no"
type="radio"
name="survey-vote"
value="No"
aria-label="{% data ui.survey.no %}"
/>
<label class="btn x-radio-label" for="survey-no">
{% octicon "thumbsdown" height="24" %}
</label>
</p>
<p class="color-text-secondary f6" hidden data-help-yes>
{% data ui.survey.yes_feedback %}
</p>
<p class="color-text-secondary f6" hidden data-help-no>
{% data ui.survey.no_feedback %}
</p>
<input
type="text"
class="d-none"
name="survey-token"
aria-hidden="true"
/>
<p hidden data-help-yes data-help-no>
<label
class="d-block mb-1 f6"
for="survey-comment"
>
<span hidden data-help-yes>{% data ui.survey.comment_yes_label %}</span>
<span hidden data-help-no>{% data ui.survey.comment_no_label %}</span>
<span class="text-normal color-text-tertiary float-right ml-1">
{% data ui.survey.optional %}
</span>
</label>
<textarea
class="form-control input-sm width-full"
name="survey-comment"
id="survey-comment"
></textarea>
</p>
<p hidden data-help-yes data-help-no>
<label
class="d-block mb-1 f6"
for="survey-email"
>
{% data ui.survey.email_label %}
<span class="text-normal color-text-tertiary float-right ml-1">
{% data ui.survey.optional %}
</span>
</label>
<input
type="email"
class="form-control input-sm width-full"
name="survey-email"
id="survey-email"
placeholder="{% data ui.survey.email_placeholder %}"
/>
<span class="f6 color-text-secondary">{% data ui.survey.not_support %}</span>
</p>
<p
class="text-right"
hidden
data-help-yes
data-help-no
>
<button type="submit" class="btn btn-sm">
{% data ui.survey.send %}
</button>
</p>
<p class="color-text-secondary f6" hidden data-help-end>
{% data ui.survey.feedback %}
</p>
</form>
{% endunless %}

View File

@@ -1,4 +0,0 @@
<button class="toggle-images tooltipped tooltipped-nw tooltipped-no-delay" id="js-toggle-images" hidden>
<span id="js-off-icon" aria-label="{% data ui.toggle_images.off %}" hidden>{% octicon "eye-closed" %}</span>
<span id="js-on-icon" aria-label="{% data ui.toggle_images.on %}" hidden>{% octicon "eye" %}</span>
</button>

View File

@@ -6,4 +6,4 @@
<a href="#" class="UnderlineNav-item tool-switcher" data-tool="curl">cURL</a>
<a href="#" class="UnderlineNav-item tool-switcher" data-tool="desktop">Desktop</a>
</div>
</nav>
</nav>

View File

@@ -1,16 +0,0 @@
const explorerUrl =
location.hostname === 'localhost'
? 'http://localhost:3000'
: 'https://graphql.github.com/explorer'
// Pass non-search query params to Explorer app via the iFrame
export default function () {
const graphiqlExplorer = document.getElementById('graphiql') as HTMLIFrameElement
const queryString = window.location.search
if (!(queryString && graphiqlExplorer)) return
window.onload = () => {
graphiqlExplorer?.contentWindow?.postMessage(queryString, explorerUrl)
}
}

View File

@@ -1,46 +0,0 @@
const xmlns = 'http://www.w3.org/2000/svg'
const plainObjectConstructor = {}.constructor
function exists(value: any) {
return value !== null && typeof value !== 'undefined'
}
function isPlainObject(value: any) {
return value.constructor === plainObjectConstructor
}
function isString(value: any) {
return typeof value === 'string'
}
function renderChildren(el: HTMLElement | SVGElement, children: Array<any>) {
for (const child of children) {
if (isPlainObject(child)) {
Object.entries(child)
.filter(([, value]) => exists(value))
.forEach(([key, value]) => el.setAttribute(key, value as string))
} else if (Array.isArray(child)) {
renderChildren(el, child)
} else if (isString(child)) {
el.append(document.createTextNode(child))
} else {
el.append(child)
}
}
}
export default function h(tagName: string, ...children: Array<any>) {
const el = ['svg', 'path'].includes(tagName)
? document.createElementNS(xmlns, tagName)
: document.createElement(tagName)
renderChildren(el, children)
return el
}
export const tags = Object.fromEntries(
['div', 'form', 'a', 'input', 'button', 'ol', 'li', 'mark'].map((tagName) => [
tagName,
(...args: Array<any>) => h(tagName, ...args),
])
)

View File

@@ -2,11 +2,7 @@
import '../stylesheets/index.scss'
import displayPlatformSpecificContent from './display-platform-specific-content'
import displayToolSpecificContent from './display-tool-specific-content'
import explorer from './explorer'
import scrollUp from './scroll-up'
import search from './search'
import wrapCodeTerms from './wrap-code-terms'
import print from './print'
import localization from './localization'
import experiment from './experiment'
import copyCode from './copy-code'
@@ -16,11 +12,7 @@ import toggleImages from './toggle-images'
document.addEventListener('DOMContentLoaded', async () => {
displayPlatformSpecificContent()
displayToolSpecificContent()
explorer()
scrollUp()
search()
wrapCodeTerms()
print()
localization()
copyCode()
initializeEvents()

View File

@@ -1,17 +0,0 @@
import { EventType, sendEvent } from './events'
export default function () {
const printButtons = document.querySelectorAll('.js-print')
Array.from(printButtons).forEach((btn) => {
// Open the print dialog when the button is clicked
btn.addEventListener('click', () => {
window.print()
})
})
// Track print events
window.onbeforeprint = function () {
sendEvent({ type: EventType.print })
}
}

View File

@@ -1,22 +0,0 @@
export default function () {
// function to scroll up to page top
const PageTopBtn = document.getElementById('js-scroll-top')
if (!PageTopBtn) return
PageTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
})
// show scroll button only when display is scroll down
window.addEventListener('scroll', function () {
const y = document.documentElement.scrollTop // get the height from page top
if (y < 100) {
PageTopBtn.classList.remove('show')
} else if (y >= 100) {
PageTopBtn.classList.add('show')
}
})
}

View File

@@ -1,3 +0,0 @@
declare module 'search-with-your-keyboard' {
export default function searchWithYourKeyboard(inputSelector: string, hitsSelector: string): void
}

View File

@@ -1,307 +0,0 @@
import { tags } from './hyperscript'
import { sendEvent, EventType } from './events'
import searchWithYourKeyboard from 'search-with-your-keyboard'
let $searchInputContainer: HTMLElement | null | undefined
let $searchResultsContainer: HTMLElement | null | undefined
let $searchOverlay: HTMLElement | null | undefined
let $searchInput: HTMLInputElement | null | undefined
let isExplorerPage: boolean
// This is our default placeholder, but it can be localized with a <meta> tag
let placeholder = 'Search topics, products...'
let version: string
let language: string
export default function search() {
// @ts-ignore
if (window.IS_NEXTJS_PAGE) return
// We don't want to mess with query params intended for the GraphQL Explorer
isExplorerPage = Boolean(document.getElementById('graphiql'))
// First, only initialize search if the elements are on the page
$searchInputContainer = document.getElementById('search-input-container')
$searchResultsContainer = document.getElementById('search-results-container')
if (!$searchInputContainer || !$searchResultsContainer) return
// This overlay exists so if you click off the search, it closes
$searchOverlay = document.querySelector('.search-overlay-desktop') as HTMLElement
// There's an index for every version/language combination
const { languages, versions, nonEnterpriseDefaultVersion } = JSON.parse(
(document.getElementById('expose') as HTMLScriptElement)?.text || ''
).searchOptions
version = deriveVersionFromPath(versions, nonEnterpriseDefaultVersion)
language = deriveLanguageCodeFromPath(languages)
// Find search placeholder text in a <meta> tag, falling back to a default
const $placeholderMeta = document.querySelector(
'meta[name="site.data.ui.search.placeholder"]'
) as HTMLMetaElement
if ($placeholderMeta) {
placeholder = $placeholderMeta.content
}
// Write the search form into its container
$searchInputContainer.append(tmplSearchInput())
$searchInput = $searchInputContainer.querySelector('input') as HTMLInputElement
// Prevent 'enter' from refreshing the page
;($searchInputContainer.querySelector('form') as HTMLFormElement).addEventListener(
'submit',
(evt) => evt.preventDefault()
)
// Search when the user finished typing
$searchInput.addEventListener('keyup', debounce(onSearch))
// Adds ability to navigate search results with keyboard (up, down, enter, esc)
searchWithYourKeyboard('#search-input-container input', '.ais-Hits-item')
// If the user already has a query in the URL, parse it and search away
if (!isExplorerPage) {
parseExistingSearch()
}
// If not on home page, decide if search panel should be open
toggleSearchDisplay() // must come after parseExistingSearch
}
// The home page and 404 pages have a standalone search
function hasStandaloneSearch() {
return document.getElementById('landing') || document.querySelector('body.error-404') !== null
}
function toggleSearchDisplay() {
// Clear/close search, if ESC is clicked
document.addEventListener('keyup', (e) => {
if (e.key === 'Escape') {
closeSearch()
}
})
// If not on homepage...
if (hasStandaloneSearch()) return
// Open panel if input is clicked
$searchInput?.addEventListener('focus', openSearch)
// Close panel if overlay is clicked
if ($searchOverlay) {
$searchOverlay.addEventListener('click', closeSearch)
}
// Open panel if page loads with query in the params/input
if ($searchInput?.value) {
openSearch()
}
}
// On most pages, opens the search panel
function openSearch() {
$searchInput?.classList.add('js-open')
$searchResultsContainer?.classList.add('js-open')
$searchOverlay?.classList.add('js-open')
}
// Close panel if not on homepage
function closeSearch() {
if (!hasStandaloneSearch()) {
$searchInput?.classList.remove('js-open')
$searchResultsContainer?.classList.remove('js-open')
$searchOverlay?.classList.remove('js-open')
}
if ($searchInput) $searchInput.value = ''
onSearch()
}
function deriveLanguageCodeFromPath(languageCodes: Array<string>) {
let languageCode = location.pathname.split('/')[1]
if (!languageCodes.includes(languageCode)) languageCode = 'en'
return languageCode
}
function deriveVersionFromPath(
allVersions: Record<string, string>,
nonEnterpriseDefaultVersion: string
) {
// fall back to the non-enterprise default version (FPT currently) on the homepage, 404 page, etc.
const versionStr = location.pathname.split('/')[2] || nonEnterpriseDefaultVersion
return allVersions[versionStr] || allVersions[nonEnterpriseDefaultVersion]
}
// Wait for the event to stop triggering for X milliseconds before responding
function debounce(fn: Function, delay = 300) {
let timer: number
return (...args: Array<any>) => {
clearTimeout(timer)
timer = window.setTimeout(() => fn.apply(null, args), delay)
}
}
// When the user finishes typing, update the results
async function onSearch() {
const query = $searchInput?.value || ''
// Update the URL with the search parameters in the query string
// UNLESS this is the GraphQL Explorer page, where a query in the URL is a GraphQL query
const pushUrl = new URL(location.toString())
pushUrl.search = query && !isExplorerPage ? new URLSearchParams({ query }).toString() : ''
history.pushState({}, '', pushUrl.toString())
// If there's a query, call the endpoint
// Otherwise, there's no results by default
let results = []
if (query.trim()) {
const endpointUrl = new URL(location.origin)
endpointUrl.pathname = '/search'
endpointUrl.search = new URLSearchParams({ language, version, query }).toString()
const response = await fetch(endpointUrl.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
results = response.ok ? await response.json() : []
}
// Either way, update the display
$searchResultsContainer?.querySelectorAll('*').forEach((el) => el.remove())
$searchResultsContainer?.append(tmplSearchResults(results))
toggleStandaloneSearch()
// Analytics tracking
if (query.trim()) {
sendEvent({
type: EventType.search,
search_query: query,
// search_context
})
}
}
// If on homepage, toggle results container if query is present
function toggleStandaloneSearch() {
if (!hasStandaloneSearch()) return
const query = $searchInput?.value
const queryPresent = Boolean(query && query.length > 0)
const $results = document.querySelector('.ais-Hits') as HTMLElement
// Primer classNames for showing and hiding the results container
const activeClass = $searchResultsContainer?.getAttribute('data-active-class')
const inactiveClass = $searchResultsContainer?.getAttribute('data-inactive-class')
if (!activeClass) {
console.error(
'container is missing required `data-active-class` attribute',
$searchResultsContainer
)
return
}
if (!inactiveClass) {
console.error(
'container is missing required `data-inactive-class` attribute',
$searchResultsContainer
)
return
}
// hide the container when no query is present
$searchResultsContainer?.classList.toggle(activeClass, queryPresent)
$searchResultsContainer?.classList.toggle(inactiveClass, !queryPresent)
if (queryPresent && $results) $results.style.display = 'block'
}
// If the user shows up with a query in the URL, go ahead and search for it
function parseExistingSearch() {
const params = new URLSearchParams(location.search)
if (!params.has('query')) return
if ($searchInput) $searchInput.value = params.get('query') || ''
onSearch()
}
/** * Template functions ***/
function tmplSearchInput() {
// only autofocus on the homepage, and only if no #hash is present in the URL
const autofocus = (hasStandaloneSearch() && !location.hash.length) || null
const { div, form, input, button } = tags
return div(
{ class: 'ais-SearchBox' },
form(
{ role: 'search', class: 'ais-SearchBox-form', novalidate: true },
input({
class: 'ais-SearchBox-input',
type: 'search',
placeholder,
autofocus,
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off',
spellcheck: 'false',
maxlength: '512',
}),
button({
class: 'ais-SearchBox-submit',
type: 'submit',
title: 'Submit the search query.',
hidden: true,
})
)
)
}
type SearchResult = {
url: string
breadcrumbs: string
heading: string
title: string
content: string
}
function tmplSearchResults(items: Array<SearchResult>) {
const { div, ol, li } = tags
return div(
{ class: 'ais-Hits', style: 'display:block' },
ol(
{ class: 'ais-Hits-list' },
items.map((item) => li({ class: 'ais-Hits-item' }, tmplSearchResult(item)))
)
)
}
function tmplSearchResult({ url, breadcrumbs, heading, title, content }: SearchResult) {
const { div, a } = tags
return div(
{ class: 'search-result border-top color-border-secondary py-3 px-2' },
a(
{ href: url, class: 'no-underline' },
div(
{
class: 'search-result-breadcrumbs d-block color-text-primary opacity-60 text-small pb-1',
},
// Breadcrumbs in search records don't include the page title
markify(breadcrumbs || '')
),
div(
{ class: 'search-result-title d-block h4-mktg color-text-primary' },
// Display page title and heading (if present exists)
markify(heading ? `${title}: ${heading}` : title)
),
div({ class: 'search-result-content d-block color-text-secondary' }, markify(content))
)
)
}
// Convert mark tags in search responses
function markify(text: string) {
const { mark } = tags
return text.split(/<\/?mark>/g).map((el, i) => (i % 2 ? mark(el) : el))
}

View File

@@ -1,29 +0,0 @@
# Layouts
The files in this directory are layouts which can be wrapped around pages.
See also [includes](includes), which are snippets of HTML or Markdown that
can be reused in multiple layouts.
## Using Layouts
Be default, `layouts/default.html` will be used for all pages.
To use a custom layout, add a `layout` value to the page's frontmatter:
```
---
title: Hello World
layout: some-layout
---
```
The example above will use the `layouts/some-layout.html` layout.
To render a page with no layout, set `layout: false`.
## Writing Layouts
Layout files should have a `.html` or `.md` extension, and they
must include the string `{{ content}}` to specify where inner content should
be injected.

View File

@@ -1,36 +0,0 @@
<!doctype html>
{% assign error = '404' %}
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
{% include head %}
<body class="d-lg-flex error-404">
{% include sidebar %}
<main class="width-full">
{% include header %}
<div class="container-xl p-responsive py-6">
<article class="markdown-body col-md-10 col-lg-7 mx-auto">
{% include error-404-deprecation-message %}
<h1>{% data ui.errors.oops %}</h1>
<div class="lead-mktg mb-5">
{% data ui.errors.page_doesnt_exist %}
</div>
<div class="col-lg-12 mt-6">
<h3 class="mb-3">{% data ui.search.need_help %}</h3>
{% include search-form %}
</div>
<div id="search-results-container" class="mt-4 d-none" data-active-class="d-block" data-inactive-class="d-none"></div>
</article>
</div>
<!-- {{ content }} -->
{% include support-section %}
{% include small-footer %}
{% include scroll-button %}
</main>
</body>
</html>

View File

@@ -1,27 +0,0 @@
<!doctype html>
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
{% include head %}
<body>
{% include header %}
<div class="container-xl p-responsive py-9">
<article class="markdown-body col-md-10 col-lg-7 mx-auto">
<h1>{% data ui.errors.oops %}</h1>
<div class="lead-mktg mb-5">
{% data ui.errors.something_went_wrong %}
{% data ui.errors.we_track_errors %}
</div>
{% if error %}
<pre><code>{{ error.stack }}</code></pre>
{% endif %}
</article>
</div>
<!-- {{ content }} -->
{% include support-section %}
{% include small-footer %}
{% include scroll-button %}
</body>
</html>

View File

@@ -1,23 +0,0 @@
import fs from 'fs'
import path from 'path'
import crypto from 'crypto'
// Get an MD4 Digest Hex content hash, loosely based on Webpack `[contenthash]`
function getContentHash(absFilePath) {
const buffer = fs.readFileSync(absFilePath)
const hash = crypto.createHash('md4')
hash.update(buffer)
return hash.digest('hex')
}
function getUrl(relFilePath) {
const absFilePath = path.join(process.cwd(), relFilePath)
return `/${relFilePath}?hash=${getContentHash(absFilePath)}`
}
export default {
main: {
js: getUrl('dist/index.js'),
css: getUrl('dist/index.css'),
},
}

View File

@@ -3,7 +3,6 @@ import renderContent from './renderContent.js'
import { ExtendedMarkdown, tags } from '../liquid-tags/extended-markdown.js'
import xLink from '../liquid-tags/link.js'
import xLinkWithIntro from '../liquid-tags/link-with-intro.js'
import xHomepageLinkWithIntro from '../liquid-tags/homepage-link-with-intro.js'
import xLinkInList from '../liquid-tags/link-in-list.js'
import xTopicLinkInList from '../liquid-tags/topic-link-in-list.js'
import xIndentedDataReference from '../liquid-tags/indented-data-reference.js'
@@ -15,7 +14,6 @@ import xIfversion from '../liquid-tags/ifversion.js'
// Include custom tags like {% link_with_intro /article/foo %}
renderContent.liquid.registerTag('link', xLink('link'))
renderContent.liquid.registerTag('link_with_intro', xLinkWithIntro)
renderContent.liquid.registerTag('homepage_link_with_intro', xHomepageLinkWithIntro)
renderContent.liquid.registerTag('link_in_list', xLinkInList)
renderContent.liquid.registerTag('topic_link_in_list', xTopicLinkInList)
renderContent.liquid.registerTag('indented_data_reference', xIndentedDataReference)

View File

@@ -6,7 +6,6 @@ import xPathUtils from '../lib/path-utils.js'
import productNames from '../lib/product-names.js'
import warmServer from '../lib/warm-server.js'
import readJsonFile from '../lib/read-json-file.js'
import builtAssets from '../lib/built-asset-urls.js'
import searchVersions from '../lib/search/versions.js'
import nonEnterpriseDefaultVersion from '../lib/non-enterprise-default-version.js'
const activeProducts = Object.values(productMap).filter(
@@ -58,9 +57,6 @@ export default async function contextualize(req, res, next) {
req.context.siteTree = siteTree
req.context.pages = pageMap
// JS + CSS asset paths
req.context.builtAssets = builtAssets
// Object exposing selected variables to client
req.context.expose = JSON.stringify({
// Languages and versions for search

View File

@@ -1,6 +1,5 @@
import FailBot from '../lib/failbot.js'
import loadSiteData from '../lib/site-data.js'
import builtAssets from '../lib/built-asset-urls.js'
import { nextApp } from './next.js'
function shouldLogException(error) {
@@ -42,9 +41,8 @@ export default async function handleError(error, req, res, next) {
// set req.context.site here so we can pass data/ui.yml text to the 500 layout
if (!req.context) {
const site = await loadSiteData()
req.context = { site: site[req.language || 'en'].site, builtAssets }
req.context = { site: site[req.language || 'en'].site }
}
// display error on the page in development and staging, but not in production
if (req.context && process.env.HEROKU_PRODUCTION_APP !== 'true') {
req.context.error = error

View File

@@ -3,17 +3,30 @@
const fs = require('fs')
const frontmatter = require('gray-matter')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path')
const homepage = path.posix.join(process.cwd(), 'content/index.md')
const { data } = frontmatter(fs.readFileSync(homepage, 'utf8'))
const productIds = data.children
module.exports = {
webpack: (config, { isServer }) => {
if (isServer) {
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, 'node_modules/@primer/css/fonts'),
to: path.join(__dirname, 'assets/fonts/inter'),
},
],
})
)
}
return config
},
// speed up production `next build` by ignoring typechecking during that step of build.
// type-checking still occurs in the Dockerfile build
future: {
webpack: 5,
},
typescript: {
ignoreBuildErrors: true,
},

682
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@
"connect-datadog": "0.0.9",
"connect-slashes": "^1.4.0",
"cookie-parser": "^1.4.5",
"copy-webpack-plugin": "^9.0.1",
"cors": "^2.8.5",
"csurf": "^1.11.0",
"dayjs": "^1.10.4",
@@ -118,7 +119,6 @@
"babel-preset-env": "^1.7.0",
"chalk": "^4.1.1",
"commander": "^7.2.0",
"copy-webpack-plugin": "^8.1.1",
"count-array-values": "^1.2.1",
"cross-env": "^7.0.3",
"csp-parse": "0.0.2",
@@ -173,8 +173,6 @@
"ts-loader": "^9.2.3",
"typescript": "^4.3.2",
"url-template": "^2.0.8",
"webpack": "^5.37.0",
"webpack-cli": "^4.7.0",
"website-scraper": "^4.2.3"
},
"engines": {
@@ -196,7 +194,7 @@
"browser-test": "start-server-and-test browser-test-server 4001 browser-test-tests",
"browser-test-server": "cross-env NODE_ENV=production WEB_CONCURRENCY=1 PORT=4001 node server.mjs",
"browser-test-tests": "cross-env BROWSER=1 NODE_OPTIONS=--experimental-vm-modules jest tests/browser/browser.js",
"build": "npm run webpack-build && next build",
"build": "next build",
"debug": "cross-env NODE_ENV=development ENABLED_LANGUAGES='en,ja' nodemon --inspect server.mjs",
"dev": "npm start",
"heroku-postbuild": "node script/early-access/clone-for-build.js && npm run build",
@@ -220,8 +218,7 @@
"sync-search-indices": "script/sync-search-indices.js",
"sync-search-server": "cross-env NODE_ENV=production WEB_CONCURRENCY=1 PORT=4002 node server.mjs",
"test": "npm run lint && cross-env NODE_OPTIONS=--experimental-vm-modules jest",
"test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch --notify --notifyMode=change --coverage",
"webpack-build": "cross-env NODE_ENV=production npx webpack --mode production"
"test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch --notify --notifyMode=change --coverage"
},
"lint-staged": {
"*.{js,mjs,ts,tsx}": "eslint --cache --fix",

View File

@@ -5,7 +5,7 @@
@import "@primer/css/labels/index.scss";
@import "@primer/css/avatars/avatar.scss";
$marketing-font-path: "/dist/fonts/";
$marketing-font-path: "/assets/fonts/inter/";
@import "@primer/css/marketing/index.scss";
@import "font-mktg.scss";

View File

@@ -3,7 +3,6 @@ import enterpriseServerReleases from '../../lib/enterprise-server-releases.js'
import { get, getDOM, head, post } from '../helpers/supertest.js'
import { describeViaActionsOnly } from '../helpers/conditional-runs.js'
import { loadPages } from '../../lib/page-data.js'
import builtAssets from '../../lib/built-asset-urls.js'
import CspParse from 'csp-parse'
import { productMap } from '../../lib/all-products.js'
import { jest } from '@jest/globals'
@@ -177,7 +176,11 @@ describe('server', () => {
const $ = await getDOM('/not-a-real-page')
expect($('h1').text()).toBe('Ooops!')
expect($.text().includes("It looks like this page doesn't exist.")).toBe(true)
expect($.text().includes('We track these errors automatically, but if the problem persists please feel free to contact us.')).toBe(true)
expect(
$.text().includes(
'We track these errors automatically, but if the problem persists please feel free to contact us.'
)
).toBe(true)
expect($.res.statusCode).toBe(404)
})
@@ -200,8 +203,12 @@ describe('server', () => {
test('renders a 500 page when errors are thrown', async () => {
const $ = await getDOM('/_500')
expect($('h1').text()).toBe('Ooops!')
expect($.text().includes("It looks like something went wrong.")).toBe(true)
expect($.text().includes("We track these errors automatically, but if the problem persists please feel free to contact us.")).toBe(true)
expect($.text().includes('It looks like something went wrong.')).toBe(true)
expect(
$.text().includes(
'We track these errors automatically, but if the problem persists please feel free to contact us.'
)
).toBe(true)
expect($.res.statusCode).toBe(500)
})
@@ -498,9 +505,7 @@ describe('server', () => {
const $ = await getDOM(
`${latestEnterprisePath}/github/getting-started-with-github/set-up-git`
)
expect(
$('a[href="/en/desktop/installing-and-configuring-github-desktop"]').length
).toBe(1)
expect($('a[href="/en/desktop/installing-and-configuring-github-desktop"]').length).toBe(1)
})
test('admin articles that link to non-admin articles have Enterprise user links', async () => {
@@ -526,16 +531,16 @@ describe('server', () => {
`${latestEnterprisePath}/admin/installation/upgrading-github-enterprise-server`
)
expect(
$(
'a[href="https://docs.microsoft.com/azure/backup/backup-azure-vms-first-look-arm"]'
).length
$('a[href="https://docs.microsoft.com/azure/backup/backup-azure-vms-first-look-arm"]')
.length
).toBe(1)
})
})
describe('article versions', () => {
test('includes links to all versions of each article', async () => {
const articlePath = 'github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/about-your-personal-dashboard'
const articlePath =
'github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/about-your-personal-dashboard'
const $ = await getDOM(
`/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}`
)
@@ -545,7 +550,10 @@ describe('server', () => {
).length
).toBe(2)
// 2.13 predates this feature, so it should be excluded:
expect($(`[data-testid=article-version-picker] a[href="/en/enterprise/2.13/user/${articlePath}"]`).length).toBe(0)
expect(
$(`[data-testid=article-version-picker] a[href="/en/enterprise/2.13/user/${articlePath}"]`)
.length
).toBe(0)
})
test('is not displayed if article has only one version', async () => {
@@ -852,8 +860,9 @@ describe('GitHub Desktop URLs', () => {
describe('static assets', () => {
test('fonts', async () => {
expect((await get('/dist/fonts/Inter-Medium.woff')).statusCode).toBe(200)
expect((await get('/dist/fonts/Inter-Regular.woff')).statusCode).toBe(200)
expect((await get('/assets/fonts/inter/Inter-Bold.woff')).statusCode).toBe(200)
expect((await get('/assets/fonts/inter/Inter-Medium.woff')).statusCode).toBe(200)
expect((await get('/assets/fonts/inter/Inter-Regular.woff')).statusCode).toBe(200)
})
})
@@ -948,37 +957,6 @@ describe('?json query param for context debugging', () => {
})
})
describe('stylesheets', () => {
it('compiles and sets the right content-type header', async () => {
const stylesheetUrl = builtAssets.main.css
const res = await get(stylesheetUrl)
expect(res.statusCode).toBe(200)
expect(res.headers['content-type']).toBe('text/css; charset=UTF-8')
})
})
describe('client-side JavaScript bundle', () => {
let res
beforeAll(async () => {
const scriptUrl = builtAssets.main.js
res = await get(scriptUrl)
})
it('returns a 200 response', async () => {
expect(res.statusCode).toBe(200)
})
it('sets the right content-type header', async () => {
expect(res.headers['content-type']).toBe('application/javascript; charset=UTF-8')
})
// TODO: configure webpack to create production bundle in the test env
// it('is not too big', async () => {
// const tooBig = 10 * 1000
// expect(res.text.length).toBeLessThan(tooBig)
// })
})
describe('static routes', () => {
it('serves content from the /assets directory', async () => {
expect((await get('/assets/images/site/be-social.gif')).statusCode).toBe(200)

View File

@@ -1,78 +0,0 @@
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { EnvironmentPlugin, ProvidePlugin } = require('webpack')
module.exports = {
mode: 'development',
devtool: process.env.NODE_ENV === 'development' ? 'eval' : 'source-map', // no 'eval' outside of development
entry: './javascripts/index.ts',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist',
},
stats: 'errors-only',
resolve: {
extensions: ['.tsx', '.ts', '.js', '.css', '.scss'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
url: false,
},
},
{
// Needed to resolve image url()s within @primer/css
loader: 'resolve-url-loader',
options: {},
},
{
loader: 'sass-loader',
options: {
sassOptions: {
quietDeps: true,
includePaths: ['./stylesheets', './node_modules'],
options: {
sourceMap: true,
sourceMapContents: false,
},
},
},
},
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'index.css',
}),
new CopyWebpackPlugin({
patterns: [{ from: 'node_modules/@primer/css/fonts', to: 'fonts' }],
}),
new EnvironmentPlugin({
NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined
DEBUG: false,
}),
new ProvidePlugin({
process: 'process/browser',
}),
],
}