From 0ddb8427dabdcf5e269363738cf6383fc247a539 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 12 Feb 2018 18:06:32 -0800 Subject: [PATCH] Mixin functions now include all mixins automatically applied transitively. Previously, only the immediately applied mixins were accounted for, but not ones that were applied transitively. Fixes https://github.com/Polymer/polymer/issues/5087 --- CHANGELOG.md | 3 +- src/gen-ts.ts | 46 +++++++++++++++++-- .../polymer/lib/elements/array-selector.d.ts | 2 +- .../lib/legacy/legacy-element-mixin.d.ts | 2 +- .../goldens/polymer/lib/mixins/dir-mixin.d.ts | 2 +- .../polymer/lib/mixins/element-mixin.d.ts | 2 +- .../polymer/lib/mixins/property-effects.d.ts | 2 +- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0810b26..f7270ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - +## [Unreleased] +- Mixin functions now include all of the additional mixins they automatically apply. Previously, only the immediately applied mixins were accounted for, but not ones that were applied transitively. ## [1.1.2] - 2018-02-08 - Elements that are constructable (usually a call to the Polymer function whose result is assigned to some variable) can now have behaviors. diff --git a/src/gen-ts.ts b/src/gen-ts.ts index 2d99369..e149398 100644 --- a/src/gen-ts.ts +++ b/src/gen-ts.ts @@ -144,7 +144,11 @@ function analyzerToAst( }); for (const analyzerDoc of analyzerDocs) { handleDocument( - analyzerDoc, tsDoc, rootDir, config.excludeIdentifiers || []); + analysis, + analyzerDoc, + tsDoc, + rootDir, + config.excludeIdentifiers || []); } for (const ref of tsDoc.referencePaths) { const resolvedRef = path.resolve(rootDir, path.dirname(tsDoc.path), ref); @@ -217,6 +221,7 @@ interface MaybePrivate { * items in the given Polymer Analyzer document. */ function handleDocument( + analysis: analyzer.Analysis, doc: analyzer.Document, root: ts.Document, rootDir: string, @@ -233,7 +238,7 @@ function handleDocument( } else if (feature.kinds.has('behavior')) { handleBehavior(feature as analyzer.PolymerBehavior, root); } else if (feature.kinds.has('element-mixin')) { - handleMixin(feature as analyzer.ElementMixin, root); + handleMixin(feature as analyzer.ElementMixin, root, analysis); } else if (feature.kinds.has('class')) { handleClass(feature as analyzer.Class, root); } else if (feature.kinds.has('function')) { @@ -393,7 +398,10 @@ function handleBehavior(feature: analyzer.PolymerBehavior, root: ts.Document) { /** * Add the given Mixin to the given TypeScript declarations document. */ -function handleMixin(feature: analyzer.ElementMixin, root: ts.Document) { +function handleMixin( + feature: analyzer.ElementMixin, + root: ts.Document, + analysis: analyzer.Analysis) { const [namespacePath, mixinName] = splitReference(feature.name); const parentNamespace = findOrCreateNamespace(root, namespacePath); @@ -410,8 +418,8 @@ function handleMixin(feature: analyzer.ElementMixin, root: ts.Document) { returns: new ts.IntersectionType([ new ts.NameType('T'), new ts.NameType(mixinName + 'Constructor'), - ...feature.mixins.map( - (m) => new ts.NameType(m.identifier + 'Constructor')) + ...Array.from(transitiveMixins(feature, analysis)) + .map((mixin) => new ts.NameType(mixin + 'Constructor')) ]), })); @@ -447,6 +455,34 @@ function handleMixin(feature: analyzer.ElementMixin, root: ts.Document) { ); }; +/** + * Mixins can automatically apply other mixins, indicated by the @appliesMixin + * annotation. However, since mixins may be applied transitively, to know the + * full set of them we need to traverse down the tree. + */ +function transitiveMixins( + parentMixin: analyzer.ElementMixin, + analysis: analyzer.Analysis, + result?: Set): Set { + if (result === undefined) { + result = new Set(); + } + for (const childRef of parentMixin.mixins) { + result.add(childRef.identifier); + const childMixinSet = + analysis.getFeatures({id: childRef.identifier, kind: 'element-mixin'}); + if (childMixinSet.size !== 1) { + console.error( + `Found ${childMixinSet.size} features for mixin ` + + `${childRef.identifier}, expected 1.`); + continue; + } + const childMixin = childMixinSet.values().next().value; + transitiveMixins(childMixin, analysis, result); + } + return result; +} + /** * Add the given Class to the given TypeScript declarations document. */ diff --git a/src/test/goldens/polymer/lib/elements/array-selector.d.ts b/src/test/goldens/polymer/lib/elements/array-selector.d.ts index f9e6c44..5f0be4a 100644 --- a/src/test/goldens/polymer/lib/elements/array-selector.d.ts +++ b/src/test/goldens/polymer/lib/elements/array-selector.d.ts @@ -29,7 +29,7 @@ declare namespace Polymer { * representing the last selected item. When `multi` is true, `selected` * is an array of multiply selected items. */ - function ArraySelectorMixin {}>(base: T): T & ArraySelectorMixinConstructor & Polymer.ElementMixinConstructor; + function ArraySelectorMixin {}>(base: T): T & ArraySelectorMixinConstructor & Polymer.ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor; interface ArraySelectorMixinConstructor { new(...args: any[]): ArraySelectorMixin; diff --git a/src/test/goldens/polymer/lib/legacy/legacy-element-mixin.d.ts b/src/test/goldens/polymer/lib/legacy/legacy-element-mixin.d.ts index 317b056..ce0ef4c 100644 --- a/src/test/goldens/polymer/lib/legacy/legacy-element-mixin.d.ts +++ b/src/test/goldens/polymer/lib/legacy/legacy-element-mixin.d.ts @@ -26,7 +26,7 @@ declare namespace Polymer { * found on the Polymer 1.x `Polymer.Base` prototype applied to all elements * defined using the `Polymer({...})` function. */ - function LegacyElementMixin {}>(base: T): T & LegacyElementMixinConstructor & Polymer.ElementMixinConstructor & Polymer.GestureEventListenersConstructor; + function LegacyElementMixin {}>(base: T): T & LegacyElementMixinConstructor & Polymer.ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor & Polymer.GestureEventListenersConstructor; interface LegacyElementMixinConstructor { new(...args: any[]): LegacyElementMixin; diff --git a/src/test/goldens/polymer/lib/mixins/dir-mixin.d.ts b/src/test/goldens/polymer/lib/mixins/dir-mixin.d.ts index 20f5050..58aa813 100644 --- a/src/test/goldens/polymer/lib/mixins/dir-mixin.d.ts +++ b/src/test/goldens/polymer/lib/mixins/dir-mixin.d.ts @@ -29,7 +29,7 @@ declare namespace Polymer { * - Changing `dir` at runtime is supported. * - Opting out of the global direction styling is permanent */ - function DirMixin {}>(base: T): T & DirMixinConstructor & Polymer.PropertyAccessorsConstructor; + function DirMixin {}>(base: T): T & DirMixinConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor; interface DirMixinConstructor { new(...args: any[]): DirMixin; diff --git a/src/test/goldens/polymer/lib/mixins/element-mixin.d.ts b/src/test/goldens/polymer/lib/mixins/element-mixin.d.ts index afcb31e..343b036 100644 --- a/src/test/goldens/polymer/lib/mixins/element-mixin.d.ts +++ b/src/test/goldens/polymer/lib/mixins/element-mixin.d.ts @@ -76,7 +76,7 @@ declare namespace Polymer { * `observedAttributes` implementation will automatically return an array * of dash-cased attributes based on `properties`) */ - function ElementMixin {}>(base: T): T & ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.PropertiesMixinConstructor; + function ElementMixin {}>(base: T): T & ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor; interface ElementMixinConstructor { new(...args: any[]): ElementMixin; diff --git a/src/test/goldens/polymer/lib/mixins/property-effects.d.ts b/src/test/goldens/polymer/lib/mixins/property-effects.d.ts index 056901e..6afd80f 100644 --- a/src/test/goldens/polymer/lib/mixins/property-effects.d.ts +++ b/src/test/goldens/polymer/lib/mixins/property-effects.d.ts @@ -46,7 +46,7 @@ declare namespace Polymer { * whereas the default when using `PropertyAccessors` standalone is to be * async by default. */ - function PropertyEffects {}>(base: T): T & PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor; + function PropertyEffects {}>(base: T): T & PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor; interface PropertyEffectsConstructor { new(...args: any[]): PropertyEffects;