kubetail-org / loadjs

A tiny async loader / dependency manager for modern browsers (899 bytes)
MIT License
2.58k stars 150 forks source link

Question about usage #64

Closed XhmikosR closed 6 years ago

XhmikosR commented 6 years ago

Hi, @amorey.

We've been using loadjs on https://www.bootstrapcdn.com/ for some time now :)

So, one thing I always wanted to restore after I added loadjs, is the SRI usage.

I can use before just fine for one script, but is there any way to do it for multiple scripts?

Here's an example, trimmed.

loadjs('/assets/js/vendor/clipboard.min.3f36881.js', 'clipboardjs', {
         // this works
         before: function(path, el) {
             el.integrity = 'xxxx';
             el.crossOrigin = 'anonymous';
          },
          numRetries: 3
      });
      loadjs(['https://code.jquery.com/jquery-3.3.1.slim.min.js', 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.bundle.min.js'], 'jquery', {
         before: function(path, el) {
             console.log(el);
             console.log(path);
             //el.integrity = 'xxxx';
             //el.crossOrigin = 'anonymous';
          },
          async: false,
          numRetries: 3
      });

Thanks in advance!

bduff9 commented 6 years ago

Are you trying to set a global before function? Or just have it always set to the same values when loading a script? Cause if its the former, correct me if I'm wrong, but I don't think there are many/any global settings currently. You could probably achieve something like this with a default settings object if this is what you want.

However, if what you are looking for is the latter, then why not you just define a function to set your values and then reference this function in the before call? Like:

function beforeLoadIsCalled (path, el) {
  el.integrity = 'xxxx';
  el.crossOrigin = 'anonymous';
}

loadjs('/assets/js/vendor/clipboard.min.3f36881.js', 'clipboardjs', {
  before: beforeLoadIsCalled,
  numRetries: 3
});

loadjs(['https://code.jquery.com/jquery-3.3.1.slim.min.js', 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.bundle.min.js'], 'jquery', {
  before: beforeLoadIsCalled,
  async: false,
  numRetries: 3
});

Does that help?

XhmikosR commented 6 years ago

Thanks for the reply @bduff9.

The problem is that each of the bundle files has a different SRI.

So, for example, I need to set a different SRI hash for the jquery file, and another one for the bootstrap file.

bduff9 commented 6 years ago

I see, that makes sense. However, I'm not sure what you are actually asking about then if you do need to set each one separately. Perhaps you could explain further what the problem is and what you need a solution to be?

XhmikosR commented 6 years ago

I might have opened this issue without trying everything first. Doing too many things at the same time, the mind gets full :)

I basically need to identify each el in before and set the appropriate SRI hash for each one. But I need to know the exact element either by order or name.

XhmikosR commented 6 years ago

So, I'll try again after resting, but I should be able to loop through the elements in before and achieve what I want.

XhmikosR commented 6 years ago

OK, this is what I ended up doing https://github.com/MaxCDN/bootstrapcdn/pull/1011/files

But I'd expect to be able to access the elements somehow. Like el[0]. Maybe I'm missing something?

amorey commented 6 years ago

Hi @XhmikosR - the before method is called once for each url so you can use the path argument to identify each script:

var jqUrl = '//code.jquery.com/jquery-3.3.1.slim.min.js',
    bsUrl = '//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js';

loadjs([jqUrl, bsUrl], {
  before: function(path, el) {
    if (path === jqUrl) el.integrity = 'sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=';
    else el.integrity = 'sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm';
    el.crossOrigin = 'anonymous';
  },
  success: function() {console.log('success: bootstrap loaded');},
  error: function(depsNotFound) {console.log('error: ' + depsNotFound);},
  async: false
});

https://jsfiddle.net/muicss/L1hysLzf/

Hope that helps. Let me know if I'm not understanding the issue correctly.

BTW - I noticed that you're using async: false and numRetries together in the pull request. Please be aware that using both features together might cause unexpected issues. Given the way the browser handles async: false, if the first script in a bundle fails and the second one loads then the retry will only apply to the first script which will cause the scripts to load out of order.

XhmikosR commented 6 years ago

Thanks for the replies, guys!

I sorted this by comparing the path in the end. It's just that I expected I would be able to access the elements like arrays.

I also dropped the numRetries; we have like 99.9% uptime so it shouldn't happen. But I might add a couple of console errors just in case, although regular users don't look at concole :)