npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

babel-plugin-as-macro

v1.0.3

Published

add macro power to javascript code

Downloads

6

Readme

babel-plugin-as-macro

Motivation

When I was using Next.js, I want to use the y18n module for internationalization. But y18n uses the file system(fs module) to save words and their meanings. Importing any module that uses the file system is prohibited by next.js. The first solution that I found, was preval module. But my need is more. I want to have a syntax as simple as :

import React from "react";
import /*as macro*/ y18n from "y18n";
{/*as macro*/{
	y18n = y18n(/* some config */);
	y18n.setLocal("en")
	var __ = y18n.__;
}}
export default function page() {
	return (
		<div>
			<p>{__("HELLO WORLD")}</p>
			<button>{y18n.__n("one button", "%d two button", 2)}</button>
		</div>
	);
}

Installation

You can install this module via npm.

npm install --save-dev babel-plugin-as-macro

Then, you must configure babel with .babelrc file.

{
	//...
	// "presets": ["next/babel"],  /* Add this line if you want to use with next.js */
	"plugin": [
		// other plugins that you want to
		// be executed before this plugin
		["as-macro",/*plugin options*/],
		// plugins that will be executed
		// after this plugin
	]
}

Usage

Macro Definition

There are three ways to define a macro. Because of the nature of macro that is not in any scope of your code, we force by default that macro definitions only be possible in the global scope.

Import Declaration

You can define macro by importing a module. You must place the comment block /*as macro*/ after import identifier.

import /*as macro*/ y18n from "y18n";
import /*as macro*/ X,{x1 as y1,x2 as y2} from "anyModule";

The import statement will be deleted from the code.

In the import statement you can use ECMAScript modules(don't use *.jsx) or node modules. We use esm module for importing ECMAScript modules.

Variable Declaration

You can define macro by defining variables. You must place the comment block /*as macro*/ after var identifier.

var /*as macro*/ y18n = require("y18n")(/*some config*/);
var /*as macro*/ __ = y18n.__ , str = "some string";

Variable declaration statement will be deleted from the code.

Block Statement

You can run any nodejs code in a macro-block like a macro definition. The syntax of macro-block is a block statement in the global that has a comment block /*as macro*/ at the first and a block statement after that.

{/*as macro*/{
// This block will be run at build time
}}

You can do anything possible in nodejs in a macro-block. Variables that are defined in macro-block with var will also be a macro.

{/*as macro*/{
	var y18n = require("y18n");
	y18n = y18n(/*some config*/);
	var __ = y18n.__;
	console.log("this will be printed at build time");
	let x = 1; /* this is not macro because this is
	a local variable that is only accessible in this block */
	while(x<10){
		console.log(x);
		x++;
	}
}}

The macro-block will be deleted from the code.

Macro Expression

Macro aims to execute some expression and replace the result in the code. Any expression that is a sequence of object memberships, function calls, and tag templates with a macro name as the main object will be caught as a macro expression.

macroName;
macroName`this macro is a function`;
macroName.property.method(some, argument).tagTemplateMethod`some string`
	.anotherProperty;

The macro expression will be run in nodejs, and the result will be replaced in the code. For replacement we use json-scriptify module.

Example 1:

var /*as macro*/ m = { x: 1 };
{
	/*as macro*/ {
		m.str = "some string";
	}
}
let obj = m;
let obj = {
	x: 1,
	str: "some string"
};

Example 2:

{
	/*as macro*/ {
		var macroFunction = function computeAtCompileTime() {
			return "Result";
		};
	}
}
console.log(macroFunction());
let obj = macroFunction;
console.log("Result");

let obj = function computeAtCompileTime() {
  return "Result";
};

Example 3:

All occurrences of a macro name, except variable definition, will be caught as macro expression. This can bite you.

var /*as macro*/ m1 = "this is a macro",
	m2 = "this is another macro";
var f = function double(m1) {
	let m2 = 2 * m1;
	return m2;
};
var f = function double("this is a macro") {
  let m2 = 2 * "this is a macro";
  return "this is another macro";
};

Plugin Options

The plugin option is an object. You can access values in this object by info tool.

followScopes

followScopes is a boolean option that is false by default. If you set this option to true, you can use macro-block or define macro variables in any scope. Then, macros only available in that scope.

var /*as macro*/ m1 = "this is a macro",
	m2 = "this is another macro";
var f = function double(m1) {
	{
		/*as macro*/ {
			var localMacro = "this is local";
		}
	}
	let m2 = 2 * m1;
	var str = localMacro;
	return m2;
};
var globalString = localMacro;
var f = function double(m1) {
	let m2 = 2 * m1;
	var str = "this is local";
	return m2;
};
var globalString = localMacro;

But, why we don't set followScopes to true by default? Because people may have mistakes like bellow.

if (bool) {
	var /*as macro*/ m = "bool is true";
} else {
	var /*as macro*/ m = "bool is false";
}
var string = m;
if (bool) {
} else {
}
var string = "bool is false";

Regardless of variable bool, string is always "bool is false". Because both macro definitions are executed and the second definition overwrites first. Moreover, macros are executed at build time when bool has not been evaluated.

Tools

info

If you want to have some information about the file in which macros are defined and executed in, You can use info. You can import it from babel-plugin-as-macro/info in any module and use it. For example, you can have conf.js module that detects some config, like language, from the file path.

// conf.js
var info = require("babel-plugin-as-macro/info");
let someConfigurations = decideFrom(info.absolutePath);
module.exports = someConfigurations;

Then, you can use this module as macro in your code.

// main.js
import /*as macro*/ conf from "./conf.js";
/* do something with conf */

info is an object that will be filled by this plugin when the plugin start.

info = {
	options, // plugin options
	filename, // the name of file or "unknown" if has transformed from string
	root, // path to root of the project(directory of package.json)
	absoluteDir, // absolute path to the file directory
	relativeDir, // path to the directory relative to the root
	envName //process.env.BABEL_ENV || process.env.NODE_ENV || "development"
};

License

MIT