// 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); }); }