index.cjs.js 18 KB

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