index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. 'use strict';
  2. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  3. var postcss = require('postcss');
  4. var topologicalSort = require('./topologicalSort');
  5. var declWhitelist = ['composes'];
  6. var declFilter = new RegExp(`^(${declWhitelist.join('|')})$`);
  7. var matchImports = /^(.+?)\s+from\s+(?:"([^"]+)"|'([^']+)'|(global))$/;
  8. var icssImport = /^:import\((?:"([^"]+)"|'([^']+)')\)/;
  9. var VISITED_MARKER = 1;
  10. function createParentName(rule, root) {
  11. return `__${root.index(rule.parent)}_${rule.selector}`;
  12. }
  13. function serializeImports(imports) {
  14. return imports.map(function (importPath) {
  15. return '`' + importPath + '`';
  16. }).join(', ');
  17. }
  18. /**
  19. * :import('G') {}
  20. *
  21. * Rule
  22. * composes: ... from 'A'
  23. * composes: ... from 'B'
  24. * Rule
  25. * composes: ... from 'A'
  26. * composes: ... from 'A'
  27. * composes: ... from 'C'
  28. *
  29. * Results in:
  30. *
  31. * graph: {
  32. * G: [],
  33. * A: [],
  34. * B: ['A'],
  35. * C: ['A'],
  36. * }
  37. */
  38. function addImportToGraph(importId, parentId, graph, visited) {
  39. var siblingsId = parentId + '_' + 'siblings';
  40. var visitedId = parentId + '_' + importId;
  41. if (visited[visitedId] !== VISITED_MARKER) {
  42. if (!Array.isArray(visited[siblingsId])) visited[siblingsId] = [];
  43. var siblings = visited[siblingsId];
  44. if (Array.isArray(graph[importId])) graph[importId] = graph[importId].concat(siblings);else graph[importId] = siblings.slice();
  45. visited[visitedId] = VISITED_MARKER;
  46. siblings.push(importId);
  47. }
  48. }
  49. module.exports = postcss.plugin('modules-extract-imports', function () {
  50. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  51. var failOnWrongOrder = options.failOnWrongOrder;
  52. return function (css) {
  53. var graph = {};
  54. var visited = {};
  55. var existingImports = {};
  56. var importDecls = {};
  57. var imports = {};
  58. var importIndex = 0;
  59. var createImportedName = typeof options.createImportedName !== 'function' ? function (importName /*, path*/) {
  60. return `i__imported_${importName.replace(/\W/g, '_')}_${importIndex++}`;
  61. } : options.createImportedName;
  62. // Check the existing imports order and save refs
  63. css.walkRules(function (rule) {
  64. var matches = icssImport.exec(rule.selector);
  65. if (matches) {
  66. var _matches = _slicedToArray(matches, 3),
  67. /*match*/doubleQuotePath = _matches[1],
  68. singleQuotePath = _matches[2];
  69. var importPath = doubleQuotePath || singleQuotePath;
  70. addImportToGraph(importPath, 'root', graph, visited);
  71. existingImports[importPath] = rule;
  72. }
  73. });
  74. // Find any declaration that supports imports
  75. css.walkDecls(declFilter, function (decl) {
  76. var matches = decl.value.match(matchImports);
  77. var tmpSymbols = void 0;
  78. if (matches) {
  79. var _matches2 = _slicedToArray(matches, 5),
  80. /*match*/symbols = _matches2[1],
  81. doubleQuotePath = _matches2[2],
  82. singleQuotePath = _matches2[3],
  83. global = _matches2[4];
  84. if (global) {
  85. // Composing globals simply means changing these classes to wrap them in global(name)
  86. tmpSymbols = symbols.split(/\s+/).map(function (s) {
  87. return `global(${s})`;
  88. });
  89. } else {
  90. var importPath = doubleQuotePath || singleQuotePath;
  91. var parentRule = createParentName(decl.parent, css);
  92. addImportToGraph(importPath, parentRule, graph, visited);
  93. importDecls[importPath] = decl;
  94. imports[importPath] = imports[importPath] || {};
  95. tmpSymbols = symbols.split(/\s+/).map(function (s) {
  96. if (!imports[importPath][s]) {
  97. imports[importPath][s] = createImportedName(s, importPath);
  98. }
  99. return imports[importPath][s];
  100. });
  101. }
  102. decl.value = tmpSymbols.join(' ');
  103. }
  104. });
  105. var importsOrder = topologicalSort(graph, failOnWrongOrder);
  106. if (importsOrder instanceof Error) {
  107. var importPath = importsOrder.nodes.find(function (importPath) {
  108. return importDecls.hasOwnProperty(importPath);
  109. });
  110. var decl = importDecls[importPath];
  111. var errMsg = 'Failed to resolve order of composed modules ' + serializeImports(importsOrder.nodes) + '.';
  112. throw decl.error(errMsg, {
  113. plugin: 'modules-extract-imports',
  114. word: 'composes'
  115. });
  116. }
  117. var lastImportRule = void 0;
  118. importsOrder.forEach(function (path) {
  119. var importedSymbols = imports[path];
  120. var rule = existingImports[path];
  121. if (!rule && importedSymbols) {
  122. rule = postcss.rule({
  123. selector: `:import("${path}")`,
  124. raws: { after: '\n' }
  125. });
  126. if (lastImportRule) css.insertAfter(lastImportRule, rule);else css.prepend(rule);
  127. }
  128. lastImportRule = rule;
  129. if (!importedSymbols) return;
  130. Object.keys(importedSymbols).forEach(function (importedSymbol) {
  131. rule.append(postcss.decl({
  132. value: importedSymbol,
  133. prop: importedSymbols[importedSymbol],
  134. raws: { before: '\n ' }
  135. }));
  136. });
  137. });
  138. };
  139. });