Created
October 25, 2016 11:51
-
-
Save kastermester/80f73b6a0687f4190ac739b78e8c4395 to your computer and use it in GitHub Desktop.
Webpack hot reloading of relay schemas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// loaction: loaders/es-preset.js | |
// This file is a combination of the following babel presets: | |
// es2015, react, stage-0. | |
// Difference is that the es2015-template-literals is taken out. Of the es2015 preset. | |
// This allows us to run relay transformations on the code afterwards (which will then transform any remaining template literals). | |
module.exports = preset({}); | |
function preset(context, opts) { | |
var moduleTypes = ["commonjs", "amd", "umd", "systemjs"]; | |
var loose = false; | |
var modules = "commonjs"; | |
var spec = false; | |
if (opts !== undefined) { | |
if (opts.loose !== undefined) loose = opts.loose; | |
if (opts.modules !== undefined) modules = opts.modules; | |
if (opts.spec !== undefined) spec = opts.spec; | |
} | |
if (typeof loose !== "boolean") throw new Error("Preset es2015 'loose' option must be a boolean."); | |
if (typeof spec !== "boolean") throw new Error("Preset es2015 'spec' option must be a boolean."); | |
if (modules !== false && moduleTypes.indexOf(modules) === -1) { | |
throw new Error("Preset es2015 'modules' option must be 'false' to indicate no modules\n" + | |
"or a module type which be be one of: 'commonjs' (default), 'amd', 'umd', 'systemjs'"); | |
} | |
return { | |
plugins: [ | |
require("babel-plugin-transform-es2015-literals"), | |
require("babel-plugin-transform-es2015-function-name"), | |
[require("babel-plugin-transform-es2015-arrow-functions"), { spec: spec }], | |
require("babel-plugin-transform-es2015-block-scoped-functions"), | |
[require("babel-plugin-transform-es2015-classes"), { loose: loose }], | |
require("babel-plugin-transform-es2015-object-super"), | |
require("babel-plugin-transform-es2015-shorthand-properties"), | |
require("babel-plugin-transform-es2015-duplicate-keys"), | |
[require("babel-plugin-transform-es2015-computed-properties"), { loose: loose }], | |
[require("babel-plugin-transform-es2015-for-of"), { loose: loose }], | |
require("babel-plugin-transform-es2015-sticky-regex"), | |
require("babel-plugin-transform-es2015-unicode-regex"), | |
require("babel-plugin-check-es2015-constants"), | |
[require("babel-plugin-transform-es2015-spread"), { loose: loose }], | |
require("babel-plugin-transform-es2015-parameters"), | |
[require("babel-plugin-transform-es2015-destructuring"), { loose: loose }], | |
require("babel-plugin-transform-es2015-block-scoping"), | |
require("babel-plugin-transform-es2015-typeof-symbol"), | |
modules === "commonjs" && [require("babel-plugin-transform-es2015-modules-commonjs"), { loose: loose }], | |
modules === "systemjs" && [require("babel-plugin-transform-es2015-modules-systemjs"), { loose: loose }], | |
modules === "amd" && [require("babel-plugin-transform-es2015-modules-amd"), { loose: loose }], | |
modules === "umd" && [require("babel-plugin-transform-es2015-modules-umd"), { loose: loose }], | |
[require("babel-plugin-transform-regenerator"), { async: false, asyncGenerators: false }], | |
// react | |
require("babel-plugin-transform-react-jsx"), | |
require("babel-plugin-transform-flow-strip-types"), | |
require("babel-plugin-syntax-flow"), | |
require("babel-plugin-syntax-jsx"), | |
// stage-3 | |
require("babel-plugin-syntax-trailing-function-commas"), | |
require("babel-plugin-transform-async-to-generator"), | |
require("babel-plugin-transform-exponentiation-operator"), | |
// stage-2 | |
require("babel-plugin-transform-class-properties"), | |
require("babel-plugin-transform-object-rest-spread"), | |
require("babel-plugin-transform-decorators"), | |
// stage-1 | |
require("babel-plugin-transform-class-constructor-call"), | |
require("babel-plugin-transform-export-extensions"), | |
// stage-0 | |
require("babel-plugin-transform-react-display-name"), | |
require("babel-plugin-syntax-trailing-function-commas"), | |
require("babel-plugin-transform-async-to-generator"), | |
require("babel-plugin-transform-exponentiation-operator"), | |
// filter out falsy values | |
].filter(Boolean) | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// location: loaders/relay-loader.js | |
var path = require('path'); | |
var fs = require('fs'); | |
var loaderUtils = require('loader-utils'); | |
var babel = require('babel-core'); | |
var babelRelayPluginConstructor = require('babel-relay-plugin'); | |
// Some of this code is shamelessly stolen from babel-loader | |
/** | |
* Error thrown by Babel formatted to conform to Webpack reporting. | |
*/ | |
function BabelLoaderError(name, message, codeFrame, hideStack, error) { | |
Error.call(this); | |
Error.captureStackTrace(this, BabelLoaderError); | |
this.name = 'BabelLoaderError'; | |
this.message = formatMessage(name, message, codeFrame); | |
this.hideStack = hideStack; | |
this.error = error; | |
} | |
BabelLoaderError.prototype = Object.create(Error.prototype); | |
BabelLoaderError.prototype.constructor = BabelLoaderError; | |
var STRIP_FILENAME_RE = /^[^:]+: /; | |
var formatMessage = function(name, message, codeFrame) { | |
return (name ? name + ': ' : '') + message + '\n\n' + codeFrame + '\n'; | |
}; | |
function transpile(source, options, callback) { | |
var result; | |
try { | |
result = babel.transform(source, options); | |
} catch (error) { | |
if (error.message && error.codeFrame) { | |
var message = error.message; | |
var name; | |
var hideStack; | |
if (error instanceof SyntaxError) { | |
message = message.replace(STRIP_FILENAME_RE, ''); | |
name = 'SyntaxError'; | |
hideStack = true; | |
} else if (error instanceof TypeError) { | |
message = message.replace(STRIP_FILENAME_RE, ''); | |
hideStack = true; | |
} | |
return callback( | |
new BabelLoaderError( | |
name, | |
message, | |
error.codeFrame, | |
hideStack, | |
error | |
) | |
); | |
} else { | |
callback(error); | |
} | |
} | |
var code = result.code; | |
var map = result.map; | |
if (map && (!map.sourcesContent || !map.sourcesContent.length)) { | |
map.sourcesContent = [source]; | |
} | |
callback(null, code, map); | |
} | |
module.exports = function(source, inputSourceMap) { | |
this.cacheable(); | |
var schemaFile = (this.options.relay && this.options.relay.schemaFile); | |
var callback = this.async(); | |
if (!schemaFile) { | |
return callback(new Error("Specify schema file in webpack config at relay.schemaFile. Must be absolute path")); | |
} | |
var webpackRemainingChain = loaderUtils.getRemainingRequest(this).split('!'); | |
var filename = webpackRemainingChain[webpackRemainingChain.length - 1]; | |
// I disable the ts-loaders behavior of depending on every single file this file actually depends on. | |
// To be completely 100% accurate this is what is needed. However it also slows down the entire system | |
// by quite a bit. (Isolated simple example cut a few seconds off hot-reload times, which I consider significant). | |
// This means if you change exports in a file (like remove a default export) other files won't be recompiled because | |
// of it. This might prove to be a bad idea, luckily it is easily removed. | |
this.clearDependencies(); | |
this.addDependency(filename); | |
if (this.options.relay && this.options.relay.omitFiles && this.options.relay.omitFiles.indexOf(filename) >= 0) { | |
return callback(null, source, inputSourceMap); | |
} | |
this.addDependency(schemaFile); | |
fs.readFile(schemaFile, 'utf-8', function(err, result) { | |
if (err) { | |
return callback(err); | |
} | |
var json; | |
try { | |
json = JSON.parse(result); | |
} catch (e) { | |
return callback(err); | |
} | |
var plugin = babelRelayPluginConstructor(json.data); | |
var babelOpts = { | |
plugins: [plugin, require('babel-plugin-transform-es2015-template-literals')], | |
filename: filename, | |
inputSourceMap: inputSourceMap, | |
sourceRoot: process.cwd(), | |
env: process.env.BABEL_ENV || process.env.NODE_ENV, | |
sourceMap: true, | |
}; | |
transpile(source, babelOpts, callback); | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var webpack = require('webpack'); | |
var path = require('path'); | |
var plugins = []; | |
var entries = [path.resolve(__dirname, 'site/index.tsx')]; | |
var env = 'development'; | |
if (process.env.NODE_ENV === 'development' || typeof(process.env.NODE_ENV) === 'undefined') { | |
plugins.push(new webpack.HotModuleReplacementPlugin()); | |
entries.unshift('webpack-hot-middleware/client'); | |
} | |
if (process.env.NODE_ENV === 'production') { | |
plugins.push(new webpack.NoErrorsPlugin()); | |
env = 'production'; | |
} | |
var relayLoader = path.resolve(__dirname, 'loaders', 'relay-loader'); | |
module.exports = { | |
entry: entries, | |
context: path.resolve(__dirname), | |
output: { | |
path: path.resolve(__dirname, 'build', 'assets'), | |
filename: 'app.js', | |
pathinfo: env === 'deveploment', | |
publicPath: '/assets/', | |
}, | |
resolve: { | |
extensions: ['', '.js', '.ts', '.tsx', '.json'], | |
}, | |
debug: true, | |
target: 'web', | |
devtool: env === 'development' ? 'cheap-module-source-map' : 'source-map', | |
plugins: plugins, | |
module: { | |
loaders: [ | |
{ | |
test: /\.ts(x?)$/, | |
loader: relayLoader + '!babel-loader!ts-loader', | |
exclude: /\/node_modules\//, | |
} | |
] | |
}, | |
HMR: env === 'development', | |
babel: { | |
"passPerPreset": false, | |
"sourceMap": true, | |
"presets": [ | |
{ | |
"plugins": [ | |
[ | |
'transform-runtime', | |
{ | |
helpers: true, | |
polyfill: true, | |
regenerator: true, | |
}, | |
], | |
], | |
}, | |
require('./loaders/preset-es'), | |
], | |
"env": { | |
"production": { | |
"presets": ["react-optimize"] | |
}, | |
// only enable it when process.env.NODE_ENV is 'development' or undefined | |
"development": { | |
"plugins": [ | |
[ | |
"react-transform", | |
{ | |
"transforms": [ | |
{ | |
"transform": "react-transform-hmr", | |
// if you use React Native, pass "react-native" instead: | |
"imports": ["react"], | |
// this is important for Webpack HMR: | |
"locals": ["module"] | |
} | |
] | |
}, | |
{ | |
// you can have many transforms, not just one | |
"transform": "react-transform-catch-errors", | |
"imports": ["react", "redbox-react"] | |
} | |
] | |
] | |
} | |
} | |
}, | |
relay: { | |
schemaFile: path.resolve(__dirname, 'data', 'schema.json'), | |
}, | |
ts: { | |
silent: true, | |
transpileOnly: true, | |
configFileName: path.resolve(__dirname, 'tsconfig.json'), | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment