@tanstack/devtools-vite 0.3.4 → 0.3.6

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.
@@ -9,7 +9,13 @@ export type TanStackDevtoolsViteConfig = {
9
9
  /**
10
10
  * The configuration options for the server event bus
11
11
  */
12
- eventBusConfig?: ServerEventBusConfig;
12
+ eventBusConfig?: ServerEventBusConfig & {
13
+ /**
14
+ * Should the server event bus be enabled or not
15
+ * @default true
16
+ */
17
+ enabled?: boolean;
18
+ };
13
19
  /**
14
20
  * Configuration for enhanced logging.
15
21
  */
@@ -1,18 +1,37 @@
1
+ import { exec } from "node:child_process";
2
+ import { devtoolsEventClient } from "@tanstack/devtools-client";
1
3
  import { ServerEventBus } from "@tanstack/devtools-event-bus/server";
2
4
  import { normalizePath } from "vite";
3
5
  import chalk from "chalk";
4
- import { handleDevToolsViteRequest } from "./utils.js";
6
+ import { handleDevToolsViteRequest, readPackageJson, tryParseJson } from "./utils.js";
5
7
  import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from "./editor.js";
6
8
  import { removeDevtools } from "./remove-devtools.js";
7
9
  import { addSourceToJsx } from "./inject-source.js";
8
10
  import { enhanceConsoleLog } from "./enhance-logs.js";
9
11
  const defineDevtoolsConfig = (config) => config;
12
+ const emitOutdatedDeps = async () => {
13
+ return await new Promise((resolve) => {
14
+ exec("npm outdated --json", (_, stdout) => {
15
+ if (stdout) {
16
+ const newOutdatedDeps = tryParseJson(stdout);
17
+ if (!newOutdatedDeps) {
18
+ return;
19
+ }
20
+ devtoolsEventClient.emit("outdated-deps-read", {
21
+ outdatedDeps: newOutdatedDeps
22
+ });
23
+ resolve(newOutdatedDeps);
24
+ }
25
+ });
26
+ });
27
+ };
10
28
  const devtools = (args) => {
11
29
  let port = 5173;
12
30
  const logging = args?.logging ?? true;
13
31
  const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true };
14
32
  const injectSourceConfig = args?.injectSource ?? { enabled: true };
15
33
  const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true;
34
+ const serverBusEnabled = args?.eventBusConfig?.enabled ?? true;
16
35
  const bus = new ServerEventBus(args?.eventBusConfig);
17
36
  return [
18
37
  {
@@ -34,21 +53,6 @@ const devtools = (args) => {
34
53
  if (command !== "serve") {
35
54
  return;
36
55
  }
37
- const solidDedupeDeps = [
38
- "solid-js",
39
- "solid-js/web",
40
- "solid-js/store",
41
- "solid-js/html",
42
- "solid-js/h"
43
- ];
44
- return {
45
- resolve: {
46
- dedupe: solidDedupeDeps
47
- },
48
- optimizeDeps: {
49
- include: solidDedupeDeps
50
- }
51
- };
52
56
  }
53
57
  },
54
58
  {
@@ -58,7 +62,9 @@ const devtools = (args) => {
58
62
  return config.mode === "development";
59
63
  },
60
64
  configureServer(server) {
61
- bus.start();
65
+ if (serverBusEnabled) {
66
+ bus.start();
67
+ }
62
68
  server.middlewares.use((req, _res, next) => {
63
69
  if (req.socket.localPort && req.socket.localPort !== port) {
64
70
  port = req.socket.localPort;
@@ -113,6 +119,35 @@ ${chalk.greenBright(`[@tanstack/devtools-vite]`)} Removed devtools code from: ${
113
119
  return transform;
114
120
  }
115
121
  },
122
+ {
123
+ name: "@tanstack/devtools:event-client-setup",
124
+ apply(config, { command }) {
125
+ if (process.env.CI || process.env.NODE_ENV !== "development" || command !== "serve")
126
+ return false;
127
+ return config.mode === "development";
128
+ },
129
+ async configureServer() {
130
+ const packageJson = await readPackageJson();
131
+ const outdatedDeps = emitOutdatedDeps().then((deps) => deps);
132
+ devtoolsEventClient.on("mounted", async () => {
133
+ devtoolsEventClient.emit("outdated-deps-read", {
134
+ outdatedDeps: await outdatedDeps
135
+ });
136
+ devtoolsEventClient.emit("package-json-read", {
137
+ packageJson
138
+ });
139
+ });
140
+ },
141
+ async handleHotUpdate({ file }) {
142
+ if (file.endsWith("package.json")) {
143
+ const newPackageJson = await readPackageJson();
144
+ devtoolsEventClient.emit("package-json-read", {
145
+ packageJson: newPackageJson
146
+ });
147
+ emitOutdatedDeps();
148
+ }
149
+ }
150
+ },
116
151
  {
117
152
  name: "@tanstack/devtools:better-console-logs",
118
153
  enforce: "pre",
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { ServerEventBus } from '@tanstack/devtools-event-bus/server'\nimport { normalizePath } from 'vite'\nimport chalk from 'chalk'\nimport { handleDevToolsViteRequest } from './utils'\nimport { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'\nimport { removeDevtools } from './remove-devtools'\nimport { addSourceToJsx } from './inject-source'\nimport { enhanceConsoleLog } from './enhance-logs'\nimport type { Plugin } from 'vite'\nimport type { EditorConfig } from './editor'\nimport type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'\n\nexport type TanStackDevtoolsViteConfig = {\n /**\n * Configuration for the editor integration. Defaults to opening in VS code\n */\n editor?: EditorConfig\n /**\n * The configuration options for the server event bus\n */\n eventBusConfig?: ServerEventBusConfig\n /**\n * Configuration for enhanced logging.\n */\n enhancedLogs?: {\n /**\n * Whether to enable enhanced logging.\n * @default true\n */\n enabled: boolean\n }\n /**\n * Whether to remove devtools from the production build.\n * @default true\n */\n removeDevtoolsOnBuild?: boolean\n\n /**\n * Whether to log information to the console.\n * @default true\n */\n logging?: boolean\n /**\n * Configuration for source injection.\n */\n injectSource?: {\n /**\n * Whether to enable source injection via data-tsd-source.\n * @default true\n */\n enabled: boolean\n }\n}\n\nexport const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>\n config\n\nexport const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {\n let port = 5173\n const logging = args?.logging ?? true\n const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }\n const injectSourceConfig = args?.injectSource ?? { enabled: true }\n const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true\n const bus = new ServerEventBus(args?.eventBusConfig)\n\n return [\n {\n enforce: 'pre',\n name: '@tanstack/devtools:inject-source',\n apply(config) {\n return config.mode === 'development' && injectSourceConfig.enabled\n },\n transform(code, id) {\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return\n\n return addSourceToJsx(code, id)\n },\n },\n {\n name: '@tanstack/devtools:config',\n enforce: 'pre',\n config(_, { command }) {\n // we do not apply any config changes for build\n if (command !== 'serve') {\n return\n }\n\n const solidDedupeDeps = [\n 'solid-js',\n 'solid-js/web',\n 'solid-js/store',\n 'solid-js/html',\n 'solid-js/h',\n ]\n\n return {\n resolve: {\n dedupe: solidDedupeDeps,\n },\n optimizeDeps: {\n include: solidDedupeDeps,\n },\n }\n },\n },\n {\n enforce: 'pre',\n name: '@tanstack/devtools:custom-server',\n apply(config) {\n // Custom server is only needed in development for piping events to the client\n return config.mode === 'development'\n },\n configureServer(server) {\n bus.start()\n\n server.middlewares.use((req, _res, next) => {\n if (req.socket.localPort && req.socket.localPort !== port) {\n port = req.socket.localPort\n }\n next()\n })\n if (server.config.server.port) {\n port = server.config.server.port\n }\n\n server.httpServer?.on('listening', () => {\n port = server.config.server.port\n })\n\n const editor = args?.editor ?? DEFAULT_EDITOR_CONFIG\n const openInEditor: EditorConfig['open'] = async (\n path,\n lineNum,\n columnNum,\n ) => {\n if (!path) {\n return\n }\n await editor.open(path, lineNum, columnNum)\n }\n server.middlewares.use((req, res, next) =>\n handleDevToolsViteRequest(req, res, next, (parsedData) => {\n const { data, routine } = parsedData\n if (routine === 'open-source') {\n return handleOpenSource({\n data: { type: data.type, data },\n openInEditor,\n })\n }\n return\n }),\n )\n },\n },\n {\n name: '@tanstack/devtools:remove-devtools-on-build',\n apply(_, { command }) {\n return command === 'build' && removeDevtoolsOnBuild\n },\n enforce: 'pre',\n transform(code, id) {\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return\n const transform = removeDevtools(code, id)\n if (!transform) return\n if (logging) {\n console.log(\n `\\n${chalk.greenBright(`[@tanstack/devtools-vite]`)} Removed devtools code from: ${id.replace(normalizePath(process.cwd()), '')}\\n`,\n )\n }\n return transform\n },\n },\n {\n name: '@tanstack/devtools:better-console-logs',\n enforce: 'pre',\n apply(config) {\n return config.mode === 'development' && enhancedLogsConfig.enabled\n },\n transform(code, id) {\n // Ignore anything external\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build') ||\n !code.includes('console.')\n )\n return\n\n return enhanceConsoleLog(code, id, port)\n },\n },\n ]\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { exec } from 'node:child_process'\nimport { devtoolsEventClient } from '@tanstack/devtools-client'\nimport { ServerEventBus } from '@tanstack/devtools-event-bus/server'\nimport { normalizePath } from 'vite'\nimport chalk from 'chalk'\nimport {\n handleDevToolsViteRequest,\n readPackageJson,\n tryParseJson,\n} from './utils'\nimport { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'\nimport { removeDevtools } from './remove-devtools'\nimport { addSourceToJsx } from './inject-source'\nimport { enhanceConsoleLog } from './enhance-logs'\nimport type { OutdatedDeps } from '@tanstack/devtools-client'\nimport type { Plugin } from 'vite'\nimport type { EditorConfig } from './editor'\nimport type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'\n\nexport type TanStackDevtoolsViteConfig = {\n /**\n * Configuration for the editor integration. Defaults to opening in VS code\n */\n editor?: EditorConfig\n /**\n * The configuration options for the server event bus\n */\n eventBusConfig?: ServerEventBusConfig & {\n /**\n * Should the server event bus be enabled or not\n * @default true\n */\n enabled?: boolean // defaults to true\n }\n /**\n * Configuration for enhanced logging.\n */\n enhancedLogs?: {\n /**\n * Whether to enable enhanced logging.\n * @default true\n */\n enabled: boolean\n }\n /**\n * Whether to remove devtools from the production build.\n * @default true\n */\n removeDevtoolsOnBuild?: boolean\n\n /**\n * Whether to log information to the console.\n * @default true\n */\n logging?: boolean\n /**\n * Configuration for source injection.\n */\n injectSource?: {\n /**\n * Whether to enable source injection via data-tsd-source.\n * @default true\n */\n enabled: boolean\n }\n}\n\nexport const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>\n config\n\nconst emitOutdatedDeps = async () => {\n return await new Promise<OutdatedDeps | null>((resolve) => {\n exec('npm outdated --json', (_, stdout) => {\n // npm outdated exits with code 1 if there are outdated packages, but still outputs valid JSON\n if (stdout) {\n const newOutdatedDeps = tryParseJson<OutdatedDeps>(stdout)\n if (!newOutdatedDeps) {\n return\n }\n devtoolsEventClient.emit('outdated-deps-read', {\n outdatedDeps: newOutdatedDeps,\n })\n resolve(newOutdatedDeps)\n }\n })\n })\n}\n\nexport const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {\n let port = 5173\n const logging = args?.logging ?? true\n const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }\n const injectSourceConfig = args?.injectSource ?? { enabled: true }\n const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true\n const serverBusEnabled = args?.eventBusConfig?.enabled ?? true\n const bus = new ServerEventBus(args?.eventBusConfig)\n\n return [\n {\n enforce: 'pre',\n name: '@tanstack/devtools:inject-source',\n apply(config) {\n return config.mode === 'development' && injectSourceConfig.enabled\n },\n transform(code, id) {\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return\n\n return addSourceToJsx(code, id)\n },\n },\n {\n name: '@tanstack/devtools:config',\n enforce: 'pre',\n config(_, { command }) {\n // we do not apply any config changes for build\n if (command !== 'serve') {\n return\n }\n\n /* const solidDedupeDeps = [\n 'solid-js',\n 'solid-js/web',\n 'solid-js/store',\n 'solid-js/html',\n 'solid-js/h',\n ]\n\n return {\n resolve: {\n dedupe: solidDedupeDeps,\n },\n optimizeDeps: {\n include: solidDedupeDeps,\n },\n } */\n },\n },\n {\n enforce: 'pre',\n name: '@tanstack/devtools:custom-server',\n apply(config) {\n // Custom server is only needed in development for piping events to the client\n return config.mode === 'development'\n },\n configureServer(server) {\n if (serverBusEnabled) {\n bus.start()\n }\n\n server.middlewares.use((req, _res, next) => {\n if (req.socket.localPort && req.socket.localPort !== port) {\n port = req.socket.localPort\n }\n next()\n })\n if (server.config.server.port) {\n port = server.config.server.port\n }\n\n server.httpServer?.on('listening', () => {\n port = server.config.server.port\n })\n\n const editor = args?.editor ?? DEFAULT_EDITOR_CONFIG\n const openInEditor: EditorConfig['open'] = async (\n path,\n lineNum,\n columnNum,\n ) => {\n if (!path) {\n return\n }\n await editor.open(path, lineNum, columnNum)\n }\n server.middlewares.use((req, res, next) =>\n handleDevToolsViteRequest(req, res, next, (parsedData) => {\n const { data, routine } = parsedData\n if (routine === 'open-source') {\n return handleOpenSource({\n data: { type: data.type, data },\n openInEditor,\n })\n }\n return\n }),\n )\n },\n },\n {\n name: '@tanstack/devtools:remove-devtools-on-build',\n apply(_, { command }) {\n return command === 'build' && removeDevtoolsOnBuild\n },\n enforce: 'pre',\n transform(code, id) {\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return\n const transform = removeDevtools(code, id)\n if (!transform) return\n if (logging) {\n console.log(\n `\\n${chalk.greenBright(`[@tanstack/devtools-vite]`)} Removed devtools code from: ${id.replace(normalizePath(process.cwd()), '')}\\n`,\n )\n }\n return transform\n },\n },\n {\n name: '@tanstack/devtools:event-client-setup',\n apply(config, { command }) {\n if (\n process.env.CI ||\n process.env.NODE_ENV !== 'development' ||\n command !== 'serve'\n )\n return false\n return config.mode === 'development'\n },\n async configureServer() {\n const packageJson = await readPackageJson()\n const outdatedDeps = emitOutdatedDeps().then((deps) => deps)\n\n // whenever a client mounts we send all the current info to the subscribers\n devtoolsEventClient.on('mounted', async () => {\n devtoolsEventClient.emit('outdated-deps-read', {\n outdatedDeps: await outdatedDeps,\n })\n devtoolsEventClient.emit('package-json-read', {\n packageJson,\n })\n })\n },\n async handleHotUpdate({ file }) {\n if (file.endsWith('package.json')) {\n const newPackageJson = await readPackageJson()\n devtoolsEventClient.emit('package-json-read', {\n packageJson: newPackageJson,\n })\n emitOutdatedDeps()\n }\n },\n },\n {\n name: '@tanstack/devtools:better-console-logs',\n enforce: 'pre',\n apply(config) {\n return config.mode === 'development' && enhancedLogsConfig.enabled\n },\n transform(code, id) {\n // Ignore anything external\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build') ||\n !code.includes('console.')\n )\n return\n\n return enhanceConsoleLog(code, id, port)\n },\n },\n ]\n}\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import { Connect } from 'vite';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
+ import { PackageJson } from '@tanstack/devtools-client';
3
4
  export declare const handleDevToolsViteRequest: (req: Connect.IncomingMessage, res: ServerResponse<IncomingMessage>, next: Connect.NextFunction, cb: (data: any) => void) => void;
4
5
  export declare const parseOpenSourceParam: (source: string) => {
5
6
  file: string | undefined;
6
7
  line: string | undefined;
7
8
  column: string | undefined;
8
9
  } | null;
10
+ export declare const tryParseJson: <T extends any>(jsonString: string | null | undefined) => T | null;
11
+ export declare const readPackageJson: () => Promise<PackageJson | null>;
package/dist/esm/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import fs from "node:fs/promises";
1
2
  import { normalizePath } from "vite";
2
3
  const handleDevToolsViteRequest = (req, res, next, cb) => {
3
4
  if (req.url?.includes("__tsd/open-source")) {
@@ -48,8 +49,30 @@ const parseOpenSourceParam = (source) => {
48
49
  const [, file, line, column] = parts;
49
50
  return { file, line, column };
50
51
  };
52
+ const tryReadFile = async (filePath) => {
53
+ try {
54
+ const data = await fs.readFile(filePath, "utf-8");
55
+ return data;
56
+ } catch (error) {
57
+ return null;
58
+ }
59
+ };
60
+ const tryParseJson = (jsonString) => {
61
+ if (!jsonString) {
62
+ return null;
63
+ }
64
+ try {
65
+ const result = JSON.parse(jsonString);
66
+ return result;
67
+ } catch (error) {
68
+ return null;
69
+ }
70
+ };
71
+ const readPackageJson = async () => tryParseJson(await tryReadFile(process.cwd() + "/package.json"));
51
72
  export {
52
73
  handleDevToolsViteRequest,
53
- parseOpenSourceParam
74
+ parseOpenSourceParam,
75
+ readPackageJson,
76
+ tryParseJson
54
77
  };
55
78
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import { normalizePath } from 'vite'\n// import fs from 'node:fs/promises'\nimport type { Connect } from 'vite'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport const handleDevToolsViteRequest = (\n req: Connect.IncomingMessage,\n res: ServerResponse<IncomingMessage>,\n next: Connect.NextFunction,\n cb: (data: any) => void,\n) => {\n if (req.url?.includes('__tsd/open-source')) {\n const searchParams = new URLSearchParams(req.url.split('?')[1])\n\n const source = searchParams.get('source')\n if (!source) {\n return\n }\n\n const parsed = parseOpenSourceParam(source)\n if (!parsed) {\n return\n }\n const { file, line, column } = parsed\n\n cb({\n type: 'open-source',\n routine: 'open-source',\n data: {\n source: file ? normalizePath(`${process.cwd()}/${file}`) : undefined,\n line,\n column,\n },\n })\n res.setHeader('Content-Type', 'text/html')\n res.write(`<script> window.close(); </script>`)\n res.end()\n return\n }\n if (!req.url?.includes('__tsd')) {\n return next()\n }\n\n const chunks: Array<any> = []\n req.on('data', (chunk) => {\n chunks.push(chunk)\n })\n req.on('end', () => {\n const dataToParse = Buffer.concat(chunks)\n try {\n const parsedData = JSON.parse(dataToParse.toString())\n cb(parsedData)\n } catch (e) {}\n res.write('OK')\n })\n}\n\nexport const parseOpenSourceParam = (source: string) => {\n // Capture everything up to the last two colon-separated numeric parts as the file.\n // This supports filenames that may themselves contain colons.\n const parts = source.match(/^(.+):(\\d+):(\\d+)$/)\n\n if (!parts) return null\n\n const [, file, line, column] = parts\n return { file, line, column }\n}\n\n/* export const tryReadFile = async (\n filePath: string\n) => {\n try {\n const data = await fs.readFile(filePath, 'utf-8')\n return data\n } catch (error) {\n\n return null\n }\n}\n\nexport const tryParseJson = (jsonString: string) => {\n try {\n const result = JSON.parse(jsonString)\n return result\n } catch (error) {\n return null\n }\n} */\n"],"names":[],"mappings":";AAKO,MAAM,4BAA4B,CACvC,KACA,KACA,MACA,OACG;AACH,MAAI,IAAI,KAAK,SAAS,mBAAmB,GAAG;AAC1C,UAAM,eAAe,IAAI,gBAAgB,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC;AAE9D,UAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,UAAM,SAAS,qBAAqB,MAAM;AAC1C,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AACA,UAAM,EAAE,MAAM,MAAM,OAAA,IAAW;AAE/B,OAAG;AAAA,MACD,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,QAAQ,OAAO,cAAc,GAAG,QAAQ,KAAK,IAAI,IAAI,EAAE,IAAI;AAAA,QAC3D;AAAA,QACA;AAAA,MAAA;AAAA,IACF,CACD;AACD,QAAI,UAAU,gBAAgB,WAAW;AACzC,QAAI,MAAM,qCAAoC;AAC9C,QAAI,IAAA;AACJ;AAAA,EACF;AACA,MAAI,CAAC,IAAI,KAAK,SAAS,OAAO,GAAG;AAC/B,WAAO,KAAA;AAAA,EACT;AAEA,QAAM,SAAqB,CAAA;AAC3B,MAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,WAAO,KAAK,KAAK;AAAA,EACnB,CAAC;AACD,MAAI,GAAG,OAAO,MAAM;AAClB,UAAM,cAAc,OAAO,OAAO,MAAM;AACxC,QAAI;AACF,YAAM,aAAa,KAAK,MAAM,YAAY,UAAU;AACpD,SAAG,UAAU;AAAA,IACf,SAAS,GAAG;AAAA,IAAC;AACb,QAAI,MAAM,IAAI;AAAA,EAChB,CAAC;AACH;AAEO,MAAM,uBAAuB,CAAC,WAAmB;AAGtD,QAAM,QAAQ,OAAO,MAAM,oBAAoB;AAE/C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,GAAG,MAAM,MAAM,MAAM,IAAI;AAC/B,SAAO,EAAE,MAAM,MAAM,OAAA;AACvB;"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport { normalizePath } from 'vite'\nimport type { Connect } from 'vite'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { PackageJson } from '@tanstack/devtools-client'\n\nexport const handleDevToolsViteRequest = (\n req: Connect.IncomingMessage,\n res: ServerResponse<IncomingMessage>,\n next: Connect.NextFunction,\n cb: (data: any) => void,\n) => {\n if (req.url?.includes('__tsd/open-source')) {\n const searchParams = new URLSearchParams(req.url.split('?')[1])\n\n const source = searchParams.get('source')\n if (!source) {\n return\n }\n\n const parsed = parseOpenSourceParam(source)\n if (!parsed) {\n return\n }\n const { file, line, column } = parsed\n\n cb({\n type: 'open-source',\n routine: 'open-source',\n data: {\n source: file ? normalizePath(`${process.cwd()}/${file}`) : undefined,\n line,\n column,\n },\n })\n res.setHeader('Content-Type', 'text/html')\n res.write(`<script> window.close(); </script>`)\n res.end()\n return\n }\n if (!req.url?.includes('__tsd')) {\n return next()\n }\n\n const chunks: Array<any> = []\n req.on('data', (chunk) => {\n chunks.push(chunk)\n })\n req.on('end', () => {\n const dataToParse = Buffer.concat(chunks)\n try {\n const parsedData = JSON.parse(dataToParse.toString())\n cb(parsedData)\n } catch (e) {}\n res.write('OK')\n })\n}\n\nexport const parseOpenSourceParam = (source: string) => {\n // Capture everything up to the last two colon-separated numeric parts as the file.\n // This supports filenames that may themselves contain colons.\n const parts = source.match(/^(.+):(\\d+):(\\d+)$/)\n\n if (!parts) return null\n\n const [, file, line, column] = parts\n return { file, line, column }\n}\n\nconst tryReadFile = async (filePath: string) => {\n try {\n const data = await fs.readFile(filePath, 'utf-8')\n return data\n } catch (error) {\n return null\n }\n}\n\nexport const tryParseJson = <T extends any>(\n jsonString: string | null | undefined,\n) => {\n if (!jsonString) {\n return null\n }\n try {\n const result = JSON.parse(jsonString)\n return result as T\n } catch (error) {\n return null\n }\n}\n\nexport const readPackageJson = async () =>\n tryParseJson<PackageJson>(await tryReadFile(process.cwd() + '/package.json'))\n"],"names":[],"mappings":";;AAMO,MAAM,4BAA4B,CACvC,KACA,KACA,MACA,OACG;AACH,MAAI,IAAI,KAAK,SAAS,mBAAmB,GAAG;AAC1C,UAAM,eAAe,IAAI,gBAAgB,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC;AAE9D,UAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,UAAM,SAAS,qBAAqB,MAAM;AAC1C,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AACA,UAAM,EAAE,MAAM,MAAM,OAAA,IAAW;AAE/B,OAAG;AAAA,MACD,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,QAAQ,OAAO,cAAc,GAAG,QAAQ,KAAK,IAAI,IAAI,EAAE,IAAI;AAAA,QAC3D;AAAA,QACA;AAAA,MAAA;AAAA,IACF,CACD;AACD,QAAI,UAAU,gBAAgB,WAAW;AACzC,QAAI,MAAM,qCAAoC;AAC9C,QAAI,IAAA;AACJ;AAAA,EACF;AACA,MAAI,CAAC,IAAI,KAAK,SAAS,OAAO,GAAG;AAC/B,WAAO,KAAA;AAAA,EACT;AAEA,QAAM,SAAqB,CAAA;AAC3B,MAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,WAAO,KAAK,KAAK;AAAA,EACnB,CAAC;AACD,MAAI,GAAG,OAAO,MAAM;AAClB,UAAM,cAAc,OAAO,OAAO,MAAM;AACxC,QAAI;AACF,YAAM,aAAa,KAAK,MAAM,YAAY,UAAU;AACpD,SAAG,UAAU;AAAA,IACf,SAAS,GAAG;AAAA,IAAC;AACb,QAAI,MAAM,IAAI;AAAA,EAChB,CAAC;AACH;AAEO,MAAM,uBAAuB,CAAC,WAAmB;AAGtD,QAAM,QAAQ,OAAO,MAAM,oBAAoB;AAE/C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,GAAG,MAAM,MAAM,MAAM,IAAI;AAC/B,SAAO,EAAE,MAAM,MAAM,OAAA;AACvB;AAEA,MAAM,cAAc,OAAO,aAAqB;AAC9C,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,SAAS,UAAU,OAAO;AAChD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAEO,MAAM,eAAe,CAC1B,eACG;AACH,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAEO,MAAM,kBAAkB,YAC7B,aAA0B,MAAM,YAAY,QAAQ,IAAA,IAAQ,eAAe,CAAC;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/devtools-vite",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "TanStack Vite plugin used to enhance the core devtools with additional functionalities",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -48,6 +48,7 @@
48
48
  "@babel/types": "^7.28.4",
49
49
  "chalk": "^5.6.2",
50
50
  "launch-editor": "^2.11.1",
51
+ "@tanstack/devtools-client": "0.0.2",
51
52
  "@tanstack/devtools-event-bus": "0.3.2"
52
53
  },
53
54
  "devDependencies": {
package/src/plugin.ts CHANGED
@@ -1,11 +1,18 @@
1
+ import { exec } from 'node:child_process'
2
+ import { devtoolsEventClient } from '@tanstack/devtools-client'
1
3
  import { ServerEventBus } from '@tanstack/devtools-event-bus/server'
2
4
  import { normalizePath } from 'vite'
3
5
  import chalk from 'chalk'
4
- import { handleDevToolsViteRequest } from './utils'
6
+ import {
7
+ handleDevToolsViteRequest,
8
+ readPackageJson,
9
+ tryParseJson,
10
+ } from './utils'
5
11
  import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'
6
12
  import { removeDevtools } from './remove-devtools'
7
13
  import { addSourceToJsx } from './inject-source'
8
14
  import { enhanceConsoleLog } from './enhance-logs'
15
+ import type { OutdatedDeps } from '@tanstack/devtools-client'
9
16
  import type { Plugin } from 'vite'
10
17
  import type { EditorConfig } from './editor'
11
18
  import type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'
@@ -18,7 +25,13 @@ export type TanStackDevtoolsViteConfig = {
18
25
  /**
19
26
  * The configuration options for the server event bus
20
27
  */
21
- eventBusConfig?: ServerEventBusConfig
28
+ eventBusConfig?: ServerEventBusConfig & {
29
+ /**
30
+ * Should the server event bus be enabled or not
31
+ * @default true
32
+ */
33
+ enabled?: boolean // defaults to true
34
+ }
22
35
  /**
23
36
  * Configuration for enhanced logging.
24
37
  */
@@ -55,12 +68,31 @@ export type TanStackDevtoolsViteConfig = {
55
68
  export const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>
56
69
  config
57
70
 
71
+ const emitOutdatedDeps = async () => {
72
+ return await new Promise<OutdatedDeps | null>((resolve) => {
73
+ exec('npm outdated --json', (_, stdout) => {
74
+ // npm outdated exits with code 1 if there are outdated packages, but still outputs valid JSON
75
+ if (stdout) {
76
+ const newOutdatedDeps = tryParseJson<OutdatedDeps>(stdout)
77
+ if (!newOutdatedDeps) {
78
+ return
79
+ }
80
+ devtoolsEventClient.emit('outdated-deps-read', {
81
+ outdatedDeps: newOutdatedDeps,
82
+ })
83
+ resolve(newOutdatedDeps)
84
+ }
85
+ })
86
+ })
87
+ }
88
+
58
89
  export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
59
90
  let port = 5173
60
91
  const logging = args?.logging ?? true
61
92
  const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }
62
93
  const injectSourceConfig = args?.injectSource ?? { enabled: true }
63
94
  const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true
95
+ const serverBusEnabled = args?.eventBusConfig?.enabled ?? true
64
96
  const bus = new ServerEventBus(args?.eventBusConfig)
65
97
 
66
98
  return [
@@ -91,7 +123,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
91
123
  return
92
124
  }
93
125
 
94
- const solidDedupeDeps = [
126
+ /* const solidDedupeDeps = [
95
127
  'solid-js',
96
128
  'solid-js/web',
97
129
  'solid-js/store',
@@ -106,7 +138,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
106
138
  optimizeDeps: {
107
139
  include: solidDedupeDeps,
108
140
  },
109
- }
141
+ } */
110
142
  },
111
143
  },
112
144
  {
@@ -117,7 +149,9 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
117
149
  return config.mode === 'development'
118
150
  },
119
151
  configureServer(server) {
120
- bus.start()
152
+ if (serverBusEnabled) {
153
+ bus.start()
154
+ }
121
155
 
122
156
  server.middlewares.use((req, _res, next) => {
123
157
  if (req.socket.localPort && req.socket.localPort !== port) {
@@ -182,6 +216,41 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
182
216
  return transform
183
217
  },
184
218
  },
219
+ {
220
+ name: '@tanstack/devtools:event-client-setup',
221
+ apply(config, { command }) {
222
+ if (
223
+ process.env.CI ||
224
+ process.env.NODE_ENV !== 'development' ||
225
+ command !== 'serve'
226
+ )
227
+ return false
228
+ return config.mode === 'development'
229
+ },
230
+ async configureServer() {
231
+ const packageJson = await readPackageJson()
232
+ const outdatedDeps = emitOutdatedDeps().then((deps) => deps)
233
+
234
+ // whenever a client mounts we send all the current info to the subscribers
235
+ devtoolsEventClient.on('mounted', async () => {
236
+ devtoolsEventClient.emit('outdated-deps-read', {
237
+ outdatedDeps: await outdatedDeps,
238
+ })
239
+ devtoolsEventClient.emit('package-json-read', {
240
+ packageJson,
241
+ })
242
+ })
243
+ },
244
+ async handleHotUpdate({ file }) {
245
+ if (file.endsWith('package.json')) {
246
+ const newPackageJson = await readPackageJson()
247
+ devtoolsEventClient.emit('package-json-read', {
248
+ packageJson: newPackageJson,
249
+ })
250
+ emitOutdatedDeps()
251
+ }
252
+ },
253
+ },
185
254
  {
186
255
  name: '@tanstack/devtools:better-console-logs',
187
256
  enforce: 'pre',
package/src/utils.ts CHANGED
@@ -1,7 +1,8 @@
1
+ import fs from 'node:fs/promises'
1
2
  import { normalizePath } from 'vite'
2
- // import fs from 'node:fs/promises'
3
3
  import type { Connect } from 'vite'
4
4
  import type { IncomingMessage, ServerResponse } from 'node:http'
5
+ import type { PackageJson } from '@tanstack/devtools-client'
5
6
 
6
7
  export const handleDevToolsViteRequest = (
7
8
  req: Connect.IncomingMessage,
@@ -66,23 +67,28 @@ export const parseOpenSourceParam = (source: string) => {
66
67
  return { file, line, column }
67
68
  }
68
69
 
69
- /* export const tryReadFile = async (
70
- filePath: string
71
- ) => {
70
+ const tryReadFile = async (filePath: string) => {
72
71
  try {
73
72
  const data = await fs.readFile(filePath, 'utf-8')
74
73
  return data
75
74
  } catch (error) {
76
-
77
75
  return null
78
76
  }
79
77
  }
80
78
 
81
- export const tryParseJson = (jsonString: string) => {
79
+ export const tryParseJson = <T extends any>(
80
+ jsonString: string | null | undefined,
81
+ ) => {
82
+ if (!jsonString) {
83
+ return null
84
+ }
82
85
  try {
83
86
  const result = JSON.parse(jsonString)
84
- return result
87
+ return result as T
85
88
  } catch (error) {
86
89
  return null
87
90
  }
88
- } */
91
+ }
92
+
93
+ export const readPackageJson = async () =>
94
+ tryParseJson<PackageJson>(await tryReadFile(process.cwd() + '/package.json'))