enhance: Vite HMR while yarn dev, and more build tuning (#9361)
* enhance: Vite HMR while yarn dev, and more build tuning * use localhost Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		
							parent
							
								
									b4b9d5d552
								
							
						
					
					
						commit
						2fe86fd869
					
				
					 15 changed files with 102 additions and 23 deletions
				
			
		|  | @ -99,9 +99,17 @@ If your language is not listed in Crowdin, please open an issue. | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| ## Development | ## Development | ||||||
| During development, it is useful to use the `yarn dev` command. | During development, it is useful to use the  | ||||||
| This command monitors the server-side and client-side source files and automatically builds them if they are modified. | 
 | ||||||
| In addition, it will also automatically start the Misskey server process. | ``` | ||||||
|  | yarn dev | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | command. | ||||||
|  | 
 | ||||||
|  | - Server-side source files and automatically builds them if they are modified. Automatically start the server process(es). | ||||||
|  | - Vite HMR (just the `vite` command) is available. The behavior may be different from production. | ||||||
|  | - Service Worker is watched by esbuild. | ||||||
| 
 | 
 | ||||||
| ## Testing | ## Testing | ||||||
| - Test codes are located in [`/test`](/test). | - Test codes are located in [`/test`](/test). | ||||||
|  |  | ||||||
|  | @ -14,7 +14,8 @@ | ||||||
| 	], | 	], | ||||||
| 	"private": true, | 	"private": true, | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
| 		"build": "yarn workspaces foreach run build && yarn run gulp", | 		"build-pre": "node ./scripts/build-pre.js", | ||||||
|  | 		"build": "yarn build-pre && yarn workspaces foreach run build && yarn run gulp", | ||||||
| 		"start": "cd packages/backend && node ./built/boot/index.js", | 		"start": "cd packages/backend && node ./built/boot/index.js", | ||||||
| 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", | 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", | ||||||
| 		"init": "yarn migrate", | 		"init": "yarn migrate", | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ | ||||||
| 		"@fastify/accepts": "4.1.0", | 		"@fastify/accepts": "4.1.0", | ||||||
| 		"@fastify/cookie": "^8.3.0", | 		"@fastify/cookie": "^8.3.0", | ||||||
| 		"@fastify/cors": "8.2.0", | 		"@fastify/cors": "8.2.0", | ||||||
|  | 		"@fastify/http-proxy": "^8.4.0", | ||||||
| 		"@fastify/multipart": "7.3.0", | 		"@fastify/multipart": "7.3.0", | ||||||
| 		"@fastify/static": "6.6.0", | 		"@fastify/static": "6.6.0", | ||||||
| 		"@fastify/view": "7.3.0", | 		"@fastify/view": "7.3.0", | ||||||
|  |  | ||||||
|  | @ -91,6 +91,7 @@ export type Mixin = { | ||||||
| 	driveUrl: string; | 	driveUrl: string; | ||||||
| 	userAgent: string; | 	userAgent: string; | ||||||
| 	clientEntry: string; | 	clientEntry: string; | ||||||
|  | 	clientManifestExists: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type Config = Source & Mixin; | export type Config = Source & Mixin; | ||||||
|  | @ -112,7 +113,10 @@ const path = process.env.NODE_ENV === 'test' | ||||||
| 
 | 
 | ||||||
| export function loadConfig() { | export function loadConfig() { | ||||||
| 	const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); | 	const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); | ||||||
| 	const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_client_dist_/manifest.json`, 'utf-8')); | 	const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json') | ||||||
|  | 	const clientManifest = clientManifestExists ? | ||||||
|  | 		JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8')) | ||||||
|  | 		: { 'src/init.ts': { file: 'src/init.ts' } }; | ||||||
| 	const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; | 	const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; | ||||||
| 
 | 
 | ||||||
| 	const mixin = {} as Mixin; | 	const mixin = {} as Mixin; | ||||||
|  | @ -134,6 +138,7 @@ export function loadConfig() { | ||||||
| 	mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`; | 	mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`; | ||||||
| 	mixin.userAgent = `Misskey/${meta.version} (${config.url})`; | 	mixin.userAgent = `Misskey/${meta.version} (${config.url})`; | ||||||
| 	mixin.clientEntry = clientManifest['src/init.ts']; | 	mixin.clientEntry = clientManifest['src/init.ts']; | ||||||
|  | 	mixin.clientManifestExists = clientManifestExists; | ||||||
| 
 | 
 | ||||||
| 	if (!config.redis.prefix) config.redis.prefix = mixin.host; | 	if (!config.redis.prefix) config.redis.prefix = mixin.host; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import { In, IsNull } from 'typeorm'; | ||||||
| import fastifyStatic from '@fastify/static'; | import fastifyStatic from '@fastify/static'; | ||||||
| import fastifyView from '@fastify/view'; | import fastifyView from '@fastify/view'; | ||||||
| import fastifyCookie from '@fastify/cookie'; | import fastifyCookie from '@fastify/cookie'; | ||||||
|  | import fastifyProxy from '@fastify/http-proxy'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import { getNoteSummary } from '@/misc/get-note-summary.js'; | import { getNoteSummary } from '@/misc/get-note-summary.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | @ -39,6 +40,7 @@ const staticAssets = `${_dirname}/../../../assets/`; | ||||||
| const clientAssets = `${_dirname}/../../../../client/assets/`; | const clientAssets = `${_dirname}/../../../../client/assets/`; | ||||||
| const assets = `${_dirname}/../../../../../built/_client_dist_/`; | const assets = `${_dirname}/../../../../../built/_client_dist_/`; | ||||||
| const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; | const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; | ||||||
|  | const viteOut = `${_dirname}/../../../../../built/_vite_/`; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ClientServerService { | export class ClientServerService { | ||||||
|  | @ -151,9 +153,6 @@ export class ClientServerService { | ||||||
| 			}, | 			}, | ||||||
| 			defaultContext: { | 			defaultContext: { | ||||||
| 				version: this.config.version, | 				version: this.config.version, | ||||||
| 				getClientEntry: () => process.env.NODE_ENV === 'production' ? |  | ||||||
| 					this.config.clientEntry : |  | ||||||
| 					JSON.parse(readFileSync(`${_dirname}/../../../../../built/_client_dist_/manifest.json`, 'utf-8'))['src/init.ts'], |  | ||||||
| 				config: this.config, | 				config: this.config, | ||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
|  | @ -164,6 +163,23 @@ export class ClientServerService { | ||||||
| 			done(); | 			done(); | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
|  | 		//#region vite assets
 | ||||||
|  | 		if (this.config.clientManifestExists) { | ||||||
|  | 			fastify.register(fastifyStatic, { | ||||||
|  | 				root: viteOut, | ||||||
|  | 				prefix: '/vite/', | ||||||
|  | 				maxAge: ms('30 days'), | ||||||
|  | 				decorateReply: false, | ||||||
|  | 			}); | ||||||
|  | 		} else { | ||||||
|  | 			fastify.register(fastifyProxy, { | ||||||
|  | 				upstream: 'http://localhost:5173', // TODO: port configuration
 | ||||||
|  | 				prefix: '/vite', | ||||||
|  | 				rewritePrefix: '/vite', | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
|  | 		//#endregion
 | ||||||
|  | 
 | ||||||
| 		//#region static assets
 | 		//#region static assets
 | ||||||
| 
 | 
 | ||||||
| 		fastify.register(fastifyStatic, { | 		fastify.register(fastifyStatic, { | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ | ||||||
| 
 | 
 | ||||||
| 	//#region Script
 | 	//#region Script
 | ||||||
| 	function importAppScript() { | 	function importAppScript() { | ||||||
| 		import(`/assets/${CLIENT_ENTRY}`) | 		import(`/vite/${CLIENT_ENTRY}`) | ||||||
| 			.catch(async e => { | 			.catch(async e => { | ||||||
| 				await checkUpdate(); | 				await checkUpdate(); | ||||||
| 				console.error(e); | 				console.error(e); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| block vars | block vars | ||||||
| 
 | 
 | ||||||
| block loadClientEntry | block loadClientEntry | ||||||
| 	- const clientEntry = getClientEntry(); | 	- const clientEntry = config.clientEntry; | ||||||
| 
 | 
 | ||||||
| doctype html | doctype html | ||||||
| 
 | 
 | ||||||
|  | @ -35,11 +35,14 @@ html | ||||||
| 		link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') | 		link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') | ||||||
| 		link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') | 		link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') | ||||||
| 		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.css') | 		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.css') | ||||||
| 		link(rel='modulepreload' href=`/assets/${clientEntry.file}`) | 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`) | ||||||
|  | 
 | ||||||
|  | 		if !config.clientManifestExists | ||||||
|  | 				script(type="module" src="/vite/@vite/client") | ||||||
| 
 | 
 | ||||||
| 		if Array.isArray(clientEntry.css) | 		if Array.isArray(clientEntry.css) | ||||||
| 			each href in clientEntry.css | 			each href in clientEntry.css | ||||||
| 				link(rel='stylesheet' href=`/assets/${href}`) | 				link(rel='stylesheet' href=`/vite/${href}`) | ||||||
| 
 | 
 | ||||||
| 		title | 		title | ||||||
| 			block title | 			block title | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 	"name": "client", | 	"name": "client", | ||||||
| 	"private": true, | 	"private": true, | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
| 		"watch": "vite build --watch --mode development", | 		"watch": "vite", | ||||||
| 		"build": "vite build", | 		"build": "vite build", | ||||||
| 		"lint": "vue-tsc --noEmit && eslint --quiet \"src/**/*.{ts,vue}\"" | 		"lint": "vue-tsc --noEmit && eslint --quiet \"src/**/*.{ts,vue}\"" | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -34,7 +34,6 @@ | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { computed } from 'vue'; | import { computed } from 'vue'; | ||||||
| import * as Acct from 'misskey-js/built/acct'; | import * as Acct from 'misskey-js/built/acct'; | ||||||
| import MkSwitch from '@/components/ui/switch.vue'; |  | ||||||
| import MkPagination from '@/components/MkPagination.vue'; | import MkPagination from '@/components/MkPagination.vue'; | ||||||
| import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; | import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; | ||||||
| import bytes from '@/filters/bytes'; | import bytes from '@/filters/bytes'; | ||||||
|  |  | ||||||
|  | @ -159,8 +159,6 @@ import { | ||||||
| } from 'chart.js'; | } from 'chart.js'; | ||||||
| import { enUS } from 'date-fns/locale'; | import { enUS } from 'date-fns/locale'; | ||||||
| import tinycolor from 'tinycolor2'; | import tinycolor from 'tinycolor2'; | ||||||
| import MagicGrid from 'magic-grid'; |  | ||||||
| import XMetrics from './metrics.vue'; |  | ||||||
| import XFederation from './overview.federation.vue'; | import XFederation from './overview.federation.vue'; | ||||||
| import XQueueChart from './overview.queue-chart.vue'; | import XQueueChart from './overview.queue-chart.vue'; | ||||||
| import XUser from './overview.user.vue'; | import XUser from './overview.user.vue'; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import * as fs from 'fs'; |  | ||||||
| import pluginVue from '@vitejs/plugin-vue'; | import pluginVue from '@vitejs/plugin-vue'; | ||||||
| import { defineConfig } from 'vite'; | import { defineConfig } from 'vite'; | ||||||
| 
 | 
 | ||||||
|  | @ -9,11 +8,9 @@ import pluginJson5 from './vite.json5'; | ||||||
| const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; | const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; | ||||||
| 
 | 
 | ||||||
| export default defineConfig(({ command, mode }) => { | export default defineConfig(({ command, mode }) => { | ||||||
| 	fs.mkdirSync(__dirname + '/../../built', { recursive: true }); |  | ||||||
| 	fs.writeFileSync(__dirname + '/../../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8'); |  | ||||||
| 
 | 
 | ||||||
| 	return { | 	return { | ||||||
| 		base: '/assets/', | 		base: '/vite/', | ||||||
| 
 | 
 | ||||||
| 		plugins: [ | 		plugins: [ | ||||||
| 			pluginVue({ | 			pluginVue({ | ||||||
|  | @ -63,10 +60,10 @@ export default defineConfig(({ command, mode }) => { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			cssCodeSplit: true, | 			cssCodeSplit: true, | ||||||
| 			outDir: __dirname + '/../../built/_client_dist_', | 			outDir: __dirname + '/../../built/_vite_', | ||||||
| 			assetsDir: '.', | 			assetsDir: '.', | ||||||
| 			emptyOutDir: false, | 			emptyOutDir: false, | ||||||
| 			sourcemap: process.env.NODE_ENV !== 'production', | 			sourcemap: process.env.NODE_ENV === 'development', | ||||||
| 			reportCompressedSize: false, | 			reportCompressedSize: false, | ||||||
| 		}, | 		}, | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								scripts/build-pre.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								scripts/build-pre.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | const fs = require('fs'); | ||||||
|  | const meta = require('../package.json'); | ||||||
|  | 
 | ||||||
|  | fs.mkdirSync(__dirname + '/../built', { recursive: true }); | ||||||
|  | fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8'); | ||||||
|  | @ -12,4 +12,5 @@ const fs = require('fs'); | ||||||
| 
 | 
 | ||||||
| 	fs.rmSync(__dirname + '/../built', { recursive: true, force: true }); | 	fs.rmSync(__dirname + '/../built', { recursive: true, force: true }); | ||||||
| 	fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true }); | 	fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true }); | ||||||
|  | 	fs.rmSync(__dirname + '/../.yarn/cache', { recursive: true, force: true }); | ||||||
| })(); | })(); | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| const execa = require('execa'); | const execa = require('execa'); | ||||||
|  | const fs = require('fs'); | ||||||
| 
 | 
 | ||||||
| (async () => { | (async () => { | ||||||
| 	await execa('yarn', ['clean'], { | 	await execa('yarn', ['clean'], { | ||||||
|  | @ -7,6 +8,12 @@ const execa = require('execa'); | ||||||
| 		stderr: process.stderr, | 		stderr: process.stderr, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | 	await execa('yarn', ['build-pre'], { | ||||||
|  | 		cwd: __dirname + '/../', | ||||||
|  | 		stdout: process.stdout, | ||||||
|  | 		stderr: process.stderr, | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	execa('yarn', ['dlx', 'gulp', 'watch'], { | 	execa('yarn', ['dlx', 'gulp', 'watch'], { | ||||||
| 		cwd: __dirname + '/../', | 		cwd: __dirname + '/../', | ||||||
| 		stdout: process.stdout, | 		stdout: process.stdout, | ||||||
|  | @ -33,6 +40,9 @@ const execa = require('execa'); | ||||||
| 
 | 
 | ||||||
| 	const start = async () => { | 	const start = async () => { | ||||||
| 		try { | 		try { | ||||||
|  | 			const exist = fs.existsSync(__dirname + '/../packages/backend/built/boot/index.js') | ||||||
|  | 			if (!exist) throw new Error('not exist yet'); | ||||||
|  | 
 | ||||||
| 			await execa('yarn', ['start'], { | 			await execa('yarn', ['start'], { | ||||||
| 				cwd: __dirname + '/../', | 				cwd: __dirname + '/../', | ||||||
| 				stdout: process.stdout, | 				stdout: process.stdout, | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								yarn.lock
									
										
									
									
									
								
							
							
						
						
									
										37
									
								
								yarn.lock
									
										
									
									
									
								
							|  | @ -917,6 +917,16 @@ __metadata: | ||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
|  | "@fastify/http-proxy@npm:^8.4.0": | ||||||
|  |   version: 8.4.0 | ||||||
|  |   resolution: "@fastify/http-proxy@npm:8.4.0" | ||||||
|  |   dependencies: | ||||||
|  |     "@fastify/reply-from": ^8.0.0 | ||||||
|  |     ws: ^8.4.2 | ||||||
|  |   checksum: 4bc4f0acac667c0c2f152e78342d8c7aeb4880d461227971ce31c85fe8d532fba616d2ef5224d6542f598a8a388d61954ed002003e2ce0695c15141e94a1a06b | ||||||
|  |   languageName: node | ||||||
|  |   linkType: hard | ||||||
|  | 
 | ||||||
| "@fastify/multipart@npm:7.3.0": | "@fastify/multipart@npm:7.3.0": | ||||||
|   version: 7.3.0 |   version: 7.3.0 | ||||||
|   resolution: "@fastify/multipart@npm:7.3.0" |   resolution: "@fastify/multipart@npm:7.3.0" | ||||||
|  | @ -933,6 +943,21 @@ __metadata: | ||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
|  | "@fastify/reply-from@npm:^8.0.0": | ||||||
|  |   version: 8.3.1 | ||||||
|  |   resolution: "@fastify/reply-from@npm:8.3.1" | ||||||
|  |   dependencies: | ||||||
|  |     "@fastify/error": ^3.0.0 | ||||||
|  |     end-of-stream: ^1.4.4 | ||||||
|  |     fast-querystring: ^1.0.0 | ||||||
|  |     fastify-plugin: ^4.0.0 | ||||||
|  |     pump: ^3.0.0 | ||||||
|  |     tiny-lru: ^10.0.0 | ||||||
|  |     undici: ^5.5.1 | ||||||
|  |   checksum: debfc85b69946ecbad21dc2b01b2740b5a562258b5e3f00c452a88691525db650499cdf3bf09d85ae3f20455372925d6d7203265d0f00fa873f380be6e16e4d7 | ||||||
|  |   languageName: node | ||||||
|  |   linkType: hard | ||||||
|  | 
 | ||||||
| "@fastify/static@npm:6.6.0": | "@fastify/static@npm:6.6.0": | ||||||
|   version: 6.6.0 |   version: 6.6.0 | ||||||
|   resolution: "@fastify/static@npm:6.6.0" |   resolution: "@fastify/static@npm:6.6.0" | ||||||
|  | @ -4100,6 +4125,7 @@ __metadata: | ||||||
|     "@fastify/accepts": 4.1.0 |     "@fastify/accepts": 4.1.0 | ||||||
|     "@fastify/cookie": ^8.3.0 |     "@fastify/cookie": ^8.3.0 | ||||||
|     "@fastify/cors": 8.2.0 |     "@fastify/cors": 8.2.0 | ||||||
|  |     "@fastify/http-proxy": ^8.4.0 | ||||||
|     "@fastify/multipart": 7.3.0 |     "@fastify/multipart": 7.3.0 | ||||||
|     "@fastify/static": 6.6.0 |     "@fastify/static": 6.6.0 | ||||||
|     "@fastify/view": 7.3.0 |     "@fastify/view": 7.3.0 | ||||||
|  | @ -16642,6 +16668,15 @@ __metadata: | ||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
|  | "undici@npm:^5.5.1": | ||||||
|  |   version: 5.14.0 | ||||||
|  |   resolution: "undici@npm:5.14.0" | ||||||
|  |   dependencies: | ||||||
|  |     busboy: ^1.6.0 | ||||||
|  |   checksum: 7a076e44d84b25844b4eb657034437b8b9bb91f17d347de474fdea1d4263ce7ae9406db79cd30de5642519277b4893f43073258bcc8fed420b295da3fdd11b26 | ||||||
|  |   languageName: node | ||||||
|  |   linkType: hard | ||||||
|  | 
 | ||||||
| "union-value@npm:^1.0.0": | "union-value@npm:^1.0.0": | ||||||
|   version: 1.0.1 |   version: 1.0.1 | ||||||
|   resolution: "union-value@npm:1.0.1" |   resolution: "union-value@npm:1.0.1" | ||||||
|  | @ -17419,7 +17454,7 @@ __metadata: | ||||||
|   languageName: node |   languageName: node | ||||||
|   linkType: hard |   linkType: hard | ||||||
| 
 | 
 | ||||||
| "ws@npm:8.11.0, ws@npm:^8.11.0": | "ws@npm:8.11.0, ws@npm:^8.11.0, ws@npm:^8.4.2": | ||||||
|   version: 8.11.0 |   version: 8.11.0 | ||||||
|   resolution: "ws@npm:8.11.0" |   resolution: "ws@npm:8.11.0" | ||||||
|   peerDependencies: |   peerDependencies: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue