Skip to content

Commit

Permalink
Merge pull request #121 from DataDog/yoann/add-more-injection-points
Browse files Browse the repository at this point in the history
[internal] Add more injection points
  • Loading branch information
yoannmoinet authored Feb 6, 2025
2 parents a6563ad + 1e6996e commit 0558ca7
Show file tree
Hide file tree
Showing 83 changed files with 2,150 additions and 1,030 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ module.exports = {
],
'no-delete-var': 'error',
'no-label-var': 'error',
'no-shadow': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'no-shadow-restricted-names': 'error',
'no-undef': 'error',
'no-undef-init': 'error',
Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ jobs:
fail-fast: false
matrix:
node:
- 18
- 20
- node-version-file: 'package.json'
- node-version: 20.x

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

steps:
- uses: actions/checkout@v4
- name: Install Node ${{matrix.node}}.x
- name: Install Node ${{matrix.node.node-version || matrix.node.node-version-file}}
uses: actions/setup-node@v4
with:
node-version: ${{matrix.node}}.x
with: ${{matrix.node}}
- run: yarn install
- run: yarn build:all
- run: yarn test
Expand All @@ -33,7 +32,7 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
node-version: '18.19.0'
node-version-file: 'package.json'
- run: yarn install
- run: yarn build:all
- run: yarn typecheck:all
Expand Down
1 change: 0 additions & 1 deletion .node-version

This file was deleted.

3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
"**/.yarn/": true,
"**/.yarnrc.yml": true,
"**/yarn.lock": true
}
},
"typescript.tsdk": "node_modules/typescript/lib"
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
"workspaces": [
"packages/*",
"packages/plugins/*",
"packages/published/*",
"packages/tests/src/_jest/fixtures/project"
"packages/published/*"
],
"volta": {
"node": "18.19.0",
"node": "18.20.5",
"yarn": "1.22.19"
},
"scripts": {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
},
"dependencies": {
"async-retry": "1.3.3",
"chalk": "2.3.1"
"chalk": "2.3.1",
"glob": "11.0.0"
},
"devDependencies": {
"@types/async-retry": "1.4.8",
"@types/chalk": "2.2.0",
"@types/node": "^18",
"esbuild": "0.24.0",
"typescript": "5.4.3",
"unplugin": "1.16.0"
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export const INJECTED_FILE = '__datadog-helper-file';
export const ALL_BUNDLERS = ['webpack', 'vite', 'esbuild', 'rollup', 'rspack', 'rolldown', 'farm'];
export const SUPPORTED_BUNDLERS = ['webpack', 'vite', 'esbuild', 'rollup', 'rspack'] as const;
export const FULL_NAME_BUNDLERS = [
'webpack4',
'webpack5',
'vite',
'esbuild',
'rollup',
'rspack',
'vite',
'webpack4',
'webpack5',
] as const;
77 changes: 71 additions & 6 deletions packages/core/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

import { INJECTED_FILE } from '@dd/core/constants';
import retry from 'async-retry';
import type { PluginBuild } from 'esbuild';
import fsp from 'fs/promises';
import fs from 'fs';
import { glob } from 'glob';
import path from 'path';
import type { RequestInit } from 'undici-types';

import type { RequestOpts } from './types';
import type { GlobalContext, Logger, RequestOpts, ResolvedEntry } from './types';

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

export const getResolvedPath = (filepath: string) => {
try {
return require.resolve(filepath);
} catch (e) {
return filepath;
// https://esbuild.github.io/api/#glob-style-entry-points
const getAllEntryFiles = (filepath: string): string[] => {
if (!filepath.includes('*')) {
return [filepath];
}

const files = glob.sync(filepath);
return files;
};

// Parse, resolve and return all the entries of esbuild.
export const getEsbuildEntries = async (
build: PluginBuild,
context: GlobalContext,
log: Logger,
): Promise<ResolvedEntry[]> => {
const entries: { name?: string; resolved: string; original: string }[] = [];
const entryPoints = build.initialOptions.entryPoints;
const entryPaths: { name?: string; path: string }[] = [];
const resolutionErrors: string[] = [];

if (Array.isArray(entryPoints)) {
for (const entry of entryPoints) {
const fullPath = entry && typeof entry === 'object' ? entry.in : entry;
entryPaths.push({ path: fullPath });
}
} else if (entryPoints && typeof entryPoints === 'object') {
entryPaths.push(
...Object.entries(entryPoints).map(([name, filepath]) => ({ name, path: filepath })),
);
}

// Resolve all the paths.
const proms = entryPaths
.flatMap((entry) =>
getAllEntryFiles(entry.path).map<[{ name?: string; path: string }, string]>((p) => [
entry,
p,
]),
)
.map(async ([entry, p]) => {
const result = await build.resolve(p, {
kind: 'entry-point',
resolveDir: context.cwd,
});

if (result.errors.length) {
resolutionErrors.push(...result.errors.map((e) => e.text));
}

if (result.path) {
// Store them for later use.
entries.push({
name: entry.name,
resolved: result.path,
original: entry.path,
});
}
});

for (const resolutionError of resolutionErrors) {
log.error(resolutionError);
}

await Promise.all(proms);
return entries;
};

export const ERROR_CODES_NO_RETRY = [400, 403, 413];
Expand Down Expand Up @@ -169,3 +231,6 @@ export const readJsonSync = (filepath: string) => {
const data = fs.readFileSync(filepath, { encoding: 'utf-8' });
return JSON.parse(data);
};

let index = 0;
export const getUniqueId = () => `${Date.now()}.${performance.now()}.${++index}`;
15 changes: 14 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,18 @@ export type BundlerReport = {
version: string;
};

export type ToInjectItem = { type: 'file' | 'code'; value: string; fallback?: ToInjectItem };
export type InjectedValue = string | (() => Promise<string>);
export enum InjectPosition {
BEFORE,
MIDDLE,
AFTER,
}
export type ToInjectItem = {
type: 'file' | 'code';
value: InjectedValue;
position?: InjectPosition;
fallback?: ToInjectItem;
};

export type GetLogger = (name: string) => Logger;
export type Logger = {
Expand Down Expand Up @@ -146,3 +157,5 @@ export type RequestOpts = {
type?: 'json' | 'text';
onRetry?: (error: Error, attempt: number) => void;
};

export type ResolvedEntry = { name?: string; resolved: string; original: string };
8 changes: 6 additions & 2 deletions packages/factory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ Most of the time they will interact via the global context.

### Injection

> This is used to prepend some code to the produced bundle.<br/>
> Particularly useful if you want to share some global context, or to automatically inject some SDK.
> This is used to inject some code to the produced bundle.<br/>
> Particularly useful :
> - to share some global context.
> - to automatically inject some SDK.
> - to initialise some global dependencies.
> - ...
<kbd>[📝 Full documentation ➡️](/packages/plugins/injection#readme)</kbd>
<!-- #internal-plugins-list -->
Expand Down
7 changes: 5 additions & 2 deletions packages/factory/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import { getUniqueId } from '@dd/core/helpers';
import type {
BuildReport,
BundlerFullName,
Expand Down Expand Up @@ -84,7 +85,7 @@ export const getContext = ({
options: OptionsWithDefaults;
bundlerName: BundlerName;
bundlerVersion: string;
injections: ToInjectItem[];
injections: Map<string, ToInjectItem>;
version: FactoryMeta['version'];
}): GlobalContext => {
const cwd = process.cwd();
Expand All @@ -101,13 +102,15 @@ export const getContext = ({
name: bundlerName,
fullName: `${bundlerName}${variant}` as BundlerFullName,
variant,
// This will be updated in the bundler-report plugin once we have the configuration.
outDir: cwd,
version: bundlerVersion,
},
build,
// This will be updated in the bundler-report plugin once we have the configuration.
cwd,
inject: (item: ToInjectItem) => {
injections.push(item);
injections.set(getUniqueId(), item);
},
start: Date.now(),
version,
Expand Down
19 changes: 16 additions & 3 deletions packages/factory/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

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

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

/* eslint-disable arca/import-ordering, arca/newline-after-import-section */
// #imports-injection-marker
import type { OptionsWithErrorTracking } from '@dd/error-tracking-plugin/types';
import * as errorTracking from '@dd/error-tracking-plugin';
Expand All @@ -40,7 +41,6 @@ import { getInjectionPlugins } from '@dd/internal-injection-plugin';
export type { types as ErrorTrackingTypes } from '@dd/error-tracking-plugin';
export type { types as TelemetryTypes } from '@dd/telemetry-plugin';
// #types-export-injection-marker
/* eslint-enable arca/import-ordering, arca/newline-after-import-section */

export const helpers = {
// Each product should have a unique entry.
Expand Down Expand Up @@ -68,7 +68,7 @@ export const buildPluginFactory = ({
}

// Create the global context.
const injections: ToInjectItem[] = [];
const injections: Map<string, ToInjectItem> = new Map();
const context: GlobalContext = getContext({
options,
bundlerVersion: bundler.version || bundler.VERSION,
Expand All @@ -91,6 +91,7 @@ export const buildPluginFactory = ({
...getGitPlugins(options, context),
...getInjectionPlugins(
bundler,
options,
context,
injections,
getLogger('datadog-injection-plugin'),
Expand Down Expand Up @@ -136,6 +137,18 @@ export const buildPluginFactory = ({
// List all our plugins in the context.
context.pluginNames.push(...plugins.map((plugin) => plugin.name));

// Verify we don't have plugins with the same name, as they would override each other.
const duplicates = new Set(
context.pluginNames.filter(
(name) => context.pluginNames.filter((n) => n === name).length > 1,
),
);
if (duplicates.size > 0) {
throw new Error(
`Duplicate plugin names: ${chalk.bold.red(Array.from(duplicates).join(', '))}`,
);
}

return plugins;
});
};
3 changes: 1 addition & 2 deletions packages/plugins/build-report/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@dd/core": "workspace:*",
"glob": "11.0.0"
"@dd/core": "workspace:*"
}
}
Loading

0 comments on commit 0558ca7

Please sign in to comment.