All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.testing.TestExternsBuilder Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2018 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp.testing;

import com.google.common.base.Joiner;
import com.google.javascript.jscomp.SourceFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Builds a string containing standard externs definitions for use in testing.
 *
 * 

The externs in this file are intentionally far from complete. Please use the actual externs * definitions (es3.js, etc.) as a model when you need to add something for a test case. */ public class TestExternsBuilder { private static final Joiner LINE_JOINER = Joiner.on('\n'); private static final String lines(String... lines) { return LINE_JOINER.join(lines); } // Closure definitions that are not technically externs but serve a similar purpose. private static final String CLOSURE_EXTERNS = lines( "/** @const */ var goog = {};", "goog.module = function(ns) {};", "goog.module.declareLegacyNamespace = function() {};", "/** @return {?} */", "goog.module.get = function(ns) {};", "goog.provide = function(ns) {};", "/** @return {?} */", "goog.require = function(ns) {};", "/** @return {?} */", "goog.requireType = function(ns) {};", "goog.loadModule = function(ns) {};", "/** @return {?} */", "goog.forwardDeclare = function(ns) {};", "goog.setTestOnly = function() {};", "goog.scope = function(fn) {};", "goog.defineClass = function(superClass, clazz) {};", "goog.declareModuleId = function(ns) {};"); private static final String BIGINT_EXTERNS = lines( "/** ", " * @constructor", " * @param {bigint|number|string} arg", " * @return {bigint}", " */", "function BigInt(arg) {}", "", "/**", " * @this {BigInt|bigint}", " * @param {number} width", " * @param {bigint} bigint", " * @return {bigint}", " */", "BigInt.asIntN = function(width, bigint) {};", "", "/**", " * @this {BigInt|bigint}", " * @param {number} width", " * @param {bigint} bigint", " * @return {bigint}", " */", "BigInt.asUintN = function(width, bigint) {};", "", "/**", " * @param {string|Array=} locales", " * @param {Object=} options", " * @return {string}", " */", "BigInt.prototype.toLocaleString = function(locales, options) {};", "", "/**", " * @this {BigInt|bigint}", " * @param {number=} radix", " * @return {string}", " */", "BigInt.prototype.toString = function(radix) {};", "", "/**", " * @return {bigint}", " */", "BigInt.prototype.valueOf = function() {};", ""); private static final String ITERABLE_EXTERNS = lines( // Symbol is needed for Symbol.iterator "/** ", " * @constructor", " * @param {*=} opt_description", " * @return {symbol}", " * @nosideeffects", " */", "function Symbol(opt_description) {}", "", "/** @const {!symbol} */ Symbol.iterator;", "", "/**", " * @record", " * @template VALUE", " */", "function IIterableResult() {};", "/** @type {boolean} */", "IIterableResult.prototype.done;", "/** @type {VALUE} */", "IIterableResult.prototype.value;", "", "/**", " * @interface", " * @template VALUE, UNUSED_RETURN_T, UNUSED_NEXT_T", " */", "function Iterator() {}", "/**", " * @param {VALUE=} value", " * @return {!IIterableResult}", " */", "Iterator.prototype.next;", "", "/**", " * @interface", " * @template VALUE", " */", "function Iterable() {}", "", "/**", " * @return {!Iterator}", " * @suppress {externsValidation}", " */", "Iterable.prototype[Symbol.iterator] = function() {};", "", "/**", " * @interface", " * @extends {Iterator}", " * @extends {Iterable}", " * @template VALUE, UNUSED_RETURN_T, UNUSED_NEXT_T", " */", "function IteratorIterable() {}", "", "/**", " * @interface", " * @extends {IteratorIterable}", " * @template VALUE, UNUSED_RETURN_T, UNUSED_NEXT_T", " */", "function Generator() {}", "/**", " * @param {?=} opt_value", " * @return {!IIterableResult}", " * @override", " */", "Generator.prototype.next = function(opt_value) {};", "/**", " * @param {VALUE} value", " * @return {!IIterableResult}", " */", "Generator.prototype.return = function(value) {};", "/**", " * @param {?} exception", " * @return {!IIterableResult}", " */", "Generator.prototype.throw = function(exception) {};", ""); private static final String STRING_EXTERNS = lines( "/**", " * @constructor", " * @implements {Iterable}", " * @param {*=} arg", " * @return {string}", " */", "function String(arg) {}", "/** @type {number} */", "String.prototype.length;", "/** @param {number} sliceArg */", "String.prototype.slice = function(sliceArg) {};", "/**", " * @this {string|String}", " * @param {*=} opt_separator", " * @param {number=} opt_limit", " * @return {!Array}", " */", "String.prototype.split = function(opt_separator, opt_limit) {};", "/**", " * @this {string|String}", " * @param {string} search_string", " * @param {number=} opt_position", " * @return {boolean}", " * @nosideeffects", " */", "String.prototype.startsWith = function(search_string, opt_position) {};", "/**", " * @this {?String|string}", " * @param {?} regex", " * @param {?} str", " * @param {string=} opt_flags", " * @return {string}", " */", "String.prototype.replace = function(regex, str, opt_flags) {};", "/**", " * @this {String|string}", " * @param {number} index", " * @return {string}", " */", "String.prototype.charAt = function(index) {};", "/**", " * @this {String|string}", " * @param {*} regexp", " * @return {Array}", " */", "String.prototype.match = function(regexp) {};", "/**", " * @this {String|string}", " * @return {string}", " */", "String.prototype.toLowerCase = function() {};", "", "/**", " * @param {number} count", " * @this {String|string}", " * @return {string}", " * @nosideeffects", " */", "String.prototype.repeat = function(count) {};", "", "/**", " * @param {string} searchString", " * @param {number=} position", " * @return {boolean}", " * @nosideeffects", " */", "String.prototype.includes = function(searchString, position) {};", ""); private static final String FUNCTION_EXTERNS = lines( "/**", " * @constructor", " * @param {...*} var_args", " */", "function Function(var_args) {}", "/** @type {!Function} */ Function.prototype.apply;", "/** @type {!Function} */ Function.prototype.bind;", "/** @type {!Function} */ Function.prototype.call;", "/** @type {number} */", "Function.prototype.length;", "/** @type {string} */", "Function.prototype.name;", ""); private static final String OBJECT_EXTERNS = lines( "/**", " * @record", " * @template THIS", " */", "function ObjectPropertyDescriptor() {}", "/** @type {(*|undefined)} */", "ObjectPropertyDescriptor.prototype.value;", "/** @type {(function(this: THIS):?)|undefined} */", "ObjectPropertyDescriptor.prototype.get;", "", "/** @type {(function(this: THIS, ?):void)|undefined} */", "ObjectPropertyDescriptor.prototype.set;", "", "/** @type {boolean|undefined} */", "ObjectPropertyDescriptor.prototype.writable;", "", "/** @type {boolean|undefined} */", "ObjectPropertyDescriptor.prototype.enumerable;", "", "/** @type {boolean|undefined} */", "ObjectPropertyDescriptor.prototype.configurable;", "", "/**", " * @constructor", " * @param {*=} opt_value", " * @return {!Object}", " */", "function Object(opt_value) {}", "", "/** @type {?Object} */ Object.prototype.__proto__;", "/** @return {string} */", "Object.prototype.toString = function() {};", "/**", " * @param {*} propertyName", " * @return {boolean}", " */", "Object.prototype.hasOwnProperty = function(propertyName) {};", "/** @type {?Function} */ Object.prototype.constructor;", "/** @return {*} */", "Object.prototype.valueOf = function() {};", "/**", " * @param {!Object} obj", " * @param {string} prop", " * @return {!ObjectPropertyDescriptor|undefined}", " * @nosideeffects", " */", "Object.getOwnPropertyDescriptor = function(obj, prop) {};", "/**", " * @param {!Object} obj", " * @param {string | symbol} prop", " * @param {!ObjectPropertyDescriptor} descriptor", " * @return {!Object}", " */", "Object.defineProperty = function(obj, prop, descriptor) {};", "", "/**", " * @template T", " * @param {T} obj", " * @param {!Object>} props", " * @return {T}", " */", "Object.defineProperties = function(obj, props) {};", "", "/**", " * @param {?Object} proto", " * @param {?Object=} opt_properties", " * @return {!Object}", " */", "Object.create = function(proto, opt_properties) {};", "/**", " * @param {!Object} obj", " * @param {?} proto", " * @return {!Object}", " */", "Object.setPrototypeOf = function(obj, proto) {};", "/**", " * @param {!Object} obj", " * @return {Object}", " * @nosideeffects", " */", "Object.getPrototypeOf = function(obj) {};", "", "/**", " * @param {!Object} target", " * @param {...(Object|null|undefined)} var_args", " * @return {!Object}", " */", "Object.assign = function(target, var_args) {};", "", "/**", " * @param {T} obj", " * @return {T}", " * @template T", " */", "Object.seal = function(obj) {}", ""); private static final String REFLECT_EXTERNS = lines( "/** @const */", "var Reflect = {}", "", "/**", " * @param {function(new: ?, ...?)} targetConstructorFn", " * @param {!Array} argList", " * @param {function(new: TARGET, ...?)=} opt_newTargetConstructorFn", " * @return {TARGET}", " * @template TARGET", " * @nosideeffects", " */", "Reflect.construct = function(", " targetConstructorFn, argList, opt_newTargetConstructorFn) {};", "", "/**", " * @param {!Object} target", " * @param {?Object} proto", " * @return {boolean}", " */", "Reflect.setPrototypeOf = function(target, proto) {};", ""); private static final String ARRAY_EXTERNS = lines( "/**", " * @interface", " * @template KEY1, VALUE1", " */", "function IObject() {};", "", "/**", " * @record", " * @extends IObject", " * @template VALUE2", " */", "function IArrayLike() {};", "", "/** @type {number} */", "IArrayLike.prototype.length;", "", "/**", " * @template T", " * @constructor", " * @implements {IArrayLike} ", " * @implements {Iterable}", " * @param {...*} var_args", " * @return {!Array}", " */", "function Array(var_args) {}", "/** @type {number} */ Array.prototype.length;", "/**", " * @param {*} arr", " * @return {boolean}", " */", "Array.isArray = function(arr) {};", "", "/**", " * @param {string|IArrayLike|!Iterable} arrayLike", " * @param {function(this:S, (string|T), number): R=} mapFn", " * @param {S=} thisObj", " * @return {!Array}", " * @template T,S,R", " */", "Array.from = function(arrayLike, mapFn, thisObj) {}", "", "/**", " * @param {...T} var_args", " * @return {number} The new length of the array.", " * @this {IArrayLike}", " * @template T", " * @modifies {this}", " */", "Array.prototype.push = function(var_args) {};", "/**", " * @this {IArrayLike}", " * @return {T}", " * @template T", " */", "Array.prototype.shift = function() {};", "/**", " * @param {?function(this:S, T, number, !Array): ?} callback", " * @param {S=} opt_thisobj", " * @this {?IArrayLike|string}", " * @template T,S", " * @return {undefined}", " */", "Array.prototype.forEach = function(callback, opt_thisobj) {};", "/**", " * @param {?function(this:S, T, number, !Array): ?} callback", " * @param {S=} opt_thisobj", " * @return {!Array}", " * @this {?IArrayLike|string}", " * @template T,S", " */", "Array.prototype.filter = function(callback, opt_thisobj) {};", "/**", " * @param {...*} var_args", " * @return {!Array}", " * @this {*}", " */", "Array.prototype.concat = function(var_args) {};", "/**", " * @param {?number=} begin Zero-based index at which to begin extraction.", " * @param {?number=} end Zero-based index at which to end extraction. slice", " * extracts up to but not including end.", " * @return {!Array}", " * @this {IArrayLike|string}", " * @template T", " * @nosideeffects", " */", "Array.prototype.slice = function(begin, end) {};", "", "/** @return {!IteratorIterable} */", "Array.prototype.values;", "", "/**", " * @param {T} searchElement", " * @param {number=} fromIndex", " * @return {boolean}", " * @this {!IArrayLike|string}", " * @template T", " * @nosideeffects", " */", "Array.prototype.includes = function(searchElement, fromIndex) {};", ""); private static final String ARGUMENTS_EXTERNS = lines( "/**", " * @constructor", " * @implements {IArrayLike}", " * @implements {Iterable}", " * @template T", " */", "function Arguments() {}", "", "/** @type {number} */", "Arguments.prototype.length;", "", "/** @type {!Arguments} */", "var arguments;", ""); private static final String CONSOLE_EXTERNS = lines( "/** @constructor */", "function Console() {};", "", "/**", " * @param {...*} var_args", " * @return {undefined}", " */", "Console.prototype.log = function(var_args) {};", "", "/** @const {!Console} */", "var console;", ""); private static final String ALERT_EXTERNS = lines( "/**", " * @param {*} message", " * @return {undefined}", " */", "function alert(message) {}", ""); private static final String PROMISE_EXTERNS = lines( "", // "/**", " * @typedef {{then: ?}}", " */", "var Thenable;", "", "", "/**", " * @interface", " * @template TYPE", " */", "function IThenable() {}", "", "", "/**", " * @param {?(function(TYPE):VALUE)=} opt_onFulfilled", " * @param {?(function(*): *)=} opt_onRejected", " * @return {RESULT}", " * @template VALUE", " *", " * @template RESULT := type('IThenable',", " * cond(isUnknown(VALUE), unknown(),", " * mapunion(VALUE, (V) =>", " * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),", " * templateTypeOf(V, 0),", " * cond(sub(V, 'Thenable'),", " * unknown(),", " * V)))))", " * =:", " */", "IThenable.prototype.then = function(opt_onFulfilled, opt_onRejected) {};", "", "", "/**", " * @param {function(", " * function((TYPE|IThenable|Thenable|null)=),", " * function(*=))} resolver", " * @constructor", " * @implements {IThenable}", " * @template TYPE", " */", "function Promise(resolver) {}", "", "", "/**", " * @param {VALUE=} opt_value", " * @return {RESULT}", " * @template VALUE", " * @template RESULT := type('Promise',", " * cond(isUnknown(VALUE), unknown(),", " * mapunion(VALUE, (V) =>", " * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),", " * templateTypeOf(V, 0),", " * cond(sub(V, 'Thenable'),", " * unknown(),", " * V)))))", " * =:", " */", "Promise.resolve = function(opt_value) {};", "", "", "/**", " * @param {*=} opt_error", " * @return {!Promise}", " */", "Promise.reject = function(opt_error) {};", "", "", "/**", " * @param {!Iterable} iterable", " * @return {!Promise>}", " * @template VALUE", " * @template RESULT := mapunion(VALUE, (V) =>", " * cond(isUnknown(V),", " * unknown(),", " * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),", " * templateTypeOf(V, 0),", " * cond(sub(V, 'Thenable'), unknown(), V))))", " * =:", " */", "Promise.all = function(iterable) {};", "", "", "/**", " * @param {!Iterable} iterable", " * @return {!Promise}", " * @template VALUE", " * @template RESULT := mapunion(VALUE, (V) =>", " * cond(isUnknown(V),", " * unknown(),", " * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),", " * templateTypeOf(V, 0),", " * cond(sub(V, 'Thenable'), unknown(), V))))", " * =:", " */", "Promise.race = function(iterable) {};", "", "", "/**", " * @param {?(function(this:void, TYPE):VALUE)=} opt_onFulfilled", " * @param {?(function(this:void, *): *)=} opt_onRejected", " * @return {RESULT}", " * @template VALUE", " *", " * @template RESULT := type('Promise',", " * cond(isUnknown(VALUE), unknown(),", " * mapunion(VALUE, (V) =>", " * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),", " * templateTypeOf(V, 0),", " * cond(sub(V, 'Thenable'),", " * unknown(),", " * V)))))", " * =:", " * @override", " */", "Promise.prototype.then = function(opt_onFulfilled, opt_onRejected) {};", "", "", "/**", " * @param {function(*): RESULT} onRejected", " * @return {!Promise}", " * @template RESULT", " */", "Promise.prototype.catch = function(onRejected) {};", "", "", "/**", " * @param {function()} callback", " * @return {!Promise}", " */", "Promise.prototype.finally = function(callback) {};", ""); private static final String ASYNC_ITERABLE_EXTERNS = lines( "/**", " * @const {symbol}", " */", "Symbol.asyncIterator;", "/**", " * @interface", " * @template VALUE, UNUSED_RETURN_T, UNUSED_NEXT_T", " */", "function AsyncIterator() {}", "/**", " * @param {?=} opt_value", " * @return {!Promise>}", " */", "AsyncIterator.prototype.next;", "/**", " * @interface", " * @template VALUE", " */", "function AsyncIterable() {}", "/**", " * @return {!AsyncIterator}", " */", "AsyncIterable.prototype[Symbol.asyncIterator] = function() {};", "/**", " * @interface", " * @extends {AsyncIterator}", " * @extends {AsyncIterable}", " * @template VALUE", " */", "function AsyncIteratorIterable() {}", "/**", " * @interface", " * @extends {AsyncIteratorIterable}", " * @template VALUE, UNUSED_RETURN_T, UNUSED_NEXT_T", " */", "function AsyncGenerator() {}", "/**", " * @param {?=} opt_value", " * @return {!Promise>}", " * @override", " */", "AsyncGenerator.prototype.next = function(opt_value) {};", "/**", " * @param {VALUE} value", " * @return {!Promise>}", " */", "AsyncGenerator.prototype.return = function(value) {};", "/**", " * @param {?} exception", " * @return {!Promise>}", " */", "AsyncGenerator.prototype.throw = function(exception) {};"); // Test cases that perform transpilation of ES6 classes but use a non-injecting compiler need // these definitions. private static final String ES6_CLASS_TRANSPILATION_EXTERNS = lines( "var $jscomp = {};", "", "/**", " * @param {?} subClass", " * @param {?} superClass", " * @return {?} newClass", " */", "$jscomp.inherits = function(subClass, superClass) {};", ""); private boolean includeBigIntExterns = false; private boolean includeIterableExterns = false; private boolean includeStringExterns = false; private boolean includeFunctionExterns = false; private boolean includeObjectExterns = false; private boolean includeArrayExterns = false; private boolean includeArgumentsExterns = false; private boolean includeConsoleExterns = false; private boolean includeAlertExterns = false; private boolean includePromiseExterns = false; private boolean includeAsyncIterableExterns = false; private boolean includeEs6ClassTranspilationExterns = false; private boolean includeReflectExterns = false; private boolean includeClosureExterns = false; private final List extraExterns = new ArrayList<>(); public TestExternsBuilder addBigInt() { includeBigIntExterns = true; return this; } public TestExternsBuilder addIterable() { includeIterableExterns = true; return this; } public TestExternsBuilder addString() { includeStringExterns = true; addIterable(); // String implements Iterable return this; } public TestExternsBuilder addFunction() { includeFunctionExterns = true; return this; } public TestExternsBuilder addObject() { includeObjectExterns = true; addFunction(); // Object.prototype.constructor has type {?Function} return this; } public TestExternsBuilder addArray() { includeArrayExterns = true; addIterable(); // Array implements Iterable return this; } public TestExternsBuilder addArguments() { includeArgumentsExterns = true; addArray(); // Arguments implements IArrayLike addIterable(); // Arguments implements Iterable return this; } public TestExternsBuilder addPromise() { includePromiseExterns = true; addIterable(); // Promise.all() and Promise.race() need Iterable return this; } /** Adds declaration of `console.log()` */ public TestExternsBuilder addConsole() { includeConsoleExterns = true; return this; } /** Adds declaration of `alert(message)` */ public TestExternsBuilder addAlert() { includeAlertExterns = true; return this; } public TestExternsBuilder addAsyncIterable() { includeAsyncIterableExterns = true; addIterable(); // IIterableResult + Symbol addPromise(); // Promise return this; } public TestExternsBuilder addReflect() { includeReflectExterns = true; addObject(); // Reflect shares many things in common with Object return this; } /** * Externs needed for successful transpilation of ES6 classes without injecting the runtime code. * *

ES6 class transpilation depends on some runtime code that we often don't want to actually * generate in test cases, so we use a non-injecting compiler and include these externs * definitions to keep the type checker happy. */ public TestExternsBuilder addEs6ClassTranspilationExterns() { includeEs6ClassTranspilationExterns = true; addFunction(); // need definition of Function.prototype.apply return this; } public TestExternsBuilder addClosureExterns() { includeClosureExterns = true; return this; } public TestExternsBuilder addExtra(String... lines) { Collections.addAll(extraExterns, lines); return this; } public String build() { List externSections = new ArrayList<>(); if (includeBigIntExterns) { externSections.add(BIGINT_EXTERNS); } if (includeIterableExterns) { externSections.add(ITERABLE_EXTERNS); } if (includeStringExterns) { externSections.add(STRING_EXTERNS); } if (includeFunctionExterns) { externSections.add(FUNCTION_EXTERNS); } if (includeObjectExterns) { externSections.add(OBJECT_EXTERNS); } if (includeArrayExterns) { externSections.add(ARRAY_EXTERNS); } if (includeReflectExterns) { externSections.add(REFLECT_EXTERNS); } if (includeArgumentsExterns) { externSections.add(ARGUMENTS_EXTERNS); } if (includeConsoleExterns) { externSections.add(CONSOLE_EXTERNS); } if (includeAlertExterns) { externSections.add(ALERT_EXTERNS); } if (includePromiseExterns) { externSections.add(PROMISE_EXTERNS); } if (includeAsyncIterableExterns) { externSections.add(ASYNC_ITERABLE_EXTERNS); } if (includeEs6ClassTranspilationExterns) { externSections.add(ES6_CLASS_TRANSPILATION_EXTERNS); } if (includeClosureExterns) { externSections.add(CLOSURE_EXTERNS); } externSections.addAll(extraExterns); return LINE_JOINER.join(externSections); } public SourceFile buildExternsFile(String filePath) { String externsString = build(); return SourceFile.fromCode(filePath, externsString); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy