Skip to content

Commit

Permalink
Add register.js for use with --import flag (#4110)
Browse files Browse the repository at this point in the history
* Add /register export for use
with --import flag

* Add !register.js to .npmignore

* Fix register.js lint

* Add missing exports to package.json

* Test startup for each loader/import scenario

* Remove exports entry from package.json to avoid breaking change

* Remove extensionless argv case from startup.spec.js

* Add version checks to startup.spec.js

* Use v20.6 instead of v20

* Improve version checks in startup.spec.js

---------

Co-authored-by: Sam Martin <[email protected]>
Co-authored-by: Sam Martin <[email protected]>
  • Loading branch information
3 people authored Feb 27, 2024
1 parent 04f1370 commit eb34abe
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 126 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
!esbuild.js
!init.js
!loader-hook.mjs
!register.js
!package.json
!cypress/**/*
!ci/**/*
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,17 @@ but code loaded using `import` might not always work.

Use the following command to enable experimental ESM support with your application:

Node.js < v20.6

```sh
node --loader dd-trace/loader-hook.mjs entrypoint.js
```

Node.js >= v20.6

```sh
node --import dd-trace/register.js entrypoint.js
```

## Serverless / Lambda

Expand Down
277 changes: 151 additions & 126 deletions integration-tests/startup.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,154 +8,179 @@ const {
} = require('./helpers')
const path = require('path')
const { assert } = require('chai')

describe('startup', () => {
let agent
let proc
let sandbox
let cwd
let startupTestFile

before(async () => {
sandbox = await createSandbox()
cwd = sandbox.folder
startupTestFile = path.join(cwd, 'startup/index.js')
})

after(async () => {
await sandbox.remove()
})

context('programmatic', () => {
beforeEach(async () => {
agent = await new FakeAgent().start()
const semver = require('semver')

const execArgvs = [
{
execArgv: []
},
{
execArgv: ['--import', 'dd-trace/register.js'],
skip: semver.satisfies(process.versions.node, '<20.6')
},
{
execArgv: ['--loader', 'dd-trace/loader-hook.mjs'],
skip: semver.satisfies(process.versions.node, '>=20.6')
}
]

execArgvs.forEach(({ execArgv, skip }) => {
const describe = skip ? globalThis.describe.skip : globalThis.describe

describe(`startup ${execArgv.join(' ')}`, () => {
let agent
let proc
let sandbox
let cwd
let startupTestFile

before(async () => {
sandbox = await createSandbox()
cwd = sandbox.folder
startupTestFile = path.join(cwd, 'startup/index.js')
})

afterEach(async () => {
proc.kill()
await agent.stop()
after(async () => {
await sandbox.remove()
})

it('works for options.port', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
env: {
AGENT_PORT: agent.port
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `127.0.0.1:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
context('programmatic', () => {
beforeEach(async () => {
agent = await new FakeAgent().start()
})
})

it('works for options.url', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
env: {
AGENT_URL: `http://localhost:${agent.port}`
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `localhost:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
afterEach(async () => {
proc.kill()
await agent.stop()
})
})
})

context('env var', () => {
beforeEach(async () => {
agent = await new FakeAgent().start()
})

afterEach(async () => {
proc.kill()
await agent.stop()
})

it('works for DD_TRACE_AGENT_PORT', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
env: {
DD_TRACE_AGENT_PORT: agent.port
}
it('works for options.port', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv,
env: {
AGENT_PORT: agent.port
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `127.0.0.1:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `127.0.0.1:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')

it('works for options.url', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv,
env: {
AGENT_URL: `http://localhost:${agent.port}`
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `localhost:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})
})

it('works for DD_TRACE_AGENT_URL', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
env: {
DD_TRACE_AGENT_URL: `http://localhost:${agent.port}`
}
context('env var', () => {
beforeEach(async () => {
agent = await new FakeAgent().start()
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `localhost:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')

afterEach(async () => {
proc.kill()
await agent.stop()
})
})
})

context('default', () => {
beforeEach(async () => {
// Note that this test will *always* listen on the default port. If that
// port is unavailable, the test will fail.
agent = await new FakeAgent(8126).start()
})
it('works for DD_TRACE_AGENT_PORT', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv,
env: {
DD_TRACE_AGENT_PORT: agent.port
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `127.0.0.1:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})

afterEach(async () => {
proc.kill()
await agent.stop()
it('works for DD_TRACE_AGENT_URL', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv,
env: {
DD_TRACE_AGENT_URL: `http://localhost:${agent.port}`
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', `localhost:${agent.port}`)
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})
})

it('works for hostname and port', async () => {
proc = await spawnProc(startupTestFile, {
cwd
context('default', () => {
beforeEach(async () => {
// Note that this test will *always* listen on the default port. If that
// port is unavailable, the test will fail.
agent = await new FakeAgent(8126).start()
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', '127.0.0.1:8126')
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')

afterEach(async () => {
proc.kill()
await agent.stop()
})
})

it('works with stealthy-require', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
env: {
STEALTHY_REQUIRE: 'true'
}
it('works for hostname and port', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', '127.0.0.1:8126')
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', '127.0.0.1:8126')
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')

it('works with stealthy-require', async () => {
proc = await spawnProc(startupTestFile, {
cwd,
execArgv,
env: {
STEALTHY_REQUIRE: 'true'
}
})
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
assert.propertyVal(headers, 'host', '127.0.0.1:8126')
assert.isArray(payload)
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'web.request')
})
})
})
})
Expand Down
4 changes: 4 additions & 0 deletions register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')

register('./loader-hook.mjs', pathToFileURL(__filename))

0 comments on commit eb34abe

Please sign in to comment.