worker.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. const versionCache = {};
  2. let currentVersion;
  3. onunhandledrejection = (e) => {
  4. throw e.reason;
  5. };
  6. onmessage = function(e) {
  7. if (e.data.version === currentVersion) {
  8. parse(e);
  9. } else {
  10. loadVersion(e.data.version).then(() => {
  11. parse(e);
  12. });
  13. }
  14. };
  15. function getDefaults() {
  16. const marked = versionCache[currentVersion];
  17. let defaults = {};
  18. if (typeof marked.getDefaults === 'function') {
  19. defaults = marked.getDefaults();
  20. delete defaults.renderer;
  21. } else if ('defaults' in marked) {
  22. for (const prop in marked.defaults) {
  23. if (prop !== 'renderer') {
  24. defaults[prop] = marked.defaults[prop];
  25. }
  26. }
  27. }
  28. return defaults;
  29. }
  30. function mergeOptions(options) {
  31. const defaults = getDefaults();
  32. const opts = {};
  33. const invalidOptions = [
  34. 'renderer',
  35. 'tokenizer',
  36. 'walkTokens',
  37. 'extensions',
  38. 'highlight',
  39. 'sanitizer',
  40. ];
  41. for (const prop in defaults) {
  42. opts[prop] = invalidOptions.includes(prop) || !(prop in options)
  43. ? defaults[prop]
  44. : options[prop];
  45. }
  46. return opts;
  47. }
  48. function parse(e) {
  49. switch (e.data.task) {
  50. case 'defaults': {
  51. postMessage({
  52. id: e.data.id,
  53. task: e.data.task,
  54. defaults: getDefaults(),
  55. });
  56. break;
  57. }
  58. case 'parse': {
  59. const marked = versionCache[currentVersion];
  60. // marked 0.0.1 had tokens array as the second parameter of lexer and no options
  61. const options = currentVersion.endsWith('@0.0.1') ? [] : mergeOptions(e.data.options);
  62. const startTime = new Date();
  63. const lexed = marked.lexer(e.data.markdown, options);
  64. const lexedList = jsonString(lexed);
  65. const parsed = marked.parser(lexed, options);
  66. const endTime = new Date();
  67. postMessage({
  68. id: e.data.id,
  69. task: e.data.task,
  70. lexed: lexedList,
  71. parsed,
  72. time: endTime - startTime,
  73. });
  74. break;
  75. }
  76. }
  77. }
  78. function jsonString(input, level) {
  79. level = level || 0;
  80. if (Array.isArray(input)) {
  81. if (input.length === 0) {
  82. return '[]';
  83. }
  84. const items = [];
  85. let i;
  86. if (!Array.isArray(input[0]) && typeof input[0] === 'object' && input[0] !== null) {
  87. for (i = 0; i < input.length; i++) {
  88. items.push(' '.repeat(2 * level) + jsonString(input[i], level + 1));
  89. }
  90. return '[\n' + items.join('\n') + '\n]';
  91. }
  92. for (i = 0; i < input.length; i++) {
  93. items.push(jsonString(input[i], level));
  94. }
  95. return '[' + items.join(', ') + ']';
  96. } else if (typeof input === 'object' && input !== null) {
  97. const props = [];
  98. for (const prop in input) {
  99. props.push(prop + ':' + jsonString(input[prop], level));
  100. }
  101. return '{' + props.join(', ') + '}';
  102. } else {
  103. return JSON.stringify(input);
  104. }
  105. }
  106. function fetchMarked(file) {
  107. return () =>
  108. fetch(file)
  109. .then((res) => res.text())
  110. .then((text) => {
  111. const g = globalThis || global;
  112. g.module = { };
  113. try {
  114. // eslint-disable-next-line no-new-func
  115. Function(text)();
  116. } catch {
  117. throw new Error(`Cannot find ${file}`);
  118. }
  119. const marked = g.marked || g.module.exports;
  120. return marked;
  121. });
  122. }
  123. function loadVersion(ver) {
  124. let promise;
  125. if (versionCache[ver]) {
  126. promise = Promise.resolve();
  127. } else {
  128. promise = import(ver + '/lib/marked.esm.js')
  129. .catch(fetchMarked(ver + '/marked.min.js'))
  130. .catch(fetchMarked(ver + '/lib/marked.umd.js'))
  131. .catch(fetchMarked(ver + '/lib/marked.js'))
  132. .then((marked) => {
  133. if (!marked) {
  134. throw Error('No marked');
  135. } else if (marked.marked) {
  136. versionCache[ver] = marked.marked;
  137. } else if (marked.default) {
  138. versionCache[ver] = marked.default;
  139. } else if (marked.lexer && marked.parser) {
  140. versionCache[ver] = marked;
  141. } else {
  142. throw new Error('Cannot find marked');
  143. }
  144. });
  145. }
  146. return promise.then(() => {
  147. currentVersion = ver;
  148. }).catch((err) => {
  149. console.error(err);
  150. throw new Error('Cannot load that version of marked');
  151. });
  152. }