mnfst 0.5.203 → 0.5.205
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/manifest.appwrite.data.js +90 -40
- package/lib/manifest.data.js +95 -23
- package/lib/manifest.integrity.json +5 -5
- package/lib/manifest.js +51 -17
- package/lib/manifest.schema.json +14 -0
- package/lib/manifest.sw.js +1 -1
- package/lib/manifest.utilities.js +102 -37
- package/lib/manifest.utilities.node.mjs +226 -13
- package/package.json +4 -2
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
/* manifest.utilities.node.mjs — built from scripts/utilities/ (DOM-free) */
|
|
2
2
|
|
|
3
|
+
if (typeof globalThis.window === 'undefined') globalThis.window = globalThis;
|
|
4
|
+
if (typeof globalThis.document === 'undefined') {
|
|
5
|
+
const inertElement = () => ({ style: {}, textContent: '', classList: { add(){}, remove(){}, contains: () => false }, setAttribute(){}, appendChild(){}, insertBefore(){} });
|
|
6
|
+
globalThis.document = {
|
|
7
|
+
documentElement: { outerHTML: '' },
|
|
8
|
+
head: { firstChild: null, lastElementChild: null, innerHTML: '', appendChild(){}, insertBefore(){} },
|
|
9
|
+
querySelectorAll: () => [],
|
|
10
|
+
querySelector: () => null,
|
|
11
|
+
getElementById: () => null,
|
|
12
|
+
getElementsByTagName: () => [],
|
|
13
|
+
createElement: inertElement,
|
|
14
|
+
addEventListener(){},
|
|
15
|
+
styleSheets: []
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (typeof globalThis.getComputedStyle === 'undefined') globalThis.getComputedStyle = () => ({ getPropertyValue: () => '' });
|
|
19
|
+
if (typeof globalThis.localStorage === 'undefined') globalThis.localStorage = { getItem: () => null, setItem(){}, removeItem(){} };
|
|
20
|
+
if (typeof globalThis.performance === 'undefined') globalThis.performance = { now: () => Date.now() };
|
|
21
|
+
if (typeof globalThis.requestAnimationFrame === 'undefined') globalThis.requestAnimationFrame = (cb) => setTimeout(cb, 0);
|
|
22
|
+
|
|
3
23
|
// Utility generators
|
|
4
24
|
// Functions that generate CSS utilities from CSS variable suffixes
|
|
5
25
|
|
|
@@ -556,8 +576,10 @@ class TailwindCompiler {
|
|
|
556
576
|
|
|
557
577
|
// Read any publish/render-provided static utilities sheet before the
|
|
558
578
|
// first compile — its classes are skipped rather than regenerated.
|
|
579
|
+
// Fails open (never "everything covered") until this resolves —
|
|
580
|
+
// compile()'s first run awaits it, capped at 2s (see static.js).
|
|
559
581
|
this.staticUtilitiesCoveredClasses = null;
|
|
560
|
-
this.detectStaticUtilitiesSheet();
|
|
582
|
+
this.staticUtilitiesReady = this.detectStaticUtilitiesSheet();
|
|
561
583
|
|
|
562
584
|
// Add critical styles IMMEDIATELY - don't wait for anything
|
|
563
585
|
this.addCriticalBlockingStylesSync();
|
|
@@ -902,9 +924,12 @@ TailwindCompiler.prototype.scanStaticClasses = async function () {
|
|
|
902
924
|
|
|
903
925
|
// Extract classes from HTML content
|
|
904
926
|
TailwindCompiler.prototype.extractClassesFromHTML = function (html, classSet) {
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
|
|
927
|
+
// Literal class="..."/class='...' only — lookbehind excludes `:class=`
|
|
928
|
+
// (Alpine binding), whose "class=" substring would otherwise match and
|
|
929
|
+
// truncate at the literal's first internal quote, leaking a stray '{'.
|
|
930
|
+
// Whitespace-only split below keeps every token Tailwind would accept
|
|
931
|
+
// (variants, arbitrary values, leading '-', '!'); only x-/$ tokens drop.
|
|
932
|
+
const classRegex = /(?<![:\w])class=["']([^"']+)["']/g;
|
|
908
933
|
let match;
|
|
909
934
|
|
|
910
935
|
while ((match = classRegex.exec(html)) !== null) {
|
|
@@ -1115,8 +1140,11 @@ TailwindCompiler.prototype.fetchThemeContent = async function () {
|
|
|
1115
1140
|
TailwindCompiler.prototype.extractThemeVariables = function (cssText) {
|
|
1116
1141
|
const variables = new Map();
|
|
1117
1142
|
|
|
1118
|
-
// Extract ALL CSS custom properties from ANY declaration block
|
|
1119
|
-
|
|
1143
|
+
// Extract ALL CSS custom properties from ANY declaration block. Terminator
|
|
1144
|
+
// is a lookahead (`;` or `}`) rather than a consumed `;` so the last
|
|
1145
|
+
// declaration in a block still matches when the author omits the
|
|
1146
|
+
// trailing semicolon (valid CSS, e.g. `:root{--x:1rem}`).
|
|
1147
|
+
const varRegex = /--([\w-]+):\s*([^;}]+)(?=[;}])/g;
|
|
1120
1148
|
|
|
1121
1149
|
let varMatch;
|
|
1122
1150
|
while ((varMatch = varRegex.exec(cssText)) !== null) {
|
|
@@ -2594,6 +2622,11 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
2594
2622
|
if (!this.hasScannedStatic) {
|
|
2595
2623
|
await this.scanStaticClasses();
|
|
2596
2624
|
|
|
2625
|
+
// Wait for the static utilities sheet's covered-class read to
|
|
2626
|
+
// settle (capped at 2s in detectStaticUtilitiesSheet) so this
|
|
2627
|
+
// first compile decision isn't a guess — see static.js.
|
|
2628
|
+
if (this.staticUtilitiesReady) await this.staticUtilitiesReady;
|
|
2629
|
+
|
|
2597
2630
|
// Fetch CSS content once for initial compilation
|
|
2598
2631
|
const themeCss = await this.fetchThemeContent();
|
|
2599
2632
|
if (themeCss) {
|
|
@@ -2775,6 +2808,13 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
2775
2808
|
// buildUtilitiesNodeModule) with the DOM-free subset of the browser plugin's
|
|
2776
2809
|
// subscripts (generators, variants, main, helpers, compile) so this exports
|
|
2777
2810
|
// the exact same generation logic the browser JIT runs, with no document.
|
|
2811
|
+
//
|
|
2812
|
+
// compileUtilities() also runs the real `tailwindcss` engine (see the
|
|
2813
|
+
// "Tailwind engine pass" section below) — a real, lazily-imported dependency
|
|
2814
|
+
// used only there, via Node's fs/require by default, or via the `tailwind`
|
|
2815
|
+
// option for runtimes (e.g. Workers) that bundle the engine/CSS themselves
|
|
2816
|
+
// instead; every other export in this file stays dependency-free and
|
|
2817
|
+
// DOM-free as before.
|
|
2778
2818
|
|
|
2779
2819
|
/** A bare TailwindCompiler instance carrying only what the pure generation
|
|
2780
2820
|
* methods (generateUtilitiesFromVars, generateCustomUtilities, parseClassName,
|
|
@@ -2798,23 +2838,190 @@ function createNodeCompiler() {
|
|
|
2798
2838
|
return instance;
|
|
2799
2839
|
}
|
|
2800
2840
|
|
|
2841
|
+
// --- Tailwind engine pass -------------------------------------------------
|
|
2842
|
+
// Bakes the plain Tailwind-style utilities (flex, gap-2, rounded-full,
|
|
2843
|
+
// w-[37px], md:, hover:, …) that the runtime's bundled Tailwind engine
|
|
2844
|
+
// (lib/manifest.tailwind.js — a Tailwind v4 "Play CDN" browser build) would
|
|
2845
|
+
// otherwise generate live. Uses the real `tailwindcss` package's compile()/
|
|
2846
|
+
// build() — the same core compiler that browser build wraps, not a fork —
|
|
2847
|
+
// which is pure JS with no native binding and no DOM requirement.
|
|
2848
|
+
//
|
|
2849
|
+
// The browser engine reads only `<style type="text/tailwindcss">` tags
|
|
2850
|
+
// (default: `@import "tailwindcss";` plus whatever such tags are on the
|
|
2851
|
+
// page) and derives its candidates from `document.querySelectorAll("[class]")`
|
|
2852
|
+
// — it does not see Manifest's theme vars, so this mirrors that: no bridging
|
|
2853
|
+
// of `themeCss` into Tailwind's `@theme`. The custom variants below mirror
|
|
2854
|
+
// injectTailwindVariants() in manifest.utilities.init.js — keep both in sync.
|
|
2855
|
+
const TAILWIND_CUSTOM_VARIANTS = [
|
|
2856
|
+
'@custom-variant touch (@media (pointer: coarse));',
|
|
2857
|
+
'@custom-variant cursor (@media (pointer: fine) and (hover: hover));',
|
|
2858
|
+
'@custom-variant pointer (@media (any-pointer: fine));',
|
|
2859
|
+
'@custom-variant mac (&:where([data-os="macos"] *));',
|
|
2860
|
+
'@custom-variant windows (&:where([data-os="windows"] *));',
|
|
2861
|
+
'@custom-variant linux (&:where([data-os="linux"] *));',
|
|
2862
|
+
'@custom-variant ios (&:where([data-os="ios"] *));',
|
|
2863
|
+
'@custom-variant android (&:where([data-os="android"] *));',
|
|
2864
|
+
'@custom-variant apple (&:where([data-os="macos"] *, [data-os="ios"] *));',
|
|
2865
|
+
'@custom-variant online (&:where([data-online="true"] *));',
|
|
2866
|
+
'@custom-variant offline (&:where([data-online="false"] *));',
|
|
2867
|
+
'@custom-variant standalone (&:where([data-standalone] *));',
|
|
2868
|
+
'@custom-variant native (&:where([data-native] *));',
|
|
2869
|
+
'@custom-variant web (&:where(html:not([data-native]) *));'
|
|
2870
|
+
].join('\n');
|
|
2871
|
+
|
|
2872
|
+
/** Balanced-brace extraction of a top-level `@layer <name> { ... }` body —
|
|
2873
|
+
* the compiled CSS nests braces (media queries, `&:hover`), so a regex can't
|
|
2874
|
+
* safely find the matching close brace. Returns '' when the layer is absent. */
|
|
2875
|
+
function extractLayerBody(css, layerName) {
|
|
2876
|
+
const marker = `@layer ${layerName} {`;
|
|
2877
|
+
const start = css.indexOf(marker);
|
|
2878
|
+
if (start === -1) return '';
|
|
2879
|
+
let depth = 1;
|
|
2880
|
+
let i = start + marker.length;
|
|
2881
|
+
const bodyStart = i;
|
|
2882
|
+
for (; i < css.length && depth > 0; i++) {
|
|
2883
|
+
if (css[i] === '{') depth++;
|
|
2884
|
+
else if (css[i] === '}') depth--;
|
|
2885
|
+
}
|
|
2886
|
+
return css.slice(bodyStart, i - 1).trim();
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
let _tailwindEnginePromise = null;
|
|
2890
|
+
/** Lazily loads tailwindcss's compile() plus its theme.css/utilities.css text
|
|
2891
|
+
* — the expensive, reusable I/O (dynamic import, two fs reads). Cached
|
|
2892
|
+
* across calls; resolves to null (fail open) when the package or Node's
|
|
2893
|
+
* fs/require aren't available in this runtime (e.g. a bare Workers isolate),
|
|
2894
|
+
* so callers degrade to Manifest-only utilities rather than throwing.
|
|
2895
|
+
*
|
|
2896
|
+
* `injected`, when given, skips the fs/require path entirely: a caller that
|
|
2897
|
+
* already has the engine and CSS in hand (a bundler-built Workers isolate,
|
|
2898
|
+
* which has no `fs`/`require` — see the `tailwind` param on compileUtilities)
|
|
2899
|
+
* supplies `{ engine, themeCss, utilitiesCss }`, where `engine` is the
|
|
2900
|
+
* imported `tailwindcss` module namespace (its `compile` export is used).
|
|
2901
|
+
* Not cached on `_tailwindEnginePromise`: cheap (no I/O, just wrapping
|
|
2902
|
+
* already-resident values) and the caller owns the instance's lifetime.
|
|
2903
|
+
*
|
|
2904
|
+
* Deliberately skips tailwindcss/preflight.css: the browser engine's own
|
|
2905
|
+
* default `@import "tailwindcss"` — see `jo`/`Io` in lib/manifest.tailwind.js
|
|
2906
|
+
* — wires up only theme.css and utilities.css and leaves preflight empty, so
|
|
2907
|
+
* matching it means never importing preflight here either (Manifest ships
|
|
2908
|
+
* its own reset; the runtime never asks Tailwind for one). */
|
|
2909
|
+
function loadTailwindEngine(injected) {
|
|
2910
|
+
if (injected) {
|
|
2911
|
+
const { engine, themeCss, utilitiesCss } = injected;
|
|
2912
|
+
if (!engine || typeof engine.compile !== 'function' || !themeCss || !utilitiesCss) {
|
|
2913
|
+
return Promise.resolve(null);
|
|
2914
|
+
}
|
|
2915
|
+
return Promise.resolve({ compile: engine.compile, base: '/', themeCss, utilitiesCss });
|
|
2916
|
+
}
|
|
2917
|
+
if (_tailwindEnginePromise) return _tailwindEnginePromise;
|
|
2918
|
+
_tailwindEnginePromise = (async () => {
|
|
2919
|
+
try {
|
|
2920
|
+
const { compile } = await import('tailwindcss');
|
|
2921
|
+
const fs = await import('node:fs');
|
|
2922
|
+
const path = await import('node:path');
|
|
2923
|
+
const { createRequire } = await import('node:module');
|
|
2924
|
+
const req = createRequire(import.meta.url);
|
|
2925
|
+
const themePath = req.resolve('tailwindcss/theme.css');
|
|
2926
|
+
const utilitiesPath = req.resolve('tailwindcss/utilities.css');
|
|
2927
|
+
const themeCss = fs.readFileSync(themePath, 'utf8');
|
|
2928
|
+
const utilitiesCss = fs.readFileSync(utilitiesPath, 'utf8');
|
|
2929
|
+
const base = path.dirname(themePath);
|
|
2930
|
+
return { compile, base, themeCss, utilitiesCss };
|
|
2931
|
+
} catch (e) {
|
|
2932
|
+
return null;
|
|
2933
|
+
}
|
|
2934
|
+
})();
|
|
2935
|
+
return _tailwindEnginePromise;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
/** Runs the real Tailwind engine over exactly `classes`, returning its
|
|
2939
|
+
* (tree-shaken, used-only) `theme` and `utilities` layer bodies as a single
|
|
2940
|
+
* CSS chunk. Returns '' when Tailwind can't be loaded here, or produced no
|
|
2941
|
+
* utility rules for these classes (the default theme's font/spacing tokens
|
|
2942
|
+
* would otherwise show up even for a class list with no real Tailwind match
|
|
2943
|
+
* — e.g. Manifest's own `.row`/`.col` — since they're always defined; only
|
|
2944
|
+
* emit the theme chunk alongside actual utility output).
|
|
2945
|
+
*
|
|
2946
|
+
* `injected` is passed straight through to loadTailwindEngine — see there.
|
|
2947
|
+
*
|
|
2948
|
+
* A fresh compiler is built per call (~a few ms — see loadTailwindEngine for
|
|
2949
|
+
* the cached, reusable part): tailwindcss's compiler.build() is an
|
|
2950
|
+
* incremental/watch-mode API that accumulates every candidate it has ever
|
|
2951
|
+
* seen, so reusing one compiler across calls with different class lists
|
|
2952
|
+
* would leak earlier calls' classes into later output — the same reason the
|
|
2953
|
+
* browser engine tracks its own "already seen" set (see the header comment)
|
|
2954
|
+
* before calling build(). */
|
|
2955
|
+
async function compileTailwindPass(classes, injected) {
|
|
2956
|
+
if (!classes.length) return '';
|
|
2957
|
+
const engine = await loadTailwindEngine(injected);
|
|
2958
|
+
if (!engine) return '';
|
|
2959
|
+
try {
|
|
2960
|
+
const { compile, base, themeCss, utilitiesCss } = engine;
|
|
2961
|
+
const input = `@layer theme, base, components, utilities;\n@import "tailwindcss/theme" layer(theme);\n@import "tailwindcss/utilities" layer(utilities);\n${TAILWIND_CUSTOM_VARIANTS}\n`;
|
|
2962
|
+
const compiler = await compile(input, {
|
|
2963
|
+
base,
|
|
2964
|
+
loadStylesheet: async (id) => {
|
|
2965
|
+
if (id === 'tailwindcss/theme') return { base, content: themeCss };
|
|
2966
|
+
if (id === 'tailwindcss/utilities') return { base, content: utilitiesCss };
|
|
2967
|
+
throw new Error(`manifest utilities: cannot load "${id}" for Tailwind bake`);
|
|
2968
|
+
},
|
|
2969
|
+
loadModule: async () => { throw new Error('manifest utilities: Tailwind plugins unsupported in bake'); }
|
|
2970
|
+
});
|
|
2971
|
+
const css = compiler.build(classes);
|
|
2972
|
+
const utilities = extractLayerBody(css, 'utilities');
|
|
2973
|
+
if (!utilities) return '';
|
|
2974
|
+
const theme = extractLayerBody(css, 'theme');
|
|
2975
|
+
const parts = [];
|
|
2976
|
+
if (theme) parts.push(`@layer theme {\n${theme}\n}`);
|
|
2977
|
+
parts.push(`@layer utilities {\n${utilities}\n}`);
|
|
2978
|
+
return parts.join('\n\n');
|
|
2979
|
+
} catch (e) {
|
|
2980
|
+
return '';
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2801
2984
|
/**
|
|
2802
|
-
* Compile the utility CSS the browser plugin
|
|
2803
|
-
*
|
|
2804
|
-
*
|
|
2985
|
+
* Compile the utility CSS the browser plugin (Manifest's own generators) plus
|
|
2986
|
+
* the framework's bundled Tailwind engine would together produce for exactly
|
|
2987
|
+
* `classes`. Manifest side: variants, theme-variable-driven utilities
|
|
2988
|
+
* (generateUtilitiesFromVars) and custom utilities (generateCustomUtilities)
|
|
2989
|
+
* discovered in `themeCss`/`baseCss`. Tailwind side: whatever real Tailwind
|
|
2990
|
+
* utilities (`flex`, `gap-2`, `rounded-full`, arbitrary values like
|
|
2991
|
+
* `w-[37px]`, and their variants) the class list resolves to, via the actual
|
|
2992
|
+
* `tailwindcss` compiler (see compileTailwindPass above) — not a fork.
|
|
2805
2993
|
* Deterministic for a given (classes, themeCss, baseCss) triple.
|
|
2806
2994
|
*
|
|
2807
|
-
*
|
|
2995
|
+
* `baseCss` should include the framework's own utility CSS (lib/manifest.css)
|
|
2996
|
+
* whenever the caller isn't already loading it: the browser plugin's compile()
|
|
2997
|
+
* finds it for free via discoverCssFiles() scanning the page's own
|
|
2998
|
+
* stylesheets, so generateCustomUtilities() recognises Manifest's semantic
|
|
2999
|
+
* utilities (.row, .col, .center, …) and bakes variants like `md:row` or
|
|
3000
|
+
* `hover:col-wrap`. Without it those variants are silently skipped here —
|
|
3001
|
+
* scripts/utilities-static.mjs loads it automatically for this reason.
|
|
3002
|
+
*
|
|
3003
|
+
* `tailwind`, when given, is passed to the Tailwind engine pass instead of
|
|
3004
|
+
* its default lazy `fs`/`require` load — `{ engine, themeCss, utilitiesCss }`
|
|
3005
|
+
* where `engine` is the imported `tailwindcss` module namespace and the two
|
|
3006
|
+
* CSS strings are the contents of `tailwindcss/theme.css` and
|
|
3007
|
+
* `tailwindcss/utilities.css`. For runtimes that can statically import
|
|
3008
|
+
* `tailwindcss` and inline its CSS at bundle time but have no `fs`/`require`
|
|
3009
|
+
* to read them from node_modules at runtime (e.g. a Cloudflare Workers
|
|
3010
|
+
* isolate) — see loadTailwindEngine. Omit it (the default) to keep using the
|
|
3011
|
+
* Node/CLI fs-based load.
|
|
3012
|
+
*
|
|
3013
|
+
* @param {{classes?: string[], themeCss?: string, baseCss?: string, tailwind?: {engine: object, themeCss: string, utilitiesCss: string}}} options
|
|
2808
3014
|
* @returns {Promise<string>}
|
|
2809
3015
|
*/
|
|
2810
|
-
export async function compileUtilities({ classes = [], themeCss = '', baseCss = '' } = {}) {
|
|
3016
|
+
export async function compileUtilities({ classes = [], themeCss = '', baseCss = '', tailwind = null } = {}) {
|
|
2811
3017
|
const compiler = createNodeCompiler();
|
|
2812
3018
|
const cssText = [baseCss, themeCss].filter(Boolean).join('\n');
|
|
3019
|
+
const uniqueClasses = Array.from(new Set(classes));
|
|
2813
3020
|
|
|
2814
3021
|
const discovered = compiler.extractCustomUtilities(cssText);
|
|
2815
3022
|
for (const [name, value] of discovered) compiler.customUtilities.set(name, value);
|
|
2816
3023
|
|
|
2817
|
-
const usedData = { classes:
|
|
3024
|
+
const usedData = { classes: uniqueClasses, variableSuffixes: [] };
|
|
2818
3025
|
|
|
2819
3026
|
const varUtilities = compiler.generateUtilitiesFromVars(cssText, usedData);
|
|
2820
3027
|
const customUtilities = compiler.generateCustomUtilities(usedData);
|
|
@@ -2822,7 +3029,13 @@ export async function compileUtilities({ classes = [], themeCss = '', baseCss =
|
|
|
2822
3029
|
let allUtilities = [varUtilities, customUtilities].filter(Boolean).join('\n\n');
|
|
2823
3030
|
allUtilities = compiler.sortUtilities(allUtilities);
|
|
2824
3031
|
|
|
2825
|
-
|
|
3032
|
+
const manifestLayer = allUtilities ? `@layer utilities {\n${allUtilities}\n}` : '';
|
|
3033
|
+
const tailwindLayer = await compileTailwindPass(uniqueClasses, tailwind);
|
|
3034
|
+
|
|
3035
|
+
const body = [manifestLayer, tailwindLayer].filter(Boolean).join('\n\n');
|
|
3036
|
+
if (!body) return '';
|
|
3037
|
+
// Layer order preamble: wins over a later `base` reset regardless of load order.
|
|
3038
|
+
return `@layer base, components, utilities;\n\n${body}`;
|
|
2826
3039
|
}
|
|
2827
3040
|
|
|
2828
3041
|
// `class:token=` (per-class conditional binding) and the `:class="..."`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mnfst",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.205",
|
|
4
4
|
"private": false,
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"templates/starter",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"README.md",
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"tailwindcss": "^4.2.4"
|
|
25
|
+
},
|
|
23
26
|
"scripts": {
|
|
24
27
|
"clean": "rimraf src/styles/manifest.css src/styles/manifest.min.css src/styles/manifest.theme.css src/styles/manifest.code.css src/styles/manifest.code.min.css lib",
|
|
25
28
|
"build": "cd src && node scripts/build.mjs",
|
|
@@ -58,7 +61,6 @@
|
|
|
58
61
|
"puppeteer": "^24.15.0",
|
|
59
62
|
"rimraf": "^5.0.0",
|
|
60
63
|
"rollup": "^4.24.2",
|
|
61
|
-
"tailwindcss": "^4.2.4",
|
|
62
64
|
"vitest": "^4.1.2"
|
|
63
65
|
},
|
|
64
66
|
"keywords": [
|