elements.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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 isScript = common.isScript;
  8. const matches = common.matches;
  9. const getScriptName = common.getScriptName;
  10. const shouldUpdate = (options) => {
  11. if (ATTRIBUTE_PRIORITIES.indexOf(options.defaultAttribute) < 0) {
  12. throw new Error(`${CONSTANTS.PLUGIN}: invalid default attribute`);
  13. }
  14. return !(options.defaultAttribute === SYNC &&
  15. options.inline.test.length === 0 &&
  16. options.async.test.length === 0 &&
  17. options.defer.test.length === 0 &&
  18. options.module.test.length === 0);
  19. };
  20. const update = (assets, options, tags) => {
  21. const update = updateElement.bind(null, assets, options);
  22. return tags.map(update);
  23. };
  24. const updateElement = (assets, options, tag) => {
  25. return (isScript(tag))
  26. ? updateScriptElement(assets, options, tag)
  27. : tag;
  28. };
  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. };