1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Add .jsx extension support (#3376)

* Add .jsx extension

* examples: add create-next-app (#3377)

* examples: add create-next-app

* fix with-typescript readme

* Upgrading with-flow example to the latest flow-bin ver. 0.59.0 (#3337)

For upgrading I used flow-upgrade module by https://yarnpkg.com/en/package/flow-upgrade

* doc'd fs-routing option & added note on `passHref` (#3384)

2 changes:

`passHref` - just added a cautionary note on the importance of `passHref`. We had a few days of no-href links on our site b/c we used a custom component instead of a raw `<a>` tag,  and Google bot wasn't crawling our links (confirmed in Google cache). Hurt our SEO a bit, so I thought it was worth noting.


`useFileSystemPublicRoutes` - this is mentioned in https://github.com/zeit/next.js/pull/914 , but it doesn't appear any doc was actually added. We use `next-routes`, and we were serving all the files in `/pages/` in addition to their route patterns (ie duplicate content), which can be a pain w/ SEO and duplicate content.

* fix typo in readme.md (#3385)

* Upgrade styled-jsx to v2.2.1 (#3358)

* Pulled encoding to top of head (#3214)

* Remove next.d.ts to use @types/next (#3297)

* Add with-mobx-state-tree example (#3179)

* Adapt with-mobx example for with-mobx-state-tree

* Remove unnecessary lastUpdate parameter to show off snapshot

* update readme

* make other.js more closely mimic index.js

* Upgrade styled-jsx to v2.2.1

Includes some bug fixes.

* Fix linting

* Make sure import that doesn’t end in .jsx works

* Move tests

* Show error when .js and .jsx both exist

* Remove .jsx when importing from ‘path.jsx’

* Fixes

* Get .jsx resolver back

* Revert "Get .jsx resolver back"

This reverts commit 6f76712caa400e6f41a6a32ff80189a95b194cce.

* Revert "Revert "Get .jsx resolver back""

This reverts commit 69e592e86e53f28d0e1f78009196b76f2f831866.

* Add remove .jsx to preset

* Remove jsx resolver

* Revert "Remove jsx resolver"

This reverts commit 5e3ef1aca134de47657d91485809cd801e13329f.

* Revert "Revert "Remove jsx resolver""

This reverts commit 8248e5066cff1c7e33dac2e5a88ffe6856e3fc4e.

* Revert "Revert "Revert "Remove jsx resolver"""

This reverts commit 2a6d418a227ea4e59874b0374628ef497e527c52.

* Make 1 component not use .jsx
This commit is contained in:
Tim Neutkens 2017-12-05 15:46:06 -08:00 committed by GitHub
parent 4be6a521ed
commit e2bcb039cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 85 additions and 15 deletions

View file

@ -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))
}
}
}
}
}

View file

@ -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'),

View file

@ -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) {

View file

@ -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'
@ -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`]
}
}
@ -192,14 +192,14 @@ 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/
}] : [])
@ -207,7 +207,7 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
test: /\.json$/,
loader: 'json-loader'
}, {
test: /\.(js|json)(\?[^?]*)?$/,
test: /\.(js|jsx|json)(\?[^?]*)?$/,
loader: 'emit-file-loader',
include: [dir, nextPagesDir],
exclude (str) {
@ -215,17 +215,34 @@ export default async function createCompiler (dir, { buildId, dev = false, quiet
},
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,
@ -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'),
@ -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) {
@ -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',

View file

@ -11,7 +11,7 @@ const defaultConfig = {
assetPrefix: '',
configOrigin: 'default',
useFileSystemPublicRoutes: true,
pagesGlobPattern: 'pages/**/*.js'
pagesGlobPattern: 'pages/**/*.+(js|jsx)'
}
export default function getConfig (dir, customConfig) {

View file

@ -27,11 +27,13 @@ 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',
i + 'index.json'
]
}
@ -39,6 +41,8 @@ function getPaths (id) {
return [
i + '.js',
join(i, 'index.js'),
i + '.jsx',
join(i, 'index.jsx'),
i + '.json',
join(i, 'index.json')
]

View file

@ -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')

View file

@ -0,0 +1,3 @@
export const Hello = () => (
<div>Hello</div>
)

View file

@ -0,0 +1,3 @@
export const World = () => (
<div>World</div>
)

View file

@ -0,0 +1,6 @@
import {World} from '../components/world'
import {Hello} from '../components/hello.jsx'
export default () => (
<div><Hello/> <World/></div>
)

View file

@ -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')