@aws-amplify/amplify-util-uibuilder 1.5.0-binary-file-size.0 → 1.5.0-studio-forms.0

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 (63) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/amplify-plugin.json +10 -3
  3. package/lib/clients/amplify-studio-client.d.ts +13 -2
  4. package/lib/clients/amplify-studio-client.d.ts.map +1 -1
  5. package/lib/clients/amplify-studio-client.js +72 -4
  6. package/lib/clients/amplify-studio-client.js.map +1 -1
  7. package/lib/commands/generateComponents.d.ts.map +1 -1
  8. package/lib/commands/generateComponents.js +36 -15
  9. package/lib/commands/generateComponents.js.map +1 -1
  10. package/lib/commands/help.d.ts +1 -1
  11. package/lib/commands/help.d.ts.map +1 -1
  12. package/lib/commands/help.js +2 -2
  13. package/lib/commands/help.js.map +1 -1
  14. package/lib/commands/utils/codegenResources.d.ts +12 -2
  15. package/lib/commands/utils/codegenResources.d.ts.map +1 -1
  16. package/lib/commands/utils/codegenResources.js +70 -3
  17. package/lib/commands/utils/codegenResources.js.map +1 -1
  18. package/lib/commands/utils/featureFlags.d.ts +3 -0
  19. package/lib/commands/utils/featureFlags.d.ts.map +1 -0
  20. package/lib/commands/utils/featureFlags.js +34 -0
  21. package/lib/commands/utils/featureFlags.js.map +1 -0
  22. package/lib/commands/utils/getUiBuilderComponentsPath.d.ts.map +1 -1
  23. package/lib/commands/utils/getUiBuilderComponentsPath.js.map +1 -1
  24. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +12 -20
  25. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
  26. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +36 -3
  27. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
  28. package/lib/commands/version.d.ts +1 -1
  29. package/lib/commands/version.d.ts.map +1 -1
  30. package/lib/commands/version.js +2 -2
  31. package/lib/commands/version.js.map +1 -1
  32. package/lib/event-handlers/handle-PostEnvAdd.d.ts +1 -1
  33. package/lib/event-handlers/handle-PostEnvAdd.d.ts.map +1 -1
  34. package/lib/event-handlers/handle-PostEnvAdd.js +4 -6
  35. package/lib/event-handlers/handle-PostEnvAdd.js.map +1 -1
  36. package/lib/event-handlers/handle-PostPull.d.ts +1 -1
  37. package/lib/event-handlers/handle-PostPull.d.ts.map +1 -1
  38. package/lib/event-handlers/handle-PostPull.js +4 -3
  39. package/lib/event-handlers/handle-PostPull.js.map +1 -1
  40. package/lib/event-handlers/handle-PostPush.d.ts +3 -0
  41. package/lib/event-handlers/handle-PostPush.d.ts.map +1 -0
  42. package/lib/event-handlers/handle-PostPush.js +15 -0
  43. package/lib/event-handlers/handle-PostPush.js.map +1 -0
  44. package/package.json +8 -7
  45. package/src/__tests__/cloneComponentsFromEnv.test.ts +11 -0
  46. package/src/__tests__/createUiBuilderComponent.test.ts +19 -1
  47. package/src/__tests__/generateComponents.test.ts +35 -1
  48. package/src/__tests__/getAmplifyDataSchema.test.ts +43 -3
  49. package/src/__tests__/syncAmplifyUiComponents.test.ts +111 -8
  50. package/src/__tests__/utils.ts +70 -0
  51. package/src/clients/amplify-studio-client.ts +146 -63
  52. package/src/commands/generateComponents.ts +46 -16
  53. package/src/commands/help.ts +5 -2
  54. package/src/commands/utils/codegenResources.ts +108 -4
  55. package/src/commands/utils/featureFlags.ts +61 -0
  56. package/src/commands/utils/getUiBuilderComponentsPath.ts +7 -2
  57. package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +74 -29
  58. package/src/commands/version.ts +5 -2
  59. package/src/event-handlers/handle-PostEnvAdd.ts +8 -6
  60. package/src/event-handlers/handle-PostPull.ts +7 -3
  61. package/src/event-handlers/handle-PostPush.ts +15 -0
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/src/__tests__/handleEvents.test.ts +0 -21
@@ -10,14 +10,19 @@ import {
10
10
  RenderTextComponentResponse,
11
11
  GenericDataSchema,
12
12
  getGenericFromDataStore,
13
+ StudioForm,
14
+ StudioSchema,
13
15
  } from '@aws-amplify/codegen-ui';
14
16
  import {
15
17
  AmplifyRenderer,
18
+ AmplifyFormRenderer,
16
19
  ReactThemeStudioTemplateRenderer,
17
20
  ReactIndexStudioTemplateRenderer,
18
21
  ModuleKind,
19
22
  ScriptTarget,
20
23
  ScriptKind,
24
+ UtilTemplateType,
25
+ ReactUtilsStudioTemplateRenderer,
21
26
  } from '@aws-amplify/codegen-ui-react';
22
27
  import { printer } from 'amplify-prompts';
23
28
  import {
@@ -83,15 +88,46 @@ export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme):
83
88
  }
84
89
  };
85
90
 
91
+ /**
92
+ * Writes form file to the work space
93
+ */
94
+ export const createUiBuilderForm = (context: $TSContext, schema: StudioForm, dataSchema?: GenericDataSchema): StudioForm => {
95
+ const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
96
+ const rendererFactory = new StudioTemplateRendererFactory(
97
+ (form: StudioForm) => (new AmplifyFormRenderer(form, dataSchema, config) as unknown) as StudioTemplateRenderer<
98
+ unknown,
99
+ StudioForm,
100
+ FrameworkOutputManager<unknown>,
101
+ RenderTextComponentResponse
102
+ >,
103
+ );
104
+
105
+ const outputPathDir = uiBuilderComponentsPath;
106
+
107
+ const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
108
+ outputPathDir,
109
+ });
110
+
111
+ try {
112
+ rendererManager.renderSchemaToTemplate(schema);
113
+ return schema;
114
+ } catch (e) {
115
+ printer.debug(e);
116
+ printer.debug('Skipping invalid form with schema');
117
+ printer.debug(JSON.stringify(schema, null, 2));
118
+ throw e;
119
+ }
120
+ };
121
+
86
122
  /**
87
123
  * Writes index file to the work space
88
124
  */
89
- export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioComponent[]): RenderTextComponentResponse => {
125
+ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioSchema[]): void => {
90
126
  const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
91
127
  const rendererFactory = new StudioTemplateRendererFactory(
92
- (component: StudioComponent[]) => (new ReactIndexStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
128
+ (schema: StudioSchema[]) => (new ReactIndexStudioTemplateRenderer(schema, config) as unknown) as StudioTemplateRenderer<
93
129
  unknown,
94
- StudioComponent[],
130
+ StudioSchema[],
95
131
  FrameworkOutputManager<unknown>,
96
132
  RenderTextComponentResponse
97
133
  >,
@@ -104,7 +140,50 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
104
140
  });
105
141
 
106
142
  try {
107
- return rendererManager.renderSchemaToTemplate(schemas);
143
+ rendererManager.renderSchemaToTemplate(schemas);
144
+ } catch (e) {
145
+ printer.debug(e);
146
+ printer.debug('Failed to generate component index file');
147
+ throw e;
148
+ }
149
+ };
150
+
151
+ type UtilFileChecks = {
152
+ hasForms: boolean;
153
+ hasViews: boolean;
154
+ };
155
+
156
+ /**
157
+ * Writes utils file to the work space
158
+ */
159
+ export const generateAmplifyUiBuilderUtilFile = (context: $TSContext, { hasForms, hasViews }: UtilFileChecks): void => {
160
+ const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
161
+ const rendererFactory = new StudioTemplateRendererFactory(
162
+ (utils: UtilTemplateType[]) => (new ReactUtilsStudioTemplateRenderer(utils, config)),
163
+ );
164
+
165
+ const outputPathDir = uiBuilderComponentsPath;
166
+
167
+ const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
168
+ outputPathDir,
169
+ });
170
+
171
+ const utils = new Set<UtilTemplateType>();
172
+
173
+ if (hasForms) {
174
+ utils.add('validation');
175
+ utils.add('formatter');
176
+ utils.add('fetchByPath');
177
+ }
178
+
179
+ if (hasViews) {
180
+ utils.add('formatter');
181
+ }
182
+
183
+ try {
184
+ if (utils.size) {
185
+ rendererManager.renderSchemaToTemplate([...utils]);
186
+ }
108
187
  } catch (e) {
109
188
  printer.debug(e);
110
189
  printer.debug('Failed to generate component index file');
@@ -119,6 +198,9 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
119
198
  export const getAmplifyDataSchema = async (
120
199
  studioClient: AmplifyStudioClient,
121
200
  ): Promise<GenericDataSchema | undefined> => {
201
+ if (!studioClient.isSupportedGraphQL) {
202
+ return undefined;
203
+ }
122
204
  try {
123
205
  const meta = stateManager.getMeta();
124
206
  const resourceName = Object.entries(meta[AmplifyCategories.API]).find(
@@ -138,3 +220,25 @@ export const getAmplifyDataSchema = async (
138
220
  return undefined;
139
221
  }
140
222
  };
223
+
224
+ /**
225
+ * generates base create/update froms from names
226
+ */
227
+ export const generateBaseForms = (modelMap: {[model: string]: Set<'create' | 'update'>}): StudioForm[] => {
228
+ const getSchema = (name: string, type: 'create' | 'update') : StudioForm => ({
229
+ name: `${name}${type === 'create' ? 'CreateForm' : 'UpdateForm'}`,
230
+ formActionType: type,
231
+ dataType: { dataSourceType: 'DataStore', dataTypeName: name },
232
+ fields: {},
233
+ sectionalElements: {},
234
+ style: {},
235
+ cta: {},
236
+ });
237
+
238
+ const schemas : StudioForm[] = [];
239
+
240
+ Object.entries(modelMap).forEach(([name, set]) => {
241
+ set.forEach(type => schemas.push(getSchema(name, type)));
242
+ });
243
+ return schemas;
244
+ };
@@ -0,0 +1,61 @@
1
+ /* eslint-disable spellcheck/spell-checker */
2
+ import {
3
+ $TSContext, FeatureFlags, pathManager, stateManager,
4
+ } from 'amplify-cli-core';
5
+
6
+ // This is copy-pasta from the category api until they can expose the method to plugins
7
+
8
+ /**
9
+ * Shorthand for Feature flag retrieval.
10
+ */
11
+ const useExperimentalPipelinedTransformerFF = (): boolean => FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer');
12
+ const transformerVersionFF = (): number => FeatureFlags.getNumber('graphQLTransformer.transformerVersion');
13
+
14
+ /**
15
+ * Inspect feature flags for the project and determine if this particular project should be using graphql v2 or v1.
16
+ * Coerces feature flags into a sane state if they are out of, and throws error on invalid configuration.
17
+ */
18
+ export const getTransformerVersion = async (context: $TSContext): Promise<number> => {
19
+ if (useExperimentalPipelinedTransformerFF() === false) {
20
+ return 1;
21
+ }
22
+
23
+ if (isLegacyFeatureFlagConfiguration()) {
24
+ await migrateToTransformerVersionFeatureFlag(context);
25
+ }
26
+
27
+ const transformerVersion = transformerVersionFF();
28
+ if (transformerVersion !== 1 && transformerVersion !== 2) {
29
+ throw new Error(`Invalid value specified for transformerVersion: '${transformerVersion}'`);
30
+ }
31
+
32
+ return transformerVersion;
33
+ };
34
+
35
+ /**
36
+ * Return whether or not the project is configured with legacy pipelined transformer feature flags, and may need to be updated.
37
+ */
38
+ const isLegacyFeatureFlagConfiguration = (): boolean => useExperimentalPipelinedTransformerFF() && transformerVersionFF() === 1;
39
+
40
+ /**
41
+ * Update project feature flags and alert the user if they have a deprecated set of feature flags.
42
+ */
43
+ const migrateToTransformerVersionFeatureFlag = async (context: $TSContext): Promise<void> => {
44
+ const projectPath = pathManager.findProjectRoot() ?? process.cwd();
45
+
46
+ const config = stateManager.getCLIJSON(projectPath, undefined, {
47
+ throwIfNotExist: false,
48
+ preserveComments: true,
49
+ });
50
+
51
+ // eslint-disable-next-line spellcheck/spell-checker
52
+ config.features.graphqltransformer.transformerversion = 2;
53
+ stateManager.setCLIJSON(projectPath, config);
54
+ await FeatureFlags.reloadValues();
55
+ // eslint-disable-next-line spellcheck/spell-checker
56
+ context.print.warning(
57
+ `\nThe project is configured with 'transformerVersion': ${transformerVersionFF()}, but 'useExperimentalPipelinedTransformer': ${useExperimentalPipelinedTransformerFF()}. Setting the 'transformerVersion': ${
58
+ config.features.graphqltransformer.transformerversion
59
+ }. 'useExperimentalPipelinedTransformer' is deprecated.`,
60
+ );
61
+ };
@@ -1,14 +1,19 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs-extra';
3
- import { extractArgs } from './extractArgs';
4
3
  import { $TSContext } from 'amplify-cli-core';
4
+ import { extractArgs } from './extractArgs';
5
5
 
6
- export const getUiBuilderComponentsPath = (context: $TSContext) => {
6
+ /**
7
+ *
8
+ * locates components path directory
9
+ */
10
+ export const getUiBuilderComponentsPath = (context: $TSContext): string => {
7
11
  const args = extractArgs(context);
8
12
  const srcDir = args.srcDir ? args.srcDir : context.exeInfo.projectConfig.javascript.config.SourceDir;
9
13
  const uiBuilderComponentsPath = path.resolve(path.join('.', srcDir, 'ui-components'));
10
14
 
11
15
  if (!fs.existsSync(uiBuilderComponentsPath)) {
16
+ // eslint-disable-next-line spellcheck/spell-checker
12
17
  fs.mkdirpSync(uiBuilderComponentsPath);
13
18
  }
14
19
 
@@ -1,30 +1,41 @@
1
1
  import { printer } from 'amplify-prompts';
2
2
  import { $TSContext } from 'amplify-cli-core';
3
3
  import {
4
- StudioComponent, StudioTheme, GenericDataSchema,
4
+ StudioComponent, StudioTheme, GenericDataSchema, StudioForm, StudioSchema,checkIsSupportedAsForm
5
5
  } from '@aws-amplify/codegen-ui';
6
- import { createUiBuilderComponent, createUiBuilderTheme } from './codegenResources';
6
+ import {
7
+ createUiBuilderComponent,
8
+ createUiBuilderForm,
9
+ createUiBuilderTheme,
10
+ generateBaseForms,
11
+ } from './codegenResources';
7
12
  import { getUiBuilderComponentsPath } from './getUiBuilderComponentsPath';
8
13
 
14
+ type CodegenResponse<T extends StudioSchema> =
15
+ | {
16
+ resultType: 'SUCCESS';
17
+ schema: T;
18
+ schemaName?: string;
19
+ }
20
+ | {
21
+ resultType: 'FAILURE';
22
+ schemaName: string;
23
+ error: Error;
24
+ schema?: T;
25
+ };
26
+
9
27
  /**
10
28
  * Returns instances of StudioComponent from the component schemas
11
29
  */
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- export const generateUiBuilderComponents = (context: $TSContext, componentSchemas: any[], dataSchema?: GenericDataSchema): ({
14
- resultType: string;
15
- component: StudioComponent;
16
- schemaName?: undefined;
17
- error?: undefined;
18
- } | {
19
- resultType: string;
20
- schemaName: string;
21
- error: Error;
22
- component?: undefined;
23
- })[] => {
24
- const componentResults = componentSchemas.map(schema => {
30
+ export const generateUiBuilderComponents = (
31
+ context: $TSContext,
32
+ componentSchemas: any[], // eslint-disable-line @typescript-eslint/no-explicit-any
33
+ dataSchema?: GenericDataSchema,
34
+ ): CodegenResponse<StudioComponent>[] => {
35
+ const componentResults = componentSchemas.map<CodegenResponse<StudioComponent>>(schema => {
25
36
  try {
26
37
  const component = createUiBuilderComponent(context, schema, dataSchema);
27
- return { resultType: 'SUCCESS', component };
38
+ return { resultType: 'SUCCESS', schema: component };
28
39
  } catch (e) {
29
40
  printer.debug(`Failure caught processing ${schema.name}`);
30
41
  printer.debug(e);
@@ -44,21 +55,11 @@ export const generateUiBuilderComponents = (context: $TSContext, componentSchema
44
55
  * Returns instances of StudioTheme from theme schemas
45
56
  */
46
57
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
- export const generateUiBuilderThemes = (context: $TSContext, themeSchemas: any[]): ({
48
- resultType: string;
49
- theme: StudioTheme;
50
- schemaName?: undefined;
51
- error?: undefined;
52
- } | {
53
- resultType: string;
54
- schemaName: string;
55
- error: Error;
56
- theme?: undefined;
57
- })[] => {
58
- const themeResults = themeSchemas.map(schema => {
58
+ export const generateUiBuilderThemes = (context: $TSContext, themeSchemas: any[]): CodegenResponse<StudioTheme>[] => {
59
+ const themeResults = themeSchemas.map<CodegenResponse<StudioTheme>>(schema => {
59
60
  try {
60
61
  const theme = createUiBuilderTheme(context, schema);
61
- return { resultType: 'SUCCESS', theme };
62
+ return { resultType: 'SUCCESS', schema: theme };
62
63
  } catch (e) {
63
64
  printer.debug(`Failure caught processing ${schema.name}`);
64
65
  printer.debug(e);
@@ -71,3 +72,47 @@ export const generateUiBuilderThemes = (context: $TSContext, themeSchemas: any[]
71
72
  );
72
73
  return themeResults;
73
74
  };
75
+
76
+ /**
77
+ * Returns instances of StudioForm from form schemas
78
+ */
79
+ export const generateUiBuilderForms = (
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ context: $TSContext,
82
+ formSchemas: any[],
83
+ dataSchema?: GenericDataSchema,
84
+ autoGenerateForms?: boolean,
85
+ ): CodegenResponse<StudioForm>[] => {
86
+ const modelMap: { [model: string]: Set<'create' | 'update'> } = {};
87
+ if (dataSchema?.dataSourceType === 'DataStore' && autoGenerateForms) {
88
+ Object.entries(dataSchema.models).forEach(([name, model]) => {
89
+ if(checkIsSupportedAsForm(model) && !model.isJoinTable) {
90
+ modelMap[name] = new Set(['create', 'update']);
91
+ }
92
+ });
93
+ }
94
+ const codegenForm = (schema: StudioForm): CodegenResponse<StudioForm> => {
95
+ try {
96
+ const form = createUiBuilderForm(context, schema, dataSchema);
97
+ return { resultType: 'SUCCESS', schema: form };
98
+ } catch (e) {
99
+ printer.debug(`Failure caught processing ${schema.name}`);
100
+ printer.debug(e);
101
+ return { resultType: 'FAILURE', schemaName: schema.name, error: e };
102
+ }
103
+ };
104
+ const formResults = formSchemas.map((schema: StudioForm) => {
105
+ if (schema?.dataType && schema.dataType?.dataSourceType === 'DataStore') {
106
+ modelMap[schema.dataType.dataTypeName]?.delete(schema.formActionType);
107
+ }
108
+ return codegenForm(schema);
109
+ });
110
+
111
+ // append remaining models
112
+ formResults.push(...generateBaseForms(modelMap).map(codegenForm));
113
+
114
+ printer.debug(
115
+ `Generated ${formResults.filter(result => result.resultType === 'SUCCESS').length} forms in ${getUiBuilderComponentsPath(context)}`,
116
+ );
117
+ return formResults;
118
+ };
@@ -1,6 +1,9 @@
1
1
  import { printer } from 'amplify-prompts';
2
2
  import path from 'path';
3
3
 
4
- export async function run() {
4
+ /**
5
+ * runs version command
6
+ */
7
+ export const run = async (): Promise<void> => {
5
8
  printer.info((await import(path.join(__dirname, '..', '..', 'package.json'))).version);
6
- }
9
+ };
@@ -1,14 +1,16 @@
1
1
  import { $TSContext } from 'amplify-cli-core';
2
2
  import { printer } from 'amplify-prompts';
3
+ import { run as cloneComponentsFromEnv } from '../commands/cloneComponentsFromEnv';
3
4
 
4
- export async function run(context: $TSContext) {
5
+ /**
6
+ *
7
+ * runs after adding an environment
8
+ */
9
+ export const run = async (context: $TSContext): Promise<void> => {
5
10
  try {
6
- await context.amplify.invokePluginMethod(context, 'ui-builder', undefined, 'executeAmplifyCommand', [
7
- context,
8
- 'cloneComponentsFromEnv',
9
- ]);
11
+ await cloneComponentsFromEnv(context);
10
12
  } catch (e) {
11
13
  // Swallow all errors from the uibuilder plugin
12
14
  printer.debug(e);
13
15
  }
14
- }
16
+ };
@@ -1,11 +1,15 @@
1
1
  import { $TSContext } from 'amplify-cli-core';
2
2
  import { printer } from 'amplify-prompts';
3
+ import { run as generateComponents } from '../commands/generateComponents';
3
4
 
4
- export async function run(context: $TSContext) {
5
+ /**
6
+ * The code to run after a pull.
7
+ */
8
+ export const run = async (context: $TSContext): Promise<void> => {
5
9
  try {
6
- await context.amplify.invokePluginMethod(context, 'ui-builder', undefined, 'executeAmplifyCommand', [context, 'generateComponents']);
10
+ await generateComponents(context);
7
11
  } catch (e) {
8
12
  // Swallow all errors from the uibuilder plugin
9
13
  printer.debug(e);
10
14
  }
11
- }
15
+ };
@@ -0,0 +1,15 @@
1
+ import { $TSContext } from 'amplify-cli-core';
2
+ import { printer } from 'amplify-prompts';
3
+ import { run as generateComponents } from '../commands/generateComponents';
4
+
5
+ /**
6
+ * The code to run after a push
7
+ */
8
+ export const run = async (context: $TSContext): Promise<void> => {
9
+ try {
10
+ await generateComponents(context);
11
+ } catch (e) {
12
+ // Swallow all errors from the uibuilder plugin
13
+ printer.debug(e);
14
+ }
15
+ };