@parcel/profiler 2.12.1-dev.3171 → 2.12.1-dev.3185

Sign up to get free protection for your applications and to get access to all the features.
package/lib/Tracer.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import type { TraceEvent, IDisposable, PluginTracer as IPluginTracer } from "@parcel/types";
1
+ import type { TraceEvent, IDisposable, MeasurementOptions, PluginTracer as IPluginTracer } from "@parcel/types";
2
2
  import type { TraceMeasurement as ITraceMeasurement } from "./types";
3
3
  export default class Tracer {
4
4
 
5
5
  onTrace(cb: (event: TraceEvent) => unknown): IDisposable;
6
- wrap(name: string, fn: () => unknown): Promise<void>;
7
- createMeasurement(name: string, category?: string, argumentName?: string, otherArgs?: Record<string, unknown>): ITraceMeasurement | null;
6
+ measure<T>({ args, categories, name }: MeasurementOptions, fn: () => T): T;
8
7
  get enabled(): boolean;
9
8
  enable(): void;
10
9
  disable(): void;
@@ -24,5 +23,6 @@ export declare class PluginTracer implements IPluginTracer {
24
23
  constructor(opts: TracerOpts);
25
24
  get enabled(): boolean;
26
25
  createMeasurement(name: string, category?: string, argumentName?: string, otherArgs?: Record<string, unknown>): ITraceMeasurement | null;
26
+ measure<T>(options: MeasurementOptions, fn: () => T): T;
27
27
  }
28
28
  export {};
package/lib/Tracer.js CHANGED
@@ -62,38 +62,37 @@ class Tracer {
62
62
  onTrace(cb) {
63
63
  return this.#traceEmitter.addListener(cb);
64
64
  }
65
- async wrap(name, fn) {
66
- let measurement = this.createMeasurement(name);
67
- try {
68
- await fn();
69
- } finally {
70
- measurement && measurement.end();
71
- }
72
- }
73
- createMeasurement(name, category = 'Core', argumentName, otherArgs) {
74
- if (!this.enabled) return null;
75
-
76
- // We create `args` in a fairly verbose way to avoid object
77
- // allocation where not required.
78
- let args;
79
- if (typeof argumentName === 'string') {
80
- args = {
81
- name: argumentName
82
- };
65
+ measure({
66
+ args = {},
67
+ categories,
68
+ name
69
+ }, fn) {
70
+ if (!this.enabled) {
71
+ return fn();
83
72
  }
84
- if (typeof otherArgs === 'object') {
85
- if (typeof args == 'undefined') {
86
- args = {};
73
+ let measurement = new TraceMeasurement(this, name, pid, tid, {
74
+ categories,
75
+ args
76
+ });
77
+ let result;
78
+ let hasFinally = false;
79
+ try {
80
+ result = fn();
81
+ // @ts-expect-error TypeScript types cannot infer that finally can exist
82
+ if (result != null && typeof result === 'object' && typeof result.finally === 'function') {
83
+ hasFinally = true;
84
+ // @ts-expect-error
85
+ // $FlowFixMe[incompatible-use] This will run for a promise type, but it cannot be easily typed in Flow
86
+ result = result.finally(() => {
87
+ measurement === null || measurement === void 0 || measurement.end();
88
+ });
87
89
  }
88
- for (const [k, v] of Object.entries(otherArgs)) {
89
- args[k] = v;
90
+ } finally {
91
+ if (!hasFinally) {
92
+ measurement === null || measurement === void 0 || measurement.end();
90
93
  }
91
94
  }
92
- const data = {
93
- categories: [category],
94
- args
95
- };
96
- return new TraceMeasurement(this, name, pid, tid, data);
95
+ return result;
97
96
  }
98
97
  get enabled() {
99
98
  return this.#enabled;
@@ -125,7 +124,44 @@ class PluginTracer {
125
124
  return tracer.enabled;
126
125
  }
127
126
  createMeasurement(name, category, argumentName, otherArgs) {
128
- return tracer.createMeasurement(name, `${this.category}:${this.origin}${typeof category === 'string' ? `:${category}` : ''}`, argumentName, otherArgs);
127
+ if (!this.enabled) return null;
128
+
129
+ // We create `args` in a fairly verbose way to avoid object
130
+ // allocation where not required.
131
+ let args;
132
+ if (typeof argumentName === 'string') {
133
+ args = {
134
+ name: argumentName
135
+ };
136
+ }
137
+ if (typeof otherArgs === 'object') {
138
+ if (typeof args == 'undefined') {
139
+ args = {};
140
+ }
141
+ for (const [k, v] of Object.entries(otherArgs)) {
142
+ args[k] = v;
143
+ }
144
+ }
145
+ const data = {
146
+ categories: [`${this.category}:${this.origin}${typeof category === 'string' ? `:${category}` : ''}`],
147
+ args
148
+ };
149
+ return new TraceMeasurement(tracer, name, pid, tid, data);
150
+ }
151
+ measure(options, fn) {
152
+ var _options$args;
153
+ if (!this.enabled) {
154
+ return fn();
155
+ }
156
+ return tracer.measure({
157
+ ...options,
158
+ // $FlowFixMe[cannot-spread-inexact]
159
+ args: {
160
+ origin: this.origin,
161
+ ...((_options$args = options.args) !== null && _options$args !== void 0 ? _options$args : {})
162
+ },
163
+ categories: [this.category, ...options.categories]
164
+ }, fn);
129
165
  }
130
166
  }
131
167
  exports.PluginTracer = PluginTracer;
package/lib/types.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- export interface TraceMeasurement {
2
- end(): void;
3
- }
1
+ export type { TraceMeasurement } from "@parcel/types-internal";
4
2
  export type TraceMeasurementData = {
5
3
  readonly categories: string[];
6
4
  readonly args?: Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcel/profiler",
3
- "version": "2.12.1-dev.3171+79b158883",
3
+ "version": "2.12.1-dev.3185+298c035e0",
4
4
  "description": "Blazing fast, zero configuration web application bundler",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -20,13 +20,14 @@
20
20
  "node": ">= 12.0.0"
21
21
  },
22
22
  "scripts": {
23
- "build-ts": "flow-to-ts src/*.js --write && tsc --emitDeclarationOnly --declaration --esModuleInterop --target es2015 --moduleResolution node16 --module node16 src/*.ts && mkdir -p lib && mv src/*.d.ts lib/. && rm src/*.ts && node build-ts.js",
23
+ "build-ts": "flow-to-ts src/*.js --write && rm -f ./src/*.d.ts && tsc --emitDeclarationOnly --declaration --esModuleInterop --target es2015 --moduleResolution node16 --module node16 src/*.ts && mkdir -p lib && mv src/*.d.ts lib/. && rm src/*.ts && node build-ts.js",
24
24
  "check-ts": "tsc --noEmit lib/index.d.ts"
25
25
  },
26
26
  "dependencies": {
27
- "@parcel/diagnostic": "2.0.0-dev.1548+79b158883",
28
- "@parcel/events": "2.0.0-dev.1548+79b158883",
27
+ "@parcel/diagnostic": "2.0.0-dev.1562+298c035e0",
28
+ "@parcel/events": "2.0.0-dev.1562+298c035e0",
29
+ "@parcel/types-internal": "2.12.1-dev.3185+298c035e0",
29
30
  "chrome-trace-event": "^1.0.2"
30
31
  },
31
- "gitHead": "79b158883170daac9cd17bdecfebff163bc99e52"
32
+ "gitHead": "298c035e0ffb82d7c5579ce233cc35dce2fe1061"
32
33
  }
package/src/Tracer.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import type {
4
4
  TraceEvent,
5
5
  IDisposable,
6
+ MeasurementOptions,
6
7
  PluginTracer as IPluginTracer,
7
8
  } from '@parcel/types';
8
9
  import type {
@@ -64,44 +65,44 @@ export default class Tracer {
64
65
  return this.#traceEmitter.addListener(cb);
65
66
  }
66
67
 
67
- async wrap(name: string, fn: () => mixed): Promise<void> {
68
- let measurement = this.createMeasurement(name);
69
- try {
70
- await fn();
71
- } finally {
72
- measurement && measurement.end();
68
+ measure<T>(
69
+ {args = {}, categories, name}: MeasurementOptions,
70
+ fn: () => T,
71
+ ): T {
72
+ if (!this.enabled) {
73
+ return fn();
73
74
  }
74
- }
75
75
 
76
- createMeasurement(
77
- name: string,
78
- category: string = 'Core',
79
- argumentName?: string,
80
- otherArgs?: {[key: string]: mixed},
81
- ): ITraceMeasurement | null {
82
- if (!this.enabled) return null;
76
+ let measurement = new TraceMeasurement(this, name, pid, tid, {
77
+ categories,
78
+ args,
79
+ });
83
80
 
84
- // We create `args` in a fairly verbose way to avoid object
85
- // allocation where not required.
86
- let args: {[key: string]: mixed};
87
- if (typeof argumentName === 'string') {
88
- args = {name: argumentName};
89
- }
90
- if (typeof otherArgs === 'object') {
91
- if (typeof args == 'undefined') {
92
- args = {};
81
+ let result: T;
82
+ let hasFinally = false;
83
+
84
+ try {
85
+ result = fn();
86
+ // @ts-expect-error TypeScript types cannot infer that finally can exist
87
+ if (
88
+ result != null &&
89
+ typeof result === 'object' &&
90
+ typeof result.finally === 'function'
91
+ ) {
92
+ hasFinally = true;
93
+ // @ts-expect-error
94
+ // $FlowFixMe[incompatible-use] This will run for a promise type, but it cannot be easily typed in Flow
95
+ result = result.finally(() => {
96
+ measurement?.end();
97
+ });
93
98
  }
94
- for (const [k, v] of Object.entries(otherArgs)) {
95
- args[k] = v;
99
+ } finally {
100
+ if (!hasFinally) {
101
+ measurement?.end();
96
102
  }
97
103
  }
98
104
 
99
- const data: TraceMeasurementData = {
100
- categories: [category],
101
- args,
102
- };
103
-
104
- return new TraceMeasurement(this, name, pid, tid, data);
105
+ return result;
105
106
  }
106
107
 
107
108
  get enabled(): boolean {
@@ -128,6 +129,7 @@ type TracerOpts = {|
128
129
  origin: string,
129
130
  category: string,
130
131
  |};
132
+
131
133
  export class PluginTracer implements IPluginTracer {
132
134
  /** @private */
133
135
  origin: string;
@@ -151,13 +153,51 @@ export class PluginTracer implements IPluginTracer {
151
153
  argumentName?: string,
152
154
  otherArgs?: {[key: string]: mixed},
153
155
  ): ITraceMeasurement | null {
154
- return tracer.createMeasurement(
155
- name,
156
- `${this.category}:${this.origin}${
157
- typeof category === 'string' ? `:${category}` : ''
158
- }`,
159
- argumentName,
160
- otherArgs,
156
+ if (!this.enabled) return null;
157
+
158
+ // We create `args` in a fairly verbose way to avoid object
159
+ // allocation where not required.
160
+ let args: {[key: string]: mixed};
161
+ if (typeof argumentName === 'string') {
162
+ args = {name: argumentName};
163
+ }
164
+ if (typeof otherArgs === 'object') {
165
+ if (typeof args == 'undefined') {
166
+ args = {};
167
+ }
168
+ for (const [k, v] of Object.entries(otherArgs)) {
169
+ args[k] = v;
170
+ }
171
+ }
172
+
173
+ const data: TraceMeasurementData = {
174
+ categories: [
175
+ `${this.category}:${this.origin}${
176
+ typeof category === 'string' ? `:${category}` : ''
177
+ }`,
178
+ ],
179
+ args,
180
+ };
181
+
182
+ return new TraceMeasurement(tracer, name, pid, tid, data);
183
+ }
184
+
185
+ measure<T>(options: MeasurementOptions, fn: () => T): T {
186
+ if (!this.enabled) {
187
+ return fn();
188
+ }
189
+
190
+ return tracer.measure(
191
+ {
192
+ ...options,
193
+ // $FlowFixMe[cannot-spread-inexact]
194
+ args: {
195
+ origin: this.origin,
196
+ ...(options.args ?? {}),
197
+ },
198
+ categories: [this.category, ...options.categories],
199
+ },
200
+ fn,
161
201
  );
162
202
  }
163
203
  }
package/src/types.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // @flow
2
2
 
3
- export interface TraceMeasurement {
4
- end(): void;
5
- }
3
+ export type {TraceMeasurement} from '@parcel/types-internal';
6
4
 
7
5
  export type TraceMeasurementData = {|
8
6
  +categories: string[],
@@ -5,74 +5,123 @@ import assert from 'assert';
5
5
  describe('Tracer', () => {
6
6
  let onTrace;
7
7
  let traceDisposable;
8
+ let opts = {name: 'test', categories: ['tracer']};
9
+
8
10
  beforeEach(() => {
9
11
  onTrace = sinon.spy();
10
12
  traceDisposable = tracer.onTrace(onTrace);
11
13
  tracer.enable();
12
14
  });
15
+
13
16
  afterEach(() => {
14
17
  traceDisposable.dispose();
15
18
  });
16
19
 
17
- it('returns no measurement when disabled', () => {
18
- tracer.disable();
19
- const measurement = tracer.createMeasurement('test');
20
- assert(measurement == null);
21
- assert(onTrace.notCalled);
22
- });
23
- it('emits a basic trace event', () => {
24
- const measurement = tracer.createMeasurement('test');
25
- measurement.end();
26
- sinon.assert.calledWith(
27
- onTrace,
28
- sinon.match({
29
- type: 'trace',
30
- name: 'test',
31
- args: undefined,
32
- duration: sinon.match.number,
33
- }),
34
- );
35
- });
36
- it('emits a complex trace event', () => {
37
- const measurement = tracer.createMeasurement('test', 'myPlugin', 'aaargh', {
38
- extra: 'data',
39
- });
40
- measurement.end();
41
- sinon.assert.calledWith(
42
- onTrace,
43
- sinon.match({
44
- type: 'trace',
45
- name: 'test',
46
- categories: ['myPlugin'],
47
- args: {extra: 'data', name: 'aaargh'},
48
- duration: sinon.match.number,
49
- }),
50
- );
51
- });
52
- it('calling end twice on measurment should be a no-op', () => {
53
- const measurement = tracer.createMeasurement('test');
54
- measurement.end();
55
- measurement.end();
56
- sinon.assert.calledOnce(onTrace);
20
+ describe('measure()', () => {
21
+ let cases = [
22
+ ['synchronous', () => () => {}],
23
+ ['asynchronous', () => async () => {}],
24
+ ];
25
+
26
+ for (let [type, createFn] of cases) {
27
+ describe(`given a ${type} function`, () => {
28
+ it('does not trace when disabled', async () => {
29
+ tracer.disable();
30
+
31
+ let result = tracer.measure(opts, sinon.spy());
32
+ if (type === 'asynchronous') {
33
+ sinon.assert.notCalled(onTrace);
34
+ await result;
35
+ }
36
+
37
+ assert(onTrace.notCalled);
38
+ });
39
+
40
+ it('emits a basic trace event', async () => {
41
+ let result = tracer.measure(opts, createFn());
42
+ if (type === 'asynchronous') {
43
+ sinon.assert.notCalled(onTrace);
44
+ await result;
45
+ }
46
+
47
+ sinon.assert.calledOnce(onTrace);
48
+ sinon.assert.calledWith(
49
+ onTrace,
50
+ sinon.match({
51
+ type: 'trace',
52
+ name: 'test',
53
+ args: {},
54
+ categories: ['tracer'],
55
+ duration: sinon.match.number,
56
+ }),
57
+ );
58
+ });
59
+
60
+ it('emits a complex trace event', async () => {
61
+ let result = tracer.measure(
62
+ {...opts, args: {hello: 'world'}},
63
+ createFn(),
64
+ );
65
+ if (type === 'asynchronous') {
66
+ sinon.assert.notCalled(onTrace);
67
+ await result;
68
+ }
69
+
70
+ sinon.assert.calledOnce(onTrace);
71
+ sinon.assert.calledWith(
72
+ onTrace,
73
+ sinon.match({
74
+ type: 'trace',
75
+ name: 'test',
76
+ args: {hello: 'world'},
77
+ categories: ['tracer'],
78
+ duration: sinon.match.number,
79
+ }),
80
+ );
81
+ });
82
+ });
83
+ }
57
84
  });
58
85
 
59
86
  describe('PluginTracer', () => {
60
- it('emits events with proper origin/category', () => {
61
- const pluginTracer = new PluginTracer({
62
- origin: 'origin',
63
- category: 'cat',
87
+ const pluginTracer = new PluginTracer({
88
+ origin: 'origin',
89
+ category: 'cat',
90
+ });
91
+
92
+ describe(`measure()`, () => {
93
+ it('emits events with origin and category', () => {
94
+ pluginTracer.measure(opts, sinon.spy());
95
+
96
+ sinon.assert.calledOnce(onTrace);
97
+ sinon.assert.calledWith(
98
+ onTrace,
99
+ sinon.match({
100
+ type: 'trace',
101
+ name: 'test',
102
+ args: {origin: 'origin'},
103
+ categories: ['cat', 'tracer'],
104
+ duration: sinon.match.number,
105
+ }),
106
+ );
107
+ });
108
+ });
109
+
110
+ describe('createMeasurement()', () => {
111
+ it('emits events with origin and category', () => {
112
+ pluginTracer.createMeasurement('test', 'customCat').end();
113
+
114
+ sinon.assert.calledOnce(onTrace);
115
+ sinon.assert.calledWith(
116
+ onTrace,
117
+ sinon.match({
118
+ type: 'trace',
119
+ name: 'test',
120
+ categories: ['cat:origin:customCat'],
121
+ duration: sinon.match.number,
122
+ }),
123
+ );
64
124
  });
65
- const measurement = pluginTracer.createMeasurement('test', 'customCat');
66
- measurement.end();
67
- sinon.assert.calledWith(
68
- onTrace,
69
- sinon.match({
70
- type: 'trace',
71
- name: 'test',
72
- categories: ['cat:origin:customCat'],
73
- duration: sinon.match.number,
74
- }),
75
- );
76
125
  });
77
126
  });
78
127
  });