index.esm.mjs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. import postcss from 'postcss';
  2. import valueParser from 'postcss-values-parser';
  3. import fs from 'fs';
  4. import path from 'path';
  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 parse(string) {
  36. return valueParser(string).parse();
  37. }
  38. function isBlockIgnored(ruleOrDeclaration) {
  39. var rule = ruleOrDeclaration.selector ? ruleOrDeclaration : ruleOrDeclaration.parent;
  40. return /(!\s*)?postcss-custom-properties:\s*off\b/i.test(rule.toString());
  41. }
  42. function isRuleIgnored(rule) {
  43. var previous = rule.prev();
  44. return Boolean(isBlockIgnored(rule) || previous && previous.type === 'comment' && /(!\s*)?postcss-custom-properties:\s*ignore\s+next\b/i.test(previous.text));
  45. }
  46. function getCustomPropertiesFromRoot(root, opts) {
  47. // initialize custom selectors
  48. const customPropertiesFromHtmlElement = {};
  49. const customPropertiesFromRootPseudo = {}; // for each html or :root rule
  50. root.nodes.slice().forEach(rule => {
  51. const customPropertiesObject = isHtmlRule(rule) ? customPropertiesFromHtmlElement : isRootRule(rule) ? customPropertiesFromRootPseudo : null; // for each custom property
  52. if (customPropertiesObject) {
  53. rule.nodes.slice().forEach(decl => {
  54. if (isCustomDecl(decl) && !isBlockIgnored(decl)) {
  55. const prop = decl.prop; // write the parsed value to the custom property
  56. customPropertiesObject[prop] = parse(decl.value).nodes; // conditionally remove the custom property declaration
  57. if (!opts.preserve) {
  58. decl.remove();
  59. }
  60. }
  61. }); // conditionally remove the empty html or :root rule
  62. if (!opts.preserve && isEmptyParent(rule) && !isBlockIgnored(rule)) {
  63. rule.remove();
  64. }
  65. }
  66. }); // return all custom properties, preferring :root properties over html properties
  67. return Object.assign({}, customPropertiesFromHtmlElement, customPropertiesFromRootPseudo);
  68. } // match html and :root rules
  69. const htmlSelectorRegExp = /^html$/i;
  70. const rootSelectorRegExp = /^:root$/i;
  71. const customPropertyRegExp = /^--[A-z][\w-]*$/; // whether the node is an html or :root rule
  72. const isHtmlRule = node => node.type === 'rule' && htmlSelectorRegExp.test(node.selector) && Object(node.nodes).length;
  73. const isRootRule = node => node.type === 'rule' && rootSelectorRegExp.test(node.selector) && Object(node.nodes).length; // whether the node is an custom property
  74. const isCustomDecl = node => node.type === 'decl' && customPropertyRegExp.test(node.prop); // whether the node is a parent without children
  75. const isEmptyParent = node => Object(node.nodes).length === 0;
  76. /* Get Custom Properties from CSS File
  77. /* ========================================================================== */
  78. function getCustomPropertiesFromCSSFile(_x) {
  79. return _getCustomPropertiesFromCSSFile.apply(this, arguments);
  80. }
  81. /* Get Custom Properties from Object
  82. /* ========================================================================== */
  83. function _getCustomPropertiesFromCSSFile() {
  84. _getCustomPropertiesFromCSSFile = _asyncToGenerator(function* (from) {
  85. const css = yield readFile(from);
  86. const root = postcss.parse(css, {
  87. from
  88. });
  89. return getCustomPropertiesFromRoot(root, {
  90. preserve: true
  91. });
  92. });
  93. return _getCustomPropertiesFromCSSFile.apply(this, arguments);
  94. }
  95. function getCustomPropertiesFromObject(object) {
  96. const customProperties = Object.assign({}, Object(object).customProperties, Object(object)['custom-properties']);
  97. for (const key in customProperties) {
  98. customProperties[key] = parse(String(customProperties[key])).nodes;
  99. }
  100. return customProperties;
  101. }
  102. /* Get Custom Properties from JSON file
  103. /* ========================================================================== */
  104. function getCustomPropertiesFromJSONFile(_x2) {
  105. return _getCustomPropertiesFromJSONFile.apply(this, arguments);
  106. }
  107. /* Get Custom Properties from JS file
  108. /* ========================================================================== */
  109. function _getCustomPropertiesFromJSONFile() {
  110. _getCustomPropertiesFromJSONFile = _asyncToGenerator(function* (from) {
  111. const object = yield readJSON(from);
  112. return getCustomPropertiesFromObject(object);
  113. });
  114. return _getCustomPropertiesFromJSONFile.apply(this, arguments);
  115. }
  116. function getCustomPropertiesFromJSFile(_x3) {
  117. return _getCustomPropertiesFromJSFile.apply(this, arguments);
  118. }
  119. /* Get Custom Properties from Imports
  120. /* ========================================================================== */
  121. function _getCustomPropertiesFromJSFile() {
  122. _getCustomPropertiesFromJSFile = _asyncToGenerator(function* (from) {
  123. const object = yield import(from);
  124. return getCustomPropertiesFromObject(object);
  125. });
  126. return _getCustomPropertiesFromJSFile.apply(this, arguments);
  127. }
  128. function getCustomPropertiesFromImports(sources) {
  129. return sources.map(source => {
  130. if (source instanceof Promise) {
  131. return source;
  132. } else if (source instanceof Function) {
  133. return source();
  134. } // read the source as an object
  135. const opts = source === Object(source) ? source : {
  136. from: String(source)
  137. }; // skip objects with Custom Properties
  138. if (opts.customProperties || opts['custom-properties']) {
  139. return opts;
  140. } // source pathname
  141. const from = path.resolve(String(opts.from || '')); // type of file being read from
  142. const type = (opts.type || path.extname(from).slice(1)).toLowerCase();
  143. return {
  144. type,
  145. from
  146. };
  147. }).reduce(
  148. /*#__PURE__*/
  149. function () {
  150. var _ref = _asyncToGenerator(function* (customProperties, source) {
  151. const _ref2 = yield source,
  152. type = _ref2.type,
  153. from = _ref2.from;
  154. if (type === 'css') {
  155. return Object.assign((yield customProperties), (yield getCustomPropertiesFromCSSFile(from)));
  156. }
  157. if (type === 'js') {
  158. return Object.assign((yield customProperties), (yield getCustomPropertiesFromJSFile(from)));
  159. }
  160. if (type === 'json') {
  161. return Object.assign((yield customProperties), (yield getCustomPropertiesFromJSONFile(from)));
  162. }
  163. return Object.assign((yield customProperties), (yield getCustomPropertiesFromObject((yield source))));
  164. });
  165. return function (_x4, _x5) {
  166. return _ref.apply(this, arguments);
  167. };
  168. }(), {});
  169. }
  170. /* Helper utilities
  171. /* ========================================================================== */
  172. const readFile = from => new Promise((resolve, reject) => {
  173. fs.readFile(from, 'utf8', (error, result) => {
  174. if (error) {
  175. reject(error);
  176. } else {
  177. resolve(result);
  178. }
  179. });
  180. });
  181. const readJSON =
  182. /*#__PURE__*/
  183. function () {
  184. var _ref3 = _asyncToGenerator(function* (from) {
  185. return JSON.parse((yield readFile(from)));
  186. });
  187. return function readJSON(_x6) {
  188. return _ref3.apply(this, arguments);
  189. };
  190. }();
  191. function transformValueAST(root, customProperties) {
  192. if (root.nodes && root.nodes.length) {
  193. root.nodes.slice().forEach(child => {
  194. if (isVarFunction(child)) {
  195. // eslint-disable-next-line no-unused-vars
  196. const _child$nodes$slice = child.nodes.slice(1, -1),
  197. propertyNode = _child$nodes$slice[0],
  198. comma = _child$nodes$slice[1],
  199. fallbacks = _child$nodes$slice.slice(2);
  200. const name = propertyNode.value;
  201. if (name in Object(customProperties)) {
  202. // conditionally replace a known custom property
  203. const nodes = asClonedArrayWithBeforeSpacing(customProperties[name], child.raws.before);
  204. child.replaceWith(...nodes);
  205. retransformValueAST({
  206. nodes
  207. }, customProperties, name);
  208. } else if (fallbacks.length) {
  209. // conditionally replace a custom property with a fallback
  210. const index = root.nodes.indexOf(child);
  211. if (index !== -1) {
  212. root.nodes.splice(index, 1, ...asClonedArrayWithBeforeSpacing(fallbacks, child.raws.before));
  213. }
  214. transformValueAST(root, customProperties);
  215. }
  216. } else {
  217. transformValueAST(child, customProperties);
  218. }
  219. });
  220. }
  221. return root;
  222. } // retransform the current ast without a custom property (to prevent recursion)
  223. function retransformValueAST(root, customProperties, withoutProperty) {
  224. const nextCustomProperties = Object.assign({}, customProperties);
  225. delete nextCustomProperties[withoutProperty];
  226. return transformValueAST(root, nextCustomProperties);
  227. } // match var() functions
  228. const varRegExp = /^var$/i; // whether the node is a var() function
  229. const isVarFunction = node => node.type === 'func' && varRegExp.test(node.value) && Object(node.nodes).length > 0; // return an array with its nodes cloned, preserving the raw
  230. const asClonedArrayWithBeforeSpacing = (array, beforeSpacing) => {
  231. const clonedArray = asClonedArray(array, null);
  232. if (clonedArray[0]) {
  233. clonedArray[0].raws.before = beforeSpacing;
  234. }
  235. return clonedArray;
  236. }; // return an array with its nodes cloned
  237. const asClonedArray = (array, parent) => array.map(node => asClonedNode(node, parent)); // return a cloned node
  238. const asClonedNode = (node, parent) => {
  239. const cloneNode = new node.constructor(node);
  240. for (const key in node) {
  241. if (key === 'parent') {
  242. cloneNode.parent = parent;
  243. } else if (Object(node[key]).constructor === Array) {
  244. cloneNode[key] = asClonedArray(node.nodes, cloneNode);
  245. } else if (Object(node[key]).constructor === Object) {
  246. cloneNode[key] = Object.assign({}, node[key]);
  247. }
  248. }
  249. return cloneNode;
  250. };
  251. var transformProperties = ((root, customProperties, opts) => {
  252. // walk decls that can be transformed
  253. root.walkDecls(decl => {
  254. if (isTransformableDecl(decl) && !isRuleIgnored(decl)) {
  255. const originalValue = decl.value;
  256. const valueAST = parse(originalValue);
  257. const value = String(transformValueAST(valueAST, customProperties)); // conditionally transform values that have changed
  258. if (value !== originalValue) {
  259. if (opts.preserve) {
  260. decl.cloneBefore({
  261. value
  262. });
  263. } else {
  264. decl.value = value;
  265. }
  266. }
  267. }
  268. });
  269. }); // match custom properties
  270. const customPropertyRegExp$1 = /^--[A-z][\w-]*$/; // match custom property inclusions
  271. const customPropertiesRegExp = /(^|[^\w-])var\([\W\w]+\)/; // whether the declaration should be potentially transformed
  272. const isTransformableDecl = decl => !customPropertyRegExp$1.test(decl.prop) && customPropertiesRegExp.test(decl.value);
  273. /* Write Custom Properties to CSS File
  274. /* ========================================================================== */
  275. function writeCustomPropertiesToCssFile(_x, _x2) {
  276. return _writeCustomPropertiesToCssFile.apply(this, arguments);
  277. }
  278. /* Write Custom Properties to JSON file
  279. /* ========================================================================== */
  280. function _writeCustomPropertiesToCssFile() {
  281. _writeCustomPropertiesToCssFile = _asyncToGenerator(function* (to, customProperties) {
  282. const cssContent = Object.keys(customProperties).reduce((cssLines, name) => {
  283. cssLines.push(`\t${name}: ${customProperties[name]};`);
  284. return cssLines;
  285. }, []).join('\n');
  286. const css = `:root {\n${cssContent}\n}\n`;
  287. yield writeFile(to, css);
  288. });
  289. return _writeCustomPropertiesToCssFile.apply(this, arguments);
  290. }
  291. function writeCustomPropertiesToJsonFile(_x3, _x4) {
  292. return _writeCustomPropertiesToJsonFile.apply(this, arguments);
  293. }
  294. /* Write Custom Properties to Common JS file
  295. /* ========================================================================== */
  296. function _writeCustomPropertiesToJsonFile() {
  297. _writeCustomPropertiesToJsonFile = _asyncToGenerator(function* (to, customProperties) {
  298. const jsonContent = JSON.stringify({
  299. 'custom-properties': customProperties
  300. }, null, ' ');
  301. const json = `${jsonContent}\n`;
  302. yield writeFile(to, json);
  303. });
  304. return _writeCustomPropertiesToJsonFile.apply(this, arguments);
  305. }
  306. function writeCustomPropertiesToCjsFile(_x5, _x6) {
  307. return _writeCustomPropertiesToCjsFile.apply(this, arguments);
  308. }
  309. /* Write Custom Properties to Module JS file
  310. /* ========================================================================== */
  311. function _writeCustomPropertiesToCjsFile() {
  312. _writeCustomPropertiesToCjsFile = _asyncToGenerator(function* (to, customProperties) {
  313. const jsContents = Object.keys(customProperties).reduce((jsLines, name) => {
  314. jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customProperties[name])}'`);
  315. return jsLines;
  316. }, []).join(',\n');
  317. const js = `module.exports = {\n\tcustomProperties: {\n${jsContents}\n\t}\n};\n`;
  318. yield writeFile(to, js);
  319. });
  320. return _writeCustomPropertiesToCjsFile.apply(this, arguments);
  321. }
  322. function writeCustomPropertiesToMjsFile(_x7, _x8) {
  323. return _writeCustomPropertiesToMjsFile.apply(this, arguments);
  324. }
  325. /* Write Custom Properties to Exports
  326. /* ========================================================================== */
  327. function _writeCustomPropertiesToMjsFile() {
  328. _writeCustomPropertiesToMjsFile = _asyncToGenerator(function* (to, customProperties) {
  329. const mjsContents = Object.keys(customProperties).reduce((mjsLines, name) => {
  330. mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customProperties[name])}'`);
  331. return mjsLines;
  332. }, []).join(',\n');
  333. const mjs = `export const customProperties = {\n${mjsContents}\n};\n`;
  334. yield writeFile(to, mjs);
  335. });
  336. return _writeCustomPropertiesToMjsFile.apply(this, arguments);
  337. }
  338. function writeCustomPropertiesToExports(customProperties, destinations) {
  339. return Promise.all(destinations.map(
  340. /*#__PURE__*/
  341. function () {
  342. var _ref = _asyncToGenerator(function* (destination) {
  343. if (destination instanceof Function) {
  344. yield destination(defaultCustomPropertiesToJSON(customProperties));
  345. } else {
  346. // read the destination as an object
  347. const opts = destination === Object(destination) ? destination : {
  348. to: String(destination)
  349. }; // transformer for Custom Properties into a JSON-compatible object
  350. const toJSON = opts.toJSON || defaultCustomPropertiesToJSON;
  351. if ('customProperties' in opts) {
  352. // write directly to an object as customProperties
  353. opts.customProperties = toJSON(customProperties);
  354. } else if ('custom-properties' in opts) {
  355. // write directly to an object as custom-properties
  356. opts['custom-properties'] = toJSON(customProperties);
  357. } else {
  358. // destination pathname
  359. const to = String(opts.to || ''); // type of file being written to
  360. const type = (opts.type || path.extname(opts.to).slice(1)).toLowerCase(); // transformed Custom Properties
  361. const customPropertiesJSON = toJSON(customProperties);
  362. if (type === 'css') {
  363. yield writeCustomPropertiesToCssFile(to, customPropertiesJSON);
  364. }
  365. if (type === 'js') {
  366. yield writeCustomPropertiesToCjsFile(to, customPropertiesJSON);
  367. }
  368. if (type === 'json') {
  369. yield writeCustomPropertiesToJsonFile(to, customPropertiesJSON);
  370. }
  371. if (type === 'mjs') {
  372. yield writeCustomPropertiesToMjsFile(to, customPropertiesJSON);
  373. }
  374. }
  375. }
  376. });
  377. return function (_x9) {
  378. return _ref.apply(this, arguments);
  379. };
  380. }()));
  381. }
  382. /* Helper utilities
  383. /* ========================================================================== */
  384. const defaultCustomPropertiesToJSON = customProperties => {
  385. return Object.keys(customProperties).reduce((customPropertiesJSON, key) => {
  386. customPropertiesJSON[key] = String(customProperties[key]);
  387. return customPropertiesJSON;
  388. }, {});
  389. };
  390. const writeFile = (to, text) => new Promise((resolve, reject) => {
  391. fs.writeFile(to, text, error => {
  392. if (error) {
  393. reject(error);
  394. } else {
  395. resolve();
  396. }
  397. });
  398. });
  399. const escapeForJS = string => string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
  400. var index = postcss.plugin('postcss-custom-properties', opts => {
  401. // whether to preserve custom selectors and rules using them
  402. const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true; // sources to import custom selectors from
  403. const importFrom = [].concat(Object(opts).importFrom || []); // destinations to export custom selectors to
  404. const exportTo = [].concat(Object(opts).exportTo || []); // promise any custom selectors are imported
  405. const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); // synchronous transform
  406. const syncTransform = root => {
  407. const customProperties = getCustomPropertiesFromRoot(root, {
  408. preserve
  409. });
  410. transformProperties(root, customProperties, {
  411. preserve
  412. });
  413. }; // asynchronous transform
  414. const asyncTransform =
  415. /*#__PURE__*/
  416. function () {
  417. var _ref = _asyncToGenerator(function* (root) {
  418. const customProperties = Object.assign({}, (yield customPropertiesPromise), getCustomPropertiesFromRoot(root, {
  419. preserve
  420. }));
  421. yield writeCustomPropertiesToExports(customProperties, exportTo);
  422. transformProperties(root, customProperties, {
  423. preserve
  424. });
  425. });
  426. return function asyncTransform(_x) {
  427. return _ref.apply(this, arguments);
  428. };
  429. }(); // whether to return synchronous function if no asynchronous operations are requested
  430. const canReturnSyncFunction = importFrom.length === 0 && exportTo.length === 0;
  431. return canReturnSyncFunction ? syncTransform : asyncTransform;
  432. });
  433. export default index;
  434. //# sourceMappingURL=index.esm.mjs.map