1import { readFile } from 'node:fs/promises'; 2import { dirname, resolve } from 'path'; 3import { fileURLToPath } from 'url'; 4import { parse } from 'acorn'; 5import dependencyTree from 'dependency-tree'; 6const __dirname = dirname(fileURLToPath(import.meta.url)); 7 8function getExports(code) { 9 // parse it with acorn 10 let ast; 11 try { 12 ast = parse(code, { 13 ecmaVersion: 11, 14 sourceType: 'module', 15 }); 16 } catch (err) { 17 return [`acorn parse error: ${err.name}: ${err.messsage}`]; 18 } 19 20 // find all names exported in the file 21 return ast.body 22 .filter((node) => node?.type === 'ExportNamedDeclaration') 23 .map((node) => { 24 const { declaration } = node; 25 if (!declaration) { 26 // e.g. "export { Fraction, controls }" 27 return []; 28 } 29 switch (declaration.type) { 30 case 'VariableDeclaration': 31 return declaration.declarations 32 .map((d) => { 33 switch (d.id.type) { 34 case 'Identifier': 35 return d.id.name; 36 case 'ObjectPattern': 37 return d.id.properties.map((p) => p.value.name); 38 default: 39 return 'unknown declaration: ' + declaration; 40 } 41 }) 42 .flat(); 43 default: 44 // FunctionDeclaration, ClassDeclaration 45 return declaration.id.name; 46 } 47 }) 48 .flat(); 49} 50 51function isDocumented(name, docs) { 52 return docs.find( 53 (d) => d.name === name || d.tags?.find((t) => t.title === 'synonyms' && t.value.split(', ').includes(name)), 54 ); 55} 56 57async function getUndocumented(path, docs) { 58 try { 59 // load the code of pattern.mjs as a string 60 const code = await readFile(path, 'utf8'); 61 return getExports(code).filter((name) => !isDocumented(name, docs)); 62 } catch (err) { 63 return [`parse error: ${err.name}: ${err.message}`]; 64 } 65} 66 67// read doc.json file 68const { docs } = JSON.parse(await readFile(resolve(__dirname, '..', 'doc.json'), 'utf8')); 69 70const paths = dependencyTree.toList({ 71 filename: 'index.mjs', 72 // filename: 'packages/core/index.mjs', 73 directory: resolve(__dirname), 74 filter: (path) => !path.includes('node_modules'), 75}); 76// const paths = ['../packages/core/pattern.mjs', '../packages/core/hap.mjs'].map((rel) => resolve(__dirname, rel)); 77 78const undocumented = Object.fromEntries( 79 await Promise.all( 80 paths.map(async (path) => [path.replace(resolve(__dirname, '..'), ''), await getUndocumented(path, docs)]), 81 ), 82); 83 84console.log(JSON.stringify(undocumented, null, 2));