build(site): [SITE-07] verify Node path origin and preserve complete notices

This commit is contained in:
Harvey Zhao committed 2026-09-15 12:18:24 +08:00
1 parent d2b2f76db8
commit d3aa4bbfe4
24 files changed
+4050 -5

No files matched your search

+3
View File
@@ -3,6 +3,7 @@ import fs from 'node:fs'
import process from 'node:process'
import { verifyConsoleNoticeSources } from './site-vendor/console/notices.ts'
import { verifyMonacoCoreNotices } from './site-vendor/monaco/core-origins.ts'
import { verifyMonacoPathNotices } from './site-vendor/monaco/node-path.ts'
import { verifyMonacoLanguageNotices } from './site-vendor/monaco/notices.ts'
import { writeOrCheckNotices } from './site-vendor/notices.ts'
@@ -22,6 +23,7 @@ assert.deepEqual(manifest.groups.filter(group => group.name !== 'console').flatM
'jsonc-parser',
'marked (Monaco core)',
'mutation-observer',
'nodejs path (Monaco core)',
'regenerator-runtime',
'style-loader',
'svelte',
@@ -89,6 +91,7 @@ for (const name of ['LICENSE', 'MIT-LICENSE', 'ATTRIBUTION.md'])
assert(vconsoleNotices?.includes(`docs/licenses/vconsole/${name}`), `Missing vConsole notice: ${name}`)
verifyConsoleNoticeSources(process.cwd(), manifest)
verifyMonacoCoreNotices(process.cwd(), manifest)
verifyMonacoPathNotices(process.cwd(), manifest)
verifyMonacoLanguageNotices(process.cwd(), manifest)
const count = writeOrCheckNotices(process.cwd(), manifest, process.argv.includes('--check'))
console.log(`Verified site notices: ${count} outputs; Monaco/vConsole/console inventories, embedded and other provenance gates remain open.`)
+18
View File
@@ -1020,6 +1020,11 @@
"source": "refactor/baselines/site-vendor/monaco-core-origins/ATTRIBUTION.md",
"target": "docs/licenses/monaco-editor/core-origins/ATTRIBUTION.md",
"sha256": "3a0ab7f1036d6b1397bf94ff2700375b97db2db3bd576d195dc6d8a88e1a758e"
},
{
"source": "refactor/baselines/site-vendor/monaco-node-path/ATTRIBUTION.md.txt",
"target": "docs/licenses/monaco-editor/core-path/ATTRIBUTION.md",
"sha256": "40583c701c0bcbd8e10935652a0a8b02e2073b0ef040fb7be7b939c437d8b8dc"
}
],
"review": "The complete upstream Monaco LICENSE/ThirdPartyNotices are retained. The unmodified bundled Codicons font exactly matches @vscode/codicons 0.0.26; its historical README, CC BY 4.0 content license, MIT code license and added attribution are distributed below. Other site assets remain subject to their separate provenance reviews.",
@@ -1199,6 +1204,19 @@
"docs/licenses/monaco-editor/core-origins/marked-vscode-license.txt",
"docs/licenses/monaco-editor/core-origins/ATTRIBUTION.md"
]
},
{
"name": "nodejs path (Monaco core)",
"version": "14.16.0",
"tarball": "https://github.com/nodejs/node/blob/bd60e93357a118204ea238d94e7a9e4209d93062/lib/path.js",
"assets": [
"docs/assets/js/vs/editor/editor.main.js",
"docs/assets/js/vs/base/worker/workerMain.js"
],
"notices": [
"docs/licenses/monaco-editor/ThirdPartyNotices.txt",
"docs/licenses/monaco-editor/core-path/ATTRIBUTION.md"
]
}
]
},
+45
View File
@@ -325,3 +325,48 @@ Browser checks cover light/dark theme colors, layout and resize, find-widget bou
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.
## Node path port and existing terms
`node-path.ts` binds the Node path attribution to both editor.main and workerMain.
The original site ThirdPartyNotices already contains the full Joyent/Node license
text, identical to Node v14.16.0 and VS Code's retained source comment. Its older
commit link is kept intact; a new core-path/ATTRIBUTION.md identifies the actual
fixed Node source and VS Code port. The site now delivers 86 notice files plus
its index. The ordinary notice build rejects a missing worker binding, original
terms, source explanation, or mismatched ported license/version.
`reproduce-node-path.ts` checks three archives and three fixed Git files. Six
explicit unused top-level exports are removed from the VS Code source; the complete
result equals both archived source-map entries. Their source keys contain different
`out-editor/.../file:/...` prefixes, so the record uses the actual keys, not a guessed
file URI. Historical TypeScript 4.5.0-dev.20211021 from VS Code's lock compiles the
full path module into both development bundles with exact unique spans. It stays
in a separate ignored compiler directory from Monaco's TypeScript 4.4.4 tools.
The corresponding shipped minified assets are checked against their fixed archives;
the complete core minifier was verified separately by reproduce-core-build.ts.
This is a modified browser port, not an unmodified Node runtime. Besides types,
parameter names and brace style, review identified a Windows join string guard,
shared basename loop variables, local validation/Error handling, the process
adapter, explicit exports and omission of Node's deprecated _makeLong alias.
The new source note records these existing adaptations without changing behavior.
Thirty deterministic valid calls are evaluated by the frozen Node v14.16.0 module
and frozen as fixtures. The reference harness supplies character constants and
string validation, rejects extra imports and cwd use, and does not emulate Node's
error messages or per-drive environment. Browser tests execute the actual Monaco
POSIX/Windows functions, compare those results, check platform alias selection,
and retain the existing Error/ERR_INVALID_ARG_TYPE behavior.
```sh
yarn verify:monaco-node-path --fetch
yarn verify:monaco-node-path
yarn build:site-notices
yarn check:site-notices
yarn test:browser test/browser/editor-path.spec.js test/browser/site-vendor.spec.js --workers=1
```
Full original core TypeScript compilation, WinJS/Unicode/other embedded origins
and the overall SITE-07 acceptance remain separate. A path-module proof is not
an exhaustive claim about every Node API or the rest of Monaco.
+93
View File
@@ -0,0 +1,93 @@
import type { VendorManifest } from '../notices.ts'
import type { Archive, Member, Remote } from './archives.ts'
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import vm from 'node:vm'
export interface PathProvenance {
archives: Archive[]
remotes: Remote[]
nodeSource: string
vscodeSource: string
lockSource: string
maps: (Member & { source: string })[]
outputs: { development: Member, signature: string, offset: number, target: { path: string, sha256: string }, editor: Member }[]
component: { name: string, version: string, tarball: string, assets: string[], notices: string[] }
attribution: { source: string, target: string, sha256: string }
legacyNotice: { source: string, target: string, sha256: string }
fixtures: { path: string, sha256: string }
}
export interface PathCase { namespace: 'win32' | 'posix', method: string, args: unknown[], expected: unknown }
// Evaluate the frozen Node module for valid deterministic path calls only. This
// harness does not emulate Node's error messages, host cwd or drive environment.
export function nodePathResults(source: string, cases: Omit<PathCase, 'expected'>[]): unknown[] {
const constants = { CHAR_UPPERCASE_A: 65, CHAR_LOWERCASE_A: 97, CHAR_UPPERCASE_Z: 90, CHAR_LOWERCASE_Z: 122, CHAR_DOT: 46, CHAR_FORWARD_SLASH: 47, CHAR_BACKWARD_SLASH: 92, CHAR_COLON: 58, CHAR_QUESTION_MARK: 63 }
const module = { exports: {} as Record<string, Record<string, (...args: unknown[]) => unknown>> }
vm.runInNewContext(source, {
module,
process: { platform: 'linux', env: {}, cwd: () => { throw new Error('Non-deterministic path cwd') } },
require: (name: string) => {
if (name === 'internal/constants')
return constants
if (name === 'internal/validators')
return { validateString: (value: unknown) => assert.equal(typeof value, 'string', 'Valid path fixture required') }
assert.equal(name, 'internal/errors', 'Unexpected Node path import')
return { codes: { ERR_INVALID_ARG_TYPE: Error } }
},
}, { timeout: 1000 })
return cases.map((entry) => {
assert(['posix', 'win32'].includes(entry.namespace))
assert(['normalize', 'basename', 'dirname', 'extname', 'join', 'relative', 'resolve', 'parse', 'format', 'isAbsolute', 'toNamespacedPath'].includes(entry.method), 'Unexpected path fixture method')
return JSON.parse(JSON.stringify(module.exports[entry.namespace]![entry.method]!(...entry.args)))
})
}
export function readPathProvenance(root: string): PathProvenance {
return JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/monaco-node-path-provenance.json'), 'utf8'))
}
export function preparePathSource(source: string): string {
for (const name of ['isAbsolute', 'join', 'format', 'parse', 'toNamespacedPath', 'delimiter']) {
const line = `export const ${name} = (process.platform === 'win32' ? win32.${name} : posix.${name});\n`
assert.equal(source.split(line).length, 2, `Missing or repeated unused path export: ${name}`)
source = source.replace(line, '')
}
return source
}
export function nodePathLicense(source: string): string {
const boundary = source.indexOf('\n\'use strict\';')
assert(boundary > 0, 'Missing Node source license boundary')
const lines = source.slice(0, boundary).trimEnd().split('\n')
assert(lines.every(line => line.startsWith('//')), 'Unexpected Node license comment')
const license = lines.map(line => line.replace(/^\/\/ ?/, '')).join('\n')
assert(license.startsWith('Copyright Joyent, Inc. and other Node contributors.'))
assert(license.endsWith('USE OR OTHER DEALINGS IN THE SOFTWARE.'))
return license
}
export function verifyMonacoPathNotices(root: string, manifest: VendorManifest): void {
const record = readPathProvenance(root)
const group = manifest.groups.find(group => group.name === 'monaco-editor')
assert(group, 'Missing Monaco path group')
assert.equal(record.component.name, 'nodejs path (Monaco core)')
assert.equal(record.component.version, '14.16.0')
assert.deepEqual(record.component.assets, ['docs/assets/js/vs/editor/editor.main.js', 'docs/assets/js/vs/base/worker/workerMain.js'], 'Wrong Node path assets')
assert.deepEqual(record.component.notices, [record.legacyNotice.target, record.attribution.target], 'Missing Node path terms or source explanation')
assert.deepEqual(group.components?.filter(component => component.name === record.component.name), [record.component], 'Wrong Node path notice binding')
for (const notice of [record.legacyNotice, record.attribution])
assert.deepEqual(group.notices.filter(item => item.target === notice.target), [notice], 'Missing Node path notice')
const license = nodePathLicense(fs.readFileSync(path.join(root, record.nodeSource), 'utf8'))
const legacy = fs.readFileSync(path.join(root, record.legacyNotice.source), 'utf8').replace(/\r\n/g, '\n')
assert(legacy.includes(license), 'Legacy notice omits actual Node path terms')
const port = fs.readFileSync(path.join(root, record.vscodeSource), 'utf8')
const start = port.indexOf('/**\n * Copyright Joyent, Inc. and other Node contributors.')
assert(start >= 0, 'Missing ported Node copyright')
const end = port.indexOf('\n */', start)
assert(end > start, 'Missing ported Node license end')
assert.equal(port.slice(start + 4, end).split('\n').map(line => line.replace(/^ \* ?/, '')).join('\n'), license, 'Ported Node license differs')
assert(port.includes('https://github.com/nodejs/node/blob/v14.16.0/lib/path.js'), 'Wrong ported Node version')
}
@@ -0,0 +1,56 @@
import type { PathCase } from './node-path.ts'
import assert from 'node:assert/strict'
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 { nodePathResults, preparePathSource, readPathProvenance, verifyMonacoPathNotices } from './node-path.ts'
const root = fileURLToPath(new URL('../../../', import.meta.url))
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use reproduce-node-path.ts [--fetch]')
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
const record = readPathProvenance(root)
const cache = new ArchiveCache(root, path.join(root, 'refactor/.cache/monaco-review'))
await cache.verify(record.archives, record.remotes, process.argv.includes('--fetch'))
verifyMonacoPathNotices(root, JSON.parse(fs.readFileSync(path.join(root, 'scripts/site-vendor/manifest.json'), 'utf8')))
const compiler = record.archives.find(archive => archive.name === 'typescript')
assert(compiler && compiler.version === '4.5.0-dev.20211021', 'Wrong path compiler')
const lock = fs.readFileSync(path.join(root, record.lockSource), 'utf8')
assert(lock.includes(`typescript@^4.5.0-dev.20211021:\n version "4.5.0-dev.20211021"`) && lock.includes(`integrity ${compiler.integrity}`), 'Path compiler not in upstream lock')
const isolated = new ArchiveCache(root, path.join(cache.directory, 'core-compiler'))
const archiveFile = `${compiler.name}-${compiler.version}.tgz`
fs.copyFileSync(path.join(cache.directory, archiveFile), path.join(isolated.directory, archiveFile))
isolated.extractCompiler(compiler)
const require = createRequire(path.join(isolated.directory, 'compiler/entry.cjs'))
const ts = require('typescript') as typeof import('typescript')
assert.equal(ts.version, compiler.version)
const prepared = preparePathSource(fs.readFileSync(path.join(root, record.vscodeSource), 'utf8'))
assert.equal(record.maps.length, 2)
for (const member of record.maps) {
const map: { sources: string[], sourcesContent: string[] } = JSON.parse(cache.member(member).toString('utf8'))
assert.equal(map.sources.filter(source => source === member.source).length, 1, 'Missing or repeated mapped path source')
assert.equal(map.sourcesContent[map.sources.indexOf(member.source)], prepared, 'Prepared path source differs')
}
const emitted = ts.transpileModule(prepared, { compilerOptions: { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.AMD, newLine: ts.NewLineKind.LineFeed } }).outputText
const anonymous = 'define(["require", "exports", "vs/base/common/process"],'
assert.equal(emitted.split(anonymous).length, 2, 'Unexpected path emission signature')
assert.equal(record.outputs.length, 2)
const outputs = record.outputs.map((output) => {
assert(/^define\(__m\[\d+\/\*vs\/base\/common\/path\*\/\], __M\(\[0\/\*require\*\/,1\/\*exports\*\/,\d+\/\*vs\/base\/common\/process\*\/\]\),$/.test(output.signature), 'Unexpected path bundle signature')
const fragment = emitted.replace(anonymous, output.signature).trimEnd()
const development = cache.member(output.development).toString('utf8')
assert.equal(development.indexOf(fragment), output.offset, 'Compiled path module differs')
assert(output.offset > 0 && !development.includes(fragment, output.offset + 1), 'Missing or repeated compiled path module')
const target = fs.readFileSync(path.join(root, output.target.path))
assert.equal(hash(target), output.target.sha256, 'Shipped path-bearing asset changed')
assert.deepEqual(target, cache.member(output.editor), 'Shipped path-bearing asset differs from archive')
return { path: output.target.path, offset: output.offset, moduleBytes: new TextEncoder().encode(fragment).length, exactModule: true, sha256: hash(target) }
})
const fixtureBytes = fs.readFileSync(path.join(root, record.fixtures.path))
assert.equal(hash(fixtureBytes), record.fixtures.sha256, 'Node path fixtures changed')
const fixtures: PathCase[] = JSON.parse(fixtureBytes.toString('utf8'))
assert.deepEqual(nodePathResults(fs.readFileSync(path.join(root, record.nodeSource), 'utf8'), fixtures), fixtures.map(fixture => fixture.expected), 'Original Node path results differ')
console.log(JSON.stringify({ archives: record.archives.length, gitSources: record.remotes.length, maps: record.maps.length, removedUnusedExports: 6, outputs, fixtures: fixtures.length, existingLicenseTextMatches: true, compiler: ts.version, fullCoreTypeScriptBuild: false, unmodifiedNodeRuntimeClaimed: false }))