Skip to content

Initialization, scopes and assignments - possible bugs / possible improvements #167

@aronmandrella

Description

@aronmandrella
  • babel-plugin-macros version: 3.0.1
  • node version: v14.15.1
  • npm version: 6.14.8

Relevant code or config

// File: test.macro.js

const { createMacro } = require("babel-plugin-macros");

let callbackCounter = 0;

// Counts macro calls and adds comments to every detected macro usage
function macroCallback({ references, state, babel, source, config }) {
  callbackCounter += 1;
  for (const importName in references) {
    for (const path of references[importName]) {
      path.addComment("leading", callbackCounter);
    }
  }
}

module.exports = createMacro(macroCallback);
// File: index.js

macro = 1;
macro;

macro("before require");
const macro = require("./test.macro");
macro("after require");

macro = "Direct assignment is always ignored";
macro.v = "This works";

{
  macro("before require");
  const macro = require("./test.macro");
  macro("after require");
}
macro;
// File: index-after-macro.js

"use strict";

macro = 1;

/*1*/
macro;

/*1*/
macro("before require");

/*1*/
macro("after require");
macro = "Direct assignment is always ignored";

/*1*/
macro.v = "This works";
{
  /*2*/
  macro("before require");

  /*2*/
  macro("after require");
}

/*1*/
macro;

Problem description and suggested solutions:

I can see few issues here:

  • You can access a macro in a scope before initialization. It's a bit unexpected and there are no warnings/errors. It may cause some issues especially if there are some global variables.

  • When you do assignments, direct assignment is completely ignored.
    Even if the macro was imported or required as const there is no error.
    I assume that it has something to do with global variables, but I think
    it would be nice, if a user could handle a scenario like this in a macro.
    Either to just print an error message like 'usage like this is not allowed' or
    to do something else.

  • Babel-plugin-macros allows for scoping (awesome), but the way it's handled right now
    makes it impossible to handle all macro usages within a single function (without some nasty tricks, atleast).
    Maybe there is a way to handle scoping better? Sometimes user may want to do some global post-processing in a file after processing all macros. References could be organised like this for example:

[
  {
    scope: 0, // id
    importName: "default/*/name",
    localName: "importName/as",
    path: PathNode(),
  },
  /* .. */
];

I gives more context about the macro usage.
Of course, localName name currently can be easily checked with Identifier name prop.
The main issue are scopes.

Also two minor things.

  • Syntax like this (similar to require()) is not recognized. Not that I need it, it's just something I noticed. It looks like a minor oversight, but maybe it's intentional.
const macro = import('./test.macro');
  • Syntax like this causes an 'Cannot read property 'name' of undefined' error. Again, just something I noticed.
import * as macro from "./test.macro";

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions