mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Use custom Babel loader to avoid using separate Babel copies for loader and loader options (#4417)
This resolves the > .value is not a valid Plugin property error showing up for people in https://github.com/zeit/next.js/issues/4227 cc @timneutkens
This commit is contained in:
parent
f620b8f455
commit
2495316235
|
@ -65,7 +65,7 @@
|
||||||
"@babel/template": "7.0.0-beta.42",
|
"@babel/template": "7.0.0-beta.42",
|
||||||
"ansi-html": "0.0.7",
|
"ansi-html": "0.0.7",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
"babel-loader": "8.0.0-beta.2",
|
"babel-loader": "8.0.0-beta.3",
|
||||||
"babel-plugin-react-require": "3.0.0",
|
"babel-plugin-react-require": "3.0.0",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "0.4.13",
|
"babel-plugin-transform-react-remove-prop-types": "0.4.13",
|
||||||
"case-sensitive-paths-webpack-plugin": "2.1.1",
|
"case-sensitive-paths-webpack-plugin": "2.1.1",
|
||||||
|
|
48
server/build/loaders/next-babel-loader.js
Normal file
48
server/build/loaders/next-babel-loader.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import babelLoader from 'babel-loader'
|
||||||
|
|
||||||
|
module.exports = babelLoader.custom(babel => {
|
||||||
|
const presetItem = babel.createConfigItem(require('../babel/preset'), {type: 'preset'})
|
||||||
|
const hotLoaderItem = babel.createConfigItem(require('react-hot-loader/babel'), {type: 'plugin'})
|
||||||
|
const reactJsxSourceItem = babel.createConfigItem(require('@babel/plugin-transform-react-jsx-source'), {type: 'plugin'})
|
||||||
|
|
||||||
|
const configs = new Set()
|
||||||
|
|
||||||
|
return {
|
||||||
|
customOptions (opts) {
|
||||||
|
const custom = {
|
||||||
|
isServer: opts.isServer,
|
||||||
|
dev: opts.dev
|
||||||
|
}
|
||||||
|
const loader = Object.assign({
|
||||||
|
cacheDirectory: true
|
||||||
|
}, opts)
|
||||||
|
delete loader.isServer
|
||||||
|
delete loader.dev
|
||||||
|
|
||||||
|
return { loader, custom }
|
||||||
|
},
|
||||||
|
config (cfg, {customOptions: {isServer, dev}}) {
|
||||||
|
const options = Object.assign({}, cfg.options)
|
||||||
|
if (cfg.hasFilesystemConfig()) {
|
||||||
|
for (const file of [cfg.babelrc, cfg.config]) {
|
||||||
|
if (file && !configs.has(file)) {
|
||||||
|
configs.add(file)
|
||||||
|
console.log(`> Using external babel configuration`)
|
||||||
|
console.log(`> Location: "${file}"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add our default preset if the no "babelrc" found.
|
||||||
|
options.presets = [...options.presets, presetItem]
|
||||||
|
}
|
||||||
|
|
||||||
|
options.plugins = [
|
||||||
|
...options.plugins,
|
||||||
|
dev && !isServer && hotLoaderItem,
|
||||||
|
dev && reactJsxSourceItem
|
||||||
|
].filter(Boolean)
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -5,7 +5,6 @@ import UglifyJSPlugin from 'uglifyjs-webpack-plugin'
|
||||||
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
|
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
|
||||||
import WriteFilePlugin from 'write-file-webpack-plugin'
|
import WriteFilePlugin from 'write-file-webpack-plugin'
|
||||||
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
|
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
|
||||||
import {loadPartialConfig, createConfigItem} from '@babel/core'
|
|
||||||
import {getPages} from './webpack/utils'
|
import {getPages} from './webpack/utils'
|
||||||
import PagesPlugin from './plugins/pages-plugin'
|
import PagesPlugin from './plugins/pages-plugin'
|
||||||
import NextJsSsrImportPlugin from './plugins/nextjs-ssr-import'
|
import NextJsSsrImportPlugin from './plugins/nextjs-ssr-import'
|
||||||
|
@ -14,10 +13,6 @@ import UnlinkFilePlugin from './plugins/unlink-file-plugin'
|
||||||
import PagesManifestPlugin from './plugins/pages-manifest-plugin'
|
import PagesManifestPlugin from './plugins/pages-manifest-plugin'
|
||||||
import BuildManifestPlugin from './plugins/build-manifest-plugin'
|
import BuildManifestPlugin from './plugins/build-manifest-plugin'
|
||||||
|
|
||||||
const presetItem = createConfigItem(require('./babel/preset'), {type: 'preset'})
|
|
||||||
const hotLoaderItem = createConfigItem(require('react-hot-loader/babel'), {type: 'plugin'})
|
|
||||||
const reactJsxSourceItem = createConfigItem(require('@babel/plugin-transform-react-jsx-source'), {type: 'plugin'})
|
|
||||||
|
|
||||||
const nextDir = path.join(__dirname, '..', '..', '..')
|
const nextDir = path.join(__dirname, '..', '..', '..')
|
||||||
const nextNodeModulesDir = path.join(nextDir, 'node_modules')
|
const nextNodeModulesDir = path.join(nextDir, 'node_modules')
|
||||||
const nextPagesDir = path.join(nextDir, 'pages')
|
const nextPagesDir = path.join(nextDir, 'pages')
|
||||||
|
@ -30,37 +25,6 @@ const interpolateNames = new Map(defaultPages.map((p) => {
|
||||||
return [path.join(nextPagesDir, p), `dist/bundles/pages/${p}`]
|
return [path.join(nextPagesDir, p), `dist/bundles/pages/${p}`]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function babelConfig (dir, {isServer, dev}) {
|
|
||||||
const mainBabelOptions = {
|
|
||||||
cacheDirectory: true,
|
|
||||||
presets: [],
|
|
||||||
plugins: [
|
|
||||||
dev && !isServer && hotLoaderItem,
|
|
||||||
dev && reactJsxSourceItem
|
|
||||||
].filter(Boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename = path.join(dir, 'filename.js')
|
|
||||||
const externalBabelConfig = loadPartialConfig({ babelrc: true, filename })
|
|
||||||
if (externalBabelConfig && externalBabelConfig.babelrc) {
|
|
||||||
// Log it out once
|
|
||||||
if (!isServer) {
|
|
||||||
console.log(`> Using external babel configuration`)
|
|
||||||
console.log(`> Location: "${externalBabelConfig.babelrc}"`)
|
|
||||||
}
|
|
||||||
mainBabelOptions.babelrc = true
|
|
||||||
} else {
|
|
||||||
mainBabelOptions.babelrc = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add our default preset if the no "babelrc" found.
|
|
||||||
if (!mainBabelOptions.babelrc) {
|
|
||||||
mainBabelOptions.presets.push(presetItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainBabelOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
function externalsConfig (dir, isServer) {
|
function externalsConfig (dir, isServer) {
|
||||||
const externals = []
|
const externals = []
|
||||||
|
|
||||||
|
@ -96,12 +60,10 @@ function externalsConfig (dir, isServer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function getBaseWebpackConfig (dir, {dev = false, isServer = false, buildId, config}) {
|
export default async function getBaseWebpackConfig (dir, {dev = false, isServer = false, buildId, config}) {
|
||||||
const babelLoaderOptions = babelConfig(dir, {dev, isServer})
|
|
||||||
|
|
||||||
const defaultLoaders = {
|
const defaultLoaders = {
|
||||||
babel: {
|
babel: {
|
||||||
loader: 'babel-loader',
|
loader: 'next-babel-loader',
|
||||||
options: babelLoaderOptions
|
options: {dev, isServer}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
test/integration/babel/.babelrc
Normal file
6
test/integration/babel/.babelrc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"next/babel",
|
||||||
|
"@babel/preset-flow"
|
||||||
|
]
|
||||||
|
}
|
10
test/integration/babel/pages/index.js
Normal file
10
test/integration/babel/pages/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// This page is written in flowtype to test Babel's functionality
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
type Props = {}
|
||||||
|
|
||||||
|
export default class MyComponent extends React.Component<Props> {
|
||||||
|
render () {
|
||||||
|
return <div id='text'>Test Babel</div>
|
||||||
|
}
|
||||||
|
}
|
9
test/integration/babel/test/.babelrc
Normal file
9
test/integration/babel/test/.babelrc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["next/babel", {
|
||||||
|
"preset-env": {
|
||||||
|
"modules": "commonjs"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
31
test/integration/babel/test/index.test.js
Normal file
31
test/integration/babel/test/index.test.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/* global jasmine, describe, beforeAll, afterAll */
|
||||||
|
|
||||||
|
import { join } from 'path'
|
||||||
|
import {
|
||||||
|
renderViaHTTP,
|
||||||
|
fetchViaHTTP,
|
||||||
|
findPort,
|
||||||
|
launchApp,
|
||||||
|
killApp
|
||||||
|
} from 'next-test-utils'
|
||||||
|
|
||||||
|
// test suits
|
||||||
|
import rendering from './rendering'
|
||||||
|
|
||||||
|
const context = {}
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
||||||
|
|
||||||
|
describe('Babel', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
context.appPort = await findPort()
|
||||||
|
context.server = await launchApp(join(__dirname, '../'), context.appPort, true)
|
||||||
|
|
||||||
|
// pre-build all pages at the start
|
||||||
|
await Promise.all([
|
||||||
|
renderViaHTTP(context.appPort, '/')
|
||||||
|
])
|
||||||
|
})
|
||||||
|
afterAll(() => killApp(context.server))
|
||||||
|
|
||||||
|
rendering(context, 'Rendering via HTTP', (p, q) => renderViaHTTP(context.appPort, p, q), (p, q) => fetchViaHTTP(context.appPort, p, q))
|
||||||
|
})
|
17
test/integration/babel/test/rendering.js
Normal file
17
test/integration/babel/test/rendering.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/* global describe, it, expect */
|
||||||
|
|
||||||
|
import cheerio from 'cheerio'
|
||||||
|
|
||||||
|
export default function ({ app }, suiteName, render, fetch) {
|
||||||
|
async function get$ (path, query) {
|
||||||
|
const html = await render(path, query)
|
||||||
|
return cheerio.load(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(suiteName, () => {
|
||||||
|
it('Should compile a page with flowtype correctly', async () => {
|
||||||
|
const $ = await get$('/')
|
||||||
|
expect($('#text').text()).toBe('Test Babel')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/* global describe, it, expect */
|
/* global describe, it, expect */
|
||||||
import webdriver from 'next-webdriver'
|
import webdriver from 'next-webdriver'
|
||||||
import { readFileSync, writeFileSync, renameSync } from 'fs'
|
import { readFileSync, writeFileSync, renameSync, existsSync } from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { waitFor, check } from 'next-test-utils'
|
import { waitFor, check } from 'next-test-utils'
|
||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
|
@ -9,33 +9,39 @@ export default (context, renderViaHTTP) => {
|
||||||
describe('Hot Module Reloading', () => {
|
describe('Hot Module Reloading', () => {
|
||||||
describe('delete a page and add it back', () => {
|
describe('delete a page and add it back', () => {
|
||||||
it('should load the page properly', async () => {
|
it('should load the page properly', async () => {
|
||||||
const browser = await webdriver(context.appPort, '/hmr/contact')
|
|
||||||
const text = await browser
|
|
||||||
.elementByCss('p').text()
|
|
||||||
expect(text).toBe('This is the contact page.')
|
|
||||||
|
|
||||||
const contactPagePath = join(__dirname, '../', 'pages', 'hmr', 'contact.js')
|
const contactPagePath = join(__dirname, '../', 'pages', 'hmr', 'contact.js')
|
||||||
const newContactPagePath = join(__dirname, '../', 'pages', 'hmr', '_contact.js')
|
const newContactPagePath = join(__dirname, '../', 'pages', 'hmr', '_contact.js')
|
||||||
|
|
||||||
// Rename the file to mimic a deleted page
|
try {
|
||||||
renameSync(contactPagePath, newContactPagePath)
|
const browser = await webdriver(context.appPort, '/hmr/contact')
|
||||||
|
const text = await browser
|
||||||
|
.elementByCss('p').text()
|
||||||
|
expect(text).toBe('This is the contact page.')
|
||||||
|
|
||||||
// wait until the 404 page comes
|
// Rename the file to mimic a deleted page
|
||||||
await check(
|
renameSync(contactPagePath, newContactPagePath)
|
||||||
() => browser.elementByCss('body').text(),
|
|
||||||
/This page could not be found/
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rename the file back to the original filename
|
// wait until the 404 page comes
|
||||||
renameSync(newContactPagePath, contactPagePath)
|
await check(
|
||||||
|
() => browser.elementByCss('body').text(),
|
||||||
|
/(This page could not be found|ENOENT)/
|
||||||
|
)
|
||||||
|
|
||||||
// wait until the page comes back
|
// Rename the file back to the original filename
|
||||||
await check(
|
renameSync(newContactPagePath, contactPagePath)
|
||||||
() => browser.elementByCss('body').text(),
|
|
||||||
/This is the contact page/
|
|
||||||
)
|
|
||||||
|
|
||||||
browser.close()
|
// wait until the page comes back
|
||||||
|
await check(
|
||||||
|
() => browser.elementByCss('body').text(),
|
||||||
|
/This is the contact page/
|
||||||
|
)
|
||||||
|
|
||||||
|
browser.close()
|
||||||
|
} finally {
|
||||||
|
if (existsSync(newContactPagePath)) {
|
||||||
|
renameSync(newContactPagePath, contactPagePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
23
yarn.lock
23
yarn.lock
|
@ -1171,13 +1171,14 @@ babel-jest@21.2.0, babel-jest@^21.2.0:
|
||||||
babel-plugin-istanbul "^4.0.0"
|
babel-plugin-istanbul "^4.0.0"
|
||||||
babel-preset-jest "^21.2.0"
|
babel-preset-jest "^21.2.0"
|
||||||
|
|
||||||
babel-loader@8.0.0-beta.2:
|
babel-loader@8.0.0-beta.3:
|
||||||
version "8.0.0-beta.2"
|
version "8.0.0-beta.3"
|
||||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.0-beta.2.tgz#4d5b67c964dc8c9cba866fd13d6b90df3acf8723"
|
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.0-beta.3.tgz#49efeea6e8058d5af860a18a6de88b8c1450645b"
|
||||||
dependencies:
|
dependencies:
|
||||||
find-cache-dir "^1.0.0"
|
find-cache-dir "^1.0.0"
|
||||||
loader-utils "^1.0.2"
|
loader-utils "^1.0.2"
|
||||||
mkdirp "^0.5.1"
|
mkdirp "^0.5.1"
|
||||||
|
util.promisify "^1.0.0"
|
||||||
|
|
||||||
babel-messages@^6.23.0:
|
babel-messages@^6.23.0:
|
||||||
version "6.23.0"
|
version "6.23.0"
|
||||||
|
@ -2544,7 +2545,7 @@ error-stack-parser@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
stackframe "^1.0.3"
|
stackframe "^1.0.3"
|
||||||
|
|
||||||
es-abstract@^1.7.0:
|
es-abstract@^1.5.1, es-abstract@^1.7.0:
|
||||||
version "1.11.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681"
|
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5341,6 +5342,13 @@ object.assign@^4.0.4:
|
||||||
has-symbols "^1.0.0"
|
has-symbols "^1.0.0"
|
||||||
object-keys "^1.0.11"
|
object-keys "^1.0.11"
|
||||||
|
|
||||||
|
object.getownpropertydescriptors@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
|
||||||
|
dependencies:
|
||||||
|
define-properties "^1.1.2"
|
||||||
|
es-abstract "^1.5.1"
|
||||||
|
|
||||||
object.omit@^2.0.0:
|
object.omit@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
|
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
|
||||||
|
@ -7439,6 +7447,13 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|
||||||
|
util.promisify@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
|
||||||
|
dependencies:
|
||||||
|
define-properties "^1.1.2"
|
||||||
|
object.getownpropertydescriptors "^2.0.3"
|
||||||
|
|
||||||
util@0.10.3, util@^0.10.3:
|
util@0.10.3, util@^0.10.3:
|
||||||
version "0.10.3"
|
version "0.10.3"
|
||||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
||||||
|
|
Loading…
Reference in a new issue