Skip to content

Brainstorm -- synchronously setting public path for webpack bundles. #1939

@internettrans

Description

@internettrans

Problem

When consuming webpack bundles with SystemJS, it is necessary to dynamically set the webpack public path, so that webpack code splits know the base url to find other assets from. This cannot be set at build time, since the webpack bundle could be hosted on a variety of domains that aren't known at build time.

The System.resolve() api is great for giving you the url to use for your webpack public path. However, it is asynchronous when using system.js and import maps, which makes it impossible to set the public path before webpack starts processing its imports and code splits). This means that webpack will attempt to download code splits at the wrong url.

Repo that reproduces problem

See https://github.com/joeldenning/systemjs-webpack-public-path for a repo that clearly shows this problem. Of particular note is the set-public-path.js file.

Related

See "Use Case 1" in #1918 (comment). Also see webpack/webpack#8833 (comment) where it was decided that to start out webpack would continue downloading code splits via webpackJsonp instead of using SystemJS module.import().

Workaround

The url for the webpack public path can be synchronously known/calculated by monkey patching System.resolve and storing the resolved urls in a way you can access them synchronously:

const moduleMap = {};

window.getPublicPath = function(name) {
  const path = moduleMap[name];
  if (path) {
    return path.slice(0, path.lastIndexOf("/") + 1);
  } else {
    throw Error("Cannot find public path for " + name);
  }
};

const originalResolve = window.System.resolve;

window.System.resolve = function(name) {
  return originalResolve.apply(this, arguments).then(function(resolved) {
    moduleMap[name] = resolved;
    return resolved;
  });
};

Proposed solutions

Expose resolved urls via new System API
Create a resolveSync API that returns null if resolution hasn't happened or completed yet, but returns the string path if it has.

const fullUrl = System.resolveSync('main')
__webpack_public_path__ = fullUrl.slice(0, fullUrl.lastIndexOf('/') + 1)

Expose read-only import map synchronously

const fullUrl = System.getImportMap().imports.main
__webpack_public_path__ = fullUrl.slice(0, fullUrl.lastIndexOf('/') + 1)

Implement and use _context.meta.url
See _context.meta.url. This one would require the most work I think, since (it appears that) SystemJS does not currently provide _context in v3.1.6. Also, it would require a change to webpack that would make it possible for a webpack user to access _context.meta.url -- right now that's not really possible because webpack doesn't even know that _context exists.

System.register([], function(_export, _context) {
  const fullUrl = _context.meta.url
  __webpack_public_path__ = fullUrl.slice(0, fullUrl.lastIndexOf('/') + 1)
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions