| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 | 
							- 'use strict';
 
- const csstree = require('css-tree');
 
- const { referencesProps } = require('./_collections.js');
 
- /**
 
-  * @typedef {import('../lib/types').XastElement} XastElement
 
-  * @typedef {import('../lib/types').PluginInfo} PluginInfo
 
-  */
 
- exports.type = 'visitor';
 
- exports.name = 'prefixIds';
 
- exports.active = false;
 
- exports.description = 'prefix IDs';
 
- /**
 
-  * extract basename from path
 
-  * @type {(path: string) => string}
 
-  */
 
- const getBasename = (path) => {
 
-   // extract everything after latest slash or backslash
 
-   const matched = path.match(/[/\\]?([^/\\]+)$/);
 
-   if (matched) {
 
-     return matched[1];
 
-   }
 
-   return '';
 
- };
 
- /**
 
-  * escapes a string for being used as ID
 
-  * @type {(string: string) => string}
 
-  */
 
- const escapeIdentifierName = (str) => {
 
-   return str.replace(/[. ]/g, '_');
 
- };
 
- /**
 
-  * @type {(string: string) => string}
 
-  */
 
- const unquote = (string) => {
 
-   if (
 
-     (string.startsWith('"') && string.endsWith('"')) ||
 
-     (string.startsWith("'") && string.endsWith("'"))
 
-   ) {
 
-     return string.slice(1, -1);
 
-   }
 
-   return string;
 
- };
 
- /**
 
-  * prefix an ID
 
-  * @type {(prefix: string, name: string) => string}
 
-  */
 
- const prefixId = (prefix, value) => {
 
-   if (value.startsWith(prefix)) {
 
-     return value;
 
-   }
 
-   return prefix + value;
 
- };
 
- /**
 
-  * prefix an #ID
 
-  * @type {(prefix: string, name: string) => string | null}
 
-  */
 
- const prefixReference = (prefix, value) => {
 
-   if (value.startsWith('#')) {
 
-     return '#' + prefixId(prefix, value.slice(1));
 
-   }
 
-   return null;
 
- };
 
- /**
 
-  * Prefixes identifiers
 
-  *
 
-  * @author strarsis <strarsis@gmail.com>
 
-  *
 
-  * @type {import('../lib/types').Plugin<{
 
-  *   prefix?: boolean | string | ((node: XastElement, info: PluginInfo) => string),
 
-  *   delim?: string,
 
-  *   prefixIds?: boolean,
 
-  *   prefixClassNames?: boolean,
 
-  * }>}
 
-  */
 
- exports.fn = (_root, params, info) => {
 
-   const { delim = '__', prefixIds = true, prefixClassNames = true } = params;
 
-   return {
 
-     element: {
 
-       enter: (node) => {
 
-         /**
 
-          * prefix, from file name or option
 
-          * @type {string}
 
-          */
 
-         let prefix = 'prefix' + delim;
 
-         if (typeof params.prefix === 'function') {
 
-           prefix = params.prefix(node, info) + delim;
 
-         } else if (typeof params.prefix === 'string') {
 
-           prefix = params.prefix + delim;
 
-         } else if (params.prefix === false) {
 
-           prefix = '';
 
-         } else if (info.path != null && info.path.length > 0) {
 
-           prefix = escapeIdentifierName(getBasename(info.path)) + delim;
 
-         }
 
-         // prefix id/class selectors and url() references in styles
 
-         if (node.name === 'style') {
 
-           // skip empty <style/> elements
 
-           if (node.children.length === 0) {
 
-             return;
 
-           }
 
-           // parse styles
 
-           let cssText = '';
 
-           if (
 
-             node.children[0].type === 'text' ||
 
-             node.children[0].type === 'cdata'
 
-           ) {
 
-             cssText = node.children[0].value;
 
-           }
 
-           /**
 
-            * @type {null | csstree.CssNode}
 
-            */
 
-           let cssAst = null;
 
-           try {
 
-             cssAst = csstree.parse(cssText, {
 
-               parseValue: true,
 
-               parseCustomProperty: false,
 
-             });
 
-           } catch {
 
-             return;
 
-           }
 
-           csstree.walk(cssAst, (node) => {
 
-             // #ID, .class selectors
 
-             if (
 
-               (prefixIds && node.type === 'IdSelector') ||
 
-               (prefixClassNames && node.type === 'ClassSelector')
 
-             ) {
 
-               node.name = prefixId(prefix, node.name);
 
-               return;
 
-             }
 
-             // url(...) references
 
-             if (
 
-               node.type === 'Url' &&
 
-               node.value.value &&
 
-               node.value.value.length > 0
 
-             ) {
 
-               const prefixed = prefixReference(
 
-                 prefix,
 
-                 unquote(node.value.value)
 
-               );
 
-               if (prefixed != null) {
 
-                 node.value.value = prefixed;
 
-               }
 
-             }
 
-           });
 
-           // update styles
 
-           if (
 
-             node.children[0].type === 'text' ||
 
-             node.children[0].type === 'cdata'
 
-           ) {
 
-             node.children[0].value = csstree.generate(cssAst);
 
-           }
 
-           return;
 
-         }
 
-         // prefix an ID attribute value
 
-         if (
 
-           prefixIds &&
 
-           node.attributes.id != null &&
 
-           node.attributes.id.length !== 0
 
-         ) {
 
-           node.attributes.id = prefixId(prefix, node.attributes.id);
 
-         }
 
-         // prefix a class attribute value
 
-         if (
 
-           prefixClassNames &&
 
-           node.attributes.class != null &&
 
-           node.attributes.class.length !== 0
 
-         ) {
 
-           node.attributes.class = node.attributes.class
 
-             .split(/\s+/)
 
-             .map((name) => prefixId(prefix, name))
 
-             .join(' ');
 
-         }
 
-         // prefix a href attribute value
 
-         // xlink:href is deprecated, must be still supported
 
-         for (const name of ['href', 'xlink:href']) {
 
-           if (
 
-             node.attributes[name] != null &&
 
-             node.attributes[name].length !== 0
 
-           ) {
 
-             const prefixed = prefixReference(prefix, node.attributes[name]);
 
-             if (prefixed != null) {
 
-               node.attributes[name] = prefixed;
 
-             }
 
-           }
 
-         }
 
-         // prefix an URL attribute value
 
-         for (const name of referencesProps) {
 
-           if (
 
-             node.attributes[name] != null &&
 
-             node.attributes[name].length !== 0
 
-           ) {
 
-             node.attributes[name] = node.attributes[name].replace(
 
-               /url\((.*?)\)/gi,
 
-               (match, url) => {
 
-                 const prefixed = prefixReference(prefix, url);
 
-                 if (prefixed == null) {
 
-                   return match;
 
-                 }
 
-                 return `url(${prefixed})`;
 
-               }
 
-             );
 
-           }
 
-         }
 
-         // prefix begin/end attribute value
 
-         for (const name of ['begin', 'end']) {
 
-           if (
 
-             node.attributes[name] != null &&
 
-             node.attributes[name].length !== 0
 
-           ) {
 
-             const parts = node.attributes[name].split(/\s*;\s+/).map((val) => {
 
-               if (val.endsWith('.end') || val.endsWith('.start')) {
 
-                 const [id, postfix] = val.split('.');
 
-                 return `${prefixId(prefix, id)}.${postfix}`;
 
-               }
 
-               return val;
 
-             });
 
-             node.attributes[name] = parts.join('; ');
 
-           }
 
-         }
 
-       },
 
-     },
 
-   };
 
- };
 
 
  |