Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion scripts/scarp-site/browser-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { resolve, extname, sep, join } from 'node:path'
import { chromium, type BrowserContext, type Page } from 'playwright'
import { auditOfflineAccessibility } from '../lib/offline-a11y.ts'
import { previewRoot } from './build.ts'
import { LATEST_RELEASE } from '../../docs/.vitepress/theme/lib/command-finder.ts'

const root=process.argv[2]?resolve(process.argv[2]):resolve(previewRoot,'scarp-docs')
const base='/scarp-docs/'
Expand Down Expand Up @@ -77,13 +78,16 @@ async function testLocale(
assert.equal(await page.locator('main').count(),1)
assert.equal(await page.locator('.features .feature').count()>8,true)
assert.equal(await page.locator('.hero h1').count(),1)
const install=page.locator('.top-nav a').filter({hasText:lang==='hi'?'इंस्टॉल':'Install'})
assert.equal(await install.getAttribute('href'),prefix+'releases/'+LATEST_RELEASE)
assert.equal(await page.locator('link[rel="canonical"]').getAttribute('href'),
'https://scarp-lang.github.io'+prefix)
assertions++

await page.goto(origin+prefix+'tooling/command-finder',{waitUntil:'networkidle'})
const widget=page.locator('[data-command-finder]')
await widget.locator('#scarp-release').waitFor({state:'visible'})
assert.equal(await widget.locator('#scarp-release').inputValue(),LATEST_RELEASE)
await widget.locator('#scarp-task').selectOption('inspect-symbols')
await widget.locator('.finder-result pre').waitFor({state:'visible'})
assert.match(await widget.locator('pre').innerText(),
Expand All @@ -92,7 +96,7 @@ async function testLocale(
await widget.locator('#scarp-release').selectOption('v0.0.5-alpha.1')
assert.equal(await widget.locator('pre').count(),0)
assert.match(await widget.locator('.finder-result').innerText(),/v0\.0\.6-alpha\.1/)
await widget.locator('#scarp-release').selectOption('v0.0.6-alpha.1')
await widget.locator('#scarp-release').selectOption(LATEST_RELEASE)
await widget.locator('pre').waitFor({state:'visible'})
assertions++

Expand Down
17 changes: 12 additions & 5 deletions scripts/scarp-site/build.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import assert from 'node:assert/strict'
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import ts from 'typescript'
import { documentationPages } from '../lib/docs-pages.ts'
import { commandAdvice, FINDER_TEXT, RELEASES, TASKS } from '../../docs/.vitepress/theme/lib/command-finder.ts'
import { escapeHtml, localLink, htmlPath, pairedRoutes, BASE } from './routes.ts'
import { renderMarkdown, type RenderedPage, type HomeAction, type HomeFeature } from './markdown.ts'
import { currentPublishedCompilerTag, currentCompilerReleaseRoute } from './current-release.ts'

const root=resolve(fileURLToPath(new URL('../..',import.meta.url)))
const docsRoot=join(root,'docs')
Expand Down Expand Up @@ -90,7 +91,7 @@ function finderData():string {
return '<script id="scarp-command-data" type="application/json">'+json+'</script>'
}

function htmlDocument(view:PageView, views:PageView[]):string{
function htmlDocument(view:PageView, views:PageView[], currentTag:string):string{
const {page,source,route,locale}=view
const meta=pairedRoutes(source)
const other=locale==='en'?meta.hindi:meta.english
Expand Down Expand Up @@ -130,9 +131,9 @@ function htmlDocument(view:PageView, views:PageView[]):string{
'<nav class="top-nav" aria-label="'+localized('Primary','मुख्य नेविगेशन',locale)+'">'+
'<a '+attrs(locale==='hi'?BASE+'hi/guide/learning-path':BASE+'guide/learning-path')+'>'+
localized('Learn','सीखें',locale)+'</a>'+
'<a '+attrs(locale==='hi'?BASE+'hi/guide/installation-alpha6':BASE+'guide/installation-alpha6')+'>'+
'<a '+attrs(currentCompilerReleaseRoute(currentTag,locale))+'>'+
localized('Install','इंस्टॉल',locale)+'</a>'+
'<a '+attrs(locale==='hi'?BASE+'hi/releases/v0.0.6-alpha.2':BASE+'releases/v0.0.6-alpha.2')+'>'+
'<a '+attrs(locale==='hi'?BASE+'hi/reference/status':BASE+'reference/status')+'>'+
localized('Releases','रिलीज़',locale)+'</a></nav>'+
'<div class="header-actions"><a '+attrs(target,'language-link')+' lang="'+(locale==='en'?'hi':'en')+'">'+
localeText+'</a><button type="button" id="search-open" aria-haspopup="dialog" aria-controls="search-panel">'+
Expand All @@ -157,6 +158,12 @@ function htmlDocument(view:PageView, views:PageView[]):string{
}

export async function buildSite():Promise<{pages:number;assets:number}>{
// Validate the published compiler before deleting any existing output.
const manifest = JSON.parse(readFileSync(join(root,'data/compiler-latest-release.json'),'utf8')) as unknown
const currentTag = currentPublishedCompilerTag(manifest)
for(const source of ['releases/'+currentTag+'.md','hi/releases/'+currentTag+'.md']) {
assert.ok(existsSync(join(docsRoot,source)), 'Missing bilingual current compiler guide: '+source)
}
// Production output is an owned .scarp/dist directory, never VitePress assets.
rmSync(production?siteRoot:previewRoot,{recursive:true,force:true})
mkdirSync(join(siteRoot,'assets'),{recursive:true})
Expand All @@ -176,7 +183,7 @@ export async function buildSite():Promise<{pages:number;assets:number}>{
for(const view of views){
const dest=join(siteRoot,htmlPath(view.source))
mkdirSync(resolve(dest,'..'),{recursive:true})
writeFileSync(dest,htmlDocument(view,views),'utf8')
writeFileSync(dest,htmlDocument(view,views,currentTag),'utf8')
}
const sitemap='<?xml version="1.0" encoding="UTF-8"?>\n'+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'+
Expand Down
45 changes: 45 additions & 0 deletions scripts/scarp-site/current-release.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import assert from 'node:assert/strict'
import { LATEST_RELEASE, RELEASES } from '../../docs/.vitepress/theme/lib/command-finder.ts'
import { BASE, type Locale } from './routes.ts'

/**
* The current-release manifest must refer to the exact compiler source commit
* in the offline CLI capability table. A published tag, never compiler main,
* determines which downloads are offered in the site navigation.
*/
export function currentPublishedCompilerTag(metadata: unknown): string {
assert.ok(metadata && typeof metadata === 'object' && !Array.isArray(metadata),
'Current compiler release manifest must be a JSON object')
const data = metadata as Record<string, unknown>
const published = RELEASES.at(-1)
assert.ok(published, 'There must be a published release')
assert.equal(published.tag, LATEST_RELEASE, 'Latest CLI version must match final published tag')
assert.equal(data.compilerRepository, 'scarp-lang/scarp', 'Untrusted compiler repository')
assert.equal(data.compilerRef, published.tag, 'Latest compiler manifest and CLI table disagree')
assert.equal(data.compilerCommit, published.source, 'Latest compiler release source SHA drift')
assert.equal(data.status, 'published-prerelease', 'Do not link unpublished development builds')
assert.match(published.tag, /^v\d+\.\d+\.\d+-(?:alpha|beta|rc)\.\d+$/,
'Current documentation must link a valid compiler prerelease tag')
assert.equal(typeof data.releasedAt, 'string')
assert.ok(!Number.isNaN(Date.parse(data.releasedAt as string)),
'Current compiler publication time is not a date')
const archives = [
'x86_64-pc-windows-msvc.zip',
'x86_64-unknown-linux-gnu.tar.gz',
'aarch64-apple-darwin.tar.gz'
].map(platform => 'scarp-' + published.tag.slice(1) + '-' + platform)
assert.ok(Array.isArray(data.assets), 'Current compiler assets are missing')
const assets = data.assets as unknown[]
assert.equal(new Set(assets).size, assets.length, 'Current compiler asset inventory has duplicates')
for (const asset of [...archives, 'SHA256SUMS']) {
assert.ok(assets.includes(asset), 'Missing official release asset: ' + asset)
}
return published.tag
}

/** Paths point to *our verified bilingual release guides*, never moving main. */
export function currentCompilerReleaseRoute(tag: string, locale: Locale): string {
assert.equal(tag, LATEST_RELEASE, 'Invalid or outdated compiler navigation tag')
assert.ok(locale === 'en' || locale === 'hi', 'Unsupported documentation language')
return BASE + (locale === 'hi' ? 'hi/' : '') + 'releases/' + tag
}
30 changes: 30 additions & 0 deletions scripts/scarp-site/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { escapeHtml, localLink, htmlPath, pairedRoutes, sourceRoute } from './ro
import { documentationPages } from '../lib/docs-pages.ts'
import { urlForPage } from '../../docs/.vitepress/site-meta.ts'
import { validateSitemap } from '../lib/sitemap-policy.ts'
import { LATEST_RELEASE } from '../../docs/.vitepress/theme/lib/command-finder.ts'
import { currentPublishedCompilerTag, currentCompilerReleaseRoute } from './current-release.ts'

const root=resolve(fileURLToPath(new URL('../..',import.meta.url)))
const docs=join(root,'docs')
Expand Down Expand Up @@ -74,6 +76,26 @@ check(fixture.html.includes('class="table-scroll" tabindex="0"'),
'Wide Markdown tables must be keyboard scrollable')
check(fixture.html.includes('tabindex="0"'),'Scrollable code must be keyboard focusable')

const latest = JSON.parse(readFileSync(join(root,'data/compiler-latest-release.json'),'utf8')) as Record<string,unknown>
check(currentPublishedCompilerTag(latest)===LATEST_RELEASE,'Current manifest and CLI release table must agree')
assert.equal(currentCompilerReleaseRoute(LATEST_RELEASE,'en'),
'/scarp-docs/releases/'+LATEST_RELEASE)
assert.equal(currentCompilerReleaseRoute(LATEST_RELEASE,'hi'),
'/scarp-docs/hi/releases/'+LATEST_RELEASE)
for(const [key,invalid] of [
['compilerRef','v0.0.6-alpha.1'],
['compilerRef','../../release'],
['compilerCommit','0'.repeat(40)],
['compilerRepository','unknown/compiler'],
['status','planned'],
['releasedAt','not-a-timestamp'],
['assets',[]]
] as const) {
assert.throws(()=>currentPublishedCompilerTag({...latest,[key]:invalid}),{name:'AssertionError'},
'Invalid current compiler metadata escaped guard: '+key)
}
assert.throws(()=>currentCompilerReleaseRoute('v0.0.6-alpha.1','en'))

const sources=documentationPages(docs)
check(sources.length===140,'Published docs count changed: update migration baseline with evidence')
for(const source of sources){
Expand Down Expand Up @@ -108,6 +130,14 @@ const hindiHome=readFileSync(join(site,'hi/index.html'),'utf8')
check(homeHtml.includes('Build Beyond Boundaries.'),'English homepage hero missing')
check(hindiHome.includes('सीमाओं से आगे'),'Hindi homepage hero missing')
check(homeHtml.includes('class="features"'),'Feature cards missing')
check(homeHtml.includes('href="'+currentCompilerReleaseRoute(LATEST_RELEASE,'en')+'">Install</a>'),
'English Install menu must open the current published archive/checksum guide')
check(hindiHome.includes('href="'+currentCompilerReleaseRoute(LATEST_RELEASE,'hi')+'">इंस्टॉल</a>'),
'Hindi Install menu must open the same published compiler release')
check(homeHtml.includes('href="/scarp-docs/reference/status">Releases</a>'),
'English releases menu must display the historical version matrix')
check(hindiHome.includes('href="/scarp-docs/hi/reference/status">रिलीज़</a>'),
'Hindi releases menu must display the historical version matrix')
const finder=readFileSync(join(site,'tooling/command-finder.html'),'utf8')
check(finder.includes('id="scarp-command-data"'),'Release-scoped CLI model missing')
check(finder.includes('v0.0.6-alpha.2'),'Latest published release missing')
Expand Down
Loading