UNPKG

4.66 kBJavaScriptView Raw
1
2/**
3 * Copyright (c) Facebook, Inc. and its affiliates.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9'use strict';
10const Collection = require('./Collection');
11
12const collections = require('./collections');
13const getParser = require('./getParser');
14const matchNode = require('./matchNode');
15const recast = require('recast');
16const template = require('./template');
17
18const Node = recast.types.namedTypes.Node;
19const NodePath = recast.types.NodePath;
20
21// Register all built-in collections
22for (var name in collections) {
23 collections[name].register();
24}
25
26/**
27 * Main entry point to the tool. The function accepts multiple different kinds
28 * of arguments as a convenience. In particular the function accepts either
29 *
30 * - a string containing source code
31 * The string is parsed with Recast
32 * - a single AST node
33 * - a single node path
34 * - an array of nodes
35 * - an array of node paths
36 *
37 * @exports jscodeshift
38 * @param {Node|NodePath|Array|string} source
39 * @param {Object} options Options to pass to Recast when passing source code
40 * @return {Collection}
41 */
42function core(source, options) {
43 return typeof source === 'string' ?
44 fromSource(source, options) :
45 fromAST(source);
46}
47
48/**
49 * Returns a collection from a node, node path, array of nodes or array of node
50 * paths.
51 *
52 * @ignore
53 * @param {Node|NodePath|Array} source
54 * @return {Collection}
55 */
56function fromAST(ast) {
57 if (Array.isArray(ast)) {
58 if (ast[0] instanceof NodePath || ast.length === 0) {
59 return Collection.fromPaths(ast);
60 } else if (Node.check(ast[0])) {
61 return Collection.fromNodes(ast);
62 }
63 } else {
64 if (ast instanceof NodePath) {
65 return Collection.fromPaths([ast]);
66 } else if (Node.check(ast)) {
67 return Collection.fromNodes([ast]);
68 }
69 }
70 throw new TypeError(
71 'Received an unexpected value ' + Object.prototype.toString.call(ast)
72 );
73}
74
75function fromSource(source, options) {
76 if (!options) {
77 options = {};
78 }
79 if (!options.parser) {
80 options.parser = getParser();
81 }
82 return fromAST(recast.parse(source, options));
83}
84
85/**
86 * Utility function to match a node against a pattern.
87 * @augments core
88 * @static
89 * @param {Node|NodePath|Object} path
90 * @parma {Object} filter
91 * @return boolean
92 */
93function match(path, filter) {
94 if (!(path instanceof NodePath)) {
95 if (typeof path.get === 'function') {
96 path = path.get();
97 } else {
98 path = {value: path};
99 }
100 }
101 return matchNode(path.value, filter);
102}
103
104const plugins = [];
105
106/**
107 * Utility function for registering plugins.
108 *
109 * Plugins are simple functions that are passed the core jscodeshift instance.
110 * They should extend jscodeshift by calling `registerMethods`, etc.
111 * This method guards against repeated registrations (the plugin callback will only be called once).
112 *
113 * @augments core
114 * @static
115 * @param {Function} plugin
116 */
117function use(plugin) {
118 if (plugins.indexOf(plugin) === -1) {
119 plugins.push(plugin);
120 plugin(core);
121 }
122}
123
124/**
125 * Returns a version of the core jscodeshift function "bound" to a specific
126 * parser.
127 *
128 * @augments core
129 * @static
130 */
131function withParser(parser) {
132 if (typeof parser === 'string') {
133 parser = getParser(parser);
134 }
135
136 const newCore = function(source, options) {
137 if (options && !options.parser) {
138 options.parser = parser;
139 } else {
140 options = {parser};
141 }
142 return core(source, options);
143 };
144
145 return enrichCore(newCore, parser);
146}
147
148/**
149* The ast-types library
150* @external astTypes
151* @see {@link https://github.com/benjamn/ast-types}
152*/
153
154function enrichCore(core, parser) {
155 // add builders and types to the function for simple access
156 Object.assign(core, recast.types.namedTypes);
157 Object.assign(core, recast.types.builders);
158 core.registerMethods = Collection.registerMethods;
159 /**
160 * @augments core
161 * @type external:astTypes
162 */
163 core.types = recast.types;
164 core.match = match;
165 core.template = template(parser);
166
167 // add mappings and filters to function
168 core.filters = {};
169 core.mappings = {};
170 for (const name in collections) {
171 if (collections[name].filters) {
172 core.filters[name] = collections[name].filters;
173 }
174 if (collections[name].mappings) {
175 core.mappings[name] = collections[name].mappings;
176 }
177 }
178 core.use = use;
179 core.withParser = withParser;
180 return core;
181}
182
183module.exports = enrichCore(core, getParser());