61 lines
3.2 KiB
JavaScript
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' : ''}`)
|