Skip to content

Commit

Permalink
feat(telemetry): add EVENT_BUILD_FEATURE_USAGE and EVENT_PACKAGE_DETE…
Browse files Browse the repository at this point in the history
…CTED (#477)
  • Loading branch information
harrytothemoon authored Dec 13, 2022
1 parent 907865f commit 616b44d
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 38 deletions.
3 changes: 2 additions & 1 deletion packages/service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"send": "0.17.1",
"ws": "8.2.3",
"dotenv": "16.0.3",
"dotenv-expand": "9.0.0"
"dotenv-expand": "9.0.0",
"find-up": "4.1.0"
},
"devDependencies": {
"@babel/types": "7.12.11",
Expand Down
154 changes: 154 additions & 0 deletions packages/service/src/analysis/events/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import findUp from 'find-up';

const REGEXP_DIRECTORY_TESTS = /[\\/]__(tests|mocks)__[\\/]/i;
const REGEXP_FILE_TEST = /\.(?:spec|test)\.[^.]+$/i;

const EVENT_BUILD_OPTIMIZED = 'SHUVI_BUILD_OPTIMIZED';
type EventBuildOptimized = {
durationInSeconds: number;
totalPageCount: number;
hasTestPages: boolean;
hasStatic404: boolean;
middlewareCount: number;
totalLoaderCount: number;
useTypeScript: boolean;
};

export function eventBuildOptimize(
routePaths: string[],
event: Omit<EventBuildOptimized, 'totalPageCount' | 'hasTestPages'>
): {
eventName: string;
payload: EventBuildOptimized;
} {
return {
eventName: EVENT_BUILD_OPTIMIZED,
payload: {
...event,
totalPageCount: routePaths.filter(path => /page\..*/i.test(path)).length,
hasTestPages: routePaths.some(
path => REGEXP_DIRECTORY_TESTS.test(path) || REGEXP_FILE_TEST.test(path)
)
}
};
}

const EVENT_PACKAGE_DETECTED = 'SHUVI_PACKAGE_DETECTED';
type EventPackageDetected = {
eventName: string;
payload: {
packageName: string;
packageVersion: string;
};
};

export async function eventPackageDetected(
dir: string
): Promise<Array<EventPackageDetected>> {
try {
const packageJsonPath = await findUp('package.json', { cwd: dir });
if (!packageJsonPath) {
return [];
}

const {
dependencies = {},
devDependencies = {}
} = require(packageJsonPath);

const deps = { ...devDependencies, ...dependencies };

return Object.keys(deps).reduce(
(
events: EventPackageDetected[],
plugin: string
): EventPackageDetected[] => {
const version = deps[plugin];
// Don't add deps without a version set
if (!version) {
return events;
}

events.push({
eventName: EVENT_PACKAGE_DETECTED,
payload: {
packageName: plugin,
packageVersion: version
}
});

return events;
},
[]
);
} catch (_) {
return [];
}
}

export const EVENT_BUILD_FEATURE_USAGE = 'SHUVI_BUILD_FEATURE_USAGE';

export type Feature =
| 'shuvi/lightningCss'
| 'shuvi/webpack-dll'
| 'swcPlugins'
| 'swcModularizeImports'
| 'swcRemoveConsole'
| 'swcReactRemoveProperties'
| 'swcJsxImportSource'
| 'swcStyledComponents'
| 'swcEmotion'
| 'swcExperimentalDecorators'
| 'swcEmitDecoratorMetadata';

const BUILD_FEATURES: Array<Feature> = [
'shuvi/lightningCss',
'shuvi/webpack-dll',
'swcPlugins',
'swcModularizeImports',
'swcRemoveConsole',
'swcReactRemoveProperties',
'swcJsxImportSource',
'swcStyledComponents',
'swcEmotion',
'swcExperimentalDecorators',
'swcEmitDecoratorMetadata'
];

export type EventBuildFeatureUsage = {
featureName: Feature;
invocationCount: number;
};

export function eventBuildFeatureUsage({
compiler,
experimental
}: {
compiler: Record<string, any> | undefined;
experimental: Record<string, any> | undefined;
}): Array<{ eventName: string; payload: EventBuildFeatureUsage }> {
const buildFeaturesMap = new Map(
[
['shuvi/lightningCss', !!experimental?.lightningCss],
['shuvi/webpack-dll', !!experimental?.preBundle],
['swcPlugins', !!experimental?.swcPlugins?.length],
['swcModularizeImports', !!experimental?.modularizeImports],
['swcRemoveConsole', !!compiler?.removeConsole],
['swcReactRemoveProperties', !!compiler?.reactRemoveProperties],
['swcJsxImportSource', !!compiler?.jsxImportSource],
['swcStyledComponents', !!compiler?.styledComponents],
['swcEmotion', !!compiler?.emotion],
['swcExperimentalDecorators', !!compiler?.experimentalDecorators],
['swcEmitDecoratorMetadata', !!compiler?.emitDecoratorMetadata]
].filter<[Feature, boolean]>(Boolean as any)
);
return BUILD_FEATURES.map(featureName => {
return {
eventName: EVENT_BUILD_FEATURE_USAGE,
payload: {
featureName,
invocationCount: buildFeaturesMap.get(featureName) ? 1 : 0
}
};
});
}
File renamed without changes.
16 changes: 15 additions & 1 deletion packages/service/src/analysis/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import * as path from 'path';
import * as fs from 'fs';
import logger from '@shuvi/utils/lib/logger';
import { eventBuildOptimize } from '@shuvi/telemetry/lib/events';
import {
eventBuildOptimize,
eventPackageDetected,
eventBuildFeatureUsage
} from './events';
import { recursiveReadDir } from '@shuvi/utils/lib/recursiveReaddir';
import { getJavaScriptInfo } from '../bundler/typescript';
import { IPluginContext, Telemetry } from '../core';
Expand Down Expand Up @@ -52,6 +56,16 @@ export const analysis = async ({

const analysisEnd = process.hrtime(analysisBegin);

const packageDetectedEvents = await eventPackageDetected(
context.paths.rootDir
);
telemetry.record(packageDetectedEvents);
telemetry.record(
eventBuildFeatureUsage({
compiler: context.config?.compiler,
experimental: context.config?.experimental
})
);
telemetry.record(
eventBuildOptimize(routePaths, {
durationInSeconds: analysisEnd[0],
Expand Down
7 changes: 7 additions & 0 deletions packages/service/src/core/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ interface ServerConfigs {

export interface Telemetry {
record(events: TelemetryEvent | TelemetryEvent[]): Promise<RecordObject>;
flush(): Promise<void>;
}

class Api {
Expand Down Expand Up @@ -155,6 +156,12 @@ class Api {
}

return this._telemetryImpl.record(events);
},
flush: () => {
if (!this._telemetryImpl) {
return Promise.resolve();
}
return this._telemetryImpl.flush();
}
};
}
Expand Down
1 change: 1 addition & 0 deletions packages/shuvi/src/tasks/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,6 @@ export async function build(options: IBuildOptions) {
await bundle(bundler);
await pluginContext.pluginRunner.afterBuild();
await analysis({ context: api.pluginContext, telemetry: api.telemetry });
await api.telemetry.flush();
return api;
}
3 changes: 1 addition & 2 deletions packages/telemetry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"is-wsl": "2.2.0",
"ci-info": "3.6.2",
"async-retry": "1.3.3",
"node-fetch": "2.6.7",
"find-up": "4.1.0"
"node-fetch": "2.6.7"
},
"devDependencies": {
"@types/async-retry": "1.3.0",
Expand Down
32 changes: 0 additions & 32 deletions packages/telemetry/src/events/build.ts

This file was deleted.

4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 616b44d

Please sign in to comment.