tailwindcss/packages/@tailwindcss-node/src/instrumentation.test.ts

139 lines
2.7 KiB
TypeScript
Raw Normal View History

import { stripVTControlCharacters } from 'util'
import { expect, it } from 'vitest'
import { Instrumentation } from './instrumentation'
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
it('should add instrumentation using start/end markers', () => {
let I = new Instrumentation()
I.start('Foo')
let x = 1
for (let i = 0; i < 100; i++) {
I.start('Bar')
x **= 2
I.end('Bar')
}
I.end('Foo')
I.hit('Potato')
I.hit('Potato')
I.hit('Potato')
I.hit('Potato')
expect.assertions(1)
I.report((output) => {
expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(`
"
Hits:
Potato × 4
Timers:
[0.xxms] Foo
[0.xxms] ↳ Bar × 100
"
`)
})
})
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
it('should measure callbacks via the `span` api', () => {
let I = new Instrumentation()
I.span('Foo', () => {
let x = 1
for (let i = 0; i < 100; i++) {
I.span('Bar', () => {
x **= 2
})
}
})
expect.assertions(1)
I.report((output) => {
expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(`
"
[0.xxms] Foo
[0.xxms] ↳ Bar × 100
"
`)
})
})
it('should measure async callbacks via the `span` api', async () => {
let I = new Instrumentation()
await I.span('Foo', async () => {
let x = 1
for (let i = 0; i < 100; i++) {
I.span('Bar', () => {
x **= 2
})
}
await new Promise((r) => setTimeout(r, 500))
})
2026-05-26 23:34:51 +02:00
expect.assertions(2)
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
I.report((output) => {
2026-05-26 23:34:51 +02:00
expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(`
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
"
2026-05-26 23:34:51 +02:00
[0.xxms] Foo
[0.xxms] ↳ Bar × 100
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
"
`)
2026-05-26 23:34:51 +02:00
let [, duration] = stripVTControlCharacters(output).match(/\[(.*)\]/)!
expect(parseFloat(duration)).toBeGreaterThanOrEqual(500)
Improve internal instrumentation module (#20119) This PR improves some of the internal instrumentation tooling we have. While working on another branch, I updated the instrumentation tooling to have a few different ways of measuring what's going on. Until now, we had an `I.start(label)` and corresponding `I.end(label)`. While this works, it also means that you have to make sure that you call `I.end(label)` before every `return` to track things properly. With this PR, I added a `I.span(label, () => /* some callback*/{})` API that essentially does that in one go. It also handles promises and resturns the value that was returned from the callback. This can be useful in situations where you have a one-liner: ```ts let css = I.span('toCss(…)', () => toCss(ast)) ``` If your callback is longer, then you end up in a situation where you have to indent your code, and if you want to stop measuring you have to drop code in 2 places and re-indent: ```diff - I.span('label', () => { … - }) ``` For this situation, I also added a `using _ = I.track(label)` API instead. This can also be used in any block and automatically inserts the `I.end(label)` on every exit point. This relies on the new `using` keyword, but we already relied on that for the instrumentation module. Last but not least, the constructor accepts a `shouldReport` which defaults to the `env.DEBUG`. The reason for this change is so that it's easier to report / not report during development instead of swapping out an environment variable. Again, this is internal so there is no public API change happening here. ## Test plan All tests should still pass.
2026-05-26 23:20:39 +02:00
})
})
it('should measure blocks until they go out of scope via `using`', () => {
let I = new Instrumentation()
{
using _ = I.track('Foo')
let x = 1
for (let i = 0; i < 100; i++) {
using _ = I.track('Bar')
x **= 2
}
}
expect.assertions(1)
I.report((output) => {
expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(`
"
[0.xxms] Foo
[0.xxms] ↳ Bar × 100
"
`)
})
})
it('should auto end pending timers when reporting', () => {
let I = new Instrumentation()
I.start('Foo')
let x = 1
for (let i = 0; i < 100; i++) {
I.start('Bar')
x **= 2
I.end('Bar')
}
I.start('Baz')
expect.assertions(1)
I.report((output) => {
expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(`
"
[0.xxms] Foo
[0.xxms] ↳ Bar × 100
[0.xxms] ↳ Baz
"
`)
})
})