or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compat.mdcomponents.mdcontext.mdcore.mddevtools.mdhooks.mdindex.mdjsx-runtime.mdtesting.md

devtools.mddocs/

0

# Development Tools

1

2

Development utilities, debugging helpers, and React DevTools integration for enhanced developer experience. These tools help with debugging, profiling, and understanding component behavior during development.

3

4

## Capabilities

5

6

### Debug Module Utilities

7

8

Development utilities for debugging and inspecting component behavior.

9

10

```typescript { .api }

11

/**

12

* Resets the history of prop warnings

13

* Useful for clearing accumulated warnings in development

14

*/

15

function resetPropWarnings(): void;

16

17

/**

18

* Gets the currently rendering VNode

19

* @returns The VNode currently being processed or null

20

*/

21

function getCurrentVNode(): VNode | null;

22

23

/**

24

* Gets the display name of a component from a VNode

25

* @param vnode - VNode to extract display name from

26

* @returns Component display name or element type

27

*/

28

function getDisplayName(vnode: VNode): string;

29

30

/**

31

* Gets the component stack trace for debugging

32

* @param vnode - VNode to get stack trace for

33

* @returns String representation of component hierarchy

34

*/

35

function getOwnerStack(vnode: VNode): string;

36

```

37

38

**Usage Examples:**

39

40

```typescript

41

import {

42

resetPropWarnings,

43

getCurrentVNode,

44

getDisplayName,

45

getOwnerStack,

46

createElement

47

} from "preact/debug";

48

49

// Development debugging helper

50

function DebugInfo() {

51

const handleDebugClick = () => {

52

const currentVNode = getCurrentVNode();

53

54

if (currentVNode) {

55

console.log("Current VNode:", currentVNode);

56

console.log("Display Name:", getDisplayName(currentVNode));

57

console.log("Owner Stack:", getOwnerStack(currentVNode));

58

} else {

59

console.log("No VNode currently rendering");

60

}

61

};

62

63

const clearWarnings = () => {

64

resetPropWarnings();

65

console.log("Prop warnings cleared");

66

};

67

68

return createElement("div", { className: "debug-panel" },

69

createElement("h3", null, "Debug Tools"),

70

createElement("button", { onClick: handleDebugClick }, "Log Current VNode"),

71

createElement("button", { onClick: clearWarnings }, "Clear Prop Warnings")

72

);

73

}

74

75

// Custom hook for component debugging

76

function useComponentDebug(componentName: string) {

77

useEffect(() => {

78

console.log(`${componentName} mounted`);

79

80

return () => {

81

console.log(`${componentName} unmounted`);

82

};

83

}, [componentName]);

84

85

useEffect(() => {

86

const currentVNode = getCurrentVNode();

87

if (currentVNode) {

88

console.log(`${componentName} rendered:`, {

89

displayName: getDisplayName(currentVNode),

90

ownerStack: getOwnerStack(currentVNode)

91

});

92

}

93

});

94

}

95

96

// Component with debug information

97

function DebuggableComponent({ data }: { data: any }) {

98

useComponentDebug('DebuggableComponent');

99

100

return createElement("div", null,

101

createElement("h4", null, "Debuggable Component"),

102

createElement("pre", null, JSON.stringify(data, null, 2))

103

);

104

}

105

```

106

107

### DevTools Integration

108

109

React DevTools integration for component inspection and profiling.

110

111

```typescript { .api }

112

/**

113

* Adds a custom name to a hook value for React DevTools display

114

* @param value - The hook value to name

115

* @param name - Custom name to display in DevTools

116

* @returns The original value unchanged

117

*/

118

function addHookName<T>(value: T, name: string): T;

119

```

120

121

**Usage Examples:**

122

123

```typescript

124

import { addHookName } from "preact/devtools";

125

import { useState, useEffect, useMemo } from "preact/hooks";

126

127

// Custom hook with DevTools naming

128

function useCounter(initialValue = 0, step = 1) {

129

const [count, setCount] = useState(initialValue);

130

131

const increment = () => setCount(c => c + step);

132

const decrement = () => setCount(c => c - step);

133

const reset = () => setCount(initialValue);

134

135

// These will show up with custom names in React DevTools

136

return {

137

count: addHookName(count, 'Count'),

138

increment: addHookName(increment, 'Increment'),

139

decrement: addHookName(decrement, 'Decrement'),

140

reset: addHookName(reset, 'Reset')

141

};

142

}

143

144

// Complex custom hook with multiple named values

145

function useApiData<T>(url: string) {

146

const [data, setData] = useState<T | null>(null);

147

const [loading, setLoading] = useState(true);

148

const [error, setError] = useState<Error | null>(null);

149

150

useEffect(() => {

151

let cancelled = false;

152

153

const fetchData = async () => {

154

try {

155

setLoading(true);

156

setError(null);

157

158

const response = await fetch(url);

159

if (!response.ok) {

160

throw new Error(`HTTP ${response.status}: ${response.statusText}`);

161

}

162

163

const result = await response.json();

164

165

if (!cancelled) {

166

setData(result);

167

setLoading(false);

168

}

169

} catch (err) {

170

if (!cancelled) {

171

setError(err instanceof Error ? err : new Error(String(err)));

172

setLoading(false);

173

}

174

}

175

};

176

177

fetchData();

178

179

return () => {

180

cancelled = true;

181

};

182

}, [url]);

183

184

const refetch = () => {

185

setData(null);

186

setLoading(true);

187

setError(null);

188

};

189

190

// Named values for better DevTools experience

191

return {

192

data: addHookName(data, 'API Data'),

193

loading: addHookName(loading, 'Loading State'),

194

error: addHookName(error, 'Error State'),

195

refetch: addHookName(refetch, 'Refetch Function')

196

};

197

}

198

199

// Component using named hooks

200

function UserProfile({ userId }: { userId: number }) {

201

const { data: user, loading, error, refetch } = useApiData<User>(`/api/users/${userId}`);

202

const counter = useCounter(0, 1);

203

204

if (loading) return createElement("div", null, "Loading...");

205

if (error) return createElement("div", null, `Error: ${error.message}`);

206

if (!user) return createElement("div", null, "User not found");

207

208

return createElement("div", null,

209

createElement("h2", null, user.name),

210

createElement("p", null, user.email),

211

createElement("div", null,

212

createElement("p", null, `Counter: ${counter.count}`),

213

createElement("button", { onClick: counter.increment }, "+"),

214

createElement("button", { onClick: counter.decrement }, "-"),

215

createElement("button", { onClick: counter.reset }, "Reset")

216

),

217

createElement("button", { onClick: refetch }, "Refetch User")

218

);

219

}

220

```

221

222

### Global Options and Hooks

223

224

Global configuration options for development and debugging.

225

226

```typescript { .api }

227

/**

228

* Global options object for customizing Preact behavior

229

*/

230

interface Options {

231

/** Hook invoked when a VNode is created */

232

vnode?(vnode: VNode): void;

233

234

/** Hook invoked before a VNode is unmounted */

235

unmount?(vnode: VNode): void;

236

237

/** Hook invoked after a VNode has been rendered/updated */

238

diffed?(vnode: VNode): void;

239

240

/** Event processing hook */

241

event?(e: Event): any;

242

243

/** Custom requestAnimationFrame implementation */

244

requestAnimationFrame?(callback: () => void): void;

245

246

/** Custom render batching function */

247

debounceRendering?(cb: () => void): void;

248

249

/** Custom debug value formatter */

250

useDebugValue?(value: string | number): void;

251

252

/** Internal hook name tracking */

253

_addHookName?(name: string | number): void;

254

255

/** Suspense resolution hook */

256

__suspenseDidResolve?(vnode: VNode, cb: () => void): void;

257

258

/** Custom attribute serialization for precompiled JSX */

259

attr?(name: string, value: any): string | void;

260

}

261

262

/**

263

* Global options object - can be modified to customize Preact behavior

264

*/

265

const options: Options;

266

```

267

268

**Usage Examples:**

269

270

```typescript

271

import { options } from "preact";

272

import { createElement } from "preact";

273

274

// Development logging setup

275

if (process.env.NODE_ENV === 'development') {

276

// Log all VNode creations

277

const originalVnode = options.vnode;

278

options.vnode = (vnode) => {

279

console.log('VNode created:', vnode.type, vnode.props);

280

originalVnode?.(vnode);

281

};

282

283

// Log component unmounts

284

const originalUnmount = options.unmount;

285

options.unmount = (vnode) => {

286

console.log('VNode unmounting:', vnode.type);

287

originalUnmount?.(vnode);

288

};

289

290

// Log render completions

291

const originalDiffed = options.diffed;

292

options.diffed = (vnode) => {

293

console.log('VNode diffed:', vnode.type);

294

originalDiffed?.(vnode);

295

};

296

297

// Custom debug value formatting

298

options.useDebugValue = (value) => {

299

if (typeof value === 'object') {

300

return JSON.stringify(value, null, 2);

301

}

302

return String(value);

303

};

304

}

305

306

// Performance monitoring

307

function setupPerformanceMonitoring() {

308

const renderTimes = new Map<string, number>();

309

310

const originalVnode = options.vnode;

311

options.vnode = (vnode) => {

312

if (typeof vnode.type === 'function') {

313

const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';

314

renderTimes.set(componentName, performance.now());

315

}

316

originalVnode?.(vnode);

317

};

318

319

const originalDiffed = options.diffed;

320

options.diffed = (vnode) => {

321

if (typeof vnode.type === 'function') {

322

const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';

323

const startTime = renderTimes.get(componentName);

324

325

if (startTime) {

326

const renderTime = performance.now() - startTime;

327

console.log(`${componentName} rendered in ${renderTime.toFixed(2)}ms`);

328

renderTimes.delete(componentName);

329

}

330

}

331

originalDiffed?.(vnode);

332

};

333

}

334

335

// Custom event handling

336

function setupEventLogging() {

337

const originalEvent = options.event;

338

options.event = (e) => {

339

// Log all events in development

340

if (process.env.NODE_ENV === 'development') {

341

console.log('Event:', e.type, e.target);

342

}

343

344

// Call original event handler if it exists

345

return originalEvent?.(e);

346

};

347

}

348

349

// Custom render batching for testing

350

function setupTestBatching() {

351

options.debounceRendering = (callback) => {

352

// Immediate rendering for tests

353

callback();

354

};

355

}

356

357

// Component that uses global options

358

function DebuggedApp() {

359

useEffect(() => {

360

if (process.env.NODE_ENV === 'development') {

361

setupPerformanceMonitoring();

362

setupEventLogging();

363

}

364

}, []);

365

366

return createElement("div", null,

367

createElement("h1", null, "Debugged Application"),

368

createElement("button", {

369

onClick: () => console.log("Button clicked")

370

}, "Test Button")

371

);

372

}

373

```

374

375

### Development Environment Setup

376

377

Utilities and patterns for setting up effective development environments.

378

379

**Usage Examples:**

380

381

```typescript

382

// Development environment configuration

383

function setupDevelopmentEnvironment() {

384

// Auto-import debug module in development

385

if (process.env.NODE_ENV === 'development') {

386

import('preact/debug').then(() => {

387

console.log('Preact debug mode enabled');

388

});

389

390

// Import devtools integration

391

import('preact/devtools').then(() => {

392

console.log('React DevTools integration enabled');

393

});

394

}

395

}

396

397

// Error boundary with debug information

398

class DebugErrorBoundary extends Component<

399

{ children: ComponentChildren; fallback?: ComponentChildren },

400

{ hasError: boolean; error?: Error }

401

> {

402

state = { hasError: false };

403

404

static getDerivedStateFromError(error: Error) {

405

return { hasError: true, error };

406

}

407

408

componentDidCatch(error: Error, errorInfo: any) {

409

console.error('Error caught by boundary:', error);

410

console.error('Error info:', errorInfo);

411

412

if (process.env.NODE_ENV === 'development') {

413

const currentVNode = getCurrentVNode();

414

if (currentVNode) {

415

console.error('Error in component:', getDisplayName(currentVNode));

416

console.error('Component stack:', getOwnerStack(currentVNode));

417

}

418

}

419

}

420

421

render() {

422

if (this.state.hasError) {

423

return this.props.fallback || createElement("div", null,

424

createElement("h2", null, "Something went wrong"),

425

process.env.NODE_ENV === 'development' &&

426

createElement("pre", null, this.state.error?.stack)

427

);

428

}

429

430

return this.props.children;

431

}

432

}

433

434

// Development-only component inspector

435

function ComponentInspector({ children }: { children: ComponentChildren }) {

436

if (process.env.NODE_ENV !== 'development') {

437

return children;

438

}

439

440

return createElement("dev-tools", {

441

style: { position: 'relative' }

442

},

443

children,

444

createElement(DebugInfo)

445

);

446

}

447

```