diff --git a/packages/babel-plugin-flow-runtime/README.md b/packages/babel-plugin-flow-runtime/README.md index 7fb0c4c..b0dbeaf 100644 --- a/packages/babel-plugin-flow-runtime/README.md +++ b/packages/babel-plugin-flow-runtime/README.md @@ -61,6 +61,7 @@ The plugin supports the following options: - `assert` - Boolean, indicates whether types should be asserted at runtime. Defaults to `true` if `process.env.NODE_ENV === 'development'`, otherwise `false`. - `annotate` - Boolean, indicates whether object or function values that have type annotations should be decorated with those types at runtime. Defaults to `true`. - `libraryName` - String, indicates which runtime to use. Defaults to `flow-runtime` +- `libraryImport` - String, either `import` or `require`, indicates how the runtime should be loaded. Defaults to `import`. If `assert` is `true`, the following code: diff --git a/packages/babel-plugin-flow-runtime/src/ConversionContext.js b/packages/babel-plugin-flow-runtime/src/ConversionContext.js index d81d33b..ee986aa 100644 --- a/packages/babel-plugin-flow-runtime/src/ConversionContext.js +++ b/packages/babel-plugin-flow-runtime/src/ConversionContext.js @@ -28,6 +28,7 @@ export default class ConversionContext { libraryName: string = 'flow-runtime'; libraryId: string = 't'; + libraryImport: string = 'import'; shouldImport: boolean = true; shouldAssert: boolean = true; shouldWarn: boolean = false; diff --git a/packages/babel-plugin-flow-runtime/src/__tests__/transform.test.js b/packages/babel-plugin-flow-runtime/src/__tests__/transform.test.js index 7a8ae75..6804c0e 100644 --- a/packages/babel-plugin-flow-runtime/src/__tests__/transform.test.js +++ b/packages/babel-plugin-flow-runtime/src/__tests__/transform.test.js @@ -25,4 +25,55 @@ describe('transform', () => { }); } } + + it('should support requiring the runtime library', () => { + testTransform(` + type User = { + id: number; + name: string; + }; + `, {assert: true, annotate: false, libraryImport: 'require'}, ` + const t = require("flow-runtime"); + + const User = t.type("User", t.object( + t.property("id", t.number()), + t.property("name", t.string()) + )); + `); + }); + + it('should support requiring a custom runtime library', () => { + testTransform(` + type User = { + id: number; + }; + `, { + assert: true, + annotate: false, + libraryName: './custom-flow-runtime', + libraryImport: 'require' + }, ` + const t = require("./custom-flow-runtime"); + + const User = t.type("User", t.object( + t.property("id", t.number()) + )); + `); + }); + + it('should reuse an existing required runtime binding', () => { + testTransform(` + const rt = require("flow-runtime"); + + type User = { + id: number; + }; + `, {assert: true, annotate: false, libraryImport: 'require'}, ` + const rt = require("flow-runtime"); + + const User = rt.type("User", rt.object( + rt.property("id", rt.number()) + )); + `); + }); }); diff --git a/packages/babel-plugin-flow-runtime/src/attachImport.js b/packages/babel-plugin-flow-runtime/src/attachImport.js index 6eef087..1d4b33e 100644 --- a/packages/babel-plugin-flow-runtime/src/attachImport.js +++ b/packages/babel-plugin-flow-runtime/src/attachImport.js @@ -6,12 +6,21 @@ import type {NodePath} from '@babel/traverse'; export default function attachImport (context: ConversionContext, program: NodePath) { - const importDeclaration = t.importDeclaration( - [t.importDefaultSpecifier(t.identifier(context.libraryId))], - t.stringLiteral(context.libraryName) - ); - - importDeclaration.importKind = 'value'; + const importDeclaration = context.libraryImport === 'require' + ? t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(context.libraryId), + t.callExpression(t.identifier('require'), [t.stringLiteral(context.libraryName)]) + ) + ]) + : t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(context.libraryId))], + t.stringLiteral(context.libraryName) + ); + + if (t.isImportDeclaration(importDeclaration)) { + importDeclaration.importKind = 'value'; + } context.shouldImport = false; diff --git a/packages/babel-plugin-flow-runtime/src/createConversionContext.js b/packages/babel-plugin-flow-runtime/src/createConversionContext.js index ae9b8d7..25ac4c1 100644 --- a/packages/babel-plugin-flow-runtime/src/createConversionContext.js +++ b/packages/babel-plugin-flow-runtime/src/createConversionContext.js @@ -12,6 +12,7 @@ export type Options = { // deprecated decorate?: boolean; libraryName?: string, + libraryImport?: string, optInOnly?: boolean; }; @@ -23,6 +24,10 @@ export default function createConversionContext (options: Options): ConversionCo context.libraryName = options.libraryName; } + if (options.libraryImport) { + context.libraryImport = options.libraryImport; + } + context.optInOnly = options.optInOnly ? true : false; context.shouldAssert = options.assert === undefined diff --git a/packages/babel-plugin-flow-runtime/src/firstPassVisitors.js b/packages/babel-plugin-flow-runtime/src/firstPassVisitors.js index ef1d6ac..2b92dc8 100644 --- a/packages/babel-plugin-flow-runtime/src/firstPassVisitors.js +++ b/packages/babel-plugin-flow-runtime/src/firstPassVisitors.js @@ -101,6 +101,20 @@ export default function firstPassVisitors (context: ConversionContext): Object { context.lastImportDeclaration = path; }, VariableDeclarator (path: NodePath) { + const init = path.get('init'); + const idPath = path.get('id'); + + if ( + context.libraryImport === 'require' && + idPath.isIdentifier() && + init.isCallExpression() && + init.get('callee').isIdentifier({name: 'require'}) && + init.get('arguments.0').isStringLiteral({value: context.libraryName}) + ) { + context.shouldImport = false; + context.libraryId = idPath.node.name; + } + for (const id of findIdentifiers(path.get('id'))) { const {name} = id.node; context.defineValue(name, path);