184 lines
4.5 KiB
JavaScript
184 lines
4.5 KiB
JavaScript
|
|
||
|
/**
|
||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
const Collection = require('./Collection');
|
||
|
|
||
|
const collections = require('./collections');
|
||
|
const getParser = require('./getParser');
|
||
|
const matchNode = require('./matchNode');
|
||
|
const recast = require('recast');
|
||
|
const template = require('./template');
|
||
|
|
||
|
const Node = recast.types.namedTypes.Node;
|
||
|
const 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);
|
||
|
}
|
||
|
|
||
|
const 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 (const 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());
|