This plugin is acts as a preprocessor: it takes a mutable class and generates a record with the following properties:
- immutable
- type-safe
update()
to do partial updatestoMap()
to export an untyped Map
It's based Facebook's immutable-js and Flow.
If you run the plugin on the following input
/* @flow */
import Record from './record';
@Record()
class MyBand {
name: string;
members: string[];
active: boolean = true;
}
it will create a file similar to this (details have been omitted):
/* @flow */
import Record from '../decorator';
import { List, Map } from 'immutable';
@Record()
class MyBand extends Record.Base {
constructor(init: MyBandInit) { /* ... */ }
get name(): string { /* ... */ }
get members(): List<string> { /* ... */ }
get active(): boolean { /* ... */ }
update(update: MyBandUpdate): MyBand { /* ... */ }
toMap(): Map<string, any> { /* ... */ }
}
type MyBandInit = { name: string; members: List<string>; active?: boolean };
type MyBandUpdate = { name?: string; members?: List<string>; active?: boolean };
The Flow type checker will prevent:
- missing fields on initalisation
- wrong type for a field
- data for undefined properties
This shows how you could use it:
const band = new MyBand({
name: 'The Be Sharps',
members: List(['Homer', 'Apu', 'Seymour', 'Clancy'])
});
console.log(band.name); // prints 'The Be Sharps'
const newBand = band.update({ members: band.members.set(3, 'Barney') });
console.log(newBand.members.get(3)); // prints 'Barney'
(1) Install the plugin:
npm install babel-plugin-immutable-record --save-dev
(2) Add the additional step to your build process, for example in Gulp 4:
var rename = require('gulp-rename');
gulp.task('generate', function () {
return gulp.src('src/**/*.t.js')
.pipe(babel({
"plugins": [
"babel-plugin-syntax-flow",
'babel-plugin-syntax-decorators',
'babel-plugin-syntax-class-properties',
"babel-plugin-immutable-record"
]
}))
.pipe(rename((path) => { path.basename = path.basename.replace('.t', '') }))
.pipe(gulp.dest('src'));
});
gulp.task('watch', function () {
gulp.watch(['src/**/*.t.js'], gulp.series('generate'));
// ...
});
NOTE: This is important. The output of the plugin
should be part of the source code, not the transpiled build.
Also, your regular watch task should ignore *.t.js
and
NOT trigger the generate
task - otherwise you created an endless loop.
(3) Finally, you need to define the decorator in your source code:
/* @flow */
function Record(): Function {
return () => {};
}
class Base {}
Record.Base = Base;
export default Record;
You can pick a custom name for the decorator (default is Record
):
{
"plugins": [
...
["babel-plugin-immutable-record", {
"decorator": "ImmutableContainer"
}]
]
}
Obviously, you'll then need to adapt the decorator's source file accordingly.
To insert a custom header at the top of the file (e.g. to disable ESLint):
{
"plugins": [
...
["babel-plugin-immutable-record", {
"header": "my comment"
}]
]
}