Files
ycode/scripts/rea/recover-project-styles.cjs
T

61 lines
3.2 KiB
JavaScript

// Consume retained REA evidence only; never reads the WorkBuddy installation.
const fs = require('node:fs')
const path = require('node:path')
const crypto = require('node:crypto')
const postcss = require('postcss')
const assert = require('node:assert/strict')
const evidence = JSON.parse(fs.readFileSync(path.resolve('.tmp/rea-todo20-new-project-dialog.json'), 'utf8').replace(/^\uFEFF/, ''))
assert.equal(evidence.evidence_id, 'ev_bc0f5546bb714cad4634168969a115be8f34e307bc09f51caf1457f5d3ed61ec')
const sheet = evidence.normalized_result.styles.items.find(item => item.file_path.endsWith('collab-YMJYRnW_.css'))
assert.ok(sheet?.source.included)
const source = sheet.source.artifact.text
const sha = crypto.createHash('sha256').update(source).digest('hex')
assert.equal(sha, '4737f6a8f8f993de933e5ebae0d94b96cb39788e160470490f607508537e5377')
const root = postcss.parse(source)
// The shared modal lives outside collab's chunk; recover its rules from the same evidence.
let modalRules = 0
for (const item of evidence.normalized_result.styles.items) {
if (item === sheet || !item.source?.included || !item.source.artifact?.text) continue
const text = item.source.artifact.text
if (!text.includes('.collab-modal__') && !text.includes('.workbuddy-topbar')) continue
assert.equal(crypto.createHash('sha256').update(text).digest('hex'), item.source.artifact.sha256)
const parsed = postcss.parse(text)
function select(container) {
const result = container.clone({ nodes: [] })
for (const node of container.nodes || []) {
if (node.type === 'rule' && /\.collab-modal__|\.workbuddy-topbar(?:[\s.:#[\]>+~,-]|-|$)/.test(node.selector)) {
result.append(node.clone()); modalRules++
} else if (node.type === 'atrule' && node.nodes && !/keyframes$/i.test(node.name)) {
const nested = select(node)
if (nested.nodes.length) result.append(nested)
}
}
return result
}
root.append(select(parsed).nodes)
}
let count = 0
root.walkRules(rule => {
// Preserve keyframe selectors and all enclosing media/supports conditions.
for (let parent = rule.parent; parent; parent = parent.parent) {
if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) return
}
rule.selectors = rule.selectors.map(selector => {
// Body/theme conditions are ancestors of the surface, never its descendants.
const ancestor = selector.match(/^(body(?:\[[^\]]+\]|\.[\w-]+|:[\w-]+(?:\([^)]*\))?)*|html(?:\[[^\]]+\]|\.[\w-]+)*|:root)\s+/)
return ancestor
? `${ancestor[1]} :where(.yc-project-surface) ${selector.slice(ancestor[0].length)}`
: `:where(.yc-project-surface) ${selector}`
})
count++
})
const output = `/* Generated from WorkBuddy 5.8.3; REA ${evidence.evidence_id}; source SHA-256 ${sha}. */\n${root.toString()}\n`
const destination = path.resolve('src/renderer/src/projects/source-projects-recovered.css')
if (process.argv.includes('--check')) {
assert.equal(fs.readFileSync(destination, 'utf8'), output)
} else {
fs.mkdirSync(path.dirname(destination), { recursive: true })
fs.writeFileSync(destination, output)
}
console.log(`PASS project styles: ${count} scoped rules (${modalRules} shared modal); source SHA verified${process.argv.includes('--check') ? '; generated file unchanged' : ''}`)