mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-15 17:16:16 -08:00
build(site): [SITE-07] reproduce console-feed vendor modules
This commit is contained in:
1 parent
3dba82a453
commit
b47ede011a
13 files changed
+663
-9
No files matched your search
@@ -67,3 +67,34 @@ No new dependency is required. Existing esbuild 0.27.7 and TypeScript 5.9.3 prov
|
||||
generation and structural parsing. Full console dependency provenance and notices
|
||||
remain SITE-07 / VENDOR-08; this build does not close them. See
|
||||
`refactor/console-modernization.md` for contracts and evidence.
|
||||
|
||||
## Historical source reconstruction
|
||||
|
||||
`refactor/baselines/console-feed-provenance.json` maps all 32 modules reachable
|
||||
through relative imports from m6b6 to the official console-feed 3.2.2 archive.
|
||||
Their rebuilt bodies exactly match the frozen bundle. The 68 remaining vendor
|
||||
modules and Parcel wrapper need further provenance; this does not establish a
|
||||
unique original installed version or recover its missing lockfile.
|
||||
|
||||
```sh
|
||||
node scripts/site-vendor/console/reproduce.ts --fetch
|
||||
node scripts/site-vendor/console/reproduce.ts
|
||||
yarn test:site-console
|
||||
```
|
||||
|
||||
The first command downloads two pinned npm archives into the dedicated ignored
|
||||
`refactor/.cache/console-feed-reproduction/` directory. The second uses that cache
|
||||
offline. Both verify SHA-512 SRI, archive SHA-256, source member fingerprints,
|
||||
compiler bytes and exact output. They load Terser 3.17.0 as a historical build
|
||||
tool with the already installed source-map 0.6.1; they do not install dependencies,
|
||||
change Yarn or execute the archived console-feed runtime. Canonical Node is required.
|
||||
The minification recipe is recovered from Parcel 1.12.5's archive; this proves a
|
||||
reproducing transform, not that the original author used precisely that Parcel
|
||||
version. `provenance.ts` separately verifies complete traversal, relative source
|
||||
mapping and external dependency boundaries. Missing, unreachable, duplicate or
|
||||
changed evidence fails instead of silently shrinking the comparison.
|
||||
|
||||
The archived LICENSE is preserved verbatim with its Facebook attribution under
|
||||
`refactor/baselines/site-vendor/console-feed-3.2.2-LICENSE.txt`. Do not rewrite it
|
||||
or infer it covers every embedded/external component. Complete notices remain
|
||||
open, including the bundled replicator and remaining dependencies.
|
||||
@@ -0,0 +1,84 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import path from 'node:path'
|
||||
import ts from 'typescript'
|
||||
|
||||
export interface Module { id: string, body: string, dependencies: Record<string, string> }
|
||||
export interface Source { id: string, member: string, sourceSha256: string, generatedSha256: string, bodySha256: string }
|
||||
export interface External { from: string, dependency: string, id: string }
|
||||
export interface Archive { name: string, version: string, tarball: string, integrity: string, sha256: string }
|
||||
export const hash = (bytes: string | Uint8Array) => createHash('sha256').update(bytes).digest('hex')
|
||||
|
||||
export function verifyArchive(bytes: Uint8Array, archive: Archive) {
|
||||
const [algorithm, expected] = archive.integrity.split('-')
|
||||
assert(algorithm === 'sha512' && expected, 'Expected SHA-512 archive integrity')
|
||||
assert.equal(createHash(algorithm).update(bytes).digest('base64'), expected, `Archive integrity changed: ${archive.name}`)
|
||||
assert.equal(hash(bytes), archive.sha256, `Archive bytes changed: ${archive.name}`)
|
||||
}
|
||||
|
||||
export function parcelModules(text: string): Map<string, Module> {
|
||||
const source = ts.createSourceFile('console.js', text, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
|
||||
let table: ts.ObjectLiteralExpression | undefined
|
||||
function visit(node: ts.Node) {
|
||||
if (ts.isObjectLiteralExpression(node) && node.properties.some(property => property.name && ts.isStringLiteral(property.name) && property.name.text === 'Focm')) {
|
||||
assert(!table, 'Duplicate Parcel module tables')
|
||||
table = node
|
||||
}
|
||||
ts.forEachChild(node, visit)
|
||||
}
|
||||
visit(source)
|
||||
assert(table, 'Missing Parcel module table')
|
||||
const result = new Map<string, Module>()
|
||||
for (const property of table.properties) {
|
||||
assert(ts.isPropertyAssignment(property) && ts.isStringLiteral(property.name) && ts.isArrayLiteralExpression(property.initializer))
|
||||
const [fn, deps] = property.initializer.elements
|
||||
assert(fn && ts.isFunctionExpression(fn) && deps && ts.isObjectLiteralExpression(deps))
|
||||
const id = property.name.text
|
||||
assert(!result.has(id), `Duplicate Parcel module: ${id}`)
|
||||
const dependencies: Record<string, string> = {}
|
||||
for (const dep of deps.properties) {
|
||||
assert(ts.isPropertyAssignment(dep) && ts.isStringLiteral(dep.name) && ts.isStringLiteral(dep.initializer))
|
||||
assert(!(dep.name.text in dependencies), 'Duplicate Parcel dependency')
|
||||
dependencies[dep.name.text] = dep.initializer.text
|
||||
}
|
||||
result.set(id, { id, dependencies, body: text.slice(fn.body.getStart(source) + 1, fn.body.end - 1).trim() })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function verifyModules(modules: Map<string, Module>, sources: Source[], external: External[], read: (member: string) => string, compile: (source: string) => string) {
|
||||
const mapped = new Map(sources.map(source => [source.id, source]))
|
||||
assert.equal(mapped.size, sources.length, 'Duplicate source mapping')
|
||||
const visited = new Set<string>()
|
||||
const dependencies: External[] = []
|
||||
function visit(id: string) {
|
||||
if (visited.has(id))
|
||||
return
|
||||
const item = mapped.get(id)
|
||||
const module = modules.get(id)
|
||||
assert(item && module, `Missing console-feed module mapping: ${id}`)
|
||||
assert(item.member.startsWith('package/lib/') && item.member.endsWith('.js') && path.posix.normalize(item.member) === item.member, 'Invalid source member')
|
||||
visited.add(id)
|
||||
const source = read(item.member)
|
||||
assert.equal(hash(source), item.sourceSha256, `Source changed: ${item.member}`)
|
||||
const compiled = compile(source)
|
||||
assert.equal(hash(compiled), item.generatedSha256, `Compiler output changed: ${id}`)
|
||||
assert.equal(hash(module.body), item.bodySha256, `Frozen module changed: ${id}`)
|
||||
assert.equal(compiled, module.body, `Rebuilt module differs: ${id}`)
|
||||
for (const [dependency, child] of Object.entries(module.dependencies)) {
|
||||
assert(modules.has(child), `Unresolved Parcel dependency: ${child}`)
|
||||
if (!dependency.startsWith('.')) {
|
||||
dependencies.push({ from: id, dependency, id: child })
|
||||
continue
|
||||
}
|
||||
const base = path.posix.join(path.posix.dirname(item.member), dependency)
|
||||
const target = mapped.get(child)?.member
|
||||
assert(target && [base, `${base}.js`, `${base}/index.js`].includes(target), `Source dependency mapping changed: ${id} ${dependency}`)
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
visit('m6b6')
|
||||
assert.equal(visited.size, sources.length, 'Unreachable source mapping')
|
||||
assert.deepEqual(dependencies, external, 'External dependency boundary changed')
|
||||
return visited.size
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { Archive, External, Source } from './provenance.ts'
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
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 { hash, parcelModules, verifyArchive, verifyModules } from './provenance.ts'
|
||||
|
||||
interface CompilerOptions { warnings: boolean, safari10: boolean, mangle: { toplevel: boolean } }
|
||||
interface Provenance {
|
||||
archive: Archive
|
||||
frozen: { path: string, sha256: string }
|
||||
compiler: { archive: Archive, member: string, sha256: string, sourceMapVersion: string, options: CompilerOptions }
|
||||
sources: Source[]
|
||||
external: External[]
|
||||
}
|
||||
interface Minifier { minify: (source: string, options: CompilerOptions) => { code?: string, error?: unknown } }
|
||||
|
||||
const root = fileURLToPath(new URL('../../../', import.meta.url))
|
||||
const record: Provenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-feed-provenance.json'), 'utf8'))
|
||||
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use reproduce.ts [--fetch]')
|
||||
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
|
||||
const cacheRoot = fs.realpathSync(path.join(root, 'refactor/.cache'))
|
||||
const cache = path.join(cacheRoot, 'console-feed-reproduction')
|
||||
fs.mkdirSync(cache, { recursive: true })
|
||||
assert.equal(fs.realpathSync(cache), cache, 'Reproduction cache must not redirect')
|
||||
for (const archive of [record.archive, record.compiler.archive]) {
|
||||
const target = path.join(cache, `${archive.name}-${archive.version}.tgz`)
|
||||
if (process.argv.includes('--fetch')) {
|
||||
const response = await fetch(archive.tarball)
|
||||
assert(response.ok, `Archive request failed: ${response.status}`)
|
||||
const bytes = new Uint8Array(await response.arrayBuffer())
|
||||
verifyArchive(bytes, archive)
|
||||
fs.writeFileSync(target, bytes)
|
||||
}
|
||||
verifyArchive(fs.readFileSync(target), archive)
|
||||
}
|
||||
const readMember = (name: string, member: string) => execFileSync('tar', ['-xOzf', path.join(cache, name), member], { maxBuffer: 8 * 1024 * 1024 })
|
||||
const compilerBytes = readMember('terser-3.17.0.tgz', record.compiler.member)
|
||||
assert.equal(hash(compilerBytes), record.compiler.sha256, 'Compiler member changed')
|
||||
const compilerPath = path.join(cache, 'terser.cjs')
|
||||
fs.writeFileSync(compilerPath, compilerBytes)
|
||||
const require = createRequire(compilerPath)
|
||||
assert.equal(require('source-map/package.json').version, record.compiler.sourceMapVersion, 'Source-map dependency changed')
|
||||
|
||||
const { minify } = require(compilerPath) as Minifier
|
||||
|
||||
const frozen = fs.readFileSync(path.join(root, record.frozen.path), 'utf8')
|
||||
assert.equal(hash(frozen), record.frozen.sha256, 'Frozen console changed')
|
||||
assert.equal(record.sources.length, 32, 'Incomplete console-feed scope')
|
||||
const count = verifyModules(parcelModules(frozen), record.sources, record.external, member => readMember('console-feed-3.2.2.tgz', member).toString('utf8'), (source) => {
|
||||
const result = minify(source, record.compiler.options)
|
||||
if (result.error)
|
||||
throw result.error
|
||||
assert(typeof result.code === 'string')
|
||||
return result.code
|
||||
})
|
||||
console.log(JSON.stringify({ exactModules: count, totalVendorModules: 100, archive: record.archive.sha256, licenseClosure: false }))
|
||||
Reference in new issue
Block a user