write assets to disk
This commit is contained in:
parent
e7ffb2c17d
commit
3032ade283
8 changed files with 57 additions and 61 deletions
|
@ -3,7 +3,6 @@
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import parseArgs from 'minimist'
|
import parseArgs from 'minimist'
|
||||||
import Server from '../server'
|
import Server from '../server'
|
||||||
import build from '../server/build'
|
|
||||||
import HotReloader from '../server/hot-reloader'
|
import HotReloader from '../server/hot-reloader'
|
||||||
import webpack from '../server/build/webpack'
|
import webpack from '../server/build/webpack'
|
||||||
|
|
||||||
|
@ -20,9 +19,8 @@ const argv = parseArgs(process.argv.slice(2), {
|
||||||
|
|
||||||
const dir = resolve(argv._[0] || '.')
|
const dir = resolve(argv._[0] || '.')
|
||||||
|
|
||||||
build(dir)
|
webpack(dir, { hotReload: true })
|
||||||
.then(async () => {
|
.then(async (compiler) => {
|
||||||
const compiler = await webpack(dir, { hotReload: true })
|
|
||||||
const hotReloader = new HotReloader(compiler)
|
const hotReloader = new HotReloader(compiler)
|
||||||
const srv = new Server({ dir, dev: true, hotReloader })
|
const srv = new Server({ dir, dev: true, hotReloader })
|
||||||
await srv.start(argv.port)
|
await srv.start(argv.port)
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
"send": "0.14.1",
|
"send": "0.14.1",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"webpack": "1.13.2",
|
"webpack": "1.13.2",
|
||||||
"webpack-dev-server": "1.16.2"
|
"webpack-dev-server": "1.16.2",
|
||||||
|
"write-file-webpack-plugin": "3.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-transform-remove-strict-mode": "0.0.2",
|
"babel-plugin-transform-remove-strict-mode": "0.0.2",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { resolve, join } from 'path'
|
import { resolve, join } from 'path'
|
||||||
import webpack from 'webpack'
|
import webpack from 'webpack'
|
||||||
import glob from 'glob-promise'
|
import glob from 'glob-promise'
|
||||||
|
import WriteFilePlugin from 'write-file-webpack-plugin'
|
||||||
|
|
||||||
export default async function createCompiler(dir, { hotReload = false } = {}) {
|
export default async function createCompiler(dir, { hotReload = false } = {}) {
|
||||||
dir = resolve(dir)
|
dir = resolve(dir)
|
||||||
|
@ -20,6 +21,16 @@ export default async function createCompiler(dir, { hotReload = false } = {}) {
|
||||||
|
|
||||||
const nodeModulesDir = resolve(__dirname, '..', '..', '..', 'node_modules')
|
const nodeModulesDir = resolve(__dirname, '..', '..', '..', 'node_modules')
|
||||||
|
|
||||||
|
const plugins = hotReload ? [
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new WriteFilePlugin({ log: false })
|
||||||
|
] : [
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
compress: { warnings: false },
|
||||||
|
sourceMap: false
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
const babelRuntimePath = require.resolve('babel-runtime/package')
|
const babelRuntimePath = require.resolve('babel-runtime/package')
|
||||||
.replace(/[\\\/]package\.json$/, '');
|
.replace(/[\\\/]package\.json$/, '');
|
||||||
|
|
||||||
|
@ -96,14 +107,7 @@ export default async function createCompiler(dir, { hotReload = false } = {}) {
|
||||||
resolve(__dirname, '..', 'loaders')
|
resolve(__dirname, '..', 'loaders')
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins,
|
||||||
hotReload
|
|
||||||
? new webpack.HotModuleReplacementPlugin()
|
|
||||||
: new webpack.optimize.UglifyJsPlugin({
|
|
||||||
compress: { warnings: false },
|
|
||||||
sourceMap: false
|
|
||||||
})
|
|
||||||
],
|
|
||||||
module: {
|
module: {
|
||||||
preLoaders: [
|
preLoaders: [
|
||||||
{ test: /\.json$/, loader: 'json-loader' }
|
{ test: /\.json$/, loader: 'json-loader' }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import WebpackDevServer from 'webpack-dev-server'
|
import WebpackDevServer from 'webpack-dev-server'
|
||||||
|
import read from './read'
|
||||||
|
|
||||||
export default class HotReloader {
|
export default class HotReloader {
|
||||||
constructor (compiler) {
|
constructor (compiler) {
|
||||||
|
@ -8,6 +9,17 @@ export default class HotReloader {
|
||||||
noInfo: true,
|
noInfo: true,
|
||||||
clientLogLevel: 'warning'
|
clientLogLevel: 'warning'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
compiler.plugin('after-emit', (compilation, callback) => {
|
||||||
|
const { assets } = compilation
|
||||||
|
for (const f of Object.keys(assets)) {
|
||||||
|
const source = assets[f]
|
||||||
|
// delete updated file caches
|
||||||
|
delete require.cache[source.existsAt]
|
||||||
|
delete read.cache[source.existsAt]
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async start () {
|
async start () {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { resolve } from 'path'
|
||||||
import send from 'send'
|
import send from 'send'
|
||||||
import Router from './router'
|
import Router from './router'
|
||||||
import { render, renderJSON } from './render'
|
import { render, renderJSON } from './render'
|
||||||
import HotReloader from './hot-reloader'
|
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
constructor ({ dir = '.', dev = false, hotReloader }) {
|
constructor ({ dir = '.', dev = false, hotReloader }) {
|
||||||
|
@ -63,11 +62,10 @@ export default class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
async render (req, res) {
|
async render (req, res) {
|
||||||
const { dir, dev, hotReloader } = this
|
const { dir, dev } = this
|
||||||
const mfs = hotReloader ? hotReloader.fileSystem : null
|
|
||||||
let html
|
let html
|
||||||
try {
|
try {
|
||||||
html = await render(req.url, { req, res }, { dir, dev, mfs })
|
html = await render(req.url, { req, res }, { dir, dev })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ('ENOENT' === err.code) {
|
if ('ENOENT' === err.code) {
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
|
@ -75,18 +73,17 @@ export default class Server {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
res.statusCode = 500
|
res.statusCode = 500
|
||||||
}
|
}
|
||||||
html = await render('/_error', { req, res, err }, { dir, dev, mfs })
|
html = await render('/_error', { req, res, err }, { dir, dev })
|
||||||
}
|
}
|
||||||
|
|
||||||
sendHTML(res, html)
|
sendHTML(res, html)
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderJSON (req, res) {
|
async renderJSON (req, res) {
|
||||||
const { dir, hotReloader } = this
|
const { dir } = this
|
||||||
const mfs = hotReloader ? hotReloader.fileSystem : null
|
|
||||||
let json
|
let json
|
||||||
try {
|
try {
|
||||||
json = await renderJSON(req.url, { dir, mfs })
|
json = await renderJSON(req.url, { dir })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ('ENOENT' === err.code) {
|
if ('ENOENT' === err.code) {
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
|
@ -94,7 +91,7 @@ export default class Server {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
res.statusCode = 500
|
res.statusCode = 500
|
||||||
}
|
}
|
||||||
json = await renderJSON('/_error.json', { dir, mfs })
|
json = await renderJSON('/_error.json', { dir })
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = JSON.stringify(json)
|
const data = JSON.stringify(json)
|
||||||
|
|
|
@ -1,42 +1,23 @@
|
||||||
import fs from 'mz/fs'
|
import fs from 'mz/fs'
|
||||||
import resolve from './resolve'
|
import resolve from './resolve'
|
||||||
|
|
||||||
const cache = {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* resolve a file like `require.resolve`,
|
* resolve a file like `require.resolve`,
|
||||||
* and read and cache the file content
|
* and read and cache the file content
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function read (path, { mfs }) {
|
async function read (path, opts = {}) {
|
||||||
const f = await (mfs ? resolveFromMFS(path, mfs) : resolve(path))
|
const f = await resolve(path)
|
||||||
if (mfs) {
|
if (cache.hasOwnProperty(f)) {
|
||||||
return mfs.readFileSync(f, 'utf8')
|
return cache[f]
|
||||||
} else {
|
|
||||||
let promise = cache[f]
|
|
||||||
if (!promise) {
|
|
||||||
promise = cache[f] = fs.readFile(f, 'utf8')
|
|
||||||
}
|
|
||||||
return promise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = fs.readFile(f, 'utf8')
|
||||||
|
cache[f] = data
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveFromMFS (path, mfs) {
|
export default read
|
||||||
const isFile = (file, cb) => {
|
export const cache = {}
|
||||||
if (!mfs.existsSync(file)) return cb(null, false)
|
|
||||||
|
|
||||||
let stat
|
read.cache = cache
|
||||||
try {
|
|
||||||
stat = mfs.statSync(file)
|
|
||||||
} catch (err) {
|
|
||||||
return cb(err)
|
|
||||||
}
|
|
||||||
cb(null, stat.isFile() || stat.isFIFO())
|
|
||||||
}
|
|
||||||
const readFile = mfs.readFile.bind(mfs)
|
|
||||||
return resolve(path, { isFile, readFile })
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = read
|
|
||||||
|
|
||||||
exports.cache = cache
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ import { relative, resolve } from 'path'
|
||||||
import { parse } from 'url'
|
import { parse } from 'url'
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react'
|
||||||
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
|
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
|
||||||
import fs from 'mz/fs'
|
import requireModule from './require'
|
||||||
import requireResolve from './resolve'
|
|
||||||
import read from './read'
|
import read from './read'
|
||||||
import Router from '../lib/router'
|
import Router from '../lib/router'
|
||||||
import Document from '../lib/document'
|
import Document from '../lib/document'
|
||||||
|
@ -14,16 +13,14 @@ import { StyleSheetServer } from '../lib/css'
|
||||||
export async function render (url, ctx = {}, {
|
export async function render (url, ctx = {}, {
|
||||||
dir = process.cwd(),
|
dir = process.cwd(),
|
||||||
dev = false,
|
dev = false,
|
||||||
staticMarkup = false,
|
staticMarkup = false
|
||||||
mfs
|
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const path = getPath(url)
|
const path = getPath(url)
|
||||||
const p = await requireResolve(resolve(dir, '.next', 'pages', path))
|
const mod = await requireModule(resolve(dir, '.next', 'pages', path))
|
||||||
const mod = require(p)
|
|
||||||
const Component = mod.default || mod
|
const Component = mod.default || mod
|
||||||
|
|
||||||
const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
||||||
const component = await read(resolve(dir, '.next', '_bundles', 'pages', path), { mfs })
|
const component = await read(resolve(dir, '.next', '_bundles', 'pages', path))
|
||||||
|
|
||||||
const { html, css } = StyleSheetServer.renderStatic(() => {
|
const { html, css } = StyleSheetServer.renderStatic(() => {
|
||||||
const app = createElement(App, {
|
const app = createElement(App, {
|
||||||
|
@ -54,9 +51,9 @@ export async function render (url, ctx = {}, {
|
||||||
return '<!DOCTYPE html>' + renderToStaticMarkup(doc)
|
return '<!DOCTYPE html>' + renderToStaticMarkup(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function renderJSON (url, { dir = process.cwd(), mfs } = {}) {
|
export async function renderJSON (url, { dir = process.cwd() } = {}) {
|
||||||
const path = getPath(url)
|
const path = getPath(url)
|
||||||
const component = await read(resolve(dir, '.next', '_bundles', 'pages', path), { mfs })
|
const component = await read(resolve(dir, '.next', '_bundles', 'pages', path))
|
||||||
return { component }
|
return { component }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
server/require.js
Normal file
6
server/require.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import resolve from './resolve'
|
||||||
|
|
||||||
|
export default async function requireModule (path) {
|
||||||
|
const f = await resolve(path)
|
||||||
|
return require(f)
|
||||||
|
}
|
Loading…
Reference in a new issue