|
|
|
|
|
import fs from 'fs' |
|
import path from 'path' |
|
import isGlob from 'is-glob' |
|
import fastGlob from 'fast-glob' |
|
import normalizePath from 'normalize-path' |
|
import { parseGlob } from '../util/parseGlob' |
|
import { env } from './sharedState' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function parseCandidateFiles(context, tailwindConfig) { |
|
let files = tailwindConfig.content.files |
|
|
|
|
|
files = files.filter((filePath) => typeof filePath === 'string') |
|
files = files.map(normalizePath) |
|
|
|
|
|
let tasks = fastGlob.generateTasks(files) |
|
|
|
|
|
let included = [] |
|
|
|
|
|
let excluded = [] |
|
|
|
for (const task of tasks) { |
|
included.push(...task.positive.map((filePath) => parseFilePath(filePath, false))) |
|
excluded.push(...task.negative.map((filePath) => parseFilePath(filePath, true))) |
|
} |
|
|
|
let paths = [...included, ...excluded] |
|
|
|
|
|
paths = resolveRelativePaths(context, paths) |
|
|
|
|
|
paths = paths.flatMap(resolvePathSymlinks) |
|
|
|
|
|
paths = paths.map(resolveGlobPattern) |
|
|
|
return paths |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function parseFilePath(filePath, ignore) { |
|
let contentPath = { |
|
original: filePath, |
|
base: filePath, |
|
ignore, |
|
pattern: filePath, |
|
glob: null, |
|
} |
|
|
|
if (isGlob(filePath)) { |
|
Object.assign(contentPath, parseGlob(filePath)) |
|
} |
|
|
|
return contentPath |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function resolveGlobPattern(contentPath) { |
|
|
|
|
|
|
|
|
|
let base = normalizePath(contentPath.base) |
|
|
|
|
|
|
|
base = fastGlob.escapePath(base) |
|
|
|
contentPath.pattern = contentPath.glob ? `${base}/${contentPath.glob}` : base |
|
contentPath.pattern = contentPath.ignore ? `!${contentPath.pattern}` : contentPath.pattern |
|
|
|
return contentPath |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function resolveRelativePaths(context, contentPaths) { |
|
let resolveFrom = [] |
|
|
|
|
|
if (context.userConfigPath && context.tailwindConfig.content.relative) { |
|
resolveFrom = [path.dirname(context.userConfigPath)] |
|
} |
|
|
|
return contentPaths.map((contentPath) => { |
|
contentPath.base = path.resolve(...resolveFrom, contentPath.base) |
|
|
|
return contentPath |
|
}) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function resolvePathSymlinks(contentPath) { |
|
let paths = [contentPath] |
|
|
|
try { |
|
let resolvedPath = fs.realpathSync(contentPath.base) |
|
if (resolvedPath !== contentPath.base) { |
|
paths.push({ |
|
...contentPath, |
|
base: resolvedPath, |
|
}) |
|
} |
|
} catch { |
|
|
|
} |
|
|
|
return paths |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function resolvedChangedContent(context, candidateFiles, fileModifiedMap) { |
|
let changedContent = context.tailwindConfig.content.files |
|
.filter((item) => typeof item.raw === 'string') |
|
.map(({ raw, extension = 'html' }) => ({ content: raw, extension })) |
|
|
|
let [changedFiles, mTimesToCommit] = resolveChangedFiles(candidateFiles, fileModifiedMap) |
|
|
|
for (let changedFile of changedFiles) { |
|
let extension = path.extname(changedFile).slice(1) |
|
changedContent.push({ file: changedFile, extension }) |
|
} |
|
|
|
return [changedContent, mTimesToCommit] |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function resolveChangedFiles(candidateFiles, fileModifiedMap) { |
|
let paths = candidateFiles.map((contentPath) => contentPath.pattern) |
|
let mTimesToCommit = new Map() |
|
|
|
let changedFiles = new Set() |
|
env.DEBUG && console.time('Finding changed files') |
|
let files = fastGlob.sync(paths, { absolute: true }) |
|
for (let file of files) { |
|
let prevModified = fileModifiedMap.get(file) || -Infinity |
|
let modified = fs.statSync(file).mtimeMs |
|
|
|
if (modified > prevModified) { |
|
changedFiles.add(file) |
|
mTimesToCommit.set(file, modified) |
|
} |
|
} |
|
env.DEBUG && console.timeEnd('Finding changed files') |
|
return [changedFiles, mTimesToCommit] |
|
} |
|
|