8

My goal is to fake out getting some requirejs code working via babel. I've found that if I add the following: if (typeof define !== "function") { var define = require("amdefine")(module); } to the top of every file while running in nodejs things seem to work out.

Here is some code I wrote, which I thought would work or nearly work:

function injectDefine(babel) {
    var header = 'if (typeof define !== "function") { var define = require("amdefine")(module); }';

    return new babel.Plugin('amdefine', {
        visitor: {
            Program: {
                enter: function(path, file) {
                    path.unshiftContainer(
                        'body',
                        babel.types.expressionStatement(
                            babel.types.stringLiteral(header)
                        )
                    );
                },
            },
        },
    });
}

require('babel-core/register')({
    stage: 0,
    plugins: [{transformer: injectDefine}],
});

require('../components/button');

The components/button file is just me trying to test that some file can load.

Other notes: I'm using babel 5, and I can't upgrade right now. I also can't use a .babelrc very easily right now.

Parris
  • 17,833
  • 17
  • 90
  • 133

2 Answers2

7

Tip 1: the environment variable BABEL_DISABLE_CACHE=1 is needed if you are doing heavy testing of plugins. If you had a script that you ran like npm run unit you may instead want to run like BABEL_DISABLE_CACHE=1 npm run unit while testing your plugin.

Tip 2: babel.parse will give you a full program out of some source. The easiest thing you could do is babel.parse(header).program.body[0].

The following ended up working:

function injectDefine(babel) {

    var header = 'if (typeof define !== "function") { var define = require("amdefine")(module); }';

    return new babel.Plugin('amdefine', {
        visitor: {
            Program: {
                enter: function(node, parent) {
                    node.body.unshift(
                        babel.parse(header).program.body[0]
                    );
                },
            },
        },
    });
}

require('babel-core/register')({
    cache: false,
    stage: 0,
    plugins: [injectDefine],
});
Parris
  • 17,833
  • 17
  • 90
  • 133
  • Very nice - are you going to wrap this up as a nice babel-plugin-inject-banner ?? - I'm happy to push it and @ credit ;) – ShortCircuit May 28 '16 at 21:59
  • @ShortCircuit ahh good idea. Feel free to push and credit! I'd love to be added as an co-owner/author (npm user name: parris). – Parris May 28 '16 at 22:14
  • AFAIK, now in Babel 7 all modifications should be done via `NodePath` API, as @quirimmo's answer demonstrates – Andy Apr 12 '20 at 22:12
4

At this stage, a cleaner solution can be to use @babel/traverse and @babel/types.

Let's suppose you want to append a comment to the top of every file, you could use some code like the following:

// Import the required modules
import * as t from "@babel/types";
import traverse from "@babel/traverse";

// Get your ast (for this, you can use @babel/parser)

// Traverse your ast
traverse(ast, {
  // When the current node is the Program node (so the main node)
  Program(path) {
    // Insert at the beginning a string "Hello World" --> not valid JS code
    path.unshiftContainer('body', t.stringLiteral("Hello World"));
  }
});
halfer
  • 19,824
  • 17
  • 99
  • 186
quirimmo
  • 9,800
  • 3
  • 30
  • 45
  • Where do you place this code in regards to your `npm scripts` defined in the `package.json` file? I also did not understand why you needed `t.stringLiteral` – vsync Jul 03 '21 at 09:41