elements.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. 'use strict';
  2. const CONSTANTS = require('./constants.js');
  3. const SYNC = 'sync';
  4. const ATTRIBUTE_PRIORITIES = [SYNC, 'async', 'defer'];
  5. const common = require('./common.js');
  6. const debug = common.debug;
  7. const matches = common.matches;
  8. const getScriptName = common.getScriptName;
  9. const shouldUpdate = (options) => {
  10. if (ATTRIBUTE_PRIORITIES.indexOf(options.defaultAttribute) < 0) {
  11. throw new Error(`${CONSTANTS.PLUGIN}: invalid default attribute`);
  12. }
  13. return !(options.defaultAttribute === SYNC &&
  14. options.inline.test.length === 0 &&
  15. options.async.test.length === 0 &&
  16. options.defer.test.length === 0 &&
  17. options.module.test.length === 0);
  18. };
  19. const update = (assets, options, tags) => {
  20. const update = updateElement.bind(null, assets, options);
  21. return tags.map(update);
  22. };
  23. const updateElement = (assets, options, tag) => {
  24. return (isScript(tag))
  25. ? updateScriptElement(assets, options, tag)
  26. : tag;
  27. };
  28. const isScript = (tag) => tag.tagName === 'script';
  29. const updateScriptElement = (assets, options, tag) => {
  30. debug(`${CONSTANTS.EVENT}: processing <script> element: ${JSON.stringify(tag)}`);
  31. return (isInline(options, tag))
  32. ? replaceWithInlineElement(assets, options, tag)
  33. : updateSrcElement(options, tag);
  34. };
  35. const isInline = (options, tag) =>
  36. matches(getScriptName(options, tag), options.inline.test);
  37. const replaceWithInlineElement = (assets, options, tag) => {
  38. const scriptName = getScriptName(options, tag);
  39. const asset = assets[scriptName];
  40. if (!asset) throw new Error(`${CONSTANTS.PLUGIN}: no asset with href '${scriptName}'`);
  41. const newTag = {
  42. tagName: 'script',
  43. closeTag: true,
  44. innerHTML: asset.source()
  45. };
  46. debug(`${CONSTANTS.PLUGIN}: replaced by: ${JSON.stringify(newTag)}`);
  47. return newTag;
  48. };
  49. const updateSrcElement = (options, tag) => {
  50. const scriptName = getScriptName(options, tag);
  51. // select new attribute, if any, by priority
  52. let newAttribute;
  53. ATTRIBUTE_PRIORITIES.some(attribute => {
  54. if (matches(scriptName, options[attribute].test)) {
  55. newAttribute = attribute;
  56. return true;
  57. }
  58. });
  59. if (!newAttribute) newAttribute = options.defaultAttribute;
  60. if (newAttribute !== SYNC) {
  61. tag.attributes[newAttribute] = true;
  62. }
  63. // possibly overwrite existing type attribute
  64. if (matches(scriptName, options.module.test)) {
  65. tag.attributes.type = 'module';
  66. }
  67. debug(`${CONSTANTS.PLUGIN}: updated to: ${JSON.stringify(tag)}`);
  68. return tag;
  69. };
  70. module.exports = {
  71. shouldUpdate,
  72. update
  73. };