diff options
author | 2021-11-04 14:01:28 -0600 | |
---|---|---|
committer | 2021-11-04 14:01:28 -0600 | |
commit | 2e1bded735f3e2149c3ff95b1c131a147dda0f34 (patch) | |
tree | 1f4433cad8db49e5be6ae8bbc4f277de9d5fa7e0 | |
parent | 2c5e67bb44398080037c02fbdd756cc66e757f1e (diff) | |
download | astro-2e1bded735f3e2149c3ff95b1c131a147dda0f34.tar.gz astro-2e1bded735f3e2149c3ff95b1c131a147dda0f34.tar.zst astro-2e1bded735f3e2149c3ff95b1c131a147dda0f34.zip |
Get Tailwind HMR working (first cut) (#1736)
* Get Tailwind HMR working
* PR feedback
* perf: improve HMR `head` performance
Co-authored-by: Nate Moore <nate@skypack.dev>
-rw-r--r-- | .changeset/shaggy-guests-type.md | 5 | ||||
-rw-r--r-- | docs/src/pages/guides/styling.md | 8 | ||||
-rw-r--r-- | examples/with-tailwindcss/package.json | 4 | ||||
-rw-r--r-- | examples/with-tailwindcss/postcss.config.js | 6 | ||||
-rw-r--r-- | examples/with-tailwindcss/src/components/Button.astro | 5 | ||||
-rw-r--r-- | examples/with-tailwindcss/src/pages/index.astro | 4 | ||||
-rw-r--r-- | packages/astro/src/@types/astro-core.ts | 8 | ||||
-rw-r--r-- | packages/astro/src/core/dev/index.ts | 65 | ||||
-rw-r--r-- | packages/astro/src/core/ssr/css.ts | 1 | ||||
-rw-r--r-- | packages/astro/src/core/ssr/html.ts | 85 | ||||
-rw-r--r-- | packages/astro/src/core/ssr/index.ts | 3 | ||||
-rw-r--r-- | packages/astro/src/runtime/client/hmr.ts | 7 | ||||
-rw-r--r-- | yarn.lock | 60 |
13 files changed, 176 insertions, 85 deletions
diff --git a/.changeset/shaggy-guests-type.md b/.changeset/shaggy-guests-type.md new file mode 100644 index 000000000..062ba96be --- /dev/null +++ b/.changeset/shaggy-guests-type.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +fix: Improve Tailwind HMR diff --git a/docs/src/pages/guides/styling.md b/docs/src/pages/guides/styling.md index 98df62b8a..a42d759df 100644 --- a/docs/src/pages/guides/styling.md +++ b/docs/src/pages/guides/styling.md @@ -235,6 +235,14 @@ Now you're ready to write Tailwind! Our recommended approach is to create a `src @tailwind utilities; ``` +Lastly, add it to your Astro page (or layout template): + +```astro +<head> + <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}> +</head> +``` + As an alternative to `src/styles/global.css`, You may also add Tailwind utilities to individual `pages/*.astro` components in `<style>` tags, but be mindful of duplication! If you end up creating multiple Tailwind-managed stylesheets for your site, make sure you're not sending the same CSS to users over and over again in separate CSS files. #### Migrating from v0.19 diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json index 7132c4391..a7ee6a2bf 100644 --- a/examples/with-tailwindcss/package.json +++ b/examples/with-tailwindcss/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "astro": "^0.21.0-next.1", - "autoprefixer": "^10.3.7", - "tailwindcss": "^2.2.17" + "autoprefixer": "^10.4.0", + "tailwindcss": "^2.2.19" } } diff --git a/examples/with-tailwindcss/postcss.config.js b/examples/with-tailwindcss/postcss.config.js index 12a703d90..7cffbeb55 100644 --- a/examples/with-tailwindcss/postcss.config.js +++ b/examples/with-tailwindcss/postcss.config.js @@ -1,6 +1,10 @@ +const path = require('path'); + module.exports = { plugins: { - tailwindcss: {}, + tailwindcss: { + config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs! + }, autoprefixer: {}, }, }; diff --git a/examples/with-tailwindcss/src/components/Button.astro b/examples/with-tailwindcss/src/components/Button.astro index 64baa50ae..966c613aa 100644 --- a/examples/with-tailwindcss/src/components/Button.astro +++ b/examples/with-tailwindcss/src/components/Button.astro @@ -1,3 +1,6 @@ -<button class="py-2 px-4 bg-green-500 text-white font-semibold rounded-lg shadow-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-opacity-75"> +--- +let { type = 'button' } = Astro.props; +--- +<button class="py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75" type={type}> <slot /> </button> diff --git a/examples/with-tailwindcss/src/pages/index.astro b/examples/with-tailwindcss/src/pages/index.astro index 10f370ba3..28d5f3dec 100644 --- a/examples/with-tailwindcss/src/pages/index.astro +++ b/examples/with-tailwindcss/src/pages/index.astro @@ -8,9 +8,7 @@ import Button from '../components/Button.astro'; <html lang="en"> <head> <meta charset="UTF-8" /> - - <link rel="icon" type="image/x-icon" href="/favicon.ico" /> - + <link rel="icon" type="image/x-icon" href="/favicon.ico" /> <title>Astro + TailwindCSS</title> <link rel="stylesheet" type="text/css" href={Astro.resolve("../styles/global.css")}> </head> diff --git a/packages/astro/src/@types/astro-core.ts b/packages/astro/src/@types/astro-core.ts index 9a5d718fc..29eb7bba4 100644 --- a/packages/astro/src/@types/astro-core.ts +++ b/packages/astro/src/@types/astro-core.ts @@ -139,8 +139,11 @@ export interface CollectionRSS { /** Generic interface for a component (Astro, Svelte, React, etc.) */ export interface ComponentInstance { + $$metadata: { + modules: { module: Record<string, unknown>; specifier: string }[]; + fileURL: URL; + }; default: AstroComponentFactory; - css?: string[]; getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult; } @@ -263,6 +266,9 @@ export interface Renderer { knownEntrypoints?: string[]; } +/** <link> tags with attributes represented by an object */ +export type Resource = Record<string, string>; + export interface RouteData { component: string; generate: (data?: any) => string; diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts index a128fa1ec..f0e82df14 100644 --- a/packages/astro/src/core/dev/index.ts +++ b/packages/astro/src/core/dev/index.ts @@ -4,6 +4,7 @@ import type { AstroConfig, ManifestData, RouteCache, RouteData } from '../../@ty import type { LogOptions } from '../logger'; import type { HmrContext, ModuleNode } from '../vite'; +import path from 'path'; import { fileURLToPath } from 'url'; import { promisify } from 'util'; import connect from 'connect'; @@ -13,6 +14,8 @@ import stripAnsi from 'strip-ansi'; import vite from '../vite.js'; import { defaultLogOptions, error, info } from '../logger.js'; import { ssr } from '../ssr/index.js'; +import { STYLE_EXTENSIONS } from '../ssr/css.js'; +import { collectResources } from '../ssr/html.js'; import { createRouteManifest, matchRoute } from '../ssr/routing.js'; import { createVite } from '../create-vite.js'; import * as msg from './messages.js'; @@ -51,7 +54,6 @@ export class AstroDevServer { httpServer: http.Server | undefined; hostname: string; port: number; - private config: AstroConfig; private logging: LogOptions; private manifest: ManifestData; @@ -92,49 +94,94 @@ export class AstroDevServer { } public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> { - if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`); + const { viteServer } = this; + if (!viteServer) throw new Error(`AstroDevServer.start() not called`); for (const module of modules) { - this.viteServer.moduleGraph.invalidateModule(module); + viteServer.moduleGraph.invalidateModule(module); } const route = this.mostRecentRoute; const pathname = route?.pathname ?? '/'; if (!route) { - this.viteServer.ws.send({ + viteServer.ws.send({ type: 'full-reload', }); return []; } try { + const filePath = new URL(`./${route.component}`, this.config.projectRoot); // try to update the most recent route const html = await ssr({ astroConfig: this.config, - filePath: new URL(`./${route.component}`, this.config.projectRoot), + filePath, logging: this.logging, mode: 'development', origin: this.origin, pathname, route, routeCache: this.routeCache, - viteServer: this.viteServer, + viteServer, }); + // collect style tags to be reloaded (needed for Tailwind HMR, etc.) + let invalidatedModules: vite.ModuleNode[] = []; + await Promise.all( + collectResources(html) + .filter(({ href }) => { + if (!href) return false; + const ext = path.extname(href); + return STYLE_EXTENSIONS.has(ext); + }) + .map(async ({ href }) => { + const viteModule = + viteServer.moduleGraph.getModuleById(`${href}?direct`) || + (await viteServer.moduleGraph.getModuleByUrl(`${href}?direct`)) || + viteServer.moduleGraph.getModuleById(href) || + (await viteServer.moduleGraph.getModuleByUrl(href)); + if (viteModule) { + invalidatedModules.push(viteModule); + viteServer.moduleGraph.invalidateModule(viteModule); + } + }) + ); + // TODO: log update - this.viteServer.ws.send({ + viteServer.ws.send({ type: 'custom', event: 'astro:reload', data: { html }, }); + + for (const viteModule of invalidatedModules) { + // Note: from the time viteServer.moduleGraph.invalidateModule() is called above until now, CSS + // is building in the background. For things like Tailwind, this can take some time. If the + // CSS is still processing by the time HMR fires, we’ll end up with stale styles on the page. + // TODO: fix this hack by figuring out how to add these styles to the { modules } above + setTimeout(() => { + viteServer.ws.send({ + type: 'update', + updates: [ + { + type: viteModule.type === 'js' ? 'js-update' : 'css-update', + path: viteModule.id || viteModule.file || viteModule.url, + acceptedPath: viteModule.url, + timestamp: Date.now(), + }, + ], + }); + }, 150); + } + return []; } catch (e) { const err = e as Error; - this.viteServer.ssrFixStacktrace(err); + viteServer.ssrFixStacktrace(err); // eslint-disable-next-line console.error(err.stack); - this.viteServer.ws.send({ + viteServer.ws.send({ type: 'full-reload', }); return []; diff --git a/packages/astro/src/core/ssr/css.ts b/packages/astro/src/core/ssr/css.ts index 688fea2a6..9fba52fca 100644 --- a/packages/astro/src/core/ssr/css.ts +++ b/packages/astro/src/core/ssr/css.ts @@ -1,7 +1,6 @@ import type vite from '../../../vendor/vite'; import path from 'path'; -import htmlparser2 from 'htmlparser2'; // https://vitejs.dev/guide/features.html#css-pre-processors export const STYLE_EXTENSIONS = new Set(['.css', '.pcss', '.scss', '.sass', '.styl', '.stylus', '.less']); diff --git a/packages/astro/src/core/ssr/html.ts b/packages/astro/src/core/ssr/html.ts index 6608bbb2f..faa6f055b 100644 --- a/packages/astro/src/core/ssr/html.ts +++ b/packages/astro/src/core/ssr/html.ts @@ -4,53 +4,68 @@ import htmlparser2 from 'htmlparser2'; /** Inject tags into HTML (note: for best performance, group as many tags as possible into as few calls as you can) */ export function injectTags(html: string, tags: vite.HtmlTagDescriptor[]): string { - // TODO: this usually takes 5ms or less, but if it becomes a bottleneck we can create a WeakMap cache let output = html; if (!tags.length) return output; const pos = { 'head-prepend': -1, head: -1, 'body-prepend': -1, body: -1 }; - try { - // parse html - const parser = new htmlparser2.Parser({ - onopentag(tagname) { - if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1; - if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1; - }, - onclosetag(tagname) { - if (tagname === 'head') pos['head'] = parser.startIndex; - if (tagname === 'body') pos['body'] = parser.startIndex; - }, - }); - parser.write(html); - parser.end(); - - // inject - const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]); - lastToFirst.forEach(([name, i]) => { - if (i === -1) { - // TODO: warn on missing tag? Is this an HTML partial? - return; + // parse html + const parser = new htmlparser2.Parser({ + onopentag(tagname) { + if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1; + if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1; + }, + onclosetag(tagname) { + if (tagname === 'head') pos['head'] = parser.startIndex; + if (tagname === 'body') pos['body'] = parser.startIndex; + }, + }); + parser.write(html); + parser.end(); + + // inject + const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]); + lastToFirst.forEach(([name, i]) => { + if (i === -1) { + // TODO: warn on missing tag? Is this an HTML partial? + return; + } + let selected = tags.filter(({ injectTo }) => { + if (name === 'head-prepend' && !injectTo) { + return true; // "head-prepend" is the default + } else { + return injectTo === name; } - let selected = tags.filter(({ injectTo }) => { - if (name === 'head-prepend' && !injectTo) { - return true; // "head-prepend" is the default - } else { - return injectTo === name; - } - }); - if (!selected.length) return; - output = output.substring(0, i) + serializeTags(selected) + html.substring(i); }); - } catch (err) { - // on invalid HTML, do nothing - } + if (!selected.length) return; + output = output.substring(0, i) + serializeTags(selected) + html.substring(i); + }); return output; } -// Everything below © Vite +type Resource = Record<string, string>; + +/** Collect resources (scans final, rendered HTML so expressions have been applied) */ +export function collectResources(html: string): Resource[] { + let resources: Resource[] = []; + const parser = new htmlparser2.Parser({ + // <link> tags are self-closing, so only use onopentag (avoid onattribute or onclosetag) + onopentag(tagname, attrs) { + if (tagname === 'link') resources.push(attrs); + }, + }); + parser.write(html); + parser.end(); + return resources; +} + +// ------------------------------------------------------------------------------- +// Everything below © Vite. Rather than invent our own tag creating API, we borrow +// Vite’s `transformIndexHtml()` API for ease-of-use and consistency. But we need +// to borrow a few private methods in Vite to make that available here. // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/html.ts +// ------------------------------------------------------------------------------- // Vite is released under the MIT license: diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts index e2c4b76c0..8823c483e 100644 --- a/packages/astro/src/core/ssr/index.ts +++ b/packages/astro/src/core/ssr/index.ts @@ -4,7 +4,6 @@ import type { AstroConfig, ComponentInstance, GetStaticPathsResult, Params, Prop import type { AstroGlobal, TopLevelAstro, SSRResult, SSRElement } from '../../@types/astro-runtime'; import type { LogOptions } from '../logger'; -import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'path'; import { renderPage, renderSlot } from '../../runtime/server/index.js'; @@ -164,7 +163,7 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna // inject tags const tags: vite.HtmlTagDescriptor[] = []; - // inject Astro HMR client (dev only) + // dev only: inject Astro HMR client if (mode === 'development') { tags.push({ tag: 'script', diff --git a/packages/astro/src/runtime/client/hmr.ts b/packages/astro/src/runtime/client/hmr.ts index ea79c7dfc..e2f65da94 100644 --- a/packages/astro/src/runtime/client/hmr.ts +++ b/packages/astro/src/runtime/client/hmr.ts @@ -6,10 +6,17 @@ if (import.meta.hot) { morphdom(document.head, doc.head, { onBeforeElUpdated(fromEl, toEl) { + // Do not update identical tags if (fromEl.isEqualNode(toEl)) { return false; } + // Do not update <link> or <script> tags + // to avoid re-fetching their contents + if (fromEl.tagName === toEl.tagName && (toEl.tagName === 'LINK' || toEl.tagName === 'SCRIPT')) { + return false; + } + return true; }, }); @@ -2549,16 +2549,16 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -autoprefixer@^10.3.7: - version "10.3.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.7.tgz#cef2562058406bd378c94aacda36bb46a97b3186" - integrity sha512-EmGpu0nnQVmMhX8ROoJ7Mx8mKYPlcUHuxkwrRYEYMz85lu7H09v8w6R1P0JPdn/hKU32GjpLBFEOuIlDWCRWvg== +autoprefixer@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.0.tgz#c3577eb32a1079a440ec253e404eaf1eb21388c8" + integrity sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA== dependencies: - browserslist "^4.17.3" - caniuse-lite "^1.0.30001264" + browserslist "^4.17.5" + caniuse-lite "^1.0.30001272" fraction.js "^4.1.1" normalize-range "^0.1.2" - picocolors "^0.2.1" + picocolors "^1.0.0" postcss-value-parser "^4.1.0" available-typed-arrays@^1.0.5: @@ -2778,15 +2778,15 @@ browserslist@^4.16.6: escalade "^3.1.1" node-releases "^1.1.75" -browserslist@^4.17.3: - version "4.17.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4" - integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ== +browserslist@^4.17.5: + version "4.17.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.5.tgz#c827bbe172a4c22b123f5e337533ceebadfdd559" + integrity sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA== dependencies: - caniuse-lite "^1.0.30001265" - electron-to-chromium "^1.3.867" + caniuse-lite "^1.0.30001271" + electron-to-chromium "^1.3.878" escalade "^3.1.1" - node-releases "^2.0.0" + node-releases "^2.0.1" picocolors "^1.0.0" buffer-crc32@~0.2.3: @@ -2933,10 +2933,10 @@ caniuse-lite@^1.0.30001254: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz#b604eed80cc54a578e4bf5a02ae3ed49f869d252" integrity sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA== -caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001265: - version "1.0.30001267" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz#b1cf2937175afc0570e4615fc2d2f9069fa0ed30" - integrity sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg== +caniuse-lite@^1.0.30001271, caniuse-lite@^1.0.30001272: + version "1.0.30001274" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz#26ca36204d15b17601ba6fc35dbdad950a647cc7" + integrity sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew== caseless@~0.12.0: version "0.12.0" @@ -3952,10 +3952,10 @@ electron-to-chromium@^1.3.830: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.843.tgz#671489bd2f59fd49b76adddc1aa02c88cd38a5c0" integrity sha512-OWEwAbzaVd1Lk9MohVw8LxMXFlnYd9oYTYxfX8KS++kLLjDfbovLOcEEXwRhG612dqGQ6+44SZvim0GXuBRiKg== -electron-to-chromium@^1.3.867: - version "1.3.871" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.871.tgz#6e87365fd72037a6c898fb46050ad4be3ac9ef62" - integrity sha512-qcLvDUPf8DSIMWarHT2ptgcqrYg62n3vPA7vhrOF24d8UNzbUBaHu2CySiENR3nEDzYgaN60071t0F6KLYMQ7Q== +electron-to-chromium@^1.3.878: + version "1.3.886" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz#ac039c4001b665b1dd0f0ed9c2e4da90ff3c9267" + integrity sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ== emmet@^2.1.5: version "2.3.4" @@ -7729,10 +7729,10 @@ node-releases@^1.1.75: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe" integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw== -node-releases@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.0.tgz#67dc74903100a7deb044037b8a2e5f453bb05400" - integrity sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA== +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== node.extend@~2.0.2: version "2.0.2" @@ -10224,10 +10224,10 @@ svelte@^3.44.0: resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.44.0.tgz#e6176cb3ad93846ddb4140e93f43098136b23f3b" integrity sha512-zWACSJBSncGiDvFfYOMFGNV5zDLOlyhftmO5yOZ0lEtQMptpElaRtl39MWz1+lYCpwUq4F3Q2lTzI9TrTL+eMA== -tailwindcss@^2.2.17: - version "2.2.17" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.17.tgz#c6332731f9ff1b6628ff589c95c38685347775e3" - integrity sha512-WgRpn+Pxn7eWqlruxnxEbL9ByVRWi3iC10z4b6dW0zSdnkPVC4hPMSWLQkkW8GCyBIv/vbJ0bxIi9dVrl4CfoA== +tailwindcss@^2.2.19: + version "2.2.19" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c" + integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw== dependencies: arg "^5.0.1" bytes "^3.0.0" |