feat: docker compose maybe

This commit is contained in:
2023-11-13 16:10:04 -05:00
parent 180b261e40
commit b625ccd8d6
8031 changed files with 2182966 additions and 0 deletions

21
node_modules/vite-plugin-tailwind-purgecss/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 AdrianGonz97
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

100
node_modules/vite-plugin-tailwind-purgecss/README.md generated vendored Normal file
View File

@ -0,0 +1,100 @@
# vite-plugin-tailwind-purgecss
[![npm version](https://img.shields.io/npm/v/vite-plugin-tailwind-purgecss?logo=npm&color=cb3837)](https://www.npmjs.com/package/vite-plugin-tailwind-purgecss)
[![license](https://img.shields.io/badge/license-MIT-%23bada55)](https://github.com/AdrianGonz97/vite-plugin-tailwind-purgecss/blob/main/LICENSE)
A simple vite plugin that **thoroughly** purges excess CSS using [PurgeCSS](https://purgecss.com/). This package should be used in combination with [Tailwind](https://tailwindcss.com/) and a Tailwind UI component library such as [Skeleton](https://skeleton.dev) or [Flowbite](https://flowbite.com/).
## ⚠ Notice ⚠
This package is still very **experimental**. Breaking changes can occur at any time. We'll stabilize once we hit a `1.0.0` release.
## Motivation
Tailwind UI component libraries are fantastic and are a joy to work with, but they come with an important caveat. The downside to them is that all of the Tailwind classes that are used in their provided components are _always_ generated, even if you don't import and use any of their components. This leads to a larger than necessary CSS bundle.
This is a limitation of how Tailwind works with it's `content` field in its config. Tailwind searches through all of the files that are specified in `content`, uses a regex to find any possible selectors, and generates their respective classes. There's no treeshaking and no purging involved.
## Why not use `postcss-purgecss` or `rollup-plugin-purgecss`?
PurgeCSS provides a suite of plugins that do a well enough job for _most_ projects. However, plugins such as [postcss-purgecss](https://github.com/FullHuman/purgecss/tree/main/packages/postcss-purgecss) and [rollup-plugin-purgecss](https://github.com/FullHuman/purgecss/tree/main/packages/rollup-plugin-purgecss) take a rather naive approach to selector extraction. Just like how Tailwind generates its classes, they _only_ analyze the content and files that are passed to them through their `content` fields, which means that if you pass a UI library to it, none of the selectors that are **unused** in your project will be properly purged.
## How it works
Ideally, we'd like to keep the selectors that are only being used in your project. We accomplish by analyzing the emitted JS chunks that are generated by Rollup, which only include the modules that are actually used in your project, and extracting out any of their possible selectors. From there, we can pass along the selectors to PurgeCSS for final processing.
Using `vite-plugin-tailwind-purgecss` with [Skeleton](https://skeleton.dev), we're able to reduce the CSS bundle size of Skeleton's [Barebones Template](https://github.com/skeletonlabs/skeleton-template-bare) from:
```
92.50 kB │ gzip: 12.92 kB
```
down to:
```
27.29 kB │ gzip: 5.40 kB
```
## Usage
### Installation
```bash
npm i -D vite-plugin-tailwind-purgecss
```
### Add to Vite
```ts
// vite.config.ts
import { purgeCss } from 'vite-plugin-tailwind-purgecss';
const config: UserConfig = {
plugins: [sveltekit(), purgeCss()],
};
```
### Safelisting
If selectors that shouldn't be purged are being removed, simply add them to the `safelist`.
```ts
// vite.config.ts
import { purgeCss } from 'vite-plugin-tailwind-purgecss';
const config: UserConfig = {
plugins: [
sveltekit(),
purgeCss({
safelist: {
// any selectors that begin with "hljs-" will not be purged
greedy: [/^hljs-/],
},
}),
],
};
```
Alternatively, if you'd like to safelist selectors directly in your stylesheets, you can do so by adding special comments:
```css
/*! purgecss start ignore */
h1 {
color: red;
}
h2 {
color: blue;
}
/*! purgecss end ignore */
```
You can also safelist selectors directly in the declaration block as well:
```css
h3 {
/*! purgecss ignore current */
color: pink;
}
```
For further configuration, you can learn more [here](https://purgecss.com/configuration.html).

View File

@ -0,0 +1,10 @@
import { Plugin } from 'vite';
import { UserDefinedOptions, ComplexSafelist } from 'purgecss';
type Options = Partial<UserDefinedOptions> & {
safelist?: ComplexSafelist;
};
type PurgeOptions = Omit<Options, 'css'>;
declare function purgeCss(purgeOptions?: PurgeOptions): Plugin;
export { purgeCss as default, purgeCss };

View File

@ -0,0 +1,228 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/index.ts
var _purgecss = require('purgecss');
// src/extractors/regex.ts
var REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g;
var REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source);
function toSource(source) {
source = Array.isArray(source) ? source : [source];
source = source.map((item) => item instanceof RegExp ? item.source : item);
return source.join("");
}
function pattern(source) {
return new RegExp(toSource(source), "g");
}
function any(sources) {
return `(?:${sources.map(toSource).join("|")})`;
}
function optional(source) {
return `(?:${toSource(source)})?`;
}
// src/extractors/default-extractor.ts
function defaultExtractor() {
let patterns = Array.from(buildRegExps());
return (content) => {
let results = [];
for (let pattern2 of patterns) {
results = [...results, ..._nullishCoalesce(content.match(pattern2), () => ( []))];
}
return results.filter((v) => v !== void 0).map(clipAtBalancedParens);
};
}
function* buildRegExps() {
let separator = ":";
let prefix = "";
let utility = any([
/\[[^\s:'"`]+:[^\s\[\]]+\]/,
/\[[^\s:'"`]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
pattern([
/-?(?:\w+)/,
optional(
any([
pattern([
/-(?:\w+-)*\[[^\s:]+\]/,
/(?![{([]])/,
/(?:\/[^\s'"`\\><$]*)?/
]),
pattern([
/-(?:\w+-)*\[[^\s]+\]/,
/(?![{([]])/,
/(?:\/[^\s'"`\\$]*)?/
]),
/[-\/][^\s'"`\\$={><]*/
])
)
])
]);
let variantPatterns = [
any([
pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
pattern([/[^\s"'`\[\\]+/, separator])
]),
any([
pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
pattern([/[^\s`\[\\]+/, separator])
])
];
for (const variantPattern of variantPatterns) {
yield pattern([
"((?=((",
variantPattern,
")+))\\2)?",
/!?/,
prefix,
utility
]);
}
yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g;
}
var SPECIALS = /([\[\]'"`])([^\[\]'"`])?/g;
var ALLOWED_CLASS_CHARACTERS = /[^"'`\s<>\]]+/;
function clipAtBalancedParens(input) {
if (!input.includes("-[")) {
return input;
}
let depth = 0;
let openStringTypes = [];
let matches = input.matchAll(SPECIALS);
const matched = Array.from(matches).flatMap((match) => {
const [, ...groups] = match;
return groups.map(
(group, idx) => Object.assign([], match, {
index: match.index + idx,
0: group
})
);
});
for (let match of matched) {
let char = match[0];
let inStringType = openStringTypes.at(-1);
if (char === inStringType) {
openStringTypes.pop();
} else if (char === "'" || char === '"' || char === "`") {
openStringTypes.push(char);
}
if (inStringType) {
continue;
} else if (char === "[") {
depth++;
continue;
} else if (char === "]") {
depth--;
continue;
}
if (depth < 0) {
return input.substring(0, match.index - 1);
}
if (depth === 0 && !ALLOWED_CLASS_CHARACTERS.test(char)) {
return input.substring(0, match.index);
}
}
return input;
}
// src/index.ts
var _estreewalker = require('estree-walker');
var _path = require('path');
var EXT_CSS = /\.(css)$/;
var MAX_STRING_LITERAL_LENGTH = 5e4;
function purgeCss(purgeOptions) {
var _a;
let viteConfig;
const selectors = /* @__PURE__ */ new Set();
const standard = [
"*",
"html",
"body",
/aria-current/,
/^\:[-a-z]+$/,
..._nullishCoalesce(((_a = purgeOptions == null ? void 0 : purgeOptions.safelist) == null ? void 0 : _a.standard), () => ( []))
];
const extractor = _nullishCoalesce((purgeOptions == null ? void 0 : purgeOptions.defaultExtractor), () => ( defaultExtractor()));
const moduleIds = /* @__PURE__ */ new Set();
return {
name: "vite-plugin-tailwind-purgecss",
apply: "build",
enforce: "post",
load(id) {
if (EXT_CSS.test(id))
return;
moduleIds.add(id);
},
configResolved(config) {
viteConfig = config;
},
async generateBundle(options, bundle) {
var _a2;
const assets = {};
for (const id of moduleIds) {
const info = this.getModuleInfo(id);
if ((info == null ? void 0 : info.isIncluded) !== true || info.code === null)
continue;
const ast = this.parse(info.code);
_estreewalker.walk.call(void 0, ast, {
enter(node, parent, key, index) {
if (node.type === "Literal" && typeof node.value === "string") {
node.value.split(/\s+/).forEach((word) => {
if (word.length < MAX_STRING_LITERAL_LENGTH) {
extractor(word).forEach((selector) => selectors.add(selector));
} else
selectors.add(word);
});
}
if (node.type === "Identifier") {
selectors.add(node.name);
}
if (node.type === "TemplateElement") {
const value = _nullishCoalesce(node.value.cooked, () => ( node.value.raw));
value.split(/\s+/).forEach((word) => {
if (word.length < MAX_STRING_LITERAL_LENGTH) {
extractor(word).forEach((selector) => selectors.add(selector));
} else
selectors.add(word);
});
}
}
});
}
for (const [fileName, chunkOrAsset] of Object.entries(bundle)) {
if (chunkOrAsset.type === "asset" && EXT_CSS.test(fileName)) {
assets[fileName] = chunkOrAsset;
}
}
for (const selector of selectors) {
standard.push(selector);
}
for (const [fileName, asset] of Object.entries(assets)) {
const purgeCSSResult = await new (0, _purgecss.PurgeCSS)().purge({
...purgeOptions,
content: [_path.join.call(void 0, viteConfig.root, "**/*.html"), ..._nullishCoalesce((purgeOptions == null ? void 0 : purgeOptions.content), () => ( []))],
css: [{ raw: asset.source.trim(), name: fileName }],
rejected: true,
rejectedCss: true,
safelist: {
...purgeOptions == null ? void 0 : purgeOptions.safelist,
standard,
greedy: [/svelte-/, /data-theme/, ..._nullishCoalesce(((_a2 = purgeOptions == null ? void 0 : purgeOptions.safelist) == null ? void 0 : _a2.greedy), () => ( []))]
}
});
if (purgeCSSResult[0]) {
delete bundle[asset.fileName];
this.emitFile({
...asset,
type: "asset",
source: purgeCSSResult[0].css
});
}
}
}
};
}
var src_default = purgeCss;
exports.default = src_default; exports.purgeCss = purgeCss;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,228 @@
// src/index.ts
import { PurgeCSS } from "purgecss";
// src/extractors/regex.ts
var REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g;
var REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source);
function toSource(source) {
source = Array.isArray(source) ? source : [source];
source = source.map((item) => item instanceof RegExp ? item.source : item);
return source.join("");
}
function pattern(source) {
return new RegExp(toSource(source), "g");
}
function any(sources) {
return `(?:${sources.map(toSource).join("|")})`;
}
function optional(source) {
return `(?:${toSource(source)})?`;
}
// src/extractors/default-extractor.ts
function defaultExtractor() {
let patterns = Array.from(buildRegExps());
return (content) => {
let results = [];
for (let pattern2 of patterns) {
results = [...results, ...content.match(pattern2) ?? []];
}
return results.filter((v) => v !== void 0).map(clipAtBalancedParens);
};
}
function* buildRegExps() {
let separator = ":";
let prefix = "";
let utility = any([
/\[[^\s:'"`]+:[^\s\[\]]+\]/,
/\[[^\s:'"`]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
pattern([
/-?(?:\w+)/,
optional(
any([
pattern([
/-(?:\w+-)*\[[^\s:]+\]/,
/(?![{([]])/,
/(?:\/[^\s'"`\\><$]*)?/
]),
pattern([
/-(?:\w+-)*\[[^\s]+\]/,
/(?![{([]])/,
/(?:\/[^\s'"`\\$]*)?/
]),
/[-\/][^\s'"`\\$={><]*/
])
)
])
]);
let variantPatterns = [
any([
pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
pattern([/[^\s"'`\[\\]+/, separator])
]),
any([
pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
pattern([/[^\s`\[\\]+/, separator])
])
];
for (const variantPattern of variantPatterns) {
yield pattern([
"((?=((",
variantPattern,
")+))\\2)?",
/!?/,
prefix,
utility
]);
}
yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g;
}
var SPECIALS = /([\[\]'"`])([^\[\]'"`])?/g;
var ALLOWED_CLASS_CHARACTERS = /[^"'`\s<>\]]+/;
function clipAtBalancedParens(input) {
if (!input.includes("-[")) {
return input;
}
let depth = 0;
let openStringTypes = [];
let matches = input.matchAll(SPECIALS);
const matched = Array.from(matches).flatMap((match) => {
const [, ...groups] = match;
return groups.map(
(group, idx) => Object.assign([], match, {
index: match.index + idx,
0: group
})
);
});
for (let match of matched) {
let char = match[0];
let inStringType = openStringTypes.at(-1);
if (char === inStringType) {
openStringTypes.pop();
} else if (char === "'" || char === '"' || char === "`") {
openStringTypes.push(char);
}
if (inStringType) {
continue;
} else if (char === "[") {
depth++;
continue;
} else if (char === "]") {
depth--;
continue;
}
if (depth < 0) {
return input.substring(0, match.index - 1);
}
if (depth === 0 && !ALLOWED_CLASS_CHARACTERS.test(char)) {
return input.substring(0, match.index);
}
}
return input;
}
// src/index.ts
import { walk } from "estree-walker";
import { join } from "path";
var EXT_CSS = /\.(css)$/;
var MAX_STRING_LITERAL_LENGTH = 5e4;
function purgeCss(purgeOptions) {
var _a;
let viteConfig;
const selectors = /* @__PURE__ */ new Set();
const standard = [
"*",
"html",
"body",
/aria-current/,
/^\:[-a-z]+$/,
...((_a = purgeOptions == null ? void 0 : purgeOptions.safelist) == null ? void 0 : _a.standard) ?? []
];
const extractor = (purgeOptions == null ? void 0 : purgeOptions.defaultExtractor) ?? defaultExtractor();
const moduleIds = /* @__PURE__ */ new Set();
return {
name: "vite-plugin-tailwind-purgecss",
apply: "build",
enforce: "post",
load(id) {
if (EXT_CSS.test(id))
return;
moduleIds.add(id);
},
configResolved(config) {
viteConfig = config;
},
async generateBundle(options, bundle) {
var _a2;
const assets = {};
for (const id of moduleIds) {
const info = this.getModuleInfo(id);
if ((info == null ? void 0 : info.isIncluded) !== true || info.code === null)
continue;
const ast = this.parse(info.code);
walk(ast, {
enter(node, parent, key, index) {
if (node.type === "Literal" && typeof node.value === "string") {
node.value.split(/\s+/).forEach((word) => {
if (word.length < MAX_STRING_LITERAL_LENGTH) {
extractor(word).forEach((selector) => selectors.add(selector));
} else
selectors.add(word);
});
}
if (node.type === "Identifier") {
selectors.add(node.name);
}
if (node.type === "TemplateElement") {
const value = node.value.cooked ?? node.value.raw;
value.split(/\s+/).forEach((word) => {
if (word.length < MAX_STRING_LITERAL_LENGTH) {
extractor(word).forEach((selector) => selectors.add(selector));
} else
selectors.add(word);
});
}
}
});
}
for (const [fileName, chunkOrAsset] of Object.entries(bundle)) {
if (chunkOrAsset.type === "asset" && EXT_CSS.test(fileName)) {
assets[fileName] = chunkOrAsset;
}
}
for (const selector of selectors) {
standard.push(selector);
}
for (const [fileName, asset] of Object.entries(assets)) {
const purgeCSSResult = await new PurgeCSS().purge({
...purgeOptions,
content: [join(viteConfig.root, "**/*.html"), ...(purgeOptions == null ? void 0 : purgeOptions.content) ?? []],
css: [{ raw: asset.source.trim(), name: fileName }],
rejected: true,
rejectedCss: true,
safelist: {
...purgeOptions == null ? void 0 : purgeOptions.safelist,
standard,
greedy: [/svelte-/, /data-theme/, ...((_a2 = purgeOptions == null ? void 0 : purgeOptions.safelist) == null ? void 0 : _a2.greedy) ?? []]
}
});
if (purgeCSSResult[0]) {
delete bundle[asset.fileName];
this.emitFile({
...asset,
type: "asset",
source: purgeCSSResult[0].css
});
}
}
}
};
}
var src_default = purgeCss;
export {
src_default as default,
purgeCss
};
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,57 @@
{
"name": "vite-plugin-tailwind-purgecss",
"version": "0.1.3",
"description": "Vite plugin for PurgeCSS",
"module": "dist/index.mjs",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"url": "https://github.com/AdrianGonz97/vite-plugin-tailwind-purgecss",
"type": "git"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"keywords": [
"purgeCSS",
"sveltekit",
"svelte",
"rollup",
"tailwind",
"tailwindcss",
"vite",
"vite-plugin",
"skeleton"
],
"author": "AdrianGonz97",
"license": "MIT",
"dependencies": {
"estree-walker": "^3.0.3",
"purgecss": "6.0.0-alpha.0"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@types/node": "^18.11.18",
"prettier": "^2.8.1",
"tsup": "^6.5.0",
"typescript": "^4.9.4",
"vite": "^4.1.1"
},
"files": [
"dist"
],
"peerDependencies": {
"vite": "^4.1.1"
},
"scripts": {
"dev": "tsup --watch",
"build": "tsup",
"lint": "tsc",
"release": "pnpm run build && changeset publish",
"format": "prettier --write \"./src/**/*.{ts,svelte}\""
}
}