function getTopLevelDeclarations()

in lib/index.ts [124:221]


function getTopLevelDeclarations(name: string, obj: any): dom.NamespaceMember[] {
    if (walkStack.has(obj) || keyStack.length > 4) {
        // Circular or too-deep reference
        const result = create.const(name, dom.type.any);
        result.comment = (walkStack.has(obj) ? 'Circular reference' : 'Too-deep object hierarchy') +
            ` from ${keyStack.join('.')}`;
        return [result];
    }

    if (!isLegalIdentifier(name)) return [];

    walkStack.add(obj);
    keyStack.push(name);
    const res = getResult();
    keyStack.pop();
    walkStack.delete(obj);
    return res;

    function getResult(): dom.NamespaceMember[] {
        if (typeof obj === 'function') {
            const funcType = getParameterListAndReturnType(obj, parseFunctionBody(obj));
            const ns = dom.create.namespace(name);
            let primaryDecl: dom.NamespaceMember;
            if (isClasslike(obj)) {
                const cls = dom.create.class(name);
                getClassPrototypeMembers(obj).forEach(m => cls.members.push(m));
                cls.members.push(dom.create.constructor(funcType[0]));
                cls.members.sort(declarationComparer);
                primaryDecl = cls;
            } else {
                const parsedFunction = parseFunctionBody(obj);
                const info = getParameterListAndReturnType(obj, parsedFunction);
                primaryDecl = dom.create.function(name, info[0], info[1]);
            }

            // Get clodule/fundule members
            const keys = getKeysOfObject(obj);
            for (const k of keys) {
                getTopLevelDeclarations(k!, obj[k!]).forEach(p => {
                    if (primaryDecl.kind === "class") {
                        // Transform certain declarations into static members
                        switch (p.kind) {
                            case 'const':
                                primaryDecl.members.push(create.property(p.name, p.type, dom.DeclarationFlags.Static));
                                break;
                            case 'function':
                                primaryDecl.members.push(
                                    create.method(p.name, p.parameters, p.returnType, dom.DeclarationFlags.Static));
                                break;
                            default:
                                ns.members.push(p);
                                break;
                        }
                    } else {
                        ns.members.push(p);
                    }
                });
                ns.members.sort(declarationComparer);
            }

            return ns.members.length > 0 ? [primaryDecl, ns] : [primaryDecl];
        } else if (typeof obj === 'object') {
            // If we can immediately resolve this to a simple declaration, just do so
            const simpleType = getTypeOfValue(obj);
            if (typeof simpleType === 'string' || simpleType.kind === 'name' || simpleType.kind === 'array') {
                const result = dom.create.const(name, simpleType);
                if (simpleType === 'string') {
                    const preview = `"${simpleType.substr(0, 100)}${simpleType.length > 100 ? '...' : ''}"`;
                    result.comment = "Value of string: " + preview;
                }
                return [result];
            }

            // If anything in here is classlike or functionlike, write it as a namespace.
            // Otherwise, write as a 'const'
            const keys = getKeysOfObject(obj);
            let constituentTypes = ValueTypes.None;
            for (const k of keys) {
                constituentTypes = constituentTypes | getValueTypes((<any> obj)[k!]);
            }
            if (constituentTypes & (ValueTypes.Class | ValueTypes.Function)) {
                const ns = dom.create.namespace(name);
                for (const k of keys) {
                    const decls = getTopLevelDeclarations(k!, (<any> obj)[k!]);
                    decls.forEach(d => ns.members.push(d));
                }
                ns.members.sort(declarationComparer);
                return [ns];
            } else {
                return [dom.create.const(name, simpleType)];
            }
        } else if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
            return [create.const(name, <dom.Type> (typeof obj))];
        } else {
            return [create.const(name, dom.type.any)];
        }
    }
}