Skip to content
This repository has been archived by the owner on Oct 24, 2021. It is now read-only.

Commit

Permalink
Mixin functions now include all mixins automatically applied transiti…
Browse files Browse the repository at this point in the history
…vely.

Previously, only the immediately applied mixins were accounted for, but
not ones that were applied transitively.

Fixes Polymer/polymer#5087
  • Loading branch information
aomarks committed Feb 13, 2018
1 parent e962db3 commit 0ddb842
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 11 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] -->
## [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.
Expand Down
46 changes: 41 additions & 5 deletions src/gen-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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')) {
Expand Down Expand Up @@ -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);

Expand All @@ -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'))
]),
}));

Expand Down Expand Up @@ -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<string>): Set<string> {
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.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/test/goldens/polymer/lib/elements/array-selector.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends new (...args: any[]) => {}>(base: T): T & ArraySelectorMixinConstructor & Polymer.ElementMixinConstructor;
function ArraySelectorMixin<T extends new (...args: any[]) => {}>(base: T): T & ArraySelectorMixinConstructor & Polymer.ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor;

interface ArraySelectorMixinConstructor {
new(...args: any[]): ArraySelectorMixin;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends new (...args: any[]) => {}>(base: T): T & LegacyElementMixinConstructor & Polymer.ElementMixinConstructor & Polymer.GestureEventListenersConstructor;
function LegacyElementMixin<T extends new (...args: any[]) => {}>(base: T): T & LegacyElementMixinConstructor & Polymer.ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor & Polymer.GestureEventListenersConstructor;

interface LegacyElementMixinConstructor {
new(...args: any[]): LegacyElementMixin;
Expand Down
2 changes: 1 addition & 1 deletion src/test/goldens/polymer/lib/mixins/dir-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ declare namespace Polymer {
* - Changing `dir` at runtime is supported.
* - Opting out of the global direction styling is permanent
*/
function DirMixin<T extends new (...args: any[]) => {}>(base: T): T & DirMixinConstructor & Polymer.PropertyAccessorsConstructor;
function DirMixin<T extends new (...args: any[]) => {}>(base: T): T & DirMixinConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor;

interface DirMixinConstructor {
new(...args: any[]): DirMixin;
Expand Down
2 changes: 1 addition & 1 deletion src/test/goldens/polymer/lib/mixins/element-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ declare namespace Polymer {
* `observedAttributes` implementation will automatically return an array
* of dash-cased attributes based on `properties`)
*/
function ElementMixin<T extends new (...args: any[]) => {}>(base: T): T & ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.PropertiesMixinConstructor;
function ElementMixin<T extends new (...args: any[]) => {}>(base: T): T & ElementMixinConstructor & Polymer.PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor & Polymer.PropertiesMixinConstructor;

interface ElementMixinConstructor {
new(...args: any[]): ElementMixin;
Expand Down
2 changes: 1 addition & 1 deletion src/test/goldens/polymer/lib/mixins/property-effects.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ declare namespace Polymer {
* whereas the default when using `PropertyAccessors` standalone is to be
* async by default.
*/
function PropertyEffects<T extends new (...args: any[]) => {}>(base: T): T & PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor;
function PropertyEffects<T extends new (...args: any[]) => {}>(base: T): T & PropertyEffectsConstructor & Polymer.TemplateStampConstructor & Polymer.PropertyAccessorsConstructor & Polymer.PropertiesChangedConstructor;

interface PropertyEffectsConstructor {
new(...args: any[]): PropertyEffects;
Expand Down

0 comments on commit 0ddb842

Please sign in to comment.