build-syntax-resolver.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. "use strict";
  2. const path = require("path");
  3. const postcssSafeParser = require("postcss-safe-parser");
  4. const { cssSyntax, cssSafeSyntax } = require("./syntaxes");
  5. const { loadModule, isModuleNotFoundError } = require("../shared/load-module");
  6. const defaultRules = [
  7. {
  8. test: /^sass$/i,
  9. lang: "sass",
  10. },
  11. {
  12. test: /^scss$/i,
  13. lang: "scss",
  14. },
  15. {
  16. test: /^less$/i,
  17. lang: "less",
  18. },
  19. {
  20. test: /^s(?:ugar)?ss$/i,
  21. lang: "sugarss",
  22. },
  23. {
  24. test: /^styl(?:us)?$/i,
  25. lang: "stylus",
  26. },
  27. {
  28. test: /^postcss$/i,
  29. lang: "css",
  30. },
  31. ];
  32. const defaultSyntaxes = {
  33. sass: "postcss-sass",
  34. scss: "postcss-scss",
  35. less: "postcss-less",
  36. sugarss: "sugarss",
  37. stylus: "postcss-styl",
  38. };
  39. module.exports = function buildSyntaxResolver(config) {
  40. const { rules = [], ...syntaxes } = config || {};
  41. const allRules = [...rules, ...defaultRules];
  42. const definedLangs = new Set([
  43. "css",
  44. ...rules.map((rule) => rule.lang),
  45. ...Object.keys(syntaxes),
  46. ]);
  47. return function resolve(baseLang, baseOptions) {
  48. let lang = baseLang || "css";
  49. const options = baseOptions || {};
  50. const cwd = process.cwd();
  51. const placeholderFilePath = path.join(cwd, `__placeholder__.${lang}`);
  52. for (const rule of allRules) {
  53. const regex = new RegExp(rule.test);
  54. if (regex.test(lang) || regex.test(placeholderFilePath)) {
  55. lang = rule.lang;
  56. break;
  57. }
  58. }
  59. lang = lang.toLowerCase();
  60. const syntax = syntaxes[lang] || defaultSyntaxes[lang];
  61. if (syntax) {
  62. if (typeof syntax === "string") {
  63. const syntaxModule = loadFromString(syntax, options);
  64. if (syntaxModule) {
  65. return syntaxModule;
  66. }
  67. if (definedLangs.has(lang)) {
  68. throw new Error(
  69. `Cannot resolve module "${syntax}". It's likely that the module isn't installed correctly. Try reinstalling by running the \`npm install ${syntax}@latest --save-dev\``
  70. );
  71. }
  72. }
  73. if (syntax === postcssSafeParser) {
  74. return cssSafeSyntax;
  75. }
  76. if (typeof syntax.parse === "function") {
  77. return syntax;
  78. }
  79. }
  80. if (!definedLangs.has(lang)) {
  81. return null;
  82. }
  83. return options.defaultSyntax || cssSyntax;
  84. };
  85. };
  86. const standardModuleResolvers = {
  87. // eslint-disable-next-line node/no-missing-require -- ignore
  88. "postcss-sass": () => require("postcss-sass"),
  89. // eslint-disable-next-line node/no-unpublished-require -- ignore
  90. "postcss-scss": () => require("postcss-scss"),
  91. // eslint-disable-next-line node/no-unpublished-require -- ignore
  92. "postcss-less": () => require("postcss-less"),
  93. // eslint-disable-next-line node/no-unpublished-require -- ignore
  94. sugarss: () => require("sugarss"),
  95. // eslint-disable-next-line node/no-unpublished-require -- ignore
  96. "postcss-styl": () => require("postcss-styl"),
  97. };
  98. function loadFromString(syntax, options) {
  99. if (syntax === "postcss") {
  100. return options.defaultSyntax || cssSyntax;
  101. }
  102. if (syntax === "postcss-safe-parser") {
  103. return cssSafeSyntax;
  104. }
  105. const loadedModule = loadModule(syntax);
  106. if (loadedModule) {
  107. return loadedModule;
  108. }
  109. /* istanbul ignore if */
  110. if (standardModuleResolvers[syntax]) {
  111. try {
  112. return standardModuleResolvers[syntax]();
  113. } catch (error) {
  114. if (!isModuleNotFoundError(error)) {
  115. throw error;
  116. }
  117. // ignore
  118. }
  119. }
  120. return null;
  121. }