function writeModules(pluginConfig, outputConfig, entryModules) {
  const indexHTML = getOutputHTML(outputConfig.dir);
  const head = query(indexHTML, predicates.hasTagName('head'));
  const body = query(indexHTML, predicates.hasTagName('body'));

  // If dynamic imports are polyfilled, we first need to feature detect so we can't load our app using the
  // 'src' attribute.
  if (pluginConfig.polyfillDynamicImports) {
    // Feature detect dynamic import in a separate script, because browsers which don't
    // support it will throw a syntax error and stop executing the module
    append(body, createScriptModule('window.importModule=src=>import(src);'));

    // The actual loading script. Loads web components and dynamic import polyfills if necessary, then
    // loads the user's modules
    append(
      body,
      createScriptModule(`
      (async () => {
        const p = [];
        if (!('attachShadow' in Element.prototype) || !('getRootNode' in Element.prototype)) {p.push('./polyfills/webcomponent-polyfills.js');}
        if (!window.importModule) {p.push('./polyfills/dynamic-import-polyfill.js');}
        if (p.length > 0) {await Promise.all(p.map(p => new Promise((rs, rj) => {const s = document.createElement('script');s.src = p;s.onerror = () => rj(\`Failed to load \${s.s}\`);s.onload = () => rs();document.head.appendChild(s);})));}
        ${entryModules.map(src => `importModule('${src}');`).join('')}
      })();
    `),
    );

    // Because the module is loaded after some js execution, add preload hints so that it is downloaded
    // from the start
    entryModules.forEach(src => {
      append(
        head,
        createElement('link', {
          rel: 'preload',
          as: 'script',
          crossorigin: 'anonymous',
          href: src,
        }),
      );
    });
  } else {
    // No polyfilling needed, so just create a module script
    entryModules.forEach(src => {
      append(body, createScript({ type: 'module', src }));
    });
  }

  writeOutputHTML(outputConfig.dir, indexHTML);
}

Writes module scripts to output HTML file and copy necessary polyfills.

Writes module scripts to output HTML file and copy necessary polyfills .


function karmaEsmFramework(karmaConfig, karmaEmitter) {
  if (!karmaConfig.files.some(file => file.type === 'module')) {
    throw new Error(
      "Did not find any test files with type='module'." +
        "Follow this format: { pattern: config.grep ? config.grep : 'test/**/*.test.js', type: 'module' }",
    );
  }

  initialize(karmaConfig, karmaEmitter);
}

The karma esm framework. Helps the user debug the case where they don't have a
module test script defined, and boots up the plugin.

The karma esm framework . Helps the user debug the case where they don t have a module test script defined and boots up the plugin .


function createCompiler(config, karmaEmitter) {
  const cache = new Map();
  const watcher = chokidar.watch([]);

  watcher.on('change', filePath => {
    if (!filePath.endsWith('.test.js') && !filePath.endsWith('.spec.js')) {
      karmaEmitter.refreshFiles();
    }
    cache.delete(filePath);
  });

  function addToCache(filePath, code) {
    cache.set(filePath, code);
    watcher.add(filePath);
  }

  function babelCompile(filePath, code) {
    return babel.transform(code, {
      filename: filePath,
      ...config.babelOptions,
    }).code;
  }

  function compile(filePath, code) {
    // if we should not babel compile some files, only add them to the cache
    if (config.babel && config.babel.exclude) {
      if (config.babel.exclude.some(exclude => minimatch(filePath, exclude))) {
        addToCache(filePath, code);
        return code;
      }
    }

    const compiled = babelCompile(filePath, code);
    addToCache(filePath, compiled);
    return compiled;
  }

  function getCached(filePath) {
    return cache.get(filePath);
  }

  return {
    compile,
    getCached,
  };
}

Sets up karma-esm compiler. Compiles files with babel, maintains
a cache and notifies karma when files change.

Sets up karma - esm compiler . Compiles files with babel maintains a cache and notifies karma when files change .


function karmaEsmPreprocessor(logger) {
  const log = logger.create('preprocessor.esm');

  function preprocess(code, file, done) {
    try {
      let compiledCode = esm.compiler.getCached(file.originalPath);
      if (!compiledCode) {
        compiledCode = esm.compiler.compile(file.originalPath, code);
      }
      done(null, compiledCode);
    } catch (e) {
      const message = `\n\n${e.message}\n at ${file.originalPath}\n\n`;
      log.error(message);
      done(null, code);
    }
  }

  return preprocess;
}

function karmaEsmPreprocessor ( logger ) { const log = logger . create ( 'preprocessor.esm' ) ; function preprocess ( code , file , done ) { try { let compiledCode = esm . compiler . getCached ( file . originalPath ) ; if ( ! compiledCode ) { compiledCode = esm . compiler . compile ( file . originalPath , code ) ; } done ( null , compiledCode ) ; } catch ( e ) { const message = ` \n \n ${ e . message } \n ${ file . originalPath } \n \n ` ; log . error ( message ) ; done ( null , code ) ; } } return preprocess ; }

Karma preprocessor to handle compiling entry point test files.

Karma preprocessor to handle compiling entry point test files .


function toAbsoluteURL(url) {
  const a = document.createElement('a');
  a.setAttribute('href', url); // <a href="hoge.html">
  // @ts-ignore
  return a.cloneNode(false).href; // -> "http://example.com/hoge.html"
}

function
toAbsoluteURL
(
url
)
{
const
a
=
document
.
createElement
(
'a'
)
;
a
.
setAttribute
(
'href'
,
url
)
;
// <a href="hoge.html">
// @ts-ignore
return
a
.
cloneNode
(
false
)
.
href
;
// -> "http://example.com/hoge.html"
}

Changes a relative URL to an absolute URL.

@example
toAbsoluteURL('hoge.html') // http://example.com/hoge.html

@param {string} url Relative URL
@returns {string} Absolute URL

Changes a relative URL to an absolute URL .


function normalize(node) {
  if (!(isElement(node) || isDocument(node) || isDocumentFragment(node))) {
    return;
  }
  if (!node.childNodes) {
    return;
  }
  var textRangeStart = -1;
  for (var i = node.childNodes.length - 1, n = void 0; i >= 0; i--) {
    n = node.childNodes[i];
    if (isTextNode(n)) {
      if (textRangeStart === -1) {
        textRangeStart = i;
      }
      if (i === 0) {
        // collapse leading text nodes
        collapseTextRange(node, 0, textRangeStart);
      }
    } else {
      // recurse
      normalize(n);
      // collapse the range after this node
      if (textRangeStart > -1) {
        collapseTextRange(node, i + 1, textRangeStart);
        textRangeStart = -1;
      }
    }
  }
}

function
normalize
(
node
)
{
if
(
!
(
isElement
(
node
)
||
isDocument
(
node
)
||
isDocumentFragment
(
node
)
)
)
{
return
;
}
if
(
!
node
.
childNodes
)
{
return
;
}
var
textRangeStart
=
1
;
for
(
var
i
=
node
.
childNodes
.
length
1
,
n
=
void
0
;
i
>=
0
;
i
)
{
n
=
node
.
childNodes
[
i
]
;
if
(
isTextNode
(
n
)
)
{
if
(
textRangeStart
===
1
)
{
textRangeStart
=
i
;
}
if
(
i
===
0
)
{
// collapse leading text nodes
collapseTextRange
(
node
,
0
,
textRangeStart
)
;
}
}
else
{
// recurse
normalize
(
n
)
;
// collapse the range after this node
if
(
textRangeStart
>
1
)
{
collapseTextRange
(
node
,
i
+
1
,
textRangeStart
)
;
textRangeStart
=
1
;
}
}
}
}

Normalize the text inside an element

Equivalent to `element.normalize()` in the browser
See https://developer.mozilla.org/en-US/docs/Web/API/Node/normalize


function getTextContent(node) {
  if (isCommentNode(node)) {
    return node.data || '';
  }
  if (isTextNode(node)) {
    return node.value || '';
  }
  var subtree = nodeWalkAll(node, isTextNode);
  return subtree.map(getTextContent).join('');
}

function getTextContent ( node ) { if ( isCommentNode ( node ) ) { return node . data || '' ; } if ( isTextNode ( node ) ) { return node . value || '' ; } var subtree = nodeWalkAll ( node , isTextNode ) ; return subtree . map ( getTextContent ) . join ( '' ) ; }

Return the text value of a node or element

Equivalent to `node.textContent` in the browser


function setTextContent(node, value) {
  if (isCommentNode(node)) {
    node.data = value;
  } else if (isTextNode(node)) {
    node.value = value;
  } else {
    var tn = newTextNode(value);
    tn.parentNode = node;
    node.childNodes = [tn];
  }
}

function setTextContent ( node , value ) { if ( isCommentNode ( node ) ) { node . data = value ; } else if ( isTextNode ( node ) ) { node . value = value ; } else { var tn = newTextNode ( value ) ; tn . parentNode = node ; node . childNodes = [ tn ] ; } }

Set the text value of a node or element

Equivalent to `node.textContent = value` in the browser


function treeMap(node, mapfn) {
  var results = [];
  nodeWalk(node, function(node) {
    results = results.concat(mapfn(node));
    return false;
  });
  return results;
}

function treeMap ( node , mapfn ) { var results = [ ] ; nodeWalk ( node , function ( node ) { results = results . concat ( mapfn ( node ) ) ; return false ; } ) ; return results ; }

Applies `mapfn` to `node` and the tree below `node`, returning a flattened
list of results.

Applies mapfn to node and the tree below node returning a flattened list of results .


function nodeWalk(node, predicate, getChildNodes) {
  if (getChildNodes === void 0) {
    getChildNodes = module.exports.defaultChildNodes;
  }
  if (predicate(node)) {
    return node;
  }
  var match = null;
  var childNodes = getChildNodes(node);
  if (childNodes) {
    for (var i = 0; i < childNodes.length; i++) {
      match = nodeWalk(childNodes[i], predicate, getChildNodes);
      if (match) {
        break;
      }
    }
  }
  return match;
}

function nodeWalk ( node , predicate , getChildNodes ) { if ( getChildNodes === void 0 ) { getChildNodes = module . exports . defaultChildNodes ; } if ( predicate ( node ) ) { return node ; } var match = null ; var childNodes = getChildNodes ( node ) ; if ( childNodes ) { for ( var i = 0 ; i < childNodes . length ; i ++ ) { match = nodeWalk ( childNodes [ i ] , predicate , getChildNodes ) ; if ( match ) { break ; } } } return match ; }

Walk the tree down from `node`, applying the `predicate` function.
Return the first node that matches the given predicate.

@returns `null` if no node matches, parse5 node object if a node matches.

Walk the tree down from node applying the predicate function . Return the first node that matches the given predicate .


function nodeWalkAll(node, predicate, matches, getChildNodes) {
  if (getChildNodes === void 0) {
    getChildNodes = module.exports.defaultChildNodes;
  }
  if (!matches) {
    matches = [];
  }
  if (predicate(node)) {
    matches.push(node);
  }
  var childNodes = getChildNodes(node);
  if (childNodes) {
    for (var i = 0; i < childNodes.length; i++) {
      nodeWalkAll(childNodes[i], predicate, matches, getChildNodes);
    }
  }
  return matches;
}

function nodeWalkAll ( node , predicate , matches , getChildNodes ) { if ( getChildNodes === void 0 ) { getChildNodes = module . exports . defaultChildNodes ; } if ( ! matches ) { matches = [ ] ; } if ( predicate ( node ) ) { matches . push ( node ) ; } var childNodes = getChildNodes ( node ) ; if ( childNodes ) { for ( var i = 0 ; i < childNodes . length ; i ++ ) { nodeWalkAll ( childNodes [ i ] , predicate , matches , getChildNodes ) ; } } return matches ; }

Walk the tree down from `node`, applying the `predicate` function.
All nodes matching the predicate function from `node` to leaves will be
returned.

Walk the tree down from node applying the predicate function . All nodes matching the predicate function from node to leaves will be returned .


function nodeWalkAncestors(node, predicate) {
  var parent = node.parentNode;
  if (!parent) {
    return undefined;
  }
  if (predicate(parent)) {
    return parent;
  }
  return nodeWalkAncestors(parent, predicate);
}

function nodeWalkAncestors ( node , predicate ) { var parent = node . parentNode ; if ( ! parent ) { return undefined ; } if ( predicate ( parent ) ) { return parent ; } return nodeWalkAncestors ( parent , predicate ) ; }

Walk the tree up from the parent of `node`, to its grandparent and so on to
the root of the tree.  Return the first ancestor that matches the given
predicate.

Walk the tree up from the parent of node to its grandparent and so on to the root of the tree . Return the first ancestor that matches the given predicate .


function query(node, predicate, getChildNodes) {
  if (getChildNodes === void 0) {
    getChildNodes = module.exports.defaultChildNodes;
  }
  var elementPredicate = AND(isElement, predicate);
  return nodeWalk(node, elementPredicate, getChildNodes);
}

function query ( node , predicate , getChildNodes ) { if ( getChildNodes === void 0 ) { getChildNodes = module . exports . defaultChildNodes ; } var elementPredicate = AND ( isElement , predicate ) ; return nodeWalk ( node , elementPredicate , getChildNodes ) ; }

Equivalent to `nodeWalk`, but only matches elements

Equivalent to nodeWalk but only matches elements


function queryAll(node, predicate, matches, getChildNodes) {
  if (getChildNodes === void 0) {
    getChildNodes = module.exports.defaultChildNodes;
  }
  var elementPredicate = AND(isElement, predicate);
  return nodeWalkAll(node, elementPredicate, matches, getChildNodes);
}

function queryAll ( node , predicate , matches , getChildNodes ) { if ( getChildNodes === void 0 ) { getChildNodes = module . exports . defaultChildNodes ; } var elementPredicate = AND ( isElement , predicate ) ; return nodeWalkAll ( node , elementPredicate , matches , getChildNodes ) ; }

Equivalent to `nodeWalkAll`, but only matches elements

Equivalent to nodeWalkAll but only matches elements


function insertNode(parent, index, newNode, replace) {
  if (!parent.childNodes) {
    parent.childNodes = [];
  }
  var newNodes = [];
  var removedNode = replace ? parent.childNodes[index] : null;
  if (newNode) {
    if (isDocumentFragment(newNode)) {
      if (newNode.childNodes) {
        newNodes = Array.from(newNode.childNodes);
        newNode.childNodes.length = 0;
      }
    } else {
      newNodes = [newNode];
      remove(newNode);
    }
  }
  if (replace) {
    removedNode = parent.childNodes[index];
  }
  Array.prototype.splice.apply(parent.childNodes, [index, replace ? 1 : 0].concat(newNodes));
  newNodes.forEach(function(n) {
    n.parentNode = parent;
  });
  if (removedNode) {
    removedNode.parentNode = undefined;
  }
}

function insertNode ( parent , index , newNode , replace ) { if ( ! parent . childNodes ) { parent . childNodes = [ ] ; } var newNodes = [ ] ; var removedNode = replace ? parent . childNodes [ index ] : null ; if ( newNode ) { if ( isDocumentFragment ( newNode ) ) { if ( newNode . childNodes ) { newNodes = Array . from ( newNode . childNodes ) ; newNode . childNodes . length = 0 ; } } else { newNodes = [ newNode ] ; remove ( newNode ) ; } } if ( replace ) { removedNode = parent . childNodes [ index ] ; } Array . prototype . splice . apply ( parent . childNodes , [ index , replace ? 1 : 0 ] . concat ( newNodes ) ) ; newNodes . forEach ( function ( n ) { n . parentNode = parent ; } ) ; if ( removedNode ) { removedNode . parentNode = undefined ; } }

Inserts `newNode` into `parent` at `index`, optionally replaceing the
current node at `index`. If `newNode` is a DocumentFragment, its childNodes
are inserted and removed from the fragment.

Inserts newNode into parent at index optionally replaceing the current node at index . If newNode is a DocumentFragment its childNodes are inserted and removed from the fragment .


function removeNodeSaveChildren(node) {
  // We can't save the children if there's no parent node to provide
  // for them.
  var fosterParent = node.parentNode;
  if (!fosterParent) {
    return;
  }
  var children = (node.childNodes || []).slice();
  for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
    var child = children_1[_i];
    insertBefore(node.parentNode, node, child);
  }
  remove(node);
}

Removes a node and places its children in its place.  If the node
has no parent, the operation is impossible and no action takes place.

Removes a node and places its children in its place . If the node has no parent the operation is impossible and no action takes place .


function getCode(filePath) {
  // take code from cache if available
  const cachedCode = esm.compiler.getCached(filePath);
  if (cachedCode) {
    return cachedCode;
  }

  if (!fs.existsSync(filePath)) {
    throw new Error(`File does not exist: ${filePath}`);
  }

  // otherwise read code from file and compile it
  const code = fs.readFileSync(filePath, 'utf-8');
  return esm.compiler.compile(filePath, code);
}

Middleware for serving es modules. Karma doesn't serve any files not specified
beforehand. This is extremely inconvenient when working with es modules, as many
files are requested on the fly.

Middleware for serving es modules . Karma doesn t serve any files not specified beforehand . This is extremely inconvenient when working with es modules as many files are requested on the fly .


function wrapTransitioningContext(Comp) {
  return props => {
    return (
      <TransitioningContext.Consumer>
        {context => <Comp context={context} {...props} />}
      </TransitioningContext.Consumer>
    );
  };
}

function wrapTransitioningContext ( Comp ) { return props => { return ( < TransitioningContext . Consumer > { context => < Comp context = { context } { ... props } / > } < / TransitioningContext . Consumer > ) ; } ; }

The below wrapper is used to support legacy context API with Context.Consumer
render prop. We need it as we want to access `context` from within
`componentDidMount` callback. If we decided to drop support for older
react native we could rewrite it using hooks or `static contextType` API.

The below wrapper is used to support legacy context API with Context . Consumer render prop . We need it as we want to access context from within componentDidMount callback . If we decided to drop support for older react native we could rewrite it using hooks or static contextType API .


function split_linebreaks(s) {
  //return s.split(/\x0d\x0a|\x0a/);

  s = s.replace(acorn.allLineBreaks, '\n');
  var out = [],
    idx = s.indexOf("\n");
  while (idx !== -1) {
    out.push(s.substring(0, idx));
    s = s.substring(idx + 1);
    idx = s.indexOf("\n");
  }
  if (s.length) {
    out.push(s);
  }
  return out;
}

function
split_linebreaks
(
s
)
{
//return s.split(/\x0d\x0a|\x0a/);
s
=
s
.
replace
(
acorn
.
allLineBreaks
,
'\n'
)
;
var
out
=
[
]
,
idx
=
s
.
indexOf
(
"\n"
)
;
while
(
idx
!==
1
)
{
out
.
push
(
s
.
substring
(
0
,
idx
)
)
;
s
=
s
.
substring
(
idx
+
1
)
;
idx
=
s
.
indexOf
(
"\n"
)
;
}
if
(
s
.
length
)
{
out
.
push
(
s
)
;
}
return
out
;
}

we could use just string.split, but IE doesn't like returning empty strings

we could use just string . split but IE doesn t like returning empty strings


function onOutputError(err) {
    if (err.code === 'EACCES') {
        console.error(err.path + " is not writable. Skipping!");
    } else {
        console.error(err);
        process.exit(0);
    }
}

function
onOutputError
(
err
)
{
if
(
err
.
code
===
'EACCES'
)
{
console
.
error
(
err
.
path
+
" is not writable. Skipping!"
)
;
}
else
{
console
.
error
(
err
)
;
process
.
exit
(
0
)
;
}
}

error handler for output stream that swallows errors silently, allowing the loop to continue over unwritable files.

error handler for output stream that swallows errors silently allowing the loop to continue over unwritable files .


function dasherizeShorthands(hash) {
    // operate in-place
    Object.keys(hash).forEach(function(key) {
        // each key value is an array
        var val = hash[key][0];
        // only dasherize one-character shorthands
        if (key.length === 1 && val.indexOf('_') > -1) {
            hash[dasherizeFlag(val)] = val;
        }
    });

    return hash;
}

translate weird python underscored keys into dashed argv, avoiding single character aliases.

translate weird python underscored keys into dashed argv avoiding single character aliases .


function filterUnusedSelectors(selectors, ignore, usedSelectors) {
    /* There are some selectors not supported for matching, like
     *   :before, :after
     * They should be removed only if the parent is not found.
     * Example: '.clearfix:before' should be removed only if there
     *          is no '.clearfix'
     */
    return selectors.filter((selector) => {
        selector = dePseudify(selector);
        /* TODO: process @-rules */
        if (selector[0] === '@') {
            return true;
        }
        for (let i = 0, len = ignore.length; i < len; ++i) {
            if (_.isRegExp(ignore[i]) && ignore[i].test(selector)) {
                return true;
            }
            if (ignore[i] === selector) {
                return true;
            }
        }
        return usedSelectors.indexOf(selector) !== -1;
    });
}

Private function used in filterUnusedRules.
@param  {Array} selectors      CSS selectors created by the CSS parser
@param  {Array} ignore         List of selectors to be ignored
@param  {Array} usedSelectors  List of Selectors found in the jsdom pages
@return {Array}                The selectors matched in the DOMs

Private function used in filterUnusedRules .


function filterEmptyAtRules(css) {
    /* Filter media queries with no remaining rules */
    css.walkAtRules((atRule) => {
        if (atRule.name === 'media' && atRule.nodes.length === 0) {
            atRule.remove();
        }
    });
}

Filter rules with no selectors remaining
@param  {Object} css             The postcss.Root node
@return {Array}


function filterUnusedRules(pages, css, ignore, usedSelectors) {
    let ignoreNextRule = false,
        ignoreNextRulesStart = false,
        unusedRules = [],
        unusedRuleSelectors,
        usedRuleSelectors;
    /* Rule format:
     *  { selectors: [ '...', '...' ],
     *    declarations: [ { property: '...', value: '...' } ]
     *  },.
     * Two steps: filter the unused selectors for each rule,
     *            filter the rules with no selectors
     */
    ignoreNextRule = false;
    css.walk((rule) => {
        if (rule.type === 'comment') {
            if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
                ignoreNextRulesStart = true;
            } else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
                ignoreNextRulesStart = false;
            } else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
                ignoreNextRule = true;
            }
        } else if (rule.type === 'rule') {
            if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
                // Don't remove animation keyframes that have selector names of '30%' or 'to'
                return;
            }
            if (ignoreNextRulesStart) {
                ignore = ignore.concat(rule.selectors);
            } else if (ignoreNextRule) {
                ignoreNextRule = false;
                ignore = ignore.concat(rule.selectors);
            }

            usedRuleSelectors = filterUnusedSelectors(
                rule.selectors,
                ignore,
                usedSelectors
            );
            unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
            if (unusedRuleSelectors && unusedRuleSelectors.length) {
                unusedRules.push({
                    type: 'rule',
                    selectors: unusedRuleSelectors,
                    position: rule.source
                });
            }
            if (usedRuleSelectors.length === 0) {
                rule.remove();
            } else {
                rule.selectors = usedRuleSelectors;
            }
        }
    });

    /* Filter the @media rules with no rules */
    filterEmptyAtRules(css);

    /* Filter unused @keyframes */
    filterKeyframes(css, unusedRules);

    return css;
}

Remove css rules not used in the dom
@param  {Array}  pages           List of jsdom pages
@param  {Object} css             The postcss.Root node
@param  {Array}  ignore          List of selectors to be ignored
@param  {Array}  usedSelectors   List of selectors that are found in {pages}
@return {Object}                 A css_parse-compatible stylesheet


function parseUncssrc(filename) {
    let options = JSON.parse(fs.readFileSync(filename, 'utf-8'));

    /* RegExps can't be stored as JSON, therefore we need to parse them manually.
     * A string is a RegExp if it starts with '/', since that wouldn't be a valid CSS selector.
     */
    options.ignore = options.ignore ? options.ignore.map(strToRegExp) : undefined;
    options.ignoreSheets = options.ignoreSheets ? options.ignoreSheets.map(strToRegExp) : undefined;

    return options;
}

Parse a given uncssrc file.
@param  {String} filename The location of the uncssrc file
@return {Object}          The options object


function parsePaths(source, stylesheets, options) {
    return stylesheets.map((sheet) => {
        let sourceProtocol;
        const isLocalFile = sheet.substr(0, 5) === 'file:';

        if (sheet.substr(0, 4) === 'http') {
            /* No need to parse, it's already a valid path */
            return sheet;
        }

        /* Check if we are fetching over http(s) */
        if (isURL(source) && !isLocalFile) {
            sourceProtocol = url.parse(source).protocol;

            if (sheet.substr(0, 2) === '//') {
                /* Use the same protocol we used for fetching this page.
                 * Default to http.
                 */
                return sourceProtocol ? sourceProtocol + sheet : 'http:' + sheet;
            }
            return url.resolve(source, sheet);
        }

        /* We are fetching local files
         * Should probably report an error if we find an absolute path and
         *   have no htmlroot specified.
         */
        /* Fix the case when there is a query string or hash */
        sheet = sheet.split('?')[0].split('#')[0];

        /* Path already parsed by jsdom or user supplied local file */
        if (isLocalFile) {
            sheet = url.parse(sheet).path.replace('%20', ' ');
            /* If on windows, remove first '/' */
            sheet = isWindows() ? sheet.substring(1) : sheet;

            if (options.htmlroot) {
                return path.join(options.htmlroot, sheet);
            }
            sheet = path.relative(path.join(path.dirname(source)), sheet);
        }

        if (sheet[0] === '/' && options.htmlroot) {
            return path.join(options.htmlroot, sheet);
        } else if (isHTML(source)) {
            return path.join(options.csspath, sheet);
        }
        return path.join(path.dirname(source), options.csspath, sheet);
    });
}

Parse paths relatives to a source.
@param  {String} source      Where the paths originate from
@param  {Array}  stylesheets List of paths
@param  {Object} options     Options, as passed to UnCSS
@return {Array}              List of paths


function readStylesheets(files, outputBanner) {
    return Promise.all(files.map((filename) => {
        if (isURL(filename)) {
            return new Promise((resolve, reject) => {
                request({
                    url: filename,
                    headers: { 'User-Agent': 'UnCSS' }
                }, (err, response, body) => {
                    if (err) {
                        return reject(err);
                    }
                    return resolve(body);
                });
            });
        } else if (fs.existsSync(filename)) {
            return new Promise((resolve, reject) => {
                fs.readFile(filename, 'utf-8', (err, contents) => {
                    if (err) {
                        return reject(err);
                    }
                    return resolve(contents);
                });
            });
        }
        throw new Error(`UnCSS: could not open ${path.join(process.cwd(), filename)}`);
    })).then((res) => {
        // res is an array of the content of each file in files (in the same order)
        if (outputBanner) {
            for (let i = 0, len = files.length; i < len; i++) {
                // We append a small banner to keep track of which file we are currently processing
                // super helpful for debugging
                const banner = `/*** uncss> filename: ${files[i].replace(/\\/g, '/')} ***/\n`;
                res[i] = banner + res[i];
            }
        }
        return res;
    });
}

Given an array of filenames, return an array of the files' contents,
only if the filename matches a regex
@param  {Array}   files  An array of the filenames to read
@return {Promise}

Given an array of filenames return an array of the files contents only if the filename matches a regex


function getHTML(files, options) {
    if (_.isString(files)) {
        files = [files];
    }

    files = _.flatten(files.map((file) => {
        if (!isURL(file) && !isHTML(file)) {
            return glob.sync(file);
        }
        return file;
    }));

    if (!files.length) {
        return Promise.reject(new Error('UnCSS: no HTML files found'));
    }

    // Save files for later reference.
    options.files = files;
    return Promise.all(files.map((file) => jsdom.fromSource(file, options)));
}

function
getHTML
(
files
,
options
)
{
if
(
_
.
isString
(
files
)
)
{
files
=
[
files
]
;
}
files
=
_
.
flatten
(
files
.
map
(
(
file
)
=>
{
if
(
!
isURL
(
file
)
&&
!
isHTML
(
file
)
)
{
return
glob
.
sync
(
file
)
;
}
return
file
;
}
)
)
;
if
(
!
files
.
length
)
{
return
Promise
.
reject
(
new
Error
(
'UnCSS: no HTML files found'
)
)
;
}
// Save files for later reference.
options
.
files
=
files
;
return
Promise
.
all
(
files
.
map
(
(
file
)
=>
jsdom
.
fromSource
(
file
,
options
)
)
)
;
}

Get the contents of HTML pages through jsdom.
@param  {Array}   files   List of HTML files
@param  {Object}  options UnCSS options
@return {Array|Promise}

Get the contents of HTML pages through jsdom .


function processWithTextApi([options, pages, stylesheets]) {
    /* If we specified a raw string of CSS, add it to the stylesheets array */
    if (options.raw) {
        if (_.isString(options.raw)) {
            stylesheets.push(options.raw);
        } else {
            throw new Error('UnCSS: options.raw - expected a string');
        }
    }

    /* At this point, there isn't any point in running the rest of the task if:
     * - We didn't specify any stylesheet links in the options object
     * - We couldn't find any stylesheet links in the HTML itself
     * - We weren't passed a string of raw CSS in addition to, or to replace
     *     either of the above
     */
    if (!_.flatten(stylesheets).length) {
        throw new Error('UnCSS: no stylesheets found');
    }

    /* OK, so we have some CSS to work with!
     * Three steps:
     * - Parse the CSS
     * - Remove the unused rules
     * - Return the optimized CSS as a string
     */
    const cssStr = stylesheets.join(' \n');
    let pcss,
        report;

    try {
        pcss = postcss.parse(cssStr);
    } catch (err) {
        /* Try and construct a helpful error message */
        throw utility.parseErrorMessage(err, cssStr);
    }
    return uncss(pages, pcss, options.ignore).then(([css, rep]) => {
        let newCssStr = '';
        postcss.stringify(css, (result) => {
            newCssStr += result;
        });

        if (options.report) {
            report = {
                original: cssStr,
                selectors: rep
            };
        }
        return [newCssStr, report];
    });
}

Do the actual work
@param  {Array}   files       List of HTML files
@param  {Object}  options     UnCSS options
@param  {Array}   pages       Pages opened by jsdom
@param  {Array}   stylesheets List of CSS files
@return {Promise}


function init(files, options, callback) {

    if (_.isFunction(options)) {
        /* There were no options, this argument is actually the callback */
        callback = options;
        options = {};
    } else if (!_.isFunction(callback)) {
        throw new TypeError('UnCSS: expected a callback');
    }

    /* Try and read options from the specified uncssrc file */
    if (options.uncssrc) {
        try {
            /* Manually-specified options take precedence over uncssrc options */
            options = _.merge(utility.parseUncssrc(options.uncssrc), options);
        } catch (err) {
            if (err instanceof SyntaxError) {
                callback(new SyntaxError('UnCSS: uncssrc file is invalid JSON.'));
                return;
            }
            callback(err);
            return;
        }
    }

    /* Assign default values to options, unless specified */
    options = _.defaults(options, {
        csspath: '',
        ignore: [],
        media: [],
        timeout: 0,
        report: false,
        ignoreSheets: [],
        html: files,
        banner: true,
        // gulp-uncss parameters:
        raw: null,
        userAgent: 'uncss',
        inject: null
    });

    process(options).then(([css, report]) => callback(null, css, report), callback);
}

Main exposed function.
Here we check the options and callback, then run the files through jsdom.
@param  {Array}    files     Array of filenames
@param  {Object}   [options] options
@param  {Function} callback(Error, String, Object)

Main exposed function . Here we check the options and callback then run the files through jsdom .


function fromSource(src, options) {
    const config = {
        features: {
            FetchExternalResources: ['script'],
            ProcessExternalResources: ['script']
        },
        virtualConsole: jsdom.createVirtualConsole().sendTo(console),
        userAgent: options.userAgent
    };

    // The htmlroot option allows root-relative URLs (starting with a slash)
    // to be used for all resources. Without it, root-relative URLs are
    // looked up relative to file://, so will not be found.
    if (options.htmlroot) {
        config.resourceLoader = function(resource, callback) {
            // See whether raw attribute value is root-relative.
            const src = resource.element.getAttribute('src');
            if (src.indexOf('/') === 0) {
                resource.url.pathname = path.join(options.htmlroot, src);
            }

            return resource.defaultFetch(callback);
        };
    }

    if (options.inject) {
        config.onload = function(window) {
            if (typeof options.inject === 'function') {
                options.inject(window);
            } else {
                require(path.join(__dirname, options.inject))(window);
            }
        };
    }

    return new Promise((resolve, reject) => {
        jsdom.env(src, config, (err, res) => {
            if (err) {
                return reject(err);
            }
            return resolve(res);
        });
    }).then((result) => {
        return new Promise((resolve) => {
            setTimeout(() => resolve(result), options.timeout);
        });
    });
}

Load a page.
@param  {String}  src
@param  {Object}  options
@return {Promise}


function getStylesheets(window, options) {
    if (Array.isArray(options.media) === false) {
        options.media = [options.media];
    }

    const media = _.union(['', 'all', 'screen'], options.media);
    const elements = window.document.querySelectorAll('link[rel="stylesheet"]');

    return Array.prototype.map.call(elements, (link) => ({
        href: link.getAttribute('href'),
        media: link.getAttribute('media') || ''
    }))
    .filter((sheet) => media.indexOf(sheet.media) !== -1)
    .map((sheet) => sheet.href);
}

function getStylesheets ( window , options ) { if ( Array . isArray ( options . media ) === false ) { options . media = [ options . media ] ; } const media = _ . union ( [ '' , 'all' , 'screen' ] , options . media ) ; const elements = window . document . querySelectorAll ( 'link[rel="stylesheet"]' ) ; return Array . prototype . map . call ( elements , ( link ) => ( { href : link . getAttribute ( 'href' ) , media : link . getAttribute ( 'media' ) || '' } ) ) . filter ( ( sheet ) => media . indexOf ( sheet . media ) !== - 1 ) . map ( ( sheet ) => sheet . href ) ; }

Extract stylesheets' hrefs from dom
@param  {Object}  window     A jsdom window
@param  {Object}  options    Options, as passed to UnCSS
@return {Array}


function findAll(window, sels) {
    const document = window.document;

    // Unwrap noscript elements.
    const elements = document.getElementsByTagName('noscript');
    Array.prototype.forEach.call(elements, (ns) => {
        const wrapper = document.createElement('div');
        wrapper.innerHTML = ns.textContent;
        // Insert each child of the <noscript> as its sibling
        Array.prototype.forEach.call(wrapper.children, (child) => {
            ns.parentNode.insertBefore(child, ns);
        });
    });

    // Do the filtering.
    return sels.filter((selector) => {
        try {
            return document.querySelector(selector);
        } catch (e) {
            return true;
        }
    });
}

Filter unused selectors.
@param  {Object}  window A jsdom window
@param  {Array}   sels   List of selectors to be filtered
@return {Array}


function openDB(name, version, { blocked, upgrade, blocking } = {}) {
    const request = indexedDB.open(name, version);
    const openPromise = wrap(request);
    if (upgrade) {
        request.addEventListener('upgradeneeded', (event) => {
            upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction));
        });
    }
    if (blocked)
        request.addEventListener('blocked', () => blocked());
    if (blocking)
        openPromise.then(db => db.addEventListener('versionchange', blocking));
    return openPromise;
}

function openDB ( name , version , { blocked , upgrade , blocking } = { } ) { const request = indexedDB . open ( name , version ) ; const openPromise = wrap ( request ) ; if ( upgrade ) { request . addEventListener ( 'upgradeneeded' , ( event ) => { upgrade ( wrap ( request . result ) , event . oldVersion , event . newVersion , wrap ( request . transaction ) ) ; } ) ; } if ( blocked ) request . addEventListener ( 'blocked' , ( ) => blocked ( ) ) ; if ( blocking ) openPromise . then ( db => db . addEventListener ( 'versionchange' , blocking ) ) ; return openPromise ; }

Open a database.

@param name Name of the database.
@param version Schema version.
@param callbacks Additional callbacks.


function deleteDB(name, { blocked } = {}) {
    const request = indexedDB.deleteDatabase(name);
    if (blocked)
        request.addEventListener('blocked', () => blocked());
    return wrap(request).then(() => undefined);
}

function deleteDB ( name , { blocked } = { } ) { const request = indexedDB . deleteDatabase ( name ) ; if ( blocked ) request . addEventListener ( 'blocked' , ( ) => blocked ( ) ) ; return wrap ( request ) . then ( ( ) => undefined ) ; }

Delete a database.

@param name Name of the database.


function getCursorAdvanceMethods() {
    return cursorAdvanceMethods || (cursorAdvanceMethods = [
        IDBCursor.prototype.advance,
        IDBCursor.prototype.continue,
        IDBCursor.prototype.continuePrimaryKey,
    ]);
}

function getCursorAdvanceMethods ( ) { return cursorAdvanceMethods || ( cursorAdvanceMethods = [ IDBCursor . prototype . advance , IDBCursor . prototype . continue , IDBCursor . prototype . continuePrimaryKey , ] ) ; }

This is a function to prevent it throwing up in node environments.

This is a function to prevent it throwing up in node environments .


function resolveOrReject(data) {
    if (data.filename) {
        console.warn(data);
    }
    if (!options.async) {
        head.removeChild(style);
    }
}

function resolveOrReject ( data ) { if ( data . filename ) { console . warn ( data ) ; } if ( ! options . async ) { head . removeChild ( style ) ; } }


function(t) {
        return function() {
            var obj = Object.create(t.prototype);
            t.apply(obj, Array.prototype.slice.call(arguments, 0));
            return obj;
        };
    }

function ( t ) { return function ( ) { var obj = Object . create ( t . prototype ) ; t . apply ( obj , Array . prototype . slice . call ( arguments , 0 ) ) ; return obj ; } ; }


function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
        var newSelectorPath, lastSelector, newJoinedSelector;
        // our new selector path
        newSelectorPath = [];

        // construct the joined selector - if & is the first thing this will be empty,
        // if not newJoinedSelector will be the last set of elements in the selector
        if (beginningPath.length > 0) {
            newSelectorPath = utils.copyArray(beginningPath);
            lastSelector = newSelectorPath.pop();
            newJoinedSelector = originalSelector.createDerived(utils.copyArray(lastSelector.elements));
        }
        else {
            newJoinedSelector = originalSelector.createDerived([]);
        }

        if (addPath.length > 0) {
            // /deep/ is a CSS4 selector - (removed, so should deprecate)
            // that is valid without anything in front of it
            // so if the & does not have a combinator that is "" or " " then
            // and there is a combinator on the parent, then grab that.
            // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
            var combinator = replacedElement.combinator, parentEl = addPath[0].elements[0];
            if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
                combinator = parentEl.combinator;
            }
            // join the elements so far with the first part of the parent
            newJoinedSelector.elements.push(new Element(
                combinator,
                parentEl.value,
                replacedElement.isVariable,
                replacedElement._index,
                replacedElement._fileInfo
            ));
            newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
        }

        // now add the joined selector - but only if it is not empty
        if (newJoinedSelector.elements.length !== 0) {
            newSelectorPath.push(newJoinedSelector);
        }

        // put together the parent selectors after the join (e.g. the rest of the parent)
        if (addPath.length > 1) {
            var restOfPath = addPath.slice(1);
            restOfPath = restOfPath.map(function (selector) {
                return selector.createDerived(selector.elements, []);
            });
            newSelectorPath = newSelectorPath.concat(restOfPath);
        }
        return newSelectorPath;
    }

joins selector path from `beginningPath` with selector path in `addPath` `replacedElement` contains element that is being replaced by `addPath` returns concatenated path

joins selector path from beginningPath with selector path in addPath replacedElement contains element that is being replaced by addPath returns concatenated path


function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) {
        var j;
        for (j = 0; j < beginningPath.length; j++) {
            var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
            result.push(newSelectorPath);
        }
        return result;
    }

function addAllReplacementsIntoPath ( beginningPath , addPaths , replacedElement , originalSelector , result ) { var j ; for ( j = 0 ; j < beginningPath . length ; j ++ ) { var newSelectorPath = addReplacementIntoPath ( beginningPath [ j ] , addPaths , replacedElement , originalSelector ) ; result . push ( newSelectorPath ) ; } return result ; }

joins selector path from `beginningPath` with every selector path in `addPaths` array `replacedElement` contains element that is being replaced by `addPath` returns array with all concatenated paths

joins selector path from beginningPath with every selector path in addPaths array replacedElement contains element that is being replaced by addPath returns array with all concatenated paths


function replaceParentSelector(paths, context, inSelector) {
        // The paths are [[Selector]]
        // The first list is a list of comma separated selectors
        // The inner list is a list of inheritance separated selectors
        // e.g.
        // .a, .b {
        //   .c {
        //   }
        // }
        // == [[.a] [.c]] [[.b] [.c]]
        //
        var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
        function findNestedSelector(element) {
            var maybeSelector;
            if (!(element.value instanceof Paren)) {
                return null;
            }

            maybeSelector = element.value.value;
            if (!(maybeSelector instanceof Selector)) {
                return null;
            }

            return maybeSelector;
        }

        // the elements from the current selector so far
        currentElements = [];
        // the current list of new selectors to add to the path.
        // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
        // by the parents
        newSelectors = [
            []
        ];

        for (i = 0; (el = inSelector.elements[i]); i++) {
            // non parent reference elements just get added
            if (el.value !== '&') {
                var nestedSelector = findNestedSelector(el);
                if (nestedSelector != null) {
                    // merge the current list of non parent selector elements
                    // on to the current list of selectors to add
                    mergeElementsOnToSelectors(currentElements, newSelectors);

                    var nestedPaths = [], replaced, replacedNewSelectors = [];
                    replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
                    hadParentSelector = hadParentSelector || replaced;
                    // the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
                    for (k = 0; k < nestedPaths.length; k++) {
                        var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
                        addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
                    }
                    newSelectors = replacedNewSelectors;
                    currentElements = [];

                } else {
                    currentElements.push(el);
                }

            } else {
                hadParentSelector = true;
                // the new list of selectors to add
                selectorsMultiplied = [];

                // merge the current list of non parent selector elements
                // on to the current list of selectors to add
                mergeElementsOnToSelectors(currentElements, newSelectors);

                // loop through our current selectors
                for (j = 0; j < newSelectors.length; j++) {
                    sel = newSelectors[j];
                    // if we don't have any parent paths, the & might be in a mixin so that it can be used
                    // whether there are parents or not
                    if (context.length === 0) {
                        // the combinator used on el should now be applied to the next element instead so that
                        // it is not lost
                        if (sel.length > 0) {
                            sel[0].elements.push(new Element(el.combinator, '', el.isVariable, el._index, el._fileInfo));
                        }
                        selectorsMultiplied.push(sel);
                    }
                    else {
                        // and the parent selectors
                        for (k = 0; k < context.length; k++) {
                            // We need to put the current selectors
                            // then join the last selector's elements on to the parents selectors
                            var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
                            // add that to our new set of selectors
                            selectorsMultiplied.push(newSelectorPath);
                        }
                    }
                }

                // our new selectors has been multiplied, so reset the state
                newSelectors = selectorsMultiplied;
                currentElements = [];
            }
        }

        // if we have any elements left over (e.g. .a& .b == .b)
        // add them on to all the current selectors
        mergeElementsOnToSelectors(currentElements, newSelectors);

        for (i = 0; i < newSelectors.length; i++) {
            length = newSelectors[i].length;
            if (length > 0) {
                paths.push(newSelectors[i]);
                lastSelector = newSelectors[i][length - 1];
                newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
            }
        }

        return hadParentSelector;
    }

replace all parent selectors inside `inSelector` by content of `context` array resulting selectors are returned inside `paths` array returns true if `inSelector` contained at least one parent selector

replace all parent selectors inside inSelector by content of context array resulting selectors are returned inside paths array returns true if inSelector contained at least one parent selector


function (value, unit) {
    this.value = parseFloat(value);
    if (isNaN(this.value)) {
        throw new Error('Dimension is not a number.');
    }
    this.unit = (unit && unit instanceof Unit) ? unit :
      new Unit(unit ? [unit] : undefined);
    this.setParent(this.unit, this);
}

function
(
value
,
unit
)
{
this
.
value
=
parseFloat
(
value
)
;
if
(
isNaN
(
this
.
value
)
)
{
throw
new
Error
(
'Dimension is not a number.'
)
;
}
this
.
unit
=
(
unit
&&
unit
instanceof
Unit
)
unit
:
new
Unit
(
unit
[
unit
]
:
undefined
)
;
this
.
setParent
(
this
.
unit
,
this
)
;
}


function parseNode(str, parseList, currentIndex, fileInfo, callback) {
        var result, returnNodes = [];
        var parser = parserInput;

        try {
            parser.start(str, false, function fail(msg, index) {
                callback({
                    message: msg,
                    index: index + currentIndex
                });
            });
            for (var x = 0, p, i; (p = parseList[x]); x++) {
                i = parser.i;
                result = parsers[p]();
                if (result) {
                    result._index = i + currentIndex;
                    result._fileInfo = fileInfo;
                    returnNodes.push(result);
                }
                else {
                    returnNodes.push(null);
                }
            }

            var endInfo = parser.end();
            if (endInfo.isFinished) {
                callback(null, returnNodes);
            }
            else {
                callback(true, null);
            }
        } catch (e) {
            throw new LessError({
                index: e.index + currentIndex,
                message: e.message
            }, imports, fileInfo.filename);
        }
    }

function parseNode ( str , parseList , currentIndex , fileInfo , callback ) { var result , returnNodes = [ ] ; var parser = parserInput ; try { parser . start ( str , false , function fail ( msg , index ) { callback ( { message : msg , index : index + currentIndex } ) ; } ) ; for ( var x = 0 , p , i ; ( p = parseList [ x ] ) ; x ++ ) { i = parser . i ; result = parsers [ p ] ( ) ; if ( result ) { result . _index = i + currentIndex ; result . _fileInfo = fileInfo ; returnNodes . push ( result ) ; } else { returnNodes . push ( null ) ; } } var endInfo = parser . end ( ) ; if ( endInfo . isFinished ) { callback ( null , returnNodes ) ; } else { callback ( true , null ) ; } } catch ( e ) { throw new LessError ( { index : e . index + currentIndex , message : e . message } , imports , fileInfo . filename ) ; } }

Used after initial parsing to create nodes on the fly

@param {String} str          - string to parse
@param {Array}  parseList    - array of parsers to run input through e.g. ["value", "important"]
@param {Number} currentIndex - start number to begin indexing
@param {Object} fileInfo     - fileInfo to attach to created nodes


function (str, callback, additionalData) {
            var root, error = null, globalVars, modifyVars, ignored, preText = '';

            globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : '';
            modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : '';

            if (context.pluginManager) {
                var preProcessors = context.pluginManager.getPreProcessors();
                for (var i = 0; i < preProcessors.length; i++) {
                    str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
                }
            }

            if (globalVars || (additionalData && additionalData.banner)) {
                preText = ((additionalData && additionalData.banner) ? additionalData.banner : '') + globalVars;
                ignored = imports.contentsIgnoredChars;
                ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
                ignored[fileInfo.filename] += preText.length;
            }

            str = str.replace(/\r\n?/g, '\n');
            // Remove potential UTF Byte Order Mark
            str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
            imports.contents[fileInfo.filename] = str;

            // Start with the primary rule.
            // The whole syntax tree is held under a Ruleset node,
            // with the `root` property set to true, so no `{}` are
            // output. The callback is called when the input is parsed.
            try {
                parserInput.start(str, context.chunkInput, function fail(msg, index) {
                    throw new LessError({
                        index: index,
                        type: 'Parse',
                        message: msg,
                        filename: fileInfo.filename
                    }, imports);
                });

                tree.Node.prototype.parse = this;
                root = new tree.Ruleset(null, this.parsers.primary());
                tree.Node.prototype.rootNode = root;
                root.root = true;
                root.firstRoot = true;
                root.functionRegistry = functionRegistry.inherit();

            } catch (e) {
                return callback(new LessError(e, imports, fileInfo.filename));
            }

            // If `i` is smaller than the `input.length - 1`,
            // it means the parser wasn't able to parse the whole
            // string, so we've got a parsing error.
            //
            // We try to extract a \n delimited string,
            // showing the line where the parse error occurred.
            // We split it up into two parts (the part which parsed,
            // and the part which didn't), so we can color them differently.
            var endInfo = parserInput.end();
            if (!endInfo.isFinished) {

                var message = endInfo.furthestPossibleErrorMessage;

                if (!message) {
                    message = 'Unrecognised input';
                    if (endInfo.furthestChar === '}') {
                        message += '. Possibly missing opening \'{\'';
                    } else if (endInfo.furthestChar === ')') {
                        message += '. Possibly missing opening \'(\'';
                    } else if (endInfo.furthestReachedEnd) {
                        message += '. Possibly missing something';
                    }
                }

                error = new LessError({
                    type: 'Parse',
                    message: message,
                    index: endInfo.furthest,
                    filename: fileInfo.filename
                }, imports);
            }

            var finish = function (e) {
                e = error || e || imports.error;

                if (e) {
                    if (!(e instanceof LessError)) {
                        e = new LessError(e, imports, fileInfo.filename);
                    }

                    return callback(e);
                }
                else {
                    return callback(null, root);
                }
            };

            if (context.processImports !== false) {
                new visitors.ImportVisitor(imports, finish)
                    .run(root);
            } else {
                return finish();
            }
        }

Parse an input string into an abstract syntax tree, @param str A string containing 'less' markup @param callback call `callback` when done. @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply


function () {
                if (parserInput.commentStore.length) {
                    var comment = parserInput.commentStore.shift();
                    return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
                }
            }

function ( ) { if ( parserInput . commentStore . length ) { var comment = parserInput . commentStore . shift ( ) ; return new ( tree . Comment ) ( comment . text , comment . isLineComment , comment . index , fileInfo ) ; } }

comments are collected by the main parsing mechanism and then assigned to nodes where the current structure allows it


function (forceEscaped) {
                    var str, index = parserInput.i, isEscaped = false;

                    parserInput.save();
                    if (parserInput.$char('~')) {
                        isEscaped = true;
                    } else if (forceEscaped) {
                        parserInput.restore();
                        return;
                    }

                    str = parserInput.$quoted();
                    if (!str) {
                        parserInput.restore();
                        return;
                    }
                    parserInput.forget();

                    return new(tree.Quoted)(str.charAt(0), str.substr(1, str.length - 2), isEscaped, index, fileInfo);
                }

function ( forceEscaped ) { var str , index = parserInput . i , isEscaped = false ; parserInput . save ( ) ; if ( parserInput . $char ( '~' ) ) { isEscaped = true ; } else if ( forceEscaped ) { parserInput . restore ( ) ; return ; } str = parserInput . $quoted ( ) ; if ( ! str ) { parserInput . restore ( ) ; return ; } parserInput . forget ( ) ; return new ( tree . Quoted ) ( str . charAt ( 0 ) , str . substr ( 1 , str . length - 2 ) , isEscaped , index , fileInfo ) ; }

A string, which supports escaping " and '  "milky way" 'he\'s the one!'

A string which supports escaping and milky way he \ s the one!


function () {
                    var ch, name, index = parserInput.i;

                    parserInput.save();
                    if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
                        ch = parserInput.currentChar();
                        if (ch === '(' || ch === '[' && !parserInput.prevChar().match(/^\s/)) {
                            // this may be a VariableCall lookup
                            var result = parsers.variableCall(name);
                            if (result) {
                                parserInput.forget();
                                return result;
                            }
                        }
                        parserInput.forget();
                        return new(tree.Variable)(name, index, fileInfo);
                    }
                    parserInput.restore();
                }

function
(
)
{
var
ch
,
name
,
index
=
parserInput
.
i
;
parserInput
.
save
(
)
;
if
(
parserInput
.
currentChar
(
)
===
'@'
&&
(
name
=
parserInput
.
$re
(
/
^@@?[\w-]+
/
)
)
)
{
ch
=
parserInput
.
currentChar
(
)
;
if
(
ch
===
'('
||
ch
===
'['
&&
!
parserInput
.
prevChar
(
)
.
match
(
/
^\s
/
)
)
{
// this may be a VariableCall lookup
var
result
=
parsers
.
variableCall
(
name
)
;
if
(
result
)
{
parserInput
.
forget
(
)
;
return
result
;
}
}
parserInput
.
forget
(
)
;
return
new
(
tree
.
Variable
)
(
name
,
index
,
fileInfo
)
;
}
parserInput
.
restore
(
)
;
}

A Variable entity, such as `@fink`, in  width: @fink + 2px  We use a different parser for variable definitions, see `parsers.variable`.


function () {
                    if (parserInput.peekNotNumeric()) {
                        return;
                    }

                    var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z_]+)?/i);
                    if (value) {
                        return new(tree.Dimension)(value[1], value[2]);
                    }
                }

function ( ) { if ( parserInput . peekNotNumeric ( ) ) { return ; } var value = parserInput . $re ( / ^([+-]?\d*\.?\d+)(%|[a-z_]+)? / i ) ; if ( value ) { return new ( tree . Dimension ) ( value [ 1 ] , value [ 2 ] ) ; } }

A Dimension, that is, a number and a unit  0.5em 95%

A Dimension that is a number and a unit 0 . 5em 95%


function () {
                    var js, index = parserInput.i;

                    parserInput.save();

                    var escape = parserInput.$char('~');
                    var jsQuote = parserInput.$char('`');

                    if (!jsQuote) {
                        parserInput.restore();
                        return;
                    }

                    js = parserInput.$re(/^[^`]*`/);
                    if (js) {
                        parserInput.forget();
                        return new(tree.JavaScript)(js.substr(0, js.length - 1), Boolean(escape), index, fileInfo);
                    }
                    parserInput.restore('invalid javascript definition');
                }

function
(
)
{
var
js
,
index
=
parserInput
.
i
;
parserInput
.
save
(
)
;
var
escape
=
parserInput
.
$char
(
'~'
)
;
var
jsQuote
=
parserInput
.
$char
(
'`'
)
;
if
(
!
jsQuote
)
{
parserInput
.
restore
(
)
;
return
;
}
js
=
parserInput
.
$re
(
/
^[^`]*`
/
)
;
if
(
js
)
{
parserInput
.
forget
(
)
;
return
new
(
tree
.
JavaScript
)
(
js
.
substr
(
0
,
js
.
length
1
)
,
Boolean
(
escape
)
,
index
,
fileInfo
)
;
}
parserInput
.
restore
(
'invalid javascript definition'
)
;
}

JavaScript code to be evaluated  `window.location.href`

JavaScript code to be evaluated window . location . href


function (parsedName) {
                var lookups, important, i = parserInput.i,
                    inValue = !!parsedName, name = parsedName;

                parserInput.save();

                if (name || (parserInput.currentChar() === '@'
                    && (name = parserInput.$re(/^(@[\w-]+)(\(\s*\))?/)))) {

                    lookups = this.mixin.ruleLookups();

                    if (!lookups && ((inValue && parserInput.$str('()') !== '()') || (name[2] !== '()'))) {
                        parserInput.restore('Missing \'[...]\' lookup in variable call');
                        return;
                    }

                    if (!inValue) {
                        name = name[1];
                    }

                    if (lookups && parsers.important()) {
                        important = true;
                    }

                    var call = new tree.VariableCall(name, i, fileInfo);
                    if (!inValue && parsers.end()) {
                        parserInput.forget();
                        return call;
                    }
                    else {
                        parserInput.forget();
                        return new tree.NamespaceValue(call, lookups, important, i, fileInfo);
                    }
                }

                parserInput.restore();
            }

Call a variable value to retrieve a detached ruleset or a value from a detached ruleset's rules.  @fink(); @fink; color: @fink[@color];

Call a variable value to retrieve a detached ruleset or a value from a detached ruleset s rules .


function () {
                    var name, params = [], match, ruleset, cond, variadic = false;
                    if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
                        parserInput.peek(/^[^{]*\}/)) {
                        return;
                    }

                    parserInput.save();

                    match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
                    if (match) {
                        name = match[1];

                        var argInfo = this.args(false);
                        params = argInfo.args;
                        variadic = argInfo.variadic;

                        // .mixincall("@{a}");
                        // looks a bit like a mixin definition..
                        // also
                        // .mixincall(@a: {rule: set;});
                        // so we have to be nice and restore
                        if (!parserInput.$char(')')) {
                            parserInput.restore('Missing closing \')\'');
                            return;
                        }

                        parserInput.commentStore.length = 0;

                        if (parserInput.$str('when')) { // Guard
                            cond = expect(parsers.conditions, 'expected condition');
                        }

                        ruleset = parsers.block();

                        if (ruleset) {
                            parserInput.forget();
                            return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
                        } else {
                            parserInput.restore();
                        }
                    } else {
                        parserInput.forget();
                    }
                }

A Mixin definition, with a list of parameters  .rounded (@radius: 2px, @color) { ... }  Until we have a finer grained state-machine, we have to do a look-ahead, to make sure we don't have a mixin call. See the `rule` function for more information.  We start by matching `.rounded (`, and then proceed on to the argument list, which has optional default values. We store the parameters in `params`, with a `value` key, if there is a value, such as in the case of `@radius`.  Once we've got our params list, and a closing `)`, we parse the `{...}` block.

A Mixin definition with a list of parameters . rounded (


function () {
                var entities = this.entities;

                return this.comment() || entities.literal() || entities.variable() || entities.url() ||
                    entities.property() || entities.call() || entities.keyword() || this.mixin.call(true) ||
                    entities.javascript();
            }

function ( ) { var entities = this . entities ; return this . comment ( ) || entities . literal ( ) || entities . variable ( ) || entities . url ( ) || entities . property ( ) || entities . call ( ) || entities . keyword ( ) || this . mixin . call ( true ) || entities . javascript ( ) ; }

Entities are the smallest recognized token, and can be found inside a rule's value.

Entities are the smallest recognized token and can be found inside a rule s value .


function () {
                var index = parserInput.i, name, value, rules, nonVendorSpecificName,
                    hasIdentifier, hasExpression, hasUnknown, hasBlock = true, isRooted = true;

                if (parserInput.currentChar() !== '@') { return; }

                value = this['import']() || this.plugin() || this.media();
                if (value) {
                    return value;
                }

                parserInput.save();

                name = parserInput.$re(/^@[a-z-]+/);

                if (!name) { return; }

                nonVendorSpecificName = name;
                if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
                    nonVendorSpecificName = '@' + name.slice(name.indexOf('-', 2) + 1);
                }

                switch (nonVendorSpecificName) {
                    case '@charset':
                        hasIdentifier = true;
                        hasBlock = false;
                        break;
                    case '@namespace':
                        hasExpression = true;
                        hasBlock = false;
                        break;
                    case '@keyframes':
                    case '@counter-style':
                        hasIdentifier = true;
                        break;
                    case '@document':
                    case '@supports':
                        hasUnknown = true;
                        isRooted = false;
                        break;
                    default:
                        hasUnknown = true;
                        break;
                }

                parserInput.commentStore.length = 0;

                if (hasIdentifier) {
                    value = this.entity();
                    if (!value) {
                        error('expected ' + name + ' identifier');
                    }
                } else if (hasExpression) {
                    value = this.expression();
                    if (!value) {
                        error('expected ' + name + ' expression');
                    }
                } else if (hasUnknown) {
                    value = this.permissiveValue(/^[{;]/);
                    hasBlock = (parserInput.currentChar() === '{');
                    if (!value) {
                        if (!hasBlock && parserInput.currentChar() !== ';') {
                            error(name + ' rule is missing block or ending semi-colon');
                        }
                    }
                    else if (!value.value) {
                        value = null;
                    }
                }

                if (hasBlock) {
                    rules = this.blockRuleset();
                }

                if (rules || (!hasBlock && value && parserInput.$char(';'))) {
                    parserInput.forget();
                    return new (tree.AtRule)(name, value, rules, index, fileInfo,
                        context.dumpLineNumbers ? getDebugInfo(index) : null,
                        isRooted
                    );
                }

                parserInput.restore('at-rule options not recognised');
            }


function () {
                var entities = this.entities, negate;

                if (parserInput.peek(/^-[@\$\(]/)) {
                    negate = parserInput.$char('-');
                }

                var o = this.sub() || entities.dimension() ||
                        entities.color() || entities.variable() ||
                        entities.property() || entities.call() ||
                        entities.quoted(true) || entities.colorKeyword() ||
                        entities.mixinLookup();

                if (negate) {
                    o.parensInOp = true;
                    o = new(tree.Negative)(o);
                }

                return o;
            }

function ( ) { var entities = this . entities , negate ; if ( parserInput . peek ( / ^-[@\$\(] / ) ) { negate = parserInput . $char ( '-' ) ; } var o = this . sub ( ) || entities . dimension ( ) || entities . color ( ) || entities . variable ( ) || entities . property ( ) || entities . call ( ) || entities . quoted ( true ) || entities . colorKeyword ( ) || entities . mixinLookup ( ) ; if ( negate ) { o . parensInOp = true ; o = new ( tree . Negative ) ( o ) ; } return o ; }

An operand is anything that can be part of an operation, such as a Color, or a Variable

An operand is anything that can be part of an operation such as a Color or a Variable


function () {
                var entities = [], e, delim, index = parserInput.i;

                do {
                    e = this.comment();
                    if (e) {
                        entities.push(e);
                        continue;
                    }
                    e = this.addition() || this.entity();
                    if (e) {
                        entities.push(e);
                        // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
                        if (!parserInput.peek(/^\/[\/*]/)) {
                            delim = parserInput.$char('/');
                            if (delim) {
                                entities.push(new(tree.Anonymous)(delim, index));
                            }
                        }
                    }
                } while (e);
                if (entities.length > 0) {
                    return new(tree.Expression)(entities);
                }
            }

Expressions either represent mathematical operations, or white-space delimited Entities.  1px solid black @var * 2

Expressions either represent mathematical operations or white - space delimited Entities . 1px solid black


function (name, args, index, currentFileInfo) {
    this.name = name;
    this.args = args;
    this.calc = name === 'calc';
    this._index = index;
    this._fileInfo = currentFileInfo;
}

function ( name , args , index , currentFileInfo ) { this . name = name ; this . args = args ; this . calc = name === 'calc' ; this . _index = index ; this . _fileInfo = currentFileInfo ; }


function(less) {
    this.less = less;
    this.require = function(prefix) {
        prefix = path.dirname(prefix);
        return function(id) {
            var str = id.substr(0, 2);
            if (str === '..' || str === './') {
                return require(path.join(prefix, id));
            }
            else {
                return require(id);
            }
        };
    };
}

function ( less ) { this . less = less ; this . require = function ( prefix ) { prefix = path . dirname ( prefix ) ; return function ( id ) { var str = id . substr ( 0 , 2 ) ; if ( str === '..' || str === './' ) { return require ( path . join ( prefix , id ) ) ; } else { return require ( id ) ; } } ; } ; }


function(less, context, rootFileInfo) {
        this.less = less;
        this.rootFilename = rootFileInfo.filename;
        this.paths = context.paths || [];  // Search paths, when importing
        this.contents = {};             // map - filename to contents of all the files
        this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
        this.mime = context.mime;
        this.error = null;
        this.context = context;
        // Deprecated? Unused outside of here, could be useful.
        this.queue = [];        // Files which haven't been imported yet
        this.files = {};        // Holds the imported parse trees.
    }

FileInfo = { 'rewriteUrls' - option - whether to adjust URL's to be relative 'filename' - full resolved filename of current file 'rootpath' - path to append to normal URLs for this node 'currentDirectory' - path to the current file, absolute 'rootFilename' - filename of the base file 'entryPath' - absolute path to the entry file 'reference' - whether the file should not be output and only output parts that are referenced

FileInfo = { rewriteUrls - option - whether to adjust URL s to be relative filename - full resolved filename of current file rootpath - path to append to normal URLs for this node currentDirectory - path to the current file absolute rootFilename - filename of the base file entryPath - absolute path to the entry file reference - whether the file should not be output and only output parts that are referenced


function hasFakeRuleset(atRuleNode) {
            var bodyRules = atRuleNode.rules;
            return bodyRules.length === 1 && (!bodyRules[0].paths || bodyRules[0].paths.length === 0);
        }

function hasFakeRuleset ( atRuleNode ) { var bodyRules = atRuleNode . rules ; return bodyRules . length === 1 && ( ! bodyRules [ 0 ] . paths || bodyRules [ 0 ] . paths . length === 0 ) ; }

if there is only one nested ruleset and that one has no path, then it is just fake ruleset

if there is only one nested ruleset and that one has no path then it is just fake ruleset


function bind(func, thisArg) {
        var curryArgs = Array.prototype.slice.call(arguments, 2);
        return function() {
            var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0));
            return func.apply(thisArg, args);
        };
    }

function bind ( func , thisArg ) { var curryArgs = Array . prototype . slice . call ( arguments , 2 ) ; return function ( ) { var args = curryArgs . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ; return func . apply ( thisArg , args ) ; } ; }


function getNodeTransitions(oldData, nextData) {
  const oldDataKeyed = oldData && getKeyedData(oldData);
  const nextDataKeyed = nextData && getKeyedData(nextData);

  return {
    entering: oldDataKeyed && getKeyedDataDifference(nextDataKeyed, oldDataKeyed),
    exiting: nextDataKeyed && getKeyedDataDifference(oldDataKeyed, nextDataKeyed)
  };
}

function getNodeTransitions ( oldData , nextData ) { const oldDataKeyed = oldData && getKeyedData ( oldData ) ; const nextDataKeyed = nextData && getKeyedData ( nextData ) ; return { entering : oldDataKeyed && getKeyedDataDifference ( nextDataKeyed , oldDataKeyed ) , exiting : nextDataKeyed && getKeyedDataDifference ( oldDataKeyed , nextDataKeyed ) } ; }

Calculate which data-points exist in oldData and not nextData -
these are the `exiting` data-points.  Also calculate which
data-points exist in nextData and not oldData - these are the
`entering` data-points.

@param  {Array} oldData   this.props.data Array
@param  {Array} nextData  this.props.data Array

@return {Object}          Object with `entering` and `exiting` properties.
entering[datum.key] will be true if the data is
entering, and similarly for `exiting`.

Calculate which data - points exist in oldData and not nextData - these are the exiting data - points . Also calculate which data - points exist in nextData and not oldData - these are the entering data - points .


function getInitialTransitionState(oldChildren, nextChildren) {
  let nodesWillExit = false;
  let nodesWillEnter = false;

  const getTransition = (oldChild, newChild) => {
    if (!newChild || oldChild.type !== newChild.type) {
      return {};
    }

    const { entering, exiting } =
      getNodeTransitions(getChildData(oldChild), getChildData(newChild)) || {};

    nodesWillExit = nodesWillExit || !!exiting;
    nodesWillEnter = nodesWillEnter || !!entering;

    return { entering: entering || false, exiting: exiting || false };
  };

  const getTransitionsFromChildren = (old, next) => {
    return old.map((child, idx) => {
      if (child && child.props && child.props.children && next[idx]) {
        return getTransitionsFromChildren(
          React.Children.toArray(old[idx].props.children),
          React.Children.toArray(next[idx].props.children)
        );
      }
      // get Transition entering and exiting nodes
      return getTransition(child, next[idx]);
    });
  };

  const childrenTransitions = getTransitionsFromChildren(
    React.Children.toArray(oldChildren),
    React.Children.toArray(nextChildren)
  );
  return {
    nodesWillExit,
    nodesWillEnter,
    childrenTransitions,
    // TODO: This may need to be refactored for the following situation.
    //       The component receives new props, and the data provided
    //       is a perfect match for the previous data and domain except
    //       for new nodes. In this case, we wouldn't want a delay before
    //       the new nodes appear.
    nodesShouldEnter: false
  };
}

If a parent component has animation enabled, calculate the transitions
for any data of any child component that supports data transitions
Data transitions are defined as any two datasets where data nodes exist
in the first set and not the second, in the second and not the first,
or both.

@param  {Children}  oldChildren   this.props.children from old props
@param  {Children}  nextChildren  this.props.children from next props

@return {Object}                  Object with the following properties:
- nodesWillExit
- nodesWillEnter
- childrenTransitions
- nodesShouldEnter

If a parent component has animation enabled calculate the transitions for any data of any child component that supports data transitions Data transitions are defined as any two datasets where data nodes exist in the first set and not the second in the second and not the first or both .


function omit(originalObject, keys = []) {
  // code based on babel's _objectWithoutProperties
  const newObject = {};
  for (const key in originalObject) {
    if (keys.indexOf(key) >= 0) {
      continue;
    }
    if (!Object.prototype.hasOwnProperty.call(originalObject, key)) {
      continue;
    }
    newObject[key] = originalObject[key];
  }
  return newObject;
}

Exported Functions
creates an object with some keys excluded
replacement for lodash.omit for performance. does not mimick the entire lodash.omit api
@param {Object} originalObject: created object will be based on this object
@param {Array<String>} keys: an array of keys to omit from the new object
@returns {Object} new object with same properties as originalObject

Exported Functions creates an object with some keys excluded replacement for lodash . omit for performance . does not mimick the entire lodash . omit api


function createDomainFunction(getDomainFromDataFunction, formatDomainFunction) {
  getDomainFromDataFunction = isFunction(getDomainFromDataFunction)
    ? getDomainFromDataFunction
    : getDomainFromData;
  formatDomainFunction = isFunction(formatDomainFunction) ? formatDomainFunction : formatDomain;
  return (props, axis) => {
    const propsDomain = getDomainFromProps(props, axis);
    if (propsDomain) {
      return formatDomainFunction(propsDomain, props, axis);
    }
    const categories = Data.getCategories(props, axis);
    const domain = categories
      ? getDomainFromCategories(props, axis, categories)
      : getDomainFromDataFunction(props, axis);
    return domain ? formatDomainFunction(domain, props, axis) : undefined;
  };
}

function createDomainFunction ( getDomainFromDataFunction , formatDomainFunction ) { getDomainFromDataFunction = isFunction ( getDomainFromDataFunction ) ? getDomainFromDataFunction : getDomainFromData ; formatDomainFunction = isFunction ( formatDomainFunction ) ? formatDomainFunction : formatDomain ; return ( props , axis ) => { const propsDomain = getDomainFromProps ( props , axis ) ; if ( propsDomain ) { return formatDomainFunction ( propsDomain , props , axis ) ; } const categories = Data . getCategories ( props , axis ) ; const domain = categories ? getDomainFromCategories ( props , axis , categories ) : getDomainFromDataFunction ( props , axis ) ; return domain ? formatDomainFunction ( domain , props , axis ) : undefined ; } ; }

Public Methods
Returns a getDomain function
@param {Function} getDomainFromDataFunction: a function that takes props and axis and
returns a domain based on data
@param {Function} formatDomainFunction: a function that takes domain, props, and axis and
returns a formatted domain
@returns {Function} a function that takes props and axis and returns a formatted domain


function formatDomain(domain, props, axis) {
  return cleanDomain(padDomain(domain, props, axis), props, axis);
}

function formatDomain ( domain , props , axis ) { return cleanDomain ( padDomain ( domain , props , axis ) , props , axis ) ; }

Returns a formatted domain.
@param {Array} domain: a domain in the form of a two element array
@param {Object} props: the props object
@param {String} axis: the current axis
@returns {Array} a domain in the form of a two element array


function getDomainFromCategories(props, axis, categories) {
  categories = categories || Data.getCategories(props, axis);
  const { polar, startAngle = 0, endAngle = 360 } = props;
  if (!categories) {
    return undefined;
  }
  const minDomain = getMinFromProps(props, axis);
  const maxDomain = getMaxFromProps(props, axis);
  const stringArray = Collection.containsStrings(categories)
    ? Data.getStringsFromCategories(props, axis)
    : [];
  const stringMap =
    stringArray.length === 0
      ? null
      : stringArray.reduce((memo, string, index) => {
          memo[string] = index + 1;
          return memo;
        }, {});
  const categoryValues = stringMap ? categories.map((value) => stringMap[value]) : categories;
  const min = minDomain !== undefined ? minDomain : Collection.getMinValue(categoryValues);
  const max = maxDomain !== undefined ? maxDomain : Collection.getMaxValue(categoryValues);
  const categoryDomain = getDomainFromMinMax(min, max);
  return polar && axis === "x" && Math.abs(startAngle - endAngle) === 360
    ? getSymmetricDomain(categoryDomain, categoryValues)
    : categoryDomain;
}

function getDomainFromCategories ( props , axis , categories ) { categories = categories || Data . getCategories ( props , axis ) ; const { polar , startAngle = 0 , endAngle = 360 } = props ; if ( ! categories ) { return undefined ; } const minDomain = getMinFromProps ( props , axis ) ; const maxDomain = getMaxFromProps ( props , axis ) ; const stringArray = Collection . containsStrings ( categories ) ? Data . getStringsFromCategories ( props , axis ) : [ ] ; const stringMap = stringArray . length === 0 ? null : stringArray . reduce ( ( memo , string , index ) => { memo [ string ] = index + 1 ; return memo ; } , { } ) ; const categoryValues = stringMap ? categories . map ( ( value ) => stringMap [ value ] ) : categories ; const min = minDomain !== undefined ? minDomain : Collection . getMinValue ( categoryValues ) ; const max = maxDomain !== undefined ? maxDomain : Collection . getMaxValue ( categoryValues ) ; const categoryDomain = getDomainFromMinMax ( min , max ) ; return polar && axis === "x" && Math . abs ( startAngle - endAngle ) === 360 ? getSymmetricDomain ( categoryDomain , categoryValues ) : categoryDomain ; }

Returns a domain based on categories if they exist
@param {Object} props: the props object
@param {String} axis: the current axis
@param {Array} categories: an array of categories corresponding to a given axis
@returns {Array|undefined} returns a domain from categories or undefined


function getDomainFromData(props, axis, dataset) {
  dataset = dataset || Data.getData(props);
  const { polar, startAngle = 0, endAngle = 360 } = props;
  const minDomain = getMinFromProps(props, axis);
  const maxDomain = getMaxFromProps(props, axis);
  if (dataset.length < 1) {
    return minDomain !== undefined && maxDomain !== undefined
      ? getDomainFromMinMax(minDomain, maxDomain)
      : undefined;
  }
  const min = minDomain !== undefined ? minDomain : getExtremeFromData(dataset, axis, "min");
  const max = maxDomain !== undefined ? maxDomain : getExtremeFromData(dataset, axis, "max");
  const domain = getDomainFromMinMax(min, max);
  return polar && axis === "x" && Math.abs(startAngle - endAngle) === 360
    ? getSymmetricDomain(domain, getFlatData(dataset, axis))
    : domain;
}

function getDomainFromData ( props , axis , dataset ) { dataset = dataset || Data . getData ( props ) ; const { polar , startAngle = 0 , endAngle = 360 } = props ; const minDomain = getMinFromProps ( props , axis ) ; const maxDomain = getMaxFromProps ( props , axis ) ; if ( dataset . length < 1 ) { return minDomain !== undefined && maxDomain !== undefined ? getDomainFromMinMax ( minDomain , maxDomain ) : undefined ; } const min = minDomain !== undefined ? minDomain : getExtremeFromData ( dataset , axis , "min" ) ; const max = maxDomain !== undefined ? maxDomain : getExtremeFromData ( dataset , axis , "max" ) ; const domain = getDomainFromMinMax ( min , max ) ; return polar && axis === "x" && Math . abs ( startAngle - endAngle ) === 360 ? getSymmetricDomain ( domain , getFlatData ( dataset , axis ) ) : domain ; }

Returns a domain from a dataset for a given axis
@param {Object} props: the props object
@param {String} axis: the current axis
@param {Array} dataset: an array of data
@returns {Array} the domain based on data


function getDomainFromMinMax(min, max) {
  const getSinglePointDomain = (val) => {
    // d3-scale does not properly resolve very small differences.
    // eslint-disable-next-line no-magic-numbers
    const verySmallNumber = val === 0 ? 2 * Math.pow(10, -10) : Math.pow(10, -10);
    const verySmallDate = 1;
    const minVal = val instanceof Date ? new Date(+val - verySmallDate) : +val - verySmallNumber;
    const maxVal = val instanceof Date ? new Date(+val + verySmallDate) : +val + verySmallNumber;
    return val === 0 ? [0, maxVal] : [minVal, maxVal];
  };
  return +min === +max ? getSinglePointDomain(max) : [min, max];
}

Returns a domain in the form of a two element array given a min and max value.
@param {Number|Date} min: the props object
@param {Number|Date} max: the current axis
@returns {Array} the minDomain based on props

Returns a domain in the form of a two element array given a min and max value .


function getDomainFromProps(props, axis) {
  const minDomain = getMinFromProps(props, axis);
  const maxDomain = getMaxFromProps(props, axis);
  if (isPlainObject(props.domain) && props.domain[axis]) {
    return props.domain[axis];
  } else if (Array.isArray(props.domain)) {
    return props.domain;
  } else if (minDomain !== undefined && maxDomain !== undefined) {
    return getDomainFromMinMax(minDomain, maxDomain);
  }
  return undefined;
}

function getDomainFromProps ( props , axis ) { const minDomain = getMinFromProps ( props , axis ) ; const maxDomain = getMaxFromProps ( props , axis ) ; if ( isPlainObject ( props . domain ) && props . domain [ axis ] ) { return props . domain [ axis ] ; } else if ( Array . isArray ( props . domain ) ) { return props . domain ; } else if ( minDomain !== undefined && maxDomain !== undefined ) { return getDomainFromMinMax ( minDomain , maxDomain ) ; } return undefined ; }

Returns a the domain for a given axis if domain is given in props
@param {Object} props: the props object
@param {String} axis: the current axis
@returns {Array|undefined} the domain based on props


function getDomainWithZero(props, axis) {
  const propsDomain = getDomainFromProps(props, axis);
  if (propsDomain) {
    return propsDomain;
  }

  const dataset = Data.getData(props);
  const y0Min = dataset.reduce((min, datum) => (datum._y0 < min ? datum._y0 : min), Infinity);

  const ensureZero = (domain) => {
    if (axis === "x") {
      return domain;
    }

    const defaultMin = y0Min !== Infinity ? y0Min : 0;
    const maxDomainProp = getMaxFromProps(props, axis);
    const minDomainProp = getMinFromProps(props, axis);
    const max =
      maxDomainProp !== undefined ? maxDomainProp : Collection.getMaxValue(domain, defaultMin);
    const min =
      minDomainProp !== undefined ? minDomainProp : Collection.getMinValue(domain, defaultMin);

    return getDomainFromMinMax(min, max);
  };

  const getDomainFunction = () => {
    return getDomainFromData(props, axis, dataset);
  };

  const formatDomainFunction = (domain) => {
    return formatDomain(ensureZero(domain), props, axis);
  };

  return createDomainFunction(getDomainFunction, formatDomainFunction)(props, axis);
}

function getDomainWithZero ( props , axis ) { const propsDomain = getDomainFromProps ( props , axis ) ; if ( propsDomain ) { return propsDomain ; } const dataset = Data . getData ( props ) ; const y0Min = dataset . reduce ( ( min , datum ) => ( datum . _y0 < min ? datum . _y0 : min ) , Infinity ) ; const ensureZero = ( domain ) => { if ( axis === "x" ) { return domain ; } const defaultMin = y0Min !== Infinity ? y0Min : 0 ; const maxDomainProp = getMaxFromProps ( props , axis ) ; const minDomainProp = getMinFromProps ( props , axis ) ; const max = maxDomainProp !== undefined ? maxDomainProp : Collection . getMaxValue ( domain , defaultMin ) ; const min = minDomainProp !== undefined ? minDomainProp : Collection . getMinValue ( domain , defaultMin ) ; return getDomainFromMinMax ( min , max ) ; } ; const getDomainFunction = ( ) => { return getDomainFromData ( props , axis , dataset ) ; } ; const formatDomainFunction = ( domain ) => { return formatDomain ( ensureZero ( domain ) , props , axis ) ; } ; return createDomainFunction ( getDomainFunction , formatDomainFunction ) ( props , axis ) ; }

Returns a domain for a given axis. This method forces the domain to include
zero unless the domain is explicitly specified in props.
@param {Object} props: the props object
@param {String} axis: the current axis
@returns {Array} the domain for the given axis

Returns a domain for a given axis . This method forces the domain to include zero unless the domain is explicitly specified in props .


function getMaxFromProps(props, axis) {
  if (isPlainObject(props.maxDomain) && props.maxDomain[axis] !== undefined) {
    return props.maxDomain[axis];
  }
  return typeof props.maxDomain === "number" ? props.maxDomain : undefined;
}

function getMaxFromProps ( props , axis ) { if ( isPlainObject ( props . maxDomain ) && props . maxDomain [ axis ] !== undefined ) { return props . maxDomain [ axis ] ; } return typeof props . maxDomain === "number" ? props . maxDomain : undefined ; }

Returns the maxDomain from props if it exists
@param {Object} props: the props object
@param {String} axis: the current axis
@returns {Number|undefined} the maxDomain based on props


function getMinFromProps(props, axis) {
  if (isPlainObject(props.minDomain) && props.minDomain[axis] !== undefined) {
    return props.minDomain[axis];
  }
  return typeof props.minDomain === "number" ? props.minDomain : undefined;
}

function getMinFromProps ( props , axis ) { if ( isPlainObject ( props . minDomain ) && props . minDomain [ axis ] !== undefined ) { return props . minDomain [ axis ] ; } return typeof props . minDomain === "number" ? props . minDomain : undefined ; }

Returns the minDomain from props if it exists
@param {Object} props: the props object
@param {String} axis: the current axis
@returns {Number|undefined} the minDomain based on props


function getSymmetricDomain(domain, values) {
  const processedData = sortedUniq(values.sort((a, b) => a - b));
  const step = processedData[1] - processedData[0];
  return [domain[0], domain[1] + step];
}

function getSymmetricDomain ( domain , values ) { const processedData = sortedUniq ( values . sort ( ( a , b ) => a - b ) ) ; const step = processedData [ 1 ] - processedData [ 0 ] ; return [ domain [ 0 ] , domain [ 1 ] + step ] ; }

Returns a symmetrically padded domain for polar charts
@param {Array} domain: the original domain
@param {Array} values: a flat array of values corresponding to either tickValues, or data values
for a given dimension i.e. only x values.
@returns {Array} the symmetric domain


function isDomainComponent(component) {
  const getRole = (child) => {
    return child && child.type ? child.type.role : "";
  };
  let role = getRole(component);
  if (role === "portal") {
    const children = React.Children.toArray(component.props.children);
    role = children.length ? getRole(children[0]) : "";
  }
  const whitelist = [
    "area",
    "axis",
    "bar",
    "boxplot",
    "candlestick",
    "errorbar",
    "group",
    "line",
    "pie",
    "scatter",
    "stack",
    "voronoi"
  ];
  return includes(whitelist, role);
}

function isDomainComponent ( component ) { const getRole = ( child ) => { return child && child . type ? child . type . role : "" ; } ; let role = getRole ( component ) ; if ( role === "portal" ) { const children = React . Children . toArray ( component . props . children ) ; role = children . length ? getRole ( children [ 0 ] ) : "" ; } const whitelist = [ "area" , "axis" , "bar" , "boxplot" , "candlestick" , "errorbar" , "group" , "line" , "pie" , "scatter" , "stack" , "voronoi" ] ; return includes ( whitelist , role ) ; }

Checks whether a given component can be used to calculate domain
@param {Component} component: a React component instance
@returns {Boolean} Returns true if the given component has a role included in the whitelist


function generateDataArray(props, axis) {
  const propsDomain = isPlainObject(props.domain) ? props.domain[axis] : props.domain;
  const domain = propsDomain || Scale.getBaseScale(props, axis).domain();
  const samples = props.samples || 1;
  const domainMax = Math.max(...domain);
  const domainMin = Math.min(...domain);
  const step = (domainMax - domainMin) / samples;
  const values = range(domainMin, domainMax, step);
  return last(values) === domainMax ? values : values.concat(domainMax);
}

function generateDataArray ( props , axis ) { const propsDomain = isPlainObject ( props . domain ) ? props . domain [ axis ] : props . domain ; const domain = propsDomain || Scale . getBaseScale ( props , axis ) . domain ( ) ; const samples = props . samples || 1 ; const domainMax = Math . max ( ... domain ) ; const domainMin = Math . min ( ... domain ) ; const step = ( domainMax - domainMin ) / samples ; const values = range ( domainMin , domainMax , step ) ; return last ( values ) === domainMax ? values : values . concat ( domainMax ) ; }

Returns generated data for a given axis based on domain and sample from props


function sortData(dataset, sortKey, sortOrder = "ascending") {
  if (!sortKey) {
    return dataset;
  }

  // Ensures previous VictoryLine api for sortKey prop stays consistent
  if (sortKey === "x" || sortKey === "y") {
    sortKey = `_${sortKey}`;
  }
  const order = sortOrder === "ascending" ? "asc" : "desc";
  return orderBy(dataset, sortKey, order);
}

Returns sorted data. If no sort keys are provided, data is returned unaltered.

Returns sorted data . If no sort keys are provided data is returned unaltered .


function getEventKey(key) {
  // creates a data accessor function
  // given a property key, path, array index, or null for identity.
  if (isFunction(key)) {
    return key;
  } else if (key === null || key === undefined) {
    return () => undefined;
  }
  // otherwise, assume it is an array index, property key or path (_.property handles all three)
  return property(key);
}

Returns a data accessor given an eventKey prop