index.es.mjs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. import parser from 'postcss-selector-parser';
  2. import fs from 'fs';
  3. import path from 'path';
  4. import postcss from 'postcss';
  5. function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  6. try {
  7. var info = gen[key](arg);
  8. var value = info.value;
  9. } catch (error) {
  10. reject(error);
  11. return;
  12. }
  13. if (info.done) {
  14. resolve(value);
  15. } else {
  16. Promise.resolve(value).then(_next, _throw);
  17. }
  18. }
  19. function _asyncToGenerator(fn) {
  20. return function () {
  21. var self = this,
  22. args = arguments;
  23. return new Promise(function (resolve, reject) {
  24. var gen = fn.apply(self, args);
  25. function _next(value) {
  26. asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
  27. }
  28. function _throw(err) {
  29. asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
  30. }
  31. _next(undefined);
  32. });
  33. };
  34. }
  35. function _defineProperty(obj, key, value) {
  36. if (key in obj) {
  37. Object.defineProperty(obj, key, {
  38. value: value,
  39. enumerable: true,
  40. configurable: true,
  41. writable: true
  42. });
  43. } else {
  44. obj[key] = value;
  45. }
  46. return obj;
  47. }
  48. function _objectSpread(target) {
  49. for (var i = 1; i < arguments.length; i++) {
  50. var source = arguments[i] != null ? arguments[i] : {};
  51. var ownKeys = Object.keys(source);
  52. if (typeof Object.getOwnPropertySymbols === 'function') {
  53. ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
  54. return Object.getOwnPropertyDescriptor(source, sym).enumerable;
  55. }));
  56. }
  57. ownKeys.forEach(function (key) {
  58. _defineProperty(target, key, source[key]);
  59. });
  60. }
  61. return target;
  62. }
  63. function _slicedToArray(arr, i) {
  64. return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
  65. }
  66. function _arrayWithHoles(arr) {
  67. if (Array.isArray(arr)) return arr;
  68. }
  69. function _iterableToArrayLimit(arr, i) {
  70. var _arr = [];
  71. var _n = true;
  72. var _d = false;
  73. var _e = undefined;
  74. try {
  75. for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
  76. _arr.push(_s.value);
  77. if (i && _arr.length === i) break;
  78. }
  79. } catch (err) {
  80. _d = true;
  81. _e = err;
  82. } finally {
  83. try {
  84. if (!_n && _i["return"] != null) _i["return"]();
  85. } finally {
  86. if (_d) throw _e;
  87. }
  88. }
  89. return _arr;
  90. }
  91. function _nonIterableRest() {
  92. throw new TypeError("Invalid attempt to destructure non-iterable instance");
  93. }
  94. /* Return a Selectors AST from a Selectors String
  95. /* ========================================================================== */
  96. var getSelectorsAstFromSelectorsString = (selectorString => {
  97. let selectorAST;
  98. parser(selectors => {
  99. selectorAST = selectors;
  100. }).processSync(selectorString);
  101. return selectorAST;
  102. });
  103. var getCustomSelectors = ((root, opts) => {
  104. // initialize custom selectors
  105. const customSelectors = {}; // for each custom selector atrule that is a child of the css root
  106. root.nodes.slice().forEach(node => {
  107. if (isCustomSelector(node)) {
  108. // extract the name and selectors from the params of the custom selector
  109. const _node$params$match = node.params.match(customSelectorParamsRegExp),
  110. _node$params$match2 = _slicedToArray(_node$params$match, 3),
  111. name = _node$params$match2[1],
  112. selectors = _node$params$match2[2]; // write the parsed selectors to the custom selector
  113. customSelectors[name] = getSelectorsAstFromSelectorsString(selectors); // conditionally remove the custom selector atrule
  114. if (!Object(opts).preserve) {
  115. node.remove();
  116. }
  117. }
  118. });
  119. return customSelectors;
  120. }); // match the custom selector name
  121. const customSelectorNameRegExp = /^custom-selector$/i; // match the custom selector params
  122. const customSelectorParamsRegExp = /^(:--[A-z][\w-]*)\s+([\W\w]+)\s*$/; // whether the atrule is a custom selector
  123. const isCustomSelector = node => node.type === 'atrule' && customSelectorNameRegExp.test(node.name) && customSelectorParamsRegExp.test(node.params);
  124. // return transformed selectors, replacing custom pseudo selectors with custom selectors
  125. function transformSelectorList(selectorList, customSelectors) {
  126. let index = selectorList.nodes.length - 1;
  127. while (index >= 0) {
  128. const transformedSelectors = transformSelector(selectorList.nodes[index], customSelectors);
  129. if (transformedSelectors.length) {
  130. selectorList.nodes.splice(index, 1, ...transformedSelectors);
  131. }
  132. --index;
  133. }
  134. return selectorList;
  135. } // return custom pseudo selectors replaced with custom selectors
  136. function transformSelector(selector, customSelectors) {
  137. const transpiledSelectors = [];
  138. for (const index in selector.nodes) {
  139. const _selector$nodes$index = selector.nodes[index],
  140. value = _selector$nodes$index.value,
  141. nodes = _selector$nodes$index.nodes;
  142. if (value in customSelectors) {
  143. var _iteratorNormalCompletion = true;
  144. var _didIteratorError = false;
  145. var _iteratorError = undefined;
  146. try {
  147. for (var _iterator = customSelectors[value].nodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  148. const replacementSelector = _step.value;
  149. const selectorClone = selector.clone();
  150. selectorClone.nodes.splice(index, 1, ...replacementSelector.clone().nodes.map(node => {
  151. // use spacing from the current usage
  152. node.spaces = _objectSpread({}, selector.nodes[index].spaces);
  153. return node;
  154. }));
  155. const retranspiledSelectors = transformSelector(selectorClone, customSelectors);
  156. adjustNodesBySelectorEnds(selectorClone.nodes, Number(index));
  157. if (retranspiledSelectors.length) {
  158. transpiledSelectors.push(...retranspiledSelectors);
  159. } else {
  160. transpiledSelectors.push(selectorClone);
  161. }
  162. }
  163. } catch (err) {
  164. _didIteratorError = true;
  165. _iteratorError = err;
  166. } finally {
  167. try {
  168. if (!_iteratorNormalCompletion && _iterator.return != null) {
  169. _iterator.return();
  170. }
  171. } finally {
  172. if (_didIteratorError) {
  173. throw _iteratorError;
  174. }
  175. }
  176. }
  177. return transpiledSelectors;
  178. } else if (nodes && nodes.length) {
  179. transformSelectorList(selector.nodes[index], customSelectors);
  180. }
  181. }
  182. return transpiledSelectors;
  183. } // match selectors by difficult-to-separate ends
  184. const withoutSelectorStartMatch = /^(tag|universal)$/;
  185. const withoutSelectorEndMatch = /^(class|id|pseudo|tag|universal)$/;
  186. const isWithoutSelectorStart = node => withoutSelectorStartMatch.test(Object(node).type);
  187. const isWithoutSelectorEnd = node => withoutSelectorEndMatch.test(Object(node).type); // adjust nodes by selector ends (so that .class:--h1 becomes h1.class rather than .classh1)
  188. const adjustNodesBySelectorEnds = (nodes, index) => {
  189. if (index && isWithoutSelectorStart(nodes[index]) && isWithoutSelectorEnd(nodes[index - 1])) {
  190. let safeIndex = index - 1;
  191. while (safeIndex && isWithoutSelectorEnd(nodes[safeIndex])) {
  192. --safeIndex;
  193. }
  194. if (safeIndex < index) {
  195. const node = nodes.splice(index, 1)[0];
  196. nodes.splice(safeIndex, 0, node);
  197. nodes[safeIndex].spaces.before = nodes[safeIndex + 1].spaces.before;
  198. nodes[safeIndex + 1].spaces.before = '';
  199. if (nodes[index]) {
  200. nodes[index].spaces.after = nodes[safeIndex].spaces.after;
  201. nodes[safeIndex].spaces.after = '';
  202. }
  203. }
  204. }
  205. };
  206. var transformRules = ((root, customSelectors, opts) => {
  207. root.walkRules(customPseudoRegExp, rule => {
  208. const selector = parser(selectors => {
  209. transformSelectorList(selectors, customSelectors, opts);
  210. }).processSync(rule.selector);
  211. if (opts.preserve) {
  212. rule.cloneBefore({
  213. selector
  214. });
  215. } else {
  216. rule.selector = selector;
  217. }
  218. });
  219. });
  220. const customPseudoRegExp = /:--[A-z][\w-]*/;
  221. /* Import Custom Selectors from CSS AST
  222. /* ========================================================================== */
  223. function importCustomSelectorsFromCSSAST(root) {
  224. return getCustomSelectors(root);
  225. }
  226. /* Import Custom Selectors from CSS File
  227. /* ========================================================================== */
  228. function importCustomSelectorsFromCSSFile(_x) {
  229. return _importCustomSelectorsFromCSSFile.apply(this, arguments);
  230. }
  231. /* Import Custom Selectors from Object
  232. /* ========================================================================== */
  233. function _importCustomSelectorsFromCSSFile() {
  234. _importCustomSelectorsFromCSSFile = _asyncToGenerator(function* (from) {
  235. const css = yield readFile(path.resolve(from));
  236. const root = postcss.parse(css, {
  237. from: path.resolve(from)
  238. });
  239. return importCustomSelectorsFromCSSAST(root);
  240. });
  241. return _importCustomSelectorsFromCSSFile.apply(this, arguments);
  242. }
  243. function importCustomSelectorsFromObject(object) {
  244. const customSelectors = Object.assign({}, Object(object).customSelectors || Object(object)['custom-selectors']);
  245. for (const key in customSelectors) {
  246. customSelectors[key] = getSelectorsAstFromSelectorsString(customSelectors[key]);
  247. }
  248. return customSelectors;
  249. }
  250. /* Import Custom Selectors from JSON file
  251. /* ========================================================================== */
  252. function importCustomSelectorsFromJSONFile(_x2) {
  253. return _importCustomSelectorsFromJSONFile.apply(this, arguments);
  254. }
  255. /* Import Custom Selectors from JS file
  256. /* ========================================================================== */
  257. function _importCustomSelectorsFromJSONFile() {
  258. _importCustomSelectorsFromJSONFile = _asyncToGenerator(function* (from) {
  259. const object = yield readJSON(path.resolve(from));
  260. return importCustomSelectorsFromObject(object);
  261. });
  262. return _importCustomSelectorsFromJSONFile.apply(this, arguments);
  263. }
  264. function importCustomSelectorsFromJSFile(_x3) {
  265. return _importCustomSelectorsFromJSFile.apply(this, arguments);
  266. }
  267. /* Import Custom Selectors from Sources
  268. /* ========================================================================== */
  269. function _importCustomSelectorsFromJSFile() {
  270. _importCustomSelectorsFromJSFile = _asyncToGenerator(function* (from) {
  271. const object = yield import(path.resolve(from));
  272. return importCustomSelectorsFromObject(object);
  273. });
  274. return _importCustomSelectorsFromJSFile.apply(this, arguments);
  275. }
  276. function importCustomSelectorsFromSources(sources) {
  277. return sources.map(source => {
  278. if (source instanceof Promise) {
  279. return source;
  280. } else if (source instanceof Function) {
  281. return source();
  282. } // read the source as an object
  283. const opts = source === Object(source) ? source : {
  284. from: String(source)
  285. }; // skip objects with custom selectors
  286. if (Object(opts).customSelectors || Object(opts)['custom-selectors']) {
  287. return opts;
  288. } // source pathname
  289. const from = String(opts.from || ''); // type of file being read from
  290. const type = (opts.type || path.extname(from).slice(1)).toLowerCase();
  291. return {
  292. type,
  293. from
  294. };
  295. }).reduce(
  296. /*#__PURE__*/
  297. function () {
  298. var _ref = _asyncToGenerator(function* (customSelectors, source) {
  299. const _ref2 = yield source,
  300. type = _ref2.type,
  301. from = _ref2.from;
  302. if (type === 'ast') {
  303. return Object.assign(customSelectors, importCustomSelectorsFromCSSAST(from));
  304. }
  305. if (type === 'css') {
  306. return Object.assign(customSelectors, (yield importCustomSelectorsFromCSSFile(from)));
  307. }
  308. if (type === 'js') {
  309. return Object.assign(customSelectors, (yield importCustomSelectorsFromJSFile(from)));
  310. }
  311. if (type === 'json') {
  312. return Object.assign(customSelectors, (yield importCustomSelectorsFromJSONFile(from)));
  313. }
  314. return Object.assign(customSelectors, importCustomSelectorsFromObject((yield source)));
  315. });
  316. return function (_x4, _x5) {
  317. return _ref.apply(this, arguments);
  318. };
  319. }(), {});
  320. }
  321. /* Helper utilities
  322. /* ========================================================================== */
  323. const readFile = from => new Promise((resolve, reject) => {
  324. fs.readFile(from, 'utf8', (error, result) => {
  325. if (error) {
  326. reject(error);
  327. } else {
  328. resolve(result);
  329. }
  330. });
  331. });
  332. const readJSON =
  333. /*#__PURE__*/
  334. function () {
  335. var _ref3 = _asyncToGenerator(function* (from) {
  336. return JSON.parse((yield readFile(from)));
  337. });
  338. return function readJSON(_x6) {
  339. return _ref3.apply(this, arguments);
  340. };
  341. }();
  342. /* Import Custom Selectors from CSS File
  343. /* ========================================================================== */
  344. function exportCustomSelectorsToCssFile(_x, _x2) {
  345. return _exportCustomSelectorsToCssFile.apply(this, arguments);
  346. }
  347. /* Import Custom Selectors from JSON file
  348. /* ========================================================================== */
  349. function _exportCustomSelectorsToCssFile() {
  350. _exportCustomSelectorsToCssFile = _asyncToGenerator(function* (to, customSelectors) {
  351. const cssContent = Object.keys(customSelectors).reduce((cssLines, name) => {
  352. cssLines.push(`@custom-selector ${name} ${customSelectors[name]};`);
  353. return cssLines;
  354. }, []).join('\n');
  355. const css = `${cssContent}\n`;
  356. yield writeFile(to, css);
  357. });
  358. return _exportCustomSelectorsToCssFile.apply(this, arguments);
  359. }
  360. function exportCustomSelectorsToJsonFile(_x3, _x4) {
  361. return _exportCustomSelectorsToJsonFile.apply(this, arguments);
  362. }
  363. /* Import Custom Selectors from Common JS file
  364. /* ========================================================================== */
  365. function _exportCustomSelectorsToJsonFile() {
  366. _exportCustomSelectorsToJsonFile = _asyncToGenerator(function* (to, customSelectors) {
  367. const jsonContent = JSON.stringify({
  368. 'custom-selectors': customSelectors
  369. }, null, ' ');
  370. const json = `${jsonContent}\n`;
  371. yield writeFile(to, json);
  372. });
  373. return _exportCustomSelectorsToJsonFile.apply(this, arguments);
  374. }
  375. function exportCustomSelectorsToCjsFile(_x5, _x6) {
  376. return _exportCustomSelectorsToCjsFile.apply(this, arguments);
  377. }
  378. /* Import Custom Selectors from Module JS file
  379. /* ========================================================================== */
  380. function _exportCustomSelectorsToCjsFile() {
  381. _exportCustomSelectorsToCjsFile = _asyncToGenerator(function* (to, customSelectors) {
  382. const jsContents = Object.keys(customSelectors).reduce((jsLines, name) => {
  383. jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
  384. return jsLines;
  385. }, []).join(',\n');
  386. const js = `module.exports = {\n\tcustomSelectors: {\n${jsContents}\n\t}\n};\n`;
  387. yield writeFile(to, js);
  388. });
  389. return _exportCustomSelectorsToCjsFile.apply(this, arguments);
  390. }
  391. function exportCustomSelectorsToMjsFile(_x7, _x8) {
  392. return _exportCustomSelectorsToMjsFile.apply(this, arguments);
  393. }
  394. /* Export Custom Selectors to Destinations
  395. /* ========================================================================== */
  396. function _exportCustomSelectorsToMjsFile() {
  397. _exportCustomSelectorsToMjsFile = _asyncToGenerator(function* (to, customSelectors) {
  398. const mjsContents = Object.keys(customSelectors).reduce((mjsLines, name) => {
  399. mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
  400. return mjsLines;
  401. }, []).join(',\n');
  402. const mjs = `export const customSelectors = {\n${mjsContents}\n};\n`;
  403. yield writeFile(to, mjs);
  404. });
  405. return _exportCustomSelectorsToMjsFile.apply(this, arguments);
  406. }
  407. function exportCustomSelectorsToDestinations(customSelectors, destinations) {
  408. return Promise.all(destinations.map(
  409. /*#__PURE__*/
  410. function () {
  411. var _ref = _asyncToGenerator(function* (destination) {
  412. if (destination instanceof Function) {
  413. yield destination(defaultCustomSelectorsToJSON(customSelectors));
  414. } else {
  415. // read the destination as an object
  416. const opts = destination === Object(destination) ? destination : {
  417. to: String(destination)
  418. }; // transformer for custom selectors into a JSON-compatible object
  419. const toJSON = opts.toJSON || defaultCustomSelectorsToJSON;
  420. if ('customSelectors' in opts) {
  421. // write directly to an object as customSelectors
  422. opts.customSelectors = toJSON(customSelectors);
  423. } else if ('custom-selectors' in opts) {
  424. // write directly to an object as custom-selectors
  425. opts['custom-selectors'] = toJSON(customSelectors);
  426. } else {
  427. // destination pathname
  428. const to = String(opts.to || ''); // type of file being written to
  429. const type = (opts.type || path.extname(opts.to).slice(1)).toLowerCase(); // transformed custom selectors
  430. const customSelectorsJSON = toJSON(customSelectors);
  431. if (type === 'css') {
  432. yield exportCustomSelectorsToCssFile(to, customSelectorsJSON);
  433. }
  434. if (type === 'js') {
  435. yield exportCustomSelectorsToCjsFile(to, customSelectorsJSON);
  436. }
  437. if (type === 'json') {
  438. yield exportCustomSelectorsToJsonFile(to, customSelectorsJSON);
  439. }
  440. if (type === 'mjs') {
  441. yield exportCustomSelectorsToMjsFile(to, customSelectorsJSON);
  442. }
  443. }
  444. }
  445. });
  446. return function (_x9) {
  447. return _ref.apply(this, arguments);
  448. };
  449. }()));
  450. }
  451. /* Helper utilities
  452. /* ========================================================================== */
  453. const defaultCustomSelectorsToJSON = customSelectors => {
  454. return Object.keys(customSelectors).reduce((customSelectorsJSON, key) => {
  455. customSelectorsJSON[key] = String(customSelectors[key]);
  456. return customSelectorsJSON;
  457. }, {});
  458. };
  459. const writeFile = (to, text) => new Promise((resolve, reject) => {
  460. fs.writeFile(to, text, error => {
  461. if (error) {
  462. reject(error);
  463. } else {
  464. resolve();
  465. }
  466. });
  467. });
  468. const escapeForJS = string => string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
  469. var index = postcss.plugin('postcss-custom-selectors', opts => {
  470. // whether to preserve custom selectors and rules using them
  471. const preserve = Boolean(Object(opts).preserve); // sources to import custom selectors from
  472. const importFrom = [].concat(Object(opts).importFrom || []); // destinations to export custom selectors to
  473. const exportTo = [].concat(Object(opts).exportTo || []); // promise any custom selectors are imported
  474. const customSelectorsPromise = importCustomSelectorsFromSources(importFrom);
  475. return (
  476. /*#__PURE__*/
  477. function () {
  478. var _ref = _asyncToGenerator(function* (root) {
  479. const customProperties = Object.assign((yield customSelectorsPromise), getCustomSelectors(root, {
  480. preserve
  481. }));
  482. yield exportCustomSelectorsToDestinations(customProperties, exportTo);
  483. transformRules(root, customProperties, {
  484. preserve
  485. });
  486. });
  487. return function (_x) {
  488. return _ref.apply(this, arguments);
  489. };
  490. }()
  491. );
  492. });
  493. export default index;
  494. //# sourceMappingURL=index.es.mjs.map