File size: 16,185 Bytes
bc20498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
// @ts-check
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "createProcessor", {
    enumerable: true,
    get: function() {
        return createProcessor;
    }
});
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
const _postcssloadconfig = /*#__PURE__*/ _interop_require_default(require("postcss-load-config"));
const _lilconfig = require("lilconfig");
const _plugins = /*#__PURE__*/ _interop_require_default(require("postcss-load-config/src/plugins" // Little bit scary, looking at private/internal API
));
const _options = /*#__PURE__*/ _interop_require_default(require("postcss-load-config/src/options" // Little bit scary, looking at private/internal API
));
const _processTailwindFeatures = /*#__PURE__*/ _interop_require_default(require("../../processTailwindFeatures"));
const _deps = require("./deps");
const _utils = require("./utils");
const _sharedState = require("../../lib/sharedState");
const _resolveConfig = /*#__PURE__*/ _interop_require_default(require("../../../resolveConfig.js"));
const _content = require("../../lib/content.js");
const _watching = require("./watching.js");
const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
const _findAtConfigPath = require("../../lib/findAtConfigPath.js");
const _log = /*#__PURE__*/ _interop_require_default(require("../../util/log"));
const _loadconfig = require("../../lib/load-config");
const _getModuleDependencies = /*#__PURE__*/ _interop_require_default(require("../../lib/getModuleDependencies"));
function _interop_require_default(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/[email protected]/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: [],
    /** @type {ReturnType<typeof load> | null} */ configBag: null,
    contextDependencies: new Set(),
    /** @type {import('../../lib/content.js').ContentPath[]} */ contentPaths: [],
    refreshContentPaths () {
        var _this_context;
        this.contentPaths = (0, _content.parseCandidateFiles)(this.context, (_this_context = this.context) === null || _this_context === void 0 ? void 0 : _this_context.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();
        }
        let config = (0, _loadconfig.loadConfig)(configPath);
        let dependencies = (0, _getModuleDependencies.default)(configPath);
        this.configBag = {
            config,
            dependencies,
            dispose () {
                for (let file of dependencies){
                    delete require.cache[require.resolve(file)];
                }
            }
        };
        // @ts-ignore
        this.configBag.config = (0, _resolveConfig.default)(this.configBag.config, {
            content: {
                files: []
            }
        });
        // Override content files if `--content` has been passed explicitly
        if ((content === null || content === void 0 ? void 0 : content.length) > 0) {
            this.configBag.config.content.files = content;
        }
        return this.configBag.config;
    },
    refreshConfigDependencies () {
        var _this_configBag;
        _sharedState.env.DEBUG && console.time("Module dependencies");
        (_this_configBag = this.configBag) === null || _this_configBag === void 0 ? void 0 : _this_configBag.dispose();
        _sharedState.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){
            if (false) {
                content.push({
                    file,
                    extension: _path.default.extname(file).slice(1)
                });
            } else {
                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: htmlContent , extension ="html"  } of rawContent){
            content.push({
                content: htmlContent,
                extension
            });
        }
        return content;
    },
    getContext ({ createContext , cliConfigPath , root , result , content  }) {
        _sharedState.env.DEBUG && console.time("Searching for config");
        var _findAtConfigPath1;
        let configPath = (_findAtConfigPath1 = (0, _findAtConfigPath.findAtConfigPath)(root, result)) !== null && _findAtConfigPath1 !== void 0 ? _findAtConfigPath1 : cliConfigPath;
        _sharedState.env.DEBUG && console.timeEnd("Searching for config");
        if (this.context) {
            this.context.changedContent = this.changedContent.splice(0);
            return this.context;
        }
        _sharedState.env.DEBUG && console.time("Loading config");
        let config = this.loadConfig(configPath, content);
        _sharedState.env.DEBUG && console.timeEnd("Loading config");
        _sharedState.env.DEBUG && console.time("Creating context");
        this.context = createContext(config, []);
        Object.assign(this.context, {
            userConfigPath: configPath
        });
        _sharedState.env.DEBUG && console.timeEnd("Creating context");
        _sharedState.env.DEBUG && console.time("Resolving content paths");
        this.refreshContentPaths();
        _sharedState.env.DEBUG && console.timeEnd("Resolving content paths");
        if (this.watcher) {
            _sharedState.env.DEBUG && console.time("Watch new files");
            this.watcher.refreshWatchedFiles();
            _sharedState.env.DEBUG && console.timeEnd("Watch new files");
        }
        for (let file of this.readContentPaths()){
            this.context.changedContent.push(file);
        }
        return this.context;
    }
};
async function createProcessor(args, cliConfigPath) {
    var _args_content;
    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 _args_content_split;
    let content = (_args_content_split = (_args_content = args["--content"]) === null || _args_content === void 0 ? void 0 : _args_content.split(/(?<!{[^}]+),/)) !== null && _args_content_split !== void 0 ? _args_content_split : [];
    let tailwindPlugin = ()=>{
        return {
            postcssPlugin: "tailwindcss",
            async Once (root, { result  }) {
                _sharedState.env.DEBUG && console.time("Compiling CSS");
                await (0, _processTailwindFeatures.default)(({ createContext  })=>{
                    console.error();
                    console.error("Rebuilding...");
                    return ()=>{
                        return state.getContext({
                            createContext,
                            cliConfigPath,
                            root,
                            result,
                            content
                        });
                    };
                })(root, result);
                _sharedState.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;
            }
            _sharedState.env.DEBUG && console.time("Recording PostCSS dependencies");
            for (let message of result.messages){
                if (message.type === "dependency") {
                    state.contextDependencies.add(message.file);
                }
            }
            _sharedState.env.DEBUG && console.timeEnd("Recording PostCSS dependencies");
            // TODO: This needs to be in a different spot
            _sharedState.env.DEBUG && console.time("Watch new files");
            state.watcher.refreshWatchedFiles();
            _sharedState.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)(result.opts.to, result.css),
                result.map && (0, _utils.outputFile)(result.opts.to + ".map", result.map.toString())
            ]);
        }).then(()=>{
            let end = process.hrtime.bigint();
            console.error();
            console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
        }).then(()=>{}, (err)=>{
            // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
            // that were collected before the error occurred
            // The result is not stored on the error so we have to store it externally
            // and pull the messages off of it here somehow
            // This results in a less than ideal DX because the watcher will not pick up
            // changes to imported CSS if one of them caused an error during the initial build
            // If you fix it and then save the main CSS file so there's no error
            // The watcher will start watching the imported CSS files and will be
            // resilient to future errors.
            if (state.watcher) {
                console.error(err);
            } else {
                return Promise.reject(err);
            }
        });
    }
    /**
   * @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, _watching.createWatcher)(args, {
                state,
                /**
         * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
         */ async rebuild (changes) {
                    let needsNewContext = changes.some((change)=>{
                        var _state_configBag;
                        return ((_state_configBag = state.configBag) === null || _state_configBag === void 0 ? void 0 : _state_configBag.dependencies.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();
        }
    };
}