@ b062e657e · fetching
current
// Collects data about functions imported from node.exe by all packages installed under the
// current directory. Currently Windows only. See README.md for details.

const childProc = require('child_process');
const fs = require('fs');
const path = require('path');

const rootDir = process.cwd();

summarizeImports();

function summarizeImports() {
    let summaryImports = [];
    let packageNames = fs.readdirSync(path.join(rootDir, 'node_modules'));
    packageNames.forEach(packageName => {
        let packageImports = getImportsForPackage(packageName);
        packageImports.forEach(packageImport => {
            let summaryImport = summaryImports.find(i => i.signature === packageImport.signature);
            if (summaryImport) {
                summaryImport.packages.push(packageName);
            } else {
                packageImport.packages = [packageName];
                summaryImports.push(packageImport);
            }
        });
    });

    console.log('Pkg count,Package names,Imported name,Imported signature');
    summaryImports.sort((a, b) => a.packages.length > b.packages.length ? -1 : 1).forEach(i => {
        console.log(
            i.packages.length + ',"' +
            i.packages.join(', ') + '","' +
            i.name + '","' +
            i.signature + '"');
    });
}

function getImportsForPackage(packageName) {
    let packageNativeModules = getNativeModulesForPackage(packageName);

    let nativeImports = [];
    packageNativeModules.forEach(nativeModule => {
        let moduleImports = getImportsForNativeModule(nativeModule);
        moduleImports.forEach(moduleImport => {
            if (!nativeImports.find(i => i.signature === moduleImport.signature)) {
                nativeImports.push(moduleImport);
            }
        });
    });
    return nativeImports;
}

function getNativeModulesForPackage(packageName) {
    let packageDir = path.join(rootDir, 'node_modules', packageName);
    return findFiles(packageDir, relativePath => {
        return relativePath.endsWith('.node') &&
            !relativePath.startsWith('node_modules') &&
            relativePath.indexOf('ia32') < 0;
    }).map(relativePath => path.join(packageDir, relativePath));
}

function getImportsForNativeModule(modulePath) {
    const dumpbinExe =
        'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\dumpbin.exe';
    const undnameExe =
        'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\undname.exe';
    const importRegex = /^ *[0-9A-F]+ +0 +([^ ]+) *$/;
    const undnameRegex = /is :- "([^"]+)"/;
    const fnameRegex = /([\w:~]+)\(/;

    let p = childProc.spawnSync(dumpbinExe, ['/imports:node.exe', modulePath]);
    let lines = p.stdout.toString().split('\r\n');

    let imports = [];
    lines.forEach(line => {
        let m = importRegex.exec(line);
        if (m) {
            let decoratedName = m[1];
            p = childProc.spawnSync(undnameExe, [decoratedName]);
            let output = p.stdout.toString();
            m = undnameRegex.exec(output);
            if (m) {
                let signature = m[1];
                m = fnameRegex.exec(signature);
                let name = (m ? m[1] : signature);
                imports.push({ name, signature });
            }
        }
    });

    return imports;
}

function findFiles(rootDir, filter, relativeDir) {
    let results = [];
    let childNames = fs.readdirSync(path.join(rootDir, relativeDir || ''));
    childNames.forEach(childName => {
        let relativePath = path.join(relativeDir || '', childName);
        let childStats = fs.statSync(path.join(rootDir, relativePath));
        if (childStats.isDirectory()) {
            let childResults = findFiles(rootDir, filter, relativePath);
            results = results.concat(childResults);
        } else {
            if (filter(relativePath)) {
                results.push(relativePath);
            }
        }
    });
    return results;
}