Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b225e38
Add .jsx extension
timneutkens Dec 3, 2017
de5d553
Merge branch 'canary' of github.com:zeit/next.js into add/jsx-extensi…
timneutkens Dec 3, 2017
7977261
Merge branch 'canary' into add/jsx-extension-resolve
arunoda Dec 3, 2017
ce89edc
examples: add create-next-app (#3377)
fouad Dec 3, 2017
0540558
Upgrading with-flow example to the latest flow-bin ver. 0.59.0 (#3337)
davscro Dec 3, 2017
1c55e77
doc'd fs-routing option & added note on `passHref` (#3384)
brandonmp Dec 3, 2017
8f0b7f5
fix typo in readme.md (#3385)
kanjielu Dec 4, 2017
32f9ab3
Upgrade styled-jsx to v2.2.1 (#3358)
giuseppeg Dec 4, 2017
e2888b8
Make sure import that doesn’t end in .jsx works
timneutkens Dec 4, 2017
8bff9a8
Move tests
timneutkens Dec 4, 2017
450aa3f
Merge branch 'canary' into add/jsx-extension-resolve
timneutkens Dec 4, 2017
6107c1a
Show error when .js and .jsx both exist
timneutkens Dec 4, 2017
4a3d4a9
Remove .jsx when importing from ‘path.jsx’
timneutkens Dec 4, 2017
a83d922
Fixes
timneutkens Dec 4, 2017
6f76712
Get .jsx resolver back
timneutkens Dec 4, 2017
69e592e
Revert "Get .jsx resolver back"
timneutkens Dec 5, 2017
60fc776
Revert "Revert "Get .jsx resolver back""
timneutkens Dec 5, 2017
630cf5a
Add remove .jsx to preset
timneutkens Dec 5, 2017
5e3ef1a
Remove jsx resolver
timneutkens Dec 5, 2017
8248e50
Revert "Remove jsx resolver"
timneutkens Dec 5, 2017
2a6d418
Revert "Revert "Remove jsx resolver""
timneutkens Dec 5, 2017
87428c9
Revert "Revert "Revert "Remove jsx resolver"""
timneutkens Dec 5, 2017
86f95be
Make 1 component not use .jsx
timneutkens Dec 5, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions server/build/babel/plugins/remove-dotjsx-from-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This plugins removes the `.jsx` extension from import statements. Because we transpile .jsx files to .js in .next
// E.g. `import Hello from '../components/hello.jsx'` will become `import Hello from '../components/hello'`
module.exports = function ({types}) {
return {
name: 'remove-dotjsx-from-import',
visitor: {
ImportDeclaration (path) {
const value = path.node.source.value
if (value.slice(-4) === '.jsx') {
path.node.source = types.stringLiteral(value.slice(0, -4))
}
}
}
}
}
1 change: 1 addition & 0 deletions server/build/babel/preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = (context, opts = {}) => ({
require.resolve('babel-preset-react')
],
plugins: [
require.resolve('./plugins/remove-dotjsx-from-import'),
require.resolve('babel-plugin-react-require'),
require.resolve('./plugins/handle-import'),
require.resolve('babel-plugin-transform-object-rest-spread'),
Expand Down
19 changes: 16 additions & 3 deletions server/build/loaders/emit-file-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@ import loaderUtils from 'loader-utils'

module.exports = function (content, sourceMap) {
this.cacheable()
const callback = this.async()
const resourcePath = this.resourcePath

const query = loaderUtils.getOptions(this)

// Allows you to do checks on the file name. For example it's used to check if there's both a .js and .jsx file.
if (query.validateFileName) {
try {
query.validateFileName(resourcePath)
} catch (err) {
callback(err)
return
}
}

const name = query.name || '[hash].[ext]'
const context = query.context || this.options.context
const regExp = query.regExp
const opts = { context, content, regExp }
const interpolatedName = loaderUtils.interpolateName(this, name, opts)

const interpolateName = query.interpolateName || ((name) => name)
const interpolatedName = interpolateName(loaderUtils.interpolateName(this, name, opts), {name, opts})
const emit = (code, map) => {
this.emitFile(interpolatedName, code, map)
this.callback(null, code, map)
callback(null, code, map)
}

if (query.transform) {
Expand Down
37 changes: 28 additions & 9 deletions server/build/webpack.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { resolve, join, sep } from 'path'
import { createHash } from 'crypto'
import { realpathSync } from 'fs'
import { realpathSync, existsSync } from 'fs'
import webpack from 'webpack'
import glob from 'glob-promise'
import WriteFilePlugin from 'write-file-webpack-plugin'
Expand Down Expand Up @@ -57,11 +57,11 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
// managing pages.
if (dev) {
for (const p of devPages) {
entries[join('bundles', p)] = [`./${p}?entry`]
entries[join('bundles', p.replace('.jsx', '.js'))] = [`./${p}?entry`]
}
} else {
for (const p of pages) {
entries[join('bundles', p)] = [`./${p}?entry`]
entries[join('bundles', p.replace('.jsx', '.js'))] = [`./${p}?entry`]
}
}

Expand Down Expand Up @@ -192,40 +192,57 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
}

const rules = (dev ? [{
test: /\.js(\?[^?]*)?$/,
test: /\.(js|jsx)(\?[^?]*)?$/,
loader: 'hot-self-accept-loader',
include: [
join(dir, 'pages'),
nextPagesDir
]
}, {
test: /\.js(\?[^?]*)?$/,
test: /\.(js|jsx)(\?[^?]*)?$/,
loader: 'react-hot-loader/webpack',
exclude: /node_modules/
}] : [])
.concat([{
test: /\.json$/,
loader: 'json-loader'
}, {
test: /\.(js|json)(\?[^?]*)?$/,
test: /\.(js|jsx|json)(\?[^?]*)?$/,
loader: 'emit-file-loader',
include: [dir, nextPagesDir],
exclude (str) {
return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
},
options: {
name: 'dist/[path][name].[ext]',
// We need to strip off .jsx on the server. Otherwise require without .jsx doesn't work.
interpolateName: (name) => name.replace('.jsx', '.js'),
validateFileName (file) {
const cases = [{from: '.js', to: '.jsx'}, {from: '.jsx', to: '.js'}]

for (const item of cases) {
const {from, to} = item
if (file.slice(-(from.length)) !== from) {
continue
}

const filePath = file.slice(0, -(from.length)) + to

if (existsSync(filePath)) {
throw new Error(`Both ${from} and ${to} file found. Please make sure you only have one of both.`)
}
}
},
// By default, our babel config does not transpile ES2015 module syntax because
// webpack knows how to handle them. (That's how it can do tree-shaking)
// But Node.js doesn't know how to handle them. So, we have to transpile them here.
transform ({ content, sourceMap, interpolatedName }) {
// Only handle .js files
if (!(/\.js$/.test(interpolatedName))) {
if (!(/\.(js|jsx)$/.test(interpolatedName))) {
return { content, sourceMap }
}

const babelRuntimePath = require.resolve('babel-runtime/package').replace(/[\\/]package\.json$/, '')

const transpiled = babelCore.transform(content, {
babelrc: false,
sourceMaps: dev ? 'both' : false,
Expand All @@ -235,6 +252,7 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
// That's why we need to do it here.
// See more: https://github.com/zeit/next.js/issues/951
plugins: [
require.resolve(join(__dirname, './babel/plugins/remove-dotjsx-from-import.js')),
[require.resolve('babel-plugin-transform-es2015-modules-commonjs')],
[
require.resolve('babel-plugin-module-resolver'),
Expand Down Expand Up @@ -291,7 +309,7 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
presets: [require.resolve('./babel/preset')]
}
}, {
test: /\.js(\?[^?]*)?$/,
test: /\.(js|jsx)(\?[^?]*)?$/,
loader: 'babel-loader',
include: [dir],
exclude (str) {
Expand Down Expand Up @@ -321,6 +339,7 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
chunkFilename: '[name]'
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
modules: [
nextNodeModulesDir,
'node_modules',
Expand Down
2 changes: 1 addition & 1 deletion server/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const defaultConfig = {
assetPrefix: '',
configOrigin: 'default',
useFileSystemPublicRoutes: true,
pagesGlobPattern: 'pages/**/*.js'
pagesGlobPattern: 'pages/**/*.+(js|jsx)'
}

export default function getConfig (dir, customConfig) {
Expand Down
4 changes: 4 additions & 0 deletions server/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,22 @@ function getPaths (id) {
const i = sep === '/' ? id : id.replace(/\//g, sep)

if (i.slice(-3) === '.js') return [i]
if (i.slice(-4) === '.jsx') return [i]
if (i.slice(-5) === '.json') return [i]

if (i[i.length - 1] === sep) {
return [
i + 'index.js',
i + 'index.jsx',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used by the ondemandentries plugin to resolve the file within webpack. That's why it's needed here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

next build -> next start etc work fine without this.

i + 'index.json'
]
}

return [
i + '.js',
join(i, 'index.js'),
i + '.jsx',
join(i, 'index.jsx'),
i + '.json',
join(i, 'index.json')
]
Expand Down
4 changes: 2 additions & 2 deletions server/utils.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { join } from 'path'
import { readdirSync, existsSync } from 'fs'

export const IS_BUNDLED_PAGE = /^bundles[/\\]pages.*\.js$/
export const MATCH_ROUTE_NAME = /^bundles[/\\]pages[/\\](.*)\.js$/
export const IS_BUNDLED_PAGE = /^bundles[/\\]pages.*\.(js|jsx)$/
export const MATCH_ROUTE_NAME = /^bundles[/\\]pages[/\\](.*)\.(js|jsx)$/

export function getAvailableChunks (dir, dist) {
const chunksDir = join(dir, dist, 'chunks')
Expand Down
3 changes: 3 additions & 0 deletions test/integration/basic/components/hello.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const Hello = () => (
<div>Hello</div>
)
3 changes: 3 additions & 0 deletions test/integration/basic/components/world.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const World = () => (
<div>World</div>
)
6 changes: 6 additions & 0 deletions test/integration/basic/pages/custom-extension.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {World} from '../components/world'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add another case where we are importing with ../components/world.jsx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

import {Hello} from '../components/hello.jsx'

export default () => (
<div><Hello/> <World/></div>
)
6 changes: 6 additions & 0 deletions test/integration/basic/test/rendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export default function ({ app }, suiteName, render, fetch) {
expect(html).not.toContain('<link rel="stylesheet" href="dedupe-style.css" class="next-head"/><link rel="stylesheet" href="dedupe-style.css" class="next-head"/>')
})

it('should render the page with custom extension', async () => {
const html = await render('/custom-extension')
expect(html).toContain('<div>Hello</div>')
expect(html).toContain('<div>World</div>')
})

test('renders styled jsx', async () => {
const $ = await get$('/styled-jsx')
const styleId = $('#blue-box').attr('class')
Expand Down