@datadog/build-plugin
Advanced tools
Comparing version 0.4.5 to 1.0.0
@@ -49,2 +49,12 @@ "use strict"; | ||
}); | ||
test('It should getContext with and without constructor', () => { | ||
const { getContext } = require('../helpers'); | ||
const BasicClass = function BasicClass() { }; | ||
const instance1 = new BasicClass(); | ||
const instance2 = new BasicClass(); | ||
instance2.constructor = null; | ||
expect(() => { | ||
getContext([instance1, instance2]); | ||
}).not.toThrow(); | ||
}); | ||
}); |
@@ -1,5 +0,6 @@ | ||
import { Stats, Report, Compilation, Compiler } from '../../types'; | ||
import { BundlerStats, Stats, Report, Compilation, Compiler } from '../../types'; | ||
export declare const mockStats: Stats; | ||
export declare const mockBundler: BundlerStats; | ||
export declare const mockReport: Report; | ||
export declare const mockCompilation: Compilation; | ||
export declare const mockCompiler: Compiler; |
@@ -28,7 +28,18 @@ "use strict"; | ||
}; | ||
exports.mockBundler = { | ||
webpack: exports.mockStats, | ||
esbuild: { | ||
warnings: [], | ||
errors: [], | ||
entrypoints: [], | ||
duration: 0, | ||
inputs: {}, | ||
outputs: {}, | ||
}, | ||
}; | ||
exports.mockReport = { | ||
timings: { | ||
tapables: {}, | ||
loaders: {}, | ||
modules: {}, | ||
tapables: new Map(), | ||
loaders: new Map(), | ||
modules: new Map(), | ||
}, | ||
@@ -35,0 +46,0 @@ dependencies: {}, |
@@ -1,5 +0,6 @@ | ||
import { Module, Compilation } from './types'; | ||
import { Module, Compilation, Context } from './types'; | ||
export declare const getPluginName: (opts: string | { | ||
name: string; | ||
}) => string; | ||
export declare const formatContext: (context?: string) => string; | ||
export declare const getDisplayName: (name: string, context?: string | undefined) => string; | ||
@@ -12,1 +13,3 @@ export declare const formatModuleName: (name: string, context: string) => string; | ||
export declare const formatDuration: (duration: number) => string; | ||
export declare const writeFile: (filePath: string, content: any) => Promise<unknown>; | ||
export declare const getContext: (args: any[]) => Context[]; |
@@ -6,8 +6,14 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fs_extra_1 = require("fs-extra"); | ||
exports.getPluginName = (opts) => typeof opts === 'string' ? opts : opts.name; | ||
// We want to ensure context ends with a slash. | ||
exports.formatContext = (context = '') => { | ||
return context.endsWith('/') ? context : `${context}/`; | ||
}; | ||
// Format a module name by trimming the user's specific part out. | ||
exports.getDisplayName = (name, context) => { | ||
let toReturn = name; | ||
if (context && name.split(context).pop()) { | ||
toReturn = name.split(context).pop(); | ||
const nameSplit = name.split(exports.formatContext(context)); | ||
if (context && nameSplit.length) { | ||
toReturn = nameSplit.pop(); | ||
} | ||
@@ -19,3 +25,5 @@ return (toReturn | ||
// Remove everything in front of /node_modules | ||
.replace(/(.*)?\/node_modules\//, '/node_modules/')); | ||
.replace(/(.*)?\/node_modules\//, '/node_modules/') | ||
// Remove any prefixing ../ | ||
.replace(/^((\.)*\/)+/, '')); | ||
}; | ||
@@ -28,3 +36,3 @@ exports.formatModuleName = (name, context) => name | ||
// let's do the same so we can integrate better with it. | ||
.replace(context, '.'); | ||
.replace(exports.formatContext(context), './'); | ||
// Find the module name and format it the same way as webpack. | ||
@@ -78,1 +86,17 @@ exports.getModuleName = (module, context, compilation) => { | ||
}; | ||
// Make it so if JSON.stringify fails it rejects the promise and not the whole process. | ||
exports.writeFile = (filePath, content) => { | ||
return new Promise((resolve) => { | ||
return fs_extra_1.outputFile(filePath, JSON.stringify(content, null, 4)).then(resolve); | ||
}); | ||
}; | ||
exports.getContext = (args) => { | ||
return args.map((arg) => { | ||
var _a, _b; | ||
return ({ | ||
type: (_b = (_a = arg === null || arg === void 0 ? void 0 : arg.constructor) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : typeof arg, | ||
name: arg === null || arg === void 0 ? void 0 : arg.name, | ||
value: typeof arg === 'string' ? arg : undefined, | ||
}); | ||
}); | ||
}; |
@@ -6,4 +6,4 @@ "use strict"; | ||
describe('Renderer', () => { | ||
test('It should outputGenerals the same with Webpack 5 and 4', () => { | ||
const { outputGenerals } = require('../renderer'); | ||
test('It should outputWebpack the same with Webpack 5 and 4', () => { | ||
const { outputWebpack } = require('../renderer'); | ||
const ar = [{ name: 'element1' }, { name: 'element2' }]; | ||
@@ -25,4 +25,4 @@ const obj = { obj0: ar[0], obj1: ar[1] }; | ||
const statsWebpack5 = Object.assign(Object.assign({}, statsDefault), { compilation: Object.assign(Object.assign({}, statsDefault.compilation), { emittedAssets: set, modules: set, entries: map, chunks: set }) }); | ||
const outputWebpack4 = outputGenerals(statsWebpack4); | ||
const outputWebpack5 = outputGenerals(statsWebpack5); | ||
const outputWebpack4 = outputWebpack(statsWebpack4); | ||
const outputWebpack5 = outputWebpack(statsWebpack5); | ||
expect(outputWebpack4).toBe(outputWebpack5); | ||
@@ -29,0 +29,0 @@ }); |
@@ -5,145 +5,12 @@ "use strict"; | ||
// Copyright 2019-Present Datadog, Inc. | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const testHelpers_1 = require("../../../__tests__/helpers/testHelpers"); | ||
const path_1 = __importDefault(require("path")); | ||
const aggregator_1 = require("../aggregator"); | ||
const exec = require('util').promisify(require('child_process').exec); | ||
const PROJECTS_ROOT = path_1.default.join(__dirname, '../../../__tests__/mocks/projects'); | ||
describe('Aggregator', () => { | ||
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () { | ||
yield exec(`yarn build`); | ||
}), 20000); | ||
for (const version of [4, 5]) { | ||
describe(`Webpack ${version}`, () => { | ||
let statsJson; | ||
let dependenciesJson; | ||
const WEBPACK_ROOT = path_1.default.join(PROJECTS_ROOT, `./webpack${version}`); | ||
const OUTPUT = path_1.default.join(WEBPACK_ROOT, './webpack-profile-debug/'); | ||
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () { | ||
const output = yield exec(`yarn workspace webpack${version} build`); | ||
// eslint-disable-next-line no-console | ||
console.log(`Build ${version} :`, output.stderr); | ||
statsJson = require(path_1.default.join(OUTPUT, './stats.json')); | ||
dependenciesJson = require(path_1.default.join(OUTPUT, './dependencies.json')); | ||
}), 20000); | ||
test('It should aggregate metrics without throwing.', () => { | ||
const { getMetrics } = require('../aggregator'); | ||
const opts = { context: '', filters: [], tags: [] }; | ||
expect(() => { | ||
getMetrics(testHelpers_1.mockReport, testHelpers_1.mockStats, opts); | ||
}).not.toThrow(); | ||
}); | ||
describe('Modules', () => { | ||
let metrics; | ||
beforeAll(() => { | ||
const indexed = aggregator_1.getIndexed(statsJson, WEBPACK_ROOT); | ||
metrics = aggregator_1.getModules(statsJson, dependenciesJson, indexed, WEBPACK_ROOT); | ||
}); | ||
test('It should give module metrics.', () => { | ||
expect(metrics.length).not.toBe(0); | ||
}); | ||
test(`It should filter out webpack's modules.`, () => { | ||
expect(metrics.find((m) => { | ||
return m.tags.find((t) => /^moduleName:webpack\/runtime/.test(t)); | ||
})).toBeUndefined(); | ||
}); | ||
test(`It should add tags about the entry and the chunk.`, () => { | ||
for (const metric of metrics) { | ||
expect(metric.tags).toContain('entryName:yolo'); | ||
expect(metric.tags).toContain('entryName:cheesecake'); | ||
expect(metric.tags).toContain('chunkName:yolo'); | ||
expect(metric.tags).toContain('chunkName:cheesecake'); | ||
} | ||
}); | ||
test('It should have 3 metrics per module.', () => { | ||
const modules = [ | ||
'./src/file0000.js', | ||
'./src/file0001.js', | ||
'./workspaces/app/file0000.js', | ||
'./workspaces/app/file0001.js', | ||
]; | ||
for (const module of modules) { | ||
const modulesMetrics = metrics.filter((m) => m.tags.includes(`moduleName:${module}`)); | ||
expect(modulesMetrics.length).toBe(3); | ||
} | ||
}); | ||
}); | ||
describe('Entries', () => { | ||
let metrics; | ||
beforeAll(() => { | ||
const indexed = aggregator_1.getIndexed(statsJson, WEBPACK_ROOT); | ||
metrics = aggregator_1.getEntries(statsJson, indexed); | ||
}); | ||
test('It should give entries metrics.', () => { | ||
expect(metrics.length).not.toBe(0); | ||
}); | ||
test('It should give 4 metrics per entry.', () => { | ||
const entries = ['yolo', 'cheesecake']; | ||
for (const entry of entries) { | ||
const entriesMetrics = metrics.filter((m) => m.tags.includes(`entryName:${entry}`)); | ||
expect(entriesMetrics.length).toBe(4); | ||
} | ||
}); | ||
}); | ||
describe('Chunks', () => { | ||
let metrics; | ||
beforeAll(() => { | ||
const indexed = aggregator_1.getIndexed(statsJson, WEBPACK_ROOT); | ||
metrics = aggregator_1.getChunks(statsJson, indexed); | ||
}); | ||
test('It should give chunks metrics.', () => { | ||
expect(metrics.length).not.toBe(0); | ||
}); | ||
test('It should give 2 metrics per chunk.', () => { | ||
const chunks = ['yolo', 'cheesecake']; | ||
for (const chunk of chunks) { | ||
const chunksMetrics = metrics.filter((m) => m.tags.includes(`chunkName:${chunk}`)); | ||
expect(chunksMetrics.length).toBe(2); | ||
} | ||
}); | ||
test(`It should add tags about the entry.`, () => { | ||
for (const metric of metrics) { | ||
expect(metric.tags.join(',')).toMatch(/entryName:(yolo|cheesecake)/); | ||
} | ||
}); | ||
}); | ||
describe('Assets', () => { | ||
let metrics; | ||
beforeAll(() => { | ||
const indexed = aggregator_1.getIndexed(statsJson, WEBPACK_ROOT); | ||
metrics = aggregator_1.getAssets(statsJson, indexed); | ||
}); | ||
test('It should give assets metrics.', () => { | ||
expect(metrics.length).not.toBe(0); | ||
}); | ||
test('It should give 1 metric per asset.', () => { | ||
const assets = ['yolo.js', 'cheesecake.js']; | ||
for (const asset of assets) { | ||
const assetsMetrics = metrics.filter((m) => m.tags.includes(`assetName:${asset}`)); | ||
expect(assetsMetrics.length).toBe(1); | ||
} | ||
}); | ||
test(`It should add tags about the entry and the chunk.`, () => { | ||
for (const metric of metrics) { | ||
expect(metric.tags).toContain('entryName:yolo'); | ||
expect(metric.tags).toContain('entryName:cheesecake'); | ||
expect(metric.tags).toContain('chunkName:yolo'); | ||
expect(metric.tags).toContain('chunkName:cheesecake'); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
test('It should aggregate metrics without throwing.', () => { | ||
const { getMetrics } = require('../aggregator'); | ||
const opts = { context: '', filters: [], tags: [] }; | ||
expect(() => { | ||
getMetrics(opts, testHelpers_1.mockReport, testHelpers_1.mockStats); | ||
}).not.toThrow(); | ||
}); | ||
}); |
@@ -18,2 +18,6 @@ "use strict"; | ||
const buildPluginMock = { | ||
log: (...args) => { | ||
// eslint-disable-next-line no-console | ||
console.log(...args); | ||
}, | ||
options: {}, | ||
@@ -25,3 +29,3 @@ }; | ||
report: testHelpers_1.mockReport, | ||
stats: testHelpers_1.mockStats, | ||
bundler: testHelpers_1.mockBundler, | ||
}); | ||
@@ -28,0 +32,0 @@ expect(typeof obj).toBe('object'); |
@@ -1,18 +0,3 @@ | ||
import { Chunk, Report, StatsJson, Stats, Module, LocalModules, IndexedObject } from '../../types'; | ||
import { Metric, MetricToSend, GetMetricsOptions } from './types'; | ||
export declare const getFromId: (coll: any[], id: string) => any; | ||
export declare const foundInModules: (input: { | ||
modules?: Module[] | undefined; | ||
}, identifier?: string | undefined) => boolean; | ||
export declare const getChunksFromModule: (stats: StatsJson, chunksPerId: { | ||
[key: string]: Chunk; | ||
}, module: Module) => Chunk[]; | ||
export declare const getEntriesFromChunk: (stats: StatsJson, chunk: Chunk, indexed: IndexedObject, parentEntries?: Set<string>, parentChunks?: Set<string>) => Set<string>; | ||
export declare const getEntryTags: (entries: Set<string>) => string[]; | ||
export declare const getChunkTags: (chunks: Chunk[]) => string[]; | ||
export declare const getModules: (stats: StatsJson, dependencies: LocalModules, indexed: IndexedObject, context: string) => Metric[]; | ||
export declare const getChunks: (stats: StatsJson, indexed: IndexedObject) => Metric[]; | ||
export declare const getAssets: (stats: StatsJson, indexed: IndexedObject) => Metric[]; | ||
export declare const getEntries: (stats: StatsJson, indexed: IndexedObject) => Metric[]; | ||
export declare const getIndexed: (stats: StatsJson, context: string) => IndexedObject; | ||
export declare const getMetrics: (report: Report, stats: Stats, opts: GetMetricsOptions) => MetricToSend[]; | ||
import { Report, BundlerStats } from '../../types'; | ||
import { MetricToSend, GetMetricsOptions } from './types'; | ||
export declare const getMetrics: (opts: GetMetricsOptions, report: Report, bundler: BundlerStats) => MetricToSend[]; |
@@ -5,396 +5,53 @@ "use strict"; | ||
// Copyright 2019-Present Datadog, Inc. | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const helpers_1 = require("../../helpers"); | ||
const helpers_2 = require("./helpers"); | ||
const flattened = (arr) => [].concat(...arr); | ||
const getType = (name) => (name.includes('.') ? name.split('.').pop() : 'unknown'); | ||
const getGenerals = (timings, stats) => [ | ||
{ | ||
metric: 'modules.count', | ||
type: 'count', | ||
value: stats.modules.length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'chunks.count', | ||
type: 'count', | ||
value: stats.chunks.length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'assets.count', | ||
type: 'count', | ||
value: stats.assets.length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'plugins.count', | ||
type: 'count', | ||
value: Object.keys(timings.tapables).length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'loaders.count', | ||
type: 'count', | ||
value: Object.keys(timings.loaders).length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'warnings.count', | ||
type: 'count', | ||
value: stats.warnings.length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'errors.count', | ||
type: 'count', | ||
value: stats.errors.length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'entries.count', | ||
type: 'count', | ||
value: Object.keys(stats.entrypoints).length, | ||
tags: [], | ||
}, | ||
{ | ||
metric: 'compilation.duration', | ||
type: 'duration', | ||
value: stats.time, | ||
tags: [], | ||
}, | ||
]; | ||
const getDependencies = (modules) => flattened(modules.map((m) => [ | ||
{ | ||
metric: 'modules.dependencies', | ||
type: 'count', | ||
value: m.dependencies.length, | ||
tags: [`moduleName:${m.name}`, `moduleType:${getType(m.name)}`], | ||
}, | ||
{ | ||
metric: 'modules.dependents', | ||
type: 'count', | ||
value: m.dependents.length, | ||
tags: [`moduleName:${m.name}`, `moduleType:${getType(m.name)}`], | ||
}, | ||
])); | ||
const getPlugins = (plugins) => { | ||
const helpers_1 = require("./helpers"); | ||
const wp = __importStar(require("./metrics/webpack")); | ||
const es = __importStar(require("./metrics/esbuild")); | ||
const common_1 = require("./metrics/common"); | ||
const getWebpackMetrics = (statsJson, opts) => { | ||
const metrics = []; | ||
for (const plugin of Object.values(plugins)) { | ||
let pluginDuration = 0; | ||
let pluginCount = 0; | ||
for (const hook of Object.values(plugin.hooks)) { | ||
let hookDuration = 0; | ||
pluginCount += hook.values.length; | ||
for (const v of hook.values) { | ||
const duration = v.end - v.start; | ||
hookDuration += duration; | ||
pluginDuration += duration; | ||
} | ||
metrics.push({ | ||
metric: 'plugins.hooks.duration', | ||
type: 'duration', | ||
value: hookDuration, | ||
tags: [`pluginName:${plugin.name}`, `hookName:${hook.name}`], | ||
}, { | ||
metric: 'plugins.hooks.increment', | ||
type: 'count', | ||
value: hook.values.length, | ||
tags: [`pluginName:${plugin.name}`, `hookName:${hook.name}`], | ||
}); | ||
} | ||
metrics.push({ | ||
metric: 'plugins.duration', | ||
type: 'duration', | ||
value: pluginDuration, | ||
tags: [`pluginName:${plugin.name}`], | ||
}, { | ||
metric: 'plugins.increment', | ||
type: 'count', | ||
value: pluginCount, | ||
tags: [`pluginName:${plugin.name}`], | ||
}); | ||
} | ||
const indexed = wp.getIndexed(statsJson, opts.context); | ||
metrics.push(...wp.getModules(statsJson, indexed, opts.context)); | ||
metrics.push(...wp.getChunks(statsJson, indexed)); | ||
metrics.push(...wp.getAssets(statsJson, indexed)); | ||
metrics.push(...wp.getEntries(statsJson, indexed)); | ||
return metrics; | ||
}; | ||
const getLoaders = (loaders) => flattened(Object.values(loaders).map((loader) => [ | ||
{ | ||
metric: 'loaders.duration', | ||
type: 'duration', | ||
value: loader.duration, | ||
tags: [`loaderName:${loader.name}`], | ||
}, | ||
{ | ||
metric: 'loaders.increment', | ||
type: 'count', | ||
value: loader.increment, | ||
tags: [`loaderName:${loader.name}`], | ||
}, | ||
])); | ||
// Register the imported tree of a module | ||
const findDependencies = (moduleName, dependencies, moduleDeps = new Set()) => { | ||
if (!dependencies[moduleName]) { | ||
return moduleDeps; | ||
} | ||
for (const dependency of dependencies[moduleName].dependencies) { | ||
if (!moduleDeps.has(dependency)) { | ||
moduleDeps.add(dependency); | ||
findDependencies(dependency, dependencies, moduleDeps); | ||
} | ||
} | ||
return moduleDeps; | ||
const getEsbuildMetrics = (stats, opts) => { | ||
const metrics = []; | ||
const indexed = es.getIndexed(stats, opts.context); | ||
metrics.push(...es.getModules(stats, indexed, opts.context)); | ||
metrics.push(...es.getAssets(stats, indexed, opts.context)); | ||
metrics.push(...es.getEntries(stats, indexed, opts.context)); | ||
return metrics; | ||
}; | ||
exports.getFromId = (coll, id) => coll.find((c) => c.id === id); | ||
exports.foundInModules = (input, identifier) => { | ||
if (!identifier || !input.modules || !input.modules.length) { | ||
return false; | ||
} | ||
return !!input.modules.find((m) => { | ||
if (m.identifier && m.identifier === identifier) { | ||
return true; | ||
// eslint-disable-next-line no-underscore-dangle | ||
exports.getMetrics = (opts, report, bundler) => { | ||
const { timings, dependencies } = report; | ||
const metrics = []; | ||
metrics.push(...common_1.getGenerals(common_1.getGeneralReport(report, bundler))); | ||
if (timings) { | ||
if (timings.tapables) { | ||
metrics.push(...common_1.getPlugins(timings.tapables)); | ||
} | ||
else if (m._identifier && m._identifier === identifier) { | ||
return true; | ||
if (timings.loaders) { | ||
metrics.push(...common_1.getLoaders(timings.loaders)); | ||
} | ||
if (m.modules && m.modules.length) { | ||
return exports.foundInModules(m, identifier); | ||
} | ||
}); | ||
}; | ||
exports.getChunksFromModule = (stats, chunksPerId, module) => { | ||
if (module.chunks.length) { | ||
return module.chunks.map((c) => chunksPerId[c]); | ||
} | ||
// Find the chunks from the chunk list directly. | ||
// Webpack may not have registered module's chunks in some cases. | ||
// eslint-disable-next-line no-underscore-dangle | ||
return stats.chunks.filter((c) => exports.foundInModules(c, module.identifier || module._identifier)); | ||
}; | ||
exports.getEntriesFromChunk = (stats, chunk, indexed, parentEntries = new Set(), parentChunks = new Set()) => { | ||
const entry = indexed.entriesPerChunkId[chunk.id]; | ||
if (entry) { | ||
parentEntries.add(entry.name); | ||
if (dependencies) { | ||
metrics.push(...common_1.getDependencies(Object.values(dependencies))); | ||
} | ||
// Escape cyclic dependencies. | ||
if (parentChunks.has(chunk.id)) { | ||
return parentEntries; | ||
if (bundler.webpack) { | ||
const statsJson = bundler.webpack.toJson({ children: false }); | ||
metrics.push(...getWebpackMetrics(statsJson, opts)); | ||
} | ||
parentChunks.add(chunk.id); | ||
chunk.parents.forEach((p) => { | ||
const parentChunk = indexed.chunksPerId[p]; | ||
if (parentChunk) { | ||
exports.getEntriesFromChunk(stats, parentChunk, indexed, parentEntries, parentChunks); | ||
} | ||
}); | ||
return parentEntries; | ||
}; | ||
exports.getEntryTags = (entries) => Array.from(entries).map((e) => `entryName:${e}`); | ||
exports.getChunkTags = (chunks) => flattened(chunks | ||
.map((c) => { | ||
if (c.names && c.names.length) { | ||
return c.names.map((n) => `chunkName:${n}`); | ||
if (bundler.esbuild) { | ||
metrics.push(...getEsbuildMetrics(bundler.esbuild, opts)); | ||
} | ||
}) | ||
.filter((c) => c)); | ||
const getMetricsFromModule = (stats, dependencies, indexed, context, module) => { | ||
const chunks = exports.getChunksFromModule(stats, indexed.chunksPerId, module); | ||
const entries = new Set(); | ||
for (const chunk of chunks) { | ||
exports.getEntriesFromChunk(stats, chunk, indexed, entries); | ||
} | ||
const chunkTags = exports.getChunkTags(chunks); | ||
const entryTags = exports.getEntryTags(entries); | ||
const moduleName = helpers_1.getDisplayName(module.name, context); | ||
// The reason we have to do two loops over modules. | ||
const tree = Array.from(findDependencies(module.name, dependencies)).map((dependencyName) => indexed.modulesPerName[dependencyName]); | ||
const treeSize = tree.reduce((previous, current) => { | ||
return previous + helpers_1.getModuleSize(current); | ||
}, 0); | ||
return [ | ||
{ | ||
metric: 'modules.size', | ||
type: 'size', | ||
value: module.size, | ||
tags: [ | ||
`moduleName:${moduleName}`, | ||
`moduleType:${getType(moduleName)}`, | ||
...entryTags, | ||
...chunkTags, | ||
], | ||
}, | ||
{ | ||
metric: 'modules.tree.size', | ||
type: 'size', | ||
value: treeSize, | ||
tags: [ | ||
`moduleName:${moduleName}`, | ||
`moduleType:${getType(moduleName)}`, | ||
...entryTags, | ||
...chunkTags, | ||
], | ||
}, | ||
{ | ||
metric: 'modules.tree.count', | ||
type: 'count', | ||
value: tree.length, | ||
tags: [ | ||
`moduleName:${moduleName}`, | ||
`moduleType:${getType(moduleName)}`, | ||
...entryTags, | ||
...chunkTags, | ||
], | ||
}, | ||
]; | ||
}; | ||
exports.getModules = (stats, dependencies, indexed, context) => { | ||
return flattened(Object.values(indexed.modulesPerName).map((module) => { | ||
return getMetricsFromModule(stats, dependencies, indexed, context, module); | ||
})); | ||
}; | ||
// Find in entries.chunks | ||
exports.getChunks = (stats, indexed) => { | ||
const chunks = stats.chunks; | ||
return flattened(chunks.map((chunk) => { | ||
const entryTags = exports.getEntryTags(exports.getEntriesFromChunk(stats, chunk, indexed)); | ||
const chunkName = chunk.names.length ? chunk.names.join(' ') : chunk.id; | ||
return [ | ||
{ | ||
metric: 'chunks.size', | ||
type: 'size', | ||
value: chunk.size, | ||
tags: [`chunkName:${chunkName}`, ...entryTags], | ||
}, | ||
{ | ||
metric: 'chunks.modules.count', | ||
type: 'count', | ||
value: chunk.modules.length, | ||
tags: [`chunkName:${chunkName}`, ...entryTags], | ||
}, | ||
]; | ||
})); | ||
}; | ||
exports.getAssets = (stats, indexed) => { | ||
const assets = stats.assets; | ||
return assets.map((asset) => { | ||
const chunks = asset.chunks.map((c) => indexed.chunksPerId[c]); | ||
const entries = new Set(); | ||
for (const chunk of chunks) { | ||
exports.getEntriesFromChunk(stats, chunk, indexed, entries); | ||
} | ||
const chunkTags = exports.getChunkTags(chunks); | ||
const entryTags = exports.getEntryTags(entries); | ||
const assetName = asset.name; | ||
return { | ||
metric: 'assets.size', | ||
type: 'size', | ||
value: asset.size, | ||
tags: [ | ||
`assetName:${assetName}`, | ||
`assetType:${getType(assetName)}`, | ||
...chunkTags, | ||
...entryTags, | ||
], | ||
}; | ||
}); | ||
}; | ||
exports.getEntries = (stats, indexed) => flattened(Object.keys(stats.entrypoints).map((entryName) => { | ||
const entry = stats.entrypoints[entryName]; | ||
const chunks = entry.chunks.map((chunkId) => indexed.chunksPerId[chunkId]); | ||
let size = 0; | ||
let moduleCount = 0; | ||
let assetsCount = 0; | ||
for (const chunk of chunks) { | ||
size += chunk.size; | ||
moduleCount += chunk.modules.length; | ||
assetsCount += chunk.files.length; | ||
} | ||
return [ | ||
{ | ||
metric: 'entries.size', | ||
type: 'size', | ||
value: size, | ||
tags: [`entryName:${entryName}`], | ||
}, | ||
{ | ||
metric: 'entries.chunks.count', | ||
type: 'count', | ||
value: chunks.length, | ||
tags: [`entryName:${entryName}`], | ||
}, | ||
{ | ||
metric: 'entries.modules.count', | ||
type: 'count', | ||
value: moduleCount, | ||
tags: [`entryName:${entryName}`], | ||
}, | ||
{ | ||
metric: 'entries.assets.count', | ||
type: 'count', | ||
value: assetsCount, | ||
tags: [`entryName:${entryName}`], | ||
}, | ||
]; | ||
})); | ||
exports.getIndexed = (stats, context) => { | ||
// Gather all modules. | ||
const modulesPerName = {}; | ||
const chunksPerId = {}; | ||
const entriesPerChunkId = {}; | ||
const addModule = (module) => { | ||
// console.log('Add Module', module.name); | ||
// No internals. | ||
if (/^webpack\/runtime/.test(module.name)) { | ||
return; | ||
} | ||
// No duplicates. | ||
if (modulesPerName[helpers_1.formatModuleName(module.name, context)]) { | ||
return; | ||
} | ||
// Modules are sometimes registered with their loader. | ||
if (module.name.includes('!')) { | ||
return; | ||
} | ||
modulesPerName[helpers_1.formatModuleName(module.name, context)] = module; | ||
}; | ||
for (const [name, entry] of Object.entries(stats.entrypoints)) { | ||
// In webpack4 we don't have the name of the entry here. | ||
entry.name = name; | ||
for (const chunkId of entry.chunks) { | ||
entriesPerChunkId[chunkId] = entry; | ||
} | ||
} | ||
for (const chunk of stats.chunks) { | ||
chunksPerId[chunk.id] = chunk; | ||
} | ||
for (const module of stats.modules) { | ||
// Sometimes modules are grouped together. | ||
if (module.modules && module.modules.length) { | ||
for (const moduleIn of module.modules) { | ||
addModule(moduleIn); | ||
} | ||
} | ||
else { | ||
addModule(module); | ||
} | ||
} | ||
return { | ||
modulesPerName, | ||
chunksPerId, | ||
entriesPerChunkId, | ||
}; | ||
}; | ||
exports.getMetrics = (report, stats, opts) => { | ||
const statsJson = stats.toJson({ children: false }); | ||
const { timings, dependencies } = report; | ||
const metrics = []; | ||
const indexed = exports.getIndexed(statsJson, opts.context); | ||
metrics.push(...getGenerals(timings, statsJson)); | ||
metrics.push(...getDependencies(Object.values(dependencies))); | ||
metrics.push(...getPlugins(timings.tapables)); | ||
metrics.push(...getLoaders(timings.loaders)); | ||
metrics.push(...exports.getModules(statsJson, dependencies, indexed, opts.context)); | ||
metrics.push(...exports.getChunks(statsJson, indexed)); | ||
metrics.push(...exports.getAssets(statsJson, indexed)); | ||
metrics.push(...exports.getEntries(statsJson, indexed)); | ||
// Format metrics to be DD ready and apply filters | ||
@@ -412,3 +69,3 @@ const metricsToSend = metrics | ||
} | ||
return metric ? helpers_2.getMetric(metric, opts) : null; | ||
return metric ? helpers_1.getMetric(metric, opts) : null; | ||
}) | ||
@@ -415,0 +72,0 @@ .filter((m) => m !== null); |
import { Metric, Options, MetricToSend } from './types'; | ||
export declare const defaultFilters: ((metric: Metric) => Metric | null)[]; | ||
export declare const getMetric: (metric: Metric, opts: Options) => MetricToSend; | ||
export declare const flattened: (arr: any[]) => never[]; | ||
export declare const getType: (name: string) => string | undefined; |
@@ -58,1 +58,3 @@ "use strict"; | ||
}); | ||
exports.flattened = (arr) => [].concat(...arr); | ||
exports.getType = (name) => (name.includes('.') ? name.split('.').pop() : 'unknown'); |
import { BuildPlugin } from '../../webpack'; | ||
import { DDHooksContext, MetricToSend } from './types'; | ||
export declare const hooks: { | ||
preoutput: (this: BuildPlugin, { report, stats }: DDHooksContext) => Promise<{ | ||
preoutput: (this: BuildPlugin, { report, bundler }: DDHooksContext) => Promise<{ | ||
metrics: MetricToSend[]; | ||
@@ -6,0 +6,0 @@ }>; |
@@ -31,3 +31,3 @@ "use strict"; | ||
}); | ||
const preoutput = function output({ report, stats }) { | ||
const preoutput = function output({ report, bundler }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -37,6 +37,6 @@ const optionsDD = getOptionsDD(this.options.datadog); | ||
try { | ||
metrics = aggregator_1.getMetrics(report, stats, Object.assign(Object.assign({}, optionsDD), { context: this.options.context })); | ||
metrics = aggregator_1.getMetrics(Object.assign(Object.assign({}, optionsDD), { context: this.options.context }), report, bundler); | ||
} | ||
catch (e) { | ||
this.log(`Couldn't aggregate metrics. ${e.toString()}`, 'error'); | ||
this.log(`Couldn't aggregate metrics: ${e.stack}`, 'error'); | ||
} | ||
@@ -48,3 +48,3 @@ return { metrics }; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const PLUGIN_NAME = this.constructor.name; | ||
const PLUGIN_NAME = this.name; | ||
const duration = Date.now() - start; | ||
@@ -51,0 +51,0 @@ const optionsDD = getOptionsDD(this.options.datadog); |
import { HooksContext } from '../types'; | ||
import { BuildPlugin } from '../webpack'; | ||
export declare const hooks: { | ||
output: (this: BuildPlugin, { report, metrics, stats }: HooksContext) => Promise<void>; | ||
output: (this: BuildPlugin, { report, metrics, bundler }: HooksContext) => Promise<void>; | ||
}; |
@@ -19,11 +19,4 @@ "use strict"; | ||
const path_1 = __importDefault(require("path")); | ||
const fs_extra_1 = require("fs-extra"); | ||
const helpers_1 = require("../helpers"); | ||
// Make it there so if JSON.stringify fails it rejects the promise and not the whole process. | ||
const writeFile = (filePath, content) => { | ||
return new Promise((resolve) => { | ||
return fs_extra_1.outputFile(filePath, JSON.stringify(content, null, 4)).then(resolve); | ||
}); | ||
}; | ||
const output = function output({ report, metrics, stats }) { | ||
const output = function output({ report, metrics, bundler }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -37,4 +30,5 @@ const opts = this.options.output; | ||
dependencies: true, | ||
stats: true, | ||
bundler: true, | ||
metrics: true, | ||
result: true, | ||
}; | ||
@@ -45,3 +39,3 @@ if (typeof opts === 'object') { | ||
files.dependencies = opts.dependencies || false; | ||
files.stats = opts.stats || false; | ||
files.bundler = opts.bundlerStats || false; | ||
files.metrics = opts.metrics || false; | ||
@@ -56,16 +50,27 @@ } | ||
const filesToWrite = {}; | ||
if (files.timings) { | ||
if (files.timings && (report === null || report === void 0 ? void 0 : report.timings)) { | ||
filesToWrite.timings = { | ||
content: { | ||
tapables: report.timings.tapables, | ||
loaders: report.timings.loaders, | ||
modules: report.timings.modules, | ||
tapables: report.timings.tapables | ||
? Array.from(report.timings.tapables.values()) | ||
: null, | ||
loaders: report.timings.loaders | ||
? Array.from(report.timings.loaders.values()) | ||
: null, | ||
modules: report.timings.modules | ||
? Array.from(report.timings.modules.values()) | ||
: null, | ||
}, | ||
}; | ||
} | ||
if (files.dependencies) { | ||
if (files.dependencies && (report === null || report === void 0 ? void 0 : report.dependencies)) { | ||
filesToWrite.dependencies = { content: report.dependencies }; | ||
} | ||
if (files.stats) { | ||
filesToWrite.stats = { content: stats.toJson({ children: false }) }; | ||
if (files.bundler) { | ||
if (bundler.webpack) { | ||
filesToWrite.bundler = { content: bundler.webpack.toJson({ children: false }) }; | ||
} | ||
if (bundler.esbuild) { | ||
filesToWrite.bundler = { content: bundler.esbuild }; | ||
} | ||
} | ||
@@ -78,3 +83,3 @@ if (metrics && files.metrics) { | ||
this.log(`Start writing ${file}.json.`); | ||
return writeFile(path_1.default.join(outputPath, `${file}.json`), filesToWrite[file].content) | ||
return helpers_1.writeFile(path_1.default.join(outputPath, `${file}.json`), filesToWrite[file].content) | ||
.then(() => { | ||
@@ -81,0 +86,0 @@ this.log(`Wrote ${file}.json in ${helpers_1.formatDuration(Date.now() - start)}`); |
import { BuildPlugin } from '../webpack'; | ||
import { HooksContext, Stats } from '../types'; | ||
export declare const outputGenerals: (stats: Stats) => void; | ||
import { HooksContext, Stats, EsbuildStats } from '../types'; | ||
export declare const outputWebpack: (stats: Stats) => void; | ||
export declare const outputEsbuild: (stats: EsbuildStats) => void; | ||
export declare const hooks: { | ||
output(this: BuildPlugin, { report, stats }: HooksContext): Promise<void>; | ||
output(this: BuildPlugin, { report, bundler }: HooksContext): Promise<void>; | ||
}; |
@@ -20,2 +20,3 @@ "use strict"; | ||
const chalk_1 = __importDefault(require("chalk")); | ||
const pretty_bytes_1 = __importDefault(require("pretty-bytes")); | ||
const helpers_1 = require("../helpers"); | ||
@@ -53,11 +54,21 @@ const TOP = 5; | ||
const outputTapables = (timings) => { | ||
const times = Object.values(timings); | ||
// Sort by time, longest first | ||
times.sort(sortDesc('duration')); | ||
if (!timings) { | ||
return; | ||
} | ||
const times = Array.from(timings.values()); | ||
if (!times.length) { | ||
return; | ||
} | ||
// Output | ||
console.log('\n===== Tapables ====='); | ||
console.log(`\n=== Top ${TOP} duration ===`); | ||
// Sort by time, longest first | ||
times.sort(sortDesc('duration')); | ||
render(times, (time) => helpers_1.formatDuration(time.duration)); | ||
console.log(`\n=== Top ${TOP} hits ===`); | ||
// Sort by time, longest first | ||
times.sort(sortDesc('increment')); | ||
render(times, (plugin) => plugin.increment); | ||
}; | ||
exports.outputGenerals = (stats) => { | ||
exports.outputWebpack = (stats) => { | ||
console.log('\n===== General ====='); | ||
@@ -87,32 +98,77 @@ // More general stuffs. | ||
}; | ||
const outputLoaders = (times) => { | ||
// Sort by time, longest first | ||
const loadersPerTime = Object.values(times).sort(sortDesc('duration')); | ||
// Sort by hits, biggest first | ||
const loadersPerIncrement = Object.values(times).sort(sortDesc('increment')); | ||
exports.outputEsbuild = (stats) => { | ||
console.log('\n===== General ====='); | ||
const nbDeps = stats.inputs ? Object.keys(stats.inputs).length : 0; | ||
const nbFiles = stats.outputs ? Object.keys(stats.outputs).length : 0; | ||
const nbWarnings = stats.warnings.length; | ||
const nbErrors = stats.errors.length; | ||
const nbEntries = stats.entrypoints ? Object.keys(stats.entrypoints).length : 0; | ||
console.log(` | ||
nbDeps: ${chalk_1.default.bold(nbDeps.toString())} | ||
nbFiles: ${chalk_1.default.bold(nbFiles.toString())} | ||
nbWarnings: ${chalk_1.default.bold(nbWarnings.toString())} | ||
nbErrors: ${chalk_1.default.bold(nbErrors.toString())} | ||
nbEntries: ${chalk_1.default.bold(nbEntries.toString())} | ||
`); | ||
}; | ||
const outputLoaders = (timings) => { | ||
if (!timings) { | ||
return; | ||
} | ||
const times = Array.from(timings.values()); | ||
if (!times.length) { | ||
return; | ||
} | ||
// Output | ||
console.log('\n===== Loaders ====='); | ||
console.log(`\n=== Top ${TOP} duration ===`); | ||
render(loadersPerTime, (loader) => helpers_1.formatDuration(loader.duration)); | ||
// Sort by time, longest first | ||
times.sort(sortDesc('duration')); | ||
render(times, (loader) => helpers_1.formatDuration(loader.duration)); | ||
console.log(`\n=== Top ${TOP} hits ===`); | ||
render(loadersPerIncrement, (loader) => loader.increment); | ||
// Sort by hits, biggest first | ||
times.sort(sortDesc('increment')); | ||
render(times, (loader) => loader.increment); | ||
}; | ||
const outputModules = (times, deps) => { | ||
// Sort by dependents, biggest first | ||
const modulesPerDependents = Object.values(deps).sort(sortDesc((mod) => mod.dependents.length)); | ||
// Sort by dependencies, biggest first | ||
const modulesPerDepencies = Object.values(deps).sort(sortDesc((mod) => mod.dependencies.length)); | ||
// Sort by duration, longest first | ||
const modulesPerTime = Object.values(times).sort(sortDesc('duration')); | ||
// Output | ||
console.log('\n===== Modules ====='); | ||
console.log(`\n=== Top ${TOP} dependents ===`); | ||
render(modulesPerDependents, (module) => module.dependents.length); | ||
console.log(`\n=== Top ${TOP} dependencies ===`); | ||
render(modulesPerDepencies, (module) => module.dependencies.length); | ||
console.log(`\n=== Top ${TOP} duration ===`); | ||
render(modulesPerTime, (module) => helpers_1.formatDuration(module.duration)); | ||
const outputModules = (deps, timings) => { | ||
if (!deps && !timings) { | ||
return; | ||
} | ||
if (deps) { | ||
const dependencies = Object.values(deps); | ||
if (!dependencies.length) { | ||
return; | ||
} | ||
console.log('\n===== Modules ====='); | ||
// Sort by dependents, biggest first | ||
dependencies.sort(sortDesc((mod) => mod.dependents.length)); | ||
console.log(`\n=== Top ${TOP} dependents ===`); | ||
render(dependencies, (module) => module.dependents.length); | ||
// Sort by dependencies, biggest first | ||
dependencies.sort(sortDesc((mod) => mod.dependencies.length)); | ||
console.log(`\n=== Top ${TOP} dependencies ===`); | ||
render(dependencies, (module) => module.dependencies.length); | ||
// Sort by size, biggest first | ||
dependencies.sort(sortDesc('size')); | ||
console.log(`\n=== Top ${TOP} size ===`); | ||
render(dependencies, (module) => pretty_bytes_1.default(module.size)); | ||
} | ||
if (timings) { | ||
const times = Array.from(timings.values()); | ||
if (!times.length) { | ||
return; | ||
} | ||
console.log('\n===== Modules ====='); | ||
// Sort by duration, longest first | ||
times.sort(sortDesc('duration')); | ||
console.log(`\n=== Top ${TOP} duration ===`); | ||
render(times, (module) => helpers_1.formatDuration(module.duration)); | ||
// Sort by increment, longest first | ||
times.sort(sortDesc('increment')); | ||
console.log(`\n=== Top ${TOP} hits ===`); | ||
render(times, (module) => module.increment); | ||
} | ||
}; | ||
exports.hooks = { | ||
output({ report, stats }) { | ||
output({ report, bundler }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -122,8 +178,16 @@ if (this.options.output === false) { | ||
} | ||
outputTapables(report.timings.tapables); | ||
outputLoaders(report.timings.loaders); | ||
outputModules(report.timings.modules, report.dependencies); | ||
exports.outputGenerals(stats); | ||
if (report) { | ||
outputTapables(report.timings.tapables); | ||
outputLoaders(report.timings.loaders); | ||
outputModules(report.dependencies, report.timings.modules); | ||
} | ||
if (bundler.webpack) { | ||
exports.outputWebpack(bundler.webpack); | ||
} | ||
if (bundler.esbuild) { | ||
exports.outputEsbuild(bundler.esbuild); | ||
} | ||
console.log(); | ||
}); | ||
}, | ||
}; |
@@ -0,1 +1,3 @@ | ||
import { Metafile, Message, BuildOptions } from 'esbuild'; | ||
import { MetricToSend } from './hooks/datadog/types'; | ||
export declare type HOOKS = 'output'; | ||
@@ -8,3 +10,12 @@ export declare type WRAPPED_HOOKS = 'preoutput' | 'output' | 'postoutput'; | ||
} | ||
export interface IndexedObject { | ||
export interface EsbuildIndexedObject { | ||
entryNames: Map<string, string>; | ||
inputsDependencies: { | ||
[key: string]: Set<string>; | ||
}; | ||
outputsDependencies: { | ||
[key: string]: Set<string>; | ||
}; | ||
} | ||
export interface WebpackIndexedObject { | ||
modulesPerName: { | ||
@@ -36,3 +47,3 @@ [key: string]: Module; | ||
dependencies?: boolean; | ||
stats?: boolean; | ||
bundlerStats?: boolean; | ||
metrics?: boolean; | ||
@@ -143,5 +154,5 @@ }; | ||
export interface TimingsReport { | ||
tapables: TapableTimings; | ||
loaders: ResultLoaders; | ||
modules: ResultModules; | ||
tapables?: TimingsMap; | ||
loaders?: TimingsMap; | ||
modules?: TimingsMap; | ||
} | ||
@@ -152,7 +163,17 @@ export interface Report { | ||
} | ||
export interface EsbuildStats extends Metafile { | ||
warnings: Message[]; | ||
errors: Message[]; | ||
entrypoints: BuildOptions['entryPoints']; | ||
duration: number; | ||
} | ||
export interface BundlerStats { | ||
webpack?: Stats; | ||
esbuild?: EsbuildStats; | ||
} | ||
export interface HooksContext { | ||
start: number; | ||
report: Report; | ||
stats: Stats; | ||
[key: string]: any; | ||
bundler: BundlerStats; | ||
metrics?: MetricToSend[]; | ||
} | ||
@@ -168,9 +189,10 @@ export interface Context { | ||
duration: number; | ||
context: Context[]; | ||
type: TAP_TYPES; | ||
context?: Context[]; | ||
type?: TAP_TYPES; | ||
} | ||
export interface TapableTiming { | ||
export interface Timing { | ||
name: string; | ||
duration?: number; | ||
hooks: { | ||
duration: number; | ||
increment: number; | ||
events: { | ||
[key: string]: { | ||
@@ -182,5 +204,3 @@ name: string; | ||
} | ||
export interface TapableTimings { | ||
[key: string]: TapableTiming; | ||
} | ||
export declare type TimingsMap = Map<string, Timing>; | ||
export interface MonitoredTaps { | ||
@@ -193,3 +213,3 @@ [key: string]: any; | ||
hooks: Hooks; | ||
timings: TapableTimings; | ||
timings: TimingsMap; | ||
} | ||
@@ -227,34 +247,5 @@ export declare type TapAsync = (...args: any[]) => void; | ||
module: string; | ||
timings: { | ||
start: number; | ||
end?: number; | ||
}; | ||
timings: Value; | ||
loaders: string[]; | ||
} | ||
export interface ResultModuleEvent { | ||
name: string; | ||
start: number; | ||
end?: number; | ||
} | ||
export interface ResultModule { | ||
name: string; | ||
increment: number; | ||
duration: number; | ||
loaders: ResultModuleEvent[]; | ||
} | ||
export interface ResultLoader { | ||
name: string; | ||
increment: number; | ||
duration: number; | ||
} | ||
export interface ResultModules { | ||
[key: string]: ResultModule; | ||
} | ||
export interface ResultLoaders { | ||
[key: string]: ResultLoader; | ||
} | ||
export interface LoadersResult { | ||
modules: ResultModules; | ||
loaders: ResultLoaders; | ||
} | ||
export interface LocalModule { | ||
@@ -261,0 +252,0 @@ name: string; |
{ | ||
"name": "@datadog/build-plugin", | ||
"version": "0.4.5", | ||
"version": "1.0.0", | ||
"license": "MIT", | ||
@@ -22,3 +22,3 @@ "author": "Datadog", | ||
"format": "yarn lint --fix", | ||
"lint": "eslint -c ./.eslintrc.js ./**/*.ts", | ||
"lint": "eslint -c ./.eslintrc.js ./**/*.ts --quiet", | ||
"oss": "yarn cli oss -d bin -d src -l mit", | ||
@@ -33,3 +33,3 @@ "prepack": "yarn build", | ||
"fs-extra": "7.0.1", | ||
"webpack": "5.49.0" | ||
"pretty-bytes": "5.6.0" | ||
}, | ||
@@ -50,2 +50,3 @@ "husky": { | ||
"clipanion": "2.6.0", | ||
"esbuild": "0.12.26", | ||
"eslint": "6.8.0", | ||
@@ -62,8 +63,10 @@ "eslint-config-prettier": "6.11.0", | ||
"prettier": "2.0.5", | ||
"ts-jest": "^26.1.0", | ||
"typescript": "3.8.3" | ||
"ts-jest": "26.1.0", | ||
"typescript": "3.8.3", | ||
"webpack": "5.49.0" | ||
}, | ||
"peerDependencies": { | ||
"esbuild": "*", | ||
"webpack": "*" | ||
} | ||
} |
@@ -13,3 +13,3 @@ # Build plugin <!-- omit in toc --> | ||
- This is a bundler plugin (webpack for now, others to come...). | ||
- This is a bundler plugin (webpack and esbuild for now). | ||
- It monitors plugins, loaders, hooks, dependencies, modules, chunks, ... | ||
@@ -29,2 +29,4 @@ - It doesn't add runtime. | ||
- [Usage](#usage) | ||
- [Webpack](#webpack) | ||
- [Esbuild](#esbuild) | ||
- [Configuration](#configuration) | ||
@@ -64,2 +66,4 @@ - [`disabled`](#disabled) | ||
### Webpack | ||
Inside your `webpack.config.js`. | ||
@@ -77,2 +81,19 @@ | ||
### Esbuild | ||
Add the plugin to your esbuild configuration. | ||
```js | ||
const esbuild = require('esbuild'); | ||
const { BuildPlugin } = require('@datadog/build-plugin/dist/esbuild'); | ||
esbuild.build({ | ||
[...] // All your configuration needs | ||
plugins: [ | ||
[...] // All your plugins | ||
BuildPlugin() | ||
] | ||
}) | ||
``` | ||
## Configuration | ||
@@ -97,3 +118,3 @@ | ||
- `metrics.json`: an array of all the metrics that would be sent to Datadog. | ||
- `stats.json`: the `stats` object of webpack. | ||
- `bundler.json`: some 'stats' from your bundler. | ||
- `timings.json`: timing data for modules, loaders and plugins. | ||
@@ -109,2 +130,3 @@ | ||
``` | ||
To only output a specified file. | ||
@@ -111,0 +133,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
135820
75
3156
1
259
5
23
10
4
+ Addedpretty-bytes@5.6.0
+ Added@esbuild/aix-ppc64@0.24.0(transitive)
+ Added@esbuild/android-arm@0.24.0(transitive)
+ Added@esbuild/android-arm64@0.24.0(transitive)
+ Added@esbuild/android-x64@0.24.0(transitive)
+ Added@esbuild/darwin-arm64@0.24.0(transitive)
+ Added@esbuild/darwin-x64@0.24.0(transitive)
+ Added@esbuild/freebsd-arm64@0.24.0(transitive)
+ Added@esbuild/freebsd-x64@0.24.0(transitive)
+ Added@esbuild/linux-arm@0.24.0(transitive)
+ Added@esbuild/linux-arm64@0.24.0(transitive)
+ Added@esbuild/linux-ia32@0.24.0(transitive)
+ Added@esbuild/linux-loong64@0.24.0(transitive)
+ Added@esbuild/linux-mips64el@0.24.0(transitive)
+ Added@esbuild/linux-ppc64@0.24.0(transitive)
+ Added@esbuild/linux-riscv64@0.24.0(transitive)
+ Added@esbuild/linux-s390x@0.24.0(transitive)
+ Added@esbuild/linux-x64@0.24.0(transitive)
+ Added@esbuild/netbsd-x64@0.24.0(transitive)
+ Added@esbuild/openbsd-arm64@0.24.0(transitive)
+ Added@esbuild/openbsd-x64@0.24.0(transitive)
+ Added@esbuild/sunos-x64@0.24.0(transitive)
+ Added@esbuild/win32-arm64@0.24.0(transitive)
+ Added@esbuild/win32-ia32@0.24.0(transitive)
+ Added@esbuild/win32-x64@0.24.0(transitive)
+ Added@types/estree@1.0.6(transitive)
+ Added@webassemblyjs/ast@1.12.1(transitive)
+ Added@webassemblyjs/floating-point-hex-parser@1.11.6(transitive)
+ Added@webassemblyjs/helper-api-error@1.11.6(transitive)
+ Added@webassemblyjs/helper-buffer@1.12.1(transitive)
+ Added@webassemblyjs/helper-numbers@1.11.6(transitive)
+ Added@webassemblyjs/helper-wasm-bytecode@1.11.6(transitive)
+ Added@webassemblyjs/helper-wasm-section@1.12.1(transitive)
+ Added@webassemblyjs/ieee754@1.11.6(transitive)
+ Added@webassemblyjs/leb128@1.11.6(transitive)
+ Added@webassemblyjs/utf8@1.11.6(transitive)
+ Added@webassemblyjs/wasm-edit@1.12.1(transitive)
+ Added@webassemblyjs/wasm-gen@1.12.1(transitive)
+ Added@webassemblyjs/wasm-opt@1.12.1(transitive)
+ Added@webassemblyjs/wasm-parser@1.12.1(transitive)
+ Added@webassemblyjs/wast-printer@1.12.1(transitive)
+ Addedacorn-import-attributes@1.9.5(transitive)
+ Addedes-module-lexer@1.5.4(transitive)
+ Addedesbuild@0.24.0(transitive)
+ Addedjson-parse-even-better-errors@2.3.1(transitive)
+ Addedpretty-bytes@5.6.0(transitive)
+ Addedwebpack@5.95.0(transitive)
- Removedwebpack@5.49.0
- Removed@types/eslint@9.6.1(transitive)
- Removed@types/eslint-scope@3.7.7(transitive)
- Removed@types/estree@0.0.50(transitive)
- Removed@webassemblyjs/ast@1.11.1(transitive)
- Removed@webassemblyjs/floating-point-hex-parser@1.11.1(transitive)
- Removed@webassemblyjs/helper-api-error@1.11.1(transitive)
- Removed@webassemblyjs/helper-buffer@1.11.1(transitive)
- Removed@webassemblyjs/helper-numbers@1.11.1(transitive)
- Removed@webassemblyjs/helper-wasm-bytecode@1.11.1(transitive)
- Removed@webassemblyjs/helper-wasm-section@1.11.1(transitive)
- Removed@webassemblyjs/ieee754@1.11.1(transitive)
- Removed@webassemblyjs/leb128@1.11.1(transitive)
- Removed@webassemblyjs/utf8@1.11.1(transitive)
- Removed@webassemblyjs/wasm-edit@1.11.1(transitive)
- Removed@webassemblyjs/wasm-gen@1.11.1(transitive)
- Removed@webassemblyjs/wasm-opt@1.11.1(transitive)
- Removed@webassemblyjs/wasm-parser@1.11.1(transitive)
- Removed@webassemblyjs/wast-printer@1.11.1(transitive)
- Removedacorn-import-assertions@1.9.0(transitive)
- Removedes-module-lexer@0.7.1(transitive)
- Removedjson-parse-better-errors@1.0.2(transitive)
- Removedwebpack@5.49.0(transitive)