123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <title>JSDoc: Source: Collection.js</title>
- <script src="scripts/prettify/prettify.js"> </script>
- <script src="scripts/prettify/lang-css.js"> </script>
- <!--[if lt IE 9]>
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
- <![endif]-->
- <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
- <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
- </head>
- <body>
- <div id="main">
- <h1 class="page-title">Source: Collection.js</h1>
-
-
- <section>
- <article>
- <pre class="prettyprint source linenums"><code>/*
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
- 'use strict';
- var assert = require('assert');
- var recast = require('recast');
- var _ = require('lodash');
- var astTypes = recast.types;
- var types = astTypes.namedTypes;
- var NodePath = astTypes.NodePath;
- var Node = types.Node;
- /**
- * This represents a generic collection of node paths. It only has a generic
- * API to access and process the elements of the list. It doesn't know anything
- * about AST types.
- *
- * @mixes traversalMethods
- * @mixes mutationMethods
- * @mixes transformMethods
- * @mixes globalMethods
- */
- class Collection {
- /**
- * @param {Array} paths An array of AST paths
- * @param {Collection} parent A parent collection
- * @param {Array} types An array of types all the paths in the collection
- * have in common. If not passed, it will be inferred from the paths.
- * @return {Collection}
- */
- constructor(paths, parent, types) {
- assert.ok(Array.isArray(paths), 'Collection is passed an array');
- assert.ok(
- paths.every(p => p instanceof NodePath),
- 'Array contains only paths'
- );
- this._parent = parent;
- this.__paths = paths;
- if (types && !Array.isArray(types)) {
- types = _toTypeArray(types);
- } else if (!types || Array.isArray(types) && types.length === 0) {
- types = _inferTypes(paths);
- }
- this._types = types.length === 0 ? _defaultType : types;
- }
- /**
- * Returns a new collection containing the nodes for which the callback
- * returns true.
- *
- * @param {function} callback
- * @return {Collection}
- */
- filter(callback) {
- return new this.constructor(this.__paths.filter(callback), this);
- }
- /**
- * Executes callback for each node/path in the collection.
- *
- * @param {function} callback
- * @return {Collection} The collection itself
- */
- forEach(callback) {
- this.__paths.forEach(
- (path, i, paths) => callback.call(path, path, i, paths)
- );
- return this;
- }
- /**
- * Executes the callback for every path in the collection and returns a new
- * collection from the return values (which must be paths).
- *
- * The callback can return null to indicate to exclude the element from the
- * new collection.
- *
- * If an array is returned, the array will be flattened into the result
- * collection.
- *
- * @param {function} callback
- * @param {Type} type Force the new collection to be of a specific type
- */
- map(callback, type) {
- var paths = [];
- this.forEach(function(path) {
- /*jshint eqnull:true*/
- var result = callback.apply(path, arguments);
- if (result == null) return;
- if (!Array.isArray(result)) {
- result = [result];
- }
- for (var i = 0; i < result.length; i++) {
- if (paths.indexOf(result[i]) === -1) {
- paths.push(result[i]);
- }
- }
- });
- return fromPaths(paths, this, type);
- }
- /**
- * Returns the number of elements in this collection.
- *
- * @return {number}
- */
- size() {
- return this.__paths.length;
- }
- /**
- * Returns the number of elements in this collection.
- *
- * @return {number}
- */
- get length() {
- return this.__paths.length;
- }
- /**
- * Returns an array of AST nodes in this collection.
- *
- * @return {Array}
- */
- nodes() {
- return this.__paths.map(p => p.value);
- }
- paths() {
- return this.__paths;
- }
- getAST() {
- if (this._parent) {
- return this._parent.getAST();
- }
- return this.__paths;
- }
- toSource(options) {
- if (this._parent) {
- return this._parent.toSource(options);
- }
- if (this.__paths.length === 1) {
- return recast.print(this.__paths[0], options).code;
- } else {
- return this.__paths.map(p => recast.print(p, options).code);
- }
- }
- /**
- * Returns a new collection containing only the element at position index.
- *
- * In case of a negative index, the element is taken from the end:
- *
- * .at(0) - first element
- * .at(-1) - last element
- *
- * @param {number} index
- * @return {Collection}
- */
- at(index) {
- return fromPaths(
- this.__paths.slice(
- index,
- index === -1 ? undefined : index + 1
- ),
- this
- );
- }
- /**
- * Proxies to NodePath#get of the first path.
- *
- * @param {string|number} ...fields
- */
- get() {
- var path = this.__paths[0];
- if (!path) {
- throw Error(
- 'You cannot call "get" on a collection with no paths. ' +
- 'Instead, check the "length" property first to verify at least 1 path exists.'
- );
- }
- return path.get.apply(path, arguments);
- }
- /**
- * Returns the type(s) of the collection. This is only used for unit tests,
- * I don't think other consumers would need it.
- *
- * @return {Array<string>}
- */
- getTypes() {
- return this._types;
- }
- /**
- * Returns true if this collection has the type 'type'.
- *
- * @param {Type} type
- * @return {boolean}
- */
- isOfType(type) {
- return !!type && this._types.indexOf(type.toString()) > -1;
- }
- }
- /**
- * Given a set of paths, this infers the common types of all paths.
- * @private
- * @param {Array} paths An array of paths.
- * @return {Type} type An AST type
- */
- function _inferTypes(paths) {
- var _types = [];
- if (paths.length > 0 && Node.check(paths[0].node)) {
- var nodeType = types[paths[0].node.type];
- var sameType = paths.length === 1 ||
- paths.every(path => nodeType.check(path.node));
- if (sameType) {
- _types = [nodeType.toString()].concat(
- astTypes.getSupertypeNames(nodeType.toString())
- );
- } else {
- // try to find a common type
- _types = _.intersection.apply(
- null,
- paths.map(path => astTypes.getSupertypeNames(path.node.type))
- );
- }
- }
- return _types;
- }
- function _toTypeArray(value) {
- value = !Array.isArray(value) ? [value] : value;
- value = value.map(v => v.toString());
- if (value.length > 1) {
- return _.union(value, _.intersection.apply(
- null,
- value.map(type => astTypes.getSupertypeNames(type))
- ));
- } else {
- return value.concat(astTypes.getSupertypeNames(value[0]));
- }
- }
- /**
- * Creates a new collection from an array of node paths.
- *
- * If type is passed, it will create a typed collection if such a collection
- * exists. The nodes or path values must be of the same type.
- *
- * Otherwise it will try to infer the type from the path list. If every
- * element has the same type, a typed collection is created (if it exists),
- * otherwise, a generic collection will be created.
- *
- * @ignore
- * @param {Array} paths An array of paths
- * @param {Collection} parent A parent collection
- * @param {Type} type An AST type
- * @return {Collection}
- */
- function fromPaths(paths, parent, type) {
- assert.ok(
- paths.every(n => n instanceof NodePath),
- 'Every element in the array should be a NodePath'
- );
- return new Collection(paths, parent, type);
- }
- /**
- * Creates a new collection from an array of nodes. This is a convenience
- * method which converts the nodes to node paths first and calls
- *
- * Collections.fromPaths(paths, parent, type)
- *
- * @ignore
- * @param {Array} nodes An array of AST nodes
- * @param {Collection} parent A parent collection
- * @param {Type} type An AST type
- * @return {Collection}
- */
- function fromNodes(nodes, parent, type) {
- assert.ok(
- nodes.every(n => Node.check(n)),
- 'Every element in the array should be a Node'
- );
- return fromPaths(
- nodes.map(n => new NodePath(n)),
- parent,
- type
- );
- }
- var CPt = Collection.prototype;
- /**
- * This function adds the provided methods to the prototype of the corresponding
- * typed collection. If no type is passed, the methods are added to
- * Collection.prototype and are available for all collections.
- *
- * @param {Object} methods Methods to add to the prototype
- * @param {Type=} type Optional type to add the methods to
- */
- function registerMethods(methods, type) {
- for (var methodName in methods) {
- if (!methods.hasOwnProperty(methodName)) {
- return;
- }
- if (hasConflictingRegistration(methodName, type)) {
- var msg = `There is a conflicting registration for method with name "${methodName}".\nYou tried to register an additional method with `;
- if (type) {
- msg += `type "${type.toString()}".`
- } else {
- msg += 'universal type.'
- }
- msg += '\nThere are existing registrations for that method with ';
- var conflictingRegistrations = CPt[methodName].typedRegistrations;
- if (conflictingRegistrations) {
- msg += `type ${Object.keys(conflictingRegistrations).join(', ')}.`;
- } else {
- msg += 'universal type.';
- }
- throw Error(msg);
- }
- if (!type) {
- CPt[methodName] = methods[methodName];
- } else {
- type = type.toString();
- if (!CPt.hasOwnProperty(methodName)) {
- installTypedMethod(methodName);
- }
- var registrations = CPt[methodName].typedRegistrations;
- registrations[type] = methods[methodName];
- astTypes.getSupertypeNames(type).forEach(function (name) {
- registrations[name] = false;
- });
- }
- }
- }
- function installTypedMethod(methodName) {
- if (CPt.hasOwnProperty(methodName)) {
- throw new Error(`Internal Error: "${methodName}" method is already installed`);
- }
- var registrations = {};
- function typedMethod() {
- var types = Object.keys(registrations);
- for (var i = 0; i < types.length; i++) {
- var currentType = types[i];
- if (registrations[currentType] && this.isOfType(currentType)) {
- return registrations[currentType].apply(this, arguments);
- }
- }
- throw Error(
- `You have a collection of type [${this.getTypes()}]. ` +
- `"${methodName}" is only defined for one of [${types.join('|')}].`
- );
- }
- typedMethod.typedRegistrations = registrations;
- CPt[methodName] = typedMethod;
- }
- function hasConflictingRegistration(methodName, type) {
- if (!type) {
- return CPt.hasOwnProperty(methodName);
- }
- if (!CPt.hasOwnProperty(methodName)) {
- return false;
- }
- var registrations = CPt[methodName] && CPt[methodName].typedRegistrations;
- if (!registrations) {
- return true;
- }
- type = type.toString();
- if (registrations.hasOwnProperty(type)) {
- return true;
- }
- return astTypes.getSupertypeNames(type.toString()).some(function (name) {
- return !!registrations[name];
- });
- }
- var _defaultType = [];
- /**
- * Sets the default collection type. In case a collection is created form an
- * empty set of paths and no type is specified, we return a collection of this
- * type.
- *
- * @ignore
- * @param {Type} type
- */
- function setDefaultCollectionType(type) {
- _defaultType = _toTypeArray(type);
- }
- exports.fromPaths = fromPaths;
- exports.fromNodes = fromNodes;
- exports.registerMethods = registerMethods;
- exports.hasConflictingRegistration = hasConflictingRegistration;
- exports.setDefaultCollectionType = setDefaultCollectionType;
- </code></pre>
- </article>
- </section>
- </div>
- <nav>
- <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-jscodeshift.html">jscodeshift</a></li></ul><h3>Externals</h3><ul><li><a href="external-astTypes.html">astTypes</a></li></ul><h3>Classes</h3><ul><li><a href="Collection.html">Collection</a></li></ul><h3>Mixins</h3><ul><li><a href="globalMethods.html">globalMethods</a></li><li><a href="mutationMethods.html">mutationMethods</a></li><li><a href="transformMethods.html">transformMethods</a></li><li><a href="traversalMethods.html">traversalMethods</a></li></ul><h3>Global</h3><ul><li><a href="global.html#registerMethods">registerMethods</a></li></ul>
- </nav>
- <br class="clear">
- <footer>
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.1</a> on Wed Sep 21 2016 16:53:09 GMT-0400 (EDT)
- </footer>
- <script> prettyPrint(); </script>
- <script src="scripts/linenumber.js"> </script>
- </body>
- </html>
|