vyriy 0.3.5 → 0.3.9
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 +48 -21
- package/cli/args/args.js +6 -0
- package/cli/args/types.d.ts +5 -1
- package/cli/cli.js +18 -2
- package/commands/new/new.js +77 -6
- package/commands/new/types.d.ts +2 -0
- package/commands/publish/index.d.ts +2 -0
- package/commands/publish/index.js +1 -0
- package/commands/publish/publish.d.ts +2 -0
- package/commands/publish/publish.js +274 -0
- package/commands/publish/types.d.ts +31 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +159 -1
- package/presets/base/createBaseFiles.d.ts +3 -0
- package/presets/base/createBaseFiles.js +307 -0
- package/presets/config.d.ts +28 -0
- package/presets/config.js +7 -0
- package/presets/createProjectFiles.js +4 -175
- package/presets/library/createLibraryUiFiles.d.ts +3 -0
- package/presets/library/createLibraryUiFiles.js +127 -0
- package/presets/packages/createPackageFiles.d.ts +3 -0
- package/presets/packages/createPackageFiles.js +39 -0
- package/presets/packages/createPackageManifest.d.ts +7 -0
- package/presets/packages/createPackageManifest.js +13 -0
- package/presets/workspaces/createWorkspaceFiles.d.ts +3 -0
- package/presets/workspaces/createWorkspaceFiles.js +98 -0
- package/project-plan/api/api.js +5 -17
- package/project-plan/api/types.d.ts +1 -0
- package/project-plan/create/create.js +68 -77
- package/project-plan/kind/kind.js +1 -16
- package/project-plan/types.d.ts +7 -7
- package/prompts/project-plan/project-plan.js +62 -24
- 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
|
@@ -9,113 +9,104 @@ const baseFeatures = [
|
|
|
9
9
|
'storybook',
|
|
10
10
|
];
|
|
11
11
|
const presetFeatures = {
|
|
12
|
+
empty: [],
|
|
12
13
|
library: ['react'],
|
|
13
14
|
api: [],
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
mfe: ['react', 'webpack'],
|
|
18
|
-
openmfe: ['react', 'webpack', 'openmfe'],
|
|
19
|
-
'mfe-bff': ['react', 'webpack', 'bff'],
|
|
20
|
-
'openmfe-bff': [
|
|
21
|
-
'react',
|
|
22
|
-
'webpack',
|
|
23
|
-
'openmfe',
|
|
24
|
-
'bff',
|
|
25
|
-
],
|
|
15
|
+
csr: ['react', 'webpack'],
|
|
16
|
+
ssr: ['react', 'webpack'],
|
|
17
|
+
ssg: ['react', 'webpack'],
|
|
26
18
|
fullstack: ['react', 'webpack'],
|
|
27
|
-
'
|
|
28
|
-
'aws-cdk',
|
|
29
|
-
'lambda',
|
|
30
|
-
'apigateway',
|
|
31
|
-
],
|
|
32
|
-
empty: [],
|
|
19
|
+
mfe: ['react', 'webpack', 'openmfe'],
|
|
33
20
|
};
|
|
34
21
|
const packagePlans = {
|
|
22
|
+
empty: [],
|
|
35
23
|
library: [{ name: 'ui', kind: 'ui', publishable: true }],
|
|
36
24
|
api: [{ name: 'api', kind: 'api', publishable: false }],
|
|
37
|
-
|
|
38
|
-
{ name: 'app', kind: '
|
|
39
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
40
|
-
],
|
|
41
|
-
'react-ssr': [
|
|
42
|
-
{ name: 'app', kind: 'core', publishable: false },
|
|
43
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
44
|
-
{ name: 'ssr', kind: 'ssr', publishable: false },
|
|
45
|
-
],
|
|
46
|
-
'react-ssg': [
|
|
47
|
-
{ name: 'app', kind: 'core', publishable: false },
|
|
48
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
49
|
-
{ name: 'ssg', kind: 'ssg', publishable: false },
|
|
50
|
-
{ name: 'content', kind: 'core', publishable: false },
|
|
51
|
-
],
|
|
52
|
-
mfe: [
|
|
53
|
-
{ name: 'mfe', kind: 'mfe', publishable: false },
|
|
54
|
-
{ name: 'ui', kind: 'ui', publishable: true },
|
|
55
|
-
],
|
|
56
|
-
openmfe: [
|
|
57
|
-
{ name: 'mfe', kind: 'mfe', publishable: false },
|
|
25
|
+
csr: [
|
|
26
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
58
27
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
59
|
-
{ name: 'openmfe-contract', kind: 'contract', publishable: true },
|
|
60
28
|
],
|
|
61
|
-
|
|
62
|
-
{ name: '
|
|
29
|
+
ssr: [
|
|
30
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
63
31
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
64
|
-
{ name: '
|
|
32
|
+
{ name: 'ssr', kind: 'app', publishable: false },
|
|
65
33
|
],
|
|
66
|
-
|
|
67
|
-
{ name: '
|
|
34
|
+
ssg: [
|
|
35
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
68
36
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
69
|
-
{ name: '
|
|
70
|
-
{ name: '
|
|
37
|
+
{ name: 'ssg', kind: 'app', publishable: false },
|
|
38
|
+
{ name: 'content', kind: 'utils', publishable: false },
|
|
71
39
|
],
|
|
72
40
|
fullstack: [
|
|
73
|
-
{ name: 'app', kind: '
|
|
41
|
+
{ name: 'app', kind: 'app', publishable: false },
|
|
74
42
|
{ name: 'ui', kind: 'ui', publishable: true },
|
|
75
43
|
{ name: 'api', kind: 'api', publishable: false },
|
|
76
44
|
],
|
|
77
|
-
|
|
45
|
+
mfe: [
|
|
46
|
+
{ name: 'mfe', kind: 'app', publishable: false },
|
|
47
|
+
{ name: 'ui', kind: 'ui', publishable: true },
|
|
78
48
|
{ name: 'api', kind: 'api', publishable: false },
|
|
79
|
-
{ name: '
|
|
49
|
+
{ name: 'openmfe-contract', kind: 'config', publishable: true },
|
|
80
50
|
],
|
|
81
|
-
empty: [],
|
|
82
51
|
};
|
|
83
52
|
const workspacePlans = {
|
|
53
|
+
empty: [],
|
|
84
54
|
library: [],
|
|
85
55
|
api: [{ name: 'api', kind: 'api' }],
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{ name: 'ssr', kind: '
|
|
89
|
-
{ name: 'web', kind: '
|
|
90
|
-
],
|
|
91
|
-
'react-ssg': [
|
|
92
|
-
{ name: 'ssg', kind: 'ssg' },
|
|
93
|
-
{ name: 'web', kind: 'web' },
|
|
94
|
-
],
|
|
95
|
-
mfe: [{ name: 'mfe', kind: 'mfe' }],
|
|
96
|
-
openmfe: [
|
|
97
|
-
{ name: 'mfe', kind: 'mfe' },
|
|
98
|
-
{ name: 'openmfe', kind: 'openmfe' },
|
|
99
|
-
],
|
|
100
|
-
'mfe-bff': [
|
|
101
|
-
{ name: 'mfe', kind: 'mfe' },
|
|
102
|
-
{ name: 'bff', kind: 'bff' },
|
|
56
|
+
csr: [{ name: 'web', kind: 'ui' }],
|
|
57
|
+
ssr: [
|
|
58
|
+
{ name: 'ssr', kind: 'ui' },
|
|
59
|
+
{ name: 'web', kind: 'ui' },
|
|
103
60
|
],
|
|
104
|
-
|
|
105
|
-
{ name: '
|
|
106
|
-
{ name: '
|
|
107
|
-
{ name: 'openmfe', kind: 'openmfe' },
|
|
61
|
+
ssg: [
|
|
62
|
+
{ name: 'ssg', kind: 'ui' },
|
|
63
|
+
{ name: 'web', kind: 'ui' },
|
|
108
64
|
],
|
|
109
65
|
fullstack: [
|
|
110
|
-
{ name: 'web', kind: '
|
|
66
|
+
{ name: 'web', kind: 'ui' },
|
|
67
|
+
{ name: 'api', kind: 'api' },
|
|
68
|
+
],
|
|
69
|
+
mfe: [
|
|
70
|
+
{ name: 'mfe', kind: 'ui' },
|
|
111
71
|
{ name: 'api', kind: 'api' },
|
|
112
72
|
],
|
|
113
|
-
'aws-serverless': [{ name: 'stack', kind: 'stack' }],
|
|
114
|
-
empty: [],
|
|
115
73
|
};
|
|
116
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
|
+
};
|
|
117
108
|
export const createProjectPlanFromPreset = ({ description, apiStyle, ciProvider, features = [], packageScope, preset, projectName, targetDirectory, }) => {
|
|
118
|
-
const api = createApiPlan({ preset, style: apiStyle });
|
|
109
|
+
const api = createApiPlan({ preset, runtime: getApiRuntimeFromFeatures(features), style: apiStyle });
|
|
119
110
|
const apiFeatures = getFeaturesFromApiPlan(api);
|
|
120
111
|
return {
|
|
121
112
|
projectName,
|
|
@@ -131,7 +122,7 @@ export const createProjectPlanFromPreset = ({ description, apiStyle, ciProvider,
|
|
|
131
122
|
...features,
|
|
132
123
|
]),
|
|
133
124
|
packages: [...packagePlans[preset]],
|
|
134
|
-
workspaces:
|
|
125
|
+
workspaces: createWorkspacePlans({ features, preset }),
|
|
135
126
|
ci: createCiPlan({ provider: ciProvider }),
|
|
136
127
|
...(api ? { api } : {}),
|
|
137
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,14 +1,14 @@
|
|
|
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' | 'apigateway' | 'lambda' | 'fargate' | 's3' | 'cloudfront' | 'openmfe'
|
|
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
14
|
export type VyriyCiPipeline = 'install' | 'lint' | 'test' | 'build' | 'deploy' | 'smoke' | 'e2e';
|
|
@@ -17,8 +17,8 @@ export type VyriyCiPlan = {
|
|
|
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,25 +2,34 @@ 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',
|
|
17
|
-
];
|
|
18
|
-
const extraFeatures = [
|
|
19
|
-
'docker',
|
|
20
|
-
'aws-api',
|
|
21
|
-
'aws-fargate',
|
|
22
|
-
'aws-static',
|
|
12
|
+
'mfe',
|
|
23
13
|
];
|
|
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
|
+
};
|
|
24
33
|
const extraFeatureMap = {
|
|
25
34
|
docker: ['docker'],
|
|
26
35
|
'aws-api': [
|
|
@@ -48,7 +57,7 @@ const directFeatureInputs = [
|
|
|
48
57
|
's3',
|
|
49
58
|
'cloudfront',
|
|
50
59
|
];
|
|
51
|
-
const apiStyles = ['rest', 'graphql'
|
|
60
|
+
const apiStyles = ['rest', 'graphql'];
|
|
52
61
|
const ciProviders = ['none', 'gitlab', 'github'];
|
|
53
62
|
const createQuestion = (readline, output) => {
|
|
54
63
|
const queuedLines = [];
|
|
@@ -96,6 +105,28 @@ const parseFeatures = (value) => [
|
|
|
96
105
|
? [feature]
|
|
97
106
|
: []))),
|
|
98
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';
|
|
99
130
|
const parseApiStyle = (value, defaultValue) => {
|
|
100
131
|
const normalizedValue = value.trim().toLowerCase();
|
|
101
132
|
const numericValue = Number.parseInt(normalizedValue, 10);
|
|
@@ -124,12 +155,13 @@ export const askProjectPlan = async ({ defaults = {}, input = stdin, output = st
|
|
|
124
155
|
const packageScope = await promptWithDefault(question, 'Package scope', defaults.packageScope ?? `@${projectName}`);
|
|
125
156
|
const description = await promptWithDefault(question, 'Description', defaults.description ?? 'Calm cloud-ready application.');
|
|
126
157
|
output.write('\nProject preset:\n');
|
|
127
|
-
presets.forEach((preset, index) => output.write(` ${index + 1}. ${preset}\n`));
|
|
128
|
-
const
|
|
129
|
-
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);
|
|
130
162
|
const defaultApiStyle = defaults.apiStyle ?? getDefaultApiStyleFromPreset(preset);
|
|
131
163
|
const apiStyle = isApiPreset(preset)
|
|
132
|
-
? parseApiStyle(await promptWithDefault(question, 'API style
|
|
164
|
+
? parseApiStyle(await promptWithDefault(question, 'API style:\n 1. rest (@vyriy/router),\n 2. graphql', defaultApiStyle), defaultApiStyle)
|
|
133
165
|
: undefined;
|
|
134
166
|
output.write('\nCI/CD provider:\n');
|
|
135
167
|
output.write(' 1. none\n');
|
|
@@ -137,10 +169,16 @@ export const askProjectPlan = async ({ defaults = {}, input = stdin, output = st
|
|
|
137
169
|
output.write(' 3. github\n');
|
|
138
170
|
const defaultCiProvider = defaults.ciProvider ?? 'none';
|
|
139
171
|
const ciProvider = parseCiProvider(await promptWithDefault(question, 'CI/CD provider number or name', defaultCiProvider), defaultCiProvider);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
const features = isApiPreset(preset)
|
|
173
|
+
? await (async () => {
|
|
174
|
+
output.write('\nInfrastructure:\n');
|
|
175
|
+
output.write(' 1. Docker\n');
|
|
176
|
+
output.write(' 2. AWS\n');
|
|
177
|
+
const defaultInfrastructure = getDefaultInfrastructureInput(defaults.features);
|
|
178
|
+
const featuresAnswer = await promptWithDefault(question, 'Infrastructure number or name', defaultInfrastructure);
|
|
179
|
+
return parseInfrastructure(featuresAnswer, defaultInfrastructure);
|
|
180
|
+
})()
|
|
181
|
+
: [];
|
|
144
182
|
const plan = createProjectPlanFromPreset({
|
|
145
183
|
projectName,
|
|
146
184
|
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>;
|