diff --git a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java index ef4a7a38..ac0b7333 100644 --- a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java +++ b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java @@ -250,6 +250,17 @@ public int compare(TypedVar o1, TypedVar o2) { treeWalker.walkDefaultInterface((FunctionType) symbol.getType()); emitNamespaceEnd(); } + emitGoogRequireSupport(namespace, isDefault ? symbol.getName() : namespace); + } + + private void emitGoogRequireSupport(String namespace, String closureNamespace) { + // goog namespace doesn't need to be goog.required. + if (namespace.equals("goog")) return; + emitNamespaceBegin("goog"); + String qualifiedClosureNamespace = INTERNAL_NAMESPACE + "." + closureNamespace; + emit("function require(name: '" + closureNamespace + "'): typeof " + qualifiedClosureNamespace + ";"); + emitBreak(); + emitNamespaceEnd(); } diff --git a/src/resources/base.js b/src/resources/base.js new file mode 100644 index 00000000..fd45f433 --- /dev/null +++ b/src/resources/base.js @@ -0,0 +1,4 @@ +/** @provideGoog */ +/** @const */ var goog = goog || {}; +goog.provide = function (arg) {}; +goog.require = function (arg) {}; diff --git a/src/resources/closure.lib.d.ts b/src/resources/closure.lib.d.ts index 49fb959f..6c98bc6e 100644 --- a/src/resources/closure.lib.d.ts +++ b/src/resources/closure.lib.d.ts @@ -5,3 +5,7 @@ declare namespace ಠ_ಠ.clutz_internal { type GlobalError = Error; var GlobalError: ErrorConstructor; } + +// Closures' goog namespace is accessible as global symbol without need for +// explicit goog.require, during the closure compiler pass. +declare var goog: typeof ಠ_ಠ.clutz_internal.goog; \ No newline at end of file diff --git a/src/test/java/com/google/javascript/clutz/ProgramSubject.java b/src/test/java/com/google/javascript/clutz/ProgramSubject.java index 60e7bf69..91b57263 100644 --- a/src/test/java/com/google/javascript/clutz/ProgramSubject.java +++ b/src/test/java/com/google/javascript/clutz/ProgramSubject.java @@ -47,17 +47,24 @@ public ProgramSubject(FailureStrategy failureStrategy, ProgramSubject.Program su public void generatesDeclarations(Boolean withExterns, String expected) { Options opts = new Options(!withExterns); + + List sourceFiles = new ArrayList<>(); + sourceFiles.add(SourceFile.fromFile("src/resources/base.js", UTF_8)); + for (File nonroot : getSubject().nonroots) { sourceFiles.add(SourceFile.fromFile(nonroot, UTF_8)); } List roots = new ArrayList<>(); + roots.add("src/resources/base.js"); + for (File root : getSubject().roots) { sourceFiles.add(SourceFile.fromFile(root, UTF_8)); roots.add(root.getPath()); } + String actual = new DeclarationGenerator(opts) .generateDeclarations(sourceFiles, NO_EXTERNS, new Depgraph(roots)); actual = DeclarationGenerator.GOLDEN_FILE_COMMENTS_REGEXP.matcher(actual).replaceAll(""); diff --git a/src/test/java/com/google/javascript/clutz/goog_require.d.ts b/src/test/java/com/google/javascript/clutz/goog_require.d.ts new file mode 100644 index 00000000..6c032427 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_require.d.ts @@ -0,0 +1,28 @@ +declare namespace ಠ_ಠ.clutz_internal.foo { + class SimpleClass { + } +} +declare namespace ಠ_ಠ.clutz_internal.goog { + function require(name: 'foo.SimpleClass'): typeof ಠ_ಠ.clutz_internal.foo.SimpleClass; +} +declare module 'goog:foo.SimpleClass' { + import alias = ಠ_ಠ.clutz_internal.foo.SimpleClass; + export default alias; +} +declare namespace ಠ_ಠ.clutz_internal.foo.simpleNamespace { + var a : number ; +} +declare namespace ಠ_ಠ.clutz_internal.goog { + function require(name: 'foo.simpleNamespace'): typeof ಠ_ಠ.clutz_internal.foo.simpleNamespace; +} +declare module 'goog:foo.simpleNamespace' { + import alias = ಠ_ಠ.clutz_internal.foo.simpleNamespace; + export = alias; +} +declare namespace ಠ_ಠ.clutz_internal.goog { + function require (name : string ) : void ; +} +declare module 'goog:goog.require' { + import alias = ಠ_ಠ.clutz_internal.goog.require; + export default alias; +} \ No newline at end of file diff --git a/src/test/java/com/google/javascript/clutz/goog_require.js b/src/test/java/com/google/javascript/clutz/goog_require.js new file mode 100644 index 00000000..e9868b46 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_require.js @@ -0,0 +1,12 @@ +goog.provide('foo.SimpleClass'); +goog.provide('foo.simpleNamespace'); + +/** + * @constructor + */ +foo.SimpleClass = function() {}; + +/** + * @const {number} + */ +foo.simpleNamespace.a = 0; diff --git a/src/test/java/com/google/javascript/clutz/goog_require_usage.ts b/src/test/java/com/google/javascript/clutz/goog_require_usage.ts new file mode 100644 index 00000000..19d50ea0 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_require_usage.ts @@ -0,0 +1,10 @@ +const SimpleClass = goog.require('foo.SimpleClass'); +const {a} = goog.require('foo.simpleNamespace'); + +// Asserting that types carry through. +var b: number = a; +// TODO(rado): this should be c: SimpleClass, but without ES6 module +// imports we cannot bring the type symbol in. +// Note that does not mean that the type won't flow through the assignment +// but simply it does not have accessible symbol in the client code. +var c = new SimpleClass(); \ No newline at end of file