Repository: nodejs/node
Pull request: doc: add esm and cjs examples to `node:v8`

This PR adds the missing `ESM` and `CJS` examples to their respective counterparts for the [V8 documentation](https://nodejs.org/api/v8.html).

For the [`setFlagsFromString`](https://nodejs.org/api/v8.html#v8setflagsfromstringflags) example, we weren't printing anything back to the user so the example looked like we just hang the terminal for one minute and then come back. So I added some events (the ones using `new Array()`) to add _some_ computation that would trigger the GC so we can get some output for the example, then it showcases the actual behavior by invoking `setFlagsFromString('--notrace-gc')` that stops the tracing, users can see we're still creating Arrays for one more second but the GC events are now stopped via `setFlagsFromString()`.

The same was happening for the [`Promise hooks`](https://nodejs.org/api/v8.html#promise-hooks) example (in this case I added its `CJS` counterpart). We were not giving any output back to the users, so it may not be clear what it's going on, I just added the invocation of one Promise (`promisePrint`) so now we can see all the `console.log` from the Promise life cycle.

The same was also happening for the [`isStringOneByteRepresentation`](https://nodejs.org/api/v8.html#v8isstringonebyterepresentationcontent) example so i just added a couple of `console.log` to print the `buffer` and see its contents.

Now all these examples give some output to users trying to understand this parts of the documentation.

There was one particular example that I couldn't make work using `ESM`, it's the [`Startup Snapshot API`](https://nodejs.org/api/v8.html#startup-snapshot-api) example, it works fine using `CJS` but when using `ESM` it throws an error, sadly I lack the V8 knowledge right now to make it work, I tested it on versions `v18.20.8`, `v20.19.6`, `v22.21.1`, `v24.12.0` and `v25.1.0`

For version  `v18.20.8` the error is:
```bash
import fs from 'node:fs';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at main (node:internal/main/mksnapshot:130:33)
    at node:internal/main/mksnapshot:170:1
```

And from version `v20.19.6` onwards the error is:
```bash
import fs from 'node:fs';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at minimalRunCjs (node:internal/main/mksnapshot:207:16)
```

I made a simple POC to reproduce the error easily [here](https://github.com/mfdebian/startup-snapshot-api-cjs-esm), I already asked for help in the discord but we couldn't make it work, maybe someone with better V8 expertise can help with this one.

That's it for now! Thank you and happy new year! :green_heart: :partying_face: 

Changed files:
filename: doc/api/v8.md
patch: @@ -7,7 +7,11 @@
 The `node:v8` module exposes APIs that are specific to the version of [V8][]
 built into the Node.js binary. It can be accessed using:
 
-```js
+```mjs
+import v8 from 'node:v8';
+```
+
+```cjs
 const v8 = require('node:v8');
 ```
 
@@ -92,9 +96,18 @@ terminating the process.
 Generating a snapshot is a synchronous operation which blocks the event loop
 for a duration depending on the heap size.
 
+```mjs
+// Print heap snapshot to the console
+import { getHeapSnapshot } from 'node:v8';
+import process from 'node:process';
+const stream = getHeapSnapshot();
+stream.pipe(process.stdout);
+```
+
 ```js
 // Print heap snapshot to the console
 const v8 = require('node:v8');
+const process = require('node:process');
 const stream = v8.getHeapSnapshot();
 stream.pipe(process.stdout);
 ```
@@ -467,11 +480,70 @@ The V8 options available for a version of Node.js may be determined by running
 
 Usage:
 
-```js
-// Print GC events to stdout for one minute.
-const v8 = require('node:v8');
-v8.setFlagsFromString('--trace_gc');
-setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
+```mjs
+import { setFlagsFromString } from 'node:v8';
+import { setInterval } from 'node:timers';
+
+// setFlagsFromString to trace garbage collection events
+setFlagsFromString('--trace-gc');
+
+// Trigger GC events by using some memory
+let arrays = [];
+const interval = setInterval(() => {
+  for (let i = 0; i < 500; i++) {
+    arrays.push(new Array(10000).fill(Math.random()));
+  }
+
+  if (arrays.length > 5000) {
+    arrays = arrays.slice(-1000);
+  }
+
+  console.log(`\n* Created ${arrays.length} arrays\n`);
+}, 100);
+
+// setFlagsFromString to stop tracing GC events after 1.5 seconds
+setTimeout(() => {
+  setFlagsFromString('--notrace-gc');
+  console.log('\nStopped tracing!\n');
+}, 1500);
+
+// Stop triggering GC events altogether after 2.5 seconds
+setTimeout(() => {
+  clearInterval(interval);
+}, 2500);
+```
+
+```cjs
+const { setFlagsFromString } = require('node:v8');
+const { setInterval } = require('node:timers');
+
+// setFlagsFromString to trace garbage collection events
+setFlagsFromString('--trace-gc');
+
+// Trigger GC events by using some memory
+let arrays = [];
+const interval = setInterval(() => {
+  for (let i = 0; i < 500; i++) {
+    arrays.push(new Array(10000).fill(Math.random()));
+  }
+
+  if (arrays.length > 5000) {
+    arrays = arrays.slice(-1000);
+  }
+
+  console.log(`\n* Created ${arrays.length} arrays\n`);
+}, 100);
+
+// setFlagsFromString to stop tracing GC events after 1.5 seconds
+setTimeout(() => {
+  console.log('\nStopped tracing!\n');
+  setFlagsFromString('--notrace-gc');
+}, 1500);
+
+// Stop triggering GC events altogether after 2.5 seconds
+setTimeout(() => {
+  clearInterval(interval);
+}, 2500);
 ```
 
 ## `v8.stopCoverage()`
@@ -551,13 +623,37 @@ terminating the process.
 Generating a snapshot is a synchronous operation which blocks the event loop
 for a duration depending on the heap size.
 
-```js
+```mjs
+import { writeHeapSnapshot } from 'node:v8';
+import { Worker, isMainThread, parentPort } from 'node:worker_threads';
+import { fileURLToPath } from 'node:url';
+
+if (isMainThread) {
+  const __filename = fileURLToPath(import.meta.url);
+  const worker = new Worker(__filename);
+
+  worker.once('message', (filename) => {
+    console.log(`worker heapdump: ${filename}`);
+    // Now get a heapdump for the main thread.
+    console.log(`main thread heapdump: ${writeHeapSnapshot()}`);
+  });
+
+  // Tell the worker to create a heapdump.
+  worker.postMessage('heapdump');
+} else {
+  parentPort.once('message', (message) => {
+    if (message === 'heapdump') {
+      // Generate a heapdump for the worker
+      // and return the filename to the parent.
+      parentPort.postMessage(writeHeapSnapshot());
+    }
+  });
+}
+```
+
+```cjs
 const { writeHeapSnapshot } = require('node:v8');
-const {
-  Worker,
-  isMainThread,
-  parentPort,
-} = require('node:worker_threads');
+const { Worker, isMainThread, parentPort } = require('node:worker_threads');
 
 if (isMainThread) {
   const worker = new Worker(__filename);
@@ -903,6 +999,71 @@ const stopHookSet = promiseHooks.createHook({
   after,
 });
 
+// Trigger the hooks by using promises
+const promiseLog = (word) => Promise.resolve(word).then(console.log);
+promiseLog('Hello');
+promiseLog('World');
+
+// To stop a hook, call the function returned at its creation.
+stopWatchingInits();
+stopWatchingSettleds();
+stopWatchingBefores();
+stopWatchingAfters();
+stopHookSet();
+```
+
+```cjs
+const { promiseHooks } = require('node:v8');
+
+// There are four lifecycle events produced by promises:
+
+// The `init` event represents the creation of a promise. This could be a
+// direct creation such as with `new Promise(...)` or a continuation such
+// as `then()` or `catch()`. It also happens whenever an async function is
+// called or does an `await`. If a continuation promise is created, the
+// `parent` will be the promise it is a continuation from.
+function init(promise, parent) {
+  console.log('a promise was created', { promise, parent });
+}
+
+// The `settled` event happens when a promise receives a resolution or
+// rejection value. This may happen synchronously such as when using
+// `Promise.resolve()` on non-promise input.
+function settled(promise) {
+  console.log('a promise resolved or rejected', { promise });
+}
+
+// The `before` event runs immediately before a `then()` or `catch()` handler
+// runs or an `await` resumes execution.
+function before(promise) {
+  console.log('a promise is about to call a then handler', { promise });
+}
+
+// The `after` event runs immediately after a `then()` handler runs or when
+// an `await` begins after resuming from another.
+function after(promise) {
+  console.log('a promise is done calling a then handler', { promise });
+}
+
+// Lifecycle hooks may be started and stopped individually
+const stopWatchingInits = promiseHooks.onInit(init);
+const stopWatchingSettleds = promiseHooks.onSettled(settled);
+const stopWatchingBefores = promiseHooks.onBefore(before);
+const stopWatchingAfters = promiseHooks.onAfter(after);
+
+// Or they may be started and stopped in groups
+const stopHookSet = promiseHooks.createHook({
+  init,
+  settled,
+  before,
+  after,
+});
+
+// Trigger the hooks by using promises
+const promisePrint = (word) => Promise.resolve(word).then(console.log);
+promisePrint('Hello');
+promisePrint('World');
+
 // To stop a hook, call the function returned at its creation.
 stopWatchingInits();
 stopWatchingSettleds();
@@ -1396,7 +1557,16 @@ is as follows.
 
 Here's an example.
 
-```js
+```mjs
+import { GCProfiler } from 'node:v8';
+const profiler = new GCProfiler();
+profiler.start();
+setTimeout(() => {
+  console.log(profiler.stop());
+}, 1000);
+```
+
+```cjs
 const { GCProfiler } = require('node:v8');
 const profiler = new GCProfiler();
 profiler.start();
@@ -1507,8 +1677,37 @@ otherwise, it returns false.
 If this method returns false, that does not mean that the string contains some characters not in `Latin-1/ISO-8859-1`.
 Sometimes a `Latin-1` string may also be represented as `UTF16`.
 
-```js
+```mjs
+import { isStringOneByteRepresentation } from 'node:v8';
+import { Buffer } from 'node:buffer';
+
+const Encoding = {
+  latin1: 1,
+  utf16le: 2,
+};
+const buffer = Buffer.alloc(100);
+function writeString(input) {
+  if (isStringOneByteRepresentation(input)) {
+    console.log(`input: '${input}'`);
+    buffer.writeUint8(Encoding.latin1);
+    buffer.writeUint32LE(input.length, 1);
+    buffer.write(input, 5, 'latin1');
+    console.log(`decoded: '${buffer.toString('latin1', 5, 5 + input.length)}'\n`);
+  } else {
+    console.log(`input: '${input}'`);
+    buffer.writeUint8(Encoding.utf16le);
+    buffer.writeUint32LE(input.length * 2, 1);
+    buffer.write(input, 5, 'utf16le');
+    console.log(`decoded: '${buffer.toString('utf16le', 5, 5 + input.length * 2)}'`);
+  }
+}
+writeString('hello');
+writeString('你好');
+```
+
+```cjs
 const { isStringOneByteRepresentation } = require('node:v8');
+const { Buffer } = require('node:buffer');
 
 const Encoding = {
   latin1: 1,
@@ -1517,13 +1716,17 @@ const Encoding = {
 const buffer = Buffer.alloc(100);
 function writeString(input) {
   if (isStringOneByteRepresentation(input)) {
+    console.log(`input: '${input}'`);
     buffer.writeUint8(Encoding.latin1);
     buffer.writeUint32LE(input.length, 1);
     buffer.write(input, 5, 'latin1');
+    console.log(`decoded: '${buffer.toString('latin1', 5, 5 + input.length)}'\n`);
   } else {
+    console.log(`input: '${input}'`);
     buffer.writeUint8(Encoding.utf16le);
     buffer.writeUint32LE(input.length * 2, 1);
     buffer.write(input, 5, 'utf16le');
+    console.log(`decoded: '${buffer.toString('utf16le', 5, 5 + input.length * 2)}'`);
   }
 }
 writeString('hello');