forked from gatsbyjs/gatsby
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change allows the user to provide a babelrc or a babel section in their package.json. It also allows gatsby to be used without having to specifiy a babelrc if the user doesn't need anything past what babel provides as a default. * It resolves all of the paths to become absolute such that the user can crawl upwards as in gatsbyjs#239. * It allows the custom usage of other babel plugins so that decorators work as in gatsbyjs#129.
- Loading branch information
Showing
10 changed files
with
206 additions
and
21 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import resolve from 'babel-core/lib/helpers/resolve' | ||
import fs from 'fs' | ||
import path from 'path' | ||
import json5 from 'json5' | ||
import startsWith from 'lodash/startswith' | ||
import invariant from 'invariant' | ||
|
||
const DEFAULT_BABEL_CONFIG = { | ||
presets: ['react', 'es2015', 'stage-0'], | ||
plugins: ['add-module-exports'], | ||
} | ||
|
||
/** | ||
* Uses babel-core helpers to resolve the plugin given it's name. It | ||
* resolves plugins in the following order: | ||
* | ||
* 1. Adding babel-type prefix and checking user's local modules | ||
* 2. Adding babel-type prefix and checking gatsby's modules | ||
* 3. Checking users's modules without prefix | ||
* 4. Checking gatsby's modules without prefix | ||
* | ||
*/ | ||
function resolvePlugin (pluginName, directory, type) { | ||
const plugin = resolve(`babel-${type}-${pluginName}`, directory) || | ||
resolve(`babel-${type}-${pluginName}`) || | ||
resolve(pluginName, directory) || | ||
resolve(pluginName) | ||
|
||
const name = !startsWith(pluginName, 'babel') ? pluginName : `babel-${type}-${pluginName}` | ||
const pluginInvariantMessage = ` | ||
You are trying to use a babel plugin which gatsby cannot find. You | ||
can install it using "npm install --save ${name}". | ||
You can use any of the gatsby provided plugins without installing them: | ||
- babel-plugin-add-module-exports | ||
- babel-preset-es2015 | ||
- babel-preset-react | ||
- babel-preset-stage-0 | ||
` | ||
|
||
invariant(plugin !== null, pluginInvariantMessage) | ||
return plugin | ||
} | ||
|
||
/** | ||
* Normalizes a babel config object to include only absolute paths. | ||
* This way babel-loader will correctly resolve babel plugins | ||
* regardless of where they are located. | ||
*/ | ||
function normalizeConfig (config, directory) { | ||
const normalizedConfig = { | ||
presets: [], | ||
plugins: [], | ||
} | ||
|
||
const presets = config.presets || [] | ||
presets.forEach(preset => { | ||
normalizedConfig.presets.push(resolvePlugin(preset, directory, 'preset')) | ||
}) | ||
|
||
const plugins = config.plugins || [] | ||
plugins.forEach(plugin => { | ||
normalizedConfig.plugins.push(resolvePlugin(plugin, directory, 'plugin')) | ||
}) | ||
|
||
return normalizedConfig | ||
} | ||
|
||
/** | ||
* Locates a babelrc in the gatsby site root directory. Parses it using | ||
* json5 (what babel uses). It throws an error if the users's babelrc is | ||
* not parseable. | ||
*/ | ||
function findBabelrc (directory) { | ||
try { | ||
const babelrc = fs.readFileSync(path.join(directory, '.babelrc'), 'utf-8') | ||
return json5.parse(babelrc) | ||
} catch (error) { | ||
if (error.code === 'ENOENT') { | ||
return null | ||
} else { | ||
throw error | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Reads the user's package.json and returns the babel section. It will | ||
* return undefined when the babel section does not exist. | ||
*/ | ||
function findBabelPackage (directory) { | ||
try { | ||
const packageJson = require(path.join(directory, 'package.json')) | ||
return packageJson.babel | ||
} catch (error) { | ||
if (error.code === 'MODULE_NOT_FOUND') { | ||
return null | ||
} else { | ||
throw error | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Returns a normalized babel config to use with babel-loader. All of | ||
* the paths will be absolute so that babel behaves as expected. | ||
*/ | ||
export default function babelConfig (program, stage) { | ||
const { directory } = program | ||
|
||
const babelrc = findBabelrc(directory) || | ||
findBabelPackage(directory) || | ||
DEFAULT_BABEL_CONFIG | ||
|
||
if (stage === 'develop') { | ||
babelrc.presets.unshift('react-hmre') | ||
} | ||
|
||
return normalizeConfig(babelrc, directory) | ||
} |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"babel": { | ||
"presets": ["react", "es2015", "stage-0"] | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
{ | ||
presets: ['react'] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
plugins: [ | ||
"transform-decorators-legacy", | ||
"transform-async-to-generator", | ||
"transform-es2015-modules-commonjs", | ||
"transform-export-extensions", | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"presets": ['react', 'es2015', 'stage-0'], | ||
"plugins": [ | ||
'transform-object-rest-spread' | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import test from 'ava' | ||
import path from 'path' | ||
import includes from 'lodash/includes' | ||
import babelConfig from '../../lib/utils/babel-config' | ||
|
||
function programStub (fixture) { | ||
const directory = path.resolve('..', 'fixtures', fixture) | ||
return { directory } | ||
} | ||
|
||
test('it returns a default babel config for babel-loader query', t => { | ||
const program = programStub('site-without-babelrc') | ||
const config = babelConfig(program) | ||
|
||
t.true(typeof config === 'object') | ||
t.truthy(config.presets.length) | ||
t.truthy(config.plugins.length) | ||
}) | ||
|
||
test('all plugins are absolute paths to avoid babel lookups', t => { | ||
const program = programStub('site-without-babelrc') | ||
const config = babelConfig(program) | ||
|
||
config.presets.forEach(preset => t.true(path.resolve(preset) === preset)) | ||
config.plugins.forEach(plugin => t.true(path.resolve(plugin) === plugin)) | ||
}) | ||
|
||
test('fixture can resolve plugins in gatsby directory (crawling up)', t => { | ||
const program = programStub('site-with-valid-babelrc') | ||
|
||
const config = babelConfig(program) | ||
t.truthy(config.presets.length) | ||
t.truthy(config.plugins.length) | ||
}) | ||
|
||
test('throws error when babelrc is not parseable', t => { | ||
const program = programStub('site-with-invalid-babelrc') | ||
|
||
t.throws(() => babelConfig(program)) | ||
}) | ||
|
||
test('can read babel from packagejson', t => { | ||
const program = programStub('site-with-valid-babelpackage') | ||
|
||
const config = babelConfig(program) | ||
t.truthy(config.presets.length) | ||
}) | ||
|
||
test('when in development has hmre', t => { | ||
const program = programStub('site-without-babelrc') | ||
const config = babelConfig(program, 'develop') | ||
|
||
// regex matches: babel followed by any amount of hyphen or word characters | ||
const presetNames = config.presets.map(p => p.match(/babel[-|\w]+/)[0]) | ||
t.true(includes(presetNames, 'babel-preset-react-hmre')) | ||
}) | ||
|
||
test('throws when a plugin is not available', t => { | ||
const program = programStub('site-with-unresolvable-babelrc') | ||
t.throws(() => babelConfig(program)) | ||
}) |