developit / htm

Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
Apache License 2.0
8.69k stars 170 forks source link

Babel Macro #150

Open nick-at-hawkfish opened 4 years ago

nick-at-hawkfish commented 4 years ago

It would be nice to have a babel macro that automatically runs the babel plugin to remove the runtime. That way, this project would work out of the box with "zero" config tools like create-react-app.

I'm thinking it could be something like:

import {html} from 'htm/preact/macro'

This is similar conceptually to CSS-IN-JS libraries providing a macro for their tools. See https://emotion.sh/docs/babel-macros

developit commented 4 years ago

Interesting! If it's not a huge undertaking I think that might be nice. The idea of it baking in the JSX pragma based on the path (like you've shown here) is nice.

klvenky commented 4 years ago

Is this taken? I am pretty new to OSS and the repo. I am not sure where to start. Can someone help me out by pointing some resources?

developit commented 3 years ago

Hi @klvenky - sorry for missing your question. Nobody has taken it yet, though I'm afraid I actually don't know where to start! I've never written or used a "babel macro" before. Maybe @nick-at-hawkfish knows?

Haroenv commented 1 year ago

it likely is something like this:

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

module.exports = createMacro(prevalMacros);

function prevalMacros({ references, state, babel }) {
  references.default.forEach(referencePath => {
    if (referencePath.parentPath.type === "TaggedTemplateExpression") {
      asTag(referencePath.parentPath.get("quasi"), state, babel);
    } else if (referencePath.parentPath.type === "CallExpression") {
      asFunction(referencePath.parentPath.get("arguments"), state, babel);
    } else {
      throw new MacroError(
        `dedent.macro can only be used as tagged template expression or function call. You tried ${
          referencePath.parentPath.type
        }.`
      );
    }
  });
}

function asTag(quasiPath, { file: { opts: { filename } } }, babel) {
  const string = quasiPath.parentPath.get("quasi").evaluate().value;
  const { types: t } = babel;

  quasiPath.parentPath.replaceWith(t.stringLiteral(html(string)));
}

function asFunction(argumentsPaths, { file: { opts: { filename } } }, babel) {
  const string = argumentsPaths[0].evaluate().value;
  const { types: t } = babel;

  argumentsPaths[0].parentPath.replaceWith(t.stringLiteral(html(string)));
}

I haven't tried it though