vyriy 0.3.4 → 0.3.8
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 +46 -21
- package/cli/args/args.js +4 -0
- package/cli/args/types.d.ts +4 -0
- package/cli/cli.js +13 -2
- package/commands/new/new.js +77 -6
- package/commands/new/types.d.ts +2 -0
- package/package.json +21 -1
- package/presets/agentsTemplate.d.ts +1 -0
- package/presets/agentsTemplate.js +105 -0
- package/presets/createProjectFiles.js +374 -18
- package/project-plan/api/api.js +5 -17
- package/project-plan/api/types.d.ts +1 -0
- package/project-plan/ci/ci.d.ts +1 -1
- package/project-plan/ci/ci.js +3 -2
- package/project-plan/create/create.js +70 -74
- package/project-plan/kind/kind.js +1 -16
- package/project-plan/types.d.ts +8 -8
- package/prompts/project-plan/project-plan.js +87 -25
- package/shared/index.d.ts +1 -0
- package/shared/index.js +1 -0
- package/shared/runCommand.d.ts +9 -0
- package/shared/runCommand.js +34 -0
- package/shared/types.d.ts +7 -0
|
@@ -6,111 +6,107 @@ const baseFeatures = [
|
|
|
6
6
|
'eslint',
|
|
7
7
|
'prettier',
|
|
8
8
|
'jest',
|
|
9
|
+
'storybook',
|
|
9
10
|
];
|
|
10
11
|
const presetFeatures = {
|
|
11
|
-
|
|
12
|
+
empty: [],
|
|
13
|
+
library: ['react'],
|
|
12
14
|
api: [],
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
mfe: ['react', 'webpack'],
|
|
17
|
-
openmfe: ['react', 'webpack', 'openmfe'],
|
|
18
|
-
'mfe-bff': ['react', 'webpack', 'bff'],
|
|
19
|
-
'openmfe-bff': [
|
|
20
|
-
'react',
|
|
21
|
-
'webpack',
|
|
22
|
-
'openmfe',
|
|
23
|
-
'bff',
|
|
24
|
-
],
|
|
15
|
+
csr: ['react', 'webpack'],
|
|
16
|
+
ssr: ['react', 'webpack'],
|
|
17
|
+
ssg: ['react', 'webpack'],
|
|
25
18
|
fullstack: ['react', 'webpack'],
|
|
26
|
-
|
|
27
|
-
empty: [],
|
|
19
|
+
mfe: ['react', 'webpack', 'openmfe'],
|
|
28
20
|
};
|
|
29
21
|
const packagePlans = {
|
|
22
|
+
empty: [],
|
|
30
23
|
library: [{ name: 'ui', kind: 'ui', publishable: true }],
|
|
31
24
|
api: [{ name: 'api', kind: 'api', publishable: false }],
|
|
32
|
-
|
|
33
|
-
{ name: 'app', kind: '
|
|
34
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
35
|
-
],
|
|
36
|
-
'react-ssr': [
|
|
37
|
-
{ name: 'app', kind: 'core', publishable: false },
|
|
38
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
39
|
-
{ name: 'ssr', kind: 'ssr', publishable: false },
|
|
40
|
-
],
|
|
41
|
-
'react-ssg': [
|
|
42
|
-
{ name: 'app', kind: 'core', publishable: false },
|
|
43
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
44
|
-
{ name: 'ssg', kind: 'ssg', publishable: false },
|
|
45
|
-
{ name: 'content', kind: 'core', publishable: false },
|
|
46
|
-
],
|
|
47
|
-
mfe: [
|
|
48
|
-
{ name: 'mfe', kind: 'mfe', publishable: false },
|
|
25
|
+
csr: [
|
|
26
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
49
27
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
50
28
|
],
|
|
51
|
-
|
|
52
|
-
{ name: '
|
|
29
|
+
ssr: [
|
|
30
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
53
31
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
54
|
-
{ name: '
|
|
32
|
+
{ name: 'ssr', kind: 'app', publishable: false },
|
|
55
33
|
],
|
|
56
|
-
|
|
57
|
-
{ name: '
|
|
34
|
+
ssg: [
|
|
35
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
58
36
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
59
|
-
{ name: '
|
|
60
|
-
|
|
61
|
-
'openmfe-bff': [
|
|
62
|
-
{ name: 'mfe', kind: 'mfe', publishable: false },
|
|
63
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
64
|
-
{ name: 'bff', kind: 'bff', publishable: false },
|
|
65
|
-
{ name: 'openmfe-contract', kind: 'contract', publishable: true },
|
|
37
|
+
{ name: 'ssg', kind: 'app', publishable: false },
|
|
38
|
+
{ name: 'content', kind: 'utils', publishable: false },
|
|
66
39
|
],
|
|
67
40
|
fullstack: [
|
|
68
|
-
{ name: 'app', kind: '
|
|
41
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
69
42
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
70
43
|
{ name: 'api', kind: 'api', publishable: false },
|
|
71
44
|
],
|
|
72
|
-
|
|
45
|
+
mfe: [
|
|
46
|
+
{ name: 'mfe', kind: 'app', publishable: false },
|
|
47
|
+
{ name: 'ui', kind: 'ui', publishable: true },
|
|
73
48
|
{ name: 'api', kind: 'api', publishable: false },
|
|
74
|
-
{ name: '
|
|
49
|
+
{ name: 'openmfe-contract', kind: 'config', publishable: true },
|
|
75
50
|
],
|
|
76
|
-
empty: [],
|
|
77
51
|
};
|
|
78
52
|
const workspacePlans = {
|
|
53
|
+
empty: [],
|
|
79
54
|
library: [],
|
|
80
55
|
api: [{ name: 'api', kind: 'api' }],
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
{ name: 'ssr', kind: '
|
|
84
|
-
{ name: 'web', kind: '
|
|
85
|
-
],
|
|
86
|
-
'react-ssg': [
|
|
87
|
-
{ name: 'ssg', kind: 'ssg' },
|
|
88
|
-
{ name: 'web', kind: 'web' },
|
|
56
|
+
csr: [{ name: 'web', kind: 'ui' }],
|
|
57
|
+
ssr: [
|
|
58
|
+
{ name: 'ssr', kind: 'ui' },
|
|
59
|
+
{ name: 'web', kind: 'ui' },
|
|
89
60
|
],
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{ name: '
|
|
93
|
-
{ name: 'openmfe', kind: 'openmfe' },
|
|
94
|
-
],
|
|
95
|
-
'mfe-bff': [
|
|
96
|
-
{ name: 'mfe', kind: 'mfe' },
|
|
97
|
-
{ name: 'bff', kind: 'bff' },
|
|
98
|
-
],
|
|
99
|
-
'openmfe-bff': [
|
|
100
|
-
{ name: 'mfe', kind: 'mfe' },
|
|
101
|
-
{ name: 'bff', kind: 'bff' },
|
|
102
|
-
{ name: 'openmfe', kind: 'openmfe' },
|
|
61
|
+
ssg: [
|
|
62
|
+
{ name: 'ssg', kind: 'ui' },
|
|
63
|
+
{ name: 'web', kind: 'ui' },
|
|
103
64
|
],
|
|
104
65
|
fullstack: [
|
|
105
|
-
{ name: 'web', kind: '
|
|
66
|
+
{ name: 'web', kind: 'ui' },
|
|
67
|
+
{ name: 'api', kind: 'api' },
|
|
68
|
+
],
|
|
69
|
+
mfe: [
|
|
70
|
+
{ name: 'mfe', kind: 'ui' },
|
|
106
71
|
{ name: 'api', kind: 'api' },
|
|
107
72
|
],
|
|
108
|
-
'aws-serverless': [{ name: 'cdk', kind: 'cdk' }],
|
|
109
|
-
empty: [],
|
|
110
73
|
};
|
|
111
74
|
const uniqueFeatures = (features) => [...new Set(features)];
|
|
75
|
+
const getApiRuntimeFromFeatures = (features) => features.includes('lambda') ? 'lambda' : 'docker';
|
|
76
|
+
const awsInfrastructureFeatures = [
|
|
77
|
+
'aws-cdk',
|
|
78
|
+
'lambda',
|
|
79
|
+
'fargate',
|
|
80
|
+
's3',
|
|
81
|
+
'cloudfront',
|
|
82
|
+
];
|
|
83
|
+
const getApiWorkspaceKindFromFeatures = (features) => {
|
|
84
|
+
if (features.includes('lambda')) {
|
|
85
|
+
return 'lambda';
|
|
86
|
+
}
|
|
87
|
+
if (features.includes('fargate')) {
|
|
88
|
+
return 'fargate';
|
|
89
|
+
}
|
|
90
|
+
return 'api';
|
|
91
|
+
};
|
|
92
|
+
const createWorkspacePlans = ({ features, preset, }) => {
|
|
93
|
+
const apiWorkspaceKind = getApiWorkspaceKindFromFeatures(features);
|
|
94
|
+
const workspaces = workspacePlans[preset].map((workspacePlan) => workspacePlan.name === 'api'
|
|
95
|
+
? {
|
|
96
|
+
...workspacePlan,
|
|
97
|
+
kind: apiWorkspaceKind,
|
|
98
|
+
}
|
|
99
|
+
: workspacePlan);
|
|
100
|
+
if (features.some((feature) => awsInfrastructureFeatures.includes(feature))) {
|
|
101
|
+
return [
|
|
102
|
+
...workspaces,
|
|
103
|
+
{ name: 'stack', kind: 'stack' },
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
return workspaces;
|
|
107
|
+
};
|
|
112
108
|
export const createProjectPlanFromPreset = ({ description, apiStyle, ciProvider, features = [], packageScope, preset, projectName, targetDirectory, }) => {
|
|
113
|
-
const api = createApiPlan({ preset, style: apiStyle });
|
|
109
|
+
const api = createApiPlan({ preset, runtime: getApiRuntimeFromFeatures(features), style: apiStyle });
|
|
114
110
|
const apiFeatures = getFeaturesFromApiPlan(api);
|
|
115
111
|
return {
|
|
116
112
|
projectName,
|
|
@@ -126,7 +122,7 @@ export const createProjectPlanFromPreset = ({ description, apiStyle, ciProvider,
|
|
|
126
122
|
...features,
|
|
127
123
|
]),
|
|
128
124
|
packages: [...packagePlans[preset]],
|
|
129
|
-
workspaces:
|
|
125
|
+
workspaces: createWorkspacePlans({ features, preset }),
|
|
130
126
|
ci: createCiPlan({ provider: ciProvider }),
|
|
131
127
|
...(api ? { api } : {}),
|
|
132
128
|
};
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
export const getProjectKindFromPreset = (preset) =>
|
|
2
|
-
switch (preset) {
|
|
3
|
-
case 'react-csr':
|
|
4
|
-
return 'csr';
|
|
5
|
-
case 'react-ssr':
|
|
6
|
-
return 'ssr';
|
|
7
|
-
case 'react-ssg':
|
|
8
|
-
return 'ssg';
|
|
9
|
-
case 'openmfe':
|
|
10
|
-
case 'mfe-bff':
|
|
11
|
-
case 'openmfe-bff':
|
|
12
|
-
return 'mfe';
|
|
13
|
-
default:
|
|
14
|
-
return preset;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
1
|
+
export const getProjectKindFromPreset = (preset) => preset;
|
package/project-plan/types.d.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
export type VyriyProjectKind = 'library' | 'api' | 'csr' | 'ssr' | 'ssg' | 'mfe' | 'fullstack' | '
|
|
2
|
-
export type VyriyPreset = '
|
|
3
|
-
export type VyriyFeature = 'typescript' | 'eslint' | 'prettier' | 'jest' | 'rest-api' | 'graphql-api' | 'react' | 'storybook' | 'webpack' | 'docker' | 'aws-cdk' | '
|
|
1
|
+
export type VyriyProjectKind = 'library' | 'api' | 'csr' | 'ssr' | 'ssg' | 'mfe' | 'fullstack' | 'empty';
|
|
2
|
+
export type VyriyPreset = 'empty' | 'library' | 'api' | 'ssr' | 'ssg' | 'csr' | 'fullstack' | 'mfe';
|
|
3
|
+
export type VyriyFeature = 'typescript' | 'eslint' | 'prettier' | 'jest' | 'rest-api' | 'graphql-api' | 'react' | 'storybook' | 'webpack' | 'docker' | 'aws-cdk' | 'apigateway' | 'lambda' | 'fargate' | 's3' | 'cloudfront' | 'openmfe';
|
|
4
4
|
export type VyriyPackagePlan = {
|
|
5
5
|
readonly name: string;
|
|
6
|
-
readonly kind: 'core' | 'ui' | 'api' | '
|
|
6
|
+
readonly kind: 'core' | 'ui' | 'api' | 'services' | 'stack' | 'config' | 'utils' | 'components' | 'app';
|
|
7
7
|
readonly publishable: boolean;
|
|
8
8
|
};
|
|
9
9
|
export type VyriyWorkspacePlan = {
|
|
10
10
|
readonly name: string;
|
|
11
|
-
readonly kind: '
|
|
11
|
+
readonly kind: 'api' | 'ui' | 'stack' | 'lambda' | 'fargate';
|
|
12
12
|
};
|
|
13
13
|
export type VyriyCiProvider = 'gitlab' | 'github';
|
|
14
|
-
export type VyriyCiPipeline = 'install' | '
|
|
14
|
+
export type VyriyCiPipeline = 'install' | 'lint' | 'test' | 'build' | 'deploy' | 'smoke' | 'e2e';
|
|
15
15
|
export type VyriyCiPlan = {
|
|
16
16
|
readonly enabled: boolean;
|
|
17
17
|
readonly providers: VyriyCiProvider[];
|
|
18
18
|
readonly pipelines: VyriyCiPipeline[];
|
|
19
19
|
};
|
|
20
|
-
export type VyriyApiStyle = 'rest' | 'graphql'
|
|
21
|
-
export type VyriyApiRuntime = '
|
|
20
|
+
export type VyriyApiStyle = 'rest' | 'graphql';
|
|
21
|
+
export type VyriyApiRuntime = 'docker' | 'lambda';
|
|
22
22
|
export type VyriyApiPlan = {
|
|
23
23
|
readonly enabled: boolean;
|
|
24
24
|
readonly style: VyriyApiStyle;
|
|
@@ -2,30 +2,62 @@ import { stdin, stdout } from 'node:process';
|
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { createProjectPlanFromPreset, getDefaultApiStyleFromPreset, isApiPreset, } from '../../project-plan/index.js';
|
|
4
4
|
const presets = [
|
|
5
|
+
'empty',
|
|
5
6
|
'library',
|
|
6
7
|
'api',
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'mfe',
|
|
11
|
-
'openmfe',
|
|
12
|
-
'mfe-bff',
|
|
13
|
-
'openmfe-bff',
|
|
8
|
+
'ssr',
|
|
9
|
+
'ssg',
|
|
10
|
+
'csr',
|
|
14
11
|
'fullstack',
|
|
15
|
-
'
|
|
16
|
-
'empty',
|
|
12
|
+
'mfe',
|
|
17
13
|
];
|
|
18
|
-
const
|
|
19
|
-
'
|
|
14
|
+
const presetDescriptions = {
|
|
15
|
+
empty: 'shared tooling without application code',
|
|
16
|
+
library: 'publishable React package for reusable UI',
|
|
17
|
+
api: 'REST or GraphQL backend API',
|
|
18
|
+
ssr: 'server-rendered React application',
|
|
19
|
+
ssg: 'build-time generated static React site',
|
|
20
|
+
csr: 'browser-rendered React application',
|
|
21
|
+
fullstack: 'React frontend with backend API',
|
|
22
|
+
mfe: 'OpenMFE widget with UI, API, SSR, and manifest',
|
|
23
|
+
};
|
|
24
|
+
const infrastructureOptions = ['docker', 'aws'];
|
|
25
|
+
const infrastructureFeatureMap = {
|
|
26
|
+
docker: ['docker'],
|
|
27
|
+
aws: [
|
|
28
|
+
'aws-cdk',
|
|
29
|
+
'lambda',
|
|
30
|
+
'apigateway',
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
const extraFeatureMap = {
|
|
34
|
+
docker: ['docker'],
|
|
35
|
+
'aws-api': [
|
|
36
|
+
'aws-cdk',
|
|
37
|
+
'lambda',
|
|
38
|
+
'apigateway',
|
|
39
|
+
],
|
|
40
|
+
'aws-fargate': [
|
|
41
|
+
'aws-cdk',
|
|
42
|
+
'fargate',
|
|
43
|
+
'docker',
|
|
44
|
+
],
|
|
45
|
+
'aws-static': [
|
|
46
|
+
'aws-cdk',
|
|
47
|
+
's3',
|
|
48
|
+
'cloudfront',
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
const directFeatureInputs = [
|
|
20
52
|
'docker',
|
|
21
53
|
'aws-cdk',
|
|
22
|
-
'
|
|
54
|
+
'apigateway',
|
|
23
55
|
'lambda',
|
|
24
56
|
'fargate',
|
|
25
57
|
's3',
|
|
26
58
|
'cloudfront',
|
|
27
59
|
];
|
|
28
|
-
const apiStyles = ['rest', 'graphql'
|
|
60
|
+
const apiStyles = ['rest', 'graphql'];
|
|
29
61
|
const ciProviders = ['none', 'gitlab', 'github'];
|
|
30
62
|
const createQuestion = (readline, output) => {
|
|
31
63
|
const queuedLines = [];
|
|
@@ -64,10 +96,37 @@ const parsePreset = (value, defaultValue) => {
|
|
|
64
96
|
}
|
|
65
97
|
return presets.includes(normalizedValue) ? normalizedValue : defaultValue;
|
|
66
98
|
};
|
|
67
|
-
const parseFeatures = (value) =>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
const parseFeatures = (value) => [
|
|
100
|
+
...new Set(value
|
|
101
|
+
.split(',')
|
|
102
|
+
.map((feature) => feature.trim())
|
|
103
|
+
.flatMap((feature) => extraFeatureMap[feature] ??
|
|
104
|
+
(directFeatureInputs.includes(feature)
|
|
105
|
+
? [feature]
|
|
106
|
+
: []))),
|
|
107
|
+
];
|
|
108
|
+
const parseInfrastructure = (value, defaultValue) => {
|
|
109
|
+
const normalizedValue = value.trim().toLowerCase();
|
|
110
|
+
const numericValue = Number.parseInt(normalizedValue, 10);
|
|
111
|
+
if (Number.isInteger(numericValue) && infrastructureOptions[numericValue - 1]) {
|
|
112
|
+
return [...infrastructureFeatureMap[infrastructureOptions[numericValue - 1]]];
|
|
113
|
+
}
|
|
114
|
+
if (infrastructureOptions.includes(normalizedValue)) {
|
|
115
|
+
return [...infrastructureFeatureMap[normalizedValue]];
|
|
116
|
+
}
|
|
117
|
+
const legacyFeatures = parseFeatures(value);
|
|
118
|
+
return legacyFeatures.length > 0 ? legacyFeatures : [...infrastructureFeatureMap[defaultValue]];
|
|
119
|
+
};
|
|
120
|
+
const getDefaultInfrastructureInput = (features) => features?.some((feature) => [
|
|
121
|
+
'aws-cdk',
|
|
122
|
+
'lambda',
|
|
123
|
+
'apigateway',
|
|
124
|
+
'fargate',
|
|
125
|
+
's3',
|
|
126
|
+
'cloudfront',
|
|
127
|
+
].includes(feature))
|
|
128
|
+
? 'aws'
|
|
129
|
+
: 'docker';
|
|
71
130
|
const parseApiStyle = (value, defaultValue) => {
|
|
72
131
|
const normalizedValue = value.trim().toLowerCase();
|
|
73
132
|
const numericValue = Number.parseInt(normalizedValue, 10);
|
|
@@ -96,12 +155,13 @@ export const askProjectPlan = async ({ defaults = {}, input = stdin, output = st
|
|
|
96
155
|
const packageScope = await promptWithDefault(question, 'Package scope', defaults.packageScope ?? `@${projectName}`);
|
|
97
156
|
const description = await promptWithDefault(question, 'Description', defaults.description ?? 'Calm cloud-ready application.');
|
|
98
157
|
output.write('\nProject preset:\n');
|
|
99
|
-
presets.forEach((preset, index) => output.write(` ${index + 1}. ${preset}\n`));
|
|
100
|
-
const
|
|
101
|
-
const
|
|
158
|
+
presets.forEach((preset, index) => output.write(` ${index + 1}. ${preset} - ${presetDescriptions[preset]}\n`));
|
|
159
|
+
const defaultPreset = defaults.preset ?? 'empty';
|
|
160
|
+
const presetAnswer = await promptWithDefault(question, 'Preset number or name', defaultPreset);
|
|
161
|
+
const preset = parsePreset(presetAnswer, defaultPreset);
|
|
102
162
|
const defaultApiStyle = defaults.apiStyle ?? getDefaultApiStyleFromPreset(preset);
|
|
103
163
|
const apiStyle = isApiPreset(preset)
|
|
104
|
-
? parseApiStyle(await promptWithDefault(question, 'API style
|
|
164
|
+
? parseApiStyle(await promptWithDefault(question, 'API style:\n 1. rest (@vyriy/router),\n 2. graphql', defaultApiStyle), defaultApiStyle)
|
|
105
165
|
: undefined;
|
|
106
166
|
output.write('\nCI/CD provider:\n');
|
|
107
167
|
output.write(' 1. none\n');
|
|
@@ -109,10 +169,12 @@ export const askProjectPlan = async ({ defaults = {}, input = stdin, output = st
|
|
|
109
169
|
output.write(' 3. github\n');
|
|
110
170
|
const defaultCiProvider = defaults.ciProvider ?? 'none';
|
|
111
171
|
const ciProvider = parseCiProvider(await promptWithDefault(question, 'CI/CD provider number or name', defaultCiProvider), defaultCiProvider);
|
|
112
|
-
output.write('\
|
|
113
|
-
output.write(
|
|
114
|
-
|
|
115
|
-
const
|
|
172
|
+
output.write('\nInfrastructure:\n');
|
|
173
|
+
output.write(' 1. Docker\n');
|
|
174
|
+
output.write(' 2. AWS\n');
|
|
175
|
+
const defaultInfrastructure = getDefaultInfrastructureInput(defaults.features);
|
|
176
|
+
const featuresAnswer = await promptWithDefault(question, 'Infrastructure number or name', defaultInfrastructure);
|
|
177
|
+
const features = parseInfrastructure(featuresAnswer, defaultInfrastructure);
|
|
116
178
|
const plan = createProjectPlanFromPreset({
|
|
117
179
|
projectName,
|
|
118
180
|
targetDirectory,
|
package/shared/index.d.ts
CHANGED
package/shared/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RunCommand, RunCommandOptions } from './types.js';
|
|
2
|
+
export declare class RunCommandError extends Error {
|
|
3
|
+
readonly args: readonly string[];
|
|
4
|
+
readonly command: string;
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
readonly exitCode: number | null;
|
|
7
|
+
constructor({ args, command, cwd }: RunCommandOptions, exitCode: number | null);
|
|
8
|
+
}
|
|
9
|
+
export declare const runCommand: RunCommand;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { env as processEnv } from 'node:process';
|
|
3
|
+
export class RunCommandError extends Error {
|
|
4
|
+
args;
|
|
5
|
+
command;
|
|
6
|
+
cwd;
|
|
7
|
+
exitCode;
|
|
8
|
+
constructor({ args = [], command, cwd }, exitCode) {
|
|
9
|
+
super(`Command failed with exit code ${exitCode ?? 'unknown'}: ${command} ${args.join(' ')}`.trim());
|
|
10
|
+
this.name = 'RunCommandError';
|
|
11
|
+
this.args = args;
|
|
12
|
+
this.command = command;
|
|
13
|
+
this.cwd = cwd;
|
|
14
|
+
this.exitCode = exitCode;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export const runCommand = async (options) => {
|
|
18
|
+
const child = spawn(options.command, [...(options.args ?? [])], {
|
|
19
|
+
cwd: options.cwd,
|
|
20
|
+
env: options.env ?? processEnv,
|
|
21
|
+
shell: false,
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
});
|
|
24
|
+
await new Promise((resolve, reject) => {
|
|
25
|
+
child.on('error', reject);
|
|
26
|
+
child.on('close', (code) => {
|
|
27
|
+
if (code === 0) {
|
|
28
|
+
resolve();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
reject(new RunCommandError(options, code));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
package/shared/types.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type ExecCommand = (command: string, args?: readonly string[]) => Promise<string>;
|
|
2
|
+
export type RunCommandOptions = {
|
|
3
|
+
readonly command: string;
|
|
4
|
+
readonly args?: readonly string[];
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
readonly env?: Record<string, string | undefined>;
|
|
7
|
+
};
|
|
8
|
+
export type RunCommand = (options: RunCommandOptions) => Promise<void>;
|
|
2
9
|
export type CommandExists = (command: string, options?: {
|
|
3
10
|
readonly execCommand?: ExecCommand;
|
|
4
11
|
}) => Promise<boolean>;
|