From ba4f88e111bbd7621382da9ec860e486cbcd72c9 Mon Sep 17 00:00:00 2001 From: teppeis Date: Sat, 8 Dec 2018 21:52:43 +0900 Subject: [PATCH] Alias colliding names in Closure Library like goog.ui.Component --- .../google/javascript/clutz/Constants.java | 2 +- .../clutz/DeclarationGenerator.java | 43 ++++++++++++++++--- .../javascript/clutz/goog_ui_component.d.ts | 36 ++++++++++++++++ .../javascript/clutz/goog_ui_component.js | 29 +++++++++++++ 4 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/google/javascript/clutz/goog_ui_component.d.ts create mode 100644 src/test/java/com/google/javascript/clutz/goog_ui_component.js diff --git a/src/main/java/com/google/javascript/clutz/Constants.java b/src/main/java/com/google/javascript/clutz/Constants.java index 03bb9dff..c3d52767 100644 --- a/src/main/java/com/google/javascript/clutz/Constants.java +++ b/src/main/java/com/google/javascript/clutz/Constants.java @@ -14,5 +14,5 @@ public interface Constants { * The alias does not affect the external module declaration, thus the user can still import the * symbol using unaliased module name (i.e. import bar from 'goog:foo.bar'). */ - static final String COLLDING_PROVIDE_ALIAS_POSTFIX = "__clutz_alias"; + static final String COLLIDING_PROVIDE_ALIAS_POSTFIX = "__clutz_alias"; } diff --git a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java index ebe64a04..8c701855 100644 --- a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java +++ b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java @@ -260,10 +260,38 @@ public static void main(String[] args) { /** * If one file defines a name and another uses it as a namespace, we have the - * Constants.COLLDING_PROVIDE_ALIAS_POSTFIX workaround. In partial mode, Clutz can't see all + * Constants.COLLIDING_PROVIDE_ALIAS_POSTFIX workaround. In partial mode, Clutz can't see all * definitions of a name, so the list of names that require aliases must be passed as an input. + * Also Closure Library uses some names as both classes and namespases. For example, + * `goog.ui.Component` is not only a class but also a namespace in `goog.ui.Component.EventType`. + * See https://github.com/google/closure-library/blob/master/closure/goog/ui/component.js */ - private Set collidingProvides = new LinkedHashSet<>(); + private Set collidingProvides = + Sets.newHashSet( + "goog.ui.AdvancedTooltip", + "goog.ui.AnimatedZippy", + "goog.ui.Checkbox", + "goog.ui.ColorPicker", + "goog.ui.Component", + "goog.ui.Container", + "goog.ui.Control", + "goog.ui.Dialog", + "goog.ui.FilteredMenu", + "goog.ui.HoverCard", + "goog.ui.Menu", + "goog.ui.MenuItem", + "goog.ui.ModalPopup", + "goog.ui.Ratings", + "goog.ui.ScrollFloater", + "goog.ui.SliderBase", + "goog.ui.SplitPane", + "goog.ui.TableSorter", + "goog.ui.Textarea", + "goog.ui.TriStateMenuItem", + "goog.ui.Zippy", + "goog.ui.editor.AbstractDialog", + "goog.ui.editor.LinkDialog", + "goog.ui.tree.BaseNode"); DeclarationGenerator(Options opts) { this.opts = opts; @@ -416,7 +444,7 @@ String generateDeclarations( legacyNamespaceReexportMap = new LegacyNamespaceReexportMapBuilder() .build(compiler.getParsedInputs(), opts.depgraph.getGoogProvides()); - collidingProvides = opts.collidingProvides; + collidingProvides.addAll(opts.collidingProvides); } unknownType = compiler.getTypeRegistry().getNativeType(JSTypeNative.UNKNOWN_TYPE); @@ -512,7 +540,7 @@ String produceDts(Depgraph depgraph) { rewrittenProvides.add(rewritenProvide); } if (needsAlias(shadowedProvides, provide, symbol)) { - emitName += Constants.COLLDING_PROVIDE_ALIAS_POSTFIX; + emitName += Constants.COLLIDING_PROVIDE_ALIAS_POSTFIX; } if (symbol == null) { // Sometimes goog.provide statements are used as pure markers for dependency management, or @@ -858,7 +886,7 @@ private void processExternSymbols() { boolean isDefault = isDefaultExport(symbol); String emitName = symbol.getName(); if (needsAlias(shadowedSymbols, symbol.getName(), symbol)) { - emitName += Constants.COLLDING_PROVIDE_ALIAS_POSTFIX; + emitName += Constants.COLLIDING_PROVIDE_ALIAS_POSTFIX; } // There is nothing to emit for a namespace, because all its symbols will be visited later, @@ -1485,6 +1513,9 @@ private TreeWalker( private String getAbsoluteName(ObjectType objectType) { String name = objectType.getDisplayName(); + if (collidingProvides.contains(name)) { + name += Constants.COLLIDING_PROVIDE_ALIAS_POSTFIX; + } // Names that do not have a namespace '.' are either platform names in the top level // namespace like `Object` or `Element`, or they are unqualified `goog.provide`s, e.g. // `goog.provide('Toplevel')`. In both cases they will be found with the naked name. @@ -1513,7 +1544,7 @@ private void walk(TypedVar symbol, String emitName) { // Since closure inlines all aliases before this step, check against // the type name. if (!isAliasedClassOrInterface(symbol, ftype)) { - visitClassOrInterface(getUnqualifiedName(symbol), ftype); + visitClassOrInterface(getUnqualifiedName(emitName), ftype); } else { if (KNOWN_CLASS_ALIASES.containsKey(symbol.getName())) { visitKnownTypeValueAlias( diff --git a/src/test/java/com/google/javascript/clutz/goog_ui_component.d.ts b/src/test/java/com/google/javascript/clutz/goog_ui_component.d.ts new file mode 100644 index 00000000..32852cc9 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_ui_component.d.ts @@ -0,0 +1,36 @@ +declare namespace ಠ_ಠ.clutz.goog.ui { + class Component__clutz_alias { + private noStructuralTyping_goog_ui_Component : any; + } +} +declare module 'goog:goog.ui.Component' { + import Component = ಠ_ಠ.clutz.goog.ui.Component__clutz_alias; + export default Component; +} +declare namespace ಠ_ಠ.clutz.goog.ui.Component { + enum EventType { + ACTION = 'action' , + } +} +declare module 'goog:goog.ui.Component.EventType' { + import EventType = ಠ_ಠ.clutz.goog.ui.Component.EventType; + export default EventType; +} +declare namespace ಠ_ಠ.clutz.goog.ui.tree { + class BaseNode__clutz_alias extends ಠ_ಠ.clutz.goog.ui.Component__clutz_alias { + private noStructuralTyping_goog_ui_tree_BaseNode : any; + } +} +declare module 'goog:goog.ui.tree.BaseNode' { + import BaseNode = ಠ_ಠ.clutz.goog.ui.tree.BaseNode__clutz_alias; + export default BaseNode; +} +declare namespace ಠ_ಠ.clutz.goog.ui.tree.BaseNode { + enum EventType { + BEFORE_COLLAPSE = 'beforecollapse' , + } +} +declare module 'goog:goog.ui.tree.BaseNode.EventType' { + import EventType = ಠ_ಠ.clutz.goog.ui.tree.BaseNode.EventType; + export default EventType; +} diff --git a/src/test/java/com/google/javascript/clutz/goog_ui_component.js b/src/test/java/com/google/javascript/clutz/goog_ui_component.js new file mode 100644 index 00000000..071b77ed --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_ui_component.js @@ -0,0 +1,29 @@ +goog.provide('goog.ui.Component'); +goog.provide('goog.ui.Component.EventType'); +goog.provide('goog.ui.tree.BaseNode'); +goog.provide('goog.ui.tree.BaseNode.EventType'); + +/** + * @constructor + */ +goog.ui.Component = function() {}; + +/** + * @enum {string} + */ +goog.ui.Component.EventType = { + ACTION: 'action' +}; + +/** + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.tree.BaseNode = function() {}; + +/** + * @enum {string} + */ +goog.ui.tree.BaseNode.EventType = { + BEFORE_COLLAPSE: 'beforecollapse' +};