Skip to content

Commit 0558ca7

Browse files
authored
Merge pull request #121 from DataDog/yoann/add-more-injection-points
[internal] Add more injection points
2 parents a6563ad + 1e6996e commit 0558ca7

File tree

83 files changed

+2150
-1030
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2150
-1030
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ module.exports = {
230230
],
231231
'no-delete-var': 'error',
232232
'no-label-var': 'error',
233-
'no-shadow': 'error',
233+
'no-shadow': 'off',
234+
'@typescript-eslint/no-shadow': ['error'],
234235
'no-shadow-restricted-names': 'error',
235236
'no-undef': 'error',
236237
'no-undef-init': 'error',

.github/workflows/ci.yaml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@ jobs:
88
fail-fast: false
99
matrix:
1010
node:
11-
- 18
12-
- 20
11+
- node-version-file: 'package.json'
12+
- node-version: 20.x
1313

14-
name: Unit tests w/ Node.js ${{matrix.node}}.x
14+
name: Unit tests w/ Node.js ${{matrix.node.node-version || matrix.node.node-version-file}}
1515
runs-on: ubuntu-latest
1616

1717
steps:
1818
- uses: actions/checkout@v4
19-
- name: Install Node ${{matrix.node}}.x
19+
- name: Install Node ${{matrix.node.node-version || matrix.node.node-version-file}}
2020
uses: actions/setup-node@v4
21-
with:
22-
node-version: ${{matrix.node}}.x
21+
with: ${{matrix.node}}
2322
- run: yarn install
2423
- run: yarn build:all
2524
- run: yarn test
@@ -33,7 +32,7 @@ jobs:
3332
- name: Install node
3433
uses: actions/setup-node@v4
3534
with:
36-
node-version: '18.19.0'
35+
node-version-file: 'package.json'
3736
- run: yarn install
3837
- run: yarn build:all
3938
- run: yarn typecheck:all

.node-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@
2525
"**/.yarn/": true,
2626
"**/.yarnrc.yml": true,
2727
"**/yarn.lock": true
28-
}
28+
},
29+
"typescript.tsdk": "node_modules/typescript/lib"
2930
}

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
"workspaces": [
1111
"packages/*",
1212
"packages/plugins/*",
13-
"packages/published/*",
14-
"packages/tests/src/_jest/fixtures/project"
13+
"packages/published/*"
1514
],
1615
"volta": {
17-
"node": "18.19.0",
16+
"node": "18.20.5",
1817
"yarn": "1.22.19"
1918
},
2019
"scripts": {

packages/core/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
},
2525
"dependencies": {
2626
"async-retry": "1.3.3",
27-
"chalk": "2.3.1"
27+
"chalk": "2.3.1",
28+
"glob": "11.0.0"
2829
},
2930
"devDependencies": {
3031
"@types/async-retry": "1.4.8",
3132
"@types/chalk": "2.2.0",
3233
"@types/node": "^18",
34+
"esbuild": "0.24.0",
3335
"typescript": "5.4.3",
3436
"unplugin": "1.16.0"
3537
}

packages/core/src/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ export const INJECTED_FILE = '__datadog-helper-file';
77
export const ALL_BUNDLERS = ['webpack', 'vite', 'esbuild', 'rollup', 'rspack', 'rolldown', 'farm'];
88
export const SUPPORTED_BUNDLERS = ['webpack', 'vite', 'esbuild', 'rollup', 'rspack'] as const;
99
export const FULL_NAME_BUNDLERS = [
10-
'webpack4',
11-
'webpack5',
12-
'vite',
1310
'esbuild',
1411
'rollup',
1512
'rspack',
13+
'vite',
14+
'webpack4',
15+
'webpack5',
1616
] as const;

packages/core/src/helpers.ts

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
import { INJECTED_FILE } from '@dd/core/constants';
66
import retry from 'async-retry';
7+
import type { PluginBuild } from 'esbuild';
78
import fsp from 'fs/promises';
89
import fs from 'fs';
10+
import { glob } from 'glob';
911
import path from 'path';
1012
import type { RequestInit } from 'undici-types';
1113

12-
import type { RequestOpts } from './types';
14+
import type { GlobalContext, Logger, RequestOpts, ResolvedEntry } from './types';
1315

1416
// Format a duration 0h 0m 0s 0ms
1517
export const formatDuration = (duration: number) => {
@@ -25,12 +27,72 @@ export const formatDuration = (duration: number) => {
2527
}${milliseconds ? `${milliseconds}ms` : ''}`.trim();
2628
};
2729

28-
export const getResolvedPath = (filepath: string) => {
29-
try {
30-
return require.resolve(filepath);
31-
} catch (e) {
32-
return filepath;
30+
// https://esbuild.github.io/api/#glob-style-entry-points
31+
const getAllEntryFiles = (filepath: string): string[] => {
32+
if (!filepath.includes('*')) {
33+
return [filepath];
3334
}
35+
36+
const files = glob.sync(filepath);
37+
return files;
38+
};
39+
40+
// Parse, resolve and return all the entries of esbuild.
41+
export const getEsbuildEntries = async (
42+
build: PluginBuild,
43+
context: GlobalContext,
44+
log: Logger,
45+
): Promise<ResolvedEntry[]> => {
46+
const entries: { name?: string; resolved: string; original: string }[] = [];
47+
const entryPoints = build.initialOptions.entryPoints;
48+
const entryPaths: { name?: string; path: string }[] = [];
49+
const resolutionErrors: string[] = [];
50+
51+
if (Array.isArray(entryPoints)) {
52+
for (const entry of entryPoints) {
53+
const fullPath = entry && typeof entry === 'object' ? entry.in : entry;
54+
entryPaths.push({ path: fullPath });
55+
}
56+
} else if (entryPoints && typeof entryPoints === 'object') {
57+
entryPaths.push(
58+
...Object.entries(entryPoints).map(([name, filepath]) => ({ name, path: filepath })),
59+
);
60+
}
61+
62+
// Resolve all the paths.
63+
const proms = entryPaths
64+
.flatMap((entry) =>
65+
getAllEntryFiles(entry.path).map<[{ name?: string; path: string }, string]>((p) => [
66+
entry,
67+
p,
68+
]),
69+
)
70+
.map(async ([entry, p]) => {
71+
const result = await build.resolve(p, {
72+
kind: 'entry-point',
73+
resolveDir: context.cwd,
74+
});
75+
76+
if (result.errors.length) {
77+
resolutionErrors.push(...result.errors.map((e) => e.text));
78+
}
79+
80+
if (result.path) {
81+
// Store them for later use.
82+
entries.push({
83+
name: entry.name,
84+
resolved: result.path,
85+
original: entry.path,
86+
});
87+
}
88+
});
89+
90+
for (const resolutionError of resolutionErrors) {
91+
log.error(resolutionError);
92+
}
93+
94+
await Promise.all(proms);
95+
return entries;
3496
};
3597

3698
export const ERROR_CODES_NO_RETRY = [400, 403, 413];
@@ -169,3 +231,6 @@ export const readJsonSync = (filepath: string) => {
169231
const data = fs.readFileSync(filepath, { encoding: 'utf-8' });
170232
return JSON.parse(data);
171233
};
234+
235+
let index = 0;
236+
export const getUniqueId = () => `${Date.now()}.${performance.now()}.${++index}`;

packages/core/src/types.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,18 @@ export type BundlerReport = {
7474
version: string;
7575
};
7676

77-
export type ToInjectItem = { type: 'file' | 'code'; value: string; fallback?: ToInjectItem };
77+
export type InjectedValue = string | (() => Promise<string>);
78+
export enum InjectPosition {
79+
BEFORE,
80+
MIDDLE,
81+
AFTER,
82+
}
83+
export type ToInjectItem = {
84+
type: 'file' | 'code';
85+
value: InjectedValue;
86+
position?: InjectPosition;
87+
fallback?: ToInjectItem;
88+
};
7889

7990
export type GetLogger = (name: string) => Logger;
8091
export type Logger = {
@@ -146,3 +157,5 @@ export type RequestOpts = {
146157
type?: 'json' | 'text';
147158
onRetry?: (error: Error, attempt: number) => void;
148159
};
160+
161+
export type ResolvedEntry = { name?: string; resolved: string; original: string };

packages/factory/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ Most of the time they will interact via the global context.
4444

4545
### Injection
4646

47-
> This is used to prepend some code to the produced bundle.<br/>
48-
> Particularly useful if you want to share some global context, or to automatically inject some SDK.
47+
> This is used to inject some code to the produced bundle.<br/>
48+
> Particularly useful :
49+
> - to share some global context.
50+
> - to automatically inject some SDK.
51+
> - to initialise some global dependencies.
52+
> - ...
4953
5054
<kbd>[📝 Full documentation ➡️](/packages/plugins/injection#readme)</kbd>
5155
<!-- #internal-plugins-list -->

packages/factory/src/helpers.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// This product includes software developed at Datadog (https://www.datadoghq.com/).
33
// Copyright 2019-Present Datadog, Inc.
44

5+
import { getUniqueId } from '@dd/core/helpers';
56
import type {
67
BuildReport,
78
BundlerFullName,
@@ -84,7 +85,7 @@ export const getContext = ({
8485
options: OptionsWithDefaults;
8586
bundlerName: BundlerName;
8687
bundlerVersion: string;
87-
injections: ToInjectItem[];
88+
injections: Map<string, ToInjectItem>;
8889
version: FactoryMeta['version'];
8990
}): GlobalContext => {
9091
const cwd = process.cwd();
@@ -101,13 +102,15 @@ export const getContext = ({
101102
name: bundlerName,
102103
fullName: `${bundlerName}${variant}` as BundlerFullName,
103104
variant,
105+
// This will be updated in the bundler-report plugin once we have the configuration.
104106
outDir: cwd,
105107
version: bundlerVersion,
106108
},
107109
build,
110+
// This will be updated in the bundler-report plugin once we have the configuration.
108111
cwd,
109112
inject: (item: ToInjectItem) => {
110-
injections.push(item);
113+
injections.set(getUniqueId(), item);
111114
},
112115
start: Date.now(),
113116
version,

packages/factory/src/index.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// This product includes software developed at Datadog (https://www.datadoghq.com/).
33
// Copyright 2019-Present Datadog, Inc.
44

5+
/* eslint-disable arca/import-ordering, arca/newline-after-import-section */
56
// This file is mostly generated.
67
// Anything between
78
// - #imports-injection-marker
@@ -22,10 +23,10 @@ import type {
2223
} from '@dd/core/types';
2324
import type { UnpluginContextMeta, UnpluginInstance, UnpluginOptions } from 'unplugin';
2425
import { createUnplugin } from 'unplugin';
26+
import chalk from 'chalk';
2527

2628
import { getContext, getLoggerFactory, validateOptions } from './helpers';
2729

28-
/* eslint-disable arca/import-ordering, arca/newline-after-import-section */
2930
// #imports-injection-marker
3031
import type { OptionsWithErrorTracking } from '@dd/error-tracking-plugin/types';
3132
import * as errorTracking from '@dd/error-tracking-plugin';
@@ -40,7 +41,6 @@ import { getInjectionPlugins } from '@dd/internal-injection-plugin';
4041
export type { types as ErrorTrackingTypes } from '@dd/error-tracking-plugin';
4142
export type { types as TelemetryTypes } from '@dd/telemetry-plugin';
4243
// #types-export-injection-marker
43-
/* eslint-enable arca/import-ordering, arca/newline-after-import-section */
4444

4545
export const helpers = {
4646
// Each product should have a unique entry.
@@ -68,7 +68,7 @@ export const buildPluginFactory = ({
6868
}
6969

7070
// Create the global context.
71-
const injections: ToInjectItem[] = [];
71+
const injections: Map<string, ToInjectItem> = new Map();
7272
const context: GlobalContext = getContext({
7373
options,
7474
bundlerVersion: bundler.version || bundler.VERSION,
@@ -91,6 +91,7 @@ export const buildPluginFactory = ({
9191
...getGitPlugins(options, context),
9292
...getInjectionPlugins(
9393
bundler,
94+
options,
9495
context,
9596
injections,
9697
getLogger('datadog-injection-plugin'),
@@ -136,6 +137,18 @@ export const buildPluginFactory = ({
136137
// List all our plugins in the context.
137138
context.pluginNames.push(...plugins.map((plugin) => plugin.name));
138139

140+
// Verify we don't have plugins with the same name, as they would override each other.
141+
const duplicates = new Set(
142+
context.pluginNames.filter(
143+
(name) => context.pluginNames.filter((n) => n === name).length > 1,
144+
),
145+
);
146+
if (duplicates.size > 0) {
147+
throw new Error(
148+
`Duplicate plugin names: ${chalk.bold.red(Array.from(duplicates).join(', '))}`,
149+
);
150+
}
151+
139152
return plugins;
140153
});
141154
};

packages/plugins/build-report/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"typecheck": "tsc --noEmit"
2020
},
2121
"dependencies": {
22-
"@dd/core": "workspace:*",
23-
"glob": "11.0.0"
22+
"@dd/core": "workspace:*"
2423
}
2524
}

0 commit comments

Comments
 (0)