Code Monkey home page Code Monkey logo

babel-plugin-macros's Introduction

babel-plugin-macros 🎣

Allows you to build simple compile-time libraries


Build Status Code Coverage version downloads MIT License All Contributors PRs Welcome Code of Conduct

The problem

Check out this guest post on the Babel.js blog for a complete write up on the problem, motivation, and solution.

Currently, each babel plugin in the babel ecosystem requires that you configure it individually. This is fine for things like language features, but can be frustrating overhead for libraries that allow for compile-time code transformation as an optimization.

This solution

babel-plugin-macros defines a standard interface for libraries that want to use compile-time code transformation without requiring the user to add a babel plugin to their build system (other than babel-plugin-macros, which is ideally already in place).

Expand for more details on the motivation

For instance, many css-in-js libraries have a css tagged template string function:

const styles = css`
  .red {
    color: red;
  }
`

The function compiles your css into (for example) an object with generated class names for each of the classes you defined in your css:

console.log(styles) // { red: "1f-d34j8rn43y587t" }

This class name can be generated at runtime (in the browser), but this has some disadvantages:

  • There is cpu usage/time overhead; the client needs to run the code to generate these classes every time the page loads
  • There is code bundle size overhead; the client needs to receive a CSS parser in order to generate these class names, and shipping this makes the amount of js the client needs to parse larger.

To help solve those issues, many css-in-js libraries write their own babel plugin that generates the class names at compile-time instead of runtime:

// Before running through babel:
const styles = css`
  .red {
    color: red;
  }
`
// After running through babel, with the library-specific plugin:
const styles = {red: '1f-d34j8rn43y587t'}

If the css-in-js library supported babel-plugin-macros instead, then they wouldn't need their own babel plugin to compile these out; they could instead rely on babel-plugin-macros to do it for them. So if a user already had babel-plugin-macros installed and configured with babel, then they wouldn't need to change their babel configuration to get the compile-time benefits of the library. This would be most useful if the boilerplate they were using came with babel-plugin-macros out of the box, which is true for create-react-app.

Although css-in-js is the most common example, there are lots of other things you could use babel-plugin-macros for, like:

  • Compiling GraphQL fragments into objects so that the client doesn't need a GraphQL parser
  • Eval-ing out code at compile time that will be baked into the runtime code, for instance to get a list of directories in the filesystem (see preval)

Table of Contents

Installation

This module is distributed via npm which is bundled with node and should be installed as one of your project's devDependencies:

npm install --save-dev babel-plugin-macros

Usage

You may like to watch this YouTube video to get an idea of what macros is and how it can be used.

User docs

Are you trying to use babel-plugin-macros? Go to other/docs/user.md.

Author docs

Are you trying to make your own macros that works with babel-plugin-macros? Go to other/docs/author.md. (you should probably read the user docs too).

Caveats

Babel cache problem

Note: This issue is not present when used in Create React App.

Most of the time you'll probably be using this with the babel cache enabled in webpack to rebuild faster. If your macro function is not pure which gets different output with same code (e.g., IO side effects) it will cause recompile mechanism fail. Unfortunately you'll also experience this problem while developing your macro as well. If there's not a change to the source code that's being transpiled, then babel will use the cache rather than running your macro again.

For now, to force recompile the code you can simply add a cache busting comment in the file:

import macro from 'non-pure.macro';

-// Do some changes of your code or
+// add a cache busting comment to force recompile.
macro('parameters');

This problem is still being worked on and is not unique to babel-plugin-macros. For more details and workarounds, please check related issues below:

FAQ

How do I find available macros?

You can write your own without publishing them to npm, but if you'd like to see existing macros you can add to your project, then take a look at the Awesome babel macros repository.

Please add any you don't see listed!

What's the difference between babel plugins and macros?

Let's use babel-plugin-console as an example.

If we used babel-plugin-console, it would look like this:

  1. Add babel-plugin-console to .babelrc
  2. Use it in a code:
function add100(a) {
  const oneHundred = 100
  console.scope('Add 100 to another number')
  return add(a, oneHundred)
}

function add(a, b) {
  return a + b
}

When that code is run, the scope function does some pretty nifty things:

Browser:

Browser console scoping add100

Node:

Node console scoping add100

Instead, let's use the macro it's shipped with like this:

  1. Add babel-plugin-macros to .babelrc (only once for all macros)
  2. Use it in a code:
import scope from 'babel-plugin-console/scope.macro'
function add100(a) {
  const oneHundred = 100
  scope('Add 100 to another number')
  return add(a, oneHundred)
}

function add(a, b) {
  return a + b
}

The result is exactly the same, but this approach has a few advantages:

Advantages:

  • requires only one entry in .babelrc for all macros used in project. Add that once and you can use all the macros you want
  • toolkits (like create-react-app) may already support babel-plugin-macros, so no configuration is needed at all
  • it's explicit. With console.scope people may be fooled that it's just a normal console API when there's really a babel transpilation going on. When you import scope, it's obvious that it's macro and does something with the code at compile time. Some ESLint rules may also have issues with plugins that look for "global" variables
  • macros are safer and easier to write, because they receive exactly the AST node to process
  • If you misconfigure babel-plugin-console you wont find out until you run the code. If you misconfigure babel-plugin-macros you'll get a compile-time error.

Drawbacks:

  • Cannot (should not) be used for implicit transpilations (like syntax plugins)
  • Explicitness is more verbose. Which some people might consider a drawback...

In what order are macros executed?

This is another advantage of babel-plugin-macros over regular plugins. The user of the macro is in control of the ordering! The order of execution is the same order as imported. The order of execution is clear, explicit and in full control of the user:

import preval from 'preval.macro'
import idx from 'idx.macro'

// preval macro is evaluated first, then idx

This differs from the current situation with babel plugins where it's prohibitively difficult to control the order plugins run in a particular file.

Does it work with function calls only?

No! Any AST node type is supported.

It can be tagged template literal:

import eval from 'eval.macro'
const val = eval`7 * 6`

A function:

import eval from 'eval.macro'
const val = eval('7 * 6')

JSX Element:

import Eval from 'eval.macro'
const val = <Eval>7 * 6</Eval>

Really, anything...

See the testing snapshot for more examples.

How about implicit optimizations at compile time?

All examples above were explicit - a macro was imported and then evaluated with a specific AST node.

Completely different story are implicit babel plugins, like transform-react-constant-elements, which process whole AST tree.

Explicit is often a better pattern than implicit because it requires others to understand how things are globally configured. This is in this spirit are babel-plugin-macros designed. However, some things do need to be implicit, and those kinds of babel plugins can't be turned into macros.

Inspiration

Thank you to @phpnode for donating the npm package babel-plugin-macros.

Other Solutions

Issues

Looking to contribute? Look for the Good First Issue label.

πŸ› Bugs

Please file an issue for bugs, missing documentation, or unexpected behavior.

See Bugs

πŸ’‘ Feature Requests

Please file an issue to suggest new features. Vote on feature requests by adding a πŸ‘. This helps maintainers prioritize what to work on.

See Feature Requests

Contributors ✨

Thanks goes to these people (emoji key):


Kent C. Dodds

πŸ’» πŸ“– πŸš‡ ⚠️

Sunil Pai

πŸ€”

Lily Scott

πŸ’¬ πŸ“–

Michiel Dral

πŸ€”

Kye Hohenberger

πŸ€”

Mitchell Hamilton

πŸ’» ⚠️

Justin Hall

πŸ“–

Brian Pedersen

πŸ’» πŸ“–

Andrew Palm

πŸ’»

Michael Hsu

πŸ“– πŸ”Œ

Bo Lingen

πŸ’»

Tyler Haas

πŸ“–

FWeinb

πŸ’»

TomΓ‘Ε‘ Ehrlich

πŸ› πŸ’»

Jonas Gierer

πŸ“–

LoΓ―c Padier

πŸ’»

Paul Sherman

πŸ’»

Conrad Buck

πŸ’» ⚠️ πŸ“–

InvictusMB

⚠️

Eric Berry

πŸ”

Futago-za Ryuu

πŸ’» ⚠️

Luc

πŸ’»

Victor Vincent

πŸ’»

я ΠΊΠΎΡ‚ΠΈΠΊ ΠΏΡƒΡ€-ΠΏΡƒΡ€

πŸ“–

Armando Sosa

πŸ“–

Matthias

πŸ’»

Jovi De Croock

πŸ’» ⚠️

Victor Arowo

πŸ“–

Alex Chan

πŸ“–

Evan Jacobs

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT

babel-plugin-macros's People

Contributors

alexanderchan avatar allcontributors[bot] avatar andarist avatar awto avatar bpedersen avatar conartist6 avatar dralletje avatar emmatown avatar evenchange4 avatar fatfisz avatar haltcase avatar invictusmb avatar jiiincho avatar kentcdodds avatar lucleray avatar matvp91 avatar michaeldeboey avatar papb avatar pshrmn avatar rhys-vdw avatar sergeysova avatar stereobooster avatar stevemao avatar suchipi avatar swyxio avatar tricoder42 avatar trysound avatar victorarowo avatar vpicone avatar wintercounter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

babel-plugin-macros's Issues

Npm test fails on clean clone (Windows)

  • babel-macros version: current master d2ee8fc
  • node version: v8.4.0
  • npm (or yarn) version: 5.3.0 (yarn: v0.21.3)

Getting the following error when running the tests:

Cannot find module 'fake/macro' from 'index.js'

What you did:
http://imgur.com/a/50zxy
git clone
cd babel-macros
npm install
npm test

What happened:
http://imgur.com/a/50zxy

> [email protected] test [Path/to/babel-macros]
> nps test

nps is executing `test` : jest --coverage
 FAIL  src\__tests__\index.js
  ● Test suite failed to run

    Cannot find module 'fake/macro' from 'index.js'

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:179:17)
      at Object.<anonymous> (src/__tests__/index.js:3:14)

Reproduction repository:
This is it!

Problem description:
Seems to be something wrong with jest's mocked module resolution on windows. Importing from fake\\mock works. Babel-jest or jest is probably assuming it's a path and windows-ifies it.

Suggested solution:
Either wait for jest to fix the issue or import without the /.

Mock macros

Problem description:
Sometimes, we would have test cases that need to mock macros.
Mocking this way would be more explicit?

    jest.mock('tagged-translations/macro', () => jest.fn())
...
    t.mockImplementation(
      text => (text === 'all-products' ? `produk-baru` : text)
    )

And in order to support this, babel-plugin-macros would need to disable some transpilations and make it mockable.
What do you guys think?

Module `path` does not exist in the Haste module map

  • babel-plugin-macros version:2.5.0
  • node version:v11.11.0
  • npm (or yarn) version:6.7.0
    Relevant code or config

What you did: I was using lingui/js-lingui for language translation after successful installation. I got and error while running my app.

What happened:It gave me an error Module path does not exist in the Haste module map

error: bundling failed: Error: Unable to resolve module path from /Users/evotan/anubhav/testing/node_modules/ba bel-plugin-macros/dist/index.js: Module path does not exist in the Haste module map

This might be related to facebook/react-native#4968
To resolve try the following:

  1. Clear watchman watches: watchman watch-del-all.
  2. Delete the node_modules folder: rm -rf node_modules && npm install.
  3. Reset Metro Bundler cache: rm -rf /tmp/metro-bundler-cache-* or npm start -- --reset-cache.
  4. Remove haste cache: rm -rf /tmp/haste-map-react-native-packager-*.
    at ModuleResolver.resolveDependency (/Users/evotan/anubhav/testing/node_modules/metro/src/node-haste/Dependenc
    yGraph/ModuleResolution.js:183:15)
    at ResolutionRequest.resolveDependency (/Users/evotan/anubhav/testing/node_modules/metro/src/node-haste/Depend
    encyGraph/ResolutionRequest.js:52:18)
    at DependencyGraph.resolveDependency (/Users/evotan/anubhav/testing/node_modules/metro/src/node-haste/Dependen
    cyGraph.js:283:16)
    at Object.resolve (/Users/evotan/anubhav/testing/node_modules/metro/src/lib/transformHelpers.js:261:42)
    at dependencies.map.result (/Users/evotan/anubhav/testing/node_modules/metro/src/DeltaBundler/traverseDependen
    cies.js:399:31)
    at Array.map ()
    at resolveDependencies (/Users/evotan/anubhav/testing/node_modules/metro/src/DeltaBundler/traverseDependencies.js:396:18)
    at /Users/evotan/anubhav/testing/node_modules/metro/src/DeltaBundler/traverseDependencies.js:269:33
    at Generator.next ()
    at asyncGeneratorStep (/Users/evotan/anubhav/testing/node_modules/metro/src/DeltaBundler/traverseDependencies.js:87:24)
    DELTA [android, dev] ./index.js β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 33.8% (306/526), failed.

Please Help!

Does not work with Babel 7 beta

  • babel-macros version: 1.2.0
  • babel-preset-env: 7.0.0-beta.3
  • node version: 8.6.0
  • npm (or yarn) version: 5.3.0

Relevant code or config

Simple index.js

const test = preval`module.exports = 1`;

console.log(test);

.babelrc

{
  "presets": [ "env" ],
  "plugins": ["babel-macros"]
}

What you did:
Ran yarn babel index.js.

What happened:
Log:

TypeError: Cannot read property 'loose' of undefined (While processing preset: "/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-preset-env/lib/index.js")
    at _default (/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-plugin-transform-es2015-modules-commonjs/lib/index.js:15:22)
    at Function.memoisePluginContainer (/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:113:13)
    at Function.normalisePlugin (/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:146:32)
    at /Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:184:30
    at Array.map (<anonymous>)
    at Function.normalisePlugins (/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
    at OptionManager.mergeOptions (/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
    at /Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:265:14
    at /Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:323:22
    at Array.map (<anonymous>)
error Command failed with exit code 1.

Problem description:
It seems that babel-macros does not play well with the latest beta version of Babel 7. A bit of a shame since I'd like to use macros to solve another issue caused by Babel 7 with some older code.

A similar error is triggered when using babel-preset-react:

TypeError: Cannot read property 'pragma' of undefined (While processing preset: "/Users/nick/Documents/Drawbotics/repos/platform-ff/node_modules/babel-preset-react/lib/index.js")

Suggested solution: None for now

Would it be possible to write an apply macro?

Hello,

First of all, thanks for this great idea. I really like things that are explicit and clear. You may not how the imported macro works, but you are clear about the fact that it is a macro, I love it.

After reading the documentation, the babel handbook and some macro examples I'm still not clear if what I want to do is possible.
I'm using functional programming a lot, and all my functions are curried, which could be a bit cumbersome to call sometimes. For that reason I want to build a small syntactic sugar macro to simulate the $ operator of Haskell. Let me explain it with an example

add $ 11 12
// output
add (11) (12)

Ideally I would want to be a le to nest them or at least group arguments with parentheses :

compose $ (add $ 11) inc
// output
compose (add (11)) (inc)

Do you think such macro is possible?

Thanks in advance

Question: can babel-plugin-macros help enable specific postcss plugins in CRA2?

Hi Kent, thanks for your excellent packages and writings, and congrats on landing macros in CRA2!
I think they're an awesome concept. I read about babel-plugin-macros' experimental support for end-user config of macros (via cosmiconfig), and this gives me hope there might be a good solution here for my use case.

I want to build a create-react-app 2 app, with tailwindcss (and emotion), without ejecting.
I don't want to eject, nor fork react-scripts, but if I understand correctly, it seems that IF there were a postcss babel macro, I might be able to config it (eg for tw as a postcss plugin).

Tailwind is supposed to be used as a postcss plugin. I found babel-plugin-tailwind-components which has a macro, and this supports passing tw classes into emotion... but I think I'd need cra2 to support something like postcss.config.js in order to specifically bring tw into cra's postcss processing.

I know this might be a step removed from babel-plugin-macros per se, but I think my use case is very well aligned w/ your intention to make non-eject cra2 more flexible and powerful. Thanks in advance for any help you can provide!

getting "The macro imported from "../macro" must be wrapped in "createMacro" which you can get from "babel-plugin-macros"" even though I DID wrap it

Hello! really excited to make my first macro! just some minor issues and i suspect a wrong error message...

  • babel-plugin-macros version: 2.2.1
  • node version: 9.4.0
  • npm (or yarn) version: 6.1.0

Relevant code or config

const { createMacro, MacroError } = require('babel-plugin-macros');
// const {getReplacement} = require('./helpers')
const { handleCreateRazor } = require('./index');

export default createMacro(bladeMacros);

function bladeMacros({ references, state, babel: { types: t } }) {
	Object.keys(references).forEach(referenceKey => {
		if (referenceKey === 'createQuery' || referenceKey === 'createFragment') {
			references[referenceKey].forEach(referencePath => {
				handleCreateRazor(referencePath, t);
			});
		} else throw new MacroError('invalid require?');
	});
}

and my fixture code:

import {Connect, query} from 'urql'
import {createQuery} from '../macro'

const movieQuery = createQuery('$id: id')
const Movie = ({id, onClose}) => (
  <div>
    <Connect
      query={query(movieQuery, {id: id})}
      children={({data}) => {
        const DATA = movieQuery(data)
        return (
          <div>
            <h2>{DATA.movie.gorilla}</h2>
            <p>{DATA.movie.monkey}</p>
            <p>{DATA.chimp}</p>
          </div>
        )
      }}
    />
  </div>
)

What you did:

i tried to run the tests

What happened:

  ● macros β€Ί basic test of functionality

    /Users/swyx/OpenSource/blade.macro/babel-blade/packages/babel-plugin-blade/src/__tests__/fixtures/macro-basic.js: The macro imported from "../macro" must be wrapped in "createMacro" which you can get from "babel-plugin-macros". Please refer to the documentation to see how to do this properly: https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/author.md#writing-a-macro

      at applyMacros (node_modules/babel-plugin-macros/dist/index.js:174:11)
      at PluginPass.ImportDeclaration (node_modules/babel-plugin-macros/dist/index.js:92:9)
      at newFn (node_modules/babel-traverse/lib/visitors.js:276:21)
      at NodePath._call (node_modules/babel-traverse/lib/path/context.js:76:18)
      at NodePath.call (node_modules/babel-traverse/lib/path/context.js:48:17)
      at NodePath.visit (node_modules/babel-traverse/lib/path/context.js:105:12)
      at TraversalContext.visitQueue (node_modules/babel-traverse/lib/context.js:150:16)
      at TraversalContext.visitMultiple (node_modules/babel-traverse/lib/context.js:103:17)
      at TraversalContext.visit (node_modules/babel-traverse/lib/context.js:190:19)
      at Function.Object.<anonymous>.traverse.node (node_modules/babel-traverse/lib/index.js:114:17)

image

Reproduction repository:

https://github.com/sw-yx/babel-blade/blob/macrosIs/packages/babel-plugin-blade/src/macro.js

Problem description: I want to do named exports similar to emotion (related issue: #5) - however it is somehow not working and i suspect the error message might be wrong? i really am not sure what to do here.

Suggested solution: not sure at all. i tried looking at how Emotion does it, thats what my solution is based on.

Feature : Configuration with babel config (.babelrc, ...)

Would it be possible to configure macros directly with the babel config (.babelrc or other babel config) :

// .babelrc
{
  "plugins": ["macros", {
    "taggedTranslations": { 
      "someConfig": {}
    }
  }]
}

Then you get it like this in the macro :

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

const configName = 'taggedTranslations'

module.exports = createMacro(taggedTranslationsMacro, { configName })

function taggedTranslationsMacro({references, state, babel, config}) {
  // config is { someConfig: {} }
}

This way, it's easier to test a specific config :

import pluginTester from 'babel-plugin-tester'
import plugin from 'babel-plugin-macros'

pluginTester({
  plugin,
  snapshot: true,
  babelOptions: { filename: __filename },
  tests: [{
    code: `...`,
    babelOptions: {
      taggedTranslations: { someConfigIWantToTest: {} } // i add my config here πŸš€
    }
  }],
})

Support options to babel-macro

Problem

import t from 'tagged-translations/macro';

const name = 'Vinh Le';

t`Hello ${name}`;

This is my use case that I need to have some configurations. It could be the location of the translation file.

The current version has no way to do this.
We might need to configure these options globally.

ReferenceError: `macro name` is not defined

  "dependencies": {
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "react-scripts": "2.1.2"
  },
  1. create-react-app macro

  2. Open this macro project.

  3. Create a file convert.macro.js

// @ts-ignore
const { createMacro } = require("babel-plugin-macros");

module.exports = createMacro(convert);

function convert({ references, state, babel }) {
  console.log(references);
}
  1. Add something to app.js
import convert from "./convert.macro";

convert();
  1. yarn start

  2. In the browser, see the error:

Γ—
ReferenceError: convert is not defined
Module../src/App.js
src/App.js:6
  3 | import "./App.css";
  4 | import convert from "./convert.macro";
  5 | 
> 6 | convert();
  7 | 
  8 | class App extends Component {
  9 |   render() {
View compiled
__webpack_require__
/Users/albertgao/codes/temp/tempm/webpack/bootstrap:782
  779 | };
  780 | 
  781 | // Execute the module function
> 782 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
      | ^  783 | 
  784 | // Flag the module as loaded
  785 | module.l = true;

Cannot apply macros when imported from a module outside of default resolution paths

  • babel-plugin-macros version: 2.4.2
  • node version: 8.9.4
  • npm (or yarn) version: npm 5.6.0

Hi, Kent! Macros are super cool. Thanks for building this plugin. lingui-js is a really cool use case that I've been integrating lately. It uses macros to identify and extract translatable strings at build time, which simplifies react internationalization a great deal. However, I've run into an issue regarding node resolve that I could use your advice on before I try and submit a patch.

Relevant code or config

I have a project that loads modules into my bundle using a webpack alias for a directory outside of my project root. These modules in turn import macros. For example: require("#EXTERNAL_PLUGINS/index.js")

where #EXTERNAL_PLUGINS is a webpack alias for something like "../external_plugins"

Babel fails to load these modules if they import a macro because of a recent change to applyMacros that adds resolve.sync with a basedir set as the source file directory (which again, is outside my working folder and therefore doesn't have node_modules)

Here is the change:

1785a0f#diff-1fdf421c05c1140f6d71444ea2b27638R155

What happened:

resolve.sync throws an exception because it is unable to locate the macro with the configured basedir. Everything works correctly when using babel-plugin-macros 2.4.1

Reproduction repository:

https://github.com/brandonc/load-macros-outside-package

Suggested solution:

I took a stab at a fix but was unable to find a suitable basedir to use with resolve.sync. This is a hard problem with zero configuration. Looking for ideas from you for a suitable change.

Clarification on the kind of code that should be returned from a macro

  • babel-plugin-macros version: ^2.0.0
  • node version: v8.11.1
  • npm (or yarn) version: Yarn 1.9.2

Not a direct issue with babel-plugin-macros, instead asking for clarification on what the returned value of a macro should look like if it returns javascript.

The title might not be that descriptive of the actual issue, but I couldn't find a better way to describe the question πŸ˜†.

Should macros return code that is expected to still be transformed by regular babel plugins, or should it return code that is consumer ready (i.e. transforming jsx down to createElement calls).

I am working on https://github.com/hamlim/inline-mdx.macro which transforms a string to a component that renders some JSX, so it currently transforms into something like this:

const Component = ({ components }) => <MDXTag ...><div>{`Some Text`}</div></MDXTag>

(... on MDXTag is just omitting some other props)

Should this macro instead return something like:

var Component = function(_ref) {
  var components = _ref.components;
  return React.createElement(MDXTag, { ... }, ...);
};

Replaced macro entry raising "undefined" error when using macros with `create-react-app`

  • babel-plugin-macros version:
    2.5.0 (came with create-react-app setup)

  • node version:
    v10.10.0

  • npm (or yarn) version:
    yarn - 1.13.0

What happened:
Hi,

I've trying to create a macro plugin within my create-react-app. I've basically followed the steps mentioned here but facing a weird issue. Not really sure if this is the place to post this (please let me know otherwise).

I am trying to create a macro that takes parameters and performs modifications on the path-name.
So basically ->

importFlavored(Hello, "./hello")
|
|----- Gets converted into ---------|
                                    v

import Hello from './hello.green'
// ./hello is modified into ./hello.green on purpose.

It seems to be working in the ASTExplorer properly (I am using the exact source for the plugin). Here's the link.
But when I build the app using yarn install, I am getting 'Hello' is not defined in the console.

Failed to compile
./src/App.js
  Line 4:   'Hello' is not defined  no-undef
  Line 9:   'Hello' is not defined  no-undef
  Line 12:  'Hello' is not defined  react/jsx-no-undef

For a split second, I am able to see the rendered component properly (I've made the entire background color green inside the component. So the component seems to get loaded)
Here is the source repo of the react-app

Kindly let me know if I am doing something wrong here, or if there is a better way to do what I am doing.

Reproduction repository:
https://github.com/gnithin/macro-trial-plugin.git

How to implement macro with async function inside?

  • babel-plugin-macros version: 2.1.0
  • node version: 8.6
  • npm (or yarn) version: yarn 1.5.1

Relevant code or config

What you did:

What happened:

Reproduction repository:

https://github.com/stereobooster/lqip.macro

Problem description:

I want to implement lqip.macro. I'm not sure how to use async lqip in babel-plugin-macros, which seems to be expecting synchronous code. Am I wrong here? What is the way to work with async functions? Thanks

Suggested solution:

Prevent macro from running without babel-marcos

I was wondering, as I am using babel-macros in a project now, if there could be a way added so macros could "catch" it when they are being imported in a normal file.

As it seems now, there isn't such thing. The this is unset, if packing with webpack I could check if I am running in node (browser won't), but I also use this on node. Possible would be to check the arguments, but as the mantra of this repo is, I think explicit is better than implicit ;)

I wonder what the best way is though:

  • Setting this seemed good, but won't work with arrow functions
  • Setting a property to check on the macro function itself:
      const macros = require(requirePath)
    + macros.with_babel_macros
      macros({
        references: referencePathsByImportName,
        state,
        babel,
      })
    
    Feels kinda hacky, but again we are putting macros into javascript,
    So who cares about hacky πŸ˜… (This one is actually also possible (cleaner?) with Symbols)
  • Passing a symbol as first argument that can be compared
    import babelMacros from 'babel-macros';
    export default ({ is_babel_macros, references, state }) => {
      if (is_babel_macros !== babelMacros.is_babel_macros) {
        ...
    

Most likely I am missing an option or some perspective, so I'd like to discuss this :)

Operator macros support?

Will it be useful to add operator macros support?

Maybe like this:

import { createOperatorMacro, operators } from 'babel-plugin-macros';

const plusMacro = ({ references, state }) => {
    // E.g.
    // Import some function
    // Replace + applications with this function application
};

export default createOperatorMacro(operators.plus, plusMacro);

And then:

import 'my-plus.macro';

console.log(1 + 2 + 3);

Turns into:

import { myAwesomeAddition } 'my-awesome-operators';

console.log(myAwesomeAddition(myAwesomeAddition(1, 2), 3));

Let's discuss TypeScript support.

I am well aware that this is Babel world project and it was meant like that from the start. However, I would like to initiate the discussion here what it would take to have macros available in TypeScript world. Macros are such a great concept it would be almost a crime to leave it like this.

Problem

I am mostly coming from the situation I am trying to solve now. The LinguiJS is going to be using macros to transform i18n elements. While in a Webpack world it's possible to run ts-loader and babel-loader in tandem, it already introduces challenges, eg. keep JSX untransformed so Babel can have enough information and do JSX transformation on its own later along with Macros.

Also, other toolings needs to solve the same. Be it extraction process of Lingui itself or Jest testing or any other bundler I suppose. None can use Webpack loaders and have its own way around. This is no good for a whole ecosystem in my opinion.

I am sure this situation is not unique and with rising adoption of TypeScript there will surely be more projects popping out trying to solve the same. I feel it would be rather unhealthy to keep ecosystem separated like this. Not only for library authors, but for users as well.

Solutions?

TypeScript has it's own "plugin" system or rather it's called custom transforms. There is some AST apparently, but I do have trouble finding a real documentation for that. Honestly, I've never been fiddling with AST in Babel, so I am probably the least capable person to deliver any valid information in here.

Would it be viable to basically have a Typescript transform that can understand babel macros out of the box? Most likely that due to AST difference the authors of macros would need to account for both variants, but on the user end it would mean complete transparency as you could use either language without hassle.

Goal

To prepare a TypeScript plugin/transform that would work in a similar fashion to babel-plugin-macros allowing user code driven transformation instead of relying on bunch of hard-to-configure plugins. Ultimately, if we can achieve some level of compatibility between these projects and bridge the gap, it make life easier for everyone.

Current stage

None :) We are seeking some smart people who have insight into TypeScript transforms and AST. We need to create a prototype that mimics the behavior of babel-plugin-macros at some basic level to verify the core idea.

Why not to use Babel-Typescript support?

One might argue that since there is TypeScript support in Babel now, it's a clear path and no need to bother with this. I would agree in case that support would be building on a real TypeScript compiler. Since it's completely separate implementation, it might introduce unexpected errors and incompatibilities. VSCode checking can tell A and then Babel will transform it into B. Current lack of some features is an example of that and more might come.

Personally, I think it's a wrong path, but that's not the point of this discussion. Perhaps I am mistaken on this and I would love to be proven otherwise.

Macros referencing local objects

Right now, macros are unbound functions:

import translate from 'i18n/macro'

translate`Hello ${name}`

// becomes
translate('Hello {name}', { name })

However, some macros might require reference to local objects. In the example above, translate function should receive i18nobject holding translations and active language:

import i18n from 'i18n' // fictional i18n lib
import translate from 'i18n/macro'

translate`Hello {name}`

// should be
i18n.translate('Hello {name}', { name })
// or
translate(i18n, 'Hello {name}', { name }) 

In React it's easy, because components have access to context:

import Translate from 'i18n-react/macro'

<Translate>Hello {name}</Translate>

// becomes
<Translate id="Hello {name}" values={{ name }} />

and component will access i18n configuration from context.

However, I don't have an idea how to handle this with JS functions. i18n might have different name (so we can't simply compile translate to i18n.translate), when initialized locally. However, it could be enforced that translate macro requires i18n in scope (as React is required when using JSX)

Attempt 1

If we allowed runtime configuration of macros, it would have to be configured in every file, given the explicit nature of macros:

import { i18n } from 'i18n'
import translate from 'i18n/macro'

// need to be explicit in every file -> error prone
translate.use(i18n)

translate`Hello {name}`

Attempt 2

Pass i18n as an argument to macro. Macro will process all AST nodes, so this should work:

translate(i18n, `Hello ${name}`)

but I just dislike this syntax (compared to i18n.translateHello {name}`)

Any ideas? So far enforcing i18n in scope seems to be easiest way to go.

read environment specific config

It would be great if babel-plugin-macros could read environment specific configuration the way babel does, e.g. using styled-components macro with this configuration in package.json would then use { displayName: true} unless in production, where it would use { displayName: true }

{
  "babelMacros": {
    "styledComponents": {
      "displayName": true,
      "env": {
        "production": {
          "displayName": false
        }
      }
    }
  }
}

What do you think?

Feature request: Support JSX attribute nodes

Hey Kent, I'd like to create a macro that is triggered on an JSX attribute node, in the same way that this babel plugin transforms attribute nodes: https://github.com/insin/babel-plugin-react-html-attrs/blob/master/lib/index.js

However, when I declare a macro and put it on an attribute, the transform function never fires.

//some.macro.js
const {createMacro} = require('babel-macros');

module.exports = createMacro(({references, babel}) => {
  console.log("Visiting some AST node...")
})
//input.js
import React from 'react'
import macro from 'some.macro.js'

function SomeComponent(){
  return <div macro={123}>Hi there</div>
}

but compiling shows that the node is never traversed:

babel input.js -o output.js
# The console statement never prints...

Discussion: set a global that specifies if executed by babel-macros

I really like this project, thanks a lot for creating this so quickly @kentcdodds!

I have a suggestion for creating a global that's true when a macro is executed in babel-macros context.

If you set a global like module.macro (like module.hot) the dependency has the choice what code path to execute.

I think this will be beneficial for dependencies that have optional macros. For example for styled-components, styled-components could be completely run in runtime, but also has an option for compile time if babel-macros is installed.

Curious what others think about this πŸ˜„ .

Struggling with AST transformation

I'm trying to acomplish the following transformation with a macro :

before :

export const f = myMacro(
  ['a','b'],
 (x1,x2) => 123
)

after :

export const f = (x1, x2) => 123
export const f___ = ['a','b']

Basically the macro takes an array and a function, it replaces the macro invocation with
the function declaration (second macro arg), and declares a variable that references the array (first arg of the macro).

I'm struggling with the tree manipulation API, parentPath.replaceWith. parentPath.replaceWithMultiple, parentPath.insertAfter. So far the only thing I manage to accomplish is replace the macro call,
I am unable to replace the variable declaration of "insertAfter" it.

Perhaps this is a question for babel-types, but I tought I'd ask here to check if it's possible in the first place.

How to identify a macros

Right now we identify a macros if the import/require source string matches this regex: /[./]macros(\.js)?$/

@gaearon mentioned:

The only thing I don’t quite like is the .macros naming, maybe we can come up with an alternative suffix.

@threepointone suggested:

The ideal solution would be to have separate syntax, e.g. importMacros, but that's likely never going to happen
Another option is to use a webpack inspired 'babel-macros?css', but that straight up looks alien
Alternate words off the top of my head - mods, plugs, socks(?), defines

So then I said:

I want to make sure that existing tooling will work as much as I can so I want to avoid anything too weird looking... πŸ€”

I don't mind macros myself, but happy to consider other names πŸ‘

To which @gaearon replied:

The thing is, it also need to be explicit enough that you wouldn’t mistake it from a regular import. Because otherwise it’ll be a debugging nightmare for the person who doesn’t know what’s going on.

Then @suchipi suggested:

Flow has import type, and sweet.js uses import ... for syntax. Is adding a similar syntax extension to babylon going too far for something like this?

And finally I said:

I'd be fine with it if babylon supported it via a plugin, then people could just include a preset we make (and CRA could include it in its own config theoretically). I like:

import macros glam from 'glam.macros'

But I'm open to pretty much anything else.


So now that we're all caught up, let's discuss the options. Other people I'd love to get input from are: @hzoo, @loganfsmyth, and @jlongster πŸ˜„

Here are the options as I see them:

  1. babylon plugin for new syntax
  2. matching imports where the source matches a regex

Any other ideas?

Feature request: support macros importing macros

I'm trying to write a macro that is to be used inside another macro but that doesn't seem to work.
Would it be possible to recursively call the plugin on imported macros?
Another use case would be that I could define a macro in my project that itself is transpiled with the same configuration as my project.

Using create react app, the errors using babel-macros are very cryptic.

  • babel-plugin-macros version: whatever react-scripts@beta is using
  • node version: v8.11.3
  • npm (or yarn) version: 6.2.0

Relevant code or config

when using a macro that loads files from the file system, such as graphql.macro it'll just give an error saying Failed to compile which is pretty cryptic.

What you did:

Load a file at an invalid path

What happened:

Failed to compile
./src/screens/Todos/List.js

Reproduction repository:

Problem description: cryptic error messages

Suggested solution: if its possible, make errors a bit more helpful.

Doesn't work as expected with babel-node

Using the latest babel-macros and node (everything pulled fresh), and the simple eval macro described in the fixtures, babel-node blows up on a file that compiles just fine.

The following input file

//input.js
import Eval from '../eval.macro'
console.log(Eval`1 + 1`);

generates the following output file (as expected) when running babel input.js -o output.js:

//output.js
console.log(2)

So that the following command prints 2 to the console as expected

babel input.js -o output.js && node output.js
#prints 2

But babel-node blows up:

babel-node input.js

/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/index.js:590
      throw err;
      ^
SyntaxError: /Users/Scott-mb/Projects/react-directive/consumer/index.js: /Users/Scott-mb/Projects/react-directive/.babelrc: Error while parsing JSON - Unexpected EOF at line 1 column 2 of the JSON5 data. Still to read: ""
    at error (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/json5/lib/json5.js:56:25)
    at word (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/json5/lib/json5.js:393:13)
    at value (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/json5/lib/json5.js:493:56)
    at Object.parse (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/json5/lib/json5.js:508:18)
    at ConfigChainBuilder.addConfig (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/build-config-chain.js:150:65)
    at ConfigChainBuilder.findConfigs (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/build-config-chain.js:96:16)
    at buildConfigChain (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/build-config-chain.js:61:13)
    at OptionManager.init (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-core/lib/transformation/file/options/option-manager.js:354:58)
    at compile (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:103:45)
    at loader (/Users/Scott-mb/.nvm/versions/node/v8.4.0/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:144:14)

Any ideas what the issue could be?

Calculating the `requirePath` assumes relative paths

Problem description:

this:

const requirePath = p.join(p.dirname(filename), source)
const macros = require(requirePath)

should also handle absolute paths and requires for node_modules

Suggested solution:

Make it handle those cases

Support caching the result of macros

First thing first: thank you for giving the world this plugin! Sorry I erased the issue template but this is more of a feature request / RFC

I imagine that facebook/create-react-app#3856 (comment) would be a showstopper for people that want to use macros as a loader, limiting the use case of macro itself, I imagine that maybe we could store the result of macros to node_modules/.cache and import it in the generated code? I'm not sure if that works and that this is the best place to discuss it, but maybe we can add docs for people who want to write macros and cache the results?

disclaimer: I'm a noob in babel and ASTs, even after seeing videos of how to write it i still don't grasp it fully... I just see it as a potential move from webpack's loader to something that is more portable and easier to use.

macro: decorators

I've been testing out CRA 2.0, but found the lack of decorators frustrating since Mobx heavily recommends them. Is there a current path to use a babel-macro so enable this feature for CRA?

Order of application in non-macro transforms chain

Order of application in non-macro transforms chain
In my macro, I need to covert async generators, but if I simply invoke babel with "preset-env":

$ babel --plugins @effectful/es/transform --presets @babel/preset-env index.js

All the async generators will be cleared and converted to plain generators. So my macro is applied somewhere between "@babel/plugin-proposal-async-generator-functions" and "@babel/plugin-transform-regenerator" - I don't have async generators but still have plain generators in my macro AST. This sounds like a pretty random and non-deterministic position. There are no definite order and anything can be broken by just adding some merge-preventing plugin in the chain after "babel-plugin-macros".

Adding "passPerPreset:true" solves the problem, but unfortunately this is not an option for zero config tools like Create React App.

I guess this happens because the next plugins are merged with the main "babel-plugin-macros" pass which simply searches for imports, while the macros are applied after that merged transform is finished for already transformed AST.

Can we make the search as a nested traversal so it is not merged?

Feature request: Keep track of variables assigned to a macro

Sorry for deleting the issue template but this is feature request.

First of all, thanks for building babel-plugin-macros. It finally makes working with babel-plugin controll- and maintainable.

Maybe my use case is a little bit special but I would propose to add a way to keep track of results and assigned variables that contain a value "returned" by the macro. To give an example:

import myMacro from './my.macro'
const res = myMacro();
console.log(res()) 

Currently this will result in an default array that only contains the CallExpression in line 2. But not the subsequent call in line 3.

My specify use case for this feature would be to write a macro that would be capable of implementing retarget a library that is using tagged template or proxy objects to generate reusable selector functions.

Example:

import retarget from 'retarget' // retarget.macro
const STATE = {
    a: { b: 'Hello World' }
}

const selectA = retarget.a;
const selectB = retarget.b;
const selectAB = retarget[selectA][selectB];

console.log(selectAB(STATE)) // Hello World

try it

If babel-plugin-macro would allow for keeping track of the result of a macro the code above could be transpiled to something like this:

const STATE = {
    a: { b: 'Hello World' }
}

const selectAB = (obj) => obj.a.b;

console.log(selectAB(STATE)) // Hello World

If you would be interested in supporting this feature I would try to implement this.

macro vs function calls

I hope I get right that this really is a way to replace AST at compile time - I love that idea!

Is there any way to indicate that this is macro rather than regular function call? Something like
myMacro!(...) in rust?

If not and the reason is babel can't parse that, maybe I could help with that.

Does not work on ASTExplorer

  • babel-plugin-macros version: 2.4.2+
  • node version: 11.9.0
  • npm (or yarn) version: 1.13.0

Relevant code or config

This commit (1785a0f) added a dependency of the resolve module, which internally uses fs.

const requirePath = resolve.sync(source, {

ASTExplorer uses Webpack's node setting to shim fs with an empty object.

https://github.com/fkling/astexplorer/blob/8b30e3d79498804bedfda4d4c802538fe0c60f5e/website/webpack.config.js#L240

What you did:

Attempted to use an import in the code (top left box) section on ASTExplorer.

What happened:

An i.statSync is not a function error

Reproduction repository:

n/a

Problem description:

  1. Go to ASTExplorer
  2. Activate the babel-plugin-macros transform
  3. Add an import statement in the code (top left) section.

Suggested solution:

path.relative?

Question: Clarify use case

Hello,
I'm just trying to wrap my head around is.

Suppose we have a lib precompile. Instead of:

  1. Add babel-plugin-precompile
  2. Import function

We're going to have:

  1. Add babel-preset-macros (only once)
  2. Import function from precompile.macros

Is that correct? (I changed numbering on purpose, so it's clear that babel-macros are one step easier)

My questions are:

  1. How is determined order of plugins? I believe precompilation should be done first before compilation, but what if some plugins/macros will require to come after?
  2. I saw only examples using tagged template literals. Does this solution work also for other elements? For example, I'm precompiling anything between <Trans> tags inside React.
  3. How does it work for plugins, which does general code optimization, like transform-react-constant-elements? I.e: plugins, which you don't import in code, but rather works implicitly.

Thank you! I'm really hoping this will grow in usable solution. I'm willing to help anytime! πŸ‘

Keep macro import for other plugins

Hey,
I have an plugin, which is supposed to run on nodes returned from macro. The catch is that I'm also checking imports in the file just to be sure I'm processing the right components.

However, once babel-plugin-macros removes an import node, other plugins won't run (obviously, because that node no longer exists).

One other thing to note is that after your macro has run, babel-plugin-macros will remove the import/require statement for you.

I'm not able to figure out the best solution here:

  1. Add option to macro to keep import nodes. Plugin would be added to config as ["macros", { keepImports: true }] (I'm happy to provide a PR if you give a green light to this)
  2. I can't append import node manually after current node, because it would be picked again by macros plugin (and probably case infinite loop).
  3. I can't extract messages as part of macro (although that would be really cool), because I need to also support users who don't use macro at all (macros basically transform Trans from @lingui/react.macro to Trans from @lingui/react, so it's possible to use the lib without macros).

Incorrect package name in exception

  • babel-plugin-macros version: 2.3.0

What you did:
Raised an exception in macro from package @lingui/react.macro.

What happened:
Message show up with appended help text: Learn more: https://www.npmjs.com/package/@lingui

Problem description:
The package name in help text is invalid, it should be
Learn more: https://www.npmjs.com/package/@lingui/react.macro

Suggested solution:
The help text is added here

According to docs, valid macros ends with /macro, /macro.js, .macro, .macro.js.
These are possible cases:

NPM Package Name Macro
package package/macro
package package/macro.js
package package/name.macro
package package/name.macro.js
package.macro package.macro
@org/macro @org/macro
@org/name.macro @org/name.macro

Looking at this table, we should replace anything after / only if package name (source) doesn't start with @

Am I right? Is it possible to import from @org/package/module/macro?

What is the recommended way to report a macro misuse error ?

I'm writing a macro with babel plugun macro (cool project btw !)
I use it within the rollup.js bundler.

If I encounter misusage of the macro I would like to report the error,
If I just throw an exception, rollup just exists without any message to the console.

I also tried throwing the exception using this method :

https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#-throwing-a-syntax-error

and it also exists with no error message in the console.

Curried function application

Hi there. This is more of a feature request than a bug report, so I hope you don't mind my not using the template.

I have the following simple library for applying static functions infix:

const Obj = {
  map: f => o => Object.keys(o).reduce((p, k) => ({ ...p, [k]: f(o[k]) }), {})
};

const infix = X => {
  const wrap = x => {
    const methods = Obj.map(f => v => wrap(f(v)(x)))(X);
    return { ...methods, unwrap: x };
  };
  return wrap;
};

Which can be used for example like so:

const { infix } = require("@masaeedu/infix")

const F = { "|>": f => x => f(x) } // or equivalently, `"|>": x => x`

const four = infix(F)(1)
  ["|>"](x => x + 1)
  ["|>"](x => x + 2).unwrap

which works identically to:

const four = F["|>"](x => x + 2)(F["|>"](x => x + 1)(1))
        // = (x => x)(x => x + 2)((x => x)(x => x + 1)(1))
        // = (x => x + 2)((x => x + 1)(1))
        // = (x => x + 2)(1 + 1)
        // = (1 + 1 + 2)

This works fine, but has a runtime cost. I'd like to perform the equivalent desugaring for any invocation of infix, and was hoping to be able to use babel-plugin-macros for this.

However right now (if I understand correctly), if infix is expressed as a macro, it will only support AST manipulation in the body of the first argument, instead of throughout the infix(F)(1)... expression.

Would you be amenable to supporting this sort of use case in addition?

Allow custom JSX macros

Followup to emotion-js/emotion#673

Problem description:

It's not possible at the moment to use macro as custom JSX.

Suggested solution:

I think it should be possible to read jsx pragma and check it against imported macros to see if we should apply macros on them too.

Not sure though if due to babel plugin ordering issue it would be easy to support this use case because jsx transform runs on Program.enter & babel macros run on ImportDeclaration. It should be possible to switch to Program.enter too - but again, plugin ordering might bite us on this.

Also not sure how to support other pragmas with it (to support both jsx macro + preact at the same time).

Thoughts?

Add default error handling wrapper.

Suggested solution:

Add a try/catch around wherever you apply the macro so that macros will have better error handling out of the box.

Bonus points:
Add a way for macros to identify themselves including a link to their repo for issues.

Clarification on type of dependency.

The documentation specifies that babel-plugin-macros should be a devDependency, but I cannot seem to find any mention of whether a macro should be a dependency or a devDependency.

Let's say I have an application my-application that depends on a package my-package that uses a macro my.macro. When I publish my-package, it is transpiled, so my-application should not require my.macro or babel-plugin-macros to consume it. Does this mean my.macro should be a devDependency of my-package?

Dependency vulnerability (npm audit)

  • babel-plugin-macros version: 2.5.0
  • node version: any
  • npm (or yarn) version: any

Relevant code or config

What you did: run npm audit

What happened:

advisory

Problem description: npm advisory due to vulnerability: https://www.npmjs.com/advisories/788

Suggested solution: release new version with the dependency cosmiconfig version number bumped to 5.2.0 (minimum)

Finding available macros

Hey I was thinking it might be useful to keep a list of available macros out in the wild in this repo?

I've just published support for babel-macros in babel-plugin-console.

I know the infamous babel-plugin-preval supports macros.

Any others?

I'm happy to send a PR documenting the list of existing macros πŸ˜„

Also I've created the following badge to celebrate this package πŸŽ‰

Babel Macro

named exports

libs like emotion will need to export multiple macros, so babel-macros could support named exports, such that the following works.

import { styled, css } from 'emotion/macros'

let red = css` color: red `

let Heavy = styled('div')`
  composes: ${red};
  font-weight: bold;
`

// ... etc ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.