@impjs/babel-plugin-transform-last-statement

0.0.1-alpha.1 • Public • Published

babel-plugin-transform-last-statement

A Babel plugin to transform the last statements of functions (as well as the program itself if enabled via its options). By default, it'll turn them into return statements.

This is part of an [attempt to push template literals as far as possible towards being used as other template formats (like nunjucks, pug...). Running functions inside the template expressions was a good step (see process-template-literals and omniformat). It made expressing conditionals, as well as loops,especially, cumbersome to read if riddled with return. This transform allows a lighter syntax like:

function template(data) {
  `<ul>
    ${() => {
      for(var i = 0; i++; i <5){
        `<li>${i}</li>`
      }
    }}
  </ul>`
}

// Will get turned into the more verbose:

function template(data) {
  return `<ul>
    ${() => {
      var _result = [];
      for(var i = 0; i++; i <5){
        _result.push(`<li>${i}</li>`);
      }
      return _result;
    }}
  </ul>`
}

Its use is not limited to templates and it will work on any kind of functions (including class and object methods):

// Input
function template(data) {
  if (data.onlyFirstname) {
    `${data.firstName}`
  }

  `${data.firstname} - ${data.lastname}`
}

// Will become the following. Note the first if
// doesn't return as we can't make any assumptions
// about whether it should return or not. 
function template(data) {
  if (data.onlyFirstname) {
    `${data.firstName}`
  }

  return `${data.firstname} - ${data.lastname}`
}

And of course, you can still explicitly return as you please.

Usage

Install the plugin with your favourite package manager. For example, with NPM:

npm install --save-dev babel-plugin-transform-last-statement

Then in your Babel configuration, add it to the list of plugins:

{
  plugins:['transform-last-statement']
}

Options

  • topLevel: By default, the plugin only processes the last statements of functions. Toggling that option on with topLevel: true will also have it process the last statement of the file too.

Processing details

The transform traverses the functions statements, last to first, to determine which to process. It looks into blocks like if,else, switch, try..., gathers the results of loops for, do{...}while... into arrays, and stops the processing if a return, throw, continue was found.

It transforms the last statement, making the function return it. It ignores:

  • function declarations, as they might be written last for readability, taking advantage of hoisting
  • variable declarations, because I couldn't see a valid use case for declaring a variable as last statement. Either return directly, or do something with the variable in further statements.
  • empty statements (for ex a line with only a semi-colon) are ignored, as it doesn't help readability to spot a ; that'll get transformed in return; and make the function return undefined instead of its last actual value.
  • break statements, though they are markers for knowing when to return in switch statements.

The transform does not:

  • take care of eliminating dead code, like a break left after an injected return. This left to minifiers

  • do clever things to detect empty blocks. If you do need that kind of block (I couldn't imagine a use case), you'll need to return explicitly:

    function tempalte() {
      `this won't be returned automatically`
      if (test){
        ;
        ;
      }
    }

See the annotated source for details on how each kind of statements is processed.

Possible future work

  • [ ] Refactor the traversal to use a visitor more similar to Babel's regular visitor.
  • [ ] Allow customisation of how the last statement is processed. This could allow, for example, to write the expression to a Writable stream

Contributing

I'm happy to consider issues or pull requests. However I can't make any guarantee on how responsive I can be reviewing them

  • For bugs, please provide an (ideally isolated) sample of the code triggering the issue, with the behaviour you'd expect.

  • For new features, please provide samples of input and output code.

  • For code contributions, please make sure:

    • that you updated tests. You can either add new .test.js file in the __tests__ folder, or a .input.js file that will get compared with the corresponding .output.js file in the __tests__/__fixtures__/index. See the index.test.js file if you need to tweak processing for specific tests.
    • your code is linted (husky should take care of setting up a git-hook before commit)
    • your commit messages follow the conventional changelog convention (commitizen can help you there)

    These links might also come handy:

    • AST Explorer: For visualizing the AST (pick babylon7 as parser) and trying out transforms (pick babelv7 as transform).
    • Babel AST specifications: List of the nodes that can be part of the AST created by Babel
    • Babel handbook: The guide for creating Babel plugins

Dependencies (0)

    Dev Dependencies (21)

    Package Sidebar

    Install

    npm i @impjs/babel-plugin-transform-last-statement

    Weekly Downloads

    0

    Version

    0.0.1-alpha.1

    License

    MIT

    Unpacked Size

    437 kB

    Total Files

    90

    Last publish

    Collaborators

    • rhumaric