GoogleChrome / workbox

📦 Workbox: JavaScript libraries for Progressive Web Apps
https://developers.google.com/web/tools/workbox/
MIT License
12.33k stars 814 forks source link

Error: Can't resolve 'workbox-precaching/createHandlerForURL' [webpack 4] #2633

Closed AlonsoK28 closed 4 years ago

AlonsoK28 commented 4 years ago

Library Affected: workbox-precaching/createHandlerForURL

Browser & Platform: All browsers for me

Issue or Feature Request Description: I´m triying to create cache-first service worker config to save some assets used in my project

Following this example https://gist.github.com/jeffposnick/fc761c06856fa10dbf93e62ce7c4bd57 i´m getting the following error ERROR in ./service-worker.js Module not found: Error: Can't resolve 'workbox-precaching/createHandlerForURL' in 'C:\xampp\htdocs\my-project'

I´m using mainly using webpack 4 in my project but I see that I have the lastest workbox 5 installed, this is correct? Or I need to use workbox 4 dependencies?

When reporting bugs, please include relevant JavaScript Console logs and links to public URLs at which the issue could be reproduced.

This is my webpack 4 config

webpack.config.prod.js

//webpack
const path = require('path');
const webpack = require('webpack');
const RemovePlugin = require('remove-files-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default;
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const { InjectManifest } = require('workbox-webpack-plugin');
//filesystem paths
const OUTPUT_PATH = path.resolve(__dirname, 'public/');
const WEBPACK_PATH = path.resolve(__dirname, 'assets', 'js', 'webpack');
const VIEWS_PATH = path.resolve(__dirname, 'modulos', 'views');
const SERVICE_WORKER_PATH = path.resolve(__dirname); 
const PUBLIC_PATH = 'public/';
module.exports = merge(common,{
    mode: 'production',
    devtool: 'none',
    output: {
      filename: '[name].bundle.[chunkhash].js',
      chunkFilename: 'lazy-modules/[name].bundle.[chunkhash].js',  //used for lazy-modules
      path: OUTPUT_PATH,
      publicPath: PUBLIC_PATH
    },
    plugins:[
      new RemovePlugin({
        after: {
          include: [
            `${WEBPACK_PATH}/dist`
          ]
        }
      }),
      new webpack.ProvidePlugin({
                $: 'jquery',
           jQuery: 'jquery'
      }),
      new HtmlWebpackPlugin({
          filename: `${VIEWS_PATH}/index.php`,
          template: `${VIEWS_PATH}/index.php`,
          chunks: ['index', 'vendor', 'lib']
      }),
      new InjectManifest({
        swSrc: `${SERVICE_WORKER_PATH}/service-worker.js`,
        swDest: 'service-worker.js',
        // Any other config if needed.
      }),
      new ScriptExtHtmlWebpackPlugin({
          sync: 'important',
          defaultAttribute: 'defer'
      }),
      //mini-css
      new OptimizeCssAssetsPlugin({
          assetNameRegExp: /\.css$/g,
          cssProcessor: require('cssnano'),
          cssProcessorPluginOptions: {
              preset: ['default', { discardComments: { removeAll: true } }],
          },
          canPrint: true
      }),
      new MiniCssExtractPlugin({
          // Options similar to the same options in webpackOptions.output
          // all options are optional
          filename: 'css/[name].style.bundle.[contenthash].css',
          chunkFilename: 'lazy-modules/[name].[contenthash].css',
          ignoreOrder: false, // Enable to remove warnings about conflicting order
      }),
      new HTMLInlineCSSWebpackPlugin({
        filter(fileName) {
          return !fileName.includes('lazy-modules');
        },
      })],
      optimization: {
        minimize: true,
        minimizer: [
          new TerserPlugin({
            test: /\.js(\?.*)?$/i,
            terserOptions: {
              output: {
                comments: false,
              },
            },
            extractComments: false,
          }),
        ],
    }
});

webpack.comon.js

//webpack
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const RemovePlugin = require('remove-files-webpack-plugin');
//filesystem paths
const PUBLIC_PATH = 'public/';
const WEBPACK_PATH = path.resolve(__dirname, 'assets', 'js', 'webpack');
const SYSTEM_PATH = path.resolve(__dirname, 'modulos', 'views', 'system');
module.exports = merge(common,{
    mode: 'development',
    // devtool: 'source-map',
    node: {
        fs: 'empty'
    },
    devServer: {
        port: 5050,
        contentBase: WEBPACK_PATH
    },
  plugins: [
        new RemovePlugin({
          after: {
            include: [
              PUBLIC_PATH
            ]
          }
        }),
        new webpack.WatchIgnorePlugin([
          /\.php$/,
          /\.html$/,
        ]),
        new MiniCssExtractPlugin()
    ]
});

service-worker.js

// Add any other logic here as needed.

import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { CacheFirst } from 'workbox-strategies/CacheFirst';
import { createHandlerForURL } from 'workbox-precaching/createHandlerForURL';
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
import { NavigationRoute } from 'workbox-routing/NavigationRoute';
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute } from 'workbox-routing/registerRoute';

precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  new NavigationRoute(createHandlerForURL('index.html'))
);

registerRoute(/^https:\/\/m.media-amazon.com\/images/, new CacheFirst({
  cacheName: 'amazon-images',
  matchOptions: {
    ignoreVary: true,
  },
  plugins: [new ExpirationPlugin({
    maxEntries: 500,
    maxAgeSeconds: 63072e3,
    purgeOnQuotaError: true,
  }), new CacheableResponsePlugin({
    statuses: [0, 200]
  })]
}));

registerRoute(/^https:\/\/res.cloudinary.com\/my-project\/image/, new CacheFirst({
  cacheName: 'product-images',
  matchOptions: {
    ignoreVary: true,
  },
  plugins: [new ExpirationPlugin({
    maxEntries: 500,
    maxAgeSeconds: 63072e3,
    purgeOnQuotaError: true,
  }), new CacheableResponsePlugin({
    statuses: [0, 200]
  })]
}));

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

package.json

{
  "name": "my-project",
  "version": "2.6.0",
  "description": "my-projectwebpack 4 project",
  "main": "index.js",
  "scripts": {
    "prod": "webpack --config webpack.prod.js",
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/AlonsoK28/my-project"
  },
  "author": "alonsokyoyama",
  "license": "ISC",
  "dependencies": {
    "@haikel/min-captcha": "^1.4.1",
    "date-fns": "^2.16.1",
    "jquery": "3.4.1",
    "lazysizes": "^5.2.2",
    "remove-files-webpack-plugin": "^1.4.0",
    "rxjs": "^6.5.5",
    "script-ext-html-webpack-plugin": "^2.1.4",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0",
    "webpack-merge": "^4.2.2",
    "workbox-cacheable-response": "^5.1.4",
    "workbox-expiration": "^5.1.4",
    "workbox-precaching": "^5.1.4",
    "workbox-routing": "^5.1.4",
    "workbox-strategies": "^5.1.4",
    "workbox-webpack-plugin": "^5.1.4"
  },
  "engines": {
    "node": ">=10.0.0 <=10.20.0"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/preset-env": "^7.9.0",
    "@fortawesome/fontawesome-free": "^5.13.0",
    "@fortawesome/fontawesome-svg-core": "^1.2.28",
    "@fortawesome/free-brands-svg-icons": "^5.13.0",
    "@fortawesome/free-regular-svg-icons": "^5.13.0",
    "@fortawesome/free-solid-svg-icons": "^5.13.0",
    "babel-loader": "^8.1.0",
    "babel-plugin-transform-imports": "^2.0.0",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.5.0",
    "expose-loader": "^0.7.5",
    "file-loader": "^4.3.0",
    "html-inline-css-webpack-plugin": "^1.8.0",
    "html-webpack-plugin": "^3.2.0",
    "image-webpack-loader": "^5.1.0",
    "json-loader": "^0.5.7",
    "mini-css-extract-plugin": "^0.8.2",
    "modernizr": "^3.9.1",
    "modernizr-loader": "^1.0.1",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "terser-webpack-plugin": "^4.2.0",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "webpack": "^4.42.1",
    "webpack-bundle-analyzer": "^3.7.0",
    "webpack-modernizr-loader": "^5.0.0"
  }
}

index.js

/**
 * @author Carlos Alonso Casales Ortega <calonso011@yahoo.com.mx>
 * webpack 4 'locahost/my-project' module.
 * @module index
 */
import { lazyModulesSelectors as selectors, amazonAdTypes as types } from 'libreriaGeneral';
import { backToTop, unveilCommon } from 'common'; 
//fontawesome
import { library, dom } from '@fortawesome/fontawesome-svg-core';
import { faAngleLeft, faAngleRight, faTrophy, faCertificate } from '@fortawesome/free-solid-svg-icons'; //fa
import { faPaperPlane as farPaperPlane} from '@fortawesome/free-regular-svg-icons'; //far
import fontAwesomeComun from 'fontawesome.5.common';

//css
import '../../css/lib/responsive_bootstrap_carousel_mega_min.css';
import '../../css/lib/animate.min.css';

import '@serviceworker/service-worker.js'; //<-- service worker is imported here to my entry module

//other code..
TheForsakenSpirit commented 4 years ago

@AlonsoK28 Hi! First. In latest version of workbox createHandlerForURL renamed to createHandlerBoundToURL.

Than check this. Service worker must be a separate file and you need register this file as service worker. (Basically injectManifest create your service-worker file and you just need correct register him)

jeffposnick commented 4 years ago

Thanks for answering the question, @TheForsakenSpirit!

There is more info on the new method name at https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-v4#navigation_route_changes

AlonsoK28 commented 4 years ago

@AlonsoK28 Hi! First. In latest version of workbox createHandlerForURL renamed to createHandlerBoundToURL.

Than check this. Service worker must be a separate file and you need register this file as service worker. (Basically injectManifest create your service-worker file and you just need correct register him)

Thanks for your answer

Im still triying to configure the SW on my project

Ive added the following

Register the SW

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function () {
    navigator.serviceWorker.register('service-worker.js');
  });
}

service-worker.js changed function name to createHandlerBoundToURL

import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { CacheFirst } from 'workbox-strategies/CacheFirst';
import { createHandlerBoundToURL } from 'workbox-precaching/createHandlerBoundToURL'; //<--renamed from createHandlerForURL
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
import { NavigationRoute } from 'workbox-routing/NavigationRoute';
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute } from 'workbox-routing/registerRoute';

precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  new NavigationRoute(createHandlerBoundToURL('index')) //<--renamed from createHandlerForURL
);

But I got this error when compile to production

Error in console

**Uncaught (in promise) TypeError: Failed to register a ServiceWorker for scope ('http://localhost/my-project/') with script ('http://localhost/my-project/service-worker.js'): ServiceWorker script evaluation failed**

and my service-worker.js is this

// Add any other logic here as needed.

import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { CacheFirst } from 'workbox-strategies/CacheFirst';
import { createHandlerBoundToURL } from 'workbox-precaching/createHandlerBoundToURL';
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
import { NavigationRoute } from 'workbox-routing/NavigationRoute';
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute } from 'workbox-routing/registerRoute';

precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  new NavigationRoute(createHandlerBoundToURL('index'))
);

registerRoute(/^https:\/\/m.media-amazon.com\/images/, new CacheFirst({
  cacheName: 'amazon-images',
  matchOptions: {
    ignoreVary: true,
  },
  plugins: [new ExpirationPlugin({
    maxEntries: 500,
    maxAgeSeconds: 63072e3,
    purgeOnQuotaError: true,
  }), new CacheableResponsePlugin({
    statuses: [0, 200]
  })]
}));

registerRoute(/^https:\/\/res.cloudinary.com\/my-project\/image/, new CacheFirst({
  cacheName: 'product-images',
  matchOptions: {
    ignoreVary: true,
  },
  plugins: [new ExpirationPlugin({
    maxEntries: 500,
    maxAgeSeconds: 63072e3,
    purgeOnQuotaError: true,
  }), new CacheableResponsePlugin({
    statuses: [0, 200]
  })]
}));

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});
TheForsakenSpirit commented 4 years ago

Hmm... I don`t see any problems with your code. @AlonsoK28 Can you try to eval your service worker code to see detailed problem?

I guess there is an problem with compilation and injection point related to different string marks " or ' .

jeffposnick commented 4 years ago

@AlonsoK28, please make sure you're following the steps in https://developers.google.com/web/tools/workbox/guides/using-bundlers to bundle up the service worker code, as the unbundled JS module imports can't be used natively in a service worker.