-
Notifications
You must be signed in to change notification settings - Fork 27.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Gzip support for JSON pages #571
Conversation
@@ -0,0 +1,22 @@ | |||
export default class JsonPagesPlugin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nkzawa This is my first webpack plugin.
Could you check whether I have done something ugly here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me 👍
|
||
while (true) { | ||
// gzip only 10 assets in parallel at a time. | ||
const currentChunk = allAssets.splice(0, 10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rauchg it didn't sounds nice to gzip all at once in parallel to me.
Is this something I don't wanna worry?
resolve(pages) | ||
}) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we don't use glob-promise
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah! I'll try to use it.
const currentChunk = allAssets.splice(0, 10) | ||
if (currentChunk.length === 0) break | ||
|
||
const promises = currentChunk.map((filePath) => gzip(filePath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be currentChunk.map(filePath)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't get this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant currentChunk.map(gzip)
const newContent = JSON.stringify({ component: content }) | ||
page.source = () => newContent | ||
page.size = () => newContent.length | ||
page.jsoned = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we change the extension to .json
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. That can be done. I'll try it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
send(req, path) | ||
.on('error', (err) => { | ||
if (err.code === 'ENOENT') { | ||
this.render404(req, res).then(resolve, reject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't resolve this
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I'll move that to here too.
] = await Promise.all([ | ||
Component.getInitialProps ? Component.getInitialProps(ctx) : {}, | ||
read(join(dir, '.next', 'bundles', 'pages', page)), | ||
read(join(dir, '.next', 'bundles', 'pages', dev ? '_error-debug' : '_error')) | ||
]) | ||
|
||
const component = JSON.parse(componentJson).component | ||
const errorComponent = JSON.parse(errorComponentJson).component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should cache the parsed result.
@nkzawa Implemented all the suggestion you made. |
@@ -41,7 +41,8 @@ webpackHotMiddlewareClient.subscribe((obj) => { | |||
const fn = handlers[obj.action] | |||
if (fn) { | |||
const data = obj.data || [] | |||
fn(...data) | |||
const route = data[0].replace(/[index]*\.json$/, '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nkzawa
I had to do this because now these files routes have .json
extension.
Do you know why this has changed?
Is there any better way to deal with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change server/hot-reloader.js
instead of this file. Maybe toRoute
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -32,7 +34,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false | |||
const defaultEntries = dev | |||
? [join(__dirname, '..', '..', 'client/webpack-hot-middleware-client')] : [] | |||
for (const p of pages) { | |||
entry[join('bundles', p)] = defaultEntries.concat([`./${p}?entry`]) | |||
entry[pagePathToJson(join('bundles', p))] = defaultEntries.concat([`./${p}?entry`]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, ideally, extensions should be changed on JsonPagesPlugin
. Is it difficult ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. That's not hard. I think that makes sense. I'll do it.
export default read | ||
export const cache = {} | ||
|
||
read.cache = cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used on hot-reloader.js
too. I think we need to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.on('error', (err) => { | ||
if (err.code === 'ENOENT') { | ||
res.statusCode = 404 | ||
res.end('Not Found') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this change is acceptable, but it'd be better if we can show the original 404.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the orginal 404, it sends a bunch of HTML too. It's okay for pages, but this is just a JSON endpoint. We don't need that. (Since user won't access these files directly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not only for JSON endpoint, but it's also for /static
. Users might access old static file URL and see 404. Anyway I think it's ok for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll work a separate PR for that.
|
||
// Delete existing json pages | ||
// Otherwise, we might keep JSON pages for deleted pages. | ||
jsonPages.forEach((pagePath) => delete compilation.assets[pagePath]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need this since compilation
instance is recreated on each build. So I think there is no json pages remaining, or is there ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Removed.
} | ||
|
||
const gzipPath = `${path}.gz` | ||
const exists = await fs.exists(gzipPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a deprecated API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah! Seems like I need to read along the Node.js API again :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like fs.access
is not recommended for this: https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback
I'll work on with renderStatic
for this to get rid of this issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nkzawa Check now.
I figured, it's safe for us to use fs.access
👍 amazing |
compiler.plugin('after-compile', (compilation, callback) => { | ||
const pages = Object | ||
.keys(compilation.assets) | ||
.filter((filename) => /^bundles\/pages.*\.js$/.test(filename)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex doesn't match the way one could wish for in windows, causing pages not to be compiled to json, causing issue #597 thus making most tests fail on windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @msand for figuring this out.
Related to #555
Fixes #568
Now we are serving JSON pages directly from the file system for most of the time. We do that with a custom webpack plugin.
So, now we could add gzip support for pages pretty quickly.
Since now we serve JSON pages directly from the filesystem, ETags are sent automatically and it fixes #568