@lipemat/js-boilerplate 11.1.0 → 11.2.0-beta.1
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/config/dev-server.config.js +6 -2
- package/config/dev-server.config.js.map +1 -1
- package/config/dev-server.config.ts +7 -2
- package/helpers/dev-server-port.js +50 -0
- package/helpers/dev-server-port.js.map +1 -0
- package/helpers/dev-server-port.ts +54 -0
- package/package.json +1 -1
- package/scripts/start.js +9 -2
- package/scripts/start.js.map +1 -1
- package/scripts/start.ts +10 -2
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { getPackageConfig } from '@lipemat/js-boilerplate-shared/helpers/package-config.js';
|
|
3
|
+
import { DEV_SERVER_PORT_START } from '../helpers/dev-server-port.js';
|
|
3
4
|
const packageConfig = getPackageConfig();
|
|
4
5
|
const url = new URL(packageConfig.url);
|
|
6
|
+
const port = 'undefined' !== typeof process.env.LIPEMAT_DEV_SERVER_PORT
|
|
7
|
+
? Number(process.env.LIPEMAT_DEV_SERVER_PORT)
|
|
8
|
+
: DEV_SERVER_PORT_START;
|
|
5
9
|
let server = 'https:' === url.protocol ? 'https' : 'http';
|
|
6
10
|
// Load local certificates for https during development.
|
|
7
11
|
if ('object' === typeof (packageConfig.certificates)) {
|
|
@@ -23,7 +27,7 @@ const config = {
|
|
|
23
27
|
},
|
|
24
28
|
webSocketURL: {
|
|
25
29
|
hostname: url.hostname,
|
|
26
|
-
port
|
|
30
|
+
port,
|
|
27
31
|
protocol: 'wss',
|
|
28
32
|
},
|
|
29
33
|
},
|
|
@@ -34,7 +38,7 @@ const config = {
|
|
|
34
38
|
},
|
|
35
39
|
host: '0.0.0.0', // Allow connections from any IP.
|
|
36
40
|
hot: true,
|
|
37
|
-
port
|
|
41
|
+
port,
|
|
38
42
|
server,
|
|
39
43
|
static: false,
|
|
40
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.config.js","sourceRoot":"","sources":["dev-server.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAC,gBAAgB,EAAC,MAAM,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"dev-server.config.js","sourceRoot":"","sources":["dev-server.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAC,gBAAgB,EAAC,MAAM,0DAA0D,CAAC;AAC1F,OAAO,EAAC,qBAAqB,EAAC,MAAM,+BAA+B,CAAC;AAGpE,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;AAEzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAE,aAAa,CAAC,GAAG,CAAE,CAAC;AAEzC,MAAM,IAAI,GAAG,WAAW,KAAK,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB;IACtE,CAAC,CAAC,MAAM,CAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAE;IAC/C,CAAC,CAAC,qBAAqB,CAAC;AAEzB,IAAI,MAAM,GAA4B,QAAQ,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACnF,wDAAwD;AACxD,IAAK,QAAQ,KAAK,OAAO,CAAE,aAAa,CAAC,YAAY,CAAE,EAAG,CAAC;IAC1D,MAAM,GAAG;QACR,IAAI,EAAE,OAAO;QACb,OAAO,EAAE;YACR,IAAI,EAAE,EAAE,CAAC,YAAY,CAAE,aAAa,CAAC,YAAY,CAAC,IAAI,CAAE;YACxD,GAAG,EAAE,EAAE,CAAC,YAAY,CAAE,aAAa,CAAC,YAAY,CAAC,GAAG,CAAE;SACtD;KACD,CAAC;AACH,CAAC;AAED,MAAM,MAAM,GAAkB;IAC7B,YAAY,EAAE,KAAK;IACnB,MAAM,EAAE;QACP,OAAO,EAAE,MAAM;QACf,OAAO,EAAE;YACR,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,KAAK;SACf;QACD,YAAY,EAAE;YACb,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,IAAI;YACJ,QAAQ,EAAE,KAAK;SACf;KACD;IACD,OAAO,EAAE;QACR,6BAA6B,EAAE,GAAG;QAClC,8BAA8B,EAAE,GAAG;QACnC,8BAA8B,EAAE,GAAG;KACnC;IACD,IAAI,EAAE,SAAS,EAAE,iCAAiC;IAClD,GAAG,EAAE,IAAI;IACT,IAAI;IACJ,MAAM;IACN,MAAM,EAAE,KAAK;CACb,CAAA;AAED,eAAe,MAAM,CAAC"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import {getPackageConfig} from '@lipemat/js-boilerplate-shared/helpers/package-config.js';
|
|
3
|
+
import {DEV_SERVER_PORT_START} from '../helpers/dev-server-port.js';
|
|
3
4
|
import type {Configuration} from 'webpack-dev-server';
|
|
4
5
|
|
|
5
6
|
const packageConfig = getPackageConfig();
|
|
6
7
|
|
|
7
8
|
const url = new URL( packageConfig.url );
|
|
8
9
|
|
|
10
|
+
const port = 'undefined' !== typeof process.env.LIPEMAT_DEV_SERVER_PORT
|
|
11
|
+
? Number( process.env.LIPEMAT_DEV_SERVER_PORT )
|
|
12
|
+
: DEV_SERVER_PORT_START;
|
|
13
|
+
|
|
9
14
|
let server: Configuration['server'] = 'https:' === url.protocol ? 'https' : 'http';
|
|
10
15
|
// Load local certificates for https during development.
|
|
11
16
|
if ( 'object' === typeof ( packageConfig.certificates ) ) {
|
|
@@ -28,7 +33,7 @@ const config: Configuration = {
|
|
|
28
33
|
},
|
|
29
34
|
webSocketURL: {
|
|
30
35
|
hostname: url.hostname,
|
|
31
|
-
port
|
|
36
|
+
port,
|
|
32
37
|
protocol: 'wss',
|
|
33
38
|
},
|
|
34
39
|
},
|
|
@@ -39,7 +44,7 @@ const config: Configuration = {
|
|
|
39
44
|
},
|
|
40
45
|
host: '0.0.0.0', // Allow connections from any IP.
|
|
41
46
|
hot: true,
|
|
42
|
-
port
|
|
47
|
+
port,
|
|
43
48
|
server,
|
|
44
49
|
static: false,
|
|
45
50
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
/**
|
|
3
|
+
* First port within the dev server range, and the default port.
|
|
4
|
+
*/
|
|
5
|
+
export const DEV_SERVER_PORT_START = 3000;
|
|
6
|
+
/**
|
|
7
|
+
* Number of ports available to the dev server.
|
|
8
|
+
*
|
|
9
|
+
* Capped to the maximum number of simultaneous PHP dev servers.
|
|
10
|
+
*/
|
|
11
|
+
export const DEV_SERVER_PORT_RANGE = 20;
|
|
12
|
+
const DEV_SERVER_HOST = '0.0.0.0';
|
|
13
|
+
/**
|
|
14
|
+
* Check if a single port is free to bind to.
|
|
15
|
+
*
|
|
16
|
+
* @param {number} port - Port to check.
|
|
17
|
+
*
|
|
18
|
+
* @return {Promise<boolean>}
|
|
19
|
+
*/
|
|
20
|
+
function isPortFree(port) {
|
|
21
|
+
return new Promise(resolve => {
|
|
22
|
+
const server = net.createServer();
|
|
23
|
+
server.unref();
|
|
24
|
+
server.on('error', () => {
|
|
25
|
+
resolve(false);
|
|
26
|
+
});
|
|
27
|
+
server.listen({ host: DEV_SERVER_HOST, port }, () => {
|
|
28
|
+
server.close(() => {
|
|
29
|
+
resolve(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the first free port within the dev server range.
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error} - When every port within the range is in use.
|
|
38
|
+
*
|
|
39
|
+
* @return {Promise<number>}
|
|
40
|
+
*/
|
|
41
|
+
export async function getDevServerPort() {
|
|
42
|
+
const lastPort = DEV_SERVER_PORT_START + DEV_SERVER_PORT_RANGE - 1;
|
|
43
|
+
for (let port = DEV_SERVER_PORT_START; port <= lastPort; port++) {
|
|
44
|
+
if (await isPortFree(port)) {
|
|
45
|
+
return port;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Unable to start the dev server: all ${DEV_SERVER_PORT_RANGE} ports (${DEV_SERVER_PORT_START}-${lastPort}) are in use.`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=dev-server-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server-port.js","sourceRoot":"","sources":["dev-server-port.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAExC,MAAM,eAAe,GAAG,SAAS,CAAC;AAElC;;;;;;GAMG;AACH,SAAS,UAAU,CAAE,IAAY;IAChC,OAAO,IAAI,OAAO,CAAE,OAAO,CAAC,EAAE;QAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,EAAE,CAAE,OAAO,EAAE,GAAG,EAAE;YACxB,OAAO,CAAE,KAAK,CAAE,CAAC;QAClB,CAAC,CAAE,CAAC;QACJ,MAAM,CAAC,MAAM,CAAE,EAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAC,EAAE,GAAG,EAAE;YAClD,MAAM,CAAC,KAAK,CAAE,GAAG,EAAE;gBAClB,OAAO,CAAE,IAAI,CAAE,CAAC;YACjB,CAAC,CAAE,CAAC;QACL,CAAC,CAAE,CAAC;IACL,CAAC,CAAE,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACrC,MAAM,QAAQ,GAAG,qBAAqB,GAAG,qBAAqB,GAAG,CAAC,CAAC;IACnE,KAAM,IAAI,IAAI,GAAG,qBAAqB,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,EAAE,EAAG,CAAC;QACnE,IAAK,MAAM,UAAU,CAAE,IAAI,CAAE,EAAG,CAAC;YAChC,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IACD,MAAM,IAAI,KAAK,CAAE,uCAAuC,qBAAqB,WAAW,qBAAqB,IAAI,QAAQ,eAAe,CAAE,CAAC;AAC5I,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* First port within the dev server range, and the default port.
|
|
5
|
+
*/
|
|
6
|
+
export const DEV_SERVER_PORT_START = 3000;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Number of ports available to the dev server.
|
|
10
|
+
*
|
|
11
|
+
* Capped to the maximum number of simultaneous PHP dev servers.
|
|
12
|
+
*/
|
|
13
|
+
export const DEV_SERVER_PORT_RANGE = 20;
|
|
14
|
+
|
|
15
|
+
const DEV_SERVER_HOST = '0.0.0.0';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if a single port is free to bind to.
|
|
19
|
+
*
|
|
20
|
+
* @param {number} port - Port to check.
|
|
21
|
+
*
|
|
22
|
+
* @return {Promise<boolean>}
|
|
23
|
+
*/
|
|
24
|
+
function isPortFree( port: number ): Promise<boolean> {
|
|
25
|
+
return new Promise( resolve => {
|
|
26
|
+
const server = net.createServer();
|
|
27
|
+
server.unref();
|
|
28
|
+
server.on( 'error', () => {
|
|
29
|
+
resolve( false );
|
|
30
|
+
} );
|
|
31
|
+
server.listen( {host: DEV_SERVER_HOST, port}, () => {
|
|
32
|
+
server.close( () => {
|
|
33
|
+
resolve( true );
|
|
34
|
+
} );
|
|
35
|
+
} );
|
|
36
|
+
} );
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the first free port within the dev server range.
|
|
41
|
+
*
|
|
42
|
+
* @throws {Error} - When every port within the range is in use.
|
|
43
|
+
*
|
|
44
|
+
* @return {Promise<number>}
|
|
45
|
+
*/
|
|
46
|
+
export async function getDevServerPort(): Promise<number> {
|
|
47
|
+
const lastPort = DEV_SERVER_PORT_START + DEV_SERVER_PORT_RANGE - 1;
|
|
48
|
+
for ( let port = DEV_SERVER_PORT_START; port <= lastPort; port++ ) {
|
|
49
|
+
if ( await isPortFree( port ) ) {
|
|
50
|
+
return port;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new Error( `Unable to start the dev server: all ${DEV_SERVER_PORT_RANGE} ports (${DEV_SERVER_PORT_START}-${lastPort}) are in use.` );
|
|
54
|
+
}
|
package/package.json
CHANGED
package/scripts/start.js
CHANGED
|
@@ -5,7 +5,9 @@ import { getPackageConfig } from '@lipemat/js-boilerplate-shared/helpers/package
|
|
|
5
5
|
import { unlinkSync, writeFile } from 'fs';
|
|
6
6
|
import WebpackDevServer from 'webpack-dev-server';
|
|
7
7
|
import { getConfig } from '../helpers/config.js';
|
|
8
|
+
import { getDevServerPort } from '../helpers/dev-server-port.js';
|
|
8
9
|
import path from 'path';
|
|
10
|
+
process.env.LIPEMAT_DEV_SERVER_PORT = String(await getDevServerPort());
|
|
9
11
|
const webpackConfig = await getConfig('webpack.dev.js');
|
|
10
12
|
const devServerConfig = await getConfig('dev-server.config.js');
|
|
11
13
|
const compiler = webpack(webpackConfig);
|
|
@@ -17,12 +19,17 @@ const server = new WebpackDevServer(devServerConfig, compiler);
|
|
|
17
19
|
* Create a `.running` file within the `dist` which only
|
|
18
20
|
* exists if this script is running.
|
|
19
21
|
*/
|
|
20
|
-
|
|
22
|
+
const runningFile = path.resolve(getPackageConfig().workingDirectory, 'dist/.running');
|
|
23
|
+
writeFile(runningFile, JSON.stringify({
|
|
24
|
+
pid: process.pid,
|
|
25
|
+
port: Number(process.env.LIPEMAT_DEV_SERVER_PORT),
|
|
26
|
+
started: new Date().toISOString(),
|
|
27
|
+
}), err => {
|
|
21
28
|
if (err) {
|
|
22
29
|
throw err;
|
|
23
30
|
}
|
|
24
31
|
process.on('exit', () => {
|
|
25
|
-
unlinkSync(
|
|
32
|
+
unlinkSync(runningFile);
|
|
26
33
|
});
|
|
27
34
|
});
|
|
28
35
|
(async () => {
|
package/scripts/start.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.js","sourceRoot":"","sources":["start.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC;AACtC,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,aAAa,CAAC;AAErC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,0DAA0D,CAAC;AAC1F,OAAO,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,IAAI,CAAC;AACzC,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAC;AAC/C,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,aAAa,GAAG,MAAM,SAAS,CAAE,gBAAgB,CAAE,CAAC;AAC1D,MAAM,eAAe,GAAG,MAAM,SAAS,CAAE,sBAAsB,CAAE,CAAC;AAElE,MAAM,QAAQ,GAAG,OAAO,CAAE,aAAa,CAAE,CAAC;AAC1C,IAAK,IAAI,KAAK,QAAQ,EAAG,CAAC;IACzB,MAAM,IAAI,KAAK,CAAE,wCAAwC,CAAE,CAAC;AAC7D,CAAC;AACD,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAE,eAAe,EAAE,QAAQ,CAAE,CAAC;AAEjE;;;GAGG;AACH,
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["start.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC;AACtC,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,aAAa,CAAC;AAErC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,0DAA0D,CAAC;AAC1F,OAAO,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,IAAI,CAAC;AACzC,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAC/D,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,CAAC,GAAG,CAAC,uBAAuB,GAAG,MAAM,CAAE,MAAM,gBAAgB,EAAE,CAAE,CAAC;AAEzE,MAAM,aAAa,GAAG,MAAM,SAAS,CAAE,gBAAgB,CAAE,CAAC;AAC1D,MAAM,eAAe,GAAG,MAAM,SAAS,CAAE,sBAAsB,CAAE,CAAC;AAElE,MAAM,QAAQ,GAAG,OAAO,CAAE,aAAa,CAAE,CAAC;AAC1C,IAAK,IAAI,KAAK,QAAQ,EAAG,CAAC;IACzB,MAAM,IAAI,KAAK,CAAE,wCAAwC,CAAE,CAAC;AAC7D,CAAC;AACD,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAE,eAAe,EAAE,QAAQ,CAAE,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAE,gBAAgB,EAAE,CAAC,gBAAgB,EAAE,eAAe,CAAE,CAAC;AACzF,SAAS,CAAE,WAAW,EAAE,IAAI,CAAC,SAAS,CAAE;IACvC,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,IAAI,EAAE,MAAM,CAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAE;IACnD,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;CACjC,CAAE,EAAE,GAAG,CAAC,EAAE;IACV,IAAK,GAAG,EAAG,CAAC;QACX,MAAM,GAAG,CAAC;IACX,CAAC;IACD,OAAO,CAAC,EAAE,CAAE,MAAM,EAAE,GAAG,EAAE;QACxB,UAAU,CAAE,WAAW,CAAE,CAAC;IAC3B,CAAC,CAAE,CAAC;AACL,CAAC,CAAE,CAAC;AAEJ,CAAE,KAAK,IAAG,EAAE;IACX,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAAC,OAAQ,GAAG,EAAG,CAAC;QAChB,OAAO,OAAO,CAAC,KAAK,CAAE,GAAG,CAAE,CAAC;IAC7B,CAAC;AACF,CAAC,CAAE,EAAE,CAAC"}
|
package/scripts/start.ts
CHANGED
|
@@ -6,8 +6,11 @@ import {getPackageConfig} from '@lipemat/js-boilerplate-shared/helpers/package-c
|
|
|
6
6
|
import {unlinkSync, writeFile} from 'fs';
|
|
7
7
|
import WebpackDevServer from 'webpack-dev-server';
|
|
8
8
|
import {getConfig} from '../helpers/config.js';
|
|
9
|
+
import {getDevServerPort} from '../helpers/dev-server-port.js';
|
|
9
10
|
import path from 'path';
|
|
10
11
|
|
|
12
|
+
process.env.LIPEMAT_DEV_SERVER_PORT = String( await getDevServerPort() );
|
|
13
|
+
|
|
11
14
|
const webpackConfig = await getConfig( 'webpack.dev.js' );
|
|
12
15
|
const devServerConfig = await getConfig( 'dev-server.config.js' );
|
|
13
16
|
|
|
@@ -21,12 +24,17 @@ const server = new WebpackDevServer( devServerConfig, compiler );
|
|
|
21
24
|
* Create a `.running` file within the `dist` which only
|
|
22
25
|
* exists if this script is running.
|
|
23
26
|
*/
|
|
24
|
-
|
|
27
|
+
const runningFile = path.resolve( getPackageConfig().workingDirectory, 'dist/.running' );
|
|
28
|
+
writeFile( runningFile, JSON.stringify( {
|
|
29
|
+
pid: process.pid,
|
|
30
|
+
port: Number( process.env.LIPEMAT_DEV_SERVER_PORT ),
|
|
31
|
+
started: new Date().toISOString(),
|
|
32
|
+
} ), err => {
|
|
25
33
|
if ( err ) {
|
|
26
34
|
throw err;
|
|
27
35
|
}
|
|
28
36
|
process.on( 'exit', () => {
|
|
29
|
-
unlinkSync(
|
|
37
|
+
unlinkSync( runningFile );
|
|
30
38
|
} );
|
|
31
39
|
} );
|
|
32
40
|
|