wikiploy 1.8.1 → 1.8.2
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.
- package/README.building your project.md +186 -74
- package/README.md +1 -1
- package/assets/test.css +1 -1
- package/assets/test.js +1 -1
- package/package.json +2 -2
- package/src/ploy_test_botapi.js +1 -1
- package/src/ploy_test_lite.js +1 -1
- /package/assets/{bot.config.public.js → public--bot.config.mjs} +0 -0
|
@@ -17,20 +17,23 @@ bot.config.*
|
|
|
17
17
|
Your `package.json` (crucial part being `scripts`):
|
|
18
18
|
```js
|
|
19
19
|
{
|
|
20
|
-
"name": "wiki-gadget-
|
|
20
|
+
"name": "wiki-gadget-yourgadgetname",
|
|
21
21
|
"type": "commonjs",
|
|
22
22
|
"scripts": {
|
|
23
23
|
"test": "mocha",
|
|
24
|
-
"build": "
|
|
24
|
+
"build-less": "lessc src/less/main.less dist/yourGadgetName.css",
|
|
25
|
+
"build-js": "browserify src/main.js -o dist/yourGadgetName.js",
|
|
26
|
+
"build": "npm run build-less && npm run build-js && node -e \"console.log('Build done');\" ",
|
|
25
27
|
"deploy-dev": "node wikiploy-dev.mjs",
|
|
26
28
|
"deploy": "node wikiploy.mjs",
|
|
27
|
-
"rollout-dev": "npm run build && npm run deploy-dev"
|
|
29
|
+
"rollout-dev": "npm run build && npm run deploy-dev",
|
|
28
30
|
"rollout": "npm run build && npm run deploy"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"browserify": "17.x",
|
|
32
34
|
"chai": "4.x",
|
|
33
35
|
"eslint": "8.x",
|
|
36
|
+
"less": "4.x",
|
|
34
37
|
"mocha": "10.x"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
@@ -64,6 +67,22 @@ function MyGadget() {
|
|
|
64
67
|
module.exports = { MyGadget };
|
|
65
68
|
```
|
|
66
69
|
|
|
70
|
+
Also should also create a starting point for your CSS `src/less/main.less`:
|
|
71
|
+
```css
|
|
72
|
+
@primary-color: darken(#3498db, 10%);
|
|
73
|
+
@secondary-color: darken(#e74c3c, 20%);
|
|
74
|
+
|
|
75
|
+
.yourGadgetName {
|
|
76
|
+
background-color: @primary-color;
|
|
77
|
+
color: white;
|
|
78
|
+
|
|
79
|
+
.header {
|
|
80
|
+
background-color: @secondary-color;
|
|
81
|
+
text-align: center;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
67
86
|
Remember to install tools after changing `package.json`:
|
|
68
87
|
```bash
|
|
69
88
|
npm i
|
|
@@ -94,12 +113,13 @@ Add `.vscode/extensions.json` to help install a set of **extensions**:
|
|
|
94
113
|
VSC **settings** `.vscode/settings.json`:
|
|
95
114
|
```js
|
|
96
115
|
{
|
|
116
|
+
"editor.detectIndentation": false,
|
|
117
|
+
"editor.useTabStops": true,
|
|
118
|
+
"editor.insertSpaces": false,
|
|
97
119
|
"search.exclude": {
|
|
98
120
|
"package-lock.json": true,
|
|
121
|
+
"**/dist/": true,
|
|
99
122
|
},
|
|
100
|
-
"editor.detectIndentation": false,
|
|
101
|
-
"editor.useTabStops": true,
|
|
102
|
-
"editor.insertSpaces": false,
|
|
103
123
|
}
|
|
104
124
|
```
|
|
105
125
|
|
|
@@ -238,37 +258,64 @@ export const username = 'Nux@Wikiploy';
|
|
|
238
258
|
export const password = '0abc...fckgw';
|
|
239
259
|
```
|
|
240
260
|
|
|
261
|
+
Your **common deployment script** `wikiploy-common.mjs`:
|
|
262
|
+
```js
|
|
263
|
+
import { DeployConfig, userPrompt } from 'wikiploy';
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Add config.
|
|
267
|
+
* @param {Array} configs DeployConfig array.
|
|
268
|
+
* @param {String} site Domian of a MW site.
|
|
269
|
+
*/
|
|
270
|
+
export function addConfig(configs, site) {
|
|
271
|
+
configs.push(new DeployConfig({
|
|
272
|
+
src: 'dist/yourGadgetName.js',
|
|
273
|
+
dst: '~/yourGadgetName.js',
|
|
274
|
+
site,
|
|
275
|
+
nowiki: true,
|
|
276
|
+
}));
|
|
277
|
+
configs.push(new DeployConfig({
|
|
278
|
+
src: 'dist/yourGadgetName.css',
|
|
279
|
+
dst: '~/yourGadgetName.css',
|
|
280
|
+
site,
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Read and setup summary.
|
|
286
|
+
* @param {WikiployLite} ployBot
|
|
287
|
+
*/
|
|
288
|
+
export async function setupSummary(ployBot) {
|
|
289
|
+
const summary = await userPrompt('Summary of changes (empty for default summary):');
|
|
290
|
+
if (typeof summary === 'string' && summary.length) {
|
|
291
|
+
ployBot.summary = () => {
|
|
292
|
+
return summary;
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
We use a separate file to define common functions, making changes to both release and dev Wikiploy scripts simpler.
|
|
298
|
+
|
|
299
|
+
Note that `~` in `dst` properties will be replaced with `User:SomeName`, representing the user space of the currently logged-in user (your user space). For more details about this basic code and `DeployConfig` properties, refer to the [Wikiploy rollout example](https://github.com/Eccenux/wikiploy-rollout-example/).
|
|
300
|
+
|
|
241
301
|
Your minimal **developer deployment script** `wikiploy-dev.mjs`:
|
|
242
302
|
```js
|
|
243
|
-
import {
|
|
303
|
+
import { WikiployLite } from 'wikiploy';
|
|
244
304
|
|
|
245
|
-
import * as botpass from './bot.config.
|
|
305
|
+
import * as botpass from './bot.config.mjs';
|
|
246
306
|
const ployBot = new WikiployLite(botpass);
|
|
247
307
|
|
|
248
|
-
//
|
|
249
|
-
|
|
308
|
+
// common deploy function(s)
|
|
309
|
+
import { addConfig, setupSummary } from './wikiploy-common.mjs';
|
|
250
310
|
|
|
251
311
|
// run asynchronously to be able to wait for results
|
|
252
312
|
(async () => {
|
|
253
313
|
// custom summary from a prompt
|
|
254
|
-
|
|
255
|
-
if (typeof summary === 'string' && summary.length) {
|
|
256
|
-
ployBot.summary = () => {
|
|
257
|
-
return summary;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
314
|
+
await setupSummary(ployBot);
|
|
260
315
|
|
|
261
316
|
// push out file(s) to wiki
|
|
262
317
|
const configs = [];
|
|
263
|
-
configs.
|
|
264
|
-
src: 'dist/yourGadetName.js',
|
|
265
|
-
dst: '~/yourGadetName.js',
|
|
266
|
-
nowiki: true,
|
|
267
|
-
}));
|
|
268
|
-
configs.push(new DeployConfig({
|
|
269
|
-
src: 'dist/yourGadetName.css',
|
|
270
|
-
dst: '~/yourGadetName.css',
|
|
271
|
-
}));
|
|
318
|
+
addConfig(configs, 'en.wikipedia.org');
|
|
272
319
|
|
|
273
320
|
await ployBot.deploy(configs);
|
|
274
321
|
|
|
@@ -278,35 +325,26 @@ ployBot.site = "en.wikipedia.org";
|
|
|
278
325
|
});
|
|
279
326
|
```
|
|
280
327
|
|
|
281
|
-
|
|
282
|
-
You can omit `dst` and it will default to `dst: '~/${src}'`. More about this basic code and `dst` in the [Wikiploy rollout example](https://github.com/Eccenux/wikiploy-rollout-example/).
|
|
283
|
-
|
|
284
|
-
Config for your **main deployment script** `wikiploy.mjs`:
|
|
328
|
+
Your **main deployment script** `wikiploy.mjs`:
|
|
285
329
|
```js
|
|
286
|
-
|
|
330
|
+
import { WikiployLite } from 'wikiploy';
|
|
331
|
+
|
|
332
|
+
import * as botpass from './bot.config.mjs';
|
|
333
|
+
const ployBot = new WikiployLite(botpass);
|
|
334
|
+
|
|
335
|
+
// common deploy function(s)
|
|
336
|
+
import { addConfig, setupSummary } from './wikiploy-common.mjs';
|
|
337
|
+
|
|
338
|
+
// run asynchronously to be able to wait for results
|
|
339
|
+
(async () => {
|
|
340
|
+
// custom summary from a prompt
|
|
341
|
+
await setupSummary(ployBot);
|
|
287
342
|
|
|
288
343
|
// push out file(s) to wiki
|
|
289
|
-
// dev version
|
|
290
344
|
const configs = [];
|
|
291
|
-
configs.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
nowiki: true,
|
|
295
|
-
}));
|
|
296
|
-
configs.push(new DeployConfig({
|
|
297
|
-
src: 'dist/yourGadetName.css',
|
|
298
|
-
dst: '~/yourGadetName.css',
|
|
299
|
-
}));
|
|
300
|
-
// gadget
|
|
301
|
-
configs.push(new DeployConfig({
|
|
302
|
-
src: 'dist/yourGadetName.js',
|
|
303
|
-
dst: 'MediaWiki:Gadget-yourGadetName.js',
|
|
304
|
-
nowiki: true,
|
|
305
|
-
}));
|
|
306
|
-
configs.push(new DeployConfig({
|
|
307
|
-
src: 'dist/yourGadetName.css',
|
|
308
|
-
dst: 'MediaWiki:Gadget-yourGadetName.css',
|
|
309
|
-
}));
|
|
345
|
+
addConfig(configs, 'pl.wikipedia.org');
|
|
346
|
+
addConfig(configs, 'en.wikipedia.org');
|
|
347
|
+
addConfig(configs, 'pl.wikisource.org');
|
|
310
348
|
|
|
311
349
|
await ployBot.deploy(configs);
|
|
312
350
|
|
|
@@ -316,31 +354,97 @@ Config for your **main deployment script** `wikiploy.mjs`:
|
|
|
316
354
|
});
|
|
317
355
|
```
|
|
318
356
|
|
|
319
|
-
|
|
357
|
+
## Appendix: Release vs dev release
|
|
358
|
+
|
|
359
|
+
### Deploying as a gadget
|
|
360
|
+
|
|
361
|
+
There are various ways you could develop gadgets. The most standard way would be to deploy to the `MediaWiki:Gadget-` namespace.
|
|
362
|
+
|
|
363
|
+
In `wikiploy-common.mjs` add:
|
|
364
|
+
```js
|
|
365
|
+
export function addConfigRelease(configs, site) {
|
|
366
|
+
configs.push(new DeployConfig({
|
|
367
|
+
src: 'dist/yourGadgetName.js',
|
|
368
|
+
dst: 'MediaWiki:Gadget-yourGadgetName.js',
|
|
369
|
+
site,
|
|
370
|
+
nowiki: true,
|
|
371
|
+
}));
|
|
372
|
+
configs.push(new DeployConfig({
|
|
373
|
+
src: 'dist/yourGadgetName.css',
|
|
374
|
+
dst: 'MediaWiki:Gadget-yourGadgetName.css',
|
|
375
|
+
site,
|
|
376
|
+
}));
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
In your main deployment script you could have something like (`wikiploy.mjs`):
|
|
381
|
+
```js
|
|
382
|
+
// dev version
|
|
383
|
+
addConfig(configs, 'en.wikipedia.org');
|
|
384
|
+
// release versions
|
|
385
|
+
addConfigRelease(configs, 'pl.wikipedia.org');
|
|
386
|
+
addConfigRelease(configs, 'pl.wikisource.org');
|
|
387
|
+
addConfigRelease(configs, 'en.wikipedia.org');
|
|
388
|
+
```
|
|
389
|
+
The script doesn't have any limits. However, you do need interface-admin rights to deploy to the `MediaWiki:Gadget-` namespace. So, you might need to use `~/` to deploy to your user-space.
|
|
390
|
+
|
|
391
|
+
### Release version in your user space
|
|
320
392
|
|
|
321
|
-
|
|
393
|
+
**If you do *not* have interface-admin rights**, the functions could look like this:
|
|
322
394
|
|
|
323
|
-
Though we only deploy to `site = "en.wikipedia.org"` you could use other configs to deploy to many wikis:
|
|
324
395
|
```js
|
|
325
|
-
function addConfig(configs, site) {
|
|
396
|
+
export function addConfig(configs, site, isRelease) {
|
|
397
|
+
let deploymentName = isRelease ? '~/yourGadgetName' : '~/yourGadgetName-dev';
|
|
326
398
|
configs.push(new DeployConfig({
|
|
327
|
-
src: 'dist/
|
|
328
|
-
dst:
|
|
399
|
+
src: 'dist/yourGadgetName.js',
|
|
400
|
+
dst: `${deploymentName}.js`,
|
|
329
401
|
site,
|
|
330
402
|
nowiki: true,
|
|
331
403
|
}));
|
|
332
404
|
configs.push(new DeployConfig({
|
|
333
|
-
src: 'dist/
|
|
334
|
-
dst:
|
|
405
|
+
src: 'dist/yourGadgetName.css',
|
|
406
|
+
dst: `${deploymentName}.css`,
|
|
335
407
|
site,
|
|
336
408
|
}));
|
|
337
409
|
}
|
|
338
|
-
|
|
339
|
-
addConfig(configs,
|
|
410
|
+
export function addConfigRelease(configs, site) {
|
|
411
|
+
addConfig(configs, site, true);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
In this case, you have both dev and release versions in your user space. Frankly, this might be the best choice, even if you have admin rights, especially if you use the *loader pattern*.
|
|
416
|
+
|
|
417
|
+
### Loader pattern for gadgets
|
|
418
|
+
|
|
419
|
+
Even if you have all administrative rights on a wiki, you might still want to load the main script from a small loader. The **loader pattern** allows you to provide conditions for loading the main script from the gadget file.
|
|
420
|
+
|
|
421
|
+
The loader pattern is most useful when you want to test specific capabilities of a device and only then load the script. This is how you could load popups (which don't work well on touch devices, i.e., devices that cannot hover).
|
|
422
|
+
|
|
423
|
+
Example of what `MediaWiki:Gadget-Popups.js` could look like:
|
|
424
|
+
```js
|
|
425
|
+
// only on devices that can hover (not on touch-only, so not on smartphones)
|
|
426
|
+
if (window.matchMedia && !window.matchMedia("(hover: none)").matches) {
|
|
427
|
+
importScript('User:Nux/Popups.js');
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
You could also have a gadget that is default, but only works on specific pages:
|
|
432
|
+
```js
|
|
433
|
+
// load config
|
|
434
|
+
var config = mw.config.get( [
|
|
435
|
+
'wgNamespaceNumber',
|
|
436
|
+
'wgTitle',
|
|
437
|
+
] );
|
|
438
|
+
// only on AfD subpages
|
|
439
|
+
if ( config.wgNamespaceNumber == 4 && config.wgTitle.startsWith('Articles for deletion/') ) {
|
|
440
|
+
importScript('User:Nux/AfD-helper.js');
|
|
441
|
+
}
|
|
340
442
|
```
|
|
341
|
-
The
|
|
443
|
+
The loader pattern is beneficial because it improves website performance. In this pattern, the main gadget code is only loaded when it can be useful.
|
|
444
|
+
|
|
445
|
+
Loading from the `User` namespace might also help make it clear who is responsible for the gadget. However, it might not work when there is no single user responsible, such as when the main author becomes inactive. The community can always decide to fork the script, so that should not be a problem in practice.
|
|
342
446
|
|
|
343
|
-
## Exporting stuff
|
|
447
|
+
## Appendix: Exporting stuff
|
|
344
448
|
|
|
345
449
|
Note that *browserify* will wrap your code with a function automatically, so you don't have to worry about leaking variables to the global scope (you should still remember to define variables with `var`, `let`, or `const`).
|
|
346
450
|
|
|
@@ -353,14 +457,14 @@ Exposing things in `main.js` could look something like this:
|
|
|
353
457
|
```js
|
|
354
458
|
var { MyGadget } = require("./MyGadget");
|
|
355
459
|
|
|
356
|
-
//
|
|
460
|
+
// instance
|
|
357
461
|
var gadget = new MyGadget();
|
|
358
462
|
|
|
359
463
|
// expose for external usage (*not* recommended)
|
|
360
|
-
window.
|
|
464
|
+
window.yourGadgetName = gadget;
|
|
361
465
|
|
|
362
466
|
// hook when object is ready
|
|
363
|
-
mw.hook('userjs.
|
|
467
|
+
mw.hook('userjs.yourGadgetName.loaded').fire(gadget);
|
|
364
468
|
|
|
365
469
|
$(function(){
|
|
366
470
|
// load Mediwiki core dependency
|
|
@@ -369,7 +473,7 @@ $(function(){
|
|
|
369
473
|
gadget.init();
|
|
370
474
|
|
|
371
475
|
// hook when initial elements are ready
|
|
372
|
-
mw.hook('userjs.
|
|
476
|
+
mw.hook('userjs.yourGadgetName.ready').fire(gadget);
|
|
373
477
|
});
|
|
374
478
|
});
|
|
375
479
|
```
|
|
@@ -378,20 +482,20 @@ As you can see, hooks are better because you can keep the name of the variable s
|
|
|
378
482
|
|
|
379
483
|
User options:
|
|
380
484
|
```js
|
|
381
|
-
mw.hook('userjs.
|
|
485
|
+
mw.hook('userjs.yourGadgetName.loaded').add(function (gadget) {
|
|
382
486
|
gadget.options.createTool = false;
|
|
383
487
|
});
|
|
384
|
-
importScript('User:Nux/
|
|
488
|
+
importScript('User:Nux/yourGadgetName.js');
|
|
385
489
|
```
|
|
386
490
|
A more advanced customization:
|
|
387
491
|
```js
|
|
388
|
-
mw.hook('userjs.
|
|
389
|
-
var $tool = $('#some-unique-
|
|
492
|
+
mw.hook('userjs.yourGadgetName.ready').add(function (gadget) {
|
|
493
|
+
var $tool = $('#some-unique-gadget-tool a');
|
|
390
494
|
$tool.text($tool.text() + ' & plugin');
|
|
391
495
|
});
|
|
392
496
|
```
|
|
393
497
|
|
|
394
|
-
## Using classes
|
|
498
|
+
## Appendix: Using classes
|
|
395
499
|
|
|
396
500
|
JS has classes for a long time. They don't work in IE and you cannot use them in default gadgets on Wikipedia. If you are building a default gadget you might want to add [babeljs](https://babeljs.io/) as a build step.
|
|
397
501
|
```bash
|
|
@@ -404,12 +508,20 @@ browserify script.js -t babelify --outfile bundle.js
|
|
|
404
508
|
If the gadget is intended for advanced users it is safe to assume they won't use IE. So for most scripts you can probably skip babelify. But as of 2023 you will need `requiresES6` option in the gadget definition.
|
|
405
509
|
|
|
406
510
|
```
|
|
407
|
-
|
|
511
|
+
yourClassyGadgetName [ResourceLoader | requiresES6 | dependencies = mediawiki.util] | yourGadgetName.js | yourGadgetName.css
|
|
408
512
|
```
|
|
409
513
|
|
|
410
514
|
|
|
411
515
|
Example `MyGadget.js` with a link in the toolbar:
|
|
412
516
|
```js
|
|
517
|
+
/**
|
|
518
|
+
* Wikiploy gadget example.
|
|
519
|
+
*
|
|
520
|
+
* History and docs:
|
|
521
|
+
* https://github.com/Eccenux/wikiploy-rollout-example
|
|
522
|
+
*
|
|
523
|
+
* Deployed using: [[Wikipedia:Wikiploy]]
|
|
524
|
+
*/
|
|
413
525
|
class MyGadget {
|
|
414
526
|
constructor() {
|
|
415
527
|
this.options = {
|
|
@@ -425,9 +537,9 @@ class MyGadget {
|
|
|
425
537
|
var linkLabel = 'My gadget dialog';
|
|
426
538
|
var itemId = 'some-unique-gadget-tool';
|
|
427
539
|
var item = mw.util.addPortletLink(portletId, '#', linkLabel, itemId);
|
|
428
|
-
$(item).on('click',
|
|
540
|
+
$(item).on('click', (evt) => {
|
|
429
541
|
evt.preventDefault();
|
|
430
|
-
|
|
542
|
+
this.openDialog();
|
|
431
543
|
});
|
|
432
544
|
}
|
|
433
545
|
}
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ The `WikiployLite` class is using a bot API to deploy scripts. You can use stand
|
|
|
52
52
|
Botpass configuration:
|
|
53
53
|
* Setup on e.g.: https://test.wikipedia.org/wiki/Special:BotPasswords
|
|
54
54
|
* Rights you should setup (if you can): `assets\Bot passwords - Test Wikipedia.png`.
|
|
55
|
-
* Example config file in: `assets\bot.config.
|
|
55
|
+
* Example config file in: `assets\public--bot.config.mjs`.
|
|
56
56
|
|
|
57
57
|
**Warning!** Never, ever publish your bot password. If you do spill your password, reset/remove the password ASAP (on Special:BotPasswords).
|
|
58
58
|
|
package/assets/test.css
CHANGED
package/assets/test.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikiploy",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "User scripts and gadgets deployment for MediaWiki (Wikipedia).",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"homepage": "https://github.com/Eccenux/Wikiploy#readme",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"mwn": "2.0.x",
|
|
30
|
-
"puppeteer": "21.
|
|
30
|
+
"puppeteer": "21.7.x"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"bump-version": "0.5.0",
|
package/src/ploy_test_botapi.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as ver from './version.js';
|
|
|
7
7
|
|
|
8
8
|
// https://test.wikipedia.org/wiki/Special:BotPasswords
|
|
9
9
|
// Note that username should contain `@`.
|
|
10
|
-
import * as botpass from './bot.config.
|
|
10
|
+
import * as botpass from './bot.config.mjs';
|
|
11
11
|
|
|
12
12
|
const version = await ver.readVersion('./package.json');
|
|
13
13
|
|
package/src/ploy_test_lite.js
CHANGED
|
@@ -3,7 +3,7 @@ import WikiployLite from './WikiployLite.js';
|
|
|
3
3
|
|
|
4
4
|
// https://test.wikipedia.org/wiki/Special:BotPasswords
|
|
5
5
|
// Note that username should contain `@`.
|
|
6
|
-
import * as botpass from './bot.config.
|
|
6
|
+
import * as botpass from './bot.config.mjs';
|
|
7
7
|
|
|
8
8
|
const ployBot = new WikiployLite(botpass);
|
|
9
9
|
// mock
|
|
File without changes
|