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

Getting "bad-precaching-response " error after service worker registeration is successful #190

Open riksphp opened 5 years ago

riksphp commented 5 years ago

🎉 Thanks for taking the time to contribute to next-offline!

It is highly appreciated that you take the time to help improve next-offline. We appreciate it if you would take the time to write up a bug report or feature request.

Sadly, if we don't receive enough information, or the issue/feature request doesn't align well with our roadmap, we might respectfull thank you for your time, and close the issue.

Bug fixes and documentation fixes are welcome.

In the case of a bug report 🐞

Please consider the following items when filing a bug report:

In the case of a feature/component request ✍️

Please consider the following items when filing a feature request:

See the contributing guide for more details.

Respect earns Respect 👏

Please respect our Code of Conduct, in short:

riksphp commented 5 years ago

Hi,

In my project, I am using NextJS+KOA+Apollo. My nextJS app is inside client in root directory.

Nextjs app is inside client directory. koa server is inside server directory.

when i am building the app via below command: next build client && tsc --project tsconfig.server.json

it creates a build directory inside client for nextjs and dist directory at the top level for koa server.

i run the code in production via below command NODE_ENV=production node dist/server/index.js

ISSUE Service worker is getting registered properly. But I am getting below error: PrecacheController.mjs:194 Uncaught (in promise) bad-precaching-response: bad-precaching-response :: [{"url":"https://my-domain/_next/bo.svg?__WB_REVISION__=e02afe0476bb357aebde18136fda06e0","status":404}] at l.o (https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-precaching.prod.js:1:1749) at async Promise.all (index 0) at async l.install (https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-precaching.prod.js:1:1221) Screen Shot 2019-09-11 at 12 58 45 PM

Below is my build file that gets generated: Screen Shot 2019-09-11 at 1 03 03 PM

tsconfig.server.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "dist",
    "target": "es2017",
    "isolatedModules": false,
    "noEmit": false
  },
  "include": ["server/**/*.ts"]
}

Below is my next.config.js (inside client direcotry)

/* eslint-disable @typescript-eslint/no-var-requires */
const withPlugins = require("next-compose-plugins");
const offline = require("next-offline");
const pino = require("next-pino");
const withTypescript = require("@zeit/next-typescript");
const withCSS = require("@zeit/next-css");
const withLess = require("@zeit/next-less");
const Dotenv = require("dotenv-webpack");
const path = require("path");
const _ = require("lodash");

const nextConfig = {
  distDir: "build",
  webpack(config) {
    config.module.rules.push({
      test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
      use: {
        loader: "url-loader",
        options: {
          limit: 100000,
          name: "[name].[ext]",
        },
      },
    });
    config.plugins.push(
      new Dotenv({
        path: path.resolve(process.cwd(), ".env"),
        systemvars: true,
      }),
    );
    return config;
  },
  // overwrites values given in the .env file with the current
  // process.env value
  env: _.omitBy(
    {
      GRAPHQL_SERVER: process.env.GRAPHQL_SERVER,
    },
    _.isUndefined,
  ),
  workboxOpts: {
    globPatterns: ["static/**/*"],
    globDirectory: "client",
    runtimeCaching: [
      {
        urlPattern: /^https?.*/,
        handler: "NetworkFirst",
        options: {
          cacheName: "offlineCache",
          expiration: {
            maxEntries: 200,
          },
        },
      },
    ],
  },
};

const cssConfig = {
  cssModules: true,
  cssLoaderOptions: {
    importLoaders: 1,
    localIdentName: "[local]",
  },
};

const lessConfig = cssConfig;

module.exports = withPlugins(
  [
    [offline],
    [pino],
    [withTypescript],
    [withCSS, cssConfig],
    [withLess, lessConfig],
  ],
  nextConfig,
);

And below is my file to start koa server


import Router from "koa-router";
 const server = new Koa();
const dev = !["production", "staging"].includes(process.env.NODE_ENV || "");
const app = next({ dir: "./client", dev });
const publicRouter = new Router();

const handle = app.getRequestHandler();

publicRouter.get("/service-worker.js", async ctx => {
      const pathname = await join(
        __dirname,
        "../../../client/build",
        "service-worker.js",
      );
      ctx.body = await app.serveStatic(ctx.req, ctx.res, pathname);
      ctx.respond = false;
    });

publicRouter.get("*", async ctx => {
      if (!ctx.path.match(/graphql/)) {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
      }
    });

server.use(async (ctx, next) => {
      ctx.res.statusCode = 200;
      await next();
    });
server.use(publicRouter.routes()).use(publicRouter.allowedMethods());
 server.listen({ port: 3000 });
riksphp commented 5 years ago

I have done a dirty fix for now. I am not sure how to handle it properly. I will really appreciate if anyone can put forth their view on this.

As bo.svg, firfox.svg, all these static files are throwing 404, Ex - (/_next/bo.svg?__WB_REVISION__=e02afe0476bb357aebde18136fda06e0) in my file to start koa server, added a condition to check this URL and serve static file from build directory like below:

publicRouter.get("*", async ctx => {
      if (ctx.path.match(/\_next/) && ctx.path.match(/\.svg/)) {
        const pathname = await join(
          __dirname,
          "../../../client/build",
          ctx.path.replace("_next/", ""),
        );
        ctx.body = await app.serveStatic(ctx.req, ctx.res, pathname);
        ctx.respond = false;
      } else if (!ctx.path.match(/graphql/)) {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
      }
    });

It served my prupose for now, but not sure how to handle this properly.

khanbaba commented 5 years ago

We are using nextjs + next-offline + express. we had the same problem, and the problem was because workbox-precacher try to get some files from '_next/' directory which the express does not serve them. I have add two directory for static files to be served by express: one is 'static' and another is '_next' as follow: app.use('/static', express.static(path.join(__dirname, '/static'))) app.use('/_next', express.static(path.join(__dirname, '/_next'))) *Note: _next is my distDir.

hanford commented 5 years ago

@khanbaba which version of next-offline are you using?

khanbaba commented 5 years ago

@khanbaba which version of next-offline are you using?

version 4.0.0

kmjennison commented 5 years ago

Possibly related: I was getting a 404 for _error.js in development, which caused the service worker installation to fail. The list of files to precache in service-worker.js included http://localhost:3000/_next/static/development/pages/next/dist/pages/_error.js, which doesn't exist.

Defining a custom _error.js file in the pages directory fixed the problem. After that, the service worker no longer included the nonexistent _error.js file above.

This was using Next v9.0.6 and next-offline v5.0.0-beta.4.

hanford commented 5 years ago

yeah @khanbaba could you try out the 5.0.0 beta? It's using a new version of workbox which is what is powering next-offline.

khanbaba commented 5 years ago

@hanford yes. I have tested next-offline 5.0.0-beta.4. The problem not solved. thanks.

chrisciampoli commented 5 years ago

Hitting the same issue when deploying to production (staging). Transitive builds dont seem to have this issue.

Moikapy commented 4 years ago

Possibly related: I was getting a 404 for _error.js in development, which caused the service worker installation to fail. The list of files to precache in service-worker.js included http://localhost:3000/_next/static/development/pages/next/dist/pages/_error.js, which doesn't exist.

Defining a custom _error.js file in the pages directory fixed the problem. After that, the service worker no longer included the nonexistent _error.js file above.

This was using Next v9.0.6 and next-offline v5.0.0-beta.4.

I created a _error.js page and this kept the error from logging in v5.0.0-beta.9

ccambournac commented 4 years ago

Don't know if it is the good way to go to but I had the same issue and added precache manifest transformation to workboxOpts in order to remove unwanted URL prefixes: https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.ManifestTransform Like this:

manifestTransforms: [
    // Basic transformation to remove a certain URL:
    (originalManifest) => {
        const manifest = originalManifest.map(entry => {
            entry.url = entry.url.replace(/^\/?_next\/public\//, '');
            return entry;
        });
        // Optionally, set warning messages.
        const warnings = [];
        return {manifest, warnings};
    }
]

I suspect however an initial misconfiguration from my side. Using next-offline 5.0.0-beta.9

cryptiklemur commented 4 years ago

looks like transformManifest is gone now, from next-offline? Also, i have an _error.tsx and am still getting the error.

Seems like i'm actually getting pre-cache requests for an older build, though i'm not sure how thats possible on Now 2

huggler commented 4 years ago

I have the same problem! I register my serviceWorker perfectly, but if i generate new version, nextjs not get the new version...and broken the page

kaaax0815 commented 3 years ago

My Error: Uncaught (in promise) bad-precaching-response: bad-precaching-response :: [{"url":"http://localhost:3000/autostatic/3Om9AVnlaeKY0VFubP78s/_buildManifest.js","status":404}] It tries to get the files from autostatic instead of _next/static. Can I somehow change the path?

Setting future.webpack to false fixed it

icflorescu commented 3 years ago

@kaaax0815 I have the same issue and I think it's fixable it by adding this next-offline option:

withOffline({
  workboxOpts: {
    // ...
    modifyURLPrefix: { 'autostatic/': '_next/static/' },
  }
})

Found it at https://github.com/hanford/next-offline/issues/263#issuecomment-738155607. See if it works for you.

kaaax0815 commented 3 years ago

@icflorescu but why does webpack change the behaviour of workbox? Could you please look into #278 ? And do you also have depraction warnings?