@babel/plugin-proposal-record-and-tuple is a Babel plugin that transforms the proposed Record and Tuple syntax into JavaScript code that can run in current environments. Records and Tuples are immutable data structures proposed for JavaScript that provide value equality semantics and deep immutability guarantees. The plugin enables developers to use the new syntax #{...} for records and #[...] for tuples in their code, transforming them into polyfill implementations that maintain the expected behavior.
npm install --save-dev @babel/plugin-proposal-record-and-tuple// ES modules
import recordTuplePlugin from "@babel/plugin-proposal-record-and-tuple";
// CommonJS
const recordTuplePlugin = require("@babel/plugin-proposal-record-and-tuple");
// For type definitions (TypeScript)
import type { Options } from "@babel/plugin-proposal-record-and-tuple";
import type { types as t, NodePath } from "@babel/core";
import type { declare } from "@babel/helper-plugin-utils";// Babel configuration (.babelrc.js or babel.config.js)
module.exports = {
plugins: [
["@babel/plugin-proposal-record-and-tuple", {
syntaxType: "hash",
importPolyfill: true
}]
]
};
// Input code with Record and Tuple syntax
const record = #{
name: "Alice",
age: 30,
hobbies: #["reading", "gaming"]
};
const tuple = #[1, 2, 3, #{x: 4, y: 5}];
// Transformed output (when importPolyfill: true)
import { Record as _Record, Tuple as _Tuple } from "@bloomberg/record-tuple-polyfill";
const record = _Record({
name: "Alice",
age: 30,
hobbies: _Tuple("reading", "gaming")
});
const tuple = _Tuple(1, 2, 3, _Record({x: 4, y: 5}));The main export is a Babel plugin function that transforms Record and Tuple syntax.
import { declare } from "@babel/helper-plugin-utils";
import syntaxRecordAndTuple from "@babel/plugin-syntax-record-and-tuple";
import type { Options as SyntaxOptions } from "@babel/plugin-syntax-record-and-tuple";
import { types as t, type NodePath } from "@babel/core";
/**
* Babel plugin factory function for transforming Record and Tuple syntax
* @param api - Babel API object with version assertion
* @param options - Plugin configuration options
* @returns Babel plugin object with visitor methods
*/
const recordTuplePlugin = declare<State>((api, options: Options) => {
api.assertVersion(7);
return {
name: "proposal-record-and-tuple",
inherits: syntaxRecordAndTuple,
visitor: {
Program(path: NodePath<t.Program>, state: State): void;
RecordExpression(path: NodePath<t.RecordExpression>, state: State): void;
TupleExpression(path: NodePath<t.TupleExpression>, state: State): void;
}
};
});
export default recordTuplePlugin;Configuration options for customizing the plugin behavior.
import type { Options as SyntaxOptions } from "@babel/plugin-syntax-record-and-tuple";
interface SyntaxOptions {
/**
* Which syntax variant to use for Record and Tuple literals
* "hash" uses #{} and #[] syntax
* "bar" uses {||} and [||] syntax (deprecated in Babel 8)
*/
syntaxType: "hash" | "bar";
}
interface Options extends SyntaxOptions {
/**
* Name of the polyfill module to import Record/Tuple from
* @default "@bloomberg/record-tuple-polyfill"
*/
polyfillModuleName?: string;
/**
* Whether to automatically import polyfill functions
* @default !!options.polyfillModuleName
*/
importPolyfill?: boolean;
}
export type { Options, SyntaxOptions };Transforms record literals into Record() constructor calls.
Input:
const person = #{
name: "John",
age: 25,
address: #{
street: "123 Main St",
city: "Boston"
}
};Output (without polyfill import):
const person = Record({
name: "John",
age: 25,
address: Record({
street: "123 Main St",
city: "Boston"
})
});Output (with polyfill import):
import { Record as _Record } from "@bloomberg/record-tuple-polyfill";
const person = _Record({
name: "John",
age: 25,
address: _Record({
street: "123 Main St",
city: "Boston"
})
});Transforms tuple literals into Tuple() constructor calls.
Input:
const coordinates = #[10, 20];
const nested = #[1, #[2, 3], 4];
const empty = #[];Output (without polyfill import):
const coordinates = Tuple(10, 20);
const nested = Tuple(1, Tuple(2, 3), 4);
const empty = Tuple();Output (with polyfill import):
import { Tuple as _Tuple } from "@bloomberg/record-tuple-polyfill";
const coordinates = _Tuple(10, 20);
const nested = _Tuple(1, _Tuple(2, 3), 4);
const empty = _Tuple();The plugin supports two syntax variants (though "bar" is deprecated):
// Records use #{}
const record = #{a: 1, b: 2};
// Tuples use #[]
const tuple = #[1, 2, 3];// Records use {||}
const record = {| a: 1, b: 2 |};
// Tuples use [||]
const tuple = [| 1, 2, 3 |];When importPolyfill is enabled, the plugin automatically:
Module Import Caching:
import statements (ES modules) and require calls (CommonJS)import { types as t, type NodePath } from "@babel/core";
/**
* Internal state object passed between visitor methods
*/
type State = {
programPath: NodePath<t.Program>;
};
/**
* Cache for storing import binding names by cache key
* Format: "Record:true" or "Tuple:false" (name:isModule)
*/
type Cache = Map<string, string>;
/**
* WeakMap for caching imports per program AST node
* Ensures single imports per file and avoids duplicates
*/
type ImportCache = WeakMap<t.Program, Cache>;// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-proposal-record-and-tuple", {
syntaxType: "hash"
}]
]
};// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-proposal-record-and-tuple", {
syntaxType: "hash",
polyfillModuleName: "my-custom-polyfill",
importPolyfill: true
}]
]
};// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-proposal-record-and-tuple", {
syntaxType: "hash",
importPolyfill: false
}]
]
};The plugin includes validation and error handling for various scenarios:
In Babel 8, the syntaxType option is no longer supported and will throw an error:
// This will throw an error in Babel 8
{
plugins: [
["@babel/plugin-proposal-record-and-tuple", {
syntaxType: "hash" // ❌ Error in Babel 8
}]
]
}
// Correct usage in Babel 8
{
plugins: [
"@babel/plugin-proposal-record-and-tuple" // ✅ No options needed
]
}The plugin validates all options using @babel/helper-validator-option:
polyfillModuleName must be a string if providedimportPolyfill must be a boolean if providedsyntaxType must be either "hash" or "bar" if provided// Internal error when Program node cannot be found
if (!programPath) {
throw new Error("Internal error: unable to find the Program node.");
}declare function for plugin creation