hanford / next-offline

make your Next.js application work offline using service workers via Google's workbox
https://github.com/hanford/next-offline
1.59k stars 111 forks source link

Just can't get the file to serve in express, always shows a 404 #232

Open focalstrategy opened 4 years ago

focalstrategy commented 4 years ago

I'm using Next.js 9.3.3. I have this in server.js (among other things, the imports etc)

const app = next({ dev });
app.prepare()
  .then(() => {
    const server = express();
    server.get("/service-worker.js", (req, res) => {
      res.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
      res.set("Content-Type", "application/javascript");

      const sw_path = path.resolve("./.next/service-worker.js");

      console.log("serve sw: "+sw_path);
      app.serveStatic(req, res, sw_path);
    });
 });

I've restarted the server with npm run dev a few times while testing.

The path the console logs out: /Users/MYUSERNAME/DIRNAME/.next/service-worker.js is correct - if I check that file, it exists fine.

However, I always hit the next.js error page which tells me 'An error 404 occurred on server'. There are no errors on server.

Adding the Content-Type header means the error page HTML content doesn't render properly because it's sent as JS, so that part works at least…

This is driving me crazy - know it's not really this repo that's the problem, but can't for the life of me work out how to actually serve the file!

Any ideas? Thanks!

AlexWheeler commented 4 years ago

@focalstrategy I added this in another similar issue, does this solve your issue?

https://github.com/hanford/next-offline/issues/229#issuecomment-606102727

focalstrategy commented 4 years ago

That looks pretty promising - I’ll post back if this works. Thanks.

focalstrategy commented 4 years ago

@AlexWheeler

Screenshot 2020-03-31 at 21 10 42

(and with ../ like you had)

With this as my next.config.js it still gets put into the same directory, doesn't get put in right place. I've tried rm -r .next to ensure no caching is affecting it, and have tried deliberately malforming this file, and next won't build, so I'm confident the file is being used.

So, seems that this doesn't change the behaviour of the plugin at all? Very weird! 😩

Edit: also tried literally pasting

const withOffline = require('next-offline')
const config = {
  workboxOpts: {
    swDest: '../public/service-worker.js',
  },
    // rest of your config
}
module.exports = withOffline(config)

as the only thing in next.config.js and no dice, still gets put into .next.

AlexWheeler commented 4 years ago

@focalstrategy ah man. So I should also note (and should have before, sorry) that we're not using a custom server.js and just letting next use its default one. Do you need a custom server.js or are you just using it to try to get the service-worker working? If you don't need it then I'd try the above ^, but remove server.js

focalstrategy commented 4 years ago

Yeah, do need the custom server, is an existing site in production use.

I might set up a new next.js app from scratch and see if it works there, really frustrating!

DimitrisJim commented 4 years ago

Minimally trying to set this up I faced a similar issue. Instead of serving the file via the next server (which is what I assume app.serveStatic does), I sent it via express with sendFile:

// Serve service-worker.js file when requested.
server.get('/service-worker.js', (req, res) => {
  res.sendFile(join(__dirname, '.next', 'service-worker.js'));
});

got the service worker registered for me now.

elias551 commented 4 years ago

Facing the same issue (404, but the file is present) after updating from 9.1 to 9.3. Tried with the workaround given by @askwheeler with no success. hard to know why serveStatic from nextjs returns an error: https://github.com/zeit/next.js/blob/04e4c457d9df92940b875c37bea0b60479241a97/packages/next/next-server/server/next-server.ts#L1372

Couldn't debug furthermore, code is minified after that. Downgraded to 9.1, working again !

elias551 commented 4 years ago

Opened an issue in next.js https://github.com/zeit/next.js/issues/11751

guilhermebruzzi commented 4 years ago

I followed the example of now 2: https://github.com/hanford/next-offline/tree/master/packages/now2-example In my case the 404 was happening because on workboxOpts of next.config.js I created a custom exclude (overriding this plugins one) and also I wasn't setting swDest: 'static/service-worker.js' as a option and I wasn't including the now.json config that allows to serve it on the root: https://github.com/hanford/next-offline/blob/master/packages/now2-example/now.json Main part of now.json:

"rewrites": [
    {
      "source": "/service-worker.js",
      "destination": "/_next/static/service-worker.js"
    }
  ],

I still can't test it on localhost (generateInDevMode: true) without the hack of doing an yarn build after putting the swDest: '../public/service-worker.js' which is not how is supposed to be configured to work in production.

If anyone got a solution to work both in production and in localhost, tell us! :D

hanford commented 4 years ago

@guilhermebruzzi I don't have a great solution for development mode at the moment. the GenerateInDev mode was a hack

Anyways, I have this issue hoping someone else can come up with a good solution or at least a proposal

hung-phan commented 4 years ago

I think you can have sth like this

// next.config.js

  workboxOpts: {
    swDest: 'static/service-worker.js',
    runtimeCaching: [

// main.js

      // offline caching
      get("/service-worker.js", (req, res) =>
        nextApplication.serveStatic(req, res, `${process.cwd()}/.next/static/service-worker.js`)
      ),

It will be able to work. serveStatic will only check in .next/static, static, public folder atm

        // Check if .next/static, static and public are in the path.
        // If not the path is not available.
        if ((untrustedFilePath.startsWith(path_1.join(this.distDir, 'static') + path_1.sep) ||
            untrustedFilePath.startsWith(path_1.join(this.dir, 'static') + path_1.sep) ||
            untrustedFilePath.startsWith(path_1.join(this.dir, 'public') + path_1.sep)) === false) {
            return false;
        }