Skip to content
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

Merged
merged 18 commits into from
Dec 31, 2016
Merged

Conversation

arunoda
Copy link
Contributor

@arunoda arunoda commented Dec 30, 2016

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

@@ -0,0 +1,22 @@
export default class JsonPagesPlugin {
Copy link
Contributor Author

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?

Copy link
Contributor

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)
Copy link
Contributor Author

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?

@rauchg rauchg mentioned this pull request Dec 30, 2016
3 tasks
resolve(pages)
})
})
}
Copy link
Contributor

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 ?

Copy link
Contributor Author

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))
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't get this?

Copy link
Contributor

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
Copy link
Contributor

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 ?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

@arunoda
Copy link
Contributor Author

arunoda commented Dec 30, 2016

@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$/, '')
Copy link
Contributor Author

@arunoda arunoda Dec 30, 2016

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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`])
Copy link
Contributor

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 ?

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Contributor Author

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')
Copy link
Contributor

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.

Copy link
Contributor Author

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)

Copy link
Contributor

@nkzawa nkzawa Dec 31, 2016

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.

Copy link
Contributor Author

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])
Copy link
Contributor

@nkzawa nkzawa Dec 31, 2016

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 ?

Copy link
Contributor Author

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)
Copy link
Contributor

@nkzawa nkzawa Dec 31, 2016

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

@nkzawa
Copy link
Contributor

nkzawa commented Dec 31, 2016

👍 amazing

@nkzawa nkzawa merged commit 165924b into vercel:master Dec 31, 2016
@arunoda arunoda deleted the gzip-on-json-pages branch December 31, 2016 13:04
compiler.plugin('after-compile', (compilation, callback) => {
const pages = Object
.keys(compilation.assets)
.filter((filename) => /^bundles\/pages.*\.js$/.test(filename))
Copy link

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.

Copy link
Contributor Author

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.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants