microsoft / dts-gen

dts-gen creates starter TypeScript definition files for any module or library.
MIT License
2.43k stars 101 forks source link

TypeError getting declarations for avsc 5.1.2 in getPropertyDeclarationsOfObject #95

Open TysonAndre opened 6 years ago

TysonAndre commented 6 years ago

This is happening because of a magic property getter, in the latest dts-gen release (And _hashStr doesn't exist)

Object.defineProperty(Service.prototype, 'hash', {                                                                                                                           
  enumerable: true,                                                                                                                                                          
  get: function () { return new Buffer(this._hashStr, 'binary'); }                                                                                                           
});

A possible solution would be to catch any Error and infer type any? (Not very familiar with dts-gen, not sure if that would work)

Stack trace:

TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.                               
    at Function.Buffer.from (buffer.js:183:11)                
    at new Buffer (buffer.js:158:17)                          
    at Object.get [as hash] (/path/to/project/node_modules/avsc/lib/services.js:282:29)            
    at getProperty (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:254:30)                                       
    at Array.map (<anonymous>) 
    at getPropertyDeclarationsOfObject (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:250:25)                   
    at getTypeOfValue (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:237:33)                                    
    at getResult (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:173:32)                                         
    at getTopLevelDeclarations (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:123:17)                           
    at getResult (/usr/local/node/lib/node_modules/dts-gen/bin/lib/index.js:148:17)      

Current implementation of getPropertyDeclarationsOfObject, which doesn't catch the error:

function getPropertyDeclarationsOfObject(obj) {                                                                                                                              
    walkStack.add(obj);                                                                                                                                                      
    const keys = getKeysOfObject(obj);                                                                                                                                       
    const result = keys.map(getProperty);                                                                                                                                    
    walkStack.delete(obj);                                                                                                                                                   
    return result;                                                                                                                                                           
    function getProperty(k) {                                                                                                                                                
        if (walkStack.has(obj[k])) {                                                                                                                                         
            return dts_dom_1.create.property(k, dom.type.any);                                                                                                               
        }                                                                                                                                                                    
        return dts_dom_1.create.property(k, getTypeOfValue(obj[k]));                                                                                                         
    }                                                                                                                                                                        
}  
TysonAndre commented 6 years ago

Example workaround to get this to finish genarating a declaration file on avsc (I think I'd need some sort of default for constituentTypes?)

EDIT: For some reason, avsc also has issues creating a namespace called prototype after including my patch (using Node 8). Adding prototype to reservedFunctionProperties fixed that.

***************
*** 63,82 ****
--- 63,84 ----
      }
  }
  exports.generateModuleDeclarationFile = generateModuleDeclarationFile;
  function generateIdentifierDeclarationFile(name, value) {
      const result = getTopLevelDeclarations(name, value);
      return result.map(d => dom.emit(d)).join('\r\n');
  }
  exports.generateIdentifierDeclarationFile = generateIdentifierDeclarationFile;
  const walkStack = new Set();
  const reservedFunctionProperties = Object.getOwnPropertyNames(() => { });
+ reservedFunctionProperties.push('prototype');
+ 
  function getKeysOfObject(obj) {
      let keys = [];
      let chain = obj;
      do {
          if (chain == null)
              break;
          keys = keys.concat(Object.getOwnPropertyNames(chain));
          chain = Object.getPrototypeOf(chain);
      } while (chain !== Object.prototype && chain !== Function.prototype);
      keys = Array.from(new Set(keys));
***************
*** 176,202 ****
                  if (simpleType === 'string') {
                      result.comment = `Value of string: "${simpleType.substr(0, 100)}${simpleType.length > 100 ? '...' : ''}"`;
                  }
                  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 = 0 /* None */;
              for (const k of keys) {
!                 constituentTypes = constituentTypes | getValueTypes(obj[k]);
              }
              if (constituentTypes & (1 /* Class */ | 2 /* Function */)) {
                  const ns = dom.create.namespace(name);
                  for (const k of keys) {
                      const decls = getTopLevelDeclarations(k, 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 [dts_dom_1.create.const(name, (typeof obj))];
--- 178,210 ----
                  if (simpleType === 'string') {
                      result.comment = `Value of string: "${simpleType.substr(0, 100)}${simpleType.length > 100 ? '...' : ''}"`;
                  }
                  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 = 0 /* None */;
              for (const k of keys) {
!                 try {
!                   constituentTypes = constituentTypes | getValueTypes(obj[k]);
!                 } catch(e) {
!                   constituentTypes = constituentTypes | getValueTypes(null);
!                 }
              }
              if (constituentTypes & (1 /* Class */ | 2 /* Function */)) {
                  const ns = dom.create.namespace(name);
                  for (const k of keys) {
+                     try {
                      const decls = getTopLevelDeclarations(k, obj[k]);
                      decls.forEach(d => ns.members.push(d));
+                     } catch(e) {}
                  }
                  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 [dts_dom_1.create.const(name, (typeof obj))];
***************
*** 244,267 ****
              return dom.type.any;
      }
  }
  function getPropertyDeclarationsOfObject(obj) {
      walkStack.add(obj);
      const keys = getKeysOfObject(obj);
      const result = keys.map(getProperty);
      walkStack.delete(obj);
      return result;
      function getProperty(k) {
!         if (walkStack.has(obj[k])) {
              return dts_dom_1.create.property(k, dom.type.any);
          }
-         return dts_dom_1.create.property(k, getTypeOfValue(obj[k]));
      }
  }
  function getClassPrototypeMembers(ctor) {
      const names = Object.getOwnPropertyNames(ctor.prototype);
      const members = names
          .filter(n => !isNameToSkip(n))
          .map(name => getPrototypeMember(name, Object.getOwnPropertyDescriptor(ctor.prototype, name).value))
          .filter(m => m !== undefined);
      members.sort();
      return members;
--- 252,283 ----
              return dom.type.any;
      }
  }
  function getPropertyDeclarationsOfObject(obj) {
      walkStack.add(obj);
      const keys = getKeysOfObject(obj);
      const result = keys.map(getProperty);
      walkStack.delete(obj);
      return result;
      function getProperty(k) {
!         try {
!           if (walkStack.has(obj[k])) {
              return dts_dom_1.create.property(k, dom.type.any);
+           }
+         } catch (e) {
+           return dts_dom_1.create.property(k, dom.type.any);
+         }
+         try {
+           return dts_dom_1.create.property(k, getTypeOfValue(obj[k]));
+         } catch (e) {
+           return dts_dom_1.create.property(k, dom.type.any);
          }
      }
  }
  function getClassPrototypeMembers(ctor) {
      const names = Object.getOwnPropertyNames(ctor.prototype);
      const members = names
          .filter(n => !isNameToSkip(n))
          .map(name => getPrototypeMember(name, Object.getOwnPropertyDescriptor(ctor.prototype, name).value))
          .filter(m => m !== undefined);
      members.sort();
      return members;