stylePluginScoped.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const postcss_selector_parser_1 = __importDefault(require("postcss-selector-parser"));
  7. const scopedPlugin = () => {
  8. return {
  9. postcssPlugin: 'uni-sfc-scoped',
  10. prepare({ processor: { plugins } }) {
  11. const hasVueSfcScoped = !!plugins.find((plugin) => plugin.postcssPlugin === 'vue-sfc-scoped');
  12. return {
  13. Rule(rule) {
  14. processRule(rule, hasVueSfcScoped);
  15. },
  16. };
  17. },
  18. };
  19. };
  20. const processedRules = new WeakSet();
  21. function processRule(rule, hasVueSfcScoped) {
  22. if (processedRules.has(rule) ||
  23. (rule.parent &&
  24. rule.parent.type === 'atrule' &&
  25. /-?keyframes$/.test(rule.parent.name))) {
  26. return;
  27. }
  28. processedRules.add(rule);
  29. rule.selector = (0, postcss_selector_parser_1.default)((selectorRoot) => {
  30. selectorRoot.each((selector) => {
  31. hasVueSfcScoped
  32. ? rewriteDeprecatedSelector(selector)
  33. : rewriteSelector(selector, selectorRoot);
  34. });
  35. }).processSync(rule.selector);
  36. }
  37. /**
  38. * @param selector
  39. * @returns
  40. */
  41. function rewriteDeprecatedSelector(selector) {
  42. const nodes = [];
  43. let deepNode;
  44. selector.each((n) => {
  45. if (deepNode) {
  46. nodes.push(n);
  47. selector.removeChild(n);
  48. }
  49. else {
  50. const { type, value } = n;
  51. if (type === 'pseudo' && value === '::v-deep') {
  52. deepNode = n;
  53. }
  54. else if (type === 'combinator' &&
  55. (value === '>>>' || value === '/deep/')) {
  56. deepNode = n;
  57. }
  58. }
  59. });
  60. if (!deepNode) {
  61. return;
  62. }
  63. if (deepNode.type === 'combinator') {
  64. const index = selector.index(deepNode);
  65. if (index > 0) {
  66. selector.insertBefore(deepNode, postcss_selector_parser_1.default.combinator({ value: ' ' }));
  67. }
  68. }
  69. // remove first combinator
  70. // ::v-deep a{color:red;} => :deep(a){color:red;}
  71. const firstNode = nodes[0];
  72. if (firstNode && firstNode.type === 'combinator' && firstNode.value === ' ') {
  73. nodes.shift();
  74. }
  75. selector.insertBefore(deepNode, postcss_selector_parser_1.default.pseudo({
  76. value: ':deep',
  77. nodes: [postcss_selector_parser_1.default.selector({ value: '', nodes })],
  78. }));
  79. selector.removeChild(deepNode);
  80. }
  81. function rewriteSelector(selector, selectorRoot) {
  82. let node = null;
  83. // find the last child node to insert attribute selector
  84. selector.each((n) => {
  85. // DEPRECATED ">>>" and "/deep/" combinator
  86. if (n.type === 'combinator' &&
  87. (n.value === '>>>' || n.value === '/deep/')) {
  88. n.value = ' ';
  89. n.spaces.before = n.spaces.after = '';
  90. // warn(
  91. // `the >>> and /deep/ combinators have been deprecated. ` +
  92. // `Use :deep() instead.`
  93. // )
  94. return false;
  95. }
  96. if (n.type === 'pseudo') {
  97. const { value } = n;
  98. // deep: inject [id] attribute at the node before the ::v-deep
  99. // combinator.
  100. if (value === ':deep' || value === '::v-deep') {
  101. if (n.nodes.length) {
  102. // .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
  103. // replace the current node with ::v-deep's inner selector
  104. let last = n;
  105. n.nodes[0].each((ss) => {
  106. selector.insertAfter(last, ss);
  107. last = ss;
  108. });
  109. // insert a space combinator before if it doesn't already have one
  110. const prev = selector.at(selector.index(n) - 1);
  111. if (!prev || !isSpaceCombinator(prev)) {
  112. selector.insertAfter(n, postcss_selector_parser_1.default.combinator({
  113. value: ' ',
  114. }));
  115. }
  116. selector.removeChild(n);
  117. }
  118. else {
  119. // DEPRECATED usage
  120. // .foo ::v-deep .bar -> .foo[xxxxxxx] .bar
  121. // warn(
  122. // `::v-deep usage as a combinator has ` +
  123. // `been deprecated. Use :deep(<inner-selector>) instead.`
  124. // )
  125. const prev = selector.at(selector.index(n) - 1);
  126. if (prev && isSpaceCombinator(prev)) {
  127. selector.removeChild(prev);
  128. }
  129. selector.removeChild(n);
  130. }
  131. return false;
  132. }
  133. // slot: use selector inside `::v-slotted` and inject [id + '-s']
  134. // instead.
  135. // ::v-slotted(.foo) -> .foo[xxxxxxx-s]
  136. if (value === ':slotted' || value === '::v-slotted') {
  137. rewriteSelector(n.nodes[0], selectorRoot);
  138. let last = n;
  139. n.nodes[0].each((ss) => {
  140. selector.insertAfter(last, ss);
  141. last = ss;
  142. });
  143. // selector.insertAfter(n, n.nodes[0])
  144. selector.removeChild(n);
  145. // since slotted attribute already scopes the selector there's no
  146. // need for the non-slot attribute.
  147. return false;
  148. }
  149. // global: replace with inner selector and do not inject [id].
  150. // ::v-global(.foo) -> .foo
  151. if (value === ':global' || value === '::v-global') {
  152. selectorRoot.insertAfter(selector, n.nodes[0]);
  153. selectorRoot.removeChild(selector);
  154. return false;
  155. }
  156. }
  157. if (n.type !== 'pseudo' && n.type !== 'combinator') {
  158. node = n;
  159. }
  160. });
  161. if (node) {
  162. ;
  163. node.spaces.after = '';
  164. }
  165. else {
  166. // For deep selectors & standalone pseudo selectors,
  167. // the attribute selectors are prepended rather than appended.
  168. // So all leading spaces must be eliminated to avoid problems.
  169. selector.first.spaces.before = '';
  170. }
  171. }
  172. function isSpaceCombinator(node) {
  173. return node.type === 'combinator' && /^\s+$/.test(node.value);
  174. }
  175. scopedPlugin.postcss = true;
  176. exports.default = scopedPlugin;