1.0.0 • Published 3 years ago

esrewrite v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

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 rewritten
  • lines: array of lines of the file contents; this is what the mod should be mutating
  • text: buffer containing the file contents; don't modify this
  • walk: the parser's walk routine that can visit every structural node in the file
  • requests: a map of the rewrites for the file, key is the old request and value is the new request
  • stats: the stats objects that the mod can write to
  • originals: a mapping of renamed files to their original locations
  • renamed: 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:

  1. the target file path
  2. the file being rewritten (context file)
  3. 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)
    }
}