49

ES6's modules are based on a flexible loader architecture (although the standard is not final, so ...).

Does this mean ES6's loader, based on system.js, can load all assets? I.e. CSS, HTML, Images, Text, .. files of any sort?

I ask because I'm starting to use WebComponents & Polymer which have their own HTML import, and implementing them with ES6, which has its own import/loader (system.js).

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
backspaces
  • 3,802
  • 6
  • 34
  • 58

3 Answers3

22

If you use SystemJS then you can load assets by using plugins:

// Will generate a <link> element for my/file.css
System.import('my/file.css!')
    .then(() => console.log('CSS file loaded'));

Alternatively, you can use an import statement. This will make sure that the CSS file is loaded before the your script executes:

import 'my/file.css!';

Finally, you can retrieve the contents of the file using the text plugin:

import cssContent from 'my/file.css!text';
console.log('CSS file contents: ', cssContent);

Another option is to add the css as a dependency in JSPM config files. Basically adding the dependency in the specific package .json file and then running 'jspm install' which will add the override to package.js & jspm.config.js

urish
  • 8,943
  • 8
  • 54
  • 75
  • 21
    This wont be forward-compatible though, right? Part of the idea in using System.js for getting to write ES6 code now. If the code can't ever be used without System.js (like when ES6 adoption is common enough to drop it), it isn't really ES6 code. – Kyeotic May 25 '15 at 21:41
19

I know you mentioned ES6 modules, but as that does not appear to support CSS natively, if you're looking for something standards-based to load resources dynamically and wish for something possibly somewhat less unpleasant than XMLHttpRequest, the new Fetch API might be used like this:

var myStylesheets = ['myStyles1.css', 'myStyles2.css'];

Promise.all(myStylesheets.map(url => fetch(url))).
    then(arr => Promise.all(arr.map(url => url.text()))).
    then(arr => {
        var style = document.createElement('style');
        style.textContent = arr.reduce(
            (prev, fileContents) => prev + fileContents, ''
        );
        document.head.appendChild(style);
    }).then(() => {
        // Do whatever now
    });

This is even cleaner with async functions:

var myStylesheets = ['myStyles1.css', 'myStyles2.css'];

async function loadStyles(stylesheets) {
    let arr = await Promise.all(stylesheets.map(url => fetch(url)))
    arr = await Promise.all(arr.map(url => url.text()))
    const style = document.createElement('style')
    style.textContent = arr.reduce(
        (prev, fileContents) => prev + fileContents, ''
    )
    document.head.appendChild(style);
    // Do whatever now
}

loadStyles(myStylesheets)

For other resource types, you can use the blob() method for images, and pending ES6 modules support, eval() for JavaScript, etc.

trusktr
  • 44,284
  • 53
  • 191
  • 263
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
1

I faced that question 8 years later :D

In my option, I think is interesting we can use the import.meta.url to be able to load assets relatively in a web scenario. That makes it more similar that esmodules import way.

In addition to the Zamir answer, if the assets/styles are public and we do not have to handle their content, there is no need to use fetch or any ajax at all. Just use HTML tags: link, img, scripts ...

eg: Loading a sibling css file relatively

mywebsite.com
  assets
      a-component.css 
      a-component.js
function loadStyle(styleUrl) {
   const linkEl = document.createElement("link");
   linkEl.setAttribute("rel", "stylesheet");
   linkEl.setAttribute("href", styleUrl);
   document.head.append(linkEl);
}

function load(currentPath, relativePath) {
  // [https:,,,mywebsite.com,assets,a-component.js]
  const parts = currentPath.split('/');
  // [https:,,,mywebsite.com,assets]
  parts.pop();
  // [https:,,,mywebsite.com,assets,./a-component.css]
  parts.push(relativePath);
  // https://mywebsite.com/assets/a-component.css
  const absoluteUrl = parts.join('/').replace('/./', '/');
  // css
  const extension = relativePath.split('.').pop();

  switch(extension) {
    case 'css':
       loadStyle(absoluteUrl);
     break;
     // others types
  }
  
}

// usage in a-component.js
// https://mywebsite.com/assets/a-component.js
load(import.meta.url, "./a-component.css");

That did help me out in a pure JS Legacy personal project without SystemJS.

Angel Fraga Parodi
  • 750
  • 10
  • 20