From 93f8fa67bf7cf914a89ff56608fa9785e215436d Mon Sep 17 00:00:00 2001 From: Ognian Tschakalov Date: Mon, 30 May 2016 19:24:06 +0200 Subject: [PATCH] feat(config): add metadataSubscribes option - add metadataSubscribers option as an array of context functions to call - pass metadata to metadataSubscribers functions - add section about cacheDirectory ENOENT error to README --- README.md | 52 +++++++++++++ package.json | 4 + src/index.js | 19 +++++ test/fixtures/metadata.js | 15 ++++ test/fixtures/metadataErr.js | 16 ++++ test/metadata.test.js | 137 +++++++++++++++++++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 test/fixtures/metadata.js create mode 100644 test/fixtures/metadataErr.js create mode 100644 test/metadata.test.js diff --git a/README.md b/README.md index 36ea8dcb..03807125 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,58 @@ require('babel-runtime/core-js/promise').default = require('bluebird'); require('./app'); ``` +### using `cacheDirectory` fails with ENOENT Error + +If using cacheDirectory results in an error similar to the following: + +``` +ERROR in ./frontend/src/main.js +Module build failed: Error: ENOENT, open 'true/350c59cae6b7bce3bb58c8240147581bfdc9cccc.json.gzip' + @ multi app +``` +(notice the `true/` in the filepath) + +That means that most likely, you're not setting the options correctly, and you're doing something similar to: + +```javascript +loaders: [ + { + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + loader: 'babel?cacheDirectory=true' + } +] +``` + +That's not the correct way of setting boolean values. You should do instead: + +```javascript +loaders: [ + { + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + loader: 'babel?cacheDirectory' + } +] +``` + +or use the [query](https://webpack.github.io/docs/using-loaders.html#query-parameters) property: + +```javascript +loaders: [ + // the optional 'runtime' transformer tells babel to require the runtime + // instead of inlining it. + { + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + loader: 'babel', + query: { + cacheDirectory: true + } + } +] +``` + ### The node API for `babel` has been moved to `babel-core`. If you receive this message it means that you have the npm package `babel` installed and use the short notation of the loader in the webpack config (which is not valid anymore as of webpack 2.x): diff --git a/package.json b/package.json index ae486626..4ac9b629 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "babel-core": "^6.0.0", "babel-eslint": "^7.1.0", "babel-plugin-istanbul": "^2.0.3", + "babel-plugin-react-intl": "2.1.3", "babel-preset-es2015": "^6.0.0", "babel-preset-latest": "^6.16.0", "codecov": "^1.0.1", @@ -31,6 +32,9 @@ "eslint-plugin-babel": "^3.3.0", "eslint-plugin-flowtype": "^2.25.0", "nyc": "^8.3.2", + "react": "^15.1.0", + "react-intl":"^2.1.2", + "react-intl-webpack-plugin":"0.0.3", "rimraf": "^2.4.3", "webpack": "2.1.0-beta.22" }, diff --git a/src/index.js b/src/index.js index 3c5caf81..0a3483ad 100644 --- a/src/index.js +++ b/src/index.js @@ -56,6 +56,7 @@ const transpile = function(source, options) { } const code = result.code; const map = result.map; + const metadata = result.metadata; if (map && (!map.sourcesContent || !map.sourcesContent.length)) { map.sourcesContent = [source]; @@ -64,9 +65,17 @@ const transpile = function(source, options) { return { code: code, map: map, + metadata: metadata, }; }; + +function passMetadata(s, context, metadata) { + if (context[s]) { + context[s](metadata); + } +} + module.exports = function(source, inputSourceMap) { let result = {}; @@ -79,6 +88,7 @@ module.exports = function(source, inputSourceMap) { const loaderOptions = loaderUtils.parseQuery(this.query); const userOptions = assign({}, globalOptions, loaderOptions); const defaultOptions = { + metadataSubscribers: [], inputSourceMap: inputSourceMap, sourceRoot: process.cwd(), filename: filename, @@ -107,11 +117,14 @@ module.exports = function(source, inputSourceMap) { const cacheDirectory = options.cacheDirectory; const cacheIdentifier = options.cacheIdentifier; + const metadataSubscribers = options.metadataSubscribers; delete options.cacheDirectory; delete options.cacheIdentifier; + delete options.metadataSubscribers; this.cacheable(); + const context = this; if (cacheDirectory) { const callback = this.async(); @@ -123,10 +136,16 @@ module.exports = function(source, inputSourceMap) { transform: transpile, }, function(err, result) { if (err) { return callback(err); } + metadataSubscribers.map(function(s) { + passMetadata(s, context, result.metadata); + }); return callback(null, result.code, result.map); }); } result = transpile(source, options); + metadataSubscribers.map(function(s) { + passMetadata(s, context, result.metadata); + }); this.callback(null, result.code, result.map); }; diff --git a/test/fixtures/metadata.js b/test/fixtures/metadata.js new file mode 100644 index 00000000..bc093acb --- /dev/null +++ b/test/fixtures/metadata.js @@ -0,0 +1,15 @@ +import {defineMessages} from 'react-intl'; +class App { + constructor(arg='test') { + var m = defineMessages({ + greeting: { + id: 'greetingId', + defaultMessage: 'Hello World!' + }, + }); + + this.result = arg; + } +} + +export default App; diff --git a/test/fixtures/metadataErr.js b/test/fixtures/metadataErr.js new file mode 100644 index 00000000..1b05d3e0 --- /dev/null +++ b/test/fixtures/metadataErr.js @@ -0,0 +1,16 @@ +import {defineMessages} from 'react-intl'; +class App { + constructor(arg='test') { + var m = defineMessages({ + greeting: { + id: 'greetingId', + defaultMessage: 'Hello World!' + }, + }); + + bla bla + this.result = arg; + } +} + +export default App; diff --git a/test/metadata.test.js b/test/metadata.test.js new file mode 100644 index 00000000..93c9584a --- /dev/null +++ b/test/metadata.test.js @@ -0,0 +1,137 @@ +"use strict"; + +import fs from "fs"; +import path from "path"; +import assign from "object-assign"; +import test from "ava"; +import mkdirp from "mkdirp"; +import rimraf from "rimraf"; +import webpack from "webpack"; +import ReactIntlPlugin from "react-intl-webpack-plugin"; + +const cacheDir = path.resolve(__dirname, "./output/cache/cachefiles"); +const outputDir = path.resolve(__dirname, "./output/metadata"); +const babelLoader = path.resolve(__dirname, "../"); +const globalConfig = { + entry: path.join(__dirname, "fixtures/metadata.js"), + output: { + path: outputDir, + filename: "[id].metadata.js", + }, + plugins: [new ReactIntlPlugin(),], + module: { + loaders: [ + { + test: /\.jsx?/, + loader: babelLoader, + query: { + metadataSubscribers: [ReactIntlPlugin.metadataContextFunctionName], + plugins: [ + ["react-intl", {enforceDescriptions: false,},], + ], + presets: [], + }, + exclude: /node_modules/, + }, + ], + }, +}; + +// Create a separate directory for each test so that the tests +// can run in parallel +test.cb.beforeEach((t) => { + const directory = path.join(outputDir, t.title.replace(/ /g, "_")); + t.context.directory = directory; + rimraf(directory, (err) => { + if (err) return t.end(err); + mkdirp(directory, t.end); + }); +}); + +test.cb.afterEach((t) => rimraf(t.context.directory, t.end)); + +test.cb("should pass metadata code snippet", function(t) { + const config = assign({}, globalConfig, { + }); + + webpack(config, function(err) { + t.is(err, null); + + fs.readdir(outputDir, function(err) { + t.is(err, null); + fs.readFile(path.resolve(outputDir, "reactIntlMessages.json"), + function(err, data) { + const text = data.toString(); + t.is(err, null); + const jsonText = JSON.parse(text); + t.is(jsonText.length, 1); + t.is(jsonText[0].id, "greetingId"); + t.is(jsonText[0].defaultMessage, "Hello World!"); + + t.end(); + }); + }); + }); +}); + +test.cb("should not throw error ", function(t) { + const config = assign({}, globalConfig, {}); + + webpack(config, function(err, stats) { + t.is(stats.compilation.errors.length, 0); + t.end(); + }); +}); + +test.cb("should throw error ", function(t) { + const config = assign({}, globalConfig, { + entry: "./test/fixtures/metadataErr.js", + }); + + webpack(config, function(err, stats) { + t.true(stats.compilation.errors.length > 0); + t.end(); + }); +}); + +test.cb("should pass metadata code snippet (cache version)", function(t) { + const config = assign({}, globalConfig, { + module: { + loaders: [ + { + test: /\.jsx?/, + loader: babelLoader, + query: { + metadataSubscribers: + [ReactIntlPlugin.metadataContextFunctionName], + plugins: [ + ["react-intl", {enforceDescriptions: false,},], + ], + cacheDirectory: cacheDir, + presets: [], + }, + exclude: /node_modules/, + }, + ], + }, + }); + + webpack(config, function(err) { + t.is(err, null); + + fs.readdir(outputDir, function(err) { + t.is(err, null); + fs.readFile(path.resolve(outputDir, "reactIntlMessages.json"), + function(err, data) { + const text = data.toString(); + t.is(err, null); + const jsonText = JSON.parse(text); + t.is(jsonText.length, 1); + t.is(jsonText[0].id, "greetingId"); + t.is(jsonText[0].defaultMessage, "Hello World!"); + + t.end(); + }); + }); + }); +});