You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
806 lines
31 KiB
806 lines
31 KiB
2 years ago
|
#!/usr/bin/env node
|
||
|
"use strict";
|
||
|
var _indexJs = require("../peers/index.js");
|
||
|
var _chokidar = _interopRequireDefault(require("chokidar"));
|
||
|
var _path = _interopRequireDefault(require("path"));
|
||
|
var _arg = _interopRequireDefault(require("arg"));
|
||
|
var _fs = _interopRequireDefault(require("fs"));
|
||
|
var _postcssLoadConfig = _interopRequireDefault(require("postcss-load-config"));
|
||
|
var _lilconfig = require("lilconfig");
|
||
|
var _plugins // Little bit scary, looking at private/internal API
|
||
|
= _interopRequireDefault(require("postcss-load-config/src/plugins"));
|
||
|
var _options // Little bit scary, looking at private/internal API
|
||
|
= _interopRequireDefault(require("postcss-load-config/src/options"));
|
||
|
var _processTailwindFeatures = _interopRequireDefault(require("./processTailwindFeatures"));
|
||
|
var _resolveConfig = _interopRequireDefault(require("../resolveConfig"));
|
||
|
var _fastGlob = _interopRequireDefault(require("fast-glob"));
|
||
|
var _getModuleDependencies = _interopRequireDefault(require("./lib/getModuleDependencies"));
|
||
|
var _log = _interopRequireDefault(require("./util/log"));
|
||
|
var _packageJson = _interopRequireDefault(require("../package.json"));
|
||
|
var _normalizePath = _interopRequireDefault(require("normalize-path"));
|
||
|
var _validateConfigJs = require("./util/validateConfig.js");
|
||
|
function _interopRequireDefault(obj) {
|
||
|
return obj && obj.__esModule ? obj : {
|
||
|
default: obj
|
||
|
};
|
||
|
}
|
||
|
let env = {
|
||
|
DEBUG: process.env.DEBUG !== undefined && process.env.DEBUG !== "0"
|
||
|
};
|
||
|
function isESM() {
|
||
|
const pkgPath = _path.default.resolve("./package.json");
|
||
|
try {
|
||
|
let pkg = JSON.parse(_fs.default.readFileSync(pkgPath, "utf8"));
|
||
|
return pkg.type && pkg.type === "module";
|
||
|
} catch (err) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
let configs = isESM() ? {
|
||
|
tailwind: "tailwind.config.cjs",
|
||
|
postcss: "postcss.config.cjs"
|
||
|
} : {
|
||
|
tailwind: "tailwind.config.js",
|
||
|
postcss: "postcss.config.js"
|
||
|
};
|
||
|
// ---
|
||
|
function indentRecursive(node, indent = 0) {
|
||
|
node.each && node.each((child, i)=>{
|
||
|
if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes("\n")) {
|
||
|
child.raws.before = `\n${node.type !== "rule" && i > 0 ? "\n" : ""}${" ".repeat(indent)}`;
|
||
|
}
|
||
|
child.raws.after = `\n${" ".repeat(indent)}`;
|
||
|
indentRecursive(child, indent + 1);
|
||
|
});
|
||
|
}
|
||
|
function formatNodes(root) {
|
||
|
indentRecursive(root);
|
||
|
if (root.first) {
|
||
|
root.first.raws.before = "";
|
||
|
}
|
||
|
}
|
||
|
async function outputFile(file, contents) {
|
||
|
if (_fs.default.existsSync(file) && await _fs.default.promises.readFile(file, "utf8") === contents) {
|
||
|
return; // Skip writing the file
|
||
|
}
|
||
|
// Write the file
|
||
|
await _fs.default.promises.writeFile(file, contents, "utf8");
|
||
|
}
|
||
|
function drainStdin() {
|
||
|
return new Promise((resolve, reject)=>{
|
||
|
let result = "";
|
||
|
process.stdin.on("data", (chunk)=>{
|
||
|
result += chunk;
|
||
|
});
|
||
|
process.stdin.on("end", ()=>resolve(result));
|
||
|
process.stdin.on("error", (err)=>reject(err));
|
||
|
});
|
||
|
}
|
||
|
function help({ message , usage , commands: commands1 , options }) {
|
||
|
let indent = 2;
|
||
|
// Render header
|
||
|
console.log();
|
||
|
console.log(`${_packageJson.default.name} v${_packageJson.default.version}`);
|
||
|
// Render message
|
||
|
if (message) {
|
||
|
console.log();
|
||
|
for (let msg of message.split("\n")){
|
||
|
console.log(msg);
|
||
|
}
|
||
|
}
|
||
|
// Render usage
|
||
|
if (usage && usage.length > 0) {
|
||
|
console.log();
|
||
|
console.log("Usage:");
|
||
|
for (let example of usage){
|
||
|
console.log(" ".repeat(indent), example);
|
||
|
}
|
||
|
}
|
||
|
// Render commands
|
||
|
if (commands1 && commands1.length > 0) {
|
||
|
console.log();
|
||
|
console.log("Commands:");
|
||
|
for (let command1 of commands1){
|
||
|
console.log(" ".repeat(indent), command1);
|
||
|
}
|
||
|
}
|
||
|
// Render options
|
||
|
if (options) {
|
||
|
let groupedOptions = {};
|
||
|
for (let [key, value] of Object.entries(options)){
|
||
|
if (typeof value === "object") {
|
||
|
groupedOptions[key] = {
|
||
|
...value,
|
||
|
flags: [
|
||
|
key
|
||
|
]
|
||
|
};
|
||
|
} else {
|
||
|
groupedOptions[value].flags.push(key);
|
||
|
}
|
||
|
}
|
||
|
console.log();
|
||
|
console.log("Options:");
|
||
|
for (let { flags: flags1 , description , deprecated } of Object.values(groupedOptions)){
|
||
|
if (deprecated) continue;
|
||
|
if (flags1.length === 1) {
|
||
|
console.log(" ".repeat(indent + 4 /* 4 = "-i, ".length */ ), flags1.slice().reverse().join(", ").padEnd(20, " "), description);
|
||
|
} else {
|
||
|
console.log(" ".repeat(indent), flags1.slice().reverse().join(", ").padEnd(24, " "), description);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
console.log();
|
||
|
}
|
||
|
function oneOf(...options) {
|
||
|
return Object.assign((value = true)=>{
|
||
|
for (let option of options){
|
||
|
let parsed = option(value);
|
||
|
if (parsed === value) {
|
||
|
return parsed;
|
||
|
}
|
||
|
}
|
||
|
throw new Error("...");
|
||
|
}, {
|
||
|
manualParsing: true
|
||
|
});
|
||
|
}
|
||
|
function loadPostcss() {
|
||
|
// Try to load a local `postcss` version first
|
||
|
try {
|
||
|
return require("postcss");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyPostcss();
|
||
|
}
|
||
|
let commands = {
|
||
|
init: {
|
||
|
run: init,
|
||
|
args: {
|
||
|
"--full": {
|
||
|
type: Boolean,
|
||
|
description: `Initialize a full \`${configs.tailwind}\` file`
|
||
|
},
|
||
|
"--postcss": {
|
||
|
type: Boolean,
|
||
|
description: `Initialize a \`${configs.postcss}\` file`
|
||
|
},
|
||
|
"-f": "--full",
|
||
|
"-p": "--postcss"
|
||
|
}
|
||
|
},
|
||
|
build: {
|
||
|
run: build,
|
||
|
args: {
|
||
|
"--input": {
|
||
|
type: String,
|
||
|
description: "Input file"
|
||
|
},
|
||
|
"--output": {
|
||
|
type: String,
|
||
|
description: "Output file"
|
||
|
},
|
||
|
"--watch": {
|
||
|
type: Boolean,
|
||
|
description: "Watch for changes and rebuild as needed"
|
||
|
},
|
||
|
"--poll": {
|
||
|
type: Boolean,
|
||
|
description: "Use polling instead of filesystem events when watching"
|
||
|
},
|
||
|
"--content": {
|
||
|
type: String,
|
||
|
description: "Content paths to use for removing unused classes"
|
||
|
},
|
||
|
"--purge": {
|
||
|
type: String,
|
||
|
deprecated: true
|
||
|
},
|
||
|
"--postcss": {
|
||
|
type: oneOf(String, Boolean),
|
||
|
description: "Load custom PostCSS configuration"
|
||
|
},
|
||
|
"--minify": {
|
||
|
type: Boolean,
|
||
|
description: "Minify the output"
|
||
|
},
|
||
|
"--config": {
|
||
|
type: String,
|
||
|
description: "Path to a custom config file"
|
||
|
},
|
||
|
"--no-autoprefixer": {
|
||
|
type: Boolean,
|
||
|
description: "Disable autoprefixer"
|
||
|
},
|
||
|
"-c": "--config",
|
||
|
"-i": "--input",
|
||
|
"-o": "--output",
|
||
|
"-m": "--minify",
|
||
|
"-w": "--watch",
|
||
|
"-p": "--poll"
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
let sharedFlags = {
|
||
|
"--help": {
|
||
|
type: Boolean,
|
||
|
description: "Display usage information"
|
||
|
},
|
||
|
"-h": "--help"
|
||
|
};
|
||
|
if (process.stdout.isTTY /* Detect redirecting output to a file */ && (process.argv[2] === undefined || process.argv.slice(2).every((flag)=>sharedFlags[flag] !== undefined))) {
|
||
|
help({
|
||
|
usage: [
|
||
|
"tailwindcss [--input input.css] [--output output.css] [--watch] [options...]",
|
||
|
"tailwindcss init [--full] [--postcss] [options...]",
|
||
|
],
|
||
|
commands: Object.keys(commands).filter((command2)=>command2 !== "build").map((command3)=>`${command3} [options]`),
|
||
|
options: {
|
||
|
...commands.build.args,
|
||
|
...sharedFlags
|
||
|
}
|
||
|
});
|
||
|
process.exit(0);
|
||
|
}
|
||
|
let command = ((arg = "")=>arg.startsWith("-") ? undefined : arg)(process.argv[2]) || "build";
|
||
|
if (commands[command] === undefined) {
|
||
|
if (_fs.default.existsSync(_path.default.resolve(command))) {
|
||
|
// TODO: Deprecate this in future versions
|
||
|
// Check if non-existing command, might be a file.
|
||
|
command = "build";
|
||
|
} else {
|
||
|
help({
|
||
|
message: `Invalid command: ${command}`,
|
||
|
usage: [
|
||
|
"tailwindcss <command> [options]"
|
||
|
],
|
||
|
commands: Object.keys(commands).filter((command4)=>command4 !== "build").map((command5)=>`${command5} [options]`),
|
||
|
options: sharedFlags
|
||
|
});
|
||
|
process.exit(1);
|
||
|
}
|
||
|
}
|
||
|
// Execute command
|
||
|
let { args: flags , run } = commands[command];
|
||
|
let args = (()=>{
|
||
|
try {
|
||
|
let result = (0, _arg).default(Object.fromEntries(Object.entries({
|
||
|
...flags,
|
||
|
...sharedFlags
|
||
|
}).filter(([_key, value])=>{
|
||
|
var ref;
|
||
|
return !(value === null || value === void 0 ? void 0 : (ref = value.type) === null || ref === void 0 ? void 0 : ref.manualParsing);
|
||
|
}).map(([key, value])=>[
|
||
|
key,
|
||
|
typeof value === "object" ? value.type : value
|
||
|
])), {
|
||
|
permissive: true
|
||
|
});
|
||
|
// Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
|
||
|
for(let i = result["_"].length - 1; i >= 0; --i){
|
||
|
let flag = result["_"][i];
|
||
|
if (!flag.startsWith("-")) continue;
|
||
|
let flagName = flag;
|
||
|
let handler = flags[flag];
|
||
|
// Resolve flagName & handler
|
||
|
while(typeof handler === "string"){
|
||
|
flagName = handler;
|
||
|
handler = flags[handler];
|
||
|
}
|
||
|
if (!handler) continue;
|
||
|
let args1 = [];
|
||
|
let offset = i + 1;
|
||
|
// Parse args for current flag
|
||
|
while(result["_"][offset] && !result["_"][offset].startsWith("-")){
|
||
|
args1.push(result["_"][offset++]);
|
||
|
}
|
||
|
// Cleanup manually parsed flags + args
|
||
|
result["_"].splice(i, 1 + args1.length);
|
||
|
// Set the resolved value in the `result` object
|
||
|
result[flagName] = handler.type(args1.length === 0 ? undefined : args1.length === 1 ? args1[0] : args1, flagName);
|
||
|
}
|
||
|
// Ensure that the `command` is always the first argument in the `args`.
|
||
|
// This is important so that we don't have to check if a default command
|
||
|
// (build) was used or not from within each plugin.
|
||
|
//
|
||
|
// E.g.: tailwindcss input.css -> _: ['build', 'input.css']
|
||
|
// E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
|
||
|
if (result["_"][0] !== command) {
|
||
|
result["_"].unshift(command);
|
||
|
}
|
||
|
return result;
|
||
|
} catch (err) {
|
||
|
if (err.code === "ARG_UNKNOWN_OPTION") {
|
||
|
help({
|
||
|
message: err.message,
|
||
|
usage: [
|
||
|
"tailwindcss <command> [options]"
|
||
|
],
|
||
|
options: sharedFlags
|
||
|
});
|
||
|
process.exit(1);
|
||
|
}
|
||
|
throw err;
|
||
|
}
|
||
|
})();
|
||
|
if (args["--help"]) {
|
||
|
help({
|
||
|
options: {
|
||
|
...flags,
|
||
|
...sharedFlags
|
||
|
},
|
||
|
usage: [
|
||
|
`tailwindcss ${command} [options]`
|
||
|
]
|
||
|
});
|
||
|
process.exit(0);
|
||
|
}
|
||
|
run();
|
||
|
// ---
|
||
|
function init() {
|
||
|
let messages = [];
|
||
|
var ref;
|
||
|
let tailwindConfigLocation = _path.default.resolve((ref = args["_"][1]) !== null && ref !== void 0 ? ref : `./${configs.tailwind}`);
|
||
|
if (_fs.default.existsSync(tailwindConfigLocation)) {
|
||
|
messages.push(`${_path.default.basename(tailwindConfigLocation)} already exists.`);
|
||
|
} else {
|
||
|
let stubFile = _fs.default.readFileSync(args["--full"] ? _path.default.resolve(__dirname, "../stubs/defaultConfig.stub.js") : _path.default.resolve(__dirname, "../stubs/simpleConfig.stub.js"), "utf8");
|
||
|
// Change colors import
|
||
|
stubFile = stubFile.replace("../colors", "tailwindcss/colors");
|
||
|
_fs.default.writeFileSync(tailwindConfigLocation, stubFile, "utf8");
|
||
|
messages.push(`Created Tailwind CSS config file: ${_path.default.basename(tailwindConfigLocation)}`);
|
||
|
}
|
||
|
if (args["--postcss"]) {
|
||
|
let postcssConfigLocation = _path.default.resolve(`./${configs.postcss}`);
|
||
|
if (_fs.default.existsSync(postcssConfigLocation)) {
|
||
|
messages.push(`${_path.default.basename(postcssConfigLocation)} already exists.`);
|
||
|
} else {
|
||
|
let stubFile = _fs.default.readFileSync(_path.default.resolve(__dirname, "../stubs/defaultPostCssConfig.stub.js"), "utf8");
|
||
|
_fs.default.writeFileSync(postcssConfigLocation, stubFile, "utf8");
|
||
|
messages.push(`Created PostCSS config file: ${_path.default.basename(postcssConfigLocation)}`);
|
||
|
}
|
||
|
}
|
||
|
if (messages.length > 0) {
|
||
|
console.log();
|
||
|
for (let message of messages){
|
||
|
console.log(message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
async function build() {
|
||
|
let input = args["--input"];
|
||
|
let output = args["--output"];
|
||
|
let shouldWatch = args["--watch"];
|
||
|
let shouldPoll = args["--poll"];
|
||
|
let shouldCoalesceWriteEvents = shouldPoll || process.platform === "win32";
|
||
|
let includePostCss = args["--postcss"];
|
||
|
// Polling interval in milliseconds
|
||
|
// Used only when polling or coalescing add/change events on Windows
|
||
|
let pollInterval = 10;
|
||
|
// TODO: Deprecate this in future versions
|
||
|
if (!input && args["_"][1]) {
|
||
|
console.error("[deprecation] Running tailwindcss without -i, please provide an input file.");
|
||
|
input = args["--input"] = args["_"][1];
|
||
|
}
|
||
|
if (input && input !== "-" && !_fs.default.existsSync(input = _path.default.resolve(input))) {
|
||
|
console.error(`Specified input file ${args["--input"]} does not exist.`);
|
||
|
process.exit(9);
|
||
|
}
|
||
|
if (args["--config"] && !_fs.default.existsSync(args["--config"] = _path.default.resolve(args["--config"]))) {
|
||
|
console.error(`Specified config file ${args["--config"]} does not exist.`);
|
||
|
process.exit(9);
|
||
|
}
|
||
|
let configPath = args["--config"] ? args["--config"] : ((defaultPath)=>_fs.default.existsSync(defaultPath) ? defaultPath : null)(_path.default.resolve(`./${configs.tailwind}`));
|
||
|
async function loadPostCssPlugins() {
|
||
|
let customPostCssPath = typeof args["--postcss"] === "string" ? args["--postcss"] : undefined;
|
||
|
let config1 = customPostCssPath ? await (async ()=>{
|
||
|
let file = _path.default.resolve(customPostCssPath);
|
||
|
// Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
|
||
|
let { config ={} } = await (0, _lilconfig).lilconfig("postcss").load(file);
|
||
|
if (typeof config === "function") {
|
||
|
config = config();
|
||
|
} else {
|
||
|
config = Object.assign({}, config);
|
||
|
}
|
||
|
if (!config.plugins) {
|
||
|
config.plugins = [];
|
||
|
}
|
||
|
return {
|
||
|
file,
|
||
|
plugins: (0, _plugins).default(config, file),
|
||
|
options: (0, _options).default(config, file)
|
||
|
};
|
||
|
})() : await (0, _postcssLoadConfig).default();
|
||
|
let configPlugins = config1.plugins;
|
||
|
let configPluginTailwindIdx = configPlugins.findIndex((plugin)=>{
|
||
|
if (typeof plugin === "function" && plugin.name === "tailwindcss") {
|
||
|
return true;
|
||
|
}
|
||
|
if (typeof plugin === "object" && plugin !== null && plugin.postcssPlugin === "tailwindcss") {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
});
|
||
|
let beforePlugins = configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx);
|
||
|
let afterPlugins = configPluginTailwindIdx === -1 ? configPlugins : configPlugins.slice(configPluginTailwindIdx + 1);
|
||
|
return [
|
||
|
beforePlugins,
|
||
|
afterPlugins,
|
||
|
config1.options
|
||
|
];
|
||
|
}
|
||
|
function loadBuiltinPostcssPlugins() {
|
||
|
let postcss = loadPostcss();
|
||
|
let IMPORT_COMMENT = "__TAILWIND_RESTORE_IMPORT__: ";
|
||
|
return [
|
||
|
[
|
||
|
(root)=>{
|
||
|
root.walkAtRules("import", (rule)=>{
|
||
|
if (rule.params.slice(1).startsWith("tailwindcss/")) {
|
||
|
rule.after(postcss.comment({
|
||
|
text: IMPORT_COMMENT + rule.params
|
||
|
}));
|
||
|
rule.remove();
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
(()=>{
|
||
|
try {
|
||
|
return require("postcss-import");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyPostcssImport();
|
||
|
})(),
|
||
|
(root)=>{
|
||
|
root.walkComments((rule)=>{
|
||
|
if (rule.text.startsWith(IMPORT_COMMENT)) {
|
||
|
rule.after(postcss.atRule({
|
||
|
name: "import",
|
||
|
params: rule.text.replace(IMPORT_COMMENT, "")
|
||
|
}));
|
||
|
rule.remove();
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
],
|
||
|
[],
|
||
|
{},
|
||
|
];
|
||
|
}
|
||
|
function resolveConfig() {
|
||
|
let config = configPath ? require(configPath) : {};
|
||
|
if (args["--purge"]) {
|
||
|
_log.default.warn("purge-flag-deprecated", [
|
||
|
"The `--purge` flag has been deprecated.",
|
||
|
"Please use `--content` instead.",
|
||
|
]);
|
||
|
if (!args["--content"]) {
|
||
|
args["--content"] = args["--purge"];
|
||
|
}
|
||
|
}
|
||
|
if (args["--content"]) {
|
||
|
let files = args["--content"].split(/(?<!{[^}]+),/);
|
||
|
let resolvedConfig = (0, _resolveConfig).default(config, {
|
||
|
content: {
|
||
|
files
|
||
|
}
|
||
|
});
|
||
|
resolvedConfig.content.files = files;
|
||
|
resolvedConfig = (0, _validateConfigJs).validateConfig(resolvedConfig);
|
||
|
return resolvedConfig;
|
||
|
}
|
||
|
let resolvedConfig = (0, _resolveConfig).default(config);
|
||
|
resolvedConfig = (0, _validateConfigJs).validateConfig(resolvedConfig);
|
||
|
return resolvedConfig;
|
||
|
}
|
||
|
function extractFileGlobs(config) {
|
||
|
return config.content.files.filter((file)=>{
|
||
|
// Strings in this case are files / globs. If it is something else,
|
||
|
// like an object it's probably a raw content object. But this object
|
||
|
// is not watchable, so let's remove it.
|
||
|
return typeof file === "string";
|
||
|
}).map((glob)=>(0, _normalizePath).default(glob));
|
||
|
}
|
||
|
function extractRawContent(config) {
|
||
|
return config.content.files.filter((file)=>{
|
||
|
return typeof file === "object" && file !== null;
|
||
|
});
|
||
|
}
|
||
|
function getChangedContent(config) {
|
||
|
let changedContent = [];
|
||
|
// Resolve globs from the content config
|
||
|
let globs = extractFileGlobs(config);
|
||
|
let files = _fastGlob.default.sync(globs);
|
||
|
for (let file of files){
|
||
|
changedContent.push({
|
||
|
content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
|
||
|
extension: _path.default.extname(file).slice(1)
|
||
|
});
|
||
|
}
|
||
|
// Resolve raw content in the tailwind config
|
||
|
for (let { raw: content , extension ="html" } of extractRawContent(config)){
|
||
|
changedContent.push({
|
||
|
content,
|
||
|
extension
|
||
|
});
|
||
|
}
|
||
|
return changedContent;
|
||
|
}
|
||
|
async function buildOnce() {
|
||
|
let config = resolveConfig();
|
||
|
let changedContent = getChangedContent(config);
|
||
|
let tailwindPlugin = ()=>{
|
||
|
return {
|
||
|
postcssPlugin: "tailwindcss",
|
||
|
Once (root, { result }) {
|
||
|
(0, _processTailwindFeatures).default(({ createContext })=>{
|
||
|
return ()=>{
|
||
|
return createContext(config, changedContent);
|
||
|
};
|
||
|
})(root, result);
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
tailwindPlugin.postcss = true;
|
||
|
let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
|
||
|
let plugins = [
|
||
|
...beforePlugins,
|
||
|
tailwindPlugin,
|
||
|
!args["--minify"] && formatNodes,
|
||
|
...afterPlugins,
|
||
|
!args["--no-autoprefixer"] && (()=>{
|
||
|
// Try to load a local `autoprefixer` version first
|
||
|
try {
|
||
|
return require("autoprefixer");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyAutoprefixer();
|
||
|
})(),
|
||
|
args["--minify"] && (()=>{
|
||
|
let options = {
|
||
|
preset: [
|
||
|
"default",
|
||
|
{
|
||
|
cssDeclarationSorter: false
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
// Try to load a local `cssnano` version first
|
||
|
try {
|
||
|
return require("cssnano");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyCssnano()(options);
|
||
|
})(),
|
||
|
].filter(Boolean);
|
||
|
let postcss = loadPostcss();
|
||
|
let processor = postcss(plugins);
|
||
|
function processCSS(css) {
|
||
|
let start = process.hrtime.bigint();
|
||
|
return Promise.resolve().then(()=>output ? _fs.default.promises.mkdir(_path.default.dirname(output), {
|
||
|
recursive: true
|
||
|
}) : null).then(()=>processor.process(css, {
|
||
|
...postcssOptions,
|
||
|
from: input,
|
||
|
to: output
|
||
|
})).then((result)=>{
|
||
|
if (!output) {
|
||
|
return process.stdout.write(result.css);
|
||
|
}
|
||
|
return Promise.all([
|
||
|
outputFile(output, result.css),
|
||
|
result.map && outputFile(output + ".map", result.map.toString()),
|
||
|
].filter(Boolean));
|
||
|
}).then(()=>{
|
||
|
let end = process.hrtime.bigint();
|
||
|
console.error();
|
||
|
console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
|
||
|
});
|
||
|
}
|
||
|
let css1 = await (()=>{
|
||
|
// Piping in data, let's drain the stdin
|
||
|
if (input === "-") {
|
||
|
return drainStdin();
|
||
|
}
|
||
|
// Input file has been provided
|
||
|
if (input) {
|
||
|
return _fs.default.readFileSync(_path.default.resolve(input), "utf8");
|
||
|
}
|
||
|
// No input file provided, fallback to default atrules
|
||
|
return "@tailwind base; @tailwind components; @tailwind utilities";
|
||
|
})();
|
||
|
return processCSS(css1);
|
||
|
}
|
||
|
let context = null;
|
||
|
async function startWatcher() {
|
||
|
let changedContent = [];
|
||
|
let configDependencies = [];
|
||
|
let contextDependencies = new Set();
|
||
|
let watcher = null;
|
||
|
function refreshConfig() {
|
||
|
env.DEBUG && console.time("Module dependencies");
|
||
|
for (let file1 of configDependencies){
|
||
|
delete require.cache[require.resolve(file1)];
|
||
|
}
|
||
|
if (configPath) {
|
||
|
configDependencies = (0, _getModuleDependencies).default(configPath).map(({ file })=>file);
|
||
|
for (let dependency of configDependencies){
|
||
|
contextDependencies.add(dependency);
|
||
|
}
|
||
|
}
|
||
|
env.DEBUG && console.timeEnd("Module dependencies");
|
||
|
return resolveConfig();
|
||
|
}
|
||
|
let [beforePlugins, afterPlugins] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
|
||
|
let plugins = [
|
||
|
...beforePlugins,
|
||
|
"__TAILWIND_PLUGIN_POSITION__",
|
||
|
!args["--minify"] && formatNodes,
|
||
|
...afterPlugins,
|
||
|
!args["--no-autoprefixer"] && (()=>{
|
||
|
// Try to load a local `autoprefixer` version first
|
||
|
try {
|
||
|
return require("autoprefixer");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyAutoprefixer();
|
||
|
})(),
|
||
|
args["--minify"] && (()=>{
|
||
|
let options = {
|
||
|
preset: [
|
||
|
"default",
|
||
|
{
|
||
|
cssDeclarationSorter: false
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
// Try to load a local `cssnano` version first
|
||
|
try {
|
||
|
return require("cssnano");
|
||
|
} catch {}
|
||
|
return (0, _indexJs).lazyCssnano()(options);
|
||
|
})(),
|
||
|
].filter(Boolean);
|
||
|
async function rebuild(config) {
|
||
|
env.DEBUG && console.time("Finished in");
|
||
|
let tailwindPlugin = ()=>{
|
||
|
return {
|
||
|
postcssPlugin: "tailwindcss",
|
||
|
Once (root, { result }) {
|
||
|
env.DEBUG && console.time("Compiling CSS");
|
||
|
(0, _processTailwindFeatures).default(({ createContext })=>{
|
||
|
console.error();
|
||
|
console.error("Rebuilding...");
|
||
|
return ()=>{
|
||
|
if (context !== null) {
|
||
|
context.changedContent = changedContent.splice(0);
|
||
|
return context;
|
||
|
}
|
||
|
env.DEBUG && console.time("Creating context");
|
||
|
context = createContext(config, changedContent.splice(0));
|
||
|
env.DEBUG && console.timeEnd("Creating context");
|
||
|
return context;
|
||
|
};
|
||
|
})(root, result);
|
||
|
env.DEBUG && console.timeEnd("Compiling CSS");
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
tailwindPlugin.postcss = true;
|
||
|
let tailwindPluginIdx = plugins.indexOf("__TAILWIND_PLUGIN_POSITION__");
|
||
|
let copy = plugins.slice();
|
||
|
copy.splice(tailwindPluginIdx, 1, tailwindPlugin);
|
||
|
let postcss = loadPostcss();
|
||
|
let processor = postcss(copy);
|
||
|
function processCSS(css) {
|
||
|
let start = process.hrtime.bigint();
|
||
|
return Promise.resolve().then(()=>output ? _fs.default.promises.mkdir(_path.default.dirname(output), {
|
||
|
recursive: true
|
||
|
}) : null).then(()=>processor.process(css, {
|
||
|
from: input,
|
||
|
to: output
|
||
|
})).then(async (result)=>{
|
||
|
for (let message of result.messages){
|
||
|
if (message.type === "dependency") {
|
||
|
contextDependencies.add(message.file);
|
||
|
}
|
||
|
}
|
||
|
watcher.add([
|
||
|
...contextDependencies
|
||
|
]);
|
||
|
if (!output) {
|
||
|
return process.stdout.write(result.css);
|
||
|
}
|
||
|
return Promise.all([
|
||
|
outputFile(output, result.css),
|
||
|
result.map && outputFile(output + ".map", result.map.toString()),
|
||
|
].filter(Boolean));
|
||
|
}).then(()=>{
|
||
|
let end = process.hrtime.bigint();
|
||
|
console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
|
||
|
}).catch((err)=>{
|
||
|
if (err.name === "CssSyntaxError") {
|
||
|
console.error(err.toString());
|
||
|
} else {
|
||
|
console.error(err);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
let css2 = await (()=>{
|
||
|
// Piping in data, let's drain the stdin
|
||
|
if (input === "-") {
|
||
|
return drainStdin();
|
||
|
}
|
||
|
// Input file has been provided
|
||
|
if (input) {
|
||
|
return _fs.default.readFileSync(_path.default.resolve(input), "utf8");
|
||
|
}
|
||
|
// No input file provided, fallback to default atrules
|
||
|
return "@tailwind base; @tailwind components; @tailwind utilities";
|
||
|
})();
|
||
|
let result1 = await processCSS(css2);
|
||
|
env.DEBUG && console.timeEnd("Finished in");
|
||
|
return result1;
|
||
|
}
|
||
|
let config2 = refreshConfig(configPath);
|
||
|
if (input) {
|
||
|
contextDependencies.add(_path.default.resolve(input));
|
||
|
}
|
||
|
watcher = _chokidar.default.watch([
|
||
|
...contextDependencies,
|
||
|
...extractFileGlobs(config2)
|
||
|
], {
|
||
|
usePolling: shouldPoll,
|
||
|
interval: shouldPoll ? pollInterval : undefined,
|
||
|
ignoreInitial: true,
|
||
|
awaitWriteFinish: shouldCoalesceWriteEvents ? {
|
||
|
stabilityThreshold: 50,
|
||
|
pollInterval: pollInterval
|
||
|
} : false
|
||
|
});
|
||
|
let chain = Promise.resolve();
|
||
|
watcher.on("change", async (file)=>{
|
||
|
if (contextDependencies.has(file)) {
|
||
|
env.DEBUG && console.time("Resolve config");
|
||
|
context = null;
|
||
|
config2 = refreshConfig(configPath);
|
||
|
env.DEBUG && console.timeEnd("Resolve config");
|
||
|
env.DEBUG && console.time("Watch new files");
|
||
|
let globs = extractFileGlobs(config2);
|
||
|
watcher.add(configDependencies);
|
||
|
watcher.add(globs);
|
||
|
env.DEBUG && console.timeEnd("Watch new files");
|
||
|
chain = chain.then(async ()=>{
|
||
|
changedContent.push(...getChangedContent(config2));
|
||
|
await rebuild(config2);
|
||
|
});
|
||
|
} else {
|
||
|
chain = chain.then(async ()=>{
|
||
|
changedContent.push({
|
||
|
content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
|
||
|
extension: _path.default.extname(file).slice(1)
|
||
|
});
|
||
|
await rebuild(config2);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
watcher.on("add", async (file)=>{
|
||
|
chain = chain.then(async ()=>{
|
||
|
changedContent.push({
|
||
|
content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
|
||
|
extension: _path.default.extname(file).slice(1)
|
||
|
});
|
||
|
await rebuild(config2);
|
||
|
});
|
||
|
});
|
||
|
chain = chain.then(()=>{
|
||
|
changedContent.push(...getChangedContent(config2));
|
||
|
return rebuild(config2);
|
||
|
});
|
||
|
}
|
||
|
if (shouldWatch) {
|
||
|
/* Abort the watcher if stdin is closed to avoid zombie processes */ if (process.stdin.isTTY) {
|
||
|
process.stdin.on("end", ()=>process.exit(0));
|
||
|
process.stdin.resume();
|
||
|
}
|
||
|
startWatcher();
|
||
|
} else {
|
||
|
buildOnce();
|
||
|
}
|
||
|
}
|