WorkboxSW.mjs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. Copyright 2018 Google LLC
  3. Use of this source code is governed by an MIT-style
  4. license that can be found in the LICENSE file or at
  5. https://opensource.org/licenses/MIT.
  6. */
  7. import '../_version.mjs';
  8. const CDN_PATH = `WORKBOX_CDN_ROOT_URL`;
  9. const MODULE_KEY_TO_NAME_MAPPING = {
  10. // TODO(philipwalton): add jsdoc tags to associate these with their module.
  11. // @name backgroundSync
  12. // @memberof workbox
  13. // @see module:workbox-background-sync
  14. backgroundSync: 'background-sync',
  15. broadcastUpdate: 'broadcast-update',
  16. cacheableResponse: 'cacheable-response',
  17. core: 'core',
  18. expiration: 'expiration',
  19. googleAnalytics: 'offline-ga',
  20. navigationPreload: 'navigation-preload',
  21. precaching: 'precaching',
  22. rangeRequests: 'range-requests',
  23. routing: 'routing',
  24. strategies: 'strategies',
  25. streams: 'streams',
  26. };
  27. /**
  28. * This class can be used to make it easy to use the various parts of
  29. * Workbox.
  30. *
  31. * @private
  32. */
  33. export class WorkboxSW {
  34. /**
  35. * Creates a proxy that automatically loads workbox namespaces on demand.
  36. *
  37. * @private
  38. */
  39. constructor() {
  40. this.v = {};
  41. this._options = {
  42. debug: self.location.hostname === 'localhost',
  43. modulePathPrefix: null,
  44. modulePathCb: null,
  45. };
  46. this._env = this._options.debug ? 'dev' : 'prod';
  47. this._modulesLoaded = false;
  48. return new Proxy(this, {
  49. get(target, key) {
  50. if (target[key]) {
  51. return target[key];
  52. }
  53. const moduleName = MODULE_KEY_TO_NAME_MAPPING[key];
  54. if (moduleName) {
  55. target.loadModule(`workbox-${moduleName}`);
  56. }
  57. return target[key];
  58. },
  59. });
  60. }
  61. /**
  62. * Updates the configuration options. You can specify whether to treat as a
  63. * debug build and whether to use a CDN or a specific path when importing
  64. * other workbox-modules
  65. *
  66. * @param {Object} [options]
  67. * @param {boolean} [options.debug] If true, `dev` builds are using, otherwise
  68. * `prod` builds are used. By default, `prod` is used unless on localhost.
  69. * @param {Function} [options.modulePathPrefix] To avoid using the CDN with
  70. * `workbox-sw` set the path prefix of where modules should be loaded from.
  71. * For example `modulePathPrefix: '/third_party/workbox/v3.0.0/'`.
  72. * @param {workbox~ModulePathCallback} [options.modulePathCb] If defined,
  73. * this callback will be responsible for determining the path of each
  74. * workbox module.
  75. *
  76. * @alias workbox.setConfig
  77. */
  78. setConfig(options = {}) {
  79. if (!this._modulesLoaded) {
  80. Object.assign(this._options, options);
  81. this._env = this._options.debug ? 'dev' : 'prod';
  82. } else {
  83. throw new Error('Config must be set before accessing workbox.* modules');
  84. }
  85. }
  86. /**
  87. * Load a Workbox module by passing in the appropriate module name.
  88. *
  89. * This is not generally needed unless you know there are modules that are
  90. * dynamically used and you want to safe guard use of the module while the
  91. * user may be offline.
  92. *
  93. * @param {string} moduleName
  94. *
  95. * @alias workbox.loadModule
  96. */
  97. loadModule(moduleName) {
  98. const modulePath = this._getImportPath(moduleName);
  99. try {
  100. importScripts(modulePath);
  101. this._modulesLoaded = true;
  102. } catch (err) {
  103. // TODO Add context of this error if using the CDN vs the local file.
  104. // We can't rely on workbox-core being loaded so using console
  105. // eslint-disable-next-line
  106. console.error(
  107. `Unable to import module '${moduleName}' from '${modulePath}'.`);
  108. throw err;
  109. }
  110. }
  111. /**
  112. * This method will get the path / CDN URL to be used for importScript calls.
  113. *
  114. * @param {string} moduleName
  115. * @return {string} URL to the desired module.
  116. *
  117. * @private
  118. */
  119. _getImportPath(moduleName) {
  120. if (this._options.modulePathCb) {
  121. return this._options.modulePathCb(moduleName, this._options.debug);
  122. }
  123. // TODO: This needs to be dynamic some how.
  124. let pathParts = [CDN_PATH];
  125. const fileName = `${moduleName}.${this._env}.js`;
  126. const pathPrefix = this._options.modulePathPrefix;
  127. if (pathPrefix) {
  128. // Split to avoid issues with developers ending / not ending with slash
  129. pathParts = pathPrefix.split('/');
  130. // We don't need a slash at the end as we will be adding
  131. // a filename regardless
  132. if (pathParts[pathParts.length - 1] === '') {
  133. pathParts.splice(pathParts.length - 1, 1);
  134. }
  135. }
  136. pathParts.push(fileName);
  137. return pathParts.join('/');
  138. }
  139. }