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

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

/*
 * 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