Files
Web_FreeCAD_Bitbybit/scripts/generate-freecad-source-inventory.mjs

75 lines
3.5 KiB
JavaScript

import { readFile, readdir, writeFile, access } from 'node:fs/promises'
import { join, resolve } from 'node:path'
const root = resolve(new URL('..', import.meta.url).pathname)
const sourceRoot = resolve(process.env.FREECAD_SOURCE_DIR || join(root, '.cache/freecad/FreeCAD'))
const outputPath = resolve(process.env.FREECAD_INVENTORY_FILE || join(root, 'config/freecad-source-inventory.json'))
const baselineCommit = '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d'
const cmakeAliases = {
AddonManager: 'ADDONMGR',
JtReader: 'JTREADER',
MeshPart: 'MESH_PART',
PartDesign: 'PART_DESIGN',
TechDraw: 'TECHDRAW',
ReverseEngineering: 'REVERSEENGINEERING',
OpenSCAD: 'OPENSCAD',
}
const read = async (path) => readFile(path, 'utf8')
const exists = async (path) => access(path).then(() => true).catch(() => false)
const moduleRoot = join(sourceRoot, 'src/Mod')
if (!(await exists(moduleRoot))) throw new Error(`FreeCAD source directory is unavailable: ${moduleRoot}`)
const buildOptions = await read(join(sourceRoot, 'cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake'))
const optionDefaults = new Map([...buildOptions.matchAll(/option\(BUILD_([A-Z0-9_]+)[^\n]*?\s(ON|OFF)\)/g)].map((match) => [match[1], match[2] === 'ON']))
const moduleNames = (await readdir(moduleRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort()
const commandPattern = /(?:addCommand|addCommand\s*\(|registerCommand)\s*\(\s*["']([^"']+)["']/g
const commandIds = (text) => [...text.matchAll(commandPattern)].map((match) => match[1]).filter((value, index, values) => values.indexOf(value) === index).sort()
const modules = []
for (const name of moduleNames) {
const modulePath = join(moduleRoot, name)
const option = cmakeAliases[name] || name.toUpperCase()
const cmakePath = join(modulePath, 'CMakeLists.txt')
const appPath = join(modulePath, 'App')
const guiPath = join(modulePath, 'Gui')
const cmake = await exists(cmakePath) ? await read(cmakePath) : ''
const pythonFiles = []
const collectPython = async (directory) => {
if (!(await exists(directory))) return
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name)
if (entry.isDirectory()) await collectPython(path)
else if (entry.name.endsWith('.py')) pythonFiles.push(path)
}
}
await collectPython(modulePath)
const commandNames = []
for (const path of pythonFiles) {
const text = await read(path)
commandNames.push(...commandIds(text))
}
modules.push({
name,
sourcePath: `src/Mod/${name}`,
cmakeOption: `BUILD_${option}`,
cmakeDefault: optionDefaults.has(option) ? optionDefaults.get(option) : null,
hasCMake: Boolean(cmake),
hasApp: await exists(appPath),
hasGui: await exists(guiPath),
pythonFileCount: pythonFiles.length,
commandIds: [...new Set(commandNames)].sort(),
runtimeStatus: 'requires-freecad-reference-probe',
})
}
const inventory = {
schemaVersion: 1,
baseline: { freecadVersion: '1.1.1', commit: baselineCommit, sourcePath: '.cache/freecad/FreeCAD' },
generatedBy: 'scripts/generate-freecad-source-inventory.mjs',
moduleCount: modules.length,
modules,
}
if (modules.length !== 34) throw new Error(`Expected 34 FreeCAD modules, found ${modules.length}.`)
await writeFile(outputPath, `${JSON.stringify(inventory, null, 2)}\n`, 'utf8')
console.log(JSON.stringify({ status: 'source-inventory-generated', output: outputPath, moduleCount: modules.length, commandCount: modules.reduce((total, module) => total + module.commandIds.length, 0) }, null, 2))