const fs = require('fs') const path = require('path') const fastGlob = require('fast-glob') const parseGlob = require('parse-glob') const sharedState = require('./sharedState') const { generateRules } = require('./generateRules') const { bigSign, cloneNodes } = require('./utils') let env = sharedState.env let contentMatchCache = sharedState.contentMatchCache const BROAD_MATCH_GLOBAL_REGEXP = /[^<>"'`\s]*[^<>"'`\s:]/g const INNER_MATCH_GLOBAL_REGEXP = /[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g function getDefaultExtractor(fileExtension) { return function (content) { if (fileExtension === 'svelte') { content = content.replace(/\sclass:/g, ' ') } let broadMatches = content.match(BROAD_MATCH_GLOBAL_REGEXP) || [] let innerMatches = content.match(INNER_MATCH_GLOBAL_REGEXP) || [] return [...broadMatches, ...innerMatches] } } function getExtractor(fileName, tailwindConfig) { const purgeOptions = tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.options const fileExtension = path.extname(fileName).slice(1) if (!purgeOptions) { return getDefaultExtractor(fileExtension) } const fileSpecificExtractor = (purgeOptions.extractors || []).find((extractor) => extractor.extensions.includes(fileExtension) ) if (fileSpecificExtractor) { return fileSpecificExtractor.extractor } return purgeOptions.defaultExtractor || getDefaultExtractor(fileExtension) } // Scans template contents for possible classes. This is a hot path on initial build but // not too important for subsequent builds. The faster the better though — if we can speed // up these regexes by 50% that could cut initial build time by like 20%. function getClassCandidates(content, extractor, contentMatchCache, candidates, seen) { for (let line of content.split('\n')) { line = line.trim() if (seen.has(line)) { continue } seen.add(line) if (contentMatchCache.has(line)) { for (let match of contentMatchCache.get(line)) { candidates.add(match) } } else { let extractorMatches = extractor(line) let lineMatchesSet = new Set(extractorMatches) for (let match of lineMatchesSet) { candidates.add(match) } contentMatchCache.set(line, lineMatchesSet) } } } function buildStylesheet(rules, context) { let sortedRules = rules.sort(([a], [z]) => bigSign(a - z)) let returnValue = { base: new Set(), components: new Set(), utilities: new Set(), screens: new Set(), } for (let [sort, rule] of sortedRules) { if (sort >= context.minimumScreen) { returnValue.screens.add(rule) continue } if (sort & context.layerOrder.base) { returnValue.base.add(rule) continue } if (sort & context.layerOrder.components) { returnValue.components.add(rule) continue } if (sort & context.layerOrder.utilities) { returnValue.utilities.add(rule) continue } } return returnValue } function expandTailwindAtRules(context, registerDependency) { return (root) => { let foundTailwind = false let layerNodes = { base: null, components: null, utilities: null, screens: null, } // Make sure this file contains Tailwind directives. If not, we can save // a lot of work and bail early. Also we don't have to register our touch // file as a dependency since the output of this CSS does not depend on // the source of any templates. Think Vue