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

Use Typescript to transpile Next.js core files instead of Babel (#5747)

- Replaces taskr-babel with taskr-typescript for the `next` package
- Makes sure Node 8+ is used, no unneeded transpilation
- Compile Next.js client side files through babel the same way pages are
- Compile Next.js client side files to esmodules, not commonjs, so that tree shaking works.
- Move error-debug.js out of next-server as it's only used/require in development
- Drop ansi-html as dependency from next-server
- Make next/link esmodule (for tree-shaking)
- Make next/router esmodule (for tree-shaking)
- add typescript compilation to next-server
- Remove last remains of Flow
- Move hoist-non-react-statics to next, out of next-server
- Move htmlescape to next, out of next-server
- Remove runtime-corejs2 from next-server
This commit is contained in:
Tim Neutkens 2018-11-28 15:03:02 +01:00 committed by GitHub
parent c801a96631
commit 15bb1c5e79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 214 additions and 289 deletions

View file

@ -52,8 +52,8 @@
"fkill": "5.1.0",
"flatten": "1.0.2",
"get-port": "3.2.0",
"jest-junit": "^5.0.0",
"jest-cli": "23.6.0",
"jest-junit": "^5.0.0",
"lerna": "^3.4.0",
"lint-staged": "4.2.3",
"mkdirp": "0.5.1",
@ -67,7 +67,8 @@
"standard": "11.0.1",
"taskr": "1.1.0",
"wait-port": "0.2.2",
"wd": "1.10.3"
"wd": "1.10.3",
"webpack-bundle-analyzer": "3.0.3"
},
"engines": {
"node": ">= 8.0.0"

View file

@ -1,16 +0,0 @@
module.exports = {
'presets': [
'@babel/preset-env',
'@babel/preset-react'
],
'plugins': [
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-class-properties',
['@babel/plugin-transform-runtime', {
'corejs': 2
}],
['babel-plugin-transform-define', {
'process.env.NEXT_VERSION': require('./package.json').version
}]
]
}

View file

@ -1,10 +1,9 @@
/* global __NEXT_DATA__ */
import { parse, format } from 'url'
import EventEmitter from '../EventEmitter'
import shallowEquals from '../shallow-equals'
import EventEmitter from '../event-emitter'
import shallowEquals from './shallow-equals'
import { loadGetInitialProps, getURL } from '../utils'
import { _rewriteUrlForNextExport } from './'
export default class Router {
static events = new EventEmitter()
@ -46,6 +45,30 @@ export default class Router {
}
}
static _rewriteUrlForNextExport (url) {
const [, hash] = url.split('#')
url = url.replace(/#.*/, '')
let [path, qs] = url.split('?')
path = path.replace(/\/$/, '')
let newPath = path
// Append a trailing slash if this path does not have an extension
if (!/\.[^/]+\/?$/.test(path)) {
newPath = `${path}/`
}
if (qs) {
newPath = `${newPath}?${qs}`
}
if (hash) {
newPath = `${newPath}#${hash}`
}
return newPath
}
onPopState = e => {
if (!e.state) {
// We get state as undefined for two reasons.
@ -147,7 +170,7 @@ export default class Router {
// Add the ending slash to the paths. So, we can serve the
// "<page>/index.html" directly for the SSR page.
if (__NEXT_DATA__.nextExport) {
as = _rewriteUrlForNextExport(as)
as = Router._rewriteUrlForNextExport(as)
}
this.abortComponentLoad(as)

View file

@ -34,8 +34,7 @@ export function isResSent (res) {
export async function loadGetInitialProps (Component, ctx) {
if (process.env.NODE_ENV !== 'production') {
if (Component.prototype && Component.prototype.getInitialProps) {
const compName = getDisplayName(Component)
const message = `"${compName}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.`
const message = `"${getDisplayName(Component)}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.`
throw new Error(message)
}
}
@ -49,8 +48,7 @@ export async function loadGetInitialProps (Component, ctx) {
}
if (!props) {
const compName = getDisplayName(Component)
const message = `"${compName}.getInitialProps()" should resolve to an object. But found "${props}" instead.`
const message = `"${getDisplayName(Component)}.getInitialProps()" should resolve to an object. But found "${props}" instead.`
throw new Error(message)
}

View file

@ -1 +0,0 @@
module.exports = require('./dist/lib/link')

View file

@ -22,17 +22,13 @@
},
"taskr": {
"requires": [
"./taskfile-babel.js"
"./taskfile-typescript.js"
]
},
"dependencies": {
"@babel/runtime-corejs2": "7.1.2",
"ansi-html": "0.0.7",
"etag": "1.8.1",
"find-up": "3.0.0",
"fresh": "0.5.2",
"hoist-non-react-statics": "3.0.1",
"htmlescape": "1.1.1",
"path-to-regexp": "2.1.0",
"prop-types": "15.6.2",
"send": "0.16.1",
@ -43,16 +39,10 @@
"react-dom": "^16.0.0"
},
"devDependencies": {
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.1.0",
"@babel/plugin-proposal-object-rest-spread": "7.0.0",
"@babel/plugin-syntax-dynamic-import": "7.0.0",
"@babel/plugin-transform-runtime": "7.1.0",
"@babel/preset-env": "7.1.0",
"@babel/preset-react": "7.0.0",
"@taskr/clear": "1.1.0",
"@taskr/watch": "1.1.0",
"taskr": "1.1.0"
"taskr": "1.1.0",
"typescript": "3.1.6"
},
"engines": {
"node": ">= 8.0.0"

View file

@ -1 +0,0 @@
module.exports = require('./dist/lib/router')

View file

@ -5,10 +5,9 @@ import send from 'send'
import generateETag from 'etag'
import fresh from 'fresh'
import requirePage, {normalizePagePath} from './require'
import { Router } from '../lib/router'
import Router from '../lib/router/router'
import { loadGetInitialProps, isResSent } from '../lib/utils'
import Head, { defaultHead } from '../lib/head'
import ErrorDebug from '../lib/error-debug'
import Loadable from '../lib/loadable'
import LoadableCapture from '../lib/loadable-capture'
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH } from 'next-server/constants'
@ -137,6 +136,7 @@ async function doRender (req, res, pathname, query, {
try {
if (err && dev) {
const ErrorDebug = require(join(distDir, SERVER_DIRECTORY, 'error-debug')).default
html = render(<ErrorDebug error={err} />)
} else {
html = render(app)

View file

@ -1,65 +0,0 @@
// taskr babel plugin with Babel 7 support
// https://github.com/lukeed/taskr/pull/305
'use strict'
const transform = require('@babel/core').transform
const flatten = require('flatten')
const BABEL_REGEX = /(^@babel\/)(preset|plugin)-(.*)/i
function getBabels () {
const pkg = require('./package.json')
return flatten(
['devDependencies', 'dependencies'].map(s => Object.keys(pkg[s] || {}))
).filter(s => BABEL_REGEX.test(s))
}
module.exports = function (task) {
let cache
task.plugin('babel', {}, function * (file, opts) {
if (opts.preload) {
delete opts.preload
// get dependencies
cache = cache || getBabels()
// attach any deps to babel's `opts`
cache.forEach(dep => {
const segs = BABEL_REGEX.exec(dep)
const type = `${segs[2]}s`
const name = `@babel/${segs[2]}-${segs[3]}`
opts[type] = opts[type] || []
// flatten all (advanced entries are arrays)
if (flatten(opts[type]).indexOf(name) === -1) {
opts[type] = opts[type].concat(name)
}
})
}
// attach file's name
opts.filename = file.base
const output = transform(file.data, opts)
if (output.map) {
const map = `${file.base}.map`
// append `sourceMappingURL` to original file
if (opts.sourceMaps !== 'both') {
output.code += Buffer.from(`\n//# sourceMappingURL=${map}`)
}
// add sourcemap to `files` array
this._.files.push({
base: map,
dir: file.dir,
data: Buffer.from(JSON.stringify(output.map))
})
}
// update file's data
file.data = Buffer.from(output.code)
})
}

View file

@ -0,0 +1,43 @@
'use strict'
try {
const ts = require('typescript')
const extname = require('path').extname
const config = require('./tsconfig.json')
module.exports = function (task) {
task.plugin('typescript', { every: true }, function * (file, options) {
const opts = {
fileName: file.base,
compilerOptions: {
...config.compilerOptions,
...options
}
}
const ext = extname(file.base)
// For example files without an extension don't have to be rewritten
if (ext) {
// Replace `.ts` with `.js`
const extRegex = new RegExp(ext.replace('.', '\\.') + '$', 'i')
file.base = file.base.replace(extRegex, '.js')
}
// compile output
const result = ts.transpileModule(file.data.toString(), opts)
if (opts.compilerOptions.sourceMap && result.sourceMapText) {
// add sourcemap to `files` array
this._.files.push({
dir: file.dir,
base: `${file.base}.map`,
data: Buffer.from(JSON.stringify(result.sourceMapText), 'utf8')
})
}
// update file's data
file.data = Buffer.from(result.outputText.replace(/process\.env\.NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8')
})
}
} catch (err) {
console.error(err)
}

View file

@ -1,27 +1,21 @@
const notifier = require('node-notifier')
export async function bin (task, opts) {
await task.source(opts.src || 'bin/*').babel().target('dist/bin', {mode: '0755'})
notify('Compiled binaries')
}
export async function lib (task, opts) {
await task.source(opts.src || 'lib/**/*.js').babel().target('dist/lib')
await task.source(opts.src || 'lib/**/*.js').typescript({module: 'commonjs'}).target('dist/lib')
notify('Compiled lib files')
}
export async function server (task, opts) {
await task.source(opts.src || 'server/**/*.js').babel().target('dist/server')
await task.source(opts.src || 'server/**/*.js').typescript({module: 'commonjs'}).target('dist/server')
notify('Compiled server files')
}
export async function build (task) {
await task.parallel(['bin', 'server', 'lib'])
await task.parallel(['server', 'lib'])
}
export default async function (task) {
await task.start('build')
await task.watch('bin/*', 'bin')
await task.watch('server/**/*.js', 'server')
await task.watch('lib/**/*.js', 'lib')
}

View file

@ -0,0 +1,10 @@
{
"compilerOptions": {
"allowJs": true,
"noEmit": true,
"module": "esnext",
"target": "ES2017",
"esModuleInterop": true,
"jsx": "react"
}
}

View file

@ -1,2 +0,0 @@
[ignore]
<PROJECT_ROOT>/.*.json

View file

@ -1,17 +0,0 @@
module.exports = {
'presets': [
'@babel/preset-env',
'@babel/preset-react'
],
'plugins': [
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-class-properties',
['@babel/plugin-transform-runtime', {
'corejs': 2
}],
['babel-plugin-transform-define', {
'process.env.NEXT_VERSION': require('./package.json').version
}]
]
}

View file

@ -19,12 +19,6 @@ export default function ({ types: t, template }) {
if (source === 'next/head') {
path.node.source.value = 'next-server/head'
}
if (source === 'next/link') {
path.node.source.value = 'next-server/link'
}
if (source === 'next/router') {
path.node.source.value = 'next-server/router'
}
}
}
}

View file

@ -15,7 +15,7 @@ import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin'
import ChunkNamesPlugin from './webpack/plugins/chunk-names-plugin'
import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin'
import {SERVER_DIRECTORY, REACT_LOADABLE_MANIFEST, CLIENT_STATIC_FILES_RUNTIME_WEBPACK, CLIENT_STATIC_FILES_RUNTIME_MAIN} from 'next-server/constants'
import {NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST, DEFAULT_PAGES_DIR} from '../lib/constants'
import {NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST_CLIENT, NEXT_PROJECT_ROOT_DIST_SERVER, DEFAULT_PAGES_DIR} from '../lib/constants'
import AutoDllPlugin from 'autodll-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import AssetsSizePlugin from './webpack/plugins/assets-size-plugin'
@ -58,7 +58,7 @@ function externalsConfig (dir, isServer, lambdas) {
]
}
const notExternalModules = ['next/app', 'next/document', 'next/error', 'http-status', 'string-hash']
const notExternalModules = ['next/app', 'next/document', 'next/link', 'next/router', 'next/error', 'http-status', 'string-hash', 'ansi-html', 'hoist-non-react-statics', 'htmlescape']
externals.push((context, request, callback) => {
if (notExternalModules.indexOf(request) !== -1) {
@ -71,11 +71,7 @@ function externalsConfig (dir, isServer, lambdas) {
}
// Default pages have to be transpiled
if (res.match(/next[/\\]dist[/\\]pages/)) {
return callback()
}
if (res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/)) {
if (res.match(/next[/\\]dist[/\\]pages/) || res.match(/next[/\\]dist[/\\]client/) || res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/) || res.match(/node_modules[/\\]@babel[/\\]runtime-corejs2[/\\]/)) {
return callback()
}
@ -200,9 +196,12 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
// Backwards compatibility
'main.js': [],
[CLIENT_STATIC_FILES_RUNTIME_MAIN]: [
path.join(NEXT_PROJECT_ROOT_DIST, 'client', (dev ? `next-dev` : 'next'))
path.join(NEXT_PROJECT_ROOT_DIST_CLIENT, (dev ? `next-dev` : 'next'))
].filter(Boolean)
} : {}
const devServerEntries = dev && isServer ? {
'error-debug.js': path.join(NEXT_PROJECT_ROOT_DIST_SERVER, 'error-debug.js')
} : {}
const resolveConfig = {
// Disable .mjs for node_modules bundling
@ -233,6 +232,7 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
entry: async () => {
return {
...clientEntries,
...devServerEntries,
// Only _error and _document when in development. The rest is handled by on-demand-entries
...pagesEntries
}
@ -273,8 +273,14 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
},
{
test: /\.(js|jsx)$/,
include: [dir],
exclude: /node_modules/,
include: [dir, NEXT_PROJECT_ROOT_DIST_CLIENT, DEFAULT_PAGES_DIR],
exclude: (path) => {
if (path.indexOf(NEXT_PROJECT_ROOT_DIST_CLIENT) === 0 || path.indexOf(DEFAULT_PAGES_DIR) === 0) {
return false
}
return /node_modules/.exec(path)
},
use: defaultLoaders.babel
}
].filter(Boolean)

View file

@ -37,11 +37,15 @@ export default class NextJsSsrImportPlugin {
// If the chunk is not part of the pages directory we have to keep the original behavior,
// otherwise webpack will error out when the file is used before the compilation finishes
// this is the case with mini-css-extract-plugin
if (!IS_BUNDLED_PAGE_REGEX.exec(chunk.name)) {
if (!IS_BUNDLED_PAGE_REGEX.exec(chunk.name) && chunk.name !== 'error-debug.js') {
return originalFn(source, chunk)
}
const pagePath = join(outputPath, dirname(chunk.name))
const relativePathToBaseDir = relative(pagePath, join(outputPath, SSR_MODULE_CACHE_FILENAME))
let relativePathToBaseDir = relative(pagePath, join(outputPath, SSR_MODULE_CACHE_FILENAME))
if (chunk.name === 'error-debug.js') {
relativePathToBaseDir = `./${relativePathToBaseDir}`
}
// Make sure even in windows, the path looks like in unix
// Node.js require system will convert it accordingly
const relativePathToBaseDirNormalized = relativePathToBaseDir.replace(/\\/g, '/')

View file

@ -1,10 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom'
import HeadManager from './head-manager'
import { createRouter } from 'next-server/dist/lib/router'
import EventEmitter from 'next-server/dist/lib/EventEmitter'
import { createRouter } from 'next/router'
import EventEmitter from 'next-server/dist/lib/event-emitter'
import {loadGetInitialProps, getURL} from 'next-server/dist/lib/utils'
import PageLoader from '../lib/page-loader'
import PageLoader from './page-loader'
import * as asset from 'next-server/asset'
import * as envConfig from 'next-server/config'
import ErrorBoundary from './error-boundary'

View file

@ -3,8 +3,8 @@
import { resolve, format, parse } from 'url'
import React, { Component, Children } from 'react'
import PropTypes from 'prop-types'
import Router, { _rewriteUrlForNextExport } from './router'
import {execOnce, getLocationOrigin} from './utils'
import Router, {Router as _Router} from 'next/router'
import {execOnce, getLocationOrigin} from 'next-server/dist/lib/utils'
function isLocal (href) {
const url = parse(href, false, true)
@ -145,7 +145,7 @@ class Link extends Component {
typeof __NEXT_DATA__ !== 'undefined' &&
__NEXT_DATA__.nextExport
) {
props.href = _rewriteUrlForNextExport(props.href)
props.href = _Router._rewriteUrlForNextExport(props.href)
}
return React.cloneElement(child, props)

View file

@ -1,6 +1,6 @@
/* global location */
import Router from 'next-server/router'
import Router from 'next/router'
import fetch from 'unfetch'
export default ({assetPrefix}) => {

View file

@ -1,5 +1,5 @@
/* global document */
import EventEmitter from 'next-server/dist/lib/EventEmitter'
import EventEmitter from 'next-server/dist/lib/event-emitter'
// smaller version of https://gist.github.com/igrigorik/a02f2359f3bc50ca7a9c
function supportsPreload (list) {

View file

@ -1,6 +1,5 @@
/* global window */
import _Router from './router'
import { execOnce } from '../utils'
import _Router from 'next-server/dist/lib/router/router'
const SingletonRouter = {
router: null, // holds the actual router instance
@ -62,18 +61,6 @@ routerEvents.forEach((event) => {
})
})
const warnAboutRouterOnAppUpdated = execOnce(() => {
console.warn(`Router.onAppUpdated is removed - visit https://err.sh/zeit/next.js/no-on-app-updated-hook for more information.`)
})
Object.defineProperty(SingletonRouter, 'onAppUpdated', {
get () { return null },
set () {
warnAboutRouterOnAppUpdated()
return null
}
})
function throwIfNoRouter () {
if (!SingletonRouter.router) {
const message = 'No router instance found.\n' +
@ -86,7 +73,7 @@ function throwIfNoRouter () {
export default SingletonRouter
// Reexport the withRoute HOC
export { default as withRouter } from './with-router'
export { default as withRouter } from '../lib/with-router'
// INTERNAL APIS
// -------------
@ -106,30 +93,6 @@ export const createRouter = function (...args) {
// Export the actual Router class, which is usually used inside the server
export const Router = _Router
export function _rewriteUrlForNextExport (url) {
const [, hash] = url.split('#')
url = url.replace(/#.*/, '')
let [path, qs] = url.split('?')
path = path.replace(/\/$/, '')
let newPath = path
// Append a trailing slash if this path does not have an extension
if (!/\.[^/]+\/?$/.test(path)) {
newPath = `${path}/`
}
if (qs) {
newPath = `${newPath}?${qs}`
}
if (hash) {
newPath = `${newPath}#${hash}`
}
return newPath
}
// This function is used to create the `withRouter` router instance
export function makePublicRouterInstance (router) {
const instance = {}

View file

@ -1,6 +1,6 @@
import 'event-source-polyfill'
import connect from './dev-error-overlay/hot-dev-client'
import Router from 'next-server/router'
import Router from 'next/router'
const handlers = {
reload (route) {

View file

@ -3,3 +3,5 @@ export const NEXT_PROJECT_ROOT = join(__dirname, '..', '..')
export const NEXT_PROJECT_ROOT_DIST = join(NEXT_PROJECT_ROOT, 'dist')
export const NEXT_PROJECT_ROOT_NODE_MODULES = join(NEXT_PROJECT_ROOT, 'node_modules')
export const DEFAULT_PAGES_DIR = join(NEXT_PROJECT_ROOT_DIST, 'pages')
export const NEXT_PROJECT_ROOT_DIST_CLIENT = join(NEXT_PROJECT_ROOT_DIST, 'client')
export const NEXT_PROJECT_ROOT_DIST_SERVER = join(NEXT_PROJECT_ROOT_DIST, 'server')

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import hoistStatics from 'hoist-non-react-statics'
import { getDisplayName } from '../utils'
import { getDisplayName } from 'next-server/dist/lib/utils'
export default function withRouter (ComposedComponent) {
const displayName = getDisplayName(ComposedComponent)
@ -14,12 +14,10 @@ export default function withRouter (ComposedComponent) {
static displayName = `withRouter(${displayName})`
render () {
const props = {
router: this.context.router,
...this.props
}
return <ComposedComponent {...props} />
return <ComposedComponent
router={this.context.router}
{...this.props}
/>
}
}

View file

@ -1 +1 @@
module.exports = require('next-server/link')
export {default} from './dist/client/link'

View file

@ -32,7 +32,7 @@
},
"taskr": {
"requires": [
"./taskfile-babel.js"
"./taskfile-typescript.js"
]
},
"dependencies": {
@ -60,6 +60,8 @@
"fresh": "0.5.2",
"friendly-errors-webpack-plugin": "1.7.0",
"glob": "7.1.2",
"hoist-non-react-statics": "3.2.0",
"htmlescape": "1.1.1",
"http-status": "1.0.1",
"launch-editor": "2.2.1",
"loader-utils": "1.1.0",
@ -91,7 +93,8 @@
"@taskr/clear": "1.1.0",
"@taskr/esnext": "1.1.0",
"@taskr/watch": "1.1.0",
"taskr": "1.1.0"
"taskr": "1.1.0",
"typescript": "3.1.6"
},
"engines": {
"node": ">= 8.0.0"

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { execOnce, loadGetInitialProps } from 'next-server/dist/lib/utils'
import { makePublicRouterInstance } from 'next-server/router'
import { makePublicRouterInstance } from 'next/router'
export default class App extends Component {
static childContextTypes = {

View file

@ -1 +1,2 @@
module.exports = require('next-server/router')
export * from './dist/client/router'
export {default} from './dist/client/router'

View file

@ -1,65 +0,0 @@
// taskr babel plugin with Babel 7 support
// https://github.com/lukeed/taskr/pull/305
'use strict'
const transform = require('@babel/core').transform
const flatten = require('flatten')
const BABEL_REGEX = /(^@babel\/)(preset|plugin)-(.*)/i
function getBabels () {
const pkg = require('./package.json')
return flatten(
['devDependencies', 'dependencies'].map(s => Object.keys(pkg[s] || {}))
).filter(s => BABEL_REGEX.test(s))
}
module.exports = function (task) {
let cache
task.plugin('babel', {}, function * (file, opts) {
if (opts.preload) {
delete opts.preload
// get dependencies
cache = cache || getBabels()
// attach any deps to babel's `opts`
cache.forEach(dep => {
const segs = BABEL_REGEX.exec(dep)
const type = `${segs[2]}s`
const name = `@babel/${segs[2]}-${segs[3]}`
opts[type] = opts[type] || []
// flatten all (advanced entries are arrays)
if (flatten(opts[type]).indexOf(name) === -1) {
opts[type] = opts[type].concat(name)
}
})
}
// attach file's name
opts.filename = file.base
const output = transform(file.data, opts)
if (output.map) {
const map = `${file.base}.map`
// append `sourceMappingURL` to original file
if (opts.sourceMaps !== 'both') {
output.code += Buffer.from(`\n//# sourceMappingURL=${map}`)
}
// add sourcemap to `files` array
this._.files.push({
base: map,
dir: file.dir,
data: Buffer.from(JSON.stringify(output.map))
})
}
// update file's data
file.data = Buffer.from(output.code)
})
}

View file

@ -0,0 +1,43 @@
'use strict'
try {
const ts = require('typescript')
const extname = require('path').extname
const config = require('./tsconfig.json')
module.exports = function (task) {
task.plugin('typescript', { every: true }, function * (file, options) {
const opts = {
fileName: file.base,
compilerOptions: {
...config.compilerOptions,
...options
}
}
const ext = extname(file.base)
// For example files without an extension don't have to be rewritten
if (ext) {
// Replace `.ts` with `.js`
const extRegex = new RegExp(ext.replace('.', '\\.') + '$', 'i')
file.base = file.base.replace(extRegex, '.js')
}
// compile output
const result = ts.transpileModule(file.data.toString(), opts)
if (opts.compilerOptions.sourceMap && result.sourceMapText) {
// add sourcemap to `files` array
this._.files.push({
dir: file.dir,
base: `${file.base}.map`,
data: Buffer.from(JSON.stringify(result.sourceMapText), 'utf8')
})
}
// update file's data
file.data = Buffer.from(result.outputText, 'utf8')
})
}
} catch (err) {
console.error(err)
}

View file

@ -5,38 +5,38 @@ export async function compile (task) {
}
export async function bin (task, opts) {
await task.source(opts.src || 'bin/*').babel().target('dist/bin', {mode: '0755'})
await task.source(opts.src || 'bin/*').typescript({module: 'commonjs'}).target('dist/bin', {mode: '0755'})
notify('Compiled binaries')
}
export async function lib (task, opts) {
await task.source(opts.src || 'lib/**/*.js').babel().target('dist/lib')
await task.source(opts.src || 'lib/**/*.js').typescript({module: 'commonjs'}).target('dist/lib')
notify('Compiled lib files')
}
export async function server (task, opts) {
await task.source(opts.src || 'server/**/*.js').babel().target('dist/server')
await task.source(opts.src || 'server/**/*.js').typescript({module: 'commonjs'}).target('dist/server')
notify('Compiled server files')
}
export async function nextbuild (task, opts) {
await task.source(opts.src || 'build/**/*.js').babel().target('dist/build')
await task.source(opts.src || 'build/**/*.js').typescript({module: 'commonjs'}).target('dist/build')
notify('Compiled build files')
}
export async function client (task, opts) {
await task.source(opts.src || 'client/**/*.js').babel().target('dist/client')
await task.source(opts.src || 'client/**/*.js').typescript().target('dist/client')
notify('Compiled client files')
}
// export is a reserved keyword for functions
export async function nextbuildstatic (task, opts) {
await task.source(opts.src || 'export/**/*.js').babel().target('dist/export')
await task.source(opts.src || 'export/**/*.js').typescript({module: 'commonjs'}).target('dist/export')
notify('Compiled export files')
}
export async function pages (task, opts) {
await task.source(opts.src || 'pages/**/*.js').babel().target('dist/pages')
await task.source(opts.src || 'pages/**/*.js').typescript().target('dist/pages')
}
export async function build (task) {

View file

@ -0,0 +1,10 @@
{
"compilerOptions": {
"allowJs": true,
"noEmit": true,
"module": "esnext",
"target": "ES2017",
"esModuleInterop": true,
"jsx": "react"
}
}

View file

@ -1,4 +1,13 @@
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')
module.exports = {
webpack (config, {isServer}) {
config.plugins.push(new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: `dist/${isServer ? 'server' : 'client'}.html`,
openAnalyzer: false
}))
return config
},
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60

View file

@ -66,6 +66,6 @@ describe('Production response size', () => {
console.log(`Response Sizes:\n${responseSizes.map(obj => ` ${obj.url}: ${obj.bytes} (bytes)`).join('\n')} \nOverall: ${responseSizeKilobytes} KB`)
// These numbers are without gzip compression!
expect(responseSizeKilobytes).toBeLessThanOrEqual(207) // Kilobytes
expect(responseSizeKilobytes).toBeLessThanOrEqual(210) // Kilobytes
})
})

View file

@ -1,5 +1,5 @@
/* eslint-env jest */
import EventEmitter from 'next-server/dist/lib/EventEmitter'
import EventEmitter from 'next-server/dist/lib/event-emitter'
describe('EventEmitter', () => {
describe('With listeners', () => {

View file

@ -1,6 +1,6 @@
/* eslint-env jest */
import shallowEquals from 'next-server/dist/lib/shallow-equals'
import shallowEquals from 'next-server/dist/lib/router/shallow-equals'
describe('Shallow Equals', () => {
it('should be true if both objects are the same', () => {