mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 23:36:15 -08:00
build(site): [SITE-07] preserve exact Stack Overflow revision and attribution
This commit is contained in:
1 parent
3136eb2402
commit
0503fe8aa8
30 files changed
+1941
-27
No files matched your search
@@ -60,11 +60,12 @@ assert.deepEqual(consoleGroup?.components?.map(component => component.name).sort
|
||||
'scheduler',
|
||||
'shallowequal',
|
||||
'simple-html-tokenizer',
|
||||
'stackoverflow-custom-stringify',
|
||||
'styled-components',
|
||||
'stylis (Emotion fork)',
|
||||
'stylis-rule-sheet',
|
||||
], 'Do not silently drop verified console component attribution')
|
||||
assert.equal(consoleGroup?.notices.length, 42, 'Missing reviewed console notice')
|
||||
assert.equal(consoleGroup?.notices.length, 45, 'Missing reviewed console notice')
|
||||
const vconsoleNotices = manifest.groups.find(group => group.name === 'vconsole')?.notices.map(notice => notice.target)
|
||||
for (const name of ['LICENSE', 'MIT-LICENSE', 'ATTRIBUTION.md'])
|
||||
assert(vconsoleNotices?.includes(`docs/licenses/vconsole/${name}`), `Missing vConsole notice: ${name}`)
|
||||
|
||||
@@ -23,7 +23,7 @@ files retain their exact upstream bytes, including final blank lines.
|
||||
`yarn check:site-notices`. The write command cannot bless altered vendor assets;
|
||||
review the new archive and license evidence before changing the manifest. The
|
||||
CLI prevents accidentally dropping any of the three groups, the ten reviewed
|
||||
Monaco/vConsole components, the 42 identified console components or their notice
|
||||
Monaco/vConsole components, the 43 identified console components or their notice
|
||||
count, and vConsole's original license, supplemental MIT body and attribution.
|
||||
Component references require their runtime assets and every notice before writing.
|
||||
Source notices
|
||||
@@ -60,7 +60,7 @@ The desktop console's owned TS entry/view and lifecycle now build through
|
||||
`build:console` / `check:console`; see [console maintenance](console/README.md).
|
||||
Its other 100 Parcel modules remain frozen and now reproduce exactly from fixed
|
||||
archives, including the Parcel loader. Its package/loader licenses, two embedded
|
||||
headers, embedded dependency licenses and upstream author notices now ship as 42 files
|
||||
headers, embedded dependency licenses and upstream author notices now ship as 45 files
|
||||
under `docs/licenses/console/`. Full embedded
|
||||
attribution remains open; the generated index explicitly preserves that boundary.
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ The fixed upstream is `refactor/baselines/site-vendor/console-original.js`, chec
|
||||
by SHA-256. Only W5CS (view) and Focm (entry) bodies change. The other 100 modules,
|
||||
their dependency maps and Parcel runtime remain byte-for-byte intact. The dead
|
||||
`/index.js.map` trailer is removed; a complete upstream source map is unavailable.
|
||||
A non-executable attribution comment is appended for the matched Stack Overflow
|
||||
snippet. The build test compares every other byte outside the two owned bodies.
|
||||
The entry injects existing React/ReactDOM 17.0.2, console-feed and
|
||||
styled-components 5.3.3. RxPG supplies the historical parser and V2JG the method
|
||||
list. Root `@types/react` 19.1.10 is used for type checking only; no React 19 runtime
|
||||
@@ -185,6 +187,18 @@ Gary Court's original README/MIT text and Austin Appleby's public-domain header
|
||||
are preserved for the two explicit MurmurHash source references. Original sources
|
||||
and excerpts are hashed; attribution is never inferred from the outer Emotion MIT.
|
||||
|
||||
There are now 42 console notice outputs. Remaining follow-ups are the Component
|
||||
Stack Overflow reference and final embedded-source review. Do not close VENDOR-08
|
||||
solely because all Parcel modules reproduce.
|
||||
`stackoverflow.ts` verifies a fixed answer revision's author, timestamp, license,
|
||||
body and code, then locates the unique module declaration for comparison.
|
||||
The customStringify function matches answer 48254637 revision 5, dated 2018-09-19,
|
||||
under CC BY-SA 4.0. Revision 1 used Map and the current revision changes duplicate
|
||||
handling; neither is the matching source. TypeScript 4.1.2 reproduces the function
|
||||
exactly before minification. `console-stackoverflow-provenance.json` retains the
|
||||
minimal revision response, source and full license; the public attribution credits
|
||||
Alexander Mills and Rob W. A generated bundle comment retains the license and
|
||||
links even when the script is read separately from the site notice index.
|
||||
|
||||
There are now 45 console notice outputs for 43 components. This includes a
|
||||
CC BY-SA snippet; never describe the bundle as MIT-only. Final embedded-source
|
||||
and mixed-license distribution review remains open. The shallowequal 1.1.0 README
|
||||
explicitly identifies react-pure-render as its code origin; review that original
|
||||
notice before closing VENDOR-08. Exact Parcel module reproduction alone is insufficient.
|
||||
@@ -26,6 +26,8 @@ export function moduleRanges(code: string) {
|
||||
return ranges
|
||||
}
|
||||
|
||||
export const attributionBanner = '\n/*! customStringify: Alexander Mills, with credit to Rob W. CC BY-SA 4.0: https://creativecommons.org/licenses/by-sa/4.0/ . Source revision: https://api.stackexchange.com/2.3/revisions/8FD5F52A-B16F-4C66-AB48-830EABB36CC0?site=stackoverflow&filter=withbody . ES3 compilation and minification; notices and source: /licenses/console/stackoverflow/ATTRIBUTION.md . Other component notices: /THIRD_PARTY_NOTICES.md */\n'
|
||||
|
||||
export async function generateConsole(root: string) {
|
||||
const upstream = fs.readFileSync(path.join(root, 'refactor/baselines/site-vendor/console-original.js'), 'utf8')
|
||||
assert.equal(createHash('sha256').update(upstream).digest('hex'), upstreamSha256, 'Frozen console source changed')
|
||||
@@ -44,5 +46,5 @@ export async function generateConsole(root: string) {
|
||||
for (const [id, range] of [...ranges].sort((a, b) => b[1].start - a[1].start))
|
||||
output = output.slice(0, range.start) + bodies.get(id) + output.slice(range.end)
|
||||
assert(output.endsWith(obsoleteMap), 'Unexpected original console source map trailer')
|
||||
return output.slice(0, -obsoleteMap.length)
|
||||
return output.slice(0, -obsoleteMap.length) + attributionBanner
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type { EmbeddedNotice } from './embedded-notices.ts'
|
||||
import type { Member, SourceMapRecord, TransformedSource } from './embedded-sources.ts'
|
||||
import type { Archive, External, Source } from './provenance.ts'
|
||||
import type { BabelRuntime, EsmSource } from './reconstruction.ts'
|
||||
import type { Revision } from './stackoverflow.ts'
|
||||
import assert from 'node:assert/strict'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
@@ -16,6 +17,7 @@ import { extractNotice } from './embedded-notices.ts'
|
||||
import { verifyMappedSources, verifyTransformedSources } from './embedded-sources.ts'
|
||||
import { hash, parcelModules, verifyArchive, verifyModules, verifyPackageEdges } from './provenance.ts'
|
||||
import { reconstructModule, verifyPrelude } from './reconstruction.ts'
|
||||
import { moduleStatement, revisionSnippet } from './stackoverflow.ts'
|
||||
|
||||
interface CompilerOptions { warnings: boolean, safari10: boolean, mangle: { toplevel: boolean }, output?: { comments: boolean } }
|
||||
interface Provenance {
|
||||
@@ -58,6 +60,16 @@ interface DerivedProvenance {
|
||||
cache: { reference: Member, upstreamUrl: string, notice: string }
|
||||
}
|
||||
interface HistoricalTypeScript { version: string, transpileModule: (source: string, options: { compilerOptions: DerivedProvenance['compiler']['options'] }) => { outputText: string } }
|
||||
interface StackOverflowProvenance {
|
||||
revision: Revision
|
||||
apiUrl: string
|
||||
response: { source: string, sha256: string }
|
||||
source: string
|
||||
target: Member
|
||||
binding: string
|
||||
compiledSha256: string
|
||||
license: { url: string, source: string, sha256: string }
|
||||
}
|
||||
|
||||
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'))
|
||||
@@ -65,6 +77,7 @@ const common: SourceGroup<CommonSource> = JSON.parse(fs.readFileSync(path.join(r
|
||||
const esm: EsmProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-esm-provenance.json'), 'utf8'))
|
||||
const embeddedSources: EmbeddedProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-embedded-sources.json'), 'utf8'))
|
||||
const derived: DerivedProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-derived-attribution.json'), 'utf8'))
|
||||
const stackOverflow: StackOverflowProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-stackoverflow-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'))
|
||||
@@ -275,4 +288,16 @@ const publicDomain = extractNotice(gitSource(derived.hash.source), derived.hash.
|
||||
assert.equal(fs.readFileSync(path.join(root, derived.hash.notice.source), 'utf8'), publicDomain, 'Public domain notice changed')
|
||||
assert(derivedMember(derived.cache.reference).toString('utf8').includes(derived.cache.upstreamUrl), 'Rule-sheet source attribution changed')
|
||||
assert(fs.readFileSync(path.join(root, derived.cache.notice), 'utf8').includes('Copyright (c) 2016 Sultan Tarimo'), 'Rule-sheet attribution missing')
|
||||
console.log(JSON.stringify({ exactModules: identified.size, consoleFeed: count, commonjs: commonCount, esm: esmCount, parcelPrelude: true, packageEdges: true, embeddedNotices: embedded.notices.length, embeddedMappedSources: mappedCount, embeddedTransformedSources: transformedCount, derivedGitSources: gitSources.size, replicatorFork: true, emotionStylisSource: true, unresolved: 0, licenseClosure: false }))
|
||||
const answerSnapshot = fs.readFileSync(path.join(root, stackOverflow.response.source))
|
||||
assert.equal(hash(answerSnapshot), stackOverflow.response.sha256, 'Frozen answer revision changed')
|
||||
const snippet = revisionSnippet(answerSnapshot, stackOverflow.revision)
|
||||
assert.equal(fs.readFileSync(path.join(root, stackOverflow.source), 'utf8'), snippet, 'Frozen answer snippet changed')
|
||||
const compiledSnippet = historicalTs.transpileModule(snippet, { compilerOptions: tsCompiler.options }).outputText.trimEnd()
|
||||
assert.equal(hash(compiledSnippet), stackOverflow.compiledSha256, 'Compiled answer snippet changed')
|
||||
assert.equal(compiledSnippet, moduleStatement(derivedMember(stackOverflow.target).toString('utf8'), stackOverflow.binding), 'Answer snippet differs from archived function')
|
||||
assert.equal(hash(fs.readFileSync(path.join(root, stackOverflow.license.source))), stackOverflow.license.sha256, 'Frozen CC license changed')
|
||||
if (process.argv.includes('--fetch')) {
|
||||
revisionSnippet(await download(stackOverflow.apiUrl), stackOverflow.revision)
|
||||
assert.equal(hash(await download(stackOverflow.license.url)), stackOverflow.license.sha256, 'Upstream CC license changed')
|
||||
}
|
||||
console.log(JSON.stringify({ exactModules: identified.size, consoleFeed: count, commonjs: commonCount, esm: esmCount, parcelPrelude: true, packageEdges: true, embeddedNotices: embedded.notices.length, embeddedMappedSources: mappedCount, embeddedTransformedSources: transformedCount, derivedGitSources: gitSources.size, replicatorFork: true, emotionStylisSource: true, stackOverflowRevision: stackOverflow.revision.number, unresolved: 0, licenseClosure: false }))
|
||||
@@ -0,0 +1,35 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import ts from 'typescript'
|
||||
import { hash } from './provenance.ts'
|
||||
|
||||
export interface Revision {
|
||||
guid: string
|
||||
postId: number
|
||||
number: number
|
||||
created: number
|
||||
userId: number
|
||||
license: string
|
||||
bodySha256: string
|
||||
snippetSha256: string
|
||||
}
|
||||
|
||||
export function revisionSnippet(response: Uint8Array, expected: Revision): string {
|
||||
const data = JSON.parse(Buffer.from(response).toString('utf8')) as { items: { revision_guid: string, post_id: number, revision_number: number, creation_date: number, user: { user_id: number }, content_license: string, body: string }[] }
|
||||
assert.equal(data.items.length, 1, 'Expected one fixed answer revision')
|
||||
const item = data.items[0]!
|
||||
assert.deepEqual([item.revision_guid, item.post_id, item.revision_number, item.creation_date, item.user.user_id, item.content_license], [expected.guid, expected.postId, expected.number, expected.created, expected.userId, expected.license], 'Answer revision identity changed')
|
||||
assert.equal(hash(item.body), expected.bodySha256, 'Answer revision body changed')
|
||||
const blocks = [...item.body.matchAll(/<pre><code>([\s\S]*?)<\/code><\/pre>/g)]
|
||||
assert.equal(blocks.length, 1, 'Expected one answer code block')
|
||||
const snippet = blocks[0]![1]!.replaceAll('&', '&')
|
||||
assert.equal(hash(snippet), expected.snippetSha256, 'Answer snippet changed')
|
||||
return snippet
|
||||
}
|
||||
|
||||
export function moduleStatement(source: string, name: string): string {
|
||||
const file = ts.createSourceFile('source.js', source, ts.ScriptTarget.Latest, true)
|
||||
const matches = file.statements.filter(statement => ts.isVariableStatement(statement) && statement.declarationList.declarations.some(declaration => ts.isIdentifier(declaration.name) && declaration.name.text === name))
|
||||
assert.equal(matches.length, 1, 'Expected one module declaration')
|
||||
return matches[0]!.getText(file)
|
||||
}
|
||||
@@ -944,14 +944,14 @@
|
||||
"name": "console",
|
||||
"version": "legacy-vendor-with-TS-adapter",
|
||||
"tarball": "https://github.com/zhw2590582/ArtPlayer/blob/5d6b2f22bc75213ac97cff0bcffa4a1ce2bc786c/docs/assets/js/console.js",
|
||||
"review": "The 100 third-party Parcel modules and loader have exact source reproduction. The two owned adapter modules are built from TypeScript. Preserved notices also cover two embedded license headers, all 17 react-inspector external source-map members, and all seven HTML tokenizer sources from its fixed Git dependency. Embedded attribution review is still incomplete; this inventory is not publication clearance. Modified replicator and Emotion/Stylis copies retain upstream author licenses; the original Emotion online Closure stage is not claimed to be reproducible. MurmurHash credits include Gary Court and Austin Appleby.",
|
||||
"review": "The 100 third-party Parcel modules and loader have exact source reproduction. The two owned adapter modules are built from TypeScript. Preserved notices also cover two embedded license headers, all 17 react-inspector external source-map members, and all seven HTML tokenizer sources from its fixed Git dependency. Embedded attribution review is still incomplete; this inventory is not publication clearance. Modified replicator and Emotion/Stylis copies retain upstream author licenses; the original Emotion online Closure stage is not claimed to be reproducible. MurmurHash credits include Gary Court and Austin Appleby. The customStringify snippet matches Stack Overflow answer revision 5 under CC BY-SA 4.0; its separate attribution, source and full license are supplied. This is a mixed-license bundle, not MIT-only.",
|
||||
"roots": [
|
||||
"docs/assets/js/console.js"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"path": "docs/assets/js/console.js",
|
||||
"sha256": "5644af7bb35bb6d0bcfa7eeae9a21b406ac4fc1b499a6c797a6534c7d33f0678",
|
||||
"sha256": "a5f1388175fd885c03dd21b35fff97dca0b548745588dfca3172579a96537163",
|
||||
"mode": "lf-text"
|
||||
}
|
||||
],
|
||||
@@ -1165,6 +1165,21 @@
|
||||
"source": "refactor/baselines/site-vendor/console-attribution/murmurhash2-public-domain.txt",
|
||||
"target": "docs/licenses/console/murmurhash2/PUBLIC-DOMAIN.txt",
|
||||
"sha256": "50e13b0a1963f0b382255d2425df245dbdfd209201621e8c157687caedbbb949"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/console-stackoverflow/CC-BY-SA-4.0.txt",
|
||||
"target": "docs/licenses/console/stackoverflow/LICENSE",
|
||||
"sha256": "28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/console-stackoverflow/source.txt",
|
||||
"target": "docs/licenses/console/stackoverflow/SOURCE.js",
|
||||
"sha256": "966fdc4a37e65e90c2dc1f6ba9385e572c75ce4c0647d35f3ce566ed9ef7f6bb"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/console-stackoverflow/ATTRIBUTION.txt",
|
||||
"target": "docs/licenses/console/stackoverflow/ATTRIBUTION.md",
|
||||
"sha256": "4ac7ae9b19a1201b6cfe6af1d35ec85d015a2de8fb1aac6b03796145e648a667"
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
@@ -1629,6 +1644,19 @@
|
||||
"notices": [
|
||||
"docs/licenses/console/murmurhash2/PUBLIC-DOMAIN.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stackoverflow-custom-stringify",
|
||||
"version": "answer 48254637 revision 5 (CC BY-SA 4.0)",
|
||||
"tarball": "https://api.stackexchange.com/2.3/revisions/8FD5F52A-B16F-4C66-AB48-830EABB36CC0?site=stackoverflow&filter=withbody",
|
||||
"assets": [
|
||||
"docs/assets/js/console.js"
|
||||
],
|
||||
"notices": [
|
||||
"docs/licenses/console/stackoverflow/LICENSE",
|
||||
"docs/licenses/console/stackoverflow/SOURCE.js",
|
||||
"docs/licenses/console/stackoverflow/ATTRIBUTION.md"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user