/*
* 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 Collection = require('./Collection');
var collections = require('./collections');
var getParser = require('./getParser');
var matchNode = require('./matchNode');
var recast = require('recast');
var template = require('./template');
var Node = recast.types.namedTypes.Node;
var NodePath = recast.types.NodePath;
// Register all built-in collections
for (var name in collections) {
collections[name].register();
}
/**
* Main entry point to the tool. The function accepts multiple different kinds
* of arguments as a convenience. In particular the function accepts either
*
* - a string containing source code
* The string is parsed with Recast
* - a single AST node
* - a single node path
* - an array of nodes
* - an array of node paths
*
* @exports jscodeshift
* @param {Node|NodePath|Array|string} source
* @param {Object} options Options to pass to Recast when passing source code
* @return {Collection}
*/
function core(source, options) {
return typeof source === 'string' ?
fromSource(source, options) :
fromAST(source);
}
/**
* Returns a collection from a node, node path, array of nodes or array of node
* paths.
*
* @ignore
* @param {Node|NodePath|Array} source
* @return {Collection}
*/
function fromAST(ast) {
if (Array.isArray(ast)) {
if (ast[0] instanceof NodePath || ast.length === 0) {
return Collection.fromPaths(ast);
} else if (Node.check(ast[0])) {
return Collection.fromNodes(ast);
}
} else {
if (ast instanceof NodePath) {
return Collection.fromPaths([ast]);
} else if (Node.check(ast)) {
return Collection.fromNodes([ast]);
}
}
throw new TypeError(
'Received an unexpected value ' + Object.prototype.toString.call(ast)
);
}
function fromSource(source, options) {
if (!options) {
options = {};
}
if (!options.parser) {
options.parser = getParser();
}
return fromAST(recast.parse(source, options));
}
/**
* Utility function to match a node against a pattern.
* @augments core
* @static
* @param {Node|NodePath|Object} path
* @parma {Object} filter
* @return boolean
*/
function match(path, filter) {
if (!(path instanceof NodePath)) {
if (typeof path.get === 'function') {
path = path.get();
} else {
path = {value: path};
}
}
return matchNode(path.value, filter);
}
var plugins = [];
/**
* Utility function for registering plugins.
*
* Plugins are simple functions that are passed the core jscodeshift instance.
* They should extend jscodeshift by calling `registerMethods`, etc.
* This method guards against repeated registrations (the plugin callback will only be called once).
*
* @augments core
* @static
* @param {Function} plugin
*/
function use(plugin) {
if (plugins.indexOf(plugin) === -1) {
plugins.push(plugin);
plugin(core);
}
}
/**
* Returns a version of the core jscodeshift function "bound" to a specific
* parser.
*
* @augments core
* @static
*/
function withParser(parser) {
if (typeof parser === 'string') {
parser = getParser(parser);
}
const newCore = function(source, options) {
if (options && !options.parser) {
options.parser = parser;
} else {
options = {parser};
}
return core(source, options);
};
return enrichCore(newCore, parser);
}
/**
* The ast-types library
* @external astTypes
* @see {@link https://github.com/benjamn/ast-types}
*/
function enrichCore(core, parser) {
// add builders and types to the function for simple access
Object.assign(core, recast.types.namedTypes);
Object.assign(core, recast.types.builders);
core.registerMethods = Collection.registerMethods;
/**
* @augments core
* @type external:astTypes
*/
core.types = recast.types;
core.match = match;
core.template = template(parser);
// add mappings and filters to function
core.filters = {};
core.mappings = {};
for (var name in collections) {
if (collections[name].filters) {
core.filters[name] = collections[name].filters;
}
if (collections[name].mappings) {
core.mappings[name] = collections[name].mappings;
}
}
core.use = use;
core.withParser = withParser;
return core;
}
module.exports = enrichCore(core, getParser());