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.
Files changed (38) hide show
  1. package/README.md +48 -21
  2. package/cli/args/args.js +6 -0
  3. package/cli/args/types.d.ts +5 -1
  4. package/cli/cli.js +18 -2
  5. package/commands/new/new.js +77 -6
  6. package/commands/new/types.d.ts +2 -0
  7. package/commands/publish/index.d.ts +2 -0
  8. package/commands/publish/index.js +1 -0
  9. package/commands/publish/publish.d.ts +2 -0
  10. package/commands/publish/publish.js +274 -0
  11. package/commands/publish/types.d.ts +31 -0
  12. package/index.d.ts +1 -0
  13. package/index.js +1 -0
  14. package/package.json +159 -1
  15. package/presets/base/createBaseFiles.d.ts +3 -0
  16. package/presets/base/createBaseFiles.js +307 -0
  17. package/presets/config.d.ts +28 -0
  18. package/presets/config.js +7 -0
  19. package/presets/createProjectFiles.js +4 -175
  20. package/presets/library/createLibraryUiFiles.d.ts +3 -0
  21. package/presets/library/createLibraryUiFiles.js +127 -0
  22. package/presets/packages/createPackageFiles.d.ts +3 -0
  23. package/presets/packages/createPackageFiles.js +39 -0
  24. package/presets/packages/createPackageManifest.d.ts +7 -0
  25. package/presets/packages/createPackageManifest.js +13 -0
  26. package/presets/workspaces/createWorkspaceFiles.d.ts +3 -0
  27. package/presets/workspaces/createWorkspaceFiles.js +98 -0
  28. package/project-plan/api/api.js +5 -17
  29. package/project-plan/api/types.d.ts +1 -0
  30. package/project-plan/create/create.js +68 -77
  31. package/project-plan/kind/kind.js +1 -16
  32. package/project-plan/types.d.ts +7 -7
  33. package/prompts/project-plan/project-plan.js +62 -24
  34. package/shared/index.d.ts +1 -0
  35. package/shared/index.js +1 -0
  36. package/shared/runCommand.d.ts +9 -0
  37. package/shared/runCommand.js +34 -0
  38. 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
- 'react-csr': ['react', 'webpack'],
15
- 'react-ssr': ['react', 'webpack'],
16
- 'react-ssg': ['react', 'webpack'],
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
- 'aws-serverless': [
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
- 'react-csr': [
38
- { name: 'app', kind: 'core', publishable: false },
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
- 'mfe-bff': [
62
- { name: 'mfe', kind: 'mfe', publishable: false },
29
+ ssr: [
30
+ { name: 'app', kind: 'app', publishable: false },
63
31
  { name: 'ui', kind: 'ui', publishable: true },
64
- { name: 'bff', kind: 'bff', publishable: false },
32
+ { name: 'ssr', kind: 'app', publishable: false },
65
33
  ],
66
- 'openmfe-bff': [
67
- { name: 'mfe', kind: 'mfe', publishable: false },
34
+ ssg: [
35
+ { name: 'app', kind: 'app', publishable: false },
68
36
  { name: 'ui', kind: 'ui', publishable: true },
69
- { name: 'bff', kind: 'bff', publishable: false },
70
- { name: 'openmfe-contract', kind: 'contract', publishable: true },
37
+ { name: 'ssg', kind: 'app', publishable: false },
38
+ { name: 'content', kind: 'utils', publishable: false },
71
39
  ],
72
40
  fullstack: [
73
- { name: 'app', kind: 'core', publishable: false },
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
- 'aws-serverless': [
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: 'stack', kind: 'stack', publishable: false },
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
- 'react-csr': [{ name: 'web', kind: 'web' }],
87
- 'react-ssr': [
88
- { name: 'ssr', kind: 'ssr' },
89
- { name: 'web', kind: 'web' },
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
- 'openmfe-bff': [
105
- { name: 'mfe', kind: 'mfe' },
106
- { name: 'bff', kind: 'bff' },
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: 'web' },
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: [...workspacePlans[preset]],
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;
@@ -1,14 +1,14 @@
1
- export type VyriyProjectKind = 'library' | 'api' | 'csr' | 'ssr' | 'ssg' | 'mfe' | 'fullstack' | 'aws-serverless' | 'empty';
2
- export type VyriyPreset = 'library' | 'api' | 'react-csr' | 'react-ssr' | 'react-ssg' | 'mfe' | 'openmfe' | 'mfe-bff' | 'openmfe-bff' | 'fullstack' | 'aws-serverless' | 'empty';
3
- export type VyriyFeature = 'typescript' | 'eslint' | 'prettier' | 'jest' | 'rest-api' | 'graphql-api' | 'react' | 'storybook' | 'webpack' | 'docker' | 'aws-cdk' | 'apigateway' | 'lambda' | 'fargate' | 's3' | 'cloudfront' | 'openmfe' | 'bff';
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' | 'bff' | 'ssr' | 'ssg' | 'mfe' | 'contract' | 'stack';
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: 'web' | 'api' | 'ssr' | 'ssg' | 'storybook' | 'bff' | 'mfe' | 'openmfe' | 'stack';
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' | 'mixed';
21
- export type VyriyApiRuntime = 'node' | 'lambda';
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
- 'react-csr',
8
- 'react-ssr',
9
- 'react-ssg',
10
- 'mfe',
11
- 'openmfe',
12
- 'mfe-bff',
13
- 'openmfe-bff',
8
+ 'ssr',
9
+ 'ssg',
10
+ 'csr',
14
11
  'fullstack',
15
- 'aws-serverless',
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', 'mixed'];
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 presetAnswer = await promptWithDefault(question, 'Preset number or name', defaults.preset ?? 'react-ssr');
129
- const preset = parsePreset(presetAnswer, defaults.preset ?? 'react-ssr');
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: 1. rest (@vyriy/router), 2. graphql, 3. mixed', defaultApiStyle), defaultApiStyle)
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
- output.write('\nAdditional infrastructure, comma-separated:\n');
141
- output.write(` ${extraFeatures.join(', ')}\n`);
142
- const featuresAnswer = await promptWithDefault(question, 'Infrastructure', defaults.features?.join(', ') ?? '');
143
- const features = parseFeatures(featuresAnswer);
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
@@ -1,5 +1,6 @@
1
1
  export * from './commandExists.js';
2
2
  export * from './execCommand.js';
3
3
  export * from './fileExists.js';
4
+ export * from './runCommand.js';
4
5
  export * from './semver.js';
5
6
  export type * from './types.js';
package/shared/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './commandExists.js';
2
2
  export * from './execCommand.js';
3
3
  export * from './fileExists.js';
4
+ export * from './runCommand.js';
4
5
  export * from './semver.js';
@@ -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>;