build(site): [SITE-07] reproduce Monaco CSS sources and verify styled editor

This commit is contained in:
Harvey Zhao committed 2026-09-15 11:55:22 +08:00
1 parent 23ac6f7128
commit d2b2f76db8
23 files changed
+16271 -5

No files matched your search

+48
View File
@@ -277,3 +277,51 @@ foreign modules, missing/repeated/already-injected dependencies and false entry
strings. Browser regressions exercise real diagnostics/formatting and all pinned
tokenizer cases. This does not close original core TS compilation, CSS reproduction,
remaining embedded-origin review, or the broader SITE-07 release requirements.
## CSS sources and historical compression
`reproduce-css.ts` separately covers all 68 core stylesheets. The AMD string table
does not give their assembly order: `coreStyleOrder` follows actual registrations.
81 fixed VS Code Git members include styles, three inline images, the codicon font,
original transport/build recipes, package/lock and license. Original `transportCSS`
is extracted syntactically from the verified recipe and executed with reads limited
to verified inputs. It removes the font query, normalizes URL quotes and embeds the
images. Every prepared result equals the corresponding core ESM archive member;
the copied font also matches. This harness does not run the full VS Code build.
The original CSS plugin rewrites paths and joins 70 separators with CRLF. The npm
development CSS is LF-only. That publication normalization is explicit and counted;
its exact upstream pipeline stage has not been established. After the fixed release
header and this documented transformation, the whole 114,942-byte development file
matches. This is source/output equivalence with an observed newline adaptation,
not an assertion that the original publishing command was reproduced unchanged.
Historical cssnano 4.1.11 and PostCSS 7.0.35 reproduce the complete 72,052-byte
minified file. Each run creates a fresh ignored tool directory, writes the fixed
historical lock, installs with Yarn 1.22.22 `--frozen-lockfile --ignore-scripts`,
and checks that the lock remains identical. The recorded closure has 153 selectors
and 143 package versions, all with registry integrity. `--fetch` downloads/verifies
source archives and permits registry access; the default uses cached archives and
an offline frozen install. No project dependency or root lock changes are needed.
The private manifest explicitly selects historical Browserslist defaults to prevent
the parent project's `last 1 Chrome version` target leaking into the archived build.
That leakage changes `transparent` to `initial`. Ambient BROWSERSLIST/config/env
overrides are rejected. Do not update historical caniuse data to silence its age
warning: these inputs exist only to reproduce the pinned asset. VS Code's archive
also obeys `LICENSE.txt eol=crlf`; only that member is converted to LF for its Git
blob check, while its frozen license bytes stay intact.
The site's existing 72,057-byte CSS differs solely by five CRLF header newlines;
the verifier requires that exact transformation and leaves the asset unchanged.
```sh
yarn verify:monaco-css --fetch
yarn verify:monaco-css
yarn test:browser test/browser/editor-styles.spec.js --workers=1
```
Browser checks cover light/dark theme colors, layout and resize, find-widget bounds,
successful CSS/font requests, loaded codicon glyphs and model disposal in all three
engines. The pinned light theme is intentionally #fffffe. Full original core TS
compilation and further embedded-origin review remain separate open work.
+105
View File
@@ -0,0 +1,105 @@
import type { Buffer } from 'node:buffer'
import assert from 'node:assert/strict'
import path from 'node:path'
import vm from 'node:vm'
import ts from 'typescript'
export function coreStyleOrder(source: string): string[] {
const ast = ts.createSourceFile('core.js', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
const tables = new Map<string, string[]>()
const visitTables = (node: ts.Node) => {
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isArrayLiteralExpression(node.initializer)
&& node.initializer.elements.every(item => ts.isStringLiteral(item))) { tables.set(node.name.text, node.initializer.elements.map(item => (item as ts.StringLiteral).text)) }
ts.forEachChild(node, visitTables)
}
visitTables(ast)
const styles: string[] = []
const visitCalls = (node: ts.Node) => {
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'define') {
const name = node.arguments[0]
if (name && ts.isElementAccessExpression(name) && ts.isIdentifier(name.expression) && ts.isNumericLiteral(name.argumentExpression)) {
const id = tables.get(name.expression.text)?.[Number(name.argumentExpression.text)]
if (id?.startsWith('vs/css!') && id !== 'vs/css!vs/editor/editor.main')
styles.push(id.slice('vs/css!'.length))
}
}
ts.forEachChild(node, visitCalls)
}
visitCalls(ast)
assert(styles.length && new Set(styles).size === styles.length, 'Missing or repeated core CSS modules')
return styles
}
interface PreparedStyle { id: string, contents: string, moduleName: string, fsPath: string }
interface CssPlugin {
writeFile: (plugin: string, entry: string, request: { toUrl: (name: string) => string }, write: (file: string, source: string) => void, config: object) => void
}
// Execute only the pinned transport function and CSS plugin, with reads restricted
// to the caller's verified source map. VM is an isolation aid, not a security sandbox.
export function prepareCoreStyles(transportRecipe: string, cssRecipe: string, ids: string[], read: (file: string) => Buffer) {
const ast = ts.createSourceFile('standalone.ts', transportRecipe, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
const functions = ast.statements.filter(node => ts.isFunctionDeclaration(node) && node.name?.text === 'transportCSS')
assert.equal(functions.length, 1, 'Expected one transportCSS function')
const code = ts.transpileModule(functions[0]!.getText(ast), { compilerOptions: { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.None } }).outputText
const assets = new Set<string>()
const context = vm.createContext({
SRC_DIR: '/source',
path: path.posix,
fs: { readFileSync: (file: string) => {
assert(file.startsWith('/source/') && !file.split('/').includes('..'), 'CSS source escaped verified inputs')
const source = `src/${file.slice('/source/'.length)}`
assets.add(source)
return read(source)
} },
})
vm.runInContext(code, context, { timeout: 1000 })
const transport = context.transportCSS as (module: string, enqueue: (font: string) => void, write: (file: string, contents: string) => void) => boolean
const fonts = new Set<string>()
const prepared: PreparedStyle[] = ids.map((id) => {
assert(/^vs\/[\w./-]+$/.test(id) && !id.split('/').includes('..'), 'Invalid CSS module')
let contents: string | undefined
assert(transport(`${id}.css`, font => fonts.add(font), (file, source) => {
assert.equal(file, `${id}.css`)
assert.equal(contents, undefined, 'Repeated CSS output')
contents = source
}), 'CSS transport rejected input')
assert(typeof contents === 'string', 'Missing transported CSS')
return { id, contents, moduleName: id, fsPath: `/source/${id}.css` }
})
let plugin: CssPlugin | undefined
const build = vm.createContext({
process: { versions: { node: '24' } },
require: { nodeRequire: (name: string) => {
if (name === 'path')
return path.posix
assert.equal(name, 'fs', 'Unexpected CSS build import')
return {
readFileSync: () => { throw new Error('Unprepared CSS resource') },
}
} },
define: (name: string, value: CssPlugin) => {
assert.equal(name, 'vs/css')
assert.equal(plugin, undefined)
plugin = value
},
inlineResources: true,
inlineResourcesLimit: 5000,
cssPluginEntryPoints: { 'vs/editor/editor.main': prepared },
})
vm.runInContext(cssRecipe, build, { timeout: 1000 })
assert(plugin, 'Missing CSS build plugin')
let joined: string | undefined
plugin.writeFile('vs/css', 'vs/editor/editor.main', { toUrl: name => name }, (file, source) => {
assert.equal(file, 'vs/editor/editor.main.css')
assert.equal(joined, undefined)
joined = source
}, {})
assert(typeof joined === 'string', 'Missing CSS bundle')
return { prepared, assets: [...assets], fonts: [...fonts], joined, normalizedSeparators: (joined.match(/\r\n/g) || []).length }
}
export function coreCssHeader(commit: string): string {
assert(/^[a-f0-9]{40}$/.test(commit))
return `/*!-----------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Version: 0.30.1(${commit})\n * Released under the MIT license\n * https://github.com/microsoft/vscode/blob/main/LICENSE.txt\n *-----------------------------------------------------------*/\n`
}
+141
View File
@@ -0,0 +1,141 @@
import type { Archive, Member } from './archives.ts'
import assert from 'node:assert/strict'
import { Buffer } from 'node:buffer'
import { execFileSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import fs from 'node:fs'
import { createRequire } from 'node:module'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { ArchiveCache, hash } from './archives.ts'
import { coreCssHeader, coreStyleOrder, prepareCoreStyles } from './css.ts'
interface Selector { selector: string, name: string, version: string, resolved: string, integrity: string }
interface LockedDependency {
version: string
resolved?: string
integrity?: string
dependencies?: Record<string, string>
optionalDependencies?: Record<string, string>
}
interface Provenance {
commit: string
archives: Archive[]
sourceArchive: { url: string, file: string, bytes: number, sha256: string }
members: { path: string, member: string, sha256: string, gitBlobSha: string, gitNormalizeCRLF?: boolean, frozen?: string }[]
tooling: { manifest: object, selectors: Selector[] }
coreJavaScript: Member
styles: { id: string, prepared: Member }[]
fonts: string[]
normalizedSeparators: number
development: Member
minified: Member
editor: Member
target: { path: string, sha256: string, crlf: number }
}
const root = fileURLToPath(new URL('../../../', import.meta.url))
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use yarn verify:monaco-css [--fetch]')
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
const yarn = process.env.npm_execpath
assert(yarn && process.env.npm_config_user_agent?.split(' ')[0] === 'yarn/1.22.22', 'Use Yarn Classic 1.22.22')
assert.equal(execFileSync(process.execPath, [yarn, '--version'], { encoding: 'utf8' }).trim(), '1.22.22')
for (const variable of ['BROWSERSLIST', 'BROWSERSLIST_CONFIG', 'BROWSERSLIST_ENV'])
assert(!process.env[variable], `Historical CSS target must not inherit ${variable}`)
const record: Provenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/monaco-css-build-provenance.json'), 'utf8'))
const cache = new ArchiveCache(root, path.join(root, 'refactor/.cache/monaco-review'))
const online = process.argv.includes('--fetch')
await cache.verify(record.archives, [], online)
assert.equal(record.sourceArchive.file, 'vscode-source.tgz')
assert.equal(record.sourceArchive.url, `https://codeload.github.com/microsoft/vscode/tar.gz/${record.commit}`)
const archivePath = path.join(cache.directory, record.sourceArchive.file)
let archive: Buffer
if (online) {
const response = await fetch(record.sourceArchive.url, { signal: AbortSignal.timeout(60000) })
assert(response.ok, `VS Code archive HTTP ${response.status}`)
archive = Buffer.from(await response.arrayBuffer())
}
else {
archive = fs.readFileSync(archivePath)
}
assert.equal(archive.length, record.sourceArchive.bytes)
assert.equal(hash(archive), record.sourceArchive.sha256, 'VS Code archive changed')
if (online)
fs.writeFileSync(archivePath, archive)
const sources = new Map<string, Buffer>()
for (const member of record.members) {
assert.equal(member.member, `vscode-${record.commit}/${member.path}`)
assert(!member.path.split('/').includes('..'), 'Invalid VS Code source')
const bytes = execFileSync('tar', ['-xOzf', archivePath, member.member], { maxBuffer: 5 * 1024 * 1024 })
assert.equal(hash(bytes), member.sha256, `Source changed: ${member.path}`)
assert.equal(Boolean(member.gitNormalizeCRLF), member.path === 'LICENSE.txt', 'Unexpected Git line-ending adaptation')
const gitBytes = member.gitNormalizeCRLF ? Buffer.from(bytes.toString('utf8').replace(/\r\n/g, '\n')) : bytes
assert.equal(createHash('sha1').update(`blob ${gitBytes.length}\0`).update(gitBytes).digest('hex'), member.gitBlobSha, 'Git blob changed')
if (member.frozen)
assert.deepEqual(fs.readFileSync(path.join(root, member.frozen)), bytes, 'Frozen CSS recipe changed')
assert(!sources.has(member.path), 'Duplicate CSS source')
sources.set(member.path, bytes)
}
function read(file: string): Buffer {
const source = sources.get(file)
assert(source, `Unverified CSS source: ${file}`)
return source
}
const ids = coreStyleOrder(cache.member(record.coreJavaScript).toString('utf8'))
assert(read('.gitattributes').toString('utf8').includes('LICENSE.txt eol=crlf'), 'Unexplained archive license line endings')
assert.deepEqual(ids, record.styles.map(style => style.id))
assert.equal(ids.length, 68)
const styles = prepareCoreStyles(read('build/lib/standalone.ts').toString('utf8'), read('src/vs/css.build.js').toString('utf8'), ids, read)
for (const [index, style] of styles.prepared.entries())
assert(Buffer.from(style.contents).equals(cache.member(record.styles[index]!.prepared)), `Prepared CSS differs: ${style.id}`)
assert.deepEqual(styles.fonts, record.fonts)
for (const font of styles.fonts)
assert.deepEqual(read(`src/${font}`), cache.read('monaco-editor-core-0.30.1', `package/esm/${font}`), 'Copied font differs')
assert.equal(styles.normalizedSeparators, record.normalizedSeparators)
assert.equal(styles.normalizedSeparators, 70)
// The archived dev CSS is LF-only; the fixed plugin joins 70 separators with CRLF.
// Record this observed publication normalization instead of ignoring whitespace.
const development = `${coreCssHeader(record.commit)}\n${styles.joined}`.replace(/\r\n/g, '\n')
assert(Buffer.from(development).equals(cache.member(record.development)), 'Complete CSS development bundle differs')
// The existing Yarn lock parser has no declarations; keep its used boundary local.
const lockfile = createRequire(import.meta.url)('@yarnpkg/lockfile') as { parse: (source: string) => { type: string, object: Record<string, LockedDependency> } }
const parsed = lockfile.parse(read('yarn.lock').toString('utf8'))
assert.equal(parsed.type, 'success')
const closure = new Map<string, Selector>()
function visit(selector: string) {
if (closure.has(selector))
return
const dependency = parsed.object[selector]
assert(dependency?.resolved && dependency.integrity, `Unpinned historical CSS dependency: ${selector}`)
closure.set(selector, { selector, name: selector.slice(0, selector.lastIndexOf('@')), version: dependency.version, resolved: dependency.resolved, integrity: dependency.integrity })
for (const [name, range] of Object.entries({ ...dependency.dependencies, ...dependency.optionalDependencies }))
visit(`${name}@${range}`)
}
visit('cssnano@^4.1.11')
assert.deepEqual([...closure.values()], record.tooling.selectors)
assert.equal(JSON.parse(read('package.json').toString('utf8')).devDependencies.cssnano, '^4.1.11')
const manifest = { name: 'monaco-css-historical-tools', private: true, browserslist: ['defaults'], dependencies: { cssnano: '^4.1.11' } }
assert.deepEqual(manifest, record.tooling.manifest)
const compiler = fs.mkdtempSync(path.join(cache.directory, 'css-build-'))
fs.writeFileSync(path.join(compiler, 'package.json'), `${JSON.stringify(manifest, null, 2)}\n`)
fs.writeFileSync(path.join(compiler, 'yarn.lock'), read('yarn.lock'))
const install = execFileSync(process.execPath, [yarn, 'install', '--frozen-lockfile', '--ignore-scripts', '--non-interactive', ...online ? [] : ['--offline']], { cwd: compiler, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 })
fs.writeFileSync(path.join(cache.directory, 'css-install-last.log'), install)
assert.deepEqual(fs.readFileSync(path.join(compiler, 'yarn.lock')), read('yarn.lock'), 'Frozen historical lock changed')
const require = createRequire(path.join(compiler, 'entry.cjs'))
const cssnanoPath = require.resolve('cssnano/package.json')
const cssRequire = createRequire(cssnanoPath)
assert.equal(require(cssnanoPath).version, '4.1.11')
assert.equal(cssRequire('postcss/package.json').version, '7.0.35')
const minified: { css: string } = await cssRequire('postcss')([require('cssnano')({ preset: 'default' })]).process(development, { from: path.join(compiler, 'editor.main.css'), map: false })
assert(Buffer.from(minified.css).equals(cache.member(record.minified)), 'Complete CSS minification differs')
assert.deepEqual(cache.member(record.minified), cache.member(record.editor), 'Editor CSS archive differs')
const target = fs.readFileSync(path.join(root, record.target.path))
assert.equal(hash(target), record.target.sha256, 'Site CSS changed')
assert.equal((target.toString('utf8').match(/\r\n/g) || []).length, record.target.crlf)
assert.equal(record.target.crlf, 5)
assert(Buffer.from(minified.css.replace(/\n/g, '\r\n')).equals(target), 'Site CSS differs beyond five header newlines')
console.log(JSON.stringify({ gitSources: sources.size, styles: ids.length, inlinedImages: styles.assets.filter(file => !file.endsWith('.css')), fonts: styles.fonts, dependencySelectors: closure.size, normalizedSeparators: styles.normalizedSeparators, developmentBytes: Buffer.byteLength(development), minifiedBytes: Buffer.byteLength(minified.css), siteBytes: target.length, exactDevelopment: true, exactMinification: true, preservedSiteCrlf: 5, compiler: { cssnano: '4.1.11', postcss: '7.0.35' }, originalCoreTypeScriptCompiled: false }))