write assets to disk

This commit is contained in:
nkzawa 2016-10-16 04:49:42 +09:00
parent e7ffb2c17d
commit 3032ade283
8 changed files with 57 additions and 61 deletions

View file

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

View file

@ -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",

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View file

@ -0,0 +1,6 @@
import resolve from './resolve'
export default async function requireModule (path) {
const f = await resolve(path)
return require(f)
}