editor-worker.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /* global createWorkerApi */// worker-util.js
  2. 'use strict';
  3. (() => {
  4. const hasCurlyBraceError = warning =>
  5. warning.text === 'Unnecessary curly bracket (CssSyntaxError)';
  6. let sugarssFallback;
  7. /** @namespace EditorWorker */
  8. createWorkerApi({
  9. async csslint(code, config) {
  10. require(['/js/csslint/parserlib', '/js/csslint/csslint']); /* global CSSLint */
  11. return CSSLint
  12. .verify(code, config).messages
  13. .map(m => Object.assign(m, {rule: {id: m.rule.id}}));
  14. },
  15. getCssPropsValues() {
  16. require(['/js/csslint/parserlib']); /* global parserlib */
  17. const {
  18. css: {Colors, GlobalKeywords, Properties},
  19. util: {describeProp},
  20. } = parserlib;
  21. const namedColors = Object.keys(Colors);
  22. const rxNonWord = /(?:<.+?>|[^-\w<(]+\d*)+/g;
  23. const res = {};
  24. // moving vendor-prefixed props to the end
  25. const cmp = (a, b) => a[0] === '-' && b[0] !== '-' ? 1 : a < b ? -1 : a > b;
  26. for (const [k, v] of Object.entries(Properties)) {
  27. if (typeof v === 'string') {
  28. let last = '';
  29. const uniq = [];
  30. // strip definitions of function arguments
  31. const desc = describeProp(v).replace(/([-\w]+)\(.*?\)/g, 'z-$1');
  32. const descNoColors = desc.replace(/<named-color>/g, '');
  33. // add a prefix to functions to group them at the end
  34. const words = descNoColors.split(rxNonWord).sort(cmp);
  35. for (let w of words) {
  36. if (w.startsWith('z-')) w = w.slice(2) + '(';
  37. if (w !== last) uniq.push(last = w);
  38. }
  39. if (desc !== descNoColors) uniq.push(...namedColors);
  40. if (uniq.length) res[k] = uniq;
  41. }
  42. }
  43. return {own: res, global: GlobalKeywords};
  44. },
  45. getRules(linter) {
  46. return ruleRetriever[linter](); // eslint-disable-line no-use-before-define
  47. },
  48. metalint(code) {
  49. require(['/js/meta-parser']); /* global metaParser */
  50. const result = metaParser.lint(code);
  51. // extract needed info
  52. result.errors = result.errors.map(err => ({
  53. code: err.code,
  54. args: err.args,
  55. message: err.message,
  56. index: err.index,
  57. }));
  58. return result;
  59. },
  60. async stylelint(opts) {
  61. require(['/vendor/stylelint-bundle/stylelint-bundle.min']); /* global stylelint */
  62. try {
  63. let res;
  64. let pass = 0;
  65. /* sugarss is used for stylus-lang by default,
  66. but it fails on normal css syntax so we retry in css mode. */
  67. const isSugarSS = opts.syntax === 'sugarss';
  68. if (sugarssFallback && isSugarSS) opts.syntax = sugarssFallback;
  69. while (
  70. ++pass <= 2 &&
  71. (res = (await stylelint.lint(opts)).results[0]) &&
  72. isSugarSS && res.warnings.some(hasCurlyBraceError)
  73. ) sugarssFallback = opts.syntax = 'css';
  74. delete res._postcssResult; // huge and unused
  75. return res;
  76. } catch (e) {
  77. delete e.postcssNode; // huge, unused, non-transferable
  78. throw e;
  79. }
  80. },
  81. });
  82. const ruleRetriever = {
  83. csslint() {
  84. require(['/js/csslint/csslint']);
  85. return CSSLint.getRuleList().map(rule => {
  86. const output = {};
  87. for (const [key, value] of Object.entries(rule)) {
  88. if (typeof value !== 'function') {
  89. output[key] = value;
  90. }
  91. }
  92. return output;
  93. });
  94. },
  95. stylelint() {
  96. require(['/vendor/stylelint-bundle/stylelint-bundle.min']);
  97. const options = {};
  98. const rxPossible = /\bpossible:("(?:[^"]*?)"|\[(?:[^\]]*?)\]|\{(?:[^}]*?)\})/g;
  99. const rxString = /"([-\w\s]{3,}?)"/g;
  100. for (const [id, rule] of Object.entries(stylelint.rules)) {
  101. const ruleCode = `${rule()}`;
  102. const sets = [];
  103. let m, mStr;
  104. while ((m = rxPossible.exec(ruleCode))) {
  105. const possible = m[1];
  106. const set = [];
  107. while ((mStr = rxString.exec(possible))) {
  108. const s = mStr[1];
  109. if (s.includes(' ')) {
  110. set.push(...s.split(/\s+/));
  111. } else {
  112. set.push(s);
  113. }
  114. }
  115. if (possible.includes('ignoreAtRules')) {
  116. set.push('ignoreAtRules');
  117. }
  118. if (possible.includes('ignoreShorthands')) {
  119. set.push('ignoreShorthands');
  120. }
  121. if (set.length) {
  122. sets.push(set);
  123. }
  124. }
  125. options[id] = sets;
  126. }
  127. return options;
  128. },
  129. };
  130. })();