Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This is a [lerna](https://github.com/lerna/lerna) powered mono-repo, composed of

- [flow-runtime](./packages/flow-runtime): The core runtime type system.
- [babel-plugin-flow-runtime](./packages/babel-plugin-flow-runtime): A babel plugin which transforms Flow type annotations into `flow-runtime` invocations.
- [flow-runtime-loader](./packages/flow-runtime-loader): A webpack loader which transforms Flow type files into `flow-runtime` modules.
- [flow-runtime-validators](./packages/flow-runtime-validators): A collection of common validators for use with flow-runtime.
- [flow-config-parser](./packages/flow-config-parser): Parses flow configuration files.
- [flow-runtime-mobx](./packages/flow-runtime-mobx): Adds mobx support to flow-runtime.
Expand Down
16 changes: 16 additions & 0 deletions packages/flow-runtime-loader/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": 4
},
"exclude": [
"transform-regenerator"
]
}],
"@babel/preset-flow"
],
"plugins": [
"@babel/plugin-proposal-object-rest-spread"
]
}
60 changes: 60 additions & 0 deletions packages/flow-runtime-loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# flow-runtime-loader

A webpack loader for importing Flow type declarations as `flow-runtime` runtime validators.

This is useful when a package ships Flow definition files next to its source, for example `*.js.flow` files, and you want to validate values against those definitions at runtime.

## Installation

```sh
npm install --save-dev flow-runtime-loader
```

## Inline Usage

```js
import { RawDraftContentState } from 'flow-runtime-loader!draft-js/lib/RawDraftContentState.js.flow';

RawDraftContentState.assert(value);
```

## Webpack Rule

```js
module.exports = {
resolve: {
extensions: ['.js.flow', '.js', '.json']
},
module: {
rules: [
{
test: /\.js\.flow$/,
use: [
{
loader: 'flow-runtime-loader',
options: {
annotate: false
}
}
]
}
]
}
};
```

Loader options are passed to `babel-plugin-flow-runtime`. Use the `babel` option for Babel transform options:

```js
{
loader: 'flow-runtime-loader',
options: {
libraryName: './custom-flow-runtime',
babel: {
sourceMaps: true
}
}
}
```

By default the loader disables project-level Babel config so that importing a dependency's `*.js.flow` file does not accidentally inherit the consuming app's transforms. Pass `babel.babelrc` or `babel.configFile` to opt back in.
3 changes: 3 additions & 0 deletions packages/flow-runtime-loader/flow-runtime-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var loader = require('./lib');

module.exports = loader.default || loader;
43 changes: 43 additions & 0 deletions packages/flow-runtime-loader/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "flow-runtime-loader",
"homepage": "https://codemix.github.io/flow-runtime",
"repository": "https://github.com/codemix/flow-runtime.git",
"version": "0.20.0",
"description": "Webpack loader for importing Flow type declarations as flow-runtime modules.",
"main": "flow-runtime-loader.js",
"scripts": {
"lint": "eslint ./src",
"lint-fix": "eslint --fix ./src",
"prepublishOnly": "npm run lint && npm run test && npm run build",
"build": "rimraf ./lib && babel -d ./lib ./src",
"test": "mocha --timeout 10000",
"watch": "mocha --timeout 10000 --watch"
},
"author": "",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.12.0",
"@babel/preset-flow": "^7.12.0",
"babel-plugin-flow-runtime": "^0.20.0"
},
"devDependencies": {
"@babel/cli": "^7.12.0",
"@babel/plugin-proposal-object-rest-spread": "^7.12.0",
"@babel/polyfill": "^7.12.0",
"@babel/preset-env": "^7.12.0",
"@babel/register": "^7.12.0",
"babel-loader": "^8.1.0",
"eslint": "^5.0.0",
"eslint-plugin-babel": "^5.0.0",
"eslint-plugin-flowtype": "^3.0.0",
"eslint-plugin-import": "^2.0.1",
"eslint-plugin-jsx-a11y": "^6.0.0",
"eslint-plugin-react": "^7.0.0",
"mocha": "^5.0.0",
"rimraf": "^2.6.3",
"webpack": "^4.43.0"
},
"eslintConfig": {
"extends": "../babel-plugin-flow-runtime/config/eslint.js"
}
}
129 changes: 129 additions & 0 deletions packages/flow-runtime-loader/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/* @flow */

import {transformSync} from '@babel/core';
import flowRuntimePlugin from 'babel-plugin-flow-runtime';

type LoaderContext = {
async?: () => (error: ?Error, result?: string, sourceMap?: Object) => void;
cacheable?: () => void;
getOptions?: () => Object;
query?: string | Object;
resourcePath?: string;
sourceMap?: boolean;
};

type LoaderOptions = {
babel?: Object;
};

export default function flowRuntimeLoader (source: string | Buffer, inputSourceMap?: Object) {
if (this.cacheable) {
this.cacheable();
}

const callback = this.async && this.async();
const input = Buffer.isBuffer(source) ? source.toString() : source;

try {
const options = getOptions(this);
const babelOptions = options.babel || {};
const flowRuntimeOptions = getFlowRuntimeOptions(options);
const result = transformSync(input, {
...babelOptions,
babelrc: babelOptions.babelrc === undefined ? false : babelOptions.babelrc,
configFile: babelOptions.configFile === undefined ? false : babelOptions.configFile,
filename: babelOptions.filename || this.resourcePath,
inputSourceMap,
plugins: getPlugins(babelOptions.plugins, flowRuntimeOptions),
presets: getPresets(babelOptions.presets),
sourceMaps: babelOptions.sourceMaps === undefined ? this.sourceMap || Boolean(inputSourceMap) : babelOptions.sourceMaps
});

const code = result && result.code ? result.code : input;
const map = result && result.map ? result.map : inputSourceMap;

if (callback) {
callback(null, code, map);
return;
}
return code;
}
catch (error) {
if (callback) {
callback(error);
return;
}
throw error;
}
}

function getOptions (context: LoaderContext): LoaderOptions {
if (typeof context.getOptions === 'function') {
return context.getOptions() || {};
}

const {query} = context;
if (!query) {
return {};
}
if (typeof query === 'object') {
return query;
}
return parseQuery(query);
}

function getFlowRuntimeOptions (options: LoaderOptions): Object {
const pluginOptions = {...options};
delete pluginOptions.babel;
return pluginOptions;
}

function getPlugins (plugins?: Array<any>, flowRuntimeOptions: Object): Array<any> {
const result = plugins ? plugins.slice() : [];
result.push([flowRuntimePlugin, flowRuntimeOptions]);
return result;
}

function getPresets (presets?: Array<any>): Array<any> {
const result = presets ? presets.slice() : [];
result.push(require.resolve('@babel/preset-flow'));
return result;
}

function parseQuery (query: string): Object {
const raw = query.charAt(0) === '?' ? query.slice(1) : query;
if (!raw) {
return {};
}
if (raw.charAt(0) === '{') {
return JSON.parse(raw);
}

return raw.split('&').reduce((options, segment) => {
if (!segment) {
return options;
}

const pair = segment.split('=');
const key = decodeURIComponent(pair[0]);
const value = pair.length > 1 ? decodeURIComponent(pair.slice(1).join('=')) : undefined;
options[key] = coerceQueryValue(value);
return options;
}, {});
}

function coerceQueryValue (value?: string): any {
if (value === undefined || value === '') {
return true;
}
if (value === 'true') {
return true;
}
if (value === 'false') {
return false;
}
if (value === 'null') {
return null;
}
return value;
}
Loading