Socket
Socket
Sign inDemoInstall

@polymer/gen-typescript-declarations

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@polymer/gen-typescript-declarations - npm Package Compare versions

Comparing version 0.3.1 to 0.3.2

6

CHANGELOG.md

@@ -9,2 +9,8 @@ # Changelog

## [0.3.2] - 2017-12-18
- Static methods are now supported on classes, elements, and mixins.
- Add `renameTypes` config option, a map of renames to apply to named types that can be configured per-project.
- Convert Closure `ITemplateArray` type to TypeScript `TemplateStringsArray`.
- Support object index signatures (e.g. `Object<foo, bar>` maps to `{[key: foo]: bar}`).
## [0.3.1] - 2017-12-15

@@ -11,0 +17,0 @@ - Convert Closure `Object` to TypeScript `object`.

40

lib/closure-types.js

@@ -109,2 +109,5 @@ "use strict";

}
else if (isParameterizedObject(node)) {
t = convertIndexableObject(node, templateTypes);
}
else if (isUnion(node)) {

@@ -138,11 +141,3 @@ t = convertUnion(node, templateTypes);

else if (isName(node)) {
if (node.name === 'Object') {
// Closure's `Object` type excludes primitives, so it is closest to
// TypeScript's `object`. (Technically this should be `object|Symbol`,
// but we will concede that technicality.)
t = new ts.NameType('object');
}
else {
t = new ts.NameType(node.name);
}
t = new ts.NameType(renameMap.get(node.name) || node.name);
}

@@ -159,2 +154,14 @@ else {

/**
* Special cases where a named type in Closure maps to something different in
* TypeScript.
*/
const renameMap = new Map([
// Closure's `Object` type excludes primitives, so it is closest to
// TypeScript's `object`. (Technically this should be `object|Symbol`, but we
// will concede that technicality.)
['Object', 'object'],
// The tagged template literal function argument.
['ITemplateArray', 'TemplateStringsArray'],
]);
/**
* Return whether the given AST node is an expression that is nullable by

@@ -181,2 +188,9 @@ * default in the Closure type system.

}
function convertIndexableObject(node, templateTypes) {
if (node.applications.length !== 2) {
console.error('Parameterized Object must have two parameters.');
return ts.anyType;
}
return new ts.IndexableObjectType(convert(node.applications[0], templateTypes), convert(node.applications[1], templateTypes));
}
function convertUnion(node, templateTypes) {

@@ -238,2 +252,10 @@ return new ts.UnionType(node.elements.map((element) => convert(element, templateTypes)));

}
/**
* Matches `Object<foo, bar>` but not `Object` (which is a NameExpression).
*/
function isParameterizedObject(node) {
return node.type === 'TypeApplication' &&
node.expression.type === 'NameExpression' &&
node.expression.name === 'Object';
}
function isUnion(node) {

@@ -240,0 +262,0 @@ return node.type === 'UnionType';

@@ -23,2 +23,11 @@ /**

};
/**
* Whenever a type with a name in this map is encountered, replace it with
* the given name. Note this only applies to named types found in places like
* function/method parameters and return types. It does not currently rename
* e.g. entire generated classes.
*/
renameTypes?: {
[name: string]: string;
};
}

@@ -25,0 +34,0 @@ /**

@@ -54,2 +54,3 @@ "use strict";

const removeReferencesResolved = new Set((config.removeReferences || []).map((r) => path.resolve(rootDir, r)));
const renameTypes = new Map(Object.entries(config.renameTypes || {}));
// Analyzer can produce multiple JS documents with the same URL (e.g. an

@@ -92,2 +93,10 @@ // HTML file with multiple inline scripts). We also might have multiple

}
for (const node of tsDoc.traverse()) {
if (node.kind === 'name') {
const renamed = renameTypes.get(node.name);
if (renamed !== undefined) {
node.name = renamed;
}
}
}
tsDoc.simplify();

@@ -198,3 +207,6 @@ // Include even documents with no members. They might be dependencies of

properties: handleProperties(feature.properties.values()),
methods: handleMethods(feature.methods.values()),
methods: [
...handleMethods(feature.staticMethods.values(), { isStatic: true }),
...handleMethods(feature.methods.values()),
]
});

@@ -211,2 +223,5 @@ parent.members.push(c);

properties: handleProperties(feature.properties.values()),
// Don't worry about about static methods when we're not constructable.
// Since there's no handle to the constructor, they could never be
// called.
methods: handleMethods(feature.methods.values()),

@@ -252,18 +267,49 @@ });

function handleMixin(feature, root) {
const [namespacePath, name] = splitReference(feature.name);
const namespace_ = findOrCreateNamespace(root, namespacePath);
// We represent mixins in two parts: a mixin function that is called to
// augment a given class with this mixin, and an interface with the
// properties and methods that are added by this mixin. We can use the same
// name for both parts because one is in value space, and the other is in
// type space.
const function_ = new ts.Mixin({ name });
function_.description = feature.description;
function_.interfaces = [name, ...feature.mixins.map((m) => m.identifier)];
namespace_.members.push(function_);
const interface_ = new ts.Interface({ name });
interface_.properties = handleProperties(feature.properties.values());
interface_.methods = handleMethods(feature.methods.values());
namespace_.members.push(interface_);
const [namespacePath, mixinName] = splitReference(feature.name);
const parentNamespace = findOrCreateNamespace(root, namespacePath);
// The mixin function. It takes a constructor, and returns an intersection of
// 1) the given constructor, 2) the constructor for this mixin, 3) the
// constructors for any other mixins that this mixin also applies.
parentNamespace.members.push(new ts.Function({
name: mixinName,
description: feature.description,
templateTypes: ['T extends new (...args: any[]) => {}'],
params: [
new ts.Param({ name: 'base', type: new ts.NameType('T') }),
],
returns: new ts.IntersectionType([
new ts.NameType('T'),
new ts.NameType(mixinName + 'Constructor'),
...feature.mixins.map((m) => new ts.NameType(m.identifier + 'Constructor'))
]),
}));
// The interface for a constructor of this mixin. Returns the instance
// interface (see below) when instantiated, and may also have methods of its
// own (static methods from the mixin class).
parentNamespace.members.push(new ts.Interface({
name: mixinName + 'Constructor',
methods: [
new ts.Method({
name: 'new',
params: [
new ts.Param({
name: 'args',
type: new ts.ArrayType(ts.anyType),
rest: true,
}),
],
returns: new ts.NameType(mixinName),
}),
...handleMethods(feature.staticMethods.values()),
],
}));
// The interface for instances of this mixin. Has the same name as the
// function.
parentNamespace.members.push(new ts.Interface({
name: mixinName,
properties: handleProperties(feature.properties.values()),
methods: handleMethods(feature.methods.values()),
}));
}
;
/**

@@ -281,3 +327,6 @@ * Add the given Class to the given TypeScript declarations document.

m.properties = handleProperties(feature.properties.values());
m.methods = handleMethods(feature.methods.values());
m.methods = [
...handleMethods(feature.staticMethods.values(), { isStatic: true }),
...handleMethods(feature.methods.values())
];
findOrCreateNamespace(root, namespacePath).members.push(m);

@@ -330,3 +379,3 @@ }

*/
function handleMethods(analyzerMethods) {
function handleMethods(analyzerMethods, opts) {
const tsMethods = [];

@@ -340,3 +389,4 @@ for (const method of analyzerMethods) {

returns: closure_types_1.closureTypeToTypeScript(method.return && method.return.type),
returnsDescription: method.return && method.return.desc
returnsDescription: method.return && method.return.desc,
isStatic: opts && opts.isStatic,
});

@@ -343,0 +393,0 @@ m.description = method.description || '';

@@ -11,7 +11,7 @@ /**

*/
export declare type Node = Document | Namespace | Class | Interface | Mixin | Function | Method | Type | ParamType | Property;
export declare type Node = Document | Namespace | Class | Interface | Function | Method | Type | ParamType | Property;
export declare class Document {
readonly kind: string;
path: string;
members: Array<Namespace | Class | Interface | Mixin | Function>;
members: Array<Namespace | Class | Interface | Function>;
referencePaths: Set<string>;

@@ -21,3 +21,3 @@ header: string;

path: string;
members?: Array<Namespace | Class | Interface | Mixin | Function>;
members?: Array<Namespace | Class | Interface | Function>;
referencePaths?: Iterable<string>;

@@ -41,7 +41,7 @@ header?: string;

description: string;
members: Array<Namespace | Class | Interface | Mixin | Function>;
members: Array<Namespace | Class | Interface | Function>;
constructor(data: {
name: string;
description?: string;
members?: Array<Namespace | Class | Interface | Mixin | Function>;
members?: Array<Namespace | Class | Interface | Function>;
});

@@ -87,15 +87,2 @@ traverse(): Iterable<Node>;

}
export declare class Mixin {
readonly kind: string;
name: string;
description: string;
interfaces: string[];
constructor(data: {
name: string;
description?: string;
interfaces?: string[];
});
traverse(): Iterable<Node>;
serialize(depth?: number): string;
}
export declare abstract class FunctionLike {

@@ -109,2 +96,3 @@ kind: string;

returnsDescription: string;
isStatic: boolean;
constructor(data: {

@@ -117,2 +105,3 @@ name: string;

returnsDescription?: string;
isStatic?: boolean;
});

@@ -159,3 +148,3 @@ serialize(depth?: number): string;

}
export declare type Type = NameType | UnionType | ArrayType | FunctionType | ConstructorType | RecordType;
export declare type Type = NameType | UnionType | ArrayType | FunctionType | ConstructorType | RecordType | IntersectionType | IndexableObjectType;
export declare class NameType {

@@ -221,4 +210,19 @@ readonly kind: string;

}
export declare class IntersectionType {
readonly kind: string;
types: Type[];
constructor(types: Type[]);
traverse(): Iterable<Node>;
serialize(): string;
}
export declare class IndexableObjectType {
readonly kind: string;
keyType: Type;
valueType: Type;
constructor(keyType: Type, valueType: Type);
traverse(): Iterable<Node>;
serialize(): string;
}
export declare const anyType: NameType;
export declare const nullType: NameType;
export declare const undefinedType: NameType;

@@ -190,33 +190,2 @@ "use strict";

exports.Interface = Interface;
// A class mixin function using the pattern described at:
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html
class Mixin {
constructor(data) {
this.kind = 'mixin';
this.name = data.name;
this.description = data.description || '';
this.interfaces = data.interfaces || [];
}
*traverse() {
yield this;
}
serialize(depth = 0) {
let out = '';
const i = indent(depth);
const i2 = indent(depth + 1);
if (this.description) {
out += formatComment(this.description, depth);
}
out += i;
if (depth === 0) {
out += 'declare ';
}
out += `function ${this.name}`;
out += `<T extends new(...args: any[]) => {}>(base: T): {\n`;
out += `${i2}new(...args: any[]): ${this.interfaces.join(' & ')}\n`;
out += `${i}} & T\n`;
return out;
}
}
exports.Mixin = Mixin;
class FunctionLike {

@@ -230,2 +199,3 @@ constructor(data) {

this.returnsDescription = data.returnsDescription || '';
this.isStatic = data.isStatic || false;
}

@@ -254,6 +224,9 @@ serialize(depth = 0) {

}
out += i;
if (depth === 0) {
out += 'declare ';
}
out += i;
if (this.kind === 'method' && this.isStatic) {
out += 'static ';
}
if (this.kind === 'function') {

@@ -541,2 +514,34 @@ out += 'function ';

exports.RecordType = RecordType;
class IntersectionType {
constructor(types) {
this.kind = 'intersection';
this.types = types;
}
*traverse() {
for (const t of this.types) {
yield* t.traverse();
}
yield this;
}
serialize() {
return this.types.map((t) => t.serialize()).join(' & ');
}
}
exports.IntersectionType = IntersectionType;
class IndexableObjectType {
constructor(keyType, valueType) {
this.kind = 'indexableObject';
this.keyType = keyType;
this.valueType = valueType;
}
*traverse() {
yield this.keyType;
yield this.valueType;
yield this;
}
serialize() {
return `{[key: ${this.keyType.serialize()}]: ${this.valueType.serialize()}}`;
}
}
exports.IndexableObjectType = IndexableObjectType;
exports.anyType = new NameType('any');

@@ -543,0 +548,0 @@ exports.nullType = new NameType('null');

{
"name": "@polymer/gen-typescript-declarations",
"version": "0.3.1",
"version": "0.3.2",
"description": "Generate TypeScript type declarations for Polymer components.",

@@ -5,0 +5,0 @@ "main": "lib/gen-ts.js",

@@ -12,4 +12,2 @@ /**

// TODO Object<foo>, Object<foo, bar>
//
// Useful resources for working on this package:

@@ -122,2 +120,4 @@ // https://eslint.org/doctrine/demo/

t = convertArray(node, templateTypes);
} else if (isParameterizedObject(node)) { // Object<foo, bar>
t = convertIndexableObject(node, templateTypes);
} else if (isUnion(node)) { // foo|bar

@@ -142,10 +142,3 @@ t = convertUnion(node, templateTypes);

} else if (isName(node)) { // string, Object, MyClass, etc.
if (node.name === 'Object') {
// Closure's `Object` type excludes primitives, so it is closest to
// TypeScript's `object`. (Technically this should be `object|Symbol`,
// but we will concede that technicality.)
t = new ts.NameType('object');
} else {
t = new ts.NameType(node.name);
}
t = new ts.NameType(renameMap.get(node.name) || node.name);
} else {

@@ -164,2 +157,15 @@ console.error('Unknown syntax.');

/**
* Special cases where a named type in Closure maps to something different in
* TypeScript.
*/
const renameMap = new Map<string, string>([
// Closure's `Object` type excludes primitives, so it is closest to
// TypeScript's `object`. (Technically this should be `object|Symbol`, but we
// will concede that technicality.)
['Object', 'object'],
// The tagged template literal function argument.
['ITemplateArray', 'TemplateStringsArray'],
]);
/**
* Return whether the given AST node is an expression that is nullable by

@@ -190,2 +196,14 @@ * default in the Closure type system.

function convertIndexableObject(
node: doctrine.type.TypeApplication,
templateTypes: string[]): ts.IndexableObjectType|ts.NameType {
if (node.applications.length !== 2) {
console.error('Parameterized Object must have two parameters.');
return ts.anyType;
}
return new ts.IndexableObjectType(
convert(node.applications[0], templateTypes),
convert(node.applications[1], templateTypes));
}
function convertUnion(

@@ -266,2 +284,12 @@ node: doctrine.type.UnionType, templateTypes: string[]): ts.Type {

/**
* Matches `Object<foo, bar>` but not `Object` (which is a NameExpression).
*/
function isParameterizedObject(node: doctrine.Type):
node is doctrine.type.TypeApplication {
return node.type === 'TypeApplication' &&
node.expression.type === 'NameExpression' &&
node.expression.name === 'Object';
}
function isUnion(node: doctrine.Type): node is doctrine.type.UnionType {

@@ -268,0 +296,0 @@ return node.type === 'UnionType';

@@ -42,2 +42,10 @@ /**

addReferences?: {[filepath: string]: string[]};
/**
* Whenever a type with a name in this map is encountered, replace it with
* the given name. Note this only applies to named types found in places like
* function/method parameters and return types. It does not currently rename
* e.g. entire generated classes.
*/
renameTypes?: {[name: string]: string};
}

@@ -75,2 +83,3 @@

(config.removeReferences || []).map((r) => path.resolve(rootDir, r)));
const renameTypes = new Map(Object.entries(config.renameTypes || {}));

@@ -115,2 +124,10 @@ // Analyzer can produce multiple JS documents with the same URL (e.g. an

}
for (const node of tsDoc.traverse()) {
if (node.kind === 'name') {
const renamed = renameTypes.get(node.name);
if (renamed !== undefined) {
node.name = renamed;
}
}
}
tsDoc.simplify();

@@ -226,3 +243,6 @@ // Include even documents with no members. They might be dependencies of

properties: handleProperties(feature.properties.values()),
methods: handleMethods(feature.methods.values()),
methods: [
...handleMethods(feature.staticMethods.values(), {isStatic: true}),
...handleMethods(feature.methods.values()),
]
});

@@ -239,2 +259,5 @@ parent.members.push(c);

properties: handleProperties(feature.properties.values()),
// Don't worry about about static methods when we're not constructable.
// Since there's no handle to the constructor, they could never be
// called.
methods: handleMethods(feature.methods.values()),

@@ -286,21 +309,54 @@ });

function handleMixin(feature: analyzer.ElementMixin, root: ts.Document) {
const [namespacePath, name] = splitReference(feature.name);
const namespace_ = findOrCreateNamespace(root, namespacePath);
const [namespacePath, mixinName] = splitReference(feature.name);
const parentNamespace = findOrCreateNamespace(root, namespacePath);
// We represent mixins in two parts: a mixin function that is called to
// augment a given class with this mixin, and an interface with the
// properties and methods that are added by this mixin. We can use the same
// name for both parts because one is in value space, and the other is in
// type space.
// The mixin function. It takes a constructor, and returns an intersection of
// 1) the given constructor, 2) the constructor for this mixin, 3) the
// constructors for any other mixins that this mixin also applies.
parentNamespace.members.push(new ts.Function({
name: mixinName,
description: feature.description,
templateTypes: ['T extends new (...args: any[]) => {}'],
params: [
new ts.Param({name: 'base', type: new ts.NameType('T')}),
],
returns: new ts.IntersectionType([
new ts.NameType('T'),
new ts.NameType(mixinName + 'Constructor'),
...feature.mixins.map(
(m) => new ts.NameType(m.identifier + 'Constructor'))
]),
}));
const function_ = new ts.Mixin({name});
function_.description = feature.description;
function_.interfaces = [name, ...feature.mixins.map((m) => m.identifier)];
namespace_.members.push(function_);
// The interface for a constructor of this mixin. Returns the instance
// interface (see below) when instantiated, and may also have methods of its
// own (static methods from the mixin class).
parentNamespace.members.push(new ts.Interface({
name: mixinName + 'Constructor',
methods: [
new ts.Method({
name: 'new',
params: [
new ts.Param({
name: 'args',
type: new ts.ArrayType(ts.anyType),
rest: true,
}),
],
returns: new ts.NameType(mixinName),
}),
...handleMethods(feature.staticMethods.values()),
],
}));
const interface_ = new ts.Interface({name});
interface_.properties = handleProperties(feature.properties.values());
interface_.methods = handleMethods(feature.methods.values());
namespace_.members.push(interface_);
}
// The interface for instances of this mixin. Has the same name as the
// function.
parentNamespace.members.push(
new ts.Interface({
name: mixinName,
properties: handleProperties(feature.properties.values()),
methods: handleMethods(feature.methods.values()),
}),
);
};

@@ -319,3 +375,6 @@ /**

m.properties = handleProperties(feature.properties.values());
m.methods = handleMethods(feature.methods.values());
m.methods = [
...handleMethods(feature.staticMethods.values(), {isStatic: true}),
...handleMethods(feature.methods.values())
];
findOrCreateNamespace(root, namespacePath).members.push(m);

@@ -379,4 +438,5 @@ }

*/
function handleMethods(analyzerMethods: Iterable<analyzer.Method>):
ts.Method[] {
function handleMethods(
analyzerMethods: Iterable<analyzer.Method>,
opts?: {isStatic?: boolean}): ts.Method[] {
const tsMethods = <ts.Method[]>[];

@@ -390,3 +450,4 @@ for (const method of analyzerMethods) {

returns: closureTypeToTypeScript(method.return && method.return.type),
returnsDescription: method.return && method.return.desc
returnsDescription: method.return && method.return.desc,
isStatic: opts && opts.isStatic,
});

@@ -393,0 +454,0 @@ m.description = method.description || '';

@@ -15,4 +15,4 @@ /**

export type Node = Document|Namespace|Class|Interface|Mixin|Function|Method|
Type|ParamType|Property;
export type Node =
Document|Namespace|Class|Interface|Function|Method|Type|ParamType|Property;

@@ -22,3 +22,3 @@ export class Document {

path: string;
members: Array<Namespace|Class|Interface|Mixin|Function>;
members: Array<Namespace|Class|Interface|Function>;
referencePaths: Set<string>;

@@ -29,3 +29,3 @@ header: string;

path: string,
members?: Array<Namespace|Class|Interface|Mixin|Function>
members?: Array<Namespace|Class|Interface|Function>
referencePaths?: Iterable<string>,

@@ -82,3 +82,3 @@ header?: string

description: string;
members: Array<Namespace|Class|Interface|Mixin|Function>;
members: Array<Namespace|Class|Interface|Function>;

@@ -88,3 +88,3 @@ constructor(data: {

description?: string;
members?: Array<Namespace|Class|Interface|Mixin|Function>,
members?: Array<Namespace|Class|Interface|Function>,
}) {

@@ -255,43 +255,2 @@ this.name = data.name;

// A class mixin function using the pattern described at:
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html
export class Mixin {
readonly kind = 'mixin';
name: string;
description: string;
interfaces: string[];
constructor(data: {
name: string,
description?: string,
interfaces?: string[],
}) {
this.name = data.name;
this.description = data.description || '';
this.interfaces = data.interfaces || [];
}
* traverse(): Iterable<Node> {
yield this;
}
serialize(depth: number = 0): string {
let out = '';
const i = indent(depth);
const i2 = indent(depth + 1);
if (this.description) {
out += formatComment(this.description, depth);
}
out += i;
if (depth === 0) {
out += 'declare ';
}
out += `function ${this.name}`;
out += `<T extends new(...args: any[]) => {}>(base: T): {\n`;
out += `${i2}new(...args: any[]): ${this.interfaces.join(' & ')}\n`;
out += `${i}} & T\n`;
return out;
}
}
export abstract class FunctionLike {

@@ -305,2 +264,3 @@ kind: string;

returnsDescription: string;
isStatic: boolean;

@@ -313,3 +273,4 @@ constructor(data: {

returns?: Type,
returnsDescription?: string
returnsDescription?: string,
isStatic?: boolean,
}) {

@@ -322,2 +283,3 @@ this.name = data.name;

this.returnsDescription = data.returnsDescription || '';
this.isStatic = data.isStatic || false;
}

@@ -350,6 +312,9 @@

out += i;
if (depth === 0) {
out += 'declare ';
}
out += i;
if (this.kind === 'method' && this.isStatic) {
out += 'static ';
}
if (this.kind === 'function') {

@@ -463,4 +428,4 @@ out += 'function ';

// A TypeScript type expression.
export type Type =
NameType|UnionType|ArrayType|FunctionType|ConstructorType|RecordType;
export type Type = NameType|UnionType|ArrayType|FunctionType|ConstructorType|
RecordType|IntersectionType|IndexableObjectType;

@@ -686,2 +651,43 @@ // string, MyClass, null, undefined, any

export class IntersectionType {
readonly kind = 'intersection';
types: Type[];
constructor(types: Type[]) {
this.types = types;
}
* traverse(): Iterable<Node> {
for (const t of this.types) {
yield* t.traverse();
}
yield this;
}
serialize(): string {
return this.types.map((t) => t.serialize()).join(' & ');
}
}
export class IndexableObjectType {
readonly kind = 'indexableObject';
keyType: Type;
valueType: Type;
constructor(keyType: Type, valueType: Type) {
this.keyType = keyType;
this.valueType = valueType;
}
* traverse(): Iterable<Node> {
yield this.keyType;
yield this.valueType;
yield this;
}
serialize(): string {
return `{[key: ${this.keyType.serialize()}]: ${this.valueType.serialize()}}`
}
}
export const anyType = new NameType('any');

@@ -688,0 +694,0 @@ export const nullType = new NameType('null');

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc