352 lines
14 KiB
JavaScript
352 lines
14 KiB
JavaScript
// @ts-check
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "createProcessor", {
|
|
enumerable: true,
|
|
get: ()=>createProcessor
|
|
});
|
|
const _path = /*#__PURE__*/ _interopRequireDefault(require("path"));
|
|
const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
|
|
const _postcssLoadConfig = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config"));
|
|
const _lilconfig = require("lilconfig");
|
|
const _plugins = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config/src/plugins" // Little bit scary, looking at private/internal API
|
|
));
|
|
const _options = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config/src/options" // Little bit scary, looking at private/internal API
|
|
));
|
|
const _processTailwindFeatures = /*#__PURE__*/ _interopRequireDefault(require("../../processTailwindFeatures"));
|
|
const _deps = require("./deps");
|
|
const _utils = require("./utils");
|
|
const _shared = require("../shared");
|
|
const _resolveConfigJs = /*#__PURE__*/ _interopRequireDefault(require("../../../resolveConfig.js"));
|
|
const _getModuleDependenciesJs = /*#__PURE__*/ _interopRequireDefault(require("../../lib/getModuleDependencies.js"));
|
|
const _contentJs = require("../../lib/content.js");
|
|
const _watchingJs = require("./watching.js");
|
|
const _fastGlob = /*#__PURE__*/ _interopRequireDefault(require("fast-glob"));
|
|
const _findAtConfigPathJs = require("../../lib/findAtConfigPath.js");
|
|
const _log = /*#__PURE__*/ _interopRequireDefault(require("../../util/log"));
|
|
function _interopRequireDefault(obj) {
|
|
return obj && obj.__esModule ? obj : {
|
|
default: obj
|
|
};
|
|
}
|
|
/**
|
|
*
|
|
* @param {string} [customPostCssPath ]
|
|
* @returns
|
|
*/ async function loadPostCssPlugins(customPostCssPath) {
|
|
let config = customPostCssPath ? await (async ()=>{
|
|
let file = _path.default.resolve(customPostCssPath);
|
|
// Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
|
|
// @ts-ignore
|
|
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 = config.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,
|
|
config.options
|
|
];
|
|
}
|
|
function loadBuiltinPostcssPlugins() {
|
|
let postcss = (0, _deps.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();
|
|
}
|
|
});
|
|
},
|
|
(0, _deps.loadPostcssImport)(),
|
|
(root)=>{
|
|
root.walkComments((rule)=>{
|
|
if (rule.text.startsWith(IMPORT_COMMENT)) {
|
|
rule.after(postcss.atRule({
|
|
name: "import",
|
|
params: rule.text.replace(IMPORT_COMMENT, "")
|
|
}));
|
|
rule.remove();
|
|
}
|
|
});
|
|
}
|
|
],
|
|
[],
|
|
{}
|
|
];
|
|
}
|
|
let state = {
|
|
/** @type {any} */ context: null,
|
|
/** @type {ReturnType<typeof createWatcher> | null} */ watcher: null,
|
|
/** @type {{content: string, extension: string}[]} */ changedContent: [],
|
|
configDependencies: new Set(),
|
|
contextDependencies: new Set(),
|
|
/** @type {import('../../lib/content.js').ContentPath[]} */ contentPaths: [],
|
|
refreshContentPaths () {
|
|
var ref;
|
|
this.contentPaths = (0, _contentJs.parseCandidateFiles)(this.context, (ref = this.context) === null || ref === void 0 ? void 0 : ref.tailwindConfig);
|
|
},
|
|
get config () {
|
|
return this.context.tailwindConfig;
|
|
},
|
|
get contentPatterns () {
|
|
return {
|
|
all: this.contentPaths.map((contentPath)=>contentPath.pattern),
|
|
dynamic: this.contentPaths.filter((contentPath)=>contentPath.glob !== undefined).map((contentPath)=>contentPath.pattern)
|
|
};
|
|
},
|
|
loadConfig (configPath, content) {
|
|
if (this.watcher && configPath) {
|
|
this.refreshConfigDependencies(configPath);
|
|
}
|
|
let config = configPath ? require(configPath) : {};
|
|
// @ts-ignore
|
|
config = (0, _resolveConfigJs.default)(config, {
|
|
content: {
|
|
files: []
|
|
}
|
|
});
|
|
// Override content files if `--content` has been passed explicitly
|
|
if ((content === null || content === void 0 ? void 0 : content.length) > 0) {
|
|
config.content.files = content;
|
|
}
|
|
return config;
|
|
},
|
|
refreshConfigDependencies (configPath) {
|
|
_shared.env.DEBUG && console.time("Module dependencies");
|
|
for (let file of this.configDependencies){
|
|
delete require.cache[require.resolve(file)];
|
|
}
|
|
if (configPath) {
|
|
let deps = (0, _getModuleDependenciesJs.default)(configPath).map(({ file })=>file);
|
|
for (let dependency of deps){
|
|
this.configDependencies.add(dependency);
|
|
}
|
|
}
|
|
_shared.env.DEBUG && console.timeEnd("Module dependencies");
|
|
},
|
|
readContentPaths () {
|
|
let content = [];
|
|
// Resolve globs from the content config
|
|
// TODO: When we make the postcss plugin async-capable this can become async
|
|
let files = _fastGlob.default.sync(this.contentPatterns.all);
|
|
for (let file of files){
|
|
content.push({
|
|
content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
|
|
extension: _path.default.extname(file).slice(1)
|
|
});
|
|
}
|
|
// Resolve raw content in the tailwind config
|
|
let rawContent = this.config.content.files.filter((file)=>{
|
|
return file !== null && typeof file === "object";
|
|
});
|
|
for (let { raw: content1 , extension ="html" } of rawContent){
|
|
content1.push({
|
|
content: content1,
|
|
extension
|
|
});
|
|
}
|
|
return content;
|
|
},
|
|
getContext ({ createContext , cliConfigPath , root , result , content }) {
|
|
if (this.context) {
|
|
this.context.changedContent = this.changedContent.splice(0);
|
|
return this.context;
|
|
}
|
|
_shared.env.DEBUG && console.time("Searching for config");
|
|
var ref;
|
|
let configPath = (ref = (0, _findAtConfigPathJs.findAtConfigPath)(root, result)) !== null && ref !== void 0 ? ref : cliConfigPath;
|
|
_shared.env.DEBUG && console.timeEnd("Searching for config");
|
|
_shared.env.DEBUG && console.time("Loading config");
|
|
let config = this.loadConfig(configPath, content);
|
|
_shared.env.DEBUG && console.timeEnd("Loading config");
|
|
_shared.env.DEBUG && console.time("Creating context");
|
|
this.context = createContext(config, []);
|
|
Object.assign(this.context, {
|
|
userConfigPath: configPath
|
|
});
|
|
_shared.env.DEBUG && console.timeEnd("Creating context");
|
|
_shared.env.DEBUG && console.time("Resolving content paths");
|
|
this.refreshContentPaths();
|
|
_shared.env.DEBUG && console.timeEnd("Resolving content paths");
|
|
if (this.watcher) {
|
|
_shared.env.DEBUG && console.time("Watch new files");
|
|
this.watcher.refreshWatchedFiles();
|
|
_shared.env.DEBUG && console.timeEnd("Watch new files");
|
|
}
|
|
_shared.env.DEBUG && console.time("Reading content files");
|
|
for (let file of this.readContentPaths()){
|
|
this.context.changedContent.push(file);
|
|
}
|
|
_shared.env.DEBUG && console.timeEnd("Reading content files");
|
|
return this.context;
|
|
}
|
|
};
|
|
async function createProcessor(args, cliConfigPath) {
|
|
var ref;
|
|
let postcss = (0, _deps.loadPostcss)();
|
|
let input = args["--input"];
|
|
let output = args["--output"];
|
|
let includePostCss = args["--postcss"];
|
|
let customPostCssPath = typeof args["--postcss"] === "string" ? args["--postcss"] : undefined;
|
|
let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins(customPostCssPath) : loadBuiltinPostcssPlugins();
|
|
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"];
|
|
}
|
|
}
|
|
var ref1;
|
|
let content = (ref1 = (ref = args["--content"]) === null || ref === void 0 ? void 0 : ref.split(/(?<!{[^}]+),/)) !== null && ref1 !== void 0 ? ref1 : [];
|
|
let tailwindPlugin = ()=>{
|
|
return {
|
|
postcssPlugin: "tailwindcss",
|
|
Once (root, { result }) {
|
|
_shared.env.DEBUG && console.time("Compiling CSS");
|
|
(0, _processTailwindFeatures.default)(({ createContext })=>{
|
|
console.error();
|
|
console.error("Rebuilding...");
|
|
return ()=>{
|
|
return state.getContext({
|
|
createContext,
|
|
cliConfigPath,
|
|
root,
|
|
result,
|
|
content
|
|
});
|
|
};
|
|
})(root, result);
|
|
_shared.env.DEBUG && console.timeEnd("Compiling CSS");
|
|
}
|
|
};
|
|
};
|
|
tailwindPlugin.postcss = true;
|
|
let plugins = [
|
|
...beforePlugins,
|
|
tailwindPlugin,
|
|
!args["--minify"] && _utils.formatNodes,
|
|
...afterPlugins,
|
|
!args["--no-autoprefixer"] && (0, _deps.loadAutoprefixer)(),
|
|
args["--minify"] && (0, _deps.loadCssNano)()
|
|
].filter(Boolean);
|
|
/** @type {import('postcss').Processor} */ // @ts-ignore
|
|
let processor = postcss(plugins);
|
|
async function readInput() {
|
|
// Piping in data, let's drain the stdin
|
|
if (input === "-") {
|
|
return (0, _utils.drainStdin)();
|
|
}
|
|
// Input file has been provided
|
|
if (input) {
|
|
return _fs.default.promises.readFile(_path.default.resolve(input), "utf8");
|
|
}
|
|
// No input file provided, fallback to default atrules
|
|
return "@tailwind base; @tailwind components; @tailwind utilities";
|
|
}
|
|
async function build() {
|
|
let start = process.hrtime.bigint();
|
|
return readInput().then((css)=>processor.process(css, {
|
|
...postcssOptions,
|
|
from: input,
|
|
to: output
|
|
})).then((result)=>{
|
|
if (!state.watcher) {
|
|
return result;
|
|
}
|
|
_shared.env.DEBUG && console.time("Recording PostCSS dependencies");
|
|
for (let message of result.messages){
|
|
if (message.type === "dependency") {
|
|
state.contextDependencies.add(message.file);
|
|
}
|
|
}
|
|
_shared.env.DEBUG && console.timeEnd("Recording PostCSS dependencies");
|
|
// TODO: This needs to be in a different spot
|
|
_shared.env.DEBUG && console.time("Watch new files");
|
|
state.watcher.refreshWatchedFiles();
|
|
_shared.env.DEBUG && console.timeEnd("Watch new files");
|
|
return result;
|
|
}).then((result)=>{
|
|
if (!output) {
|
|
process.stdout.write(result.css);
|
|
return;
|
|
}
|
|
return Promise.all([
|
|
(0, _utils.outputFile)(output, result.css),
|
|
result.map && (0, _utils.outputFile)(output + ".map", result.map.toString())
|
|
]);
|
|
}).then(()=>{
|
|
let end = process.hrtime.bigint();
|
|
console.error();
|
|
console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
|
|
});
|
|
}
|
|
/**
|
|
* @param {{file: string, content(): Promise<string>, extension: string}[]} changes
|
|
*/ async function parseChanges(changes) {
|
|
return Promise.all(changes.map(async (change)=>({
|
|
content: await change.content(),
|
|
extension: change.extension
|
|
})));
|
|
}
|
|
if (input !== undefined && input !== "-") {
|
|
state.contextDependencies.add(_path.default.resolve(input));
|
|
}
|
|
return {
|
|
build,
|
|
watch: async ()=>{
|
|
state.watcher = (0, _watchingJs.createWatcher)(args, {
|
|
state,
|
|
/**
|
|
* @param {{file: string, content(): Promise<string>, extension: string}[]} changes
|
|
*/ async rebuild (changes) {
|
|
let needsNewContext = changes.some((change)=>{
|
|
return state.configDependencies.has(change.file) || state.contextDependencies.has(change.file);
|
|
});
|
|
if (needsNewContext) {
|
|
state.context = null;
|
|
} else {
|
|
for (let change of (await parseChanges(changes))){
|
|
state.changedContent.push(change);
|
|
}
|
|
}
|
|
return build();
|
|
}
|
|
});
|
|
await build();
|
|
}
|
|
};
|
|
}
|