1
0
mirror of synced 2026-01-10 09:02:35 -05:00

Merge pull request #33706 from github/repo-sync

Repo sync
This commit is contained in:
docs-bot
2024-06-25 14:29:58 -07:00
committed by GitHub
9 changed files with 138 additions and 38 deletions

View File

@@ -5,7 +5,7 @@
# --------------------------------------------------------------------------------
# To update the sha, run `docker pull node:$VERSION-alpine`
# look for something like: `Digest: sha256:0123456789abcdef`
FROM node:20-alpine@sha256:66c7d989b6dabba6b4305b88f40912679aebd9f387a5b16ffa76dfb9ae90b060 as base
FROM node:20-alpine@sha256:66f7f89199daea88a6b5d5aadaa6d20f7a16a90fc35274deda8e901e267d4bd7 as base
# This directory is owned by the node user
ARG APP_HOME=/home/node/app

View File

@@ -35,6 +35,8 @@ export default async function contextualize(
const context: Context = {}
req.context = context
console.log('CREATING CONTEXT')
req.context.process = { env: {} }
// define each context property explicitly for code-search friendliness

View File

@@ -1,6 +1,10 @@
import type { Response, NextFunction } from 'express'
import type { ExtendedRequest } from '@/types'
import { defaultCacheControl } from './cache-control.js'
export default function fastHead(req, res, next) {
export default function fastHead(req: ExtendedRequest, res: Response, next: NextFunction) {
if (!req.context) throw new Error('request is not contextualized')
const { context } = req
const { page } = context
if (page) {

View File

@@ -5,10 +5,19 @@
// solution to serve this directly.
import fs from 'fs'
import type { Response, NextFunction } from 'express'
import type { ExtendedRequest } from '@/types'
import { SURROGATE_ENUMS, setFastlySurrogateKey } from './set-fastly-surrogate-key.js'
import { assetCacheControl } from './cache-control.js'
const MAP = {
type IconConfig = {
contentType: string
buffer: () => Buffer
}
const MAP: {
[uri: string]: IconConfig
} = {
'/favicon.ico': {
contentType: 'image/x-icon',
buffer: getBuffer('assets/images/site/favicon.ico'),
@@ -35,8 +44,8 @@ MAP['/apple-touch-icon-precomposed.png'] = MAP['/apple-touch-icon.png']
MAP['/apple-touch-icon-120x120-precomposed.png'] = MAP['/apple-touch-icon-120x120.png']
MAP['/apple-touch-icon-152x152-precomposed.png'] = MAP['/apple-touch-icon-152x152.png']
function getBuffer(filePath) {
let buffer
function getBuffer(filePath: string) {
let buffer: Buffer
if (!fs.existsSync(filePath)) {
throw new Error(`${filePath} not found on disk`)
}
@@ -51,7 +60,7 @@ function getBuffer(filePath) {
}
}
export default function favicons(req, res, next) {
export default function favicons(req: ExtendedRequest, res: Response, next: NextFunction) {
if (!MAP[req.path]) return next()
// This makes sure the CDN caching survives each production deployment.

View File

@@ -49,14 +49,14 @@ import features from '@/versions/middleware/features.js'
import productExamples from './context/product-examples'
import productGroups from './context/product-groups'
import featuredLinks from '@/landings/middleware/featured-links'
import learningTrack from '@/learning-track/middleware/learning-track.js'
import learningTrack from '@/learning-track/middleware/learning-track'
import next from './next.js'
import renderPage from './render-page.js'
import assetPreprocessing from '@/assets/middleware/asset-preprocessing'
import archivedAssetRedirects from '@/archives/middleware/archived-asset-redirects'
import favicons from './favicons.js'
import favicons from './favicons'
import setStaticAssetCaching from '@/assets/middleware/static-asset-caching'
import fastHead from './fast-head.js'
import fastHead from './fast-head'
import fastlyCacheTest from './fastly-cache-test.js'
import trailingSlashes from './trailing-slashes.js'
import fastlyBehavior from './fastly-behavior.js'

View File

@@ -1,7 +1,7 @@
import { describe, expect, test, vi } from 'vitest'
import { SURROGATE_ENUMS } from '#src/frame/middleware/set-fastly-surrogate-key.js'
import { get } from '#src/tests/helpers/e2etest.js'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js'
import { get } from '@/tests/helpers/e2etest.js'
describe('favicon assets', () => {
vi.setConfig({ testTimeout: 60 * 1000 })

View File

@@ -1,25 +1,48 @@
import { getPathWithoutLanguage, getPathWithoutVersion } from '#src/frame/lib/path-utils.js'
import getLinkData from '../lib/get-link-data.js'
import { renderContent } from '#src/content-render/index.js'
import { getDeepDataByLanguage } from '#src/data-directory/lib/get-data.js'
import type { Response, NextFunction } from 'express'
import type {
Context,
ExtendedRequest,
LearningTrack,
LearningTracks,
TrackGuide,
Page,
} from '@/types'
import { getPathWithoutLanguage, getPathWithoutVersion } from '@/frame/lib/path-utils.js'
import getLinkData from '../lib/get-link-data.js'
import { renderContent } from '@/content-render/index.js'
import { getDeepDataByLanguage } from '@/data-directory/lib/get-data.js'
export default async function learningTrack(
req: ExtendedRequest,
res: Response,
next: NextFunction,
) {
if (!req.context) throw new Error('request is not contextualized')
export default async function learningTrack(req, res, next) {
const noTrack = () => {
req.context.currentLearningTrack = {}
req.context!.currentLearningTrack = null
return next()
}
if (!req.context.page) return next()
const trackName = req.query.learn
if (!trackName) return noTrack()
if (!req.query.learn) return noTrack()
if (Array.isArray(req.query.learn)) return noTrack()
const trackName = req.query.learn as string
let trackProduct = req.context.currentProduct
const allLearningTracks = getDeepDataByLanguage('learning-tracks', req.language)
if (req.langauge !== 'en') {
let trackProduct = req.context.currentProduct as string
// TODO: Once getDeepDataByLanguage is ported to TS
// a more appropriate API would be to use `getDeepDataByLanguage<LearningTracks)(...)`
const allLearningTracks = getDeepDataByLanguage('learning-tracks', req.language) as LearningTracks
if (req.language !== 'en') {
// Don't trust the `.guides` from the translation. It too often has
// broken Liquid (e.g. `{% ifversion fpt 또는 ghec 또는 ghes %}`)
const allEnglishLearningTracks = getDeepDataByLanguage('learning-tracks', 'en')
const allEnglishLearningTracks = getDeepDataByLanguage(
'learning-tracks',
'en',
) as LearningTracks
for (const [key, tracks] of Object.entries(allLearningTracks)) {
if (!(key in allEnglishLearningTracks)) {
// This can happen when the translation of
@@ -47,7 +70,12 @@ export default async function learningTrack(req, res, next) {
// the case where a learning track has guide paths for a different product
// than the current learning track product.
if (!tracksPerProduct) {
trackProduct = req.query.learnProduct
if (!req.query.learnProduct) return noTrack()
if (Array.isArray(req.query.learnProduct)) {
trackProduct = req.query.learnProduct[0] as string
} else {
trackProduct = req.query.learnProduct as string
}
tracksPerProduct = allLearningTracks[trackProduct]
}
if (!tracksPerProduct) return noTrack()
@@ -57,14 +85,14 @@ export default async function learningTrack(req, res, next) {
// The trackTitle comes from a data .yml file and may use Liquid templating, so we need to render it
const renderOpts = { textOnly: true }
const trackTitle = await renderContent(track.title, req.context, renderOpts)
const trackTitle = (await renderContent(track.title, req.context, renderOpts)) as string
const currentLearningTrack = { trackName, trackProduct, trackTitle }
const currentLearningTrack: LearningTrack = { trackName, trackProduct, trackTitle }
const guidePath = getPathWithoutLanguage(getPathWithoutVersion(req.pagePath))
// The raw track.guides will return all guide paths, need to use getLinkData
// so we only get guides available in the current version
const trackGuides = await getLinkData(track.guides, req.context)
const trackGuides = (await getLinkData(track.guides, req.context)) as TrackGuide[]
const trackGuidePaths = trackGuides.map((guide) => {
return getPathWithoutLanguage(getPathWithoutVersion(guide.href))
@@ -97,8 +125,13 @@ export default async function learningTrack(req, res, next) {
if (guideIndex > 0) {
const prevGuidePath = trackGuidePaths[guideIndex - 1]
const result = await getLinkData(prevGuidePath, req.context, { title: true, intro: false })
if (!result) return noTrack()
const resultData = await getLinkData(prevGuidePath, req.context, {
title: true,
intro: false,
fullTitle: false,
})
if (!resultData) return noTrack()
const result = resultData as { href: string; page: Page; title: string }
const href = result.href
const title = result.title
@@ -107,8 +140,13 @@ export default async function learningTrack(req, res, next) {
if (guideIndex < trackGuidePaths.length - 1) {
const nextGuidePath = trackGuidePaths[guideIndex + 1]
const result = await getLinkData(nextGuidePath, req.context, { title: true, intro: false })
if (!result) return noTrack()
const resultData = await getLinkData(nextGuidePath, req.context, {
title: true,
intro: false,
fullTitle: false,
})
if (!resultData) return noTrack()
const result = resultData as { href: string; page: Page; title: string }
const href = result.href
const title = result.title
@@ -123,7 +161,11 @@ export default async function learningTrack(req, res, next) {
// Find the index of a learning track guide path in an array of guide paths,
// return -1 if not found.
async function indexOfLearningTrackGuide(trackGuidePaths, guidePath, context) {
async function indexOfLearningTrackGuide(
trackGuidePaths: string[],
guidePath: string,
context: Context,
) {
let guideIndex = -1
for (let i = 0; i < trackGuidePaths.length; i++) {

View File

@@ -1,16 +1,19 @@
import { describe, expect, test } from 'vitest'
import { loadPages, loadPageMap } from '#src/frame/lib/page-data.js'
import loadRedirects from '#src/redirects/lib/precompile.js'
import { getDeepDataByLanguage } from '#src/data-directory/lib/get-data.js'
import { checkURL } from '#src/tests/helpers/check-url.js'
import type { LearningTracks } from '@/types'
import { loadPages, loadPageMap } from '@/frame/lib/page-data.js'
import loadRedirects from '@/redirects/lib/precompile.js'
import { getDeepDataByLanguage } from '@/data-directory/lib/get-data.js'
import { checkURL } from '@/tests/helpers/check-url.js'
const pageList = await loadPages(undefined, ['en'])
const pages = await loadPageMap(pageList)
const redirects = await loadRedirects(pageList)
describe('learning tracks', () => {
const allLearningTracks = getDeepDataByLanguage('learning-tracks', 'en')
// TODO: Once getDeepDataByLanguage is ported to TS
// a more appropriate API would be to use `getDeepDataByLanguage<LearningTracks)(...)`
const allLearningTracks = getDeepDataByLanguage('learning-tracks', 'en') as LearningTracks
const topLevels = Object.keys(allLearningTracks)
test.each(topLevels)('learning-track in data/learning-tracks/%s.yml', (topLevel) => {
@@ -33,6 +36,12 @@ describe('learning tracks', () => {
expect(length, errorMessage).toEqual(size)
}
type Trouble = {
uri: string
index: number
redirects: string | undefined
}
type TroubleTuple = [string, Trouble[]]
const troubles = Object.entries(learningTracks)
.map(([learningTrackKey, learningTrack]) => {
return [
@@ -42,7 +51,7 @@ describe('learning tracks', () => {
.filter(Boolean),
]
})
.filter(([, trouble]) => trouble.length > 0)
.filter(([, trouble]) => trouble.length > 0) as TroubleTuple[]
let errorMessage = `In data/learning-tracks/${topLevel}.yml there are ${troubles.length} guides that are not correct.\n`
let fixables = 0

View File

@@ -159,6 +159,39 @@ export type Context = {
productUserExamples?: ProductExample[]
productGroups?: ProductGroup[]
featuredLinks?: FeaturedLinksExpanded
currentLearningTrack?: LearningTrack | null
}
export type LearningTracks = {
[group: string]: {
[track: string]: {
title: string
description: string
versions?: FrontmatterVersions
guides: string[]
}
}
}
export type LearningTrack = {
trackName: string
trackProduct: string
trackTitle: string
numberOfGuides?: number
currentGuideIndex?: number
nextGuide?: {
href: string
title: string
}
prevGuide?: {
href: string
title: string
}
}
export type TrackGuide = {
href: string
page: Page
title: string
intro: string
}
export type FeaturedLinkExpanded = {
@@ -311,6 +344,7 @@ export type Page = {
earlyAccessToc?: boolean
autogenerated?: string
featuredLinks?: FeaturedLinksExpanded
redirect_from?: string[]
}
type ChangeLog = {