"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = setupContext;

var _fs = _interopRequireDefault(require("fs"));

var _url = _interopRequireDefault(require("url"));

var _os = _interopRequireDefault(require("os"));

var _path = _interopRequireDefault(require("path"));

var _crypto = _interopRequireDefault(require("crypto"));

var _chokidar = _interopRequireDefault(require("chokidar"));

var _postcss = _interopRequireDefault(require("postcss"));

var _dlv = _interopRequireDefault(require("dlv"));

var _postcssSelectorParser = _interopRequireDefault(require("postcss-selector-parser"));

var _quickLru = _interopRequireDefault(require("quick-lru"));

var _normalizePath = _interopRequireDefault(require("normalize-path"));

var _hashConfig = _interopRequireDefault(require("../../util/hashConfig"));

var _transformThemeValue = _interopRequireDefault(require("../../util/transformThemeValue"));

var _parseObjectStyles = _interopRequireDefault(require("../../util/parseObjectStyles"));

var _getModuleDependencies = _interopRequireDefault(require("../../lib/getModuleDependencies"));

var _prefixSelector = _interopRequireDefault(require("../../util/prefixSelector"));

var _resolveConfig = _interopRequireDefault(require("../../../resolveConfig"));

var _corePlugins = _interopRequireDefault(require("../corePlugins"));

var _isPlainObject = _interopRequireDefault(require("../../util/isPlainObject"));

var _escapeClassName = _interopRequireDefault(require("../../util/escapeClassName"));

var _nameClass = _interopRequireDefault(require("../../util/nameClass"));

var _pluginUtils = require("../../util/pluginUtils");

var sharedState = _interopRequireWildcard(require("./sharedState"));

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

let contextMap = sharedState.contextMap;
let configContextMap = sharedState.configContextMap;
let contextSourcesMap = sharedState.contextSourcesMap;
let env = sharedState.env; // Earmarks a directory for our touch files.
// If the directory already exists we delete any existing touch files,
// invalidating any caches associated with them.

const touchDir = env.TAILWIND_TOUCH_DIR || _path.default.join(_os.default.homedir() || _os.default.tmpdir(), '.tailwindcss', 'touch');

if (!sharedState.env.TAILWIND_DISABLE_TOUCH) {
  if (_fs.default.existsSync(touchDir)) {
    for (let file of _fs.default.readdirSync(touchDir)) {
      try {
        _fs.default.unlinkSync(_path.default.join(touchDir, file));
      } catch (_err) {}
    }
  } else {
    _fs.default.mkdirSync(touchDir, {
      recursive: true
    });
  }
} // This is used to trigger rebuilds. Just updating the timestamp
// is significantly faster than actually writing to the file (10x).


function touch(filename) {
  let time = new Date();

  try {
    _fs.default.utimesSync(filename, time, time);
  } catch (err) {
    _fs.default.closeSync(_fs.default.openSync(filename, 'w'));
  }
}

function isObject(value) {
  return typeof value === 'object' && value !== null;
}

function isEmpty(obj) {
  return Object.keys(obj).length === 0;
}

function isString(value) {
  return typeof value === 'string' || value instanceof String;
}

function toPath(value) {
  if (Array.isArray(value)) {
    return value;
  }

  let inBrackets = false;
  let parts = [];
  let chunk = '';

  for (let i = 0; i < value.length; i++) {
    let char = value[i];

    if (char === '[') {
      inBrackets = true;
      parts.push(chunk);
      chunk = '';
      continue;
    }

    if (char === ']' && inBrackets) {
      inBrackets = false;
      parts.push(chunk);
      chunk = '';
      continue;
    }

    if (char === '.' && !inBrackets && chunk.length > 0) {
      parts.push(chunk);
      chunk = '';
      continue;
    }

    chunk = chunk + char;
  }

  if (chunk.length > 0) {
    parts.push(chunk);
  }

  return parts;
}

function resolveConfigPath(pathOrConfig) {
  // require('tailwindcss')({ theme: ..., variants: ... })
  if (isObject(pathOrConfig) && pathOrConfig.config === undefined && !isEmpty(pathOrConfig)) {
    return null;
  } // require('tailwindcss')({ config: 'custom-config.js' })


  if (isObject(pathOrConfig) && pathOrConfig.config !== undefined && isString(pathOrConfig.config)) {
    return _path.default.resolve(pathOrConfig.config);
  } // require('tailwindcss')({ config: { theme: ..., variants: ... } })


  if (isObject(pathOrConfig) && pathOrConfig.config !== undefined && isObject(pathOrConfig.config)) {
    return null;
  } // require('tailwindcss')('custom-config.js')


  if (isString(pathOrConfig)) {
    return _path.default.resolve(pathOrConfig);
  } // require('tailwindcss')


  for (const configFile of ['./tailwind.config.js', './tailwind.config.cjs']) {
    try {
      const configPath = _path.default.resolve(configFile);

      _fs.default.accessSync(configPath);

      return configPath;
    } catch (err) {}
  }

  return null;
}

let configPathCache = new _quickLru.default({
  maxSize: 100
}); // Get the config object based on a path

function getTailwindConfig(configOrPath) {
  let userConfigPath = resolveConfigPath(configOrPath);

  if (sharedState.env.TAILWIND_DISABLE_TOUCH) {
    if (userConfigPath !== null) {
      let [prevConfig, prevConfigHash, prevDeps, prevModified] = configPathCache.get(userConfigPath) || [];
      let newDeps = (0, _getModuleDependencies.default)(userConfigPath).map(dep => dep.file);
      let modified = false;
      let newModified = new Map();

      for (let file of newDeps) {
        let time = _fs.default.statSync(file).mtimeMs;

        newModified.set(file, time);

        if (!prevModified || !prevModified.has(file) || time > prevModified.get(file)) {
          modified = true;
        }
      } // It hasn't changed (based on timestamps)


      if (!modified) {
        return [prevConfig, userConfigPath, prevConfigHash, prevDeps];
      } // It has changed (based on timestamps), or first run


      for (let file of newDeps) {
        delete require.cache[file];
      }

      let newConfig = (0, _resolveConfig.default)(require(userConfigPath));
      let newHash = (0, _hashConfig.default)(newConfig);
      configPathCache.set(userConfigPath, [newConfig, newHash, newDeps, newModified]);
      return [newConfig, userConfigPath, newHash, newDeps];
    } // It's a plain object, not a path


    let newConfig = (0, _resolveConfig.default)(configOrPath.config === undefined ? configOrPath : configOrPath.config);
    return [newConfig, null, (0, _hashConfig.default)(newConfig), []];
  }

  if (userConfigPath !== null) {
    let [prevConfig, prevModified = -Infinity, prevConfigHash] = configPathCache.get(userConfigPath) || [];

    let modified = _fs.default.statSync(userConfigPath).mtimeMs; // It hasn't changed (based on timestamp)


    if (modified <= prevModified) {
      return [prevConfig, userConfigPath, prevConfigHash];
    } // It has changed (based on timestamp), or first run


    delete require.cache[userConfigPath];
    let newConfig = (0, _resolveConfig.default)(require(userConfigPath));
    let newHash = (0, _hashConfig.default)(newConfig);
    configPathCache.set(userConfigPath, [newConfig, modified, newHash]);
    return [newConfig, userConfigPath, newHash];
  } // It's a plain object, not a path


  let newConfig = (0, _resolveConfig.default)(configOrPath.config === undefined ? configOrPath : configOrPath.config);
  return [newConfig, null, (0, _hashConfig.default)(newConfig)];
}

let fileModifiedMap = new Map();

function trackModified(files) {
  let changed = false;

  for (let file of files) {
    if (!file) continue;

    let pathname = _url.default.parse(file).pathname;

    let newModified = _fs.default.statSync(decodeURIComponent(pathname)).mtimeMs;

    if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
      changed = true;
    }

    fileModifiedMap.set(file, newModified);
  }

  return changed;
}

function generateTouchFileName() {
  let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  let randomChars = '';
  let randomCharsLength = 12;
  let bytes = null;

  try {
    bytes = _crypto.default.randomBytes(randomCharsLength);
  } catch (_error) {
    bytes = _crypto.default.pseudoRandomBytes(randomCharsLength);
  }

  for (let i = 0; i < randomCharsLength; i++) {
    randomChars += chars[bytes[i] % chars.length];
  }

  return _path.default.join(touchDir, `touch-${process.pid}-${randomChars}`);
}

function rebootWatcher(context) {
  if (env.TAILWIND_DISABLE_TOUCH) {
    return;
  }

  if (context.touchFile === null) {
    context.touchFile = generateTouchFileName();
    touch(context.touchFile);
  }

  if (env.TAILWIND_MODE === 'build') {
    return;
  }

  if (env.TAILWIND_MODE === 'watch' || env.TAILWIND_MODE === undefined && env.NODE_ENV === 'development') {
    Promise.resolve(context.watcher ? context.watcher.close() : null).then(() => {
      context.watcher = _chokidar.default.watch([...context.candidateFiles, ...context.configDependencies], {
        ignoreInitial: true
      });
      context.watcher.on('add', file => {
        context.changedFiles.add(_path.default.resolve('.', file));
        touch(context.touchFile);
      });
      context.watcher.on('change', file => {
        // If it was a config dependency, touch the config file to trigger a new context.
        // This is not really that clean of a solution but it's the fastest, because we
        // can do a very quick check on each build to see if the config has changed instead
        // of having to get all of the module dependencies and check every timestamp each
        // time.
        if (context.configDependencies.has(file)) {
          for (let dependency of context.configDependencies) {
            delete require.cache[require.resolve(dependency)];
          }

          touch(context.configPath);
        } else {
          context.changedFiles.add(_path.default.resolve('.', file));
          touch(context.touchFile);
        }
      });
      context.watcher.on('unlink', file => {
        // Touch the config file if any of the dependencies are deleted.
        if (context.configDependencies.has(file)) {
          for (let dependency of context.configDependencies) {
            delete require.cache[require.resolve(dependency)];
          }

          touch(context.configPath);
        }
      });
    });
  }
}

function insertInto(list, value, {
  before = []
} = {}) {
  before = [].concat(before);

  if (before.length <= 0) {
    list.push(value);
    return;
  }

  let idx = list.length - 1;

  for (let other of before) {
    let iidx = list.indexOf(other);
    if (iidx === -1) continue;
    idx = Math.min(idx, iidx);
  }

  list.splice(idx, 0, value);
}

function parseStyles(styles) {
  if (!Array.isArray(styles)) {
    return parseStyles([styles]);
  }

  return styles.flatMap(style => {
    let isNode = !Array.isArray(style) && !(0, _isPlainObject.default)(style);
    return isNode ? style : (0, _parseObjectStyles.default)(style);
  });
}

function getClasses(selector) {
  let parser = (0, _postcssSelectorParser.default)(selectors => {
    let allClasses = [];
    selectors.walkClasses(classNode => {
      allClasses.push(classNode.value);
    });
    return allClasses;
  });
  return parser.transformSync(selector);
}

function extractCandidates(node) {
  let classes = node.type === 'rule' ? getClasses(node.selector) : [];

  if (node.type === 'atrule') {
    node.walkRules(rule => {
      classes = [...classes, ...getClasses(rule.selector)];
    });
  }

  return classes;
}

function withIdentifiers(styles) {
  return parseStyles(styles).flatMap(node => {
    let nodeMap = new Map();
    let candidates = extractCandidates(node); // If this isn't "on-demandable", assign it a universal candidate.

    if (candidates.length === 0) {
      return [['*', node]];
    }

    return candidates.map(c => {
      if (!nodeMap.has(node)) {
        nodeMap.set(node, node);
      }

      return [c, nodeMap.get(node)];
    });
  });
}

function buildPluginApi(tailwindConfig, context, {
  variantList,
  variantMap,
  offsets
}) {
  function getConfigValue(path, defaultValue) {
    return path ? (0, _dlv.default)(tailwindConfig, path, defaultValue) : tailwindConfig;
  }

  function applyConfiguredPrefix(selector) {
    return (0, _prefixSelector.default)(tailwindConfig.prefix, selector);
  }

  function prefixIdentifier(identifier, options) {
    if (identifier === '*') {
      return '*';
    }

    if (!options.respectPrefix) {
      return identifier;
    }

    if (typeof context.tailwindConfig.prefix === 'function') {
      return (0, _prefixSelector.default)(context.tailwindConfig.prefix, `.${identifier}`).substr(1);
    }

    return context.tailwindConfig.prefix + identifier;
  }

  return {
    addVariant(variantName, applyThisVariant, options = {}) {
      insertInto(variantList, variantName, options);
      variantMap.set(variantName, applyThisVariant);
    },

    postcss: _postcss.default,
    prefix: applyConfiguredPrefix,
    e: _escapeClassName.default,
    config: getConfigValue,

    theme(path, defaultValue) {
      const [pathRoot, ...subPaths] = toPath(path);
      const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue);
      return (0, _transformThemeValue.default)(pathRoot)(value);
    },

    corePlugins: path => {
      if (Array.isArray(tailwindConfig.corePlugins)) {
        return tailwindConfig.corePlugins.includes(path);
      }

      return getConfigValue(['corePlugins', path], true);
    },
    variants: (path, defaultValue) => {
      if (Array.isArray(tailwindConfig.variants)) {
        return tailwindConfig.variants;
      }

      return getConfigValue(['variants', path], defaultValue);
    },

    addBase(base) {
      for (let [identifier, rule] of withIdentifiers(base)) {
        let prefixedIdentifier = prefixIdentifier(identifier, {});
        let offset = offsets.base++;

        if (!context.candidateRuleMap.has(prefixedIdentifier)) {
          context.candidateRuleMap.set(prefixedIdentifier, []);
        }

        context.candidateRuleMap.get(prefixedIdentifier).push([{
          sort: offset,
          layer: 'base'
        }, rule]);
      }
    },

    addComponents(components, options) {
      let defaultOptions = {
        variants: [],
        respectPrefix: true,
        respectImportant: false,
        respectVariants: true
      };
      options = Object.assign({}, defaultOptions, Array.isArray(options) ? {
        variants: options
      } : options);

      for (let [identifier, rule] of withIdentifiers(components)) {
        let prefixedIdentifier = prefixIdentifier(identifier, options);
        let offset = offsets.components++;

        if (!context.candidateRuleMap.has(prefixedIdentifier)) {
          context.candidateRuleMap.set(prefixedIdentifier, []);
        }

        context.candidateRuleMap.get(prefixedIdentifier).push([{
          sort: offset,
          layer: 'components',
          options
        }, rule]);
      }
    },

    addUtilities(utilities, options) {
      let defaultOptions = {
        variants: [],
        respectPrefix: true,
        respectImportant: true,
        respectVariants: true
      };
      options = Object.assign({}, defaultOptions, Array.isArray(options) ? {
        variants: options
      } : options);

      for (let [identifier, rule] of withIdentifiers(utilities)) {
        let prefixedIdentifier = prefixIdentifier(identifier, options);
        let offset = offsets.utilities++;

        if (!context.candidateRuleMap.has(prefixedIdentifier)) {
          context.candidateRuleMap.set(prefixedIdentifier, []);
        }

        context.candidateRuleMap.get(prefixedIdentifier).push([{
          sort: offset,
          layer: 'utilities',
          options
        }, rule]);
      }
    },

    matchUtilities: function (utilities, options) {
      let defaultOptions = {
        variants: [],
        respectPrefix: true,
        respectImportant: true,
        respectVariants: true
      };
      options = { ...defaultOptions,
        ...options
      };
      let offset = offsets.utilities++;

      for (let identifier in utilities) {
        let prefixedIdentifier = prefixIdentifier(identifier, options);
        let rule = utilities[identifier];

        function wrapped(modifier) {
          let {
            type = 'any'
          } = options;
          let value = (0, _pluginUtils.coerceValue)(type, modifier, options.values);

          if (value === undefined) {
            return [];
          }

          let includedRules = [];
          let ruleSets = [].concat(rule(value, {
            includeRules(rules) {
              includedRules.push(...rules);
            }

          })).filter(Boolean).map(declaration => ({
            [(0, _nameClass.default)(identifier, modifier)]: declaration
          }));
          return [...includedRules, ...ruleSets];
        }

        let withOffsets = [{
          sort: offset,
          layer: 'utilities',
          options
        }, wrapped];

        if (!context.candidateRuleMap.has(prefixedIdentifier)) {
          context.candidateRuleMap.set(prefixedIdentifier, []);
        }

        context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets);
      }
    }
  };
}

function extractVariantAtRules(node) {
  node.walkAtRules(atRule => {
    if (['responsive', 'variants'].includes(atRule.name)) {
      extractVariantAtRules(atRule);
      atRule.before(atRule.nodes);
      atRule.remove();
    }
  });
}

function collectLayerPlugins(root) {
  let layerPlugins = [];
  root.each(node => {
    if (node.type === 'atrule' && ['responsive', 'variants'].includes(node.name)) {
      node.name = 'layer';
      node.params = 'utilities';
    }
  }); // Walk @layer rules and treat them like plugins

  root.walkAtRules('layer', layerNode => {
    extractVariantAtRules(layerNode);

    if (layerNode.params === 'base') {
      for (let node of layerNode.nodes) {
        layerPlugins.push(function ({
          addBase
        }) {
          addBase(node, {
            respectPrefix: false
          });
        });
      }
    } else if (layerNode.params === 'components') {
      for (let node of layerNode.nodes) {
        layerPlugins.push(function ({
          addComponents
        }) {
          addComponents(node, {
            respectPrefix: false
          });
        });
      }
    } else if (layerNode.params === 'utilities') {
      for (let node of layerNode.nodes) {
        layerPlugins.push(function ({
          addUtilities
        }) {
          addUtilities(node, {
            respectPrefix: false
          });
        });
      }
    }
  });
  return layerPlugins;
}

function registerPlugins(tailwindConfig, plugins, context) {
  let variantList = [];
  let variantMap = new Map();
  let offsets = {
    base: 0n,
    components: 0n,
    utilities: 0n
  };
  let pluginApi = buildPluginApi(tailwindConfig, context, {
    variantList,
    variantMap,
    offsets
  });

  for (let plugin of plugins) {
    if (Array.isArray(plugin)) {
      for (let pluginItem of plugin) {
        pluginItem(pluginApi);
      }
    } else {
      plugin(pluginApi);
    }
  }

  let highestOffset = (args => args.reduce((m, e) => e > m ? e : m))([offsets.base, offsets.components, offsets.utilities]);

  let reservedBits = BigInt(highestOffset.toString(2).length);
  context.layerOrder = {
    base: 1n << reservedBits << 0n,
    components: 1n << reservedBits << 1n,
    utilities: 1n << reservedBits << 2n
  };
  reservedBits += 3n;
  context.variantOrder = variantList.reduce((map, variant, i) => map.set(variant, 1n << BigInt(i) << reservedBits), new Map());
  context.minimumScreen = [...context.variantOrder.values()].shift(); // Build variantMap

  for (let [variantName, variantFunction] of variantMap.entries()) {
    let sort = context.variantOrder.get(variantName);
    context.variantMap.set(variantName, [sort, variantFunction]);
  }
}

function cleanupContext(context) {
  if (context.watcher) {
    context.watcher.close();
  }
} // Retrieve an existing context from cache if possible (since contexts are unique per
// source path), or set up a new one (including setting up watchers and registering
// plugins) then return it


function setupContext(configOrPath) {
  return (result, root) => {
    let foundTailwind = false;
    root.walkAtRules('tailwind', () => {
      foundTailwind = true;
    });
    let sourcePath = result.opts.from;
    let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] = getTailwindConfig(configOrPath);
    let isConfigFile = userConfigPath !== null;
    let contextDependencies = new Set(sharedState.env.TAILWIND_DISABLE_TOUCH ? configDependencies : []); // If there are no @tailwind rules, we don't consider this CSS file or it's dependencies
    // to be dependencies of the context. Can reuse the context even if they change.
    // We may want to think about `@layer` being part of this trigger too, but it's tough
    // because it's impossible for a layer in one file to end up in the actual @tailwind rule
    // in another file since independent sources are effectively isolated.

    if (foundTailwind) {
      contextDependencies.add(sourcePath);

      for (let message of result.messages) {
        if (message.type === 'dependency') {
          contextDependencies.add(message.file);
        }
      }
    }

    if (sharedState.env.TAILWIND_DISABLE_TOUCH) {
      for (let file of configDependencies) {
        result.messages.push({
          type: 'dependency',
          plugin: 'tailwindcss-jit',
          parent: result.opts.from,
          file
        });
      }
    } else {
      if (isConfigFile) {
        contextDependencies.add(userConfigPath);
      }
    }

    let contextDependenciesChanged = trackModified([...contextDependencies]);
    process.env.DEBUG && console.log('Source path:', sourcePath);

    if (!contextDependenciesChanged) {
      // If this file already has a context in the cache and we don't need to
      // reset the context, return the cached context.
      if (isConfigFile && contextMap.has(sourcePath)) {
        return contextMap.get(sourcePath);
      } // If the config used already exists in the cache, return that.


      if (configContextMap.has(tailwindConfigHash)) {
        let context = configContextMap.get(tailwindConfigHash);
        contextSourcesMap.get(context).add(sourcePath);
        contextMap.set(sourcePath, context);
        return context;
      }
    } // If this source is in the context map, get the old context.
    // Remove this source from the context sources for the old context,
    // and clean up that context if no one else is using it. This can be
    // called by many processes in rapid succession, so we check for presence
    // first because the first process to run this code will wipe it out first.


    if (contextMap.has(sourcePath)) {
      let oldContext = contextMap.get(sourcePath);

      if (contextSourcesMap.has(oldContext)) {
        contextSourcesMap.get(oldContext).delete(sourcePath);

        if (contextSourcesMap.get(oldContext).size === 0) {
          contextSourcesMap.delete(oldContext);
          cleanupContext(oldContext);
        }
      }
    }

    process.env.DEBUG && console.log('Setting up new context...');
    let context = {
      changedFiles: new Set(),
      ruleCache: new Set(),
      watcher: null,
      scannedContent: false,
      touchFile: null,
      classCache: new Map(),
      applyClassCache: new Map(),
      notClassCache: new Set(),
      postCssNodeCache: new Map(),
      candidateRuleMap: new Map(),
      configPath: userConfigPath,
      tailwindConfig: tailwindConfig,
      configDependencies: new Set(),
      candidateFiles: (Array.isArray(tailwindConfig.purge) ? tailwindConfig.purge : tailwindConfig.purge.content).map(path => (0, _normalizePath.default)(path)),
      variantMap: new Map(),
      stylesheetCache: null,
      fileModifiedMap: new Map()
    }; // ---
    // Update all context tracking state

    configContextMap.set(tailwindConfigHash, context);
    contextMap.set(sourcePath, context);

    if (!contextSourcesMap.has(context)) {
      contextSourcesMap.set(context, new Set());
    }

    contextSourcesMap.get(context).add(sourcePath); // ---

    if (isConfigFile && !sharedState.env.TAILWIND_DISABLE_TOUCH) {
      for (let dependency of (0, _getModuleDependencies.default)(userConfigPath)) {
        if (dependency.file === userConfigPath) {
          continue;
        }

        context.configDependencies.add(dependency.file);
      }
    }

    rebootWatcher(context);
    let corePluginList = Object.entries(_corePlugins.default).map(([name, plugin]) => {
      if (!tailwindConfig.corePlugins.includes(name)) {
        return null;
      }

      return plugin;
    }).filter(Boolean);
    let userPlugins = tailwindConfig.plugins.map(plugin => {
      if (plugin.__isOptionsFunction) {
        plugin = plugin();
      }

      return typeof plugin === 'function' ? plugin : plugin.handler;
    });
    let layerPlugins = collectLayerPlugins(root); // TODO: This is a workaround for backwards compatibility, since custom variants
    // were historically sorted before screen/stackable variants.

    let beforeVariants = [_corePlugins.default['pseudoClassVariants']];
    let afterVariants = [_corePlugins.default['directionVariants'], _corePlugins.default['reducedMotionVariants'], _corePlugins.default['darkVariants'], _corePlugins.default['screenVariants']];
    registerPlugins(context.tailwindConfig, [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins], context);
    return context;
  };
}