diff --git a/packages/codegen-ui-react/lib/__tests__/helpers/amplify-js-versioning.test.ts b/packages/codegen-ui-react/lib/__tests__/helpers/amplify-js-versioning.test.ts new file mode 100644 index 00000000..302fd625 --- /dev/null +++ b/packages/codegen-ui-react/lib/__tests__/helpers/amplify-js-versioning.test.ts @@ -0,0 +1,71 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { getAmplifyJSVersionToRender } from '../../helpers/amplify-js-versioning'; +import { AMPLIFY_JS_V5, AMPLIFY_JS_V6 } from '../../utils/constants'; + +let isAmplifyJSV6Enabled = false; + +describe('Helpers', () => { + beforeEach(() => { + isAmplifyJSV6Enabled = false; + }); + + it('should return v5 if aws-amplify dependency is undefined', () => { + expect(getAmplifyJSVersionToRender({}, { isAmplifyJSV6Enabled })).toBe(AMPLIFY_JS_V5); + }); + + it('should return v6 if aws-amplify dependency is v6 but v6 is disabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^6.0.0' }, { isAmplifyJSV6Enabled })).toBe(AMPLIFY_JS_V6); + }); + + it('should return v5 if aws-amplify dependency is v5 and v6 is enabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^5.0.0' }, { isAmplifyJSV6Enabled: true })).toBe( + AMPLIFY_JS_V5, + ); + }); + + it('should return v5 if aws-amplify dependency is v5', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^5.0.2' }, { isAmplifyJSV6Enabled })).toBe(AMPLIFY_JS_V5); + }); + + it('should return v5 if aws-amplify dependency is latest but v6 is disabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': 'latest' }, { isAmplifyJSV6Enabled })).toBe(AMPLIFY_JS_V5); + }); + + it('should return v6 if aws-amplify dependency is latest and v6 is enabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': 'latest' }, { isAmplifyJSV6Enabled: true })).toBe( + AMPLIFY_JS_V6, + ); + }); + + it('should return v6 if aws-amplify dependency is v6 and v6 is enabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^6.0.2' }, { isAmplifyJSV6Enabled: true })).toBe( + AMPLIFY_JS_V6, + ); + }); + + it('should return v5 if aws-amplify dependency is a v5 tagged release', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^5.0.2-beta' }, { isAmplifyJSV6Enabled: true })).toBe( + AMPLIFY_JS_V5, + ); + }); + + it('should return v6 if aws-amplify dependency is a v6 tagged release and v6 is enabled', () => { + expect(getAmplifyJSVersionToRender({ 'aws-amplify': '^6.0.2-beta' }, { isAmplifyJSV6Enabled: true })).toBe( + AMPLIFY_JS_V6, + ); + }); +}); diff --git a/packages/codegen-ui-react/lib/__tests__/react-studio-dependency-provider.test.ts b/packages/codegen-ui-react/lib/__tests__/react-studio-dependency-provider.test.ts index e2f69ae3..189b5207 100644 --- a/packages/codegen-ui-react/lib/__tests__/react-studio-dependency-provider.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/react-studio-dependency-provider.test.ts @@ -19,6 +19,9 @@ import { ReactRequiredDependencyProvider } from '..'; describe('ReactStudioDependencyProvider', () => { const requiredDependencies = new ReactRequiredDependencyProvider().getRequiredDependencies(false); const requiredDependenciesWithStorageManager = new ReactRequiredDependencyProvider().getRequiredDependencies(true); + const requiredDependenciesWithAmplifyJSV6 = new ReactRequiredDependencyProvider().getRequiredDependencies(true, { + dependencies: { 'aws-amplify': '^6.0.0' }, + }); describe('getRequiredDependencies', () => { it('has required dependencies', () => { @@ -50,5 +53,25 @@ describe('ReactStudioDependencyProvider', () => { requiredDependenciesWithStorageManager.filter((dep) => dep.dependencyName === '@aws-amplify/ui-react-storage'), ).toBeTruthy(); }); + + it('includes amplify js v6 semver range', () => { + expect(requiredDependenciesWithAmplifyJSV6).toMatchObject([ + { + dependencyName: '@aws-amplify/ui-react', + supportedSemVerPattern: '^6.0.0', + reason: 'Required to leverage Amplify UI primitives, and Amplify Studio component helper functions.', + }, + { + dependencyName: 'aws-amplify', + supportedSemVerPattern: '^6.0.0', + reason: 'Required to leverage DataStore.', + }, + { + dependencyName: '@aws-amplify/ui-react-storage', + supportedSemVerPattern: '^3.0.0', + reason: 'Required to leverage StorageManager.', + }, + ]); + }); }); }); diff --git a/packages/codegen-ui-react/lib/helpers/amplify-js-versioning.ts b/packages/codegen-ui-react/lib/helpers/amplify-js-versioning.ts new file mode 100644 index 00000000..ac5debfe --- /dev/null +++ b/packages/codegen-ui-react/lib/helpers/amplify-js-versioning.ts @@ -0,0 +1,69 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import semverGte from 'semver/functions/gte'; +import semverValid from 'semver/functions/valid'; +import semverCoerce from 'semver/functions/coerce'; +import { AMPLIFY_JS_V5, AMPLIFY_JS_V6 } from '../utils/constants'; +import { ImportValue } from '../imports'; + +export function isAmplifyJSV6RenderingEnabled(): boolean { + return false; +} + +export function getLatestAmplifyJSV6RenderingEnabled( + isAmplifyJSV6Enabled: boolean = isAmplifyJSV6RenderingEnabled(), +): string { + if (isAmplifyJSV6Enabled) { + return AMPLIFY_JS_V6; + } + return AMPLIFY_JS_V5; +} + +export function getAmplifyJSVersionToRender( + dependencies: { [key: string]: string } = {}, + { isAmplifyJSV6Enabled }: { isAmplifyJSV6Enabled: boolean } = { + isAmplifyJSV6Enabled: isAmplifyJSV6RenderingEnabled(), + }, +) { + const awsAmplifyVersion = dependencies['aws-amplify']; + // semver will error on a 'latest' value so do this first + if (awsAmplifyVersion === 'latest') { + return getLatestAmplifyJSV6RenderingEnabled(isAmplifyJSV6Enabled); + } + // Allows to use tagged releases + // e.g. semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7' + const sanitizedVersion = semverValid(semverCoerce(awsAmplifyVersion)); + + if (sanitizedVersion) { + // check if >= 6 + if (semverGte(sanitizedVersion, AMPLIFY_JS_V6)) { + return AMPLIFY_JS_V6; + } + } + // If there isn't a version for aws-amplify in the project + // then this is an older version of the project not running latest + // cli, so default to 5 + // If there is a version and it's 5 return 5 + // If the version is 6 but v6 isn't enabled yet, return 5 + return AMPLIFY_JS_V5; +} + +export function getAmplifyJSAPIImport(renderConfigDependencies: { [key: string]: string } = {}) { + if (getAmplifyJSVersionToRender(renderConfigDependencies) === AMPLIFY_JS_V6) { + return ImportValue.GENERATE_CLIENT; + } + return ImportValue.API; +} diff --git a/packages/codegen-ui-react/lib/imports/import-mapping.ts b/packages/codegen-ui-react/lib/imports/import-mapping.ts index 23425b0e..d383134a 100644 --- a/packages/codegen-ui-react/lib/imports/import-mapping.ts +++ b/packages/codegen-ui-react/lib/imports/import-mapping.ts @@ -23,6 +23,7 @@ export enum ImportSource { LOCAL_SCHEMA = '../models/schema', UTILS = './utils', AMPLIFY = 'aws-amplify', + AMPLIFY_API = 'aws-amplify/api', } export enum ImportValue { @@ -55,6 +56,7 @@ export enum ImportValue { API = 'API', PAGINATION = 'Pagination', PLACEHOLDER = 'Placeholder', + GENERATE_CLIENT = 'generateClient', } export const ImportMapping: Record = { @@ -86,4 +88,5 @@ export const ImportMapping: Record = { [ImportValue.API]: ImportSource.AMPLIFY, [ImportValue.PAGINATION]: ImportSource.UI_REACT, [ImportValue.PLACEHOLDER]: ImportSource.UI_REACT, + [ImportValue.GENERATE_CLIENT]: ImportSource.AMPLIFY_API, }; diff --git a/packages/codegen-ui-react/lib/react-required-dependency-provider.ts b/packages/codegen-ui-react/lib/react-required-dependency-provider.ts index 409c29b1..c7a4a1c0 100644 --- a/packages/codegen-ui-react/lib/react-required-dependency-provider.ts +++ b/packages/codegen-ui-react/lib/react-required-dependency-provider.ts @@ -14,22 +14,28 @@ limitations under the License. */ import { RequiredDependency, RequiredDependencyProvider } from '@aws-amplify/codegen-ui'; +import { getAmplifyJSVersionToRender } from './helpers/amplify-js-versioning'; +import { AMPLIFY_JS_V5 } from './utils/constants'; type SemVerRequiredDependency = RequiredDependency & { supportedSemVerPattern: string; }; export class ReactRequiredDependencyProvider extends RequiredDependencyProvider { - getRequiredDependencies(hasStorageManager?: boolean): SemVerRequiredDependency[] { + getRequiredDependencies( + hasStorageManager?: boolean, + config?: { dependencies: { [key: string]: string } }, + ): SemVerRequiredDependency[] { + const amplifyJSVersion = getAmplifyJSVersionToRender(config?.dependencies); const dependencies = [ { dependencyName: '@aws-amplify/ui-react', - supportedSemVerPattern: '^4.6.0', + supportedSemVerPattern: amplifyJSVersion === AMPLIFY_JS_V5 ? '>=4.6.0 <6.0.0' : '^6.0.0', reason: 'Required to leverage Amplify UI primitives, and Amplify Studio component helper functions.', }, { dependencyName: 'aws-amplify', - supportedSemVerPattern: '^5.0.2', + supportedSemVerPattern: amplifyJSVersion === AMPLIFY_JS_V5 ? '^5.0.2' : '^6.0.0', reason: 'Required to leverage DataStore.', }, ]; @@ -37,7 +43,7 @@ export class ReactRequiredDependencyProvider extends RequiredDependencyProvider< if (hasStorageManager) { dependencies.push({ dependencyName: '@aws-amplify/ui-react-storage', - supportedSemVerPattern: '^1.1.0', + supportedSemVerPattern: amplifyJSVersion === AMPLIFY_JS_V5 ? '^1.1.0' : '^3.0.0', reason: 'Required to leverage StorageManager.', }); } diff --git a/packages/codegen-ui-react/lib/utils/constants.ts b/packages/codegen-ui-react/lib/utils/constants.ts index aca061da..3ca88f80 100644 --- a/packages/codegen-ui-react/lib/utils/constants.ts +++ b/packages/codegen-ui-react/lib/utils/constants.ts @@ -18,3 +18,7 @@ export const COMPOSITE_PRIMARY_KEY_PROP_NAME = 'id'; export const STORAGE_FILE_KEY = 'key'; export const STORAGE_FILE_ALGO_TYPE = 'SHA-1'; + +export const AMPLIFY_JS_V5 = '5.0.0'; + +export const AMPLIFY_JS_V6 = '6.0.0'; diff --git a/packages/codegen-ui-react/package-lock.json b/packages/codegen-ui-react/package-lock.json index 131d50d3..44882d72 100644 --- a/packages/codegen-ui-react/package-lock.json +++ b/packages/codegen-ui-react/package-lock.json @@ -12,6 +12,7 @@ "@aws-amplify/codegen-ui": "2.15.9", "@typescript/vfs": "~1.3.5", "pluralize": "^8.0.0", + "semver": "^7.5.4", "typescript": "<=4.5.0" }, "devDependencies": { @@ -23,8 +24,7 @@ "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", - "pascalcase": "1.0.0", - "semver": "^7.3.5" + "pascalcase": "1.0.0" }, "optionalDependencies": { "prettier": "2.3.2" @@ -231,9 +231,9 @@ } }, "node_modules/@aws-amplify/codegen-ui": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.13.1.tgz", - "integrity": "sha512-E348akzfNse6vtYMWW1ZFJwmI5km4TUzr7zW+mhw3/7gqqCE5ARG2gy6voL9+w8oC+XAg0g7H3GstpxdLhCVKA==", + "version": "2.15.9", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.15.9.tgz", + "integrity": "sha512-Sdi+8Q+nrWrzJEJVUstT4iRUQ5nYaHAZb9J0wsuE2QPRec5qHYMMgOtjTznjrMOzGAfJ9Efwq7iEHVOWJ8XQGQ==", "dependencies": { "change-case": "^4.1.2", "yup": "^0.32.11" @@ -18800,10 +18800,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -18818,7 +18817,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -18829,8 +18827,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { "version": "0.18.0", @@ -21148,9 +21145,9 @@ } }, "@aws-amplify/codegen-ui": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.13.1.tgz", - "integrity": "sha512-E348akzfNse6vtYMWW1ZFJwmI5km4TUzr7zW+mhw3/7gqqCE5ARG2gy6voL9+w8oC+XAg0g7H3GstpxdLhCVKA==", + "version": "2.15.9", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.15.9.tgz", + "integrity": "sha512-Sdi+8Q+nrWrzJEJVUstT4iRUQ5nYaHAZb9J0wsuE2QPRec5qHYMMgOtjTznjrMOzGAfJ9Efwq7iEHVOWJ8XQGQ==", "requires": { "change-case": "^4.1.2", "yup": "^0.32.11" @@ -36596,10 +36593,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" }, @@ -36608,7 +36604,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -36616,8 +36611,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, diff --git a/packages/codegen-ui-react/package.json b/packages/codegen-ui-react/package.json index def0d76a..afbe2b5a 100644 --- a/packages/codegen-ui-react/package.json +++ b/packages/codegen-ui-react/package.json @@ -28,14 +28,14 @@ "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", - "pascalcase": "1.0.0", - "semver": "^7.3.5" + "pascalcase": "1.0.0" }, "dependencies": { "@aws-amplify/codegen-ui": "2.15.9", "@typescript/vfs": "~1.3.5", "pluralize": "^8.0.0", - "typescript": "<=4.5.0" + "typescript": "<=4.5.0", + "semver": "^7.5.4" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", diff --git a/packages/codegen-ui/lib/required-dependency-provider.ts b/packages/codegen-ui/lib/required-dependency-provider.ts index 41c6d812..d9b2954b 100644 --- a/packages/codegen-ui/lib/required-dependency-provider.ts +++ b/packages/codegen-ui/lib/required-dependency-provider.ts @@ -19,5 +19,8 @@ export type RequiredDependency = { }; export abstract class RequiredDependencyProvider { - abstract getRequiredDependencies(hasStorageManager?: boolean): DependencyType[]; + abstract getRequiredDependencies( + hasStorageManager?: boolean, + config?: { dependencies: { [key: string]: string } }, + ): DependencyType[]; }