chrome-devtools-frontend 1.0.961907 → 1.0.964440

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.eslintignore +5 -4
  2. package/AUTHORS +1 -0
  3. package/config/gni/devtools_grd_files.gni +1 -1
  4. package/docs/resource_management.md +119 -0
  5. package/docs/workflows.md +7 -0
  6. package/front_end/core/common/ParsedURL.ts +12 -10
  7. package/front_end/core/host/UserMetrics.ts +2 -1
  8. package/front_end/core/i18n/locales/en-US.json +2 -2
  9. package/front_end/core/i18n/locales/en-XL.json +2 -2
  10. package/front_end/core/protocol_client/InspectorBackend.ts +7 -7
  11. package/front_end/core/root/Runtime.ts +2 -0
  12. package/front_end/core/sdk/CSSProperty.ts +22 -110
  13. package/front_end/core/sdk/DOMModel.ts +2 -2
  14. package/front_end/core/sdk/DebuggerModel.ts +1 -1
  15. package/front_end/entrypoints/main/MainImpl.ts +7 -2
  16. package/front_end/generated/InspectorBackendCommands.js +8 -4
  17. package/front_end/generated/protocol-mapping.d.ts +12 -1
  18. package/front_end/generated/protocol-proxy-api.d.ts +11 -1
  19. package/front_end/generated/protocol-tsconfig.json +2 -2
  20. package/front_end/generated/protocol.ts +16787 -0
  21. package/front_end/models/javascript_metadata/NativeFunctions.js +7480 -4147
  22. package/front_end/models/persistence/IsolatedFileSystem.ts +3 -2
  23. package/front_end/models/persistence/PersistenceActions.ts +2 -2
  24. package/front_end/models/text_utils/text_utils-legacy.ts +0 -5
  25. package/front_end/models/text_utils/text_utils.ts +0 -2
  26. package/front_end/models/workspace/UISourceCode.ts +4 -5
  27. package/front_end/panels/animation/AnimationUI.ts +2 -1
  28. package/front_end/panels/application/AppManifestView.ts +7 -1
  29. package/front_end/panels/application/components/BackForwardCacheStrings.ts +1 -1
  30. package/front_end/panels/application/components/FrameDetailsView.ts +1 -0
  31. package/front_end/panels/elements/StylePropertyTreeElement.ts +13 -0
  32. package/front_end/panels/elements/StylesSidebarPane.ts +73 -4
  33. package/front_end/panels/elements/stylesSectionTree.css +28 -0
  34. package/front_end/panels/media/PlayerListView.ts +2 -0
  35. package/front_end/panels/media/playerListView.css +3 -0
  36. package/front_end/panels/sensors/sensors-meta.ts +2 -2
  37. package/front_end/panels/sources/NavigatorView.ts +1 -1
  38. package/front_end/ui/components/diff_view/DiffView.ts +2 -2
  39. package/front_end/ui/components/docs/icon_button/basic.ts +1 -1
  40. package/front_end/ui/components/icon_button/IconButton.ts +1 -1
  41. package/front_end/ui/legacy/GlassPane.ts +2 -0
  42. package/front_end/ui/legacy/components/inline_editor/cssLength.css +1 -0
  43. package/front_end/ui/legacy/softDropDownButton.css +2 -0
  44. package/front_end/ui/legacy/themeColors.css +3 -1
  45. package/front_end/ui/legacy/toolbar.css +6 -0
  46. package/package.json +1 -1
  47. package/scripts/build/devtools_plugin.js +103 -0
  48. package/scripts/build/ninja/{rollup.gni → bundle.gni} +2 -2
  49. package/scripts/build/ninja/devtools_entrypoint.gni +32 -24
  50. package/scripts/build/rollup.config.js +3 -93
  51. package/scripts/devtools_paths.js +3 -2
  52. package/scripts/devtools_paths.py +4 -0
  53. package/scripts/javascript_natives/helpers.js +211 -0
  54. package/scripts/javascript_natives/index.js +57 -194
  55. package/scripts/javascript_natives/package.json +8 -3
  56. package/scripts/javascript_natives/test.d.ts +9 -0
  57. package/scripts/javascript_natives/tests.js +195 -0
  58. package/scripts/protocol_typescript/protocol_dts_generator.ts +4 -9
  59. package/scripts/whitespaces.txt +1 -0
  60. package/front_end/generated/protocol.d.ts +0 -16771
  61. package/front_end/models/text_utils/CodeMirrorUtils.ts +0 -77
@@ -2,19 +2,20 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- const WebIDL2 = require('webidl2');
6
- const fs = require('fs');
7
- const path = require('path');
8
- const ts = require('typescript');
9
- const glob = require('glob');
10
- const methods = {
11
- __proto__: null
12
- };
5
+ import * as fs from 'fs';
6
+ import glob from 'glob';
7
+ import * as path from 'path';
8
+ import ts from 'typescript';
9
+ import * as WebIDL2 from 'webidl2';
10
+
11
+ import {parseTSFunction, postProcess, walkRoot} from './helpers.js';
12
+
13
13
  const program = ts.createProgram(
14
14
  [
15
- path.join(__dirname, 'node_modules', 'typescript', 'lib', 'lib.esnext.d.ts'),
15
+ new URL('node_modules/typescript/lib/lib.esnext.d.ts', import.meta.url).pathname,
16
16
  ],
17
- {noLib: true});
17
+ {noLib: false, types: []});
18
+
18
19
  for (const file of program.getSourceFiles()) {
19
20
  ts.forEachChild(file, node => {
20
21
  if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
@@ -31,201 +32,63 @@ for (const file of program.getSourceFiles()) {
31
32
  });
32
33
  }
33
34
 
34
- function parseTSFunction(func, node) {
35
- if (!func.name.escapedText) {
36
- return;
37
- }
38
-
39
- const args = func.parameters
40
- .map(p => {
41
- let text = p.name.escapedText;
42
- if (p.questionToken) {
43
- text = '?' + text;
44
- }
45
- if (p.dotDotDotToken) {
46
- text = '...' + text;
47
- }
48
- return text;
49
- })
50
- .filter(x => x !== 'this');
51
- storeMethod(node.name.text, func.name.escapedText, args);
52
- }
53
-
54
35
  // Assume the DevTools front-end repository is at
55
36
  // `devtools/devtools-frontend`, where `devtools` is on the same level
56
37
  // as `chromium`. This matches `scripts/npm_test.js`.
57
- glob(
58
- '../../../../chromium/src/third_party/blink/renderer/+(core|modules)/**/*.idl', {cwd: process.env.PWD},
59
- function(er, files) {
60
- for (const file of files) {
61
- if (file.includes('testing')) {
62
- continue;
63
- }
64
- const data = fs.readFileSync(path.join(process.env.PWD, file), 'utf8');
65
- const lines = data.split('\n');
66
- const newLines = [];
67
- for (const line of lines) {
68
- if (!line.includes(' attribute ')) {
69
- newLines.push(line);
70
- }
71
- }
38
+ const files =
39
+ glob.sync('../../../../chromium/src/third_party/blink/renderer/+(core|modules)/**/*.idl', {cwd: process.env.PWD});
72
40
 
73
- try {
74
- WebIDL2.parse(newLines.join('\n')).forEach(walk);
75
- } catch (e) {
76
- // console.error(file);
77
- }
78
- }
79
- WebIDL2
80
- .parse(`
81
- namespace console {
82
- void assert(optional boolean condition = false, any... data);
83
- void clear();
84
- void count(optional DOMString label = "default");
85
- void debug(any... data);
86
- void dir(any item, optional object? options);
87
- void dirxml(any... data);
88
- void error(any... data);
89
- void group(any... data);
90
- void groupCollapsed(any... data);
91
- void groupEnd();
92
- void info(any... data);
93
- void log(any... data);
94
- void profile(optional DOMString title);
95
- void profileEnd(optional DOMString title);
96
- void table(any... tabularData);
97
- void time(optional DOMString label);
98
- void timeEnd(optional DOMString label);
99
- void timeStamp(optional DOMString name);
100
- void trace(any... data);
101
- void warn(any... data);
102
- };
103
- `).forEach(walk);
104
- postProcess();
105
- });
106
-
107
- function walk(thing, parent) {
108
- if (thing.type === 'interface') {
109
- const constructor = thing.extAttrs.find(extAttr => extAttr.name === 'Constructor');
110
- if (constructor && constructor.arguments && thing.extAttrs.find(extAttr => extAttr.name === 'Exposed')) {
111
- storeMethod('Window', thing.name, constructor.arguments.map(argName));
112
- }
113
-
114
- const namedConstructor = thing.extAttrs.find(extAttr => extAttr.name === 'NamedConstructor');
115
- if (namedConstructor && namedConstructor.arguments) {
116
- storeMethod('Window', namedConstructor.rhs.value, namedConstructor.arguments.map(argName));
117
- }
41
+ for (const file of files) {
42
+ if (file.includes('testing')) {
43
+ continue;
118
44
  }
119
- if (thing.type.includes('operation')) {
120
- storeMethod(thing.static ? (parent.name + 'Constructor') : parent.name, thing.name, thing.arguments.map(argName));
121
- return;
122
- }
123
- if (thing.members) {
124
- for (const member of thing.members) {
125
- walk(member, thing);
45
+ const data = fs.readFileSync(path.join(process.env.PWD, file), 'utf8');
46
+ const lines = data.split('\n');
47
+ const newLines = [];
48
+ for (const line of lines) {
49
+ if (!line.includes(' attribute ')) {
50
+ newLines.push(line);
126
51
  }
127
52
  }
128
- }
129
-
130
- function argName(a) {
131
- let name = a.name;
132
- if (a.optional) {
133
- name = '?' + name;
134
- }
135
- if (a.variadic) {
136
- name = '...' + name;
137
- }
138
- return name;
139
- }
140
53
 
141
- function storeMethod(parent, name, args) {
142
- if (!methods[name]) {
143
- methods[name] = {__proto__: null};
144
- }
145
- if (!methods[name][parent]) {
146
- methods[name][parent] = [];
54
+ try {
55
+ WebIDL2.parse(newLines.join('\n')).forEach(walkRoot);
56
+ } catch (e) {
57
+ // console.error(file);
147
58
  }
148
- methods[name][parent].push(args);
149
- }
150
59
 
151
- function postProcess() {
152
- for (const name in methods) {
153
- const jsonParents = new Set();
154
- for (const parent in methods[name]) {
155
- const signatures = methods[name][parent];
156
- signatures.sort((a, b) => a.length - b.length);
157
- const filteredSignatures = [];
158
- for (const signature of signatures) {
159
- const smallerIndex = filteredSignatures.findIndex(smaller => startsThesame(smaller, signature));
160
- if (smallerIndex !== -1) {
161
- filteredSignatures[smallerIndex] = (signature.map((arg, index) => {
162
- const otherArg = filteredSignatures[smallerIndex][index];
163
- if (otherArg) {
164
- return otherArg.length > arg.length ? otherArg : arg;
165
- }
166
- if (arg.startsWith('?') || arg.startsWith('...')) {
167
- return arg;
168
- }
169
- return '?' + arg;
170
- }));
171
- } else {
172
- filteredSignatures.push(signature);
173
- }
174
- }
60
+ // Source for Console spec: https://console.spec.whatwg.org/#idl-index
61
+ WebIDL2
62
+ .parse(`
63
+ [Exposed=(Window,Worker,Worklet)]
64
+ namespace console { // but see namespace object requirements below
65
+ // Logging
66
+ undefined assert(optional boolean condition = false, any... data);
67
+ undefined clear();
68
+ undefined debug(any... data);
69
+ undefined error(any... data);
70
+ undefined info(any... data);
71
+ undefined log(any... data);
72
+ undefined table(optional any tabularData, optional sequence<DOMString> properties);
73
+ undefined trace(any... data);
74
+ undefined warn(any... data);
75
+ undefined dir(optional any item, optional object? options);
76
+ undefined dirxml(any... data);
175
77
 
176
- function startsThesame(smaller, bigger) {
177
- for (let i = 0; i < smaller.length; i++) {
178
- const withoutQuestion = str => /[\?]?(.*)/.exec(str)[1];
179
- if (withoutQuestion(smaller[i]) !== withoutQuestion(bigger[i])) {
180
- return false;
181
- }
182
- }
183
- return true;
184
- }
78
+ // Counting
79
+ undefined count(optional DOMString label = "default");
80
+ undefined countReset(optional DOMString label = "default");
185
81
 
186
- methods[name][parent] = filteredSignatures;
187
- jsonParents.add(JSON.stringify(filteredSignatures));
188
- }
189
- if (jsonParents.size === 1) {
190
- methods[name] = {'*': JSON.parse(jsonParents.values().next().value)};
191
- }
192
- for (const parent in methods[name]) {
193
- const signatures = methods[name][parent];
194
- if (signatures.length === 1 && !signatures[0].length) {
195
- delete methods[name][parent];
196
- }
197
- }
198
- if (!Object.keys(methods[name]).length) {
199
- delete methods[name];
200
- }
201
- }
202
- const functions = [];
203
- for (const name in methods) {
204
- if (methods[name]['*']) {
205
- functions.push({name, signatures: methods[name]['*']});
206
- } else {
207
- for (const parent in methods[name]) {
208
- if (parent.endsWith('Constructor')) {
209
- functions.push({
210
- name,
211
- signatures: methods[name][parent],
212
- static: true,
213
- receiver: parent.substring(0, parent.length - 'Constructor'.length)
214
- });
215
- } else {
216
- functions.push({name, signatures: methods[name][parent], receiver: parent});
217
- }
218
- }
219
- }
220
- }
82
+ // Grouping
83
+ undefined group(any... data);
84
+ undefined groupCollapsed(any... data);
85
+ undefined groupEnd();
221
86
 
222
- fs.writeFileSync(
223
- path.join(__dirname, '..', '..', 'front_end', 'models', 'javascript_metadata', 'NativeFunctions.js'),
224
- `// Copyright 2020 The Chromium Authors. All rights reserved.
225
- // Use of this source code is governed by a BSD-style license that can be
226
- // found in the LICENSE file.
227
- // Generated from ${path.relative(path.join(__dirname, '..', '..'), __filename)}
228
-
229
- export const NativeFunctions = ${JSON.stringify(functions, null, 2)};
230
- `);
87
+ // Timing
88
+ undefined time(optional DOMString label = "default");
89
+ undefined timeLog(optional DOMString label = "default", any... data);
90
+ undefined timeEnd(optional DOMString label = "default");
91
+ };
92
+ `).forEach(walkRoot);
231
93
  }
94
+ postProcess();
@@ -1,11 +1,16 @@
1
1
  {
2
+ "private": true,
2
3
  "main": "index.js",
3
4
  "scripts": {
4
- "start": "node index.js"
5
+ "start": "node index.js",
6
+ "test": "mocha tests.js"
5
7
  },
8
+ "type": "module",
6
9
  "dependencies": {
10
+ "@types/webidl2": "^23.13.6",
7
11
  "glob": "^7.1.2",
8
- "typescript": "^2.8.3",
9
- "webidl2": "^10.3.3"
12
+ "mocha": "^9.1.4",
13
+ "typescript": "latest",
14
+ "webidl2": "^24.2.0"
10
15
  }
11
16
  }
@@ -0,0 +1,9 @@
1
+ interface Array<T> {
2
+ at(index: number): T|undefined;
3
+ diffSig(oneSig: number): T|undefined;
4
+ }
5
+
6
+ interface ReadonlyArray<T> {
7
+ at(index: number): T|undefined;
8
+ diffSig(twoSig: number): T|undefined;
9
+ }
@@ -0,0 +1,195 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as assert from 'assert';
6
+ import ts from 'typescript';
7
+ import * as WebIDL2 from 'webidl2';
8
+
9
+ import {clearState, parseTSFunction, postProcess, walkRoot} from './helpers.js';
10
+
11
+ describe('NativeFunction signature generation', function() {
12
+ this.afterEach(() => {
13
+ clearState();
14
+ });
15
+
16
+ it('should produce correct signatures for IDL interface', function() {
17
+ WebIDL2
18
+ .parse(`
19
+ [
20
+ Exposed=Window
21
+ ] interface Document : Node {
22
+ [CallWith=Document] constructor();
23
+ [Affects=Nothing] HTMLCollection getElementsByTagName(DOMString localName);
24
+ [Affects=Nothing] HTMLCollection getElementsByTagNameNS(DOMString? namespaceURI, DOMString localName);
25
+ [Affects=Nothing] HTMLCollection getElementsByClassName(DOMString classNames);
26
+
27
+ [NewObject, DoNotTestNewObject, PerWorldBindings, RaisesException, ImplementedAs=CreateElementForBinding] Element createElement(DOMString localName);
28
+ [NewObject, DoNotTestNewObject, RaisesException] Element createElementNS(DOMString? namespaceURI, DOMString qualifiedName);
29
+ [NewObject] DocumentFragment createDocumentFragment();
30
+ [NewObject] Text createTextNode(DOMString data);
31
+ };
32
+ `).forEach(walkRoot);
33
+ const output = postProcess(/* dryRun: */ true);
34
+ const expected = `export const NativeFunctions = [
35
+ {
36
+ name: "getElementsByTagName",
37
+ signatures: [["localName"]]
38
+ },
39
+ {
40
+ name: "getElementsByTagNameNS",
41
+ signatures: [["namespaceURI","localName"]]
42
+ },
43
+ {
44
+ name: "getElementsByClassName",
45
+ signatures: [["classNames"]]
46
+ },
47
+ {
48
+ name: "createElement",
49
+ signatures: [["localName"]]
50
+ },
51
+ {
52
+ name: "createElementNS",
53
+ signatures: [["namespaceURI","qualifiedName"]]
54
+ },
55
+ {
56
+ name: "createTextNode",
57
+ signatures: [["data"]]
58
+ }
59
+ ];`;
60
+ assert.equal(output, expected);
61
+ });
62
+
63
+ it('should produce correct signatures for IDL mixin interface', function() {
64
+ WebIDL2
65
+ .parse(`[
66
+ LegacyTreatAsPartialInterface,
67
+ Exposed=(Window,Worker)
68
+ ] interface mixin WindowOrWorkerGlobalScope {
69
+ [CallWith=ScriptState] void reportError(any e);
70
+
71
+ [RaisesException] DOMString atob(DOMString atob);
72
+ [CallWith=ScriptState, RuntimeCallStatsCounter=WindowSetTimeout] long setTimeout(Function handler, optional long timeout = 0, any... arguments);
73
+ [CallWith=ScriptState] long setTimeout(ScriptString handler, optional long timeout = 0, any... arguments);
74
+ };
75
+ `).forEach(walkRoot);
76
+ const output = postProcess(/* dryRun: */ true);
77
+ const expected = `export const NativeFunctions = [
78
+ {
79
+ name: "reportError",
80
+ signatures: [["e"]]
81
+ },
82
+ {
83
+ name: "atob",
84
+ signatures: [["atob"]]
85
+ },
86
+ {
87
+ name: "setTimeout",
88
+ signatures: [["handler","?timeout","...arguments"]]
89
+ }
90
+ ];`;
91
+ assert.equal(output, expected);
92
+ });
93
+
94
+ it('should produce correct signatures for Console IDL', function() {
95
+ WebIDL2
96
+ .parse(`
97
+ [Exposed=(Window,Worker,Worklet)]
98
+ namespace console {
99
+ undefined assert(optional boolean condition = false, any... data);
100
+ undefined table(optional any tabularData, optional sequence<DOMString> properties);
101
+ undefined count(optional DOMString label = "default");
102
+ undefined groupEnd();
103
+ };
104
+ `).forEach(walkRoot);
105
+ const output = postProcess(/* dryRun: */ true);
106
+ const expected = `export const NativeFunctions = [
107
+ {
108
+ name: "assert",
109
+ signatures: [["?condition","...data"]]
110
+ },
111
+ {
112
+ name: "table",
113
+ signatures: [["?tabularData","?properties"]]
114
+ },
115
+ {
116
+ name: "count",
117
+ signatures: [["?label"]]
118
+ }
119
+ ];`;
120
+ assert.equal(output, expected);
121
+ });
122
+
123
+ it('should produce correct signatures for Console IDL', function() {
124
+ WebIDL2
125
+ .parse(`
126
+ // https://html.spec.whatwg.org/C/#the-slot-element
127
+ [
128
+ Exposed=Window,
129
+ HTMLConstructor
130
+ ] interface HTMLSlotElement : HTMLElement {
131
+ [CEReactions, Reflect] attribute DOMString name;
132
+ [ImplementedAs=AssignedNodesForBinding] sequence<Node> assignedNodes(optional AssignedNodesOptions options = {});
133
+ [ImplementedAs=AssignedElementsForBinding] sequence<Element> assignedElements(optional AssignedNodesOptions options = {});
134
+ [RaisesException] void assign((Element or Text)... nodes);
135
+ };
136
+ `).forEach(walkRoot);
137
+ const output = postProcess(/* dryRun: */ true);
138
+ const expected = `export const NativeFunctions = [
139
+ {
140
+ name: "assignedNodes",
141
+ signatures: [["?options"]]
142
+ },
143
+ {
144
+ name: "assignedElements",
145
+ signatures: [["?options"]]
146
+ },
147
+ {
148
+ name: "assign",
149
+ signatures: [["...nodes"]]
150
+ }
151
+ ];`;
152
+ assert.equal(output, expected);
153
+ });
154
+
155
+ it('should produce correct signatures for typescript typings', function() {
156
+ const program = ts.createProgram(
157
+ [
158
+ new URL('test.d.ts', import.meta.url).pathname,
159
+ ],
160
+ {noLib: true, types: []});
161
+
162
+ for (const file of program.getSourceFiles()) {
163
+ ts.forEachChild(file, node => {
164
+ if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
165
+ for (const member of node.members) {
166
+ if (member.kind === ts.SyntaxKind.MethodSignature) {
167
+ parseTSFunction(member, node);
168
+ }
169
+ }
170
+ }
171
+ if (node.kind === ts.SyntaxKind.FunctionDeclaration) {
172
+ parseTSFunction(node, {name: {text: 'Window'}});
173
+ }
174
+ });
175
+ }
176
+ const output = postProcess(/* dryRun: */ true);
177
+ const expected = `export const NativeFunctions = [
178
+ {
179
+ name: "at",
180
+ signatures: [["index"]]
181
+ },
182
+ {
183
+ name: "diffSig",
184
+ signatures: [["oneSig"]],
185
+ receiver: "Array"
186
+ },
187
+ {
188
+ name: "diffSig",
189
+ signatures: [["twoSig"]],
190
+ receiver: "ReadonlyArray"
191
+ }
192
+ ];`;
193
+ assert.equal(output, expected);
194
+ });
195
+ });
@@ -62,15 +62,10 @@ const emitHeaderComments = () => {
62
62
  emitLine();
63
63
  };
64
64
 
65
- const emitModule = (moduleName: string, domains: Protocol.Domain[]) => {
66
- moduleName = toTitleCase(moduleName);
65
+ const emitModule = (domains: Protocol.Domain[]) => {
67
66
  emitHeaderComments();
68
- emitOpenBlock(`declare namespace ${moduleName}`);
69
67
  emitGlobalTypeDefs();
70
68
  domains.forEach(emitDomain);
71
- emitCloseBlock();
72
- emitLine();
73
- emitLine('export = Protocol;');
74
69
  };
75
70
 
76
71
  const emitGlobalTypeDefs = () => {
@@ -443,9 +438,9 @@ const flushEmitToFile = (path: string) => {
443
438
  const main = () => {
444
439
  const FRONTEND_GENERATED_DIR = path.resolve(__dirname, path.join('../../front_end/generated'));
445
440
 
446
- const destProtocolFilePath = path.join(FRONTEND_GENERATED_DIR, 'protocol.d.ts');
447
- const protocolModuleName = path.basename(destProtocolFilePath, '.d.ts');
448
- emitModule(protocolModuleName, protocolDomains);
441
+ const destProtocolFilePath = path.join(FRONTEND_GENERATED_DIR, 'protocol.ts');
442
+ const protocolModuleName = path.basename(destProtocolFilePath, '.ts');
443
+ emitModule(protocolDomains);
449
444
  flushEmitToFile(destProtocolFilePath);
450
445
 
451
446
  const destMappingFilePath = path.join(FRONTEND_GENERATED_DIR, 'protocol-mapping.d.ts');
@@ -8,3 +8,4 @@ Surma was here.
8
8
  .
9
9
  .
10
10
  Today's answer to life the universe and everything is 161!
11
+ No good text, but numbers ... 2!