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>
This commit is contained in:
parent
2c5e67bb44
commit
2e1bded735
13 changed files with 175 additions and 84 deletions
5
.changeset/shaggy-guests-type.md
Normal file
5
.changeset/shaggy-guests-type.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: Improve Tailwind HMR
|
|
@ -235,6 +235,14 @@ Now you're ready to write Tailwind! Our recommended approach is to create a `src
|
||||||
@tailwind utilities;
|
@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.
|
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
|
#### Migrating from v0.19
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"astro": "^0.21.0-next.1",
|
"astro": "^0.21.0-next.1",
|
||||||
"autoprefixer": "^10.3.7",
|
"autoprefixer": "^10.4.0",
|
||||||
"tailwindcss": "^2.2.17"
|
"tailwindcss": "^2.2.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {
|
||||||
|
config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs!
|
||||||
|
},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 />
|
<slot />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,9 +8,7 @@ import Button from '../components/Button.astro';
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<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>
|
<title>Astro + TailwindCSS</title>
|
||||||
<link rel="stylesheet" type="text/css" href={Astro.resolve("../styles/global.css")}>
|
<link rel="stylesheet" type="text/css" href={Astro.resolve("../styles/global.css")}>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -139,8 +139,11 @@ export interface CollectionRSS {
|
||||||
|
|
||||||
/** Generic interface for a component (Astro, Svelte, React, etc.) */
|
/** Generic interface for a component (Astro, Svelte, React, etc.) */
|
||||||
export interface ComponentInstance {
|
export interface ComponentInstance {
|
||||||
|
$$metadata: {
|
||||||
|
modules: { module: Record<string, unknown>; specifier: string }[];
|
||||||
|
fileURL: URL;
|
||||||
|
};
|
||||||
default: AstroComponentFactory;
|
default: AstroComponentFactory;
|
||||||
css?: string[];
|
|
||||||
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
|
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +266,9 @@ export interface Renderer {
|
||||||
knownEntrypoints?: string[];
|
knownEntrypoints?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** <link> tags with attributes represented by an object */
|
||||||
|
export type Resource = Record<string, string>;
|
||||||
|
|
||||||
export interface RouteData {
|
export interface RouteData {
|
||||||
component: string;
|
component: string;
|
||||||
generate: (data?: any) => string;
|
generate: (data?: any) => string;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import type { AstroConfig, ManifestData, RouteCache, RouteData } from '../../@ty
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
import type { HmrContext, ModuleNode } from '../vite';
|
import type { HmrContext, ModuleNode } from '../vite';
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import connect from 'connect';
|
import connect from 'connect';
|
||||||
|
@ -13,6 +14,8 @@ import stripAnsi from 'strip-ansi';
|
||||||
import vite from '../vite.js';
|
import vite from '../vite.js';
|
||||||
import { defaultLogOptions, error, info } from '../logger.js';
|
import { defaultLogOptions, error, info } from '../logger.js';
|
||||||
import { ssr } from '../ssr/index.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 { createRouteManifest, matchRoute } from '../ssr/routing.js';
|
||||||
import { createVite } from '../create-vite.js';
|
import { createVite } from '../create-vite.js';
|
||||||
import * as msg from './messages.js';
|
import * as msg from './messages.js';
|
||||||
|
@ -51,7 +54,6 @@ export class AstroDevServer {
|
||||||
httpServer: http.Server | undefined;
|
httpServer: http.Server | undefined;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
port: number;
|
port: number;
|
||||||
|
|
||||||
private config: AstroConfig;
|
private config: AstroConfig;
|
||||||
private logging: LogOptions;
|
private logging: LogOptions;
|
||||||
private manifest: ManifestData;
|
private manifest: ManifestData;
|
||||||
|
@ -92,49 +94,94 @@ export class AstroDevServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> {
|
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) {
|
for (const module of modules) {
|
||||||
this.viteServer.moduleGraph.invalidateModule(module);
|
viteServer.moduleGraph.invalidateModule(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = this.mostRecentRoute;
|
const route = this.mostRecentRoute;
|
||||||
const pathname = route?.pathname ?? '/';
|
const pathname = route?.pathname ?? '/';
|
||||||
|
|
||||||
if (!route) {
|
if (!route) {
|
||||||
this.viteServer.ws.send({
|
viteServer.ws.send({
|
||||||
type: 'full-reload',
|
type: 'full-reload',
|
||||||
});
|
});
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const filePath = new URL(`./${route.component}`, this.config.projectRoot);
|
||||||
// try to update the most recent route
|
// try to update the most recent route
|
||||||
const html = await ssr({
|
const html = await ssr({
|
||||||
astroConfig: this.config,
|
astroConfig: this.config,
|
||||||
filePath: new URL(`./${route.component}`, this.config.projectRoot),
|
filePath,
|
||||||
logging: this.logging,
|
logging: this.logging,
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
origin: this.origin,
|
origin: this.origin,
|
||||||
pathname,
|
pathname,
|
||||||
route,
|
route,
|
||||||
routeCache: this.routeCache,
|
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
|
// TODO: log update
|
||||||
this.viteServer.ws.send({
|
viteServer.ws.send({
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
event: 'astro:reload',
|
event: 'astro:reload',
|
||||||
data: { html },
|
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 [];
|
return [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as Error;
|
const err = e as Error;
|
||||||
this.viteServer.ssrFixStacktrace(err);
|
viteServer.ssrFixStacktrace(err);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
this.viteServer.ws.send({
|
viteServer.ws.send({
|
||||||
type: 'full-reload',
|
type: 'full-reload',
|
||||||
});
|
});
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import type vite from '../../../vendor/vite';
|
import type vite from '../../../vendor/vite';
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import htmlparser2 from 'htmlparser2';
|
|
||||||
|
|
||||||
// https://vitejs.dev/guide/features.html#css-pre-processors
|
// https://vitejs.dev/guide/features.html#css-pre-processors
|
||||||
export const STYLE_EXTENSIONS = new Set(['.css', '.pcss', '.scss', '.sass', '.styl', '.stylus', '.less']);
|
export const STYLE_EXTENSIONS = new Set(['.css', '.pcss', '.scss', '.sass', '.styl', '.stylus', '.less']);
|
||||||
|
|
|
@ -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) */
|
/** 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 {
|
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;
|
let output = html;
|
||||||
if (!tags.length) return output;
|
if (!tags.length) return output;
|
||||||
|
|
||||||
const pos = { 'head-prepend': -1, head: -1, 'body-prepend': -1, body: -1 };
|
const pos = { 'head-prepend': -1, head: -1, 'body-prepend': -1, body: -1 };
|
||||||
|
|
||||||
try {
|
// parse html
|
||||||
// parse html
|
const parser = new htmlparser2.Parser({
|
||||||
const parser = new htmlparser2.Parser({
|
onopentag(tagname) {
|
||||||
onopentag(tagname) {
|
if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1;
|
||||||
if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1;
|
if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1;
|
||||||
if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1;
|
},
|
||||||
},
|
onclosetag(tagname) {
|
||||||
onclosetag(tagname) {
|
if (tagname === 'head') pos['head'] = parser.startIndex;
|
||||||
if (tagname === 'head') pos['head'] = parser.startIndex;
|
if (tagname === 'body') pos['body'] = parser.startIndex;
|
||||||
if (tagname === 'body') pos['body'] = parser.startIndex;
|
},
|
||||||
},
|
});
|
||||||
});
|
parser.write(html);
|
||||||
parser.write(html);
|
parser.end();
|
||||||
parser.end();
|
|
||||||
|
|
||||||
// inject
|
// inject
|
||||||
const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]);
|
const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]);
|
||||||
lastToFirst.forEach(([name, i]) => {
|
lastToFirst.forEach(([name, i]) => {
|
||||||
if (i === -1) {
|
if (i === -1) {
|
||||||
// TODO: warn on missing tag? Is this an HTML partial?
|
// TODO: warn on missing tag? Is this an HTML partial?
|
||||||
return;
|
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) {
|
if (!selected.length) return;
|
||||||
// on invalid HTML, do nothing
|
output = output.substring(0, i) + serializeTags(selected) + html.substring(i);
|
||||||
}
|
});
|
||||||
|
|
||||||
return output;
|
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
|
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/html.ts
|
||||||
|
// -------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Vite is released under the MIT license:
|
// Vite is released under the MIT license:
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import type { AstroConfig, ComponentInstance, GetStaticPathsResult, Params, Prop
|
||||||
import type { AstroGlobal, TopLevelAstro, SSRResult, SSRElement } from '../../@types/astro-runtime';
|
import type { AstroGlobal, TopLevelAstro, SSRResult, SSRElement } from '../../@types/astro-runtime';
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { renderPage, renderSlot } from '../../runtime/server/index.js';
|
import { renderPage, renderSlot } from '../../runtime/server/index.js';
|
||||||
|
@ -164,7 +163,7 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
|
||||||
// inject tags
|
// inject tags
|
||||||
const tags: vite.HtmlTagDescriptor[] = [];
|
const tags: vite.HtmlTagDescriptor[] = [];
|
||||||
|
|
||||||
// inject Astro HMR client (dev only)
|
// dev only: inject Astro HMR client
|
||||||
if (mode === 'development') {
|
if (mode === 'development') {
|
||||||
tags.push({
|
tags.push({
|
||||||
tag: 'script',
|
tag: 'script',
|
||||||
|
|
|
@ -6,10 +6,17 @@ if (import.meta.hot) {
|
||||||
|
|
||||||
morphdom(document.head, doc.head, {
|
morphdom(document.head, doc.head, {
|
||||||
onBeforeElUpdated(fromEl, toEl) {
|
onBeforeElUpdated(fromEl, toEl) {
|
||||||
|
// Do not update identical tags
|
||||||
if (fromEl.isEqualNode(toEl)) {
|
if (fromEl.isEqualNode(toEl)) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
60
yarn.lock
60
yarn.lock
|
@ -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"
|
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||||
|
|
||||||
autoprefixer@^10.3.7:
|
autoprefixer@^10.4.0:
|
||||||
version "10.3.7"
|
version "10.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.7.tgz#cef2562058406bd378c94aacda36bb46a97b3186"
|
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.0.tgz#c3577eb32a1079a440ec253e404eaf1eb21388c8"
|
||||||
integrity sha512-EmGpu0nnQVmMhX8ROoJ7Mx8mKYPlcUHuxkwrRYEYMz85lu7H09v8w6R1P0JPdn/hKU32GjpLBFEOuIlDWCRWvg==
|
integrity sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist "^4.17.3"
|
browserslist "^4.17.5"
|
||||||
caniuse-lite "^1.0.30001264"
|
caniuse-lite "^1.0.30001272"
|
||||||
fraction.js "^4.1.1"
|
fraction.js "^4.1.1"
|
||||||
normalize-range "^0.1.2"
|
normalize-range "^0.1.2"
|
||||||
picocolors "^0.2.1"
|
picocolors "^1.0.0"
|
||||||
postcss-value-parser "^4.1.0"
|
postcss-value-parser "^4.1.0"
|
||||||
|
|
||||||
available-typed-arrays@^1.0.5:
|
available-typed-arrays@^1.0.5:
|
||||||
|
@ -2778,15 +2778,15 @@ browserslist@^4.16.6:
|
||||||
escalade "^3.1.1"
|
escalade "^3.1.1"
|
||||||
node-releases "^1.1.75"
|
node-releases "^1.1.75"
|
||||||
|
|
||||||
browserslist@^4.17.3:
|
browserslist@^4.17.5:
|
||||||
version "4.17.4"
|
version "4.17.5"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.5.tgz#c827bbe172a4c22b123f5e337533ceebadfdd559"
|
||||||
integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==
|
integrity sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite "^1.0.30001265"
|
caniuse-lite "^1.0.30001271"
|
||||||
electron-to-chromium "^1.3.867"
|
electron-to-chromium "^1.3.878"
|
||||||
escalade "^3.1.1"
|
escalade "^3.1.1"
|
||||||
node-releases "^2.0.0"
|
node-releases "^2.0.1"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
|
|
||||||
buffer-crc32@~0.2.3:
|
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"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz#b604eed80cc54a578e4bf5a02ae3ed49f869d252"
|
||||||
integrity sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==
|
integrity sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001265:
|
caniuse-lite@^1.0.30001271, caniuse-lite@^1.0.30001272:
|
||||||
version "1.0.30001267"
|
version "1.0.30001274"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz#b1cf2937175afc0570e4615fc2d2f9069fa0ed30"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz#26ca36204d15b17601ba6fc35dbdad950a647cc7"
|
||||||
integrity sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg==
|
integrity sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.843.tgz#671489bd2f59fd49b76adddc1aa02c88cd38a5c0"
|
||||||
integrity sha512-OWEwAbzaVd1Lk9MohVw8LxMXFlnYd9oYTYxfX8KS++kLLjDfbovLOcEEXwRhG612dqGQ6+44SZvim0GXuBRiKg==
|
integrity sha512-OWEwAbzaVd1Lk9MohVw8LxMXFlnYd9oYTYxfX8KS++kLLjDfbovLOcEEXwRhG612dqGQ6+44SZvim0GXuBRiKg==
|
||||||
|
|
||||||
electron-to-chromium@^1.3.867:
|
electron-to-chromium@^1.3.878:
|
||||||
version "1.3.871"
|
version "1.3.886"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.871.tgz#6e87365fd72037a6c898fb46050ad4be3ac9ef62"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz#ac039c4001b665b1dd0f0ed9c2e4da90ff3c9267"
|
||||||
integrity sha512-qcLvDUPf8DSIMWarHT2ptgcqrYg62n3vPA7vhrOF24d8UNzbUBaHu2CySiENR3nEDzYgaN60071t0F6KLYMQ7Q==
|
integrity sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ==
|
||||||
|
|
||||||
emmet@^2.1.5:
|
emmet@^2.1.5:
|
||||||
version "2.3.4"
|
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"
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe"
|
||||||
integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==
|
integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==
|
||||||
|
|
||||||
node-releases@^2.0.0:
|
node-releases@^2.0.1:
|
||||||
version "2.0.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.0.tgz#67dc74903100a7deb044037b8a2e5f453bb05400"
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
|
||||||
integrity sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==
|
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
|
||||||
|
|
||||||
node.extend@~2.0.2:
|
node.extend@~2.0.2:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.44.0.tgz#e6176cb3ad93846ddb4140e93f43098136b23f3b"
|
||||||
integrity sha512-zWACSJBSncGiDvFfYOMFGNV5zDLOlyhftmO5yOZ0lEtQMptpElaRtl39MWz1+lYCpwUq4F3Q2lTzI9TrTL+eMA==
|
integrity sha512-zWACSJBSncGiDvFfYOMFGNV5zDLOlyhftmO5yOZ0lEtQMptpElaRtl39MWz1+lYCpwUq4F3Q2lTzI9TrTL+eMA==
|
||||||
|
|
||||||
tailwindcss@^2.2.17:
|
tailwindcss@^2.2.19:
|
||||||
version "2.2.17"
|
version "2.2.19"
|
||||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.17.tgz#c6332731f9ff1b6628ff589c95c38685347775e3"
|
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c"
|
||||||
integrity sha512-WgRpn+Pxn7eWqlruxnxEbL9ByVRWi3iC10z4b6dW0zSdnkPVC4hPMSWLQkkW8GCyBIv/vbJ0bxIi9dVrl4CfoA==
|
integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==
|
||||||
dependencies:
|
dependencies:
|
||||||
arg "^5.0.1"
|
arg "^5.0.1"
|
||||||
bytes "^3.0.0"
|
bytes "^3.0.0"
|
||||||
|
|
Loading…
Reference in a new issue