esrewrite
v1.0.0
Published
rewrite imports across files
Downloads
2
Readme
esrewrite
Rewrite file specifiers to point to new locations across source files that may vary in their content type. Useful for renaming files in bulk.
Usage
You need two inputs to use the program: the list of files that were renamed and a graph that outlines the dependencies between the files, including those that were not renamed.
esrewrite(
renames: Array.<Array.<Path, Path>>,
dependencyGraph: Map.<
dependencies: Map.<Path, Object>,
dependents: Map.<Path, Object>
>,
options: Map
)
Example
Consider a simple scenario where the file src/a.js
is being renamed to
src/new-a.js
.
const renames = [
[ 'src/a.js', 'src/new-a.js' ]
]
const graph = {
dependencies: {
'src/a.js': [],
'src/b.js': [{ id: 'src/a.js', userRequest: './a' }]
},
dependents: {
'src/a.js': [{ id: 'src/b.js', userRequest: './a' }],
'src/b.js': []
}
}
esrewrite(renames, graph, {
parsers: [
{
when: file => file.endsWith('.js'),
use: require('esrewrite/parsers/javascript')
}
]
})
After applying, src/b.js
now imports src/a-new.js
instead of src/a.js
.
Options
{
// when present, patterns specified in @include, @exclude and @ignore will
// be resolved relative to the given directory
context: Path,
// do not write changes to disk, hook into @stats to read the changes and
// report on them
dry: Boolean,
// provide such a map to keep track of warnings and the changes to be
// applied by the rewriter for reporting
stats: Map.<
warnings: Array.<String>,
mods: Array.<String>
>,
// process only the files that are under any of the given paths
include: Array.<Union.<Path, RegExp, Minimatch>>,
// do not process files that are under any of the given paths
exclude: Array.<Union.<Path, RegExp, Minimatch>>,
// ignore files that are renamed but do not show up as part of the
// dependency graph; no warnings will be issued and they will be left
// unprocessed
ignore: Array.<Union.<Path, RegExp, Minimatch>>,
// provide any requests that need to be rewritten and are not part of the
// dependency graph, key is the source file, value is a map of the old
// request to the new request
//
// {
// "additionalRequests": {
// "src/b.js": {
// "./a": "./xxx"
// }
// }
// }
//
additionalRequests: Map.<Path, Map.<Path, String>>,
// content type parsers, see Parsing section for more information
parsers: Array.<
Map.<
when: (Path): Boolean,
use: Map.<
parse: (String): Any,
walk: (Any, Function): void,
>
>
>,
// the modifications to apply for the file in question; these have access
// to the matching parser, see Modding section for more information
mods: Array.<
Map.<
when: (Path): Boolean,
apply: (ParsingContext): void
>
>,
// optimize the specifier based on the context, see the Optimizing section
// for more information
optimize: (Path, Path, Object.<request: String>): String,
}
Parsing
A parser implements two interfaces:
parse: (String): Any
The output of which, usually an AST, is fed to the walk routine:
walk: (Any, Function): void
Walk is what esrewrite cares most about; it applies the visitor function to every node extracted in the parse routine. The visitor is a mod that is responsible for actually modifying the contents of the file.
Modding
A mod is a function that is applied to a single file and gets provided with all the parameters it may need to perform the rewrites:
file
: the file being rewrittenlines
: array of lines of the file contents; this is what the mod should be mutatingtext
: buffer containing the file contents; don't modify thiswalk
: the parser's walk routine that can visit every structural node in the filerequests
: a map of the rewrites for the file, key is the old request and value is the new requeststats
: the stats objects that the mod can write tooriginals
: a mapping of renamed files to their original locationsrenamed
: a mapping of original files to their renamed locations
For every adjustment made, be sure to log it in stats.mods
. For warnings,
write to stats.warnings
.
See existing mods under ./lib/mods/
for guidance.
Optimizing
Specifiers can be tuned to better mimic what a human would write. This is especially relevant if the renames use absolute filepaths as you probably do not want that in source files.
The optimize
interface enables this. It receives the following parameters:
- the target file path
- the file being rewritten (context file)
- the original request
The output of optimize
must be a string to be used in place of the target's
filepath.
Here is a sample optimizer that roots all filepaths to the current working directory:
{
optimize: (target, context, { request }) => {
return require('path').relative(process.cwd(), target)
}
}