chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,204 @@
|
||||
// Recover literal SVG JSX from the existing REA export without executing captured code.
|
||||
import crypto from 'node:crypto'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { parse } from '@babel/parser'
|
||||
|
||||
const inputPath = '.tmp/rea-runtime-scripts.json'
|
||||
const outputPath = 'src/renderer/src/workbuddy/message-actions/SourceActionIcons.tsx'
|
||||
const bundle = JSON.parse(await fs.readFile(inputPath, 'utf8'))
|
||||
const mainItem = bundle.items.find(item => item.file_path?.endsWith('src-0FGg1seX.js'))
|
||||
const chevronItem = bundle.items.find(item => item.file_path?.endsWith('chevron-down-icon-Ve_gE8h-.js'))
|
||||
|
||||
if (!mainItem?.source?.included || !chevronItem?.source?.included) {
|
||||
throw new Error('The REA export is missing one or more captured icon sources')
|
||||
}
|
||||
|
||||
function verifiedSource(item) {
|
||||
const source = item.source.artifact.text
|
||||
const sha256 = crypto.createHash('sha256').update(source).digest('hex')
|
||||
if (sha256 !== item.source.artifact.sha256) throw new Error(`REA source digest mismatch: ${item.file_path}`)
|
||||
return { source, sha256 }
|
||||
}
|
||||
|
||||
function findAssignment(ast, name) {
|
||||
const matches = []
|
||||
function visit(node) {
|
||||
if (!node || typeof node !== 'object') return
|
||||
if (node.type === 'AssignmentExpression' && node.left.type === 'Identifier' && node.left.name === name) {
|
||||
matches.push(node)
|
||||
}
|
||||
for (const value of Object.values(node)) {
|
||||
if (Array.isArray(value)) value.forEach(visit)
|
||||
else if (value && typeof value === 'object') visit(value)
|
||||
}
|
||||
}
|
||||
visit(ast.program)
|
||||
if (matches.length !== 1) throw new Error(`Expected one assignment for ${name}, found ${matches.length}`)
|
||||
return matches[0]
|
||||
}
|
||||
|
||||
function keyName(node) {
|
||||
if (node.type === 'Identifier') return node.name
|
||||
if (node.type === 'StringLiteral' || node.type === 'NumericLiteral') return String(node.value)
|
||||
return undefined
|
||||
}
|
||||
|
||||
function staticValue(node) {
|
||||
if (node.type === 'StringLiteral') return { kind: 'string', value: node.value }
|
||||
if (node.type === 'NumericLiteral') return { kind: 'number', value: node.value }
|
||||
if (node.type === 'BooleanLiteral') return { kind: 'boolean', value: node.value }
|
||||
if (node.type === 'NullLiteral') return { kind: 'null' }
|
||||
if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {
|
||||
return { kind: 'string', value: node.quasis[0].value.cooked ?? node.quasis[0].value.raw }
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function escapeAttribute(value) {
|
||||
return String(value).replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<')
|
||||
}
|
||||
|
||||
function isFactoryCall(node) {
|
||||
if (node.type !== 'CallExpression' || node.arguments.length < 2) return false
|
||||
const callee = node.callee
|
||||
return callee.type === 'SequenceExpression'
|
||||
|| callee.type === 'MemberExpression'
|
||||
|| callee.type === 'Identifier'
|
||||
}
|
||||
|
||||
function renderChild(node, depth, bindings) {
|
||||
if (!node || node.type === 'NullLiteral') return ''
|
||||
if (node.type === 'ArrayExpression') return node.elements.map(child => renderChild(child, depth, bindings)).filter(Boolean).join('\n')
|
||||
if (node.type === 'StringLiteral') return `${' '.repeat(depth)}${node.value}`
|
||||
if (!isFactoryCall(node)) throw new Error(`Unsupported captured SVG child node: ${node.type}`)
|
||||
const tagNode = node.arguments[0]
|
||||
const propsNode = node.arguments[1]
|
||||
const tag = staticValue(tagNode)
|
||||
if (tag?.kind !== 'string' || propsNode.type !== 'ObjectExpression') {
|
||||
throw new Error('Captured SVG element is not a literal JSX factory call')
|
||||
}
|
||||
|
||||
const attrs = []
|
||||
let children
|
||||
for (const property of propsNode.properties) {
|
||||
if (property.type === 'SpreadElement') {
|
||||
const binding = bindings.get(property.argument.name)
|
||||
if (!binding || binding.kind !== 'spread') throw new Error('Unexpected dynamic SVG props spread')
|
||||
attrs.push(`{...${binding.value}}`)
|
||||
continue
|
||||
}
|
||||
if (property.type !== 'ObjectProperty') throw new Error('Unsupported SVG props member')
|
||||
const name = keyName(property.key)
|
||||
if (!name || name === 'key') continue
|
||||
if (name === 'children') {
|
||||
children = property.value
|
||||
continue
|
||||
}
|
||||
const value = staticValue(property.value)
|
||||
if (value?.kind === 'string') {
|
||||
attrs.push(`${name}="${escapeAttribute(value.value)}"`)
|
||||
} else if (value?.kind === 'number') {
|
||||
attrs.push(`${name}={${value.value}}`)
|
||||
} else if (value?.kind === 'boolean') {
|
||||
attrs.push(value.value ? name : `${name}={false}`)
|
||||
} else if (value?.kind === 'null') {
|
||||
attrs.push(`${name}={null}`)
|
||||
} else if (property.value.type === 'Identifier') {
|
||||
const binding = bindings.get(property.value.name)
|
||||
if (!binding || binding.kind !== 'expression') throw new Error(`Unexpected dynamic SVG prop: ${name}`)
|
||||
attrs.push(`${name}={${binding.value}}`)
|
||||
} else {
|
||||
throw new Error(`Unsupported captured SVG attribute value: ${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
const pad = ' '.repeat(depth)
|
||||
const attrText = attrs.map(attribute => `${' '.repeat(depth + 1)}${attribute}`).join('\n')
|
||||
const renderedChildren = children ? renderChild(children, depth + 1, bindings) : ''
|
||||
if (!renderedChildren) return `${pad}<${tag.value}${attrs.length ? `\n${attrText}\n${pad}` : ''} />`
|
||||
return `${pad}<${tag.value}${attrs.length ? `\n${attrText}\n${pad}` : ''}>\n${renderedChildren}\n${pad}</${tag.value}>`
|
||||
}
|
||||
|
||||
function componentSource(assignment, exportedName, sourceLabel, sha256) {
|
||||
const arrow = assignment.right
|
||||
if (arrow.type !== 'ArrowFunctionExpression' || arrow.params.length !== 1) {
|
||||
throw new Error(`Unexpected icon component form: ${assignment.left.name}`)
|
||||
}
|
||||
const param = arrow.params[0]
|
||||
let signature
|
||||
let bindings
|
||||
if (param.type === 'ObjectPattern') {
|
||||
const propsBindings = new Map()
|
||||
for (const property of param.properties) {
|
||||
if (property.type === 'ObjectProperty' && property.value.type === 'Identifier') {
|
||||
const key = keyName(property.key)
|
||||
if (key === 'className' || key === 'style') propsBindings.set(property.value.name, { kind: 'expression', value: key })
|
||||
} else if (property.type === 'RestElement' && property.argument.type === 'Identifier') {
|
||||
propsBindings.set(property.argument.name, { kind: 'spread', value: 'props' })
|
||||
}
|
||||
}
|
||||
signature = '({ className, style, ...props }: IconProps)'
|
||||
bindings = propsBindings
|
||||
} else if (param.type === 'Identifier') {
|
||||
signature = '(props: IconProps)'
|
||||
bindings = new Map([[param.name, { kind: 'spread', value: 'props' }]])
|
||||
} else {
|
||||
throw new Error(`Unsupported icon component parameter: ${assignment.left.name}`)
|
||||
}
|
||||
const body = arrow.body
|
||||
if (!isFactoryCall(body)) throw new Error(`Unsupported icon component body: ${assignment.left.name}`)
|
||||
const svg = renderChild(body, 2, bindings)
|
||||
return `/** REA ${bundle.evidence_id}; ${sourceLabel} SHA-256 ${sha256}; UTF-16 [${assignment.start}, ${assignment.end}). */\nexport function ${exportedName}${signature}: JSX.Element {\n return (\n${svg}\n )\n}\n`
|
||||
}
|
||||
|
||||
const main = verifiedSource(mainItem)
|
||||
const chevron = verifiedSource(chevronItem)
|
||||
const mainAst = parse(main.source, { sourceType: 'module', plugins: ['jsx'] })
|
||||
const chevronAst = parse(chevron.source, { sourceType: 'module', plugins: ['jsx'] })
|
||||
const mainIcons = [
|
||||
['ThumbsUpIcon', 'SourceThumbsUpIcon'],
|
||||
['ThumbsUpSolidIcon', 'SourceThumbsUpSolidIcon'],
|
||||
['ThumbsDownIcon', 'SourceThumbsDownIcon'],
|
||||
['ThumbsDownSolidIcon', 'SourceThumbsDownSolidIcon'],
|
||||
['ResendIcon', 'SourceResendIcon'],
|
||||
['RevertIcon', 'SourceRevertIcon']
|
||||
]
|
||||
const snippets = [
|
||||
"import type { JSX, SVGProps } from 'react'",
|
||||
'',
|
||||
'type IconProps = SVGProps<SVGSVGElement>',
|
||||
'',
|
||||
'// No dedicated regenerate icon declaration was found in the captured bundle.',
|
||||
''
|
||||
]
|
||||
|
||||
for (const [sourceName, exportedName] of mainIcons) {
|
||||
const assignment = findAssignment(mainAst, sourceName)
|
||||
snippets.push(componentSource(
|
||||
assignment,
|
||||
exportedName,
|
||||
'src-0FGg1seX.js',
|
||||
main.sha256
|
||||
))
|
||||
}
|
||||
|
||||
const chevronAssignment = findAssignment(chevronAst, 'ChevronDownIconImpl')
|
||||
snippets.push(componentSource(
|
||||
chevronAssignment,
|
||||
'SourceFoldChevronIcon',
|
||||
'chevron-down-icon-Ve_gE8h-.js',
|
||||
chevron.sha256
|
||||
))
|
||||
|
||||
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
||||
await fs.writeFile(outputPath, `${snippets.join('\n')}\n`)
|
||||
console.log(JSON.stringify({
|
||||
outputPath,
|
||||
evidenceId: bundle.evidence_id,
|
||||
sources: [
|
||||
{ file: 'src-0FGg1seX.js', sha256: main.sha256, icons: mainIcons.length },
|
||||
{ file: 'chevron-down-icon-Ve_gE8h-.js', sha256: chevron.sha256, icons: 1 }
|
||||
],
|
||||
regenerate: 'no dedicated icon declaration found'
|
||||
}, null, 2))
|
||||
Reference in new issue
Block a user