utils.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. "use strict";
  2. const path = require("path");
  3. const whatwgURL = require("whatwg-url");
  4. const { domSymbolTree } = require("./living/helpers/internal-constants");
  5. const SYMBOL_TREE_POSITION = require("symbol-tree").TreePosition;
  6. exports.hasWeakRefs = typeof WeakRef === "function";
  7. exports.toFileUrl = function (fileName) {
  8. // Beyond just the `path.resolve`, this is mostly for the benefit of Windows,
  9. // where we need to convert "\" to "/" and add an extra "/" prefix before the
  10. // drive letter.
  11. let pathname = path.resolve(process.cwd(), fileName).replace(/\\/g, "/");
  12. if (pathname[0] !== "/") {
  13. pathname = "/" + pathname;
  14. }
  15. // path might contain spaces, so convert those to %20
  16. return "file://" + encodeURI(pathname);
  17. };
  18. /**
  19. * Define a set of properties on an object, by copying the property descriptors
  20. * from the original object.
  21. *
  22. * - `object` {Object} the target object
  23. * - `properties` {Object} the source from which to copy property descriptors
  24. */
  25. exports.define = function define(object, properties) {
  26. for (const name of Object.getOwnPropertyNames(properties)) {
  27. const propDesc = Object.getOwnPropertyDescriptor(properties, name);
  28. Object.defineProperty(object, name, propDesc);
  29. }
  30. };
  31. /**
  32. * Define a list of constants on a constructor and its .prototype
  33. *
  34. * - `Constructor` {Function} the constructor to define the constants on
  35. * - `propertyMap` {Object} key/value map of properties to define
  36. */
  37. exports.addConstants = function addConstants(Constructor, propertyMap) {
  38. for (const property in propertyMap) {
  39. const value = propertyMap[property];
  40. addConstant(Constructor, property, value);
  41. addConstant(Constructor.prototype, property, value);
  42. }
  43. };
  44. function addConstant(object, property, value) {
  45. Object.defineProperty(object, property, {
  46. configurable: false,
  47. enumerable: true,
  48. writable: false,
  49. value
  50. });
  51. }
  52. exports.mixin = (target, source) => {
  53. const keys = Reflect.ownKeys(source);
  54. for (let i = 0; i < keys.length; ++i) {
  55. if (keys[i] in target) {
  56. continue;
  57. }
  58. Object.defineProperty(target, keys[i], Object.getOwnPropertyDescriptor(source, keys[i]));
  59. }
  60. };
  61. let memoizeQueryTypeCounter = 0;
  62. /**
  63. * Returns a version of a method that memoizes specific types of calls on the object
  64. *
  65. * - `fn` {Function} the method to be memozied
  66. */
  67. exports.memoizeQuery = function memoizeQuery(fn) {
  68. // Only memoize query functions with arity <= 2
  69. if (fn.length > 2) {
  70. return fn;
  71. }
  72. const type = memoizeQueryTypeCounter++;
  73. return function (...args) {
  74. if (!this._memoizedQueries) {
  75. return fn.apply(this, args);
  76. }
  77. if (!this._memoizedQueries[type]) {
  78. this._memoizedQueries[type] = Object.create(null);
  79. }
  80. let key;
  81. if (args.length === 1 && typeof args[0] === "string") {
  82. key = args[0];
  83. } else if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "string") {
  84. key = args[0] + "::" + args[1];
  85. } else {
  86. return fn.apply(this, args);
  87. }
  88. if (!(key in this._memoizedQueries[type])) {
  89. this._memoizedQueries[type][key] = fn.apply(this, args);
  90. }
  91. return this._memoizedQueries[type][key];
  92. };
  93. };
  94. function isValidAbsoluteURL(str) {
  95. return whatwgURL.parseURL(str) !== null;
  96. }
  97. exports.isValidTargetOrigin = function (str) {
  98. return str === "*" || str === "/" || isValidAbsoluteURL(str);
  99. };
  100. exports.simultaneousIterators = function* (first, second) {
  101. for (;;) {
  102. const firstResult = first.next();
  103. const secondResult = second.next();
  104. if (firstResult.done && secondResult.done) {
  105. return;
  106. }
  107. yield [
  108. firstResult.done ? null : firstResult.value,
  109. secondResult.done ? null : secondResult.value
  110. ];
  111. }
  112. };
  113. exports.treeOrderSorter = function (a, b) {
  114. const compare = domSymbolTree.compareTreePosition(a, b);
  115. if (compare & SYMBOL_TREE_POSITION.PRECEDING) { // b is preceding a
  116. return 1;
  117. }
  118. if (compare & SYMBOL_TREE_POSITION.FOLLOWING) {
  119. return -1;
  120. }
  121. // disconnected or equal:
  122. return 0;
  123. };
  124. /* eslint-disable global-require */
  125. exports.Canvas = null;
  126. let canvasInstalled = false;
  127. try {
  128. require.resolve("canvas");
  129. canvasInstalled = true;
  130. } catch (e) {
  131. // canvas is not installed
  132. }
  133. if (canvasInstalled) {
  134. const Canvas = require("canvas");
  135. if (typeof Canvas.createCanvas === "function") {
  136. // In browserify, the require will succeed but return an empty object
  137. exports.Canvas = Canvas;
  138. }
  139. }