mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
* remove unused file using syntax.js instead * add a way to remove files that are generated from the tests Essentially setting the restore cache contents to `null` as a special value. * combine the stdout and stderr output * add jest-diff instead of relying on it existing *somewhere* This also came with a small API change. * add `--runInBand` to the test script * add dedicated cli tests for the new Tailwind CLI
171 lines
4.9 KiB
JavaScript
171 lines
4.9 KiB
JavaScript
let { rm, existsSync } = require('fs')
|
|
let path = require('path')
|
|
let fs = require('fs/promises')
|
|
|
|
let chokidar = require('chokidar')
|
|
|
|
let resolveToolRoot = require('./resolve-tool-root')
|
|
|
|
module.exports = function ({
|
|
/** Output directory, relative to the tool. */
|
|
output = 'dist',
|
|
|
|
/** Input directory, relative to the tool. */
|
|
input = 'src',
|
|
|
|
/** Whether or not you want to cleanup the output directory. */
|
|
cleanup = true,
|
|
} = {}) {
|
|
let toolRoot = resolveToolRoot()
|
|
let fileCache = {}
|
|
|
|
let absoluteOutputFolder = path.resolve(toolRoot, output)
|
|
let absoluteInputFolder = path.resolve(toolRoot, input)
|
|
|
|
if (cleanup) {
|
|
beforeAll((done) => rm(absoluteOutputFolder, { recursive: true, force: true }, done))
|
|
afterEach((done) => rm(absoluteOutputFolder, { recursive: true, force: true }, done))
|
|
}
|
|
|
|
// Restore all written files
|
|
afterEach(async () => {
|
|
await Promise.all(
|
|
Object.entries(fileCache).map(async ([file, content]) => {
|
|
try {
|
|
if (content === null) {
|
|
return await fs.unlink(file)
|
|
} else {
|
|
return await fs.writeFile(file, content, 'utf8')
|
|
}
|
|
} catch {}
|
|
})
|
|
)
|
|
})
|
|
|
|
async function readdir(start, parent = []) {
|
|
let files = await fs.readdir(start, { withFileTypes: true })
|
|
let resolvedFiles = await Promise.all(
|
|
files.map((file) => {
|
|
if (file.isDirectory()) {
|
|
return readdir(path.resolve(start, file.name), [...parent, file.name])
|
|
}
|
|
return parent.concat(file.name).join(path.sep)
|
|
})
|
|
)
|
|
return resolvedFiles.flat(Infinity)
|
|
}
|
|
|
|
async function resolveFile(fileOrRegex, directory) {
|
|
if (fileOrRegex instanceof RegExp) {
|
|
let files = await readdir(directory)
|
|
if (files.length === 0) {
|
|
throw new Error(`No files exists in "${directory}"`)
|
|
}
|
|
|
|
let filtered = files.filter((file) => fileOrRegex.test(file))
|
|
if (filtered.length === 0) {
|
|
throw new Error(`Not a single file matched: ${fileOrRegex}`)
|
|
} else if (filtered.length > 1) {
|
|
throw new Error(`Multiple files matched: ${fileOrRegex}`)
|
|
}
|
|
|
|
return filtered[0]
|
|
}
|
|
|
|
return fileOrRegex
|
|
}
|
|
|
|
return {
|
|
cleanupFile(file) {
|
|
let filePath = path.resolve(toolRoot, file)
|
|
fileCache[filePath] = null
|
|
},
|
|
async fileExists(file) {
|
|
let filePath = path.resolve(toolRoot, file)
|
|
return existsSync(filePath)
|
|
},
|
|
async removeFile(file) {
|
|
let filePath = path.resolve(toolRoot, file)
|
|
if (!fileCache[filePath]) {
|
|
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
|
|
}
|
|
await fs.unlink(filePath)
|
|
},
|
|
async readOutputFile(file) {
|
|
file = await resolveFile(file, absoluteOutputFolder)
|
|
return fs.readFile(path.resolve(absoluteOutputFolder, file), 'utf8')
|
|
},
|
|
async appendToInputFile(file, contents) {
|
|
let filePath = path.resolve(absoluteInputFolder, file)
|
|
if (!fileCache[filePath]) {
|
|
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
|
|
}
|
|
|
|
return fs.appendFile(filePath, contents, 'utf8')
|
|
},
|
|
async writeInputFile(file, contents) {
|
|
let filePath = path.resolve(absoluteInputFolder, file)
|
|
if (!fileCache[filePath]) {
|
|
try {
|
|
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') {
|
|
fileCache[filePath] = null // Sentinel value to `delete` the file afterwards. This also means that we are writing to a `new` file inside the test.
|
|
} else {
|
|
throw err
|
|
}
|
|
}
|
|
}
|
|
|
|
return fs.writeFile(path.resolve(absoluteInputFolder, file), contents, 'utf8')
|
|
},
|
|
async waitForOutputFileCreation(file) {
|
|
if (file instanceof RegExp) {
|
|
let r = file
|
|
let watcher = chokidar.watch(absoluteOutputFolder)
|
|
|
|
return new Promise((resolve) => {
|
|
watcher.on('add', (file) => {
|
|
if (r.test(file)) {
|
|
watcher.close()
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
} else {
|
|
let filePath = path.resolve(absoluteOutputFolder, file)
|
|
let watcher = chokidar.watch(filePath)
|
|
|
|
let watcherPromise = new Promise((resolve) => {
|
|
watcher.once('add', () => {
|
|
watcher.close()
|
|
resolve()
|
|
})
|
|
})
|
|
|
|
if (existsSync(filePath)) {
|
|
watcher.close()
|
|
return Promise.resolve()
|
|
}
|
|
|
|
return watcherPromise
|
|
}
|
|
},
|
|
async waitForOutputFileChange(file, cb = () => {}) {
|
|
file = await resolveFile(file, absoluteOutputFolder)
|
|
|
|
let filePath = path.resolve(absoluteOutputFolder, file)
|
|
let watcher = chokidar.watch(filePath)
|
|
|
|
return new Promise((resolve) => {
|
|
let chain = Promise.resolve()
|
|
watcher.once('change', () => {
|
|
watcher.close()
|
|
chain.then(() => resolve())
|
|
})
|
|
chain.then(() => cb())
|
|
})
|
|
},
|
|
}
|
|
}
|