npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

esrewrite

v1.0.0

Published

rewrite imports across files

Downloads

4

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 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)
    }
}