@tramvai/cli 2.147.1 → 2.148.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/README.md +3 -3
- package/lib/api/build/providers/shared.js +4 -3
- package/lib/api/build/providers/shared.js.map +1 -1
- package/lib/api/start/application.js.map +1 -1
- package/lib/api/start/index.js +5 -2
- package/lib/api/start/index.js.map +1 -1
- package/lib/api/start/providers/application/shared.js +2 -5
- package/lib/api/start/providers/application/shared.js.map +1 -1
- package/lib/api/start/providers/child-app/shared.js +2 -2
- package/lib/api/start/providers/child-app/shared.js.map +1 -1
- package/lib/api/start/providers/module/shared.js +2 -2
- package/lib/api/start/providers/module/shared.js.map +1 -1
- package/lib/api/start-prod/application.js +2 -0
- package/lib/api/start-prod/application.js.map +1 -1
- package/lib/api/start-prod/index.js +5 -2
- package/lib/api/start-prod/index.js.map +1 -1
- package/lib/api/start-prod/providers/application.js +2 -5
- package/lib/api/start-prod/providers/application.js.map +1 -1
- package/lib/api/start-prod/providers/child-app.js +2 -5
- package/lib/api/start-prod/providers/child-app.js.map +1 -1
- package/lib/commands/createApp.js +1 -0
- package/lib/commands/createApp.js.map +1 -1
- package/lib/commands/static/application.js +4 -1
- package/lib/commands/static/application.js.map +1 -1
- package/lib/config/configManager.d.ts +0 -3
- package/lib/config/configManager.js +5 -5
- package/lib/config/configManager.js.map +1 -1
- package/lib/di/providers/config.js +1 -1
- package/lib/di/providers/config.js.map +1 -1
- package/lib/di/providers/index.d.ts +1 -0
- package/lib/di/providers/index.js +1 -0
- package/lib/di/providers/index.js.map +1 -1
- package/lib/di/providers/network.d.ts +2 -0
- package/lib/di/providers/network.js +17 -0
- package/lib/di/providers/network.js.map +1 -0
- package/lib/di/tokens/index.d.ts +1 -0
- package/lib/di/tokens/index.js +1 -0
- package/lib/di/tokens/index.js.map +1 -1
- package/lib/di/tokens/network.d.ts +4 -0
- package/lib/di/tokens/network.js +6 -0
- package/lib/di/tokens/network.js.map +1 -0
- package/lib/library/webpack/constants/stats.d.ts +1 -1
- package/lib/library/webpack/utils/threadLoader.js +1 -1
- package/lib/library/webpack/utils/threadLoader.js.map +1 -1
- package/lib/models/config.js +8 -2
- package/lib/models/config.js.map +1 -1
- package/lib/models/port-manager.d.ts +30 -0
- package/lib/models/port-manager.js +72 -0
- package/lib/models/port-manager.js.map +1 -0
- package/lib/schema/topLevelSchema.d.ts +3 -0
- package/lib/schema/topLevelSchema.js +3 -0
- package/lib/schema/topLevelSchema.js.map +1 -1
- package/lib/utils/detectPortSync.d.ts +0 -13
- package/lib/utils/detectPortSync.js +0 -21
- package/lib/utils/detectPortSync.js.map +1 -1
- package/package.json +4 -5
- package/schema.json +3 -0
- package/src/api/build/providers/shared.ts +5 -11
- package/src/api/start/__integration__/start.test.ts +3 -3
- package/src/api/start/application.ts +1 -0
- package/src/api/start/index.ts +5 -2
- package/src/api/start/providers/application/shared.ts +6 -12
- package/src/api/start/providers/child-app/shared.ts +5 -4
- package/src/api/start/providers/module/shared.ts +5 -4
- package/src/api/start-prod/application.ts +5 -1
- package/src/api/start-prod/index.ts +5 -2
- package/src/api/start-prod/providers/application.ts +8 -16
- package/src/api/start-prod/providers/child-app.ts +6 -7
- package/src/commands/createApp.ts +2 -0
- package/src/commands/static/application.ts +7 -0
- package/src/config/configManager.ts +6 -5
- package/src/di/providers/config.ts +1 -1
- package/src/di/providers/index.ts +1 -0
- package/src/di/providers/network.ts +16 -0
- package/src/di/tokens/index.ts +1 -0
- package/src/di/tokens/network.ts +5 -0
- package/src/library/webpack/utils/threadLoader.ts +1 -1
- package/src/models/config.spec.ts +83 -0
- package/src/models/config.ts +8 -2
- package/src/models/port-manager.ts +79 -0
- package/src/schema/topLevelSchema.js +3 -0
- package/src/schema/tramvai.spec.ts +8 -2
- package/src/utils/detectPortSync.ts +0 -24
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
configProviders,
|
|
9
9
|
packageManagerProviders,
|
|
10
10
|
stdProviders,
|
|
11
|
+
networkProviders,
|
|
11
12
|
} from '../di/providers';
|
|
12
13
|
import { COMMAND_MAP_TOKEN, COMMAND_PARAMETERS_TOKEN, COMMAND_RUNNER_TOKEN } from '../di/tokens';
|
|
13
14
|
|
|
@@ -25,6 +26,7 @@ export const createApp = ({
|
|
|
25
26
|
...commandsProviders,
|
|
26
27
|
...packageManagerProviders,
|
|
27
28
|
...builderProviders,
|
|
29
|
+
...networkProviders,
|
|
28
30
|
...providers,
|
|
29
31
|
{
|
|
30
32
|
provide: COMMAND_PARAMETERS_TOKEN,
|
|
@@ -17,6 +17,7 @@ import { startStaticServer } from './staticServer';
|
|
|
17
17
|
import { startServer } from './server';
|
|
18
18
|
import { handleServerOutput } from './utils/handle-server-output';
|
|
19
19
|
import { appBundleInfo } from '../../utils/dev-app/request';
|
|
20
|
+
import { PortManager } from '../../models/port-manager';
|
|
20
21
|
|
|
21
22
|
// eslint-disable-next-line max-statements
|
|
22
23
|
export const staticApp = async (
|
|
@@ -24,11 +25,17 @@ export const staticApp = async (
|
|
|
24
25
|
configEntry: ApplicationConfigEntry,
|
|
25
26
|
options: Params
|
|
26
27
|
): Promise<CommandResult> => {
|
|
28
|
+
const network = new PortManager({ configEntry, commandParams: options });
|
|
29
|
+
|
|
30
|
+
await network.computeAvailablePorts();
|
|
31
|
+
|
|
27
32
|
const clientConfigManager = createConfigManager(configEntry, {
|
|
28
33
|
env: 'production',
|
|
29
34
|
...options,
|
|
30
35
|
buildType: 'client',
|
|
31
36
|
modern: false,
|
|
37
|
+
port: network.port,
|
|
38
|
+
staticPort: network.staticPort,
|
|
32
39
|
});
|
|
33
40
|
const serverConfigManager = clientConfigManager.withSettings({ buildType: 'server' });
|
|
34
41
|
|
|
@@ -13,6 +13,7 @@ import moduleVersion from '../utils/moduleVersion';
|
|
|
13
13
|
import { packageVersion } from '../utils/packageVersion';
|
|
14
14
|
import { showConfig } from './showConfig';
|
|
15
15
|
import type { Target } from '../typings/target';
|
|
16
|
+
import { PortManager } from '../models/port-manager';
|
|
16
17
|
|
|
17
18
|
// @TODO: maybe split settings depending on env?
|
|
18
19
|
export interface Settings<E extends Env> {
|
|
@@ -92,10 +93,7 @@ export type ConfigManager<
|
|
|
92
93
|
assetsPrefix?: string;
|
|
93
94
|
};
|
|
94
95
|
|
|
95
|
-
export const DEFAULT_PORT = 3000;
|
|
96
96
|
export const DEFAULT_STATIC_HOST = 'localhost';
|
|
97
|
-
export const DEFAULT_STATIC_PORT = 4000;
|
|
98
|
-
export const DEFAULT_STATIC_MODULE_PORT = 4040;
|
|
99
97
|
|
|
100
98
|
// eslint-disable-next-line max-statements, complexity
|
|
101
99
|
export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E extends Env = Env>(
|
|
@@ -153,10 +151,13 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
|
|
|
153
151
|
rootDir,
|
|
154
152
|
buildType,
|
|
155
153
|
debug,
|
|
156
|
-
port: Number(settings.port ?? DEFAULT_PORT),
|
|
154
|
+
port: Number(settings.port ?? PortManager.DEFAULT_PORT),
|
|
157
155
|
staticHost: settings.staticHost ?? DEFAULT_STATIC_HOST,
|
|
158
156
|
staticPort: Number(
|
|
159
|
-
settings.staticPort ??
|
|
157
|
+
settings.staticPort ??
|
|
158
|
+
(type === 'module'
|
|
159
|
+
? PortManager.DEFAULT_MODULE_STATIC_PORT
|
|
160
|
+
: PortManager.DEFAULT_STATIC_PORT)
|
|
160
161
|
),
|
|
161
162
|
modern,
|
|
162
163
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -57,7 +57,7 @@ export const configProviders: readonly Provider[] = [
|
|
|
57
57
|
const { content, isSuccessful } = getTramvaiConfig(rootDir);
|
|
58
58
|
|
|
59
59
|
if (!isSuccessful) {
|
|
60
|
-
throw new Error('Config neither passed as parameter
|
|
60
|
+
throw new Error('Config neither passed as parameter nor found in file system');
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const manager = new ConfigManager({ config: content, syncConfigFile: syncJsonFile });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { provide } from '@tinkoff/dippy';
|
|
2
|
+
import type { Provider } from '@tinkoff/dippy';
|
|
3
|
+
|
|
4
|
+
import { COMMAND_PARAMETERS_TOKEN, CONFIG_ENTRY_TOKEN, PORT_MANAGER_TOKEN } from '../tokens';
|
|
5
|
+
import { PortManager } from '../../models/port-manager';
|
|
6
|
+
|
|
7
|
+
export const networkProviders: readonly Provider[] = [
|
|
8
|
+
provide({
|
|
9
|
+
provide: PORT_MANAGER_TOKEN,
|
|
10
|
+
useClass: PortManager,
|
|
11
|
+
deps: {
|
|
12
|
+
configEntry: CONFIG_ENTRY_TOKEN,
|
|
13
|
+
commandParams: COMMAND_PARAMETERS_TOKEN,
|
|
14
|
+
},
|
|
15
|
+
}),
|
|
16
|
+
] as const;
|
package/src/di/tokens/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ const createWorkerPoolConfig = (configManager: ConfigManager<CliConfigEntry>) =>
|
|
|
23
23
|
|
|
24
24
|
const isApplicable = (configManager: ConfigManager<CliConfigEntry>) => {
|
|
25
25
|
return (
|
|
26
|
-
// thread-loader uses child_process.fork
|
|
26
|
+
// thread-loader uses child_process.fork under the hood, and sometimes (50/50) work in these processes does not get into inspector.Session profile
|
|
27
27
|
!process.env.TRAMVAI_CPU_PROFILE &&
|
|
28
28
|
// TODO: check that there is still issue with windows systems and thread-loader
|
|
29
29
|
process.platform !== 'win32' &&
|
|
@@ -440,3 +440,86 @@ it('should populate defaults for overridable options', () => {
|
|
|
440
440
|
}
|
|
441
441
|
`);
|
|
442
442
|
});
|
|
443
|
+
|
|
444
|
+
it('should throw an error if added aditional unknown property', () => {
|
|
445
|
+
const config: any = {
|
|
446
|
+
projects: {
|
|
447
|
+
app: {
|
|
448
|
+
unknownOption: 'something',
|
|
449
|
+
name: 'test-app',
|
|
450
|
+
root: 'src',
|
|
451
|
+
type: 'application',
|
|
452
|
+
output: {
|
|
453
|
+
client: 'assets/compiled',
|
|
454
|
+
},
|
|
455
|
+
sourceMap: false,
|
|
456
|
+
externals: ['test'],
|
|
457
|
+
fileSystemPages: { enabled: true },
|
|
458
|
+
experiments: {
|
|
459
|
+
webpack: {
|
|
460
|
+
backCompat: true,
|
|
461
|
+
},
|
|
462
|
+
transpilation: {
|
|
463
|
+
loader: {
|
|
464
|
+
development: 'swc',
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
dedupe: {
|
|
469
|
+
strategy: 'semver',
|
|
470
|
+
},
|
|
471
|
+
define: {
|
|
472
|
+
shared: {
|
|
473
|
+
'process.env.APP_ID': 'app',
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
svgo: {
|
|
477
|
+
plugins: [
|
|
478
|
+
{
|
|
479
|
+
name: 'test-plugin',
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
'child-app': {
|
|
485
|
+
name: 'test-child-app',
|
|
486
|
+
root: 'packages/child-app',
|
|
487
|
+
type: 'child-app',
|
|
488
|
+
sourceMap: {
|
|
489
|
+
development: true,
|
|
490
|
+
},
|
|
491
|
+
experiments: {
|
|
492
|
+
transpilation: {
|
|
493
|
+
loader: {},
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
define: {
|
|
497
|
+
shared: {
|
|
498
|
+
commonProp: 'unknown',
|
|
499
|
+
},
|
|
500
|
+
production: {
|
|
501
|
+
'process.env.PROD': 'true',
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
webpack: {
|
|
505
|
+
resolveAlias: {
|
|
506
|
+
stream: 'browser-stream',
|
|
507
|
+
},
|
|
508
|
+
provide: {
|
|
509
|
+
Buffer: ['buffer', 'Buffer'],
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
expect.assertions(2);
|
|
517
|
+
try {
|
|
518
|
+
const configManager = new ConfigManager({ config, syncConfigFile });
|
|
519
|
+
} catch (e: any) {
|
|
520
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
|
521
|
+
expect(e.message).toMatch('Config validation failed');
|
|
522
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
|
523
|
+
expect(true).toBeTruthy();
|
|
524
|
+
}
|
|
525
|
+
});
|
package/src/models/config.ts
CHANGED
|
@@ -81,9 +81,15 @@ export class ConfigManager {
|
|
|
81
81
|
configParameters.projects[projectName] = merge(projectsConfig, entry);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
const ajv = new Ajv({
|
|
84
|
+
const ajv = new Ajv({
|
|
85
|
+
useDefaults: true,
|
|
86
|
+
allowUnionTypes: true,
|
|
87
|
+
strict: true,
|
|
88
|
+
strictSchema: false,
|
|
89
|
+
});
|
|
85
90
|
|
|
86
|
-
ajv.addKeyword(
|
|
91
|
+
ajv.addKeyword({
|
|
92
|
+
keyword: 'cli_overridable',
|
|
87
93
|
modifying: true,
|
|
88
94
|
errors: false,
|
|
89
95
|
validate(schemaValue, currentValue, propSchema) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import detectPort from 'detect-port';
|
|
2
|
+
|
|
3
|
+
import type { ConfigEntry } from '../typings/configEntry/common';
|
|
4
|
+
import type { Params as StartParams } from '../api/start';
|
|
5
|
+
import type { Params as StartProdParams } from '../api/start-prod';
|
|
6
|
+
|
|
7
|
+
interface NetworkConstructorPayload {
|
|
8
|
+
configEntry: ConfigEntry;
|
|
9
|
+
commandParams: StartParams | StartProdParams;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class PortManager {
|
|
13
|
+
static DEFAULT_PORT = 3000;
|
|
14
|
+
static DEFAULT_MODULE_PORT = 4040;
|
|
15
|
+
static DEFAULT_STATIC_PORT = 4000;
|
|
16
|
+
static DEFAULT_MODULE_STATIC_PORT = 4040;
|
|
17
|
+
|
|
18
|
+
private configEntry: ConfigEntry;
|
|
19
|
+
private commandParams: StartParams | StartProdParams;
|
|
20
|
+
|
|
21
|
+
public port: number | null = null;
|
|
22
|
+
public staticPort: number | null = null;
|
|
23
|
+
|
|
24
|
+
constructor({ configEntry, commandParams }: NetworkConstructorPayload) {
|
|
25
|
+
this.configEntry = configEntry;
|
|
26
|
+
this.commandParams = commandParams;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Try to detect port considering the fact, that if user requests
|
|
31
|
+
* a port explicitly, we should not try to detect a free one.
|
|
32
|
+
*
|
|
33
|
+
* Also, handle zero port (it means any random port) as the edge case,
|
|
34
|
+
* because we must pass a final number to the config manager.
|
|
35
|
+
*/
|
|
36
|
+
public async computeAvailablePorts(): Promise<void> {
|
|
37
|
+
if (this.commandParams.port !== undefined && this.commandParams.port !== 0) {
|
|
38
|
+
// @ts-expect-error There is a string actually
|
|
39
|
+
this.port = parseInt(this.commandParams.port, 10);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (this.commandParams.staticPort !== undefined && this.commandParams.staticPort !== 0) {
|
|
43
|
+
// @ts-expect-error There is a string actually
|
|
44
|
+
this.staticPort = parseInt(this.commandParams.staticPort, 10);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
switch (this.configEntry.type) {
|
|
48
|
+
case 'child-app':
|
|
49
|
+
await this.forChildApp();
|
|
50
|
+
break;
|
|
51
|
+
|
|
52
|
+
case 'module':
|
|
53
|
+
await this.forModule();
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case 'application':
|
|
57
|
+
await this.forApplication();
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
default:
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private async forApplication(): Promise<void> {
|
|
66
|
+
this.port = this.port ?? (await detectPort(PortManager.DEFAULT_PORT));
|
|
67
|
+
this.staticPort = this.staticPort ?? (await detectPort(PortManager.DEFAULT_STATIC_PORT));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private async forModule(): Promise<void> {
|
|
71
|
+
this.port = this.port ?? (await detectPort(PortManager.DEFAULT_MODULE_PORT));
|
|
72
|
+
this.staticPort = this.staticPort ?? (await detectPort(PortManager.DEFAULT_MODULE_STATIC_PORT));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async forChildApp(): Promise<void> {
|
|
76
|
+
this.port = this.port ?? (await detectPort(PortManager.DEFAULT_MODULE_PORT));
|
|
77
|
+
this.staticPort = this.staticPort ?? (await detectPort(PortManager.DEFAULT_MODULE_STATIC_PORT));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -19,6 +19,7 @@ module.exports = (autogeneratedSchema) => {
|
|
|
19
19
|
patternProperties: {
|
|
20
20
|
'^[$a-zA-Z_-][0-9a-zA-Z_$-]*$': {
|
|
21
21
|
if: {
|
|
22
|
+
type: 'object',
|
|
22
23
|
properties: {
|
|
23
24
|
type: { const: 'application' },
|
|
24
25
|
},
|
|
@@ -26,6 +27,7 @@ module.exports = (autogeneratedSchema) => {
|
|
|
26
27
|
then: autogeneratedSchema.properties.application,
|
|
27
28
|
else: {
|
|
28
29
|
if: {
|
|
30
|
+
type: 'object',
|
|
29
31
|
properties: {
|
|
30
32
|
type: { const: 'module' },
|
|
31
33
|
},
|
|
@@ -33,6 +35,7 @@ module.exports = (autogeneratedSchema) => {
|
|
|
33
35
|
then: autogeneratedSchema.properties.module,
|
|
34
36
|
else: {
|
|
35
37
|
if: {
|
|
38
|
+
type: 'object',
|
|
36
39
|
properties: {
|
|
37
40
|
type: { const: 'child-app' },
|
|
38
41
|
},
|
|
@@ -9,7 +9,7 @@ jest.mock('resolve');
|
|
|
9
9
|
|
|
10
10
|
describe('JSON schema для tramvai.json', () => {
|
|
11
11
|
it('Схема успешно компилируется', () => {
|
|
12
|
-
const ajv = new Ajv();
|
|
12
|
+
const ajv = new Ajv({ strict: true, strictSchema: false, allowUnionTypes: true });
|
|
13
13
|
|
|
14
14
|
expect(() => ajv.compile(schema)).not.toThrow();
|
|
15
15
|
});
|
|
@@ -35,7 +35,13 @@ describe('JSON schema для tramvai.json', () => {
|
|
|
35
35
|
|
|
36
36
|
const originalConfig = clone(config);
|
|
37
37
|
|
|
38
|
-
const ajv = new Ajv({
|
|
38
|
+
const ajv = new Ajv({
|
|
39
|
+
allErrors: true,
|
|
40
|
+
useDefaults: true,
|
|
41
|
+
strict: true,
|
|
42
|
+
strictSchema: false,
|
|
43
|
+
allowUnionTypes: true,
|
|
44
|
+
});
|
|
39
45
|
const validate = ajv.compile(schema);
|
|
40
46
|
const valid = validate(config);
|
|
41
47
|
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import stripAnsi from 'strip-ansi';
|
|
3
|
-
|
|
4
|
-
interface Payload {
|
|
5
|
-
request?: number;
|
|
6
|
-
fallback: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Try to detect port synchronously considering the fact, that if user requests
|
|
11
|
-
* a port explicitly, we should not try to detect a free one.
|
|
12
|
-
*
|
|
13
|
-
* Also, handle zero port (it means any random port) as the edge case,
|
|
14
|
-
* because we must pass a final number to the config manager.
|
|
15
|
-
*/
|
|
16
|
-
export const detectPortSync = ({ request, fallback }: Payload): number => {
|
|
17
|
-
if (request !== undefined && request !== 0) {
|
|
18
|
-
return request;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const commandResult = execSync(`npx detect-port ${request ?? fallback}`, { encoding: 'utf-8' });
|
|
22
|
-
|
|
23
|
-
return parseInt(stripAnsi(commandResult.toString()), 10);
|
|
24
|
-
};
|