diff --git a/.vscode/launch.json b/.vscode/launch.json index 78289cba0f9..3aaa8b16558 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -39,9 +39,9 @@ "name": "Debug schematics", "skipFiles": ["/**"], "program": "${workspaceFolder}/node_modules/@angular/cli/bin/ng", - "args": ["add", "@spartacus/schematics@latest"], + "args": ["add", "--skip-confirmation", "@spartacus/schematics@latest"], "console": "integratedTerminal", - "outFiles": ["${workspaceFolder}/**/*.js"] + "outFiles": ["${workspaceFolder}/node_modules/@spartacus/**/*.js"] }, // to debug a schematics Jest test, make sure that a spec.ts file is currently opened. diff --git a/docs/libs/creating-lib.md b/docs/libs/creating-lib.md index 4afad7ac4a2..38a0f38bc08 100644 --- a/docs/libs/creating-lib.md +++ b/docs/libs/creating-lib.md @@ -8,16 +8,19 @@ This document can also serve as the guideline for the future schematic that can ## Table of contents -- [Naming conventions](#Naming-conventions) -- [Generating a library](#Generating-a-library) -- [Aligning with the other libs](#Aligning-with-the-other-libs) - - [Modifying the generated files](#Modifying-the-generated-files) - - [Additional changes to existing files](#Additional-changes-to-existing-files) -- [Multi-entry point library](#multi-entry-point-library) -- [Testing](#Testing) -- [Schematics](#Schematics) - - [Configuring Schematics](#Configuring Schematics) - - [Testing Schematics](#Testing Schematics) +- [Creating a Spartacus library](#creating-a-spartacus-library) + - [Table of contents](#table-of-contents) + - [Naming conventions](#naming-conventions) + - [Generating a library](#generating-a-library) + - [Aligning with the other libs](#aligning-with-the-other-libs) + - [Modifying the generated files](#modifying-the-generated-files) + - [Additional changes to existing files](#additional-changes-to-existing-files) + - [Multi-entry point library](#multi-entry-point-library) + - [Process](#process) + - [Testing](#testing) + - [Schematics](#schematics) + - [Configuring Schematics](#configuring-schematics) + - [Testing Schematics](#testing-schematics) ## Naming conventions @@ -384,6 +387,7 @@ There are couple of required changes to make sure schematics will work properly - add new feature lib schema.json elements in schematics folder - `feature-libs\\schematics\add-\schema.json` where the `lib-name` is the name of the new library - add new feature chain method to 'shouldAddFeature' and function to add it - `feature-libs\\schematics\add-\index.ts` where the `lib-name` is the name of the new library - create new feature lib module in - `projects/storefrontapp/src/app/spartacus/features` +- create your schematics configuration in e.g. `projects/schematics/src/shared/lib-configs/asm-schematics-config.ts` and add it to the `projects/schematics/src/shared/schematics-config-mappings.ts` file. ### Testing Schematics diff --git a/docs/migration/5_0.md b/docs/migration/5_0.md index f493a68ab47..379051601e8 100644 --- a/docs/migration/5_0.md +++ b/docs/migration/5_0.md @@ -658,3 +658,7 @@ Due to i18next migration, certain set of keys have been migrated (from `_plural` #### Template Changes - Changed `` to `` + +## Schematics + +`Account` and `Profile` CLI names have been changed to `User-Account` and `User-Profile`, respectively, to better reflect their purpose. diff --git a/feature-libs/asm/schematics/add-asm/index.ts b/feature-libs/asm/schematics/add-asm/index.ts index d81c4df6097..6af3d3e81dd 100644 --- a/feature-libs/asm/schematics/add-asm/index.ts +++ b/feature-libs/asm/schematics/add-asm/index.ts @@ -1,73 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - ASM_MODULE, - ASM_ROOT_MODULE, - CLI_ASM_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusAsmOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_ASM, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - ASM_FEATURE_NAME_CONSTANT, - ASM_FOLDER_NAME, - ASM_MODULE_NAME, - ASM_TRANSLATIONS, - ASM_TRANSLATION_CHUNKS_CONFIG, - SPARTACUS_ASM_ASSETS, - SPARTACUS_ASM_ROOT, - SCSS_FILE_NAME, -} from '../constants'; export function addAsmFeatures(options: SpartacusAsmOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_ASM_FEATURE, options.features) - ? addAsmFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addAsmFeature(options: SpartacusAsmOptions): Rule { - return addLibraryFeature(options, { - folderName: ASM_FOLDER_NAME, - moduleName: ASM_MODULE_NAME, - featureModule: { - name: ASM_MODULE, - importPath: SPARTACUS_ASM, - }, - rootModule: { - name: ASM_ROOT_MODULE, - importPath: SPARTACUS_ASM_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_ASM_ROOT, - namedImports: [ASM_FEATURE_NAME_CONSTANT], - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_ASM, - }, - i18n: { - resources: ASM_TRANSLATIONS, - chunks: ASM_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_ASM_ASSETS, - }, - }); -} diff --git a/feature-libs/asm/schematics/add-asm/index_spec.ts b/feature-libs/asm/schematics/add-asm/index_spec.ts index 76e9b59db64..f384c8e1ff5 100644 --- a/feature-libs/asm/schematics/add-asm/index_spec.ts +++ b/feature-libs/asm/schematics/add-asm/index_spec.ts @@ -10,21 +10,25 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_ASM_FEATURE, + asmFeatureModulePath, + ASM_FEATURE_NAME, LibraryOptions as SpartacusAsmOptions, SpartacusOptions, + SPARTACUS_ASM, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/asm/asm-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/asm.scss'; describe('Spartacus Asm schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_ASM, + collectionPath + ); let appTree: UnitTestTree; @@ -57,7 +61,7 @@ describe('Spartacus Asm schematics: ng-add', () => { const asmFeatureOptions: SpartacusAsmOptions = { ...libraryNoFeaturesOptions, - features: [CLI_ASM_FEATURE], + features: [ASM_FEATURE_NAME], }; beforeEach(async () => { @@ -99,7 +103,7 @@ describe('Spartacus Asm schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(asmFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -137,10 +141,15 @@ describe('Spartacus Asm schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(asmFeatureModulePath); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -169,7 +178,7 @@ describe('Spartacus Asm schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(asmFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/asm/schematics/add-library/index.ts b/feature-libs/asm/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/asm/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/asm/schematics/collection.json b/feature-libs/asm/schematics/collection.json index 744605e423c..453e966bead 100644 --- a/feature-libs/asm/schematics/collection.json +++ b/feature-libs/asm/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-asm/index#addAsmFeatures", "description": "Add and configure Spartacus' Asm features", diff --git a/feature-libs/asm/schematics/constants.ts b/feature-libs/asm/schematics/constants.ts deleted file mode 100644 index d087684edde..00000000000 --- a/feature-libs/asm/schematics/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { SPARTACUS_ASM } from '@spartacus/schematics'; - -export const ASM_FOLDER_NAME = 'asm'; -export const ASM_MODULE_NAME = 'Asm'; - -export const ASM_FEATURE_NAME_CONSTANT = 'ASM_FEATURE'; -export const SPARTACUS_ASM_ROOT = `${SPARTACUS_ASM}/root`; -export const SPARTACUS_ASM_ASSETS = `${SPARTACUS_ASM}/assets`; -export const ASM_TRANSLATIONS = 'asmTranslations'; -export const ASM_TRANSLATION_CHUNKS_CONFIG = 'asmTranslationChunksConfig'; -export const SCSS_FILE_NAME = 'asm.scss'; diff --git a/feature-libs/cart/schematics/add-cart/__snapshots__/index_spec.ts.snap b/feature-libs/cart/schematics/add-cart/__snapshots__/index_spec.ts.snap index f87db502b40..5b05392da0d 100644 --- a/feature-libs/cart/schematics/add-cart/__snapshots__/index_spec.ts.snap +++ b/feature-libs/cart/schematics/add-cart/__snapshots__/index_spec.ts.snap @@ -44,10 +44,20 @@ import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; [CART_BASE_FEATURE]: { module: () => import('@spartacus/cart/base').then((m) => m.CartBaseModule), - }, [MINI_CART_FEATURE]: { + }, + } + }), + provideConfig({ + featureModules: { + [MINI_CART_FEATURE]: { module: () => import('@spartacus/cart/base/components/mini-cart').then((m) => m.MiniCartModule), - }, [ADD_TO_CART_FEATURE]: { + }, + } + }), + provideConfig({ + featureModules: { + [ADD_TO_CART_FEATURE]: { module: () => import('@spartacus/cart/base/components/add-to-cart').then((m) => m.AddToCartModule), }, @@ -193,7 +203,7 @@ exports[`Spartacus Cart schematics: ng-add Cart Base feature general setup styli }" `; -exports[`Spartacus Cart schematics: ng-add Quick Order feature Cart Import Export feature eager loading should import appropriate modules 1`] = ` +exports[`Spartacus Cart schematics: ng-add Cart Import Export feature eager loading should import appropriate modules 1`] = ` "import { NgModule } from '@angular/core'; import { ImportExportModule } from \\"@spartacus/cart/import-export\\"; import { importExportTranslationChunksConfig, importExportTranslations } from \\"@spartacus/cart/import-export/assets\\"; @@ -217,7 +227,7 @@ export class CartImportExportFeatureModule { } " `; -exports[`Spartacus Cart schematics: ng-add Quick Order feature Cart Import Export feature general setup should add the feature using the lazy loading syntax 1`] = ` +exports[`Spartacus Cart schematics: ng-add Cart Import Export feature general setup should add the feature using the lazy loading syntax 1`] = ` "import { NgModule } from '@angular/core'; import { importExportTranslationChunksConfig, importExportTranslations } from \\"@spartacus/cart/import-export/assets\\"; import { CART_IMPORT_EXPORT_FEATURE, ImportExportRootModule } from \\"@spartacus/cart/import-export/root\\"; @@ -248,9 +258,9 @@ export class CartImportExportFeatureModule { } " `; -exports[`Spartacus Cart schematics: ng-add Quick Order feature Cart Import Export feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/cart\\";"`; +exports[`Spartacus Cart schematics: ng-add Cart Import Export feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/cart\\";"`; -exports[`Spartacus Cart schematics: ng-add Quick Order feature Cart Import Export feature general setup styling should update angular.json 1`] = ` +exports[`Spartacus Cart schematics: ng-add Cart Import Export feature general setup styling should update angular.json 1`] = ` "{ \\"$schema\\": \\"./node_modules/@angular/cli/lib/config/schema.json\\", \\"version\\": 1, @@ -784,7 +794,12 @@ import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; [CART_WISH_LIST_FEATURE]: { module: () => import('@spartacus/cart/wish-list').then((m) => m.WishListModule), - }, [ADD_TO_WISHLIST_FEATURE]: { + }, + } + }), + provideConfig({ + featureModules: { + [ADD_TO_WISHLIST_FEATURE]: { module: () => import('@spartacus/cart/wish-list/components/add-to-wishlist').then((m) => m.AddToWishListModule), }, diff --git a/feature-libs/cart/schematics/add-cart/index.ts b/feature-libs/cart/schematics/add-cart/index.ts index a99a95bc8a6..5346286099c 100644 --- a/feature-libs/cart/schematics/add-cart/index.ts +++ b/feature-libs/cart/schematics/add-cart/index.ts @@ -1,274 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - ADD_TO_CART_MODULE, - ADD_TO_WISHLIST_MODULE, - CART_BASE_MODULE, - CART_BASE_ROOT_MODULE, - CART_IMPORT_EXPORT_MODULE, - CART_IMPORT_EXPORT_ROOT_MODULE, - CART_WISHLIST_MODULE, - CART_WISHLIST_ROOT_MODULE, - CLI_CART_BASE_FEATURE, - CLI_CART_IMPORT_EXPORT_FEATURE, - CLI_CART_QUICK_ORDER_FEATURE, - CLI_CART_SAVED_CART_FEATURE, - CLI_CART_WISHLIST_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusCartOptions, - MINI_CART_MODULE, - QUICK_ORDER_MODULE, - QUICK_ORDER_ROOT_MODULE, readPackageJson, - SAVED_CART_MODULE, - SAVED_CART_ROOT_MODULE, - shouldAddFeature, - SPARTACUS_CART, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - ADD_TO_CART_ENTRY_POINT, - ADD_TO_CART_FEATURE_NAME_CONSTANT, - ADD_TO_WISHLIST_ENTRY_POINT, - ADD_TO_WISHLIST_FEATURE_NAME_CONSTANT, - CART_BASE_FEATURE_MODULE_NAME, - CART_BASE_FEATURE_NAME_CONSTANT, - CART_BASE_TRANSLATIONS, - CART_BASE_TRANSLATION_CHUNKS_CONFIG, - CART_FOLDER_NAME, - CART_IMPORT_EXPORT_FEATURE_NAME_CONSTANT, - CART_IMPORT_EXPORT_MODULE_NAME, - CART_IMPORT_EXPORT_TRANSLATIONS, - CART_IMPORT_EXPORT_TRANSLATION_CHUNKS_CONFIG, - CART_QUICK_ORDER_FEATURE_NAME_CONSTANT, - CART_QUICK_ORDER_MODULE_NAME, - CART_SAVED_CART_FEATURE_NAME_CONSTANT, - CART_SAVED_CART_MODULE_NAME, - CART_WISHLIST_FEATURE_MODULE_NAME, - CART_WISHLIST_FEATURE_NAME_CONSTANT, - CART_WISHLIST_TRANSLATIONS, - CART_WISHLIST_TRANSLATION_CHUNKS_CONFIG, - MINI_CART_ENTRY_POINT, - MINI_CART_FEATURE_NAME_CONSTANT, - QUICK_ORDER_TRANSLATIONS, - QUICK_ORDER_TRANSLATION_CHUNKS_CONFIG, - SAVED_CART_TRANSLATIONS, - SAVED_CART_TRANSLATION_CHUNKS_CONFIG, - SCSS_FILE_NAME, - SPARTACUS_CART_BASE, - SPARTACUS_CART_BASE_ASSETS, - SPARTACUS_CART_BASE_ROOT, - SPARTACUS_CART_IMPORT_EXPORT, - SPARTACUS_CART_IMPORT_EXPORT_ASSETS, - SPARTACUS_CART_IMPORT_EXPORT_ROOT, - SPARTACUS_CART_WISHLIST, - SPARTACUS_CART_WISHLIST_ASSETS, - SPARTACUS_CART_WISHLIST_ROOT, - SPARTACUS_QUICK_ORDER, - SPARTACUS_QUICK_ORDER_ASSETS, - SPARTACUS_QUICK_ORDER_ROOT, - SPARTACUS_SAVED_CART, - SPARTACUS_SAVED_CART_ASSETS, - SPARTACUS_SAVED_CART_ROOT, -} from '../constants'; export function addCartFeatures(options: SpartacusCartOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); - return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), - - shouldAddFeature(CLI_CART_BASE_FEATURE, options.features) - ? addCartBaseFeature(options) - : noop(), - - shouldAddFeature(CLI_CART_WISHLIST_FEATURE, options.features) - ? addWishListFeature(options) - : noop(), + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); - shouldAddFeature(CLI_CART_SAVED_CART_FEATURE, options.features) - ? addSavedCartFeature(options) - : noop(), + return chain([ + analyzeApplication(options, features), - shouldAddFeature(CLI_CART_QUICK_ORDER_FEATURE, options.features) - ? addQuickOrderFeature(options) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_CART_IMPORT_EXPORT_FEATURE, options.features) - ? addCartImportExportFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addCartBaseFeature(options: SpartacusCartOptions): Rule { - return addLibraryFeature(options, { - folderName: CART_FOLDER_NAME, - moduleName: CART_BASE_FEATURE_MODULE_NAME, - featureModule: [ - { - name: CART_BASE_MODULE, - importPath: SPARTACUS_CART_BASE, - }, - { - name: MINI_CART_MODULE, - importPath: MINI_CART_ENTRY_POINT, - }, - { - name: ADD_TO_CART_MODULE, - importPath: ADD_TO_CART_ENTRY_POINT, - }, - ], - rootModule: { - name: CART_BASE_ROOT_MODULE, - importPath: SPARTACUS_CART_BASE_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CART_BASE_ROOT, - namedImports: [ - CART_BASE_FEATURE_NAME_CONSTANT, - MINI_CART_FEATURE_NAME_CONSTANT, - ADD_TO_CART_FEATURE_NAME_CONSTANT, - ], - }, - i18n: { - resources: CART_BASE_TRANSLATIONS, - chunks: CART_BASE_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CART_BASE_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CART, - }, - }); -} - -function addWishListFeature(options: SpartacusCartOptions): Rule { - return addLibraryFeature(options, { - folderName: CART_FOLDER_NAME, - moduleName: CART_WISHLIST_FEATURE_MODULE_NAME, - featureModule: [ - { - name: CART_WISHLIST_MODULE, - importPath: SPARTACUS_CART_WISHLIST, - }, - { - name: ADD_TO_WISHLIST_MODULE, - importPath: ADD_TO_WISHLIST_ENTRY_POINT, - }, - ], - rootModule: { - name: CART_WISHLIST_ROOT_MODULE, - importPath: SPARTACUS_CART_WISHLIST_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CART_WISHLIST_ROOT, - namedImports: [ - CART_WISHLIST_FEATURE_NAME_CONSTANT, - ADD_TO_WISHLIST_FEATURE_NAME_CONSTANT, - ], - }, - i18n: { - resources: CART_WISHLIST_TRANSLATIONS, - chunks: CART_WISHLIST_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CART_WISHLIST_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CART, - }, - }); -} - -function addSavedCartFeature(options: SpartacusCartOptions): Rule { - return addLibraryFeature(options, { - folderName: CART_FOLDER_NAME, - moduleName: CART_SAVED_CART_MODULE_NAME, - featureModule: { - name: SAVED_CART_MODULE, - importPath: SPARTACUS_SAVED_CART, - }, - rootModule: { - name: SAVED_CART_ROOT_MODULE, - importPath: SPARTACUS_SAVED_CART_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_SAVED_CART_ROOT, - namedImports: [CART_SAVED_CART_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: SAVED_CART_TRANSLATIONS, - chunks: SAVED_CART_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_SAVED_CART_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CART, - }, - }); -} - -function addQuickOrderFeature(options: SpartacusCartOptions): Rule { - return addLibraryFeature(options, { - folderName: CART_FOLDER_NAME, - moduleName: CART_QUICK_ORDER_MODULE_NAME, - featureModule: { - name: QUICK_ORDER_MODULE, - importPath: SPARTACUS_QUICK_ORDER, - }, - rootModule: { - name: QUICK_ORDER_ROOT_MODULE, - importPath: SPARTACUS_QUICK_ORDER_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_QUICK_ORDER_ROOT, - namedImports: [CART_QUICK_ORDER_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: QUICK_ORDER_TRANSLATIONS, - chunks: QUICK_ORDER_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_QUICK_ORDER_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CART, - }, - }); -} - -function addCartImportExportFeature(options: SpartacusCartOptions): Rule { - return addLibraryFeature(options, { - folderName: CART_FOLDER_NAME, - moduleName: CART_IMPORT_EXPORT_MODULE_NAME, - featureModule: { - name: CART_IMPORT_EXPORT_MODULE, - importPath: SPARTACUS_CART_IMPORT_EXPORT, - }, - rootModule: { - name: CART_IMPORT_EXPORT_ROOT_MODULE, - importPath: SPARTACUS_CART_IMPORT_EXPORT_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CART_IMPORT_EXPORT_ROOT, - namedImports: [CART_IMPORT_EXPORT_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: CART_IMPORT_EXPORT_TRANSLATIONS, - chunks: CART_IMPORT_EXPORT_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CART_IMPORT_EXPORT_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CART, - }, - }); -} diff --git a/feature-libs/cart/schematics/add-cart/index_spec.ts b/feature-libs/cart/schematics/add-cart/index_spec.ts index 161774056a8..577717b6f82 100644 --- a/feature-libs/cart/schematics/add-cart/index_spec.ts +++ b/feature-libs/cart/schematics/add-cart/index_spec.ts @@ -10,33 +10,33 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_CART_BASE_FEATURE, - CLI_CART_IMPORT_EXPORT_FEATURE, - CLI_CART_QUICK_ORDER_FEATURE, - CLI_CART_SAVED_CART_FEATURE, - CLI_CART_WISHLIST_FEATURE, + cartBaseFeatureModulePath, + CART_BASE_FEATURE_NAME, + CART_IMPORT_EXPORT_FEATURE_NAME, + CART_QUICK_ORDER_FEATURE_NAME, + CART_SAVED_CART_FEATURE_NAME, + CART_WISHLIST_FEATURE_NAME, + importExportFeatureModulePath, LibraryOptions as SpartacusCartOptions, + quickOrderFeatureModulePath, + savedCartFeatureModulePath, SpartacusOptions, + SPARTACUS_CART, SPARTACUS_SCHEMATICS, + userFeatureModulePath, + wishListFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const savedCartFeatureModulePath = - 'src/app/spartacus/features/cart/cart-saved-cart-feature.module.ts'; -const cartBaseFeatureModulePath = - 'src/app/spartacus/features/cart/cart-base-feature.module.ts'; -const wishListFeatureModulePath = - 'src/app/spartacus/features/cart/wish-list-feature.module.ts'; -const quickOrderFeatureModulePath = - 'src/app/spartacus/features/cart/cart-quick-order-feature.module.ts'; -const importExportFeatureModulePath = - 'src/app/spartacus/features/cart/cart-import-export-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/cart.scss'; describe('Spartacus Cart schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_CART, + collectionPath + ); let appTree: UnitTestTree; @@ -69,27 +69,27 @@ describe('Spartacus Cart schematics: ng-add', () => { const savedCartFeatureOptions: SpartacusCartOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CART_SAVED_CART_FEATURE], + features: [CART_SAVED_CART_FEATURE_NAME], }; const cartBaseFeatureOptions: SpartacusCartOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CART_BASE_FEATURE], + features: [CART_BASE_FEATURE_NAME], }; const wishListFeatureOptions: SpartacusCartOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CART_WISHLIST_FEATURE], + features: [CART_WISHLIST_FEATURE_NAME], }; const quickOrderFeatureOptions: SpartacusCartOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CART_QUICK_ORDER_FEATURE], + features: [CART_QUICK_ORDER_FEATURE_NAME], }; const cartImportExportFeatureOptions: SpartacusCartOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CART_IMPORT_EXPORT_FEATURE], + features: [CART_IMPORT_EXPORT_FEATURE_NAME], }; beforeEach(async () => { @@ -166,19 +166,24 @@ describe('Spartacus Cart schematics: ng-add', () => { }); }); - describe('Saved Cart feature', () => { + describe('Cart Base feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner - .runSchematicAsync('ng-add', savedCartFeatureOptions, appTree) + .runSchematicAsync('ng-add', cartBaseFeatureOptions, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(savedCartFeatureModulePath); + const module = appTree.readContent(cartBaseFeatureModulePath); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -197,29 +202,29 @@ describe('Spartacus Cart schematics: ng-add', () => { appTree = await schematicRunner .runSchematicAsync( 'ng-add', - { ...savedCartFeatureOptions, lazy: false }, + { ...cartBaseFeatureOptions, lazy: false }, appTree ) .toPromise(); }); it('should import appropriate modules', async () => { - const module = appTree.readContent(savedCartFeatureModulePath); + const module = appTree.readContent(cartBaseFeatureModulePath); expect(module).toMatchSnapshot(); }); }); }); - describe('Cart Base feature', () => { + describe('Cart Import Export feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner - .runSchematicAsync('ng-add', cartBaseFeatureOptions, appTree) + .runSchematicAsync('ng-add', cartImportExportFeatureOptions, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(cartBaseFeatureModulePath); + const module = appTree.readContent(importExportFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -241,29 +246,29 @@ describe('Spartacus Cart schematics: ng-add', () => { appTree = await schematicRunner .runSchematicAsync( 'ng-add', - { ...cartBaseFeatureOptions, lazy: false }, + { ...cartImportExportFeatureOptions, lazy: false }, appTree ) .toPromise(); }); it('should import appropriate modules', async () => { - const module = appTree.readContent(cartBaseFeatureModulePath); + const module = appTree.readContent(importExportFeatureModulePath); expect(module).toMatchSnapshot(); }); }); }); - describe('Wish List feature', () => { + describe('Quick Order feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner - .runSchematicAsync('ng-add', wishListFeatureOptions, appTree) + .runSchematicAsync('ng-add', quickOrderFeatureOptions, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(wishListFeatureModulePath); + const module = appTree.readContent(quickOrderFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -285,32 +290,42 @@ describe('Spartacus Cart schematics: ng-add', () => { appTree = await schematicRunner .runSchematicAsync( 'ng-add', - { ...wishListFeatureOptions, lazy: false }, + { ...quickOrderFeatureOptions, lazy: false }, appTree ) .toPromise(); }); it('should import appropriate modules', async () => { - const module = appTree.readContent(wishListFeatureModulePath); + const module = appTree.readContent(quickOrderFeatureModulePath); expect(module).toMatchSnapshot(); }); }); }); - describe('Quick Order feature', () => { + describe('Saved Cart feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner - .runSchematicAsync('ng-add', quickOrderFeatureOptions, appTree) + .runSchematicAsync('ng-add', savedCartFeatureOptions, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(quickOrderFeatureModulePath); + const module = appTree.readContent(savedCartFeatureModulePath); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const baseCartFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(baseCartFeatureModule).toBeFalsy(); + + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -329,64 +344,70 @@ describe('Spartacus Cart schematics: ng-add', () => { appTree = await schematicRunner .runSchematicAsync( 'ng-add', - { ...quickOrderFeatureOptions, lazy: false }, + { ...savedCartFeatureOptions, lazy: false }, appTree ) .toPromise(); }); it('should import appropriate modules', async () => { - const module = appTree.readContent(quickOrderFeatureModulePath); + const module = appTree.readContent(savedCartFeatureModulePath); expect(module).toMatchSnapshot(); }); }); + }); - describe('Cart Import Export feature', () => { - describe('general setup', () => { - beforeEach(async () => { - appTree = await schematicRunner - .runSchematicAsync( - 'ng-add', - cartImportExportFeatureOptions, - appTree - ) - .toPromise(); - }); + describe('Wish List feature', () => { + describe('general setup', () => { + beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', wishListFeatureOptions, appTree) + .toPromise(); + }); - it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(importExportFeatureModulePath); - expect(module).toMatchSnapshot(); - }); + it('should add the feature using the lazy loading syntax', async () => { + const module = appTree.readContent(wishListFeatureModulePath); + expect(module).toMatchSnapshot(); + }); - describe('styling', () => { - it('should create a proper scss file', () => { - const scssContent = appTree.readContent(scssFilePath); - expect(scssContent).toMatchSnapshot(); - }); + it('should NOT install the required feature dependencies', async () => { + const baseCartFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(baseCartFeatureModule).toBeFalsy(); - it('should update angular.json', async () => { - const content = appTree.readContent('/angular.json'); - expect(content).toMatchSnapshot(); - }); - }); + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); }); - describe('eager loading', () => { - beforeEach(async () => { - appTree = await schematicRunner - .runSchematicAsync( - 'ng-add', - { ...cartImportExportFeatureOptions, lazy: false }, - appTree - ) - .toPromise(); + describe('styling', () => { + it('should create a proper scss file', () => { + const scssContent = appTree.readContent(scssFilePath); + expect(scssContent).toMatchSnapshot(); }); - it('should import appropriate modules', async () => { - const module = appTree.readContent(importExportFeatureModulePath); - expect(module).toMatchSnapshot(); + it('should update angular.json', async () => { + const content = appTree.readContent('/angular.json'); + expect(content).toMatchSnapshot(); }); }); }); + + describe('eager loading', () => { + beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...wishListFeatureOptions, lazy: false }, + appTree + ) + .toPromise(); + }); + + it('should import appropriate modules', async () => { + const module = appTree.readContent(wishListFeatureModulePath); + expect(module).toMatchSnapshot(); + }); + }); }); }); diff --git a/feature-libs/cart/schematics/add-library/index.ts b/feature-libs/cart/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/cart/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/cart/schematics/collection.json b/feature-libs/cart/schematics/collection.json index e4812c358d0..ae550ac2be9 100644 --- a/feature-libs/cart/schematics/collection.json +++ b/feature-libs/cart/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-cart/index#addCartFeatures", "description": "Add and configure Spartacus' Cart library", diff --git a/feature-libs/cart/schematics/constants.ts b/feature-libs/cart/schematics/constants.ts deleted file mode 100644 index 2bc790dfc83..00000000000 --- a/feature-libs/cart/schematics/constants.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { SPARTACUS_CART } from '@spartacus/schematics'; - -export const CART_FOLDER_NAME = 'cart'; -export const SCSS_FILE_NAME = 'cart.scss'; - -export const CART_BASE_FEATURE_MODULE_NAME = 'CartBase'; -export const CART_BASE_FEATURE_NAME_CONSTANT = 'CART_BASE_FEATURE'; -export const SPARTACUS_CART_BASE = `${SPARTACUS_CART}/base`; -export const SPARTACUS_CART_BASE_ROOT = `${SPARTACUS_CART_BASE}/root`; -export const SPARTACUS_CART_BASE_ASSETS = `${SPARTACUS_CART_BASE}/assets`; -export const CART_BASE_TRANSLATIONS = 'cartBaseTranslations'; -export const CART_BASE_TRANSLATION_CHUNKS_CONFIG = - 'cartBaseTranslationChunksConfig'; -export const MINI_CART_FEATURE_NAME_CONSTANT = 'MINI_CART_FEATURE'; -export const MINI_CART_ENTRY_POINT = `${SPARTACUS_CART_BASE}/components/mini-cart`; -export const ADD_TO_CART_FEATURE_NAME_CONSTANT = 'ADD_TO_CART_FEATURE'; -export const ADD_TO_CART_ENTRY_POINT = `${SPARTACUS_CART_BASE}/components/add-to-cart`; - -export const CART_WISHLIST_FEATURE_MODULE_NAME = 'WishList'; -export const CART_WISHLIST_FEATURE_NAME_CONSTANT = 'CART_WISH_LIST_FEATURE'; -export const SPARTACUS_CART_WISHLIST = `${SPARTACUS_CART}/wish-list`; -export const SPARTACUS_CART_WISHLIST_ROOT = `${SPARTACUS_CART_WISHLIST}/root`; -export const SPARTACUS_CART_WISHLIST_ASSETS = `${SPARTACUS_CART_WISHLIST}/assets`; -export const CART_WISHLIST_TRANSLATIONS = 'wishListTranslations'; -export const CART_WISHLIST_TRANSLATION_CHUNKS_CONFIG = - 'wishListTranslationChunksConfig'; -export const ADD_TO_WISHLIST_FEATURE_NAME_CONSTANT = 'ADD_TO_WISHLIST_FEATURE'; -export const ADD_TO_WISHLIST_ENTRY_POINT = `${SPARTACUS_CART_WISHLIST}/components/add-to-wishlist`; - -export const CART_SAVED_CART_MODULE_NAME = 'CartSavedCart'; -export const CART_SAVED_CART_FEATURE_NAME_CONSTANT = 'CART_SAVED_CART_FEATURE'; -export const SPARTACUS_SAVED_CART = `${SPARTACUS_CART}/saved-cart`; -export const SPARTACUS_SAVED_CART_ROOT = `${SPARTACUS_SAVED_CART}/root`; -export const SPARTACUS_SAVED_CART_ASSETS = `${SPARTACUS_SAVED_CART}/assets`; -export const SAVED_CART_TRANSLATIONS = 'savedCartTranslations'; -export const SAVED_CART_TRANSLATION_CHUNKS_CONFIG = - 'savedCartTranslationChunksConfig'; - -export const CART_QUICK_ORDER_MODULE_NAME = 'CartQuickOrder'; -export const CART_QUICK_ORDER_FEATURE_NAME_CONSTANT = - 'CART_QUICK_ORDER_FEATURE'; -export const SPARTACUS_QUICK_ORDER = `${SPARTACUS_CART}/quick-order`; -export const SPARTACUS_QUICK_ORDER_ROOT = `${SPARTACUS_QUICK_ORDER}/root`; -export const SPARTACUS_QUICK_ORDER_ASSETS = `${SPARTACUS_QUICK_ORDER}/assets`; -export const QUICK_ORDER_TRANSLATIONS = 'quickOrderTranslations'; -export const QUICK_ORDER_TRANSLATION_CHUNKS_CONFIG = - 'quickOrderTranslationChunksConfig'; - -export const CART_IMPORT_EXPORT_MODULE_NAME = 'CartImportExport'; -export const CART_IMPORT_EXPORT_FEATURE_NAME_CONSTANT = - 'CART_IMPORT_EXPORT_FEATURE'; -export const SPARTACUS_CART_IMPORT_EXPORT = `${SPARTACUS_CART}/import-export`; -export const SPARTACUS_CART_IMPORT_EXPORT_ROOT = `${SPARTACUS_CART_IMPORT_EXPORT}/root`; -export const SPARTACUS_CART_IMPORT_EXPORT_ASSETS = `${SPARTACUS_CART_IMPORT_EXPORT}/assets`; -export const CART_IMPORT_EXPORT_TRANSLATIONS = 'importExportTranslations'; -export const CART_IMPORT_EXPORT_TRANSLATION_CHUNKS_CONFIG = - 'importExportTranslationChunksConfig'; diff --git a/feature-libs/checkout/b2b/checkout-b2b.module.ts b/feature-libs/checkout/b2b/checkout-b2b.module.ts index 7cae41e825f..a54cc630f30 100644 --- a/feature-libs/checkout/b2b/checkout-b2b.module.ts +++ b/feature-libs/checkout/b2b/checkout-b2b.module.ts @@ -2,11 +2,9 @@ import { NgModule } from '@angular/core'; import { CheckoutB2BComponentsModule } from '@spartacus/checkout/b2b/components'; import { CheckoutB2BCoreModule } from '@spartacus/checkout/b2b/core'; import { CheckoutB2BOccModule } from '@spartacus/checkout/b2b/occ'; -import { CheckoutModule } from '@spartacus/checkout/base'; @NgModule({ imports: [ - CheckoutModule, CheckoutB2BComponentsModule, CheckoutB2BCoreModule, CheckoutB2BOccModule, diff --git a/feature-libs/checkout/b2b/root/checkout-b2b-root.module.ts b/feature-libs/checkout/b2b/root/checkout-b2b-root.module.ts index 06946e7553f..6a351bd8057 100644 --- a/feature-libs/checkout/b2b/root/checkout-b2b-root.module.ts +++ b/feature-libs/checkout/b2b/root/checkout-b2b-root.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { - CheckoutRootModule, CHECKOUT_BASE_CMS_COMPONENTS, CHECKOUT_FEATURE, } from '@spartacus/checkout/base/root'; @@ -35,7 +34,7 @@ export function defaultCheckoutComponentsConfig() { } @NgModule({ - imports: [CheckoutRootModule, CheckoutB2BEventModule], + imports: [CheckoutB2BEventModule], providers: [ provideDefaultConfig(defaultB2BCheckoutConfig), provideDefaultConfig(defaultCheckoutB2BRoutingConfig), diff --git a/feature-libs/checkout/scheduled-replenishment/checkout-scheduled-replenishment.module.ts b/feature-libs/checkout/scheduled-replenishment/checkout-scheduled-replenishment.module.ts index 6aefeb67f0b..4c5cecde270 100644 --- a/feature-libs/checkout/scheduled-replenishment/checkout-scheduled-replenishment.module.ts +++ b/feature-libs/checkout/scheduled-replenishment/checkout-scheduled-replenishment.module.ts @@ -1,8 +1,7 @@ import { NgModule } from '@angular/core'; -import { CheckoutB2BModule } from '@spartacus/checkout/b2b'; import { CheckoutScheduledReplenishmentComponentsModule } from '@spartacus/checkout/scheduled-replenishment/components'; @NgModule({ - imports: [CheckoutB2BModule, CheckoutScheduledReplenishmentComponentsModule], + imports: [CheckoutScheduledReplenishmentComponentsModule], }) export class CheckoutScheduledReplenishmentModule {} diff --git a/feature-libs/checkout/scheduled-replenishment/root/checkout-scheduled-replenishment-root.module.ts b/feature-libs/checkout/scheduled-replenishment/root/checkout-scheduled-replenishment-root.module.ts index 9086f40bfbd..24e9a4f6341 100644 --- a/feature-libs/checkout/scheduled-replenishment/root/checkout-scheduled-replenishment-root.module.ts +++ b/feature-libs/checkout/scheduled-replenishment/root/checkout-scheduled-replenishment-root.module.ts @@ -1,8 +1,5 @@ import { NgModule } from '@angular/core'; -import { - CheckoutB2BRootModule, - CHECKOUT_B2B_CMS_COMPONENTS, -} from '@spartacus/checkout/b2b/root'; +import { CHECKOUT_B2B_CMS_COMPONENTS } from '@spartacus/checkout/b2b/root'; import { CHECKOUT_FEATURE } from '@spartacus/checkout/base/root'; import { CmsConfig, provideDefaultConfigFactory } from '@spartacus/core'; import { CheckoutScheduledReplenishmentEventModule } from './events/checkout-scheduled-replenishment-event.module'; @@ -28,7 +25,7 @@ export function defaultCheckoutComponentsConfig() { } @NgModule({ - imports: [CheckoutB2BRootModule, CheckoutScheduledReplenishmentEventModule], + imports: [CheckoutScheduledReplenishmentEventModule], providers: [provideDefaultConfigFactory(defaultCheckoutComponentsConfig)], }) export class CheckoutScheduledReplenishmentRootModule {} diff --git a/feature-libs/checkout/schematics/add-checkout/__snapshots__/index_spec.ts.snap b/feature-libs/checkout/schematics/add-checkout/__snapshots__/index_spec.ts.snap index 1bf5ad2fce7..9880a578a79 100644 --- a/feature-libs/checkout/schematics/add-checkout/__snapshots__/index_spec.ts.snap +++ b/feature-libs/checkout/schematics/add-checkout/__snapshots__/index_spec.ts.snap @@ -5,25 +5,29 @@ exports[`Spartacus Checkout schematics: ng-add Checkout feature b2b eager loadin import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule } from \\"@spartacus/checkout/base/root\\"; import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; @NgModule({ declarations: [], imports: [ + CheckoutRootModule, + CheckoutModule, CheckoutB2BRootModule, CheckoutB2BModule ], providers: [provideConfig({ i18n: { - resources: checkoutB2BTranslations, - chunks: checkoutB2BTranslationChunksConfig, + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, }, }), provideConfig({ i18n: { - resources: checkoutTranslations, - chunks: checkoutTranslationChunksConfig, + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, }, }) ] @@ -75,32 +79,33 @@ exports[`Spartacus Checkout schematics: ng-add Checkout feature b2b general setu import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; -import { CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; @NgModule({ declarations: [], imports: [ + CheckoutRootModule, CheckoutB2BRootModule ], providers: [provideConfig({ featureModules: { [CHECKOUT_FEATURE]: { module: () => - import('@spartacus/checkout/b2b').then((m) => m.CheckoutB2BModule), + import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), }, } }), provideConfig({ i18n: { - resources: checkoutB2BTranslations, - chunks: checkoutB2BTranslationChunksConfig, + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, }, }), provideConfig({ i18n: { - resources: checkoutTranslations, - chunks: checkoutTranslationChunksConfig, + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, }, }) ] @@ -109,6 +114,22 @@ export class CheckoutFeatureModule { } " `; +exports[`Spartacus Checkout schematics: ng-add Checkout feature b2b general setup should add the feature using the lazy loading syntax 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; + +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule + ] +}) +export class CheckoutWrapperModule { } +" +`; + exports[`Spartacus Checkout schematics: ng-add Checkout feature b2b general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/checkout\\";"`; exports[`Spartacus Checkout schematics: ng-add Checkout feature b2b general setup styling should update angular.json 1`] = ` @@ -422,8 +443,12 @@ exports[`Spartacus Checkout schematics: ng-add Checkout feature base general set exports[`Spartacus Checkout schematics: ng-add Checkout feature scheduled replenishment eager loading should import appropriate modules 1`] = ` "import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; +import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule } from \\"@spartacus/checkout/base/root\\"; import { CheckoutScheduledReplenishmentModule } from \\"@spartacus/checkout/scheduled-replenishment\\"; import { checkoutScheduledReplenishmentTranslationChunksConfig, checkoutScheduledReplenishmentTranslations } from \\"@spartacus/checkout/scheduled-replenishment/assets\\"; import { CheckoutScheduledReplenishmentRootModule } from \\"@spartacus/checkout/scheduled-replenishment/root\\"; @@ -432,16 +457,14 @@ import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; @NgModule({ declarations: [], imports: [ + CheckoutRootModule, + CheckoutModule, + CheckoutB2BRootModule, + CheckoutB2BModule, CheckoutScheduledReplenishmentRootModule, CheckoutScheduledReplenishmentModule ], providers: [provideConfig({ - i18n: { - resources: checkoutScheduledReplenishmentTranslations, - chunks: checkoutScheduledReplenishmentTranslationChunksConfig, - }, - }), - provideConfig({ i18n: { resources: checkoutTranslations, chunks: checkoutTranslationChunksConfig, @@ -452,6 +475,12 @@ import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; resources: checkoutB2BTranslations, chunks: checkoutB2BTranslationChunksConfig, }, + }), + provideConfig({ + i18n: { + resources: checkoutScheduledReplenishmentTranslations, + chunks: checkoutScheduledReplenishmentTranslationChunksConfig, + }, }) ] }) @@ -500,8 +529,9 @@ export class SpartacusConfigurationModule { } exports[`Spartacus Checkout schematics: ng-add Checkout feature scheduled replenishment general setup should add the feature using the lazy loading syntax 1`] = ` "import { NgModule } from '@angular/core'; import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; +import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; -import { CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; import { checkoutScheduledReplenishmentTranslationChunksConfig, checkoutScheduledReplenishmentTranslations } from \\"@spartacus/checkout/scheduled-replenishment/assets\\"; import { CheckoutScheduledReplenishmentRootModule } from \\"@spartacus/checkout/scheduled-replenishment/root\\"; import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; @@ -509,22 +539,18 @@ import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; @NgModule({ declarations: [], imports: [ + CheckoutRootModule, + CheckoutB2BRootModule, CheckoutScheduledReplenishmentRootModule ], providers: [provideConfig({ featureModules: { [CHECKOUT_FEATURE]: { module: () => - import('@spartacus/checkout/scheduled-replenishment').then((m) => m.CheckoutScheduledReplenishmentModule), + import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), }, } }), - provideConfig({ - i18n: { - resources: checkoutScheduledReplenishmentTranslations, - chunks: checkoutScheduledReplenishmentTranslationChunksConfig, - }, - }), provideConfig({ i18n: { resources: checkoutTranslations, @@ -536,6 +562,12 @@ import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; resources: checkoutB2BTranslations, chunks: checkoutB2BTranslationChunksConfig, }, + }), + provideConfig({ + i18n: { + resources: checkoutScheduledReplenishmentTranslations, + chunks: checkoutScheduledReplenishmentTranslationChunksConfig, + }, }) ] }) @@ -543,6 +575,24 @@ export class CheckoutFeatureModule { } " `; +exports[`Spartacus Checkout schematics: ng-add Checkout feature scheduled replenishment general setup should add the feature using the lazy loading syntax 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { CheckoutScheduledReplenishmentModule } from \\"@spartacus/checkout/scheduled-replenishment\\"; + +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule, + CheckoutScheduledReplenishmentModule + ] +}) +export class CheckoutWrapperModule { } +" +`; + exports[`Spartacus Checkout schematics: ng-add Checkout feature scheduled replenishment general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/checkout\\";"`; exports[`Spartacus Checkout schematics: ng-add Checkout feature scheduled replenishment general setup styling should update angular.json 1`] = ` diff --git a/feature-libs/checkout/schematics/add-checkout/index.ts b/feature-libs/checkout/schematics/add-checkout/index.ts index 146d4b05940..54a9323e6f5 100644 --- a/feature-libs/checkout/schematics/add-checkout/index.ts +++ b/feature-libs/checkout/schematics/add-checkout/index.ts @@ -1,204 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addFeatureTranslations, - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CHECKOUT_B2B_MODULE, - CHECKOUT_B2B_ROOT_MODULE, - CHECKOUT_BASE_FEATURE_NAME_CONSTANT, - CHECKOUT_BASE_MODULE, - CHECKOUT_BASE_MODULE_NAME, - CHECKOUT_BASE_ROOT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, - CLI_CART_BASE_FEATURE, - CLI_CHECKOUT_B2B_FEATURE, - CLI_CHECKOUT_BASE_FEATURE, - CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, - CLI_ORDER_FEATURE, - configureB2bFeatures, - FeatureConfig, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusCheckoutOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_CART, - SPARTACUS_CHECKOUT, - SPARTACUS_CHECKOUT_B2B, - SPARTACUS_CHECKOUT_B2B_ASSETS, - SPARTACUS_CHECKOUT_B2B_ROOT, - SPARTACUS_CHECKOUT_BASE, - SPARTACUS_CHECKOUT_BASE_ASSETS, - SPARTACUS_CHECKOUT_BASE_ROOT, - SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT, - SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS, - SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT, - SPARTACUS_ORDER, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - CHECKOUT_B2B_TRANSLATIONS, - CHECKOUT_B2B_TRANSLATION_CHUNKS_CONFIG, - CHECKOUT_BASE_TRANSLATIONS, - CHECKOUT_BASE_TRANSLATION_CHUNKS_CONFIG, - CHECKOUT_FOLDER_NAME, - CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATIONS, - CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATION_CHUNKS_CONFIG, - SCSS_FILE_NAME, -} from '../constants'; - -const checkoutBaseFeatureConfig: FeatureConfig = { - folderName: CHECKOUT_FOLDER_NAME, - moduleName: CHECKOUT_BASE_MODULE_NAME, - featureModule: { - name: CHECKOUT_BASE_MODULE, - importPath: SPARTACUS_CHECKOUT_BASE, - }, - rootModule: { - name: CHECKOUT_BASE_ROOT_MODULE, - importPath: SPARTACUS_CHECKOUT_BASE_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CHECKOUT_BASE_ROOT, - namedImports: [CHECKOUT_BASE_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: CHECKOUT_BASE_TRANSLATIONS, - chunks: CHECKOUT_BASE_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CHECKOUT_BASE_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CHECKOUT, - }, - dependencyManagement: { - featureName: CLI_CHECKOUT_BASE_FEATURE, - featureDependencies: { - [SPARTACUS_CART]: [CLI_CART_BASE_FEATURE], - [SPARTACUS_ORDER]: [CLI_ORDER_FEATURE], - }, - }, - recreate: true, -}; - -const checkoutB2bFeatureConfig: FeatureConfig = { - folderName: CHECKOUT_FOLDER_NAME, - moduleName: CHECKOUT_BASE_MODULE_NAME, - featureModule: { - name: CHECKOUT_B2B_MODULE, - importPath: SPARTACUS_CHECKOUT_B2B, - }, - rootModule: { - name: CHECKOUT_B2B_ROOT_MODULE, - importPath: SPARTACUS_CHECKOUT_B2B_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CHECKOUT_BASE_ROOT, - namedImports: [CHECKOUT_BASE_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: CHECKOUT_B2B_TRANSLATIONS, - chunks: CHECKOUT_B2B_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CHECKOUT_B2B_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CHECKOUT, - }, - dependencyManagement: { - featureName: CLI_CHECKOUT_B2B_FEATURE, - featureDependencies: { - [SPARTACUS_CART]: [CLI_CART_BASE_FEATURE], - [SPARTACUS_ORDER]: [CLI_ORDER_FEATURE], - }, - }, - recreate: true, -}; - -const checkoutScheduleReplenishmentFeatureConfig: FeatureConfig = { - folderName: CHECKOUT_FOLDER_NAME, - moduleName: CHECKOUT_BASE_MODULE_NAME, - featureModule: { - name: CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, - importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT, - }, - rootModule: { - name: CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, - importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CHECKOUT_BASE_ROOT, - namedImports: [CHECKOUT_BASE_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATIONS, - chunks: CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_CHECKOUT, - }, - dependencyManagement: { - featureName: CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, - featureDependencies: { - [SPARTACUS_CART]: [CLI_CART_BASE_FEATURE], - [SPARTACUS_ORDER]: [CLI_ORDER_FEATURE], - }, - }, - recreate: true, -}; export function addCheckoutFeatures(options: SpartacusCheckoutOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); - return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); - determineCheckoutFeatures(options, packageJson), - ]); - }; -} - -function determineCheckoutFeatures( - options: SpartacusCheckoutOptions, - packageJson: any -): Rule { - if ( - shouldAddFeature( - CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, - options.features - ) - ) { return chain([ - addLibraryFeature(options, checkoutScheduleReplenishmentFeatureConfig), - addFeatureTranslations(options, checkoutBaseFeatureConfig), - addFeatureTranslations(options, checkoutB2bFeatureConfig), - - configureB2bFeatures(options, packageJson), - ]); - } + analyzeApplication(options, features), - if (shouldAddFeature(CLI_CHECKOUT_B2B_FEATURE, options.features)) { - return chain([ - addLibraryFeature(options, checkoutB2bFeatureConfig), - addFeatureTranslations(options, checkoutBaseFeatureConfig), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - configureB2bFeatures(options, packageJson), + finalizeInstallation(options, features), ]); - } - - if (shouldAddFeature(CLI_CHECKOUT_BASE_FEATURE, options.features)) { - return addLibraryFeature(options, checkoutBaseFeatureConfig); - } - - return noop(); + }; } diff --git a/feature-libs/checkout/schematics/add-checkout/index_spec.ts b/feature-libs/checkout/schematics/add-checkout/index_spec.ts index 6f25d225ea2..9790aa8b3a8 100644 --- a/feature-libs/checkout/schematics/add-checkout/index_spec.ts +++ b/feature-libs/checkout/schematics/add-checkout/index_spec.ts @@ -10,24 +10,31 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_CHECKOUT_B2B_FEATURE, - CLI_CHECKOUT_BASE_FEATURE, - CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, + cartBaseFeatureModulePath, + checkoutFeatureModulePath, + checkoutWrapperModulePath, + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, LibraryOptions as SpartacusCheckoutOptions, + orderFeatureModulePath, SpartacusOptions, + SPARTACUS_CHECKOUT, SPARTACUS_CONFIGURATION_MODULE, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/checkout/checkout-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/checkout.scss'; describe('Spartacus Checkout schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_CHECKOUT, + collectionPath + ); let appTree: UnitTestTree; @@ -60,18 +67,18 @@ describe('Spartacus Checkout schematics: ng-add', () => { const checkoutBaseFeatureOptions: SpartacusCheckoutOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CHECKOUT_BASE_FEATURE], + features: [CHECKOUT_BASE_FEATURE_NAME], }; const checkoutB2BFeatureOptions: SpartacusCheckoutOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CHECKOUT_B2B_FEATURE], + features: [CHECKOUT_B2B_FEATURE_NAME], }; const checkoutScheduledReplenishmentFeatureOptions: SpartacusCheckoutOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE], + features: [CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME], }; beforeEach(async () => { @@ -117,7 +124,7 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(checkoutFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -156,8 +163,25 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + expect(appTree.readContent(checkoutWrapperModulePath)).toBeFalsy(); + }); + + it('should NOT install the required feature dependencies', async () => { + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -185,8 +209,10 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + expect(appTree.readContent(checkoutWrapperModulePath)).toBeFalsy(); }); }); }); @@ -194,14 +220,35 @@ describe('Spartacus Checkout schematics: ng-add', () => { describe('b2b', () => { describe('general setup', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', checkoutBaseFeatureOptions, appTree) + .toPromise(); appTree = await schematicRunner .runSchematicAsync('ng-add', checkoutB2BFeatureOptions, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + const wrapperModule = appTree.readContent(checkoutWrapperModulePath); + expect(wrapperModule).toMatchSnapshot(); + }); + + it('should NOT install the required feature dependencies', async () => { + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -228,6 +275,13 @@ describe('Spartacus Checkout schematics: ng-add', () => { describe('eager loading', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...checkoutBaseFeatureOptions, lazy: false }, + appTree + ) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', @@ -238,8 +292,10 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + expect(appTree.readContent(checkoutWrapperModulePath)).toBeFalsy(); }); }); }); @@ -247,6 +303,12 @@ describe('Spartacus Checkout schematics: ng-add', () => { describe('scheduled replenishment', () => { describe('general setup', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', checkoutBaseFeatureOptions, appTree) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync('ng-add', checkoutB2BFeatureOptions, appTree) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', @@ -257,8 +319,26 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + const wrapperModule = appTree.readContent(checkoutWrapperModulePath); + expect(wrapperModule).toMatchSnapshot(); + }); + + it('should NOT install the required feature dependencies', async () => { + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -285,6 +365,20 @@ describe('Spartacus Checkout schematics: ng-add', () => { describe('eager loading', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...checkoutBaseFeatureOptions, lazy: false }, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...checkoutB2BFeatureOptions, lazy: false }, + appTree + ) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', @@ -295,8 +389,10 @@ describe('Spartacus Checkout schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(checkoutFeatureModulePath); expect(module).toMatchSnapshot(); + + expect(appTree.readContent(checkoutWrapperModulePath)).toBeFalsy(); }); }); }); diff --git a/feature-libs/checkout/schematics/add-checkout/schema.json b/feature-libs/checkout/schematics/add-checkout/schema.json index 9a2d224c4fc..26f97410b01 100644 --- a/feature-libs/checkout/schematics/add-checkout/schema.json +++ b/feature-libs/checkout/schematics/add-checkout/schema.json @@ -43,11 +43,11 @@ }, { "value": "Checkout-B2B", - "label": "Checkout (b2b feature, includes Base Checkout)" + "label": "Checkout B2B (b2b feature, requires Base Checkout)" }, { "value": "Checkout-Scheduled-Replenishment", - "label": "Checkout Scheduled Replenishment (b2b feature, includes Base and B2B Checkout)" + "label": "Checkout Scheduled Replenishment (b2b feature, requires Base and B2B Checkout)" } ] } diff --git a/feature-libs/checkout/schematics/add-library/index.ts b/feature-libs/checkout/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/checkout/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/checkout/schematics/collection.json b/feature-libs/checkout/schematics/collection.json index 43824be9075..fe693690d8b 100644 --- a/feature-libs/checkout/schematics/collection.json +++ b/feature-libs/checkout/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-checkout/index#addCheckoutFeatures", "description": "Add and configure Spartacus' Checkout library", diff --git a/feature-libs/checkout/schematics/constants.ts b/feature-libs/checkout/schematics/constants.ts deleted file mode 100644 index 2fbb84c6572..00000000000 --- a/feature-libs/checkout/schematics/constants.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const CHECKOUT_FOLDER_NAME = 'checkout'; -export const SCSS_FILE_NAME = 'checkout.scss'; - -export const CHECKOUT_BASE_TRANSLATIONS = 'checkoutTranslations'; -export const CHECKOUT_BASE_TRANSLATION_CHUNKS_CONFIG = - 'checkoutTranslationChunksConfig'; - -export const CHECKOUT_B2B_TRANSLATIONS = 'checkoutB2BTranslations'; -export const CHECKOUT_B2B_TRANSLATION_CHUNKS_CONFIG = - 'checkoutB2BTranslationChunksConfig'; - -export const CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATIONS = - 'checkoutScheduledReplenishmentTranslations'; -export const CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATION_CHUNKS_CONFIG = - 'checkoutScheduledReplenishmentTranslationChunksConfig'; diff --git a/feature-libs/order/schematics/add-library/index.ts b/feature-libs/order/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/order/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/order/schematics/add-order/index.ts b/feature-libs/order/schematics/add-order/index.ts index 71a35680f0c..7be710fd413 100644 --- a/feature-libs/order/schematics/add-order/index.ts +++ b/feature-libs/order/schematics/add-order/index.ts @@ -1,84 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_CART_BASE_FEATURE, - CLI_ORDER_FEATURE, - CLI_USER_ACCOUNT_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusOrderOptions, - ORDER_MODULE, - ORDER_ROOT_MODULE, readPackageJson, - shouldAddFeature, - SPARTACUS_CART, - SPARTACUS_ORDER, - SPARTACUS_USER, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - ORDER_FEATURE_NAME_CONSTANT, - ORDER_FOLDER_NAME, - ORDER_MODULE_NAME, - ORDER_TRANSLATIONS, - ORDER_TRANSLATION_CHUNKS_CONFIG, - SCSS_FILE_NAME, - SPARTACUS_ORDER_ASSETS, - SPARTACUS_ORDER_ROOT, -} from '../constants'; export function addOrderFeatures(options: SpartacusOrderOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_ORDER_FEATURE, options.features) - ? addOrderFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addOrderFeature(options: SpartacusOrderOptions): Rule { - return addLibraryFeature(options, { - folderName: ORDER_FOLDER_NAME, - moduleName: ORDER_MODULE_NAME, - featureModule: { - name: ORDER_MODULE, - importPath: SPARTACUS_ORDER, - }, - rootModule: { - name: ORDER_ROOT_MODULE, - importPath: SPARTACUS_ORDER_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_ORDER_ROOT, - namedImports: [ORDER_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: ORDER_TRANSLATIONS, - chunks: ORDER_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_ORDER_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_ORDER, - }, - dependencyManagement: { - featureName: CLI_ORDER_FEATURE, - featureDependencies: { - [SPARTACUS_CART]: [CLI_CART_BASE_FEATURE], - [SPARTACUS_USER]: [CLI_USER_ACCOUNT_FEATURE], - }, - }, - }); -} diff --git a/feature-libs/order/schematics/add-order/index_spec.ts b/feature-libs/order/schematics/add-order/index_spec.ts index 2bde89c6d95..c5db3c32aeb 100644 --- a/feature-libs/order/schematics/add-order/index_spec.ts +++ b/feature-libs/order/schematics/add-order/index_spec.ts @@ -10,21 +10,26 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_ORDER_FEATURE, + cartBaseFeatureModulePath, LibraryOptions as SpartacusOrderOptions, + orderFeatureModulePath, + ORDER_FEATURE_NAME, SpartacusOptions, + SPARTACUS_ORDER, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/order/order-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/order.scss'; describe('Spartacus Order schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_ORDER, + collectionPath + ); let appTree: UnitTestTree; @@ -57,7 +62,7 @@ describe('Spartacus Order schematics: ng-add', () => { const orderFeatureOptions: SpartacusOrderOptions = { ...libraryNoFeaturesOptions, - features: [CLI_ORDER_FEATURE], + features: [ORDER_FEATURE_NAME], }; beforeEach(async () => { @@ -103,7 +108,7 @@ describe('Spartacus Order schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(orderFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -141,10 +146,20 @@ describe('Spartacus Order schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(orderFeatureModulePath); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const baseCartFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(baseCartFeatureModule).toBeFalsy(); + + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -170,7 +185,7 @@ describe('Spartacus Order schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(orderFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/order/schematics/collection.json b/feature-libs/order/schematics/collection.json index 27aa3693864..d20283bd0d3 100644 --- a/feature-libs/order/schematics/collection.json +++ b/feature-libs/order/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-order/index#addOrderFeatures", "description": "Add and configure Spartacus' Order library", diff --git a/feature-libs/order/schematics/constants.ts b/feature-libs/order/schematics/constants.ts deleted file mode 100644 index d0a077df386..00000000000 --- a/feature-libs/order/schematics/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { SPARTACUS_ORDER } from '@spartacus/schematics'; - -export const ORDER_FOLDER_NAME = 'order'; -export const ORDER_MODULE_NAME = 'order'; -export const SCSS_FILE_NAME = 'order.scss'; - -export const ORDER_FEATURE_NAME_CONSTANT = 'ORDER_FEATURE'; -export const SPARTACUS_ORDER_ROOT = `${SPARTACUS_ORDER}/root`; -export const SPARTACUS_ORDER_ASSETS = `${SPARTACUS_ORDER}/assets`; -export const ORDER_TRANSLATIONS = 'orderTranslations'; -export const ORDER_TRANSLATION_CHUNKS_CONFIG = 'orderTranslationChunksConfig'; diff --git a/feature-libs/organization/schematics/add-library/index.ts b/feature-libs/organization/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/organization/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/organization/schematics/add-organization/index.ts b/feature-libs/organization/schematics/add-organization/index.ts index 06e90c47774..58271e86a2c 100644 --- a/feature-libs/organization/schematics/add-organization/index.ts +++ b/feature-libs/organization/schematics/add-organization/index.ts @@ -1,45 +1,20 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - ADMINISTRATION_MODULE, - ADMINISTRATION_ROOT_MODULE, - CLI_ORGANIZATION_ADMINISTRATION_FEATURE, - CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE, - configureB2bFeatures, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusOrganizationOptions, - ORDER_APPROVAL_MODULE, - ORDER_APPROVAL_ROOT_MODULE, readPackageJson, - shouldAddFeature, - SPARTACUS_ORGANIZATION, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - ORDER_APPROVAL_TRANSLATIONS, - ORDER_APPROVAL_TRANSLATION_CHUNKS_CONFIG, - ORGANIZATION_ADMINISTRATION_FEATURE_NAME_CONSTANT, - ORGANIZATION_ADMINISTRATION_MODULE_NAME, - ORGANIZATION_FOLDER_NAME, - ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME_CONSTANT, - ORGANIZATION_ORDER_APPROVAL_MODULE_NAME, - ORGANIZATION_TRANSLATIONS, - ORGANIZATION_TRANSLATION_CHUNKS_CONFIG, - SCSS_FILE_NAME, - SPARTACUS_ADMINISTRATION, - SPARTACUS_ADMINISTRATION_ASSETS, - SPARTACUS_ADMINISTRATION_ROOT, - SPARTACUS_ORDER_APPROVAL, - SPARTACUS_ORDER_APPROVAL_ASSETS, - SPARTACUS_ORDER_APPROVAL_ROOT, -} from '../constants'; export function addSpartacusOrganization( options: SpartacusOrganizationOptions @@ -48,84 +23,17 @@ export function addSpartacusOrganization( const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + analyzeApplication(options, features), - shouldAddFeature( - CLI_ORGANIZATION_ADMINISTRATION_FEATURE, - options.features - ) - ? chain([ - addAdministrationFeature(options), - configureB2bFeatures(options, packageJson), - ]) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature( - CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE, - options.features - ) - ? chain([ - addOrderApprovalsFeature(options), - configureB2bFeatures(options, packageJson), - ]) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addAdministrationFeature(options: SpartacusOrganizationOptions): Rule { - return addLibraryFeature(options, { - folderName: ORGANIZATION_FOLDER_NAME, - moduleName: ORGANIZATION_ADMINISTRATION_MODULE_NAME, - featureModule: { - name: ADMINISTRATION_MODULE, - importPath: SPARTACUS_ADMINISTRATION, - }, - rootModule: { - name: ADMINISTRATION_ROOT_MODULE, - importPath: SPARTACUS_ADMINISTRATION_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_ADMINISTRATION_ROOT, - namedImports: [ORGANIZATION_ADMINISTRATION_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: ORGANIZATION_TRANSLATIONS, - chunks: ORGANIZATION_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_ADMINISTRATION_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_ORGANIZATION, - }, - }); -} - -function addOrderApprovalsFeature(options: SpartacusOrganizationOptions): Rule { - return addLibraryFeature(options, { - folderName: ORGANIZATION_FOLDER_NAME, - moduleName: ORGANIZATION_ORDER_APPROVAL_MODULE_NAME, - featureModule: { - name: ORDER_APPROVAL_MODULE, - importPath: SPARTACUS_ORDER_APPROVAL, - }, - rootModule: { - name: ORDER_APPROVAL_ROOT_MODULE, - importPath: SPARTACUS_ORDER_APPROVAL_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_ORDER_APPROVAL_ROOT, - namedImports: [ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: ORDER_APPROVAL_TRANSLATIONS, - chunks: ORDER_APPROVAL_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_ORDER_APPROVAL_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_ORGANIZATION, - }, - }); -} diff --git a/feature-libs/organization/schematics/add-organization/index_spec.ts b/feature-libs/organization/schematics/add-organization/index_spec.ts index fe1ef709d4a..b97d5e33de4 100644 --- a/feature-libs/organization/schematics/add-organization/index_spec.ts +++ b/feature-libs/organization/schematics/add-organization/index_spec.ts @@ -10,25 +10,29 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_ORGANIZATION_ADMINISTRATION_FEATURE, - CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE, LibraryOptions as SpartacusOrganizationOptions, + orderFeatureModulePath, + organizationAdministrationFeatureModulePath, + organizationOrderApprovalFeatureModulePath, + ORGANIZATION_ADMINISTRATION_FEATURE_NAME, + ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME, SpartacusOptions, SPARTACUS_CONFIGURATION_MODULE, + SPARTACUS_ORGANIZATION, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const administrationFeatureModulePath = - 'src/app/spartacus/features/organization/organization-administration-feature.module.ts'; -const orderApprovalFeatureModulePath = - 'src/app/spartacus/features/organization/organization-order-approval-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/organization.scss'; describe('Spartacus Organization schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_ORGANIZATION, + collectionPath + ); let appTree: UnitTestTree; @@ -61,12 +65,12 @@ describe('Spartacus Organization schematics: ng-add', () => { const administrationFeatureOptions: SpartacusOrganizationOptions = { ...libraryNoFeaturesOptions, - features: [CLI_ORGANIZATION_ADMINISTRATION_FEATURE], + features: [ORGANIZATION_ADMINISTRATION_FEATURE_NAME], }; const orderApprovalFeatureOptions: SpartacusOrganizationOptions = { ...libraryNoFeaturesOptions, - features: [CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE], + features: [ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME], }; beforeEach(async () => { @@ -108,8 +112,12 @@ describe('Spartacus Organization schematics: ng-add', () => { }); it('should not install administration nor order-approval features', () => { - expect(appTree.exists(administrationFeatureModulePath)).toBeFalsy(); - expect(appTree.exists(orderApprovalFeatureModulePath)).toBeFalsy(); + expect( + appTree.exists(organizationAdministrationFeatureModulePath) + ).toBeFalsy(); + expect( + appTree.exists(organizationOrderApprovalFeatureModulePath) + ).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -147,10 +155,17 @@ describe('Spartacus Organization schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(administrationFeatureModulePath); + const module = appTree.readContent( + organizationAdministrationFeatureModulePath + ); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -185,7 +200,9 @@ describe('Spartacus Organization schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(administrationFeatureModulePath); + const module = appTree.readContent( + organizationAdministrationFeatureModulePath + ); expect(module).toMatchSnapshot(); }); }); @@ -200,10 +217,20 @@ describe('Spartacus Organization schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(orderApprovalFeatureModulePath); + const module = appTree.readContent( + organizationOrderApprovalFeatureModulePath + ); expect(module).toMatchSnapshot(); }); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent(orderFeatureModulePath); + expect(orderFeatureModule).toBeFalsy(); + }); + describe('styling', () => { it('should create a proper scss file', () => { const scssContent = appTree.readContent(scssFilePath); @@ -238,7 +265,9 @@ describe('Spartacus Organization schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(orderApprovalFeatureModulePath); + const module = appTree.readContent( + organizationOrderApprovalFeatureModulePath + ); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/organization/schematics/collection.json b/feature-libs/organization/schematics/collection.json index 643737db5d0..28adfb361d8 100644 --- a/feature-libs/organization/schematics/collection.json +++ b/feature-libs/organization/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-organization/index#addSpartacusOrganization", "description": "Add and configure Spartacus' Organization library", diff --git a/feature-libs/organization/schematics/constants.ts b/feature-libs/organization/schematics/constants.ts deleted file mode 100644 index 78551842b77..00000000000 --- a/feature-libs/organization/schematics/constants.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { SPARTACUS_ORGANIZATION } from '@spartacus/schematics'; - -export const ORGANIZATION_FOLDER_NAME = 'organization'; -export const SCSS_FILE_NAME = 'organization.scss'; - -export const ORGANIZATION_ADMINISTRATION_MODULE_NAME = - 'OrganizationAdministration'; -export const ORGANIZATION_ADMINISTRATION_FEATURE_NAME_CONSTANT = - 'ORGANIZATION_ADMINISTRATION_FEATURE'; -export const SPARTACUS_ADMINISTRATION = `${SPARTACUS_ORGANIZATION}/administration`; -export const SPARTACUS_ADMINISTRATION_ROOT = `${SPARTACUS_ADMINISTRATION}/root`; -export const SPARTACUS_ADMINISTRATION_ASSETS = `${SPARTACUS_ADMINISTRATION}/assets`; -export const ORGANIZATION_TRANSLATIONS = 'organizationTranslations'; -export const ORGANIZATION_TRANSLATION_CHUNKS_CONFIG = - 'organizationTranslationChunksConfig'; - -export const ORGANIZATION_ORDER_APPROVAL_MODULE_NAME = - 'OrganizationOrderApproval'; -export const ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME_CONSTANT = - 'ORGANIZATION_ORDER_APPROVAL_FEATURE'; -export const SPARTACUS_ORDER_APPROVAL = `${SPARTACUS_ORGANIZATION}/order-approval`; -export const SPARTACUS_ORDER_APPROVAL_ROOT = `${SPARTACUS_ORDER_APPROVAL}/root`; -export const SPARTACUS_ORDER_APPROVAL_ASSETS = `${SPARTACUS_ORDER_APPROVAL}/assets`; -export const ORDER_APPROVAL_TRANSLATIONS = 'orderApprovalTranslations'; -export const ORDER_APPROVAL_TRANSLATION_CHUNKS_CONFIG = - 'orderApprovalTranslationChunksConfig'; diff --git a/feature-libs/product-configurator/rulebased/cpq/rulebased-cpq-configurator.module.ts b/feature-libs/product-configurator/rulebased/cpq/rulebased-cpq-configurator.module.ts index cd312c9ff15..4222edc4858 100644 --- a/feature-libs/product-configurator/rulebased/cpq/rulebased-cpq-configurator.module.ts +++ b/feature-libs/product-configurator/rulebased/cpq/rulebased-cpq-configurator.module.ts @@ -1,17 +1,12 @@ import { NgModule } from '@angular/core'; -import { RulebasedConfiguratorModule } from '@spartacus/product-configurator/rulebased'; import { CpqConfiguratorOccModule } from './occ/cpq-configurator-occ.module'; import { CpqConfiguratorRestModule } from './rest/cpq-configurator-rest.module'; /** - * Exposes the rulebased configurator including the CPQ flavor, which connects to CPQ directly via + * Exposes the CPQ flavor of rulebase configurator, which connects to CPQ directly via * REST APIs and to commerce via OCC */ @NgModule({ - imports: [ - RulebasedConfiguratorModule, - CpqConfiguratorOccModule, - CpqConfiguratorRestModule, - ], + imports: [CpqConfiguratorOccModule, CpqConfiguratorRestModule], }) export class RulebasedCpqConfiguratorModule {} diff --git a/feature-libs/product-configurator/schematics/add-library/index.ts b/feature-libs/product-configurator/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/product-configurator/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/product-configurator/schematics/add-product-configurator/__snapshots__/index_spec.ts.snap b/feature-libs/product-configurator/schematics/add-product-configurator/__snapshots__/index_spec.ts.snap index 8b9108ad285..bf4e37f2bf6 100644 --- a/feature-libs/product-configurator/schematics/add-product-configurator/__snapshots__/index_spec.ts.snap +++ b/feature-libs/product-configurator/schematics/add-product-configurator/__snapshots__/index_spec.ts.snap @@ -4,6 +4,7 @@ exports[`Spartacus product configurator schematics: ng-add Product config featur "import { NgModule } from '@angular/core'; import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; import { configuratorTranslationChunksConfig, configuratorTranslations } from \\"@spartacus/product-configurator/common/assets\\"; +import { RulebasedConfiguratorModule } from \\"@spartacus/product-configurator/rulebased\\"; import { RulebasedCpqConfiguratorModule } from \\"@spartacus/product-configurator/rulebased/cpq\\"; import { CpqConfiguratorRootModule, RulebasedConfiguratorRootModule } from \\"@spartacus/product-configurator/rulebased/root\\"; import { TextfieldConfiguratorModule } from \\"@spartacus/product-configurator/textfield\\"; @@ -13,8 +14,9 @@ import { TextfieldConfiguratorRootModule } from \\"@spartacus/product-configurat declarations: [], imports: [ RulebasedConfiguratorRootModule, - RulebasedCpqConfiguratorModule, + RulebasedConfiguratorModule, CpqConfiguratorRootModule, + RulebasedCpqConfiguratorModule, TextfieldConfiguratorRootModule, TextfieldConfiguratorModule ], @@ -85,7 +87,7 @@ import { PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, TextfieldConfiguratorRootModule featureModules: { [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE]: { module: () => - import('@spartacus/product-configurator/rulebased/cpq').then((m) => m.RulebasedCpqConfiguratorModule), + import('./rulebased-configurator-wrapper.module').then((m) => m.RulebasedConfiguratorWrapperModule), }, } }), @@ -109,6 +111,22 @@ export class ProductConfiguratorFeatureModule { } " `; +exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ and Textfield general setup should add the feature using the lazy loading syntax, including VC as well 2`] = ` +"import { NgModule } from '@angular/core'; +import { RulebasedConfiguratorModule } from \\"@spartacus/product-configurator/rulebased\\"; +import { RulebasedCpqConfiguratorModule } from \\"@spartacus/product-configurator/rulebased/cpq\\"; + +@NgModule({ + declarations: [], + imports: [ + RulebasedConfiguratorModule, + RulebasedCpqConfiguratorModule + ] +}) +export class RulebasedConfiguratorWrapperModule { } +" +`; + exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ and Textfield general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/product-configurator\\";"`; exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ and Textfield general setup styling should update angular.json 1`] = ` @@ -241,6 +259,7 @@ exports[`Spartacus product configurator schematics: ng-add Product config featur "import { NgModule } from '@angular/core'; import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; import { configuratorTranslationChunksConfig, configuratorTranslations } from \\"@spartacus/product-configurator/common/assets\\"; +import { RulebasedConfiguratorModule } from \\"@spartacus/product-configurator/rulebased\\"; import { RulebasedCpqConfiguratorModule } from \\"@spartacus/product-configurator/rulebased/cpq\\"; import { CpqConfiguratorRootModule, RulebasedConfiguratorRootModule } from \\"@spartacus/product-configurator/rulebased/root\\"; @@ -248,8 +267,9 @@ import { CpqConfiguratorRootModule, RulebasedConfiguratorRootModule } from \\"@s declarations: [], imports: [ RulebasedConfiguratorRootModule, - RulebasedCpqConfiguratorModule, - CpqConfiguratorRootModule + RulebasedConfiguratorModule, + CpqConfiguratorRootModule, + RulebasedCpqConfiguratorModule ], providers: [provideConfig({ i18n: { @@ -316,7 +336,7 @@ import { CpqConfiguratorRootModule, PRODUCT_CONFIGURATOR_RULEBASED_FEATURE, Rule featureModules: { [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE]: { module: () => - import('@spartacus/product-configurator/rulebased/cpq').then((m) => m.RulebasedCpqConfiguratorModule), + import('./rulebased-configurator-wrapper.module').then((m) => m.RulebasedConfiguratorWrapperModule), }, } }), @@ -332,6 +352,22 @@ export class ProductConfiguratorFeatureModule { } " `; +exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ general setup should add the feature using the lazy loading syntax, and include VC as well 2`] = ` +"import { NgModule } from '@angular/core'; +import { RulebasedConfiguratorModule } from \\"@spartacus/product-configurator/rulebased\\"; +import { RulebasedCpqConfiguratorModule } from \\"@spartacus/product-configurator/rulebased/cpq\\"; + +@NgModule({ + declarations: [], + imports: [ + RulebasedConfiguratorModule, + RulebasedCpqConfiguratorModule + ] +}) +export class RulebasedConfiguratorWrapperModule { } +" +`; + exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/product-configurator\\";"`; exports[`Spartacus product configurator schematics: ng-add Product config feature CPQ general setup styling should update angular.json 1`] = ` diff --git a/feature-libs/product-configurator/schematics/add-product-configurator/index.ts b/feature-libs/product-configurator/schematics/add-product-configurator/index.ts index fb0d85dc300..d82387bfe96 100644 --- a/feature-libs/product-configurator/schematics/add-product-configurator/index.ts +++ b/feature-libs/product-configurator/schematics/add-product-configurator/index.ts @@ -1,45 +1,20 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, - CLI_PRODUCT_CONFIGURATOR_VC_FEATURE, - configureB2bFeatures, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusProductConfiguratorOptions, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE, - PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE, - PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE, readPackageJson, - shouldAddFeature, - SPARTACUS_PRODUCT_CONFIGURATOR, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - PRODUCT_CONFIGURATOR_FOLDER_NAME, - PRODUCT_CONFIGURATOR_MODULE_NAME, - PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT, - PRODUCT_CONFIGURATOR_SCSS_FILE_NAME, - PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME_CONSTANT, - PRODUCT_CONFIGURATOR_TRANSLATIONS, - PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG, - SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, - SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED, - SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ, - SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, - SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD, - SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, -} from '../constants'; export function addProductConfiguratorFeatures( options: SpartacusProductConfiguratorOptions @@ -48,129 +23,17 @@ export function addProductConfiguratorFeatures( const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); - return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); - shouldAddFeature(CLI_PRODUCT_CONFIGURATOR_VC_FEATURE, options.features) || - shouldAddFeature(CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, options.features) - ? addProductConfiguratorRulebasedFeature(options) - : noop(), + return chain([ + analyzeApplication(options, features), - shouldAddFeature(CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, options.features) - ? chain([ - addCpqRulebasedRootModule(options), - configureB2bFeatures(options, packageJson), - ]) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature( - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, - options.features - ) - ? addProductConfiguratorTextfieldFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -/** - * Called with or without CPQ enabled, and uses a different - * application module for CPQ - * @param options Schematics options - * @returns - */ -function addProductConfiguratorRulebasedFeature( - options: SpartacusProductConfiguratorOptions -): Rule { - let featureModuleName = PRODUCT_CONFIGURATOR_RULEBASED_MODULE; - let featureModuleImportPath = SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED; - if ( - shouldAddFeature(CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, options.features) - ) { - featureModuleName = PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE; - featureModuleImportPath = SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ; - } - - return addLibraryFeature(options, { - folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, - moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, - featureModule: { - name: featureModuleName, - importPath: featureModuleImportPath, - }, - rootModule: { - name: PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, - namedImports: [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: PRODUCT_CONFIGURATOR_TRANSLATIONS, - chunks: PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, - }, - styles: { - scssFileName: PRODUCT_CONFIGURATOR_SCSS_FILE_NAME, - importStyle: SPARTACUS_PRODUCT_CONFIGURATOR, - }, - }); -} -/** - * Needed to set the CPQ specific root module that - * enforces early login and must not be active for - * other configurators - * @param options Schematics options - * @returns - */ -function addCpqRulebasedRootModule( - options: SpartacusProductConfiguratorOptions -): Rule { - return addLibraryFeature(options, { - folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, - moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, - featureModule: { - name: PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ, - }, - rootModule: { - name: PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, - namedImports: [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT], - }, - }); -} - -function addProductConfiguratorTextfieldFeature( - options: SpartacusProductConfiguratorOptions -): Rule { - return addLibraryFeature(options, { - folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, - moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, - featureModule: { - name: PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD, - }, - rootModule: { - name: PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, - namedImports: [PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: PRODUCT_CONFIGURATOR_TRANSLATIONS, - chunks: PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, - }, - styles: { - scssFileName: PRODUCT_CONFIGURATOR_SCSS_FILE_NAME, - importStyle: SPARTACUS_PRODUCT_CONFIGURATOR, - }, - }); -} diff --git a/feature-libs/product-configurator/schematics/add-product-configurator/index_spec.ts b/feature-libs/product-configurator/schematics/add-product-configurator/index_spec.ts index 1ed609aa0c5..d1dd50ef609 100644 --- a/feature-libs/product-configurator/schematics/add-product-configurator/index_spec.ts +++ b/feature-libs/product-configurator/schematics/add-product-configurator/index_spec.ts @@ -8,23 +8,31 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, - CLI_PRODUCT_CONFIGURATOR_VC_FEATURE, + cartBaseFeatureModulePath, + checkoutFeatureModulePath, LibraryOptions as SpartacusProductConfiguratorOptions, + orderFeatureModulePath, + productConfiguratorFeatureModulePath, + productConfiguratorRulebasedWrapperModulePath, + PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME, + PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME, + PRODUCT_CONFIGURATOR_VC_FEATURE_NAME, SPARTACUS_CONFIGURATION_MODULE, + SPARTACUS_PRODUCT_CONFIGURATOR, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/product-configurator/product-configurator-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/product-configurator.scss'; describe('Spartacus product configurator schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_PRODUCT_CONFIGURATOR, + collectionPath + ); let appTree: UnitTestTree; @@ -51,17 +59,17 @@ describe('Spartacus product configurator schematics: ng-add', () => { const libraryOptionsOnlyVC: SpartacusProductConfiguratorOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_CONFIGURATOR_VC_FEATURE], + features: [PRODUCT_CONFIGURATOR_VC_FEATURE_NAME], }; const libraryOptionsOnlyCPQ: SpartacusProductConfiguratorOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE], + features: [PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME], }; const libraryOptionsOnlyTextfield: SpartacusProductConfiguratorOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE], + features: [PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME], }; beforeEach(async () => { @@ -138,8 +146,34 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); + }); + + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const checkoutFeatureModule = appTree.readContent( + checkoutFeatureModulePath + ); + expect(checkoutFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -167,8 +201,14 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); }); }); }); @@ -176,14 +216,44 @@ describe('Spartacus product configurator schematics: ng-add', () => { describe('CPQ', () => { describe('general setup', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', libraryOptionsOnlyVC, appTree) + .toPromise(); appTree = await schematicRunner .runSchematicAsync('ng-add', libraryOptionsOnlyCPQ, appTree) .toPromise(); }); it('should add the feature using the lazy loading syntax, and include VC as well', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + const wrapperModule = appTree.readContent( + productConfiguratorRulebasedWrapperModulePath + ); + expect(wrapperModule).toMatchSnapshot(); + }); + + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const checkoutFeatureModule = appTree.readContent( + checkoutFeatureModulePath + ); + expect(checkoutFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -210,6 +280,13 @@ describe('Spartacus product configurator schematics: ng-add', () => { describe('eager loading', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...libraryOptionsOnlyVC, lazy: false }, + appTree + ) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', @@ -220,8 +297,14 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); }); }); }); @@ -235,8 +318,34 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); + }); + + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const checkoutFeatureModule = appTree.readContent( + checkoutFeatureModulePath + ); + expect(checkoutFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -273,8 +382,14 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); }); }); }); @@ -282,14 +397,17 @@ describe('Spartacus product configurator schematics: ng-add', () => { describe('CPQ and Textfield', () => { describe('general setup', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', libraryOptionsOnlyVC, appTree) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', { ...libraryNoFeaturesOptions, features: [ - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, + PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME, + PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME, ], }, appTree @@ -298,8 +416,35 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax, including VC as well', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + const wrapperModule = appTree.readContent( + productConfiguratorRulebasedWrapperModulePath + ); + expect(wrapperModule).toMatchSnapshot(); + }); + + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent( + orderFeatureModulePath + ); + expect(orderFeatureModule).toBeFalsy(); + + const cartBaseFeatureModule = appTree.readContent( + cartBaseFeatureModulePath + ); + expect(cartBaseFeatureModule).toBeFalsy(); + + const checkoutFeatureModule = appTree.readContent( + checkoutFeatureModulePath + ); + expect(checkoutFeatureModule).toBeFalsy(); }); describe('styling', () => { @@ -326,14 +471,21 @@ describe('Spartacus product configurator schematics: ng-add', () => { describe('eager loading', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...libraryOptionsOnlyVC, lazy: false }, + appTree + ) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', { ...libraryNoFeaturesOptions, features: [ - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, + PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME, + PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME, ], lazy: false, }, @@ -343,8 +495,14 @@ describe('Spartacus product configurator schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent( + productConfiguratorFeatureModulePath + ); expect(module).toMatchSnapshot(); + + expect( + appTree.readContent(productConfiguratorRulebasedWrapperModulePath) + ).toBeFalsy(); }); }); }); diff --git a/feature-libs/product-configurator/schematics/collection.json b/feature-libs/product-configurator/schematics/collection.json index 19821bc8ff6..a26a33db6e7 100644 --- a/feature-libs/product-configurator/schematics/collection.json +++ b/feature-libs/product-configurator/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-product-configurator/index#addProductConfiguratorFeatures", "description": "Add and configure Spartacus' product configurator features", diff --git a/feature-libs/product-configurator/schematics/constants.ts b/feature-libs/product-configurator/schematics/constants.ts deleted file mode 100644 index 608ee1f5f7a..00000000000 --- a/feature-libs/product-configurator/schematics/constants.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { SPARTACUS_PRODUCT_CONFIGURATOR } from '@spartacus/schematics'; - -export const PRODUCT_CONFIGURATOR_MODULE_NAME = 'ProductConfigurator'; -export const PRODUCT_CONFIGURATOR_FOLDER_NAME = 'product-configurator'; -export const PRODUCT_CONFIGURATOR_SCSS_FILE_NAME = 'product-configurator.scss'; -export const PRODUCT_CONFIGURATOR_TRANSLATIONS = 'configuratorTranslations'; -export const PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG = - 'configuratorTranslationChunksConfig'; - -export const PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE_NAME = - 'ProductConfiguratorTextfield'; -export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME_CONSTANT = - 'PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE'; -export const SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD = - '@spartacus/product-configurator/textfield'; -export const SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT = `${SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD}/root`; - -export const PRODUCT_CONFIGURATOR_RULEBASED_MODULE_NAME = - 'ProductConfiguratorRulebased'; -export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT = - 'PRODUCT_CONFIGURATOR_RULEBASED_FEATURE'; -export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED = - '@spartacus/product-configurator/rulebased'; -export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT = `${SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED}/root`; -export const SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS = `${SPARTACUS_PRODUCT_CONFIGURATOR}/common/assets`; -export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ = `${SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED}/cpq`; diff --git a/feature-libs/product/schematics/add-library/index.ts b/feature-libs/product/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/product/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/product/schematics/add-product/index.ts b/feature-libs/product/schematics/add-product/index.ts index cff4702341e..b5b7ea7ed1a 100644 --- a/feature-libs/product/schematics/add-product/index.ts +++ b/feature-libs/product/schematics/add-product/index.ts @@ -1,162 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - BULK_PRICING_MODULE, - BULK_PRICING_ROOT_MODULE, - CLI_PRODUCT_BULK_PRICING_FEATURE, - CLI_PRODUCT_IMAGE_ZOOM_FEATURE, - CLI_PRODUCT_VARIANTS_FEATURE, - configureB2bFeatures, - IMAGE_ZOOM_MODULE, - IMAGE_ZOOM_ROOT_MODULE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusProductOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_PRODUCT, validateSpartacusInstallation, - VARIANTS_MODULE, - VARIANTS_ROOT_MODULE, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - BULK_PRICING_FEATURE_NAME_CONSTANT, - BULK_PRICING_MODULE_NAME, - BULK_PRICING_TRANSLATIONS, - BULK_PRICING_TRANSLATION_CHUNKS_CONFIG, - IMAGE_ZOOM_FEATURE_NAME_CONSTANT, - IMAGE_ZOOM_MODULE_NAME, - IMAGE_ZOOM_TRANSLATIONS, - IMAGE_ZOOM_TRANSLATION_CHUNKS_CONFIG, - PRODUCT_FOLDER_NAME, - PRODUCT_SCSS_FILE_NAME, - SPARTACUS_BULK_PRICING, - SPARTACUS_BULK_PRICING_ASSETS, - SPARTACUS_BULK_PRICING_ROOT, - SPARTACUS_IMAGE_ZOOM, - SPARTACUS_IMAGE_ZOOM_ASSETS, - SPARTACUS_IMAGE_ZOOM_ROOT, - SPARTACUS_VARIANTS, - SPARTACUS_VARIANTS_ASSETS, - SPARTACUS_VARIANTS_ROOT, - VARIANTS_FEATURE_NAME_CONSTANT, - VARIANTS_MODULE_NAME, - VARIANTS_TRANSLATIONS, - VARIANTS_TRANSLATION_CHUNKS_CONFIG, -} from '../constants'; export function addSpartacusProduct(options: SpartacusProductOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); - return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); - shouldAddFeature(CLI_PRODUCT_BULK_PRICING_FEATURE, options.features) - ? chain([ - addBulkPricingFeature(options), - configureB2bFeatures(options, packageJson), - ]) - : noop(), + return chain([ + analyzeApplication(options, features), - shouldAddFeature(CLI_PRODUCT_VARIANTS_FEATURE, options.features) - ? addVariantsFeature(options) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_PRODUCT_IMAGE_ZOOM_FEATURE, options.features) - ? addImageZoom(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -export function addBulkPricingFeature(options: SpartacusProductOptions): Rule { - return addLibraryFeature(options, { - folderName: PRODUCT_FOLDER_NAME, - moduleName: BULK_PRICING_MODULE_NAME, - featureModule: { - name: BULK_PRICING_MODULE, - importPath: SPARTACUS_BULK_PRICING, - }, - rootModule: { - name: BULK_PRICING_ROOT_MODULE, - importPath: SPARTACUS_BULK_PRICING_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_BULK_PRICING_ROOT, - namedImports: [BULK_PRICING_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: BULK_PRICING_TRANSLATIONS, - chunks: BULK_PRICING_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_BULK_PRICING_ASSETS, - }, - styles: { - scssFileName: PRODUCT_SCSS_FILE_NAME, - importStyle: SPARTACUS_PRODUCT, - }, - }); -} - -export function addVariantsFeature(options: SpartacusProductOptions): Rule { - return addLibraryFeature(options, { - folderName: PRODUCT_FOLDER_NAME, - moduleName: VARIANTS_MODULE_NAME, - featureModule: { - name: VARIANTS_MODULE, - importPath: SPARTACUS_VARIANTS, - }, - rootModule: { - name: VARIANTS_ROOT_MODULE, - importPath: SPARTACUS_VARIANTS_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_VARIANTS_ROOT, - namedImports: [VARIANTS_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: VARIANTS_TRANSLATIONS, - chunks: VARIANTS_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_VARIANTS_ASSETS, - }, - styles: { - scssFileName: PRODUCT_SCSS_FILE_NAME, - importStyle: SPARTACUS_PRODUCT, - }, - }); -} - -export function addImageZoom(options: SpartacusProductOptions): Rule { - return addLibraryFeature(options, { - folderName: PRODUCT_FOLDER_NAME, - moduleName: IMAGE_ZOOM_MODULE_NAME, - featureModule: { - name: IMAGE_ZOOM_MODULE, - importPath: SPARTACUS_IMAGE_ZOOM, - }, - rootModule: { - name: IMAGE_ZOOM_ROOT_MODULE, - importPath: SPARTACUS_IMAGE_ZOOM_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_IMAGE_ZOOM_ROOT, - namedImports: [IMAGE_ZOOM_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: IMAGE_ZOOM_TRANSLATIONS, - chunks: IMAGE_ZOOM_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_IMAGE_ZOOM_ASSETS, - }, - styles: { - scssFileName: PRODUCT_SCSS_FILE_NAME, - importStyle: SPARTACUS_PRODUCT, - }, - }); -} diff --git a/feature-libs/product/schematics/add-product/index_spec.ts b/feature-libs/product/schematics/add-product/index_spec.ts index a5438c88dc2..35539805c8e 100644 --- a/feature-libs/product/schematics/add-product/index_spec.ts +++ b/feature-libs/product/schematics/add-product/index_spec.ts @@ -10,28 +10,29 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_PRODUCT_BULK_PRICING_FEATURE, - CLI_PRODUCT_IMAGE_ZOOM_FEATURE, - CLI_PRODUCT_VARIANTS_FEATURE, LibraryOptions as SpartacusProductOptions, + productBulkPricingFeatureModulePath, + productImageZoomFeatureModulePath, + productVariantsFeatureModulePath, + PRODUCT_BULK_PRICING_FEATURE_NAME, + PRODUCT_IMAGE_ZOOM_FEATURE_NAME, + PRODUCT_VARIANTS_FEATURE_NAME, SpartacusOptions, SPARTACUS_CONFIGURATION_MODULE, + SPARTACUS_PRODUCT, SPARTACUS_SCHEMATICS, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const bulkPricingModulePath = - 'src/app/spartacus/features/product/product-bulk-pricing-feature.module.ts'; -const variantsFeatureModulePath = - 'src/app/spartacus/features/product/product-variants-feature.module.ts'; -const imageZoomFeatureModulePath = - 'src/app/spartacus/features/product/product-image-zoom-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/product.scss'; describe('Spartacus Product schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_PRODUCT, + collectionPath + ); let appTree: UnitTestTree; @@ -64,17 +65,17 @@ describe('Spartacus Product schematics: ng-add', () => { const bulkPricingOptions: SpartacusProductOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_BULK_PRICING_FEATURE], + features: [PRODUCT_BULK_PRICING_FEATURE_NAME], }; const variantsOptions: SpartacusProductOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_VARIANTS_FEATURE], + features: [PRODUCT_VARIANTS_FEATURE_NAME], }; const imageZoomOptions: SpartacusProductOptions = { ...libraryNoFeaturesOptions, - features: [CLI_PRODUCT_IMAGE_ZOOM_FEATURE], + features: [PRODUCT_IMAGE_ZOOM_FEATURE_NAME], }; beforeEach(async () => { @@ -116,9 +117,9 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(bulkPricingModulePath)).toBeFalsy(); - expect(appTree.exists(variantsFeatureModulePath)).toBeFalsy(); - expect(appTree.exists(imageZoomFeatureModulePath)).toBeFalsy(); + expect(appTree.exists(productBulkPricingFeatureModulePath)).toBeFalsy(); + expect(appTree.exists(productVariantsFeatureModulePath)).toBeFalsy(); + expect(appTree.exists(productImageZoomFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -156,7 +157,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(bulkPricingModulePath); + const module = appTree.readContent(productBulkPricingFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -194,7 +195,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(bulkPricingModulePath); + const module = appTree.readContent(productBulkPricingFeatureModulePath); expect(module).toMatchSnapshot(); }); }); @@ -209,7 +210,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(variantsFeatureModulePath); + const module = appTree.readContent(productVariantsFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -238,7 +239,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(variantsFeatureModulePath); + const module = appTree.readContent(productVariantsFeatureModulePath); expect(module).toMatchSnapshot(); }); }); @@ -253,7 +254,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(imageZoomFeatureModulePath); + const module = appTree.readContent(productImageZoomFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -282,7 +283,7 @@ describe('Spartacus Product schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(imageZoomFeatureModulePath); + const module = appTree.readContent(productImageZoomFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/product/schematics/collection.json b/feature-libs/product/schematics/collection.json index 9d48972e527..122fe8e6a78 100644 --- a/feature-libs/product/schematics/collection.json +++ b/feature-libs/product/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-product/index#addSpartacusProduct", "description": "Add and configure Spartacus' Product library", diff --git a/feature-libs/product/schematics/constants.ts b/feature-libs/product/schematics/constants.ts deleted file mode 100644 index e11d67eaf17..00000000000 --- a/feature-libs/product/schematics/constants.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { SPARTACUS_PRODUCT } from '@spartacus/schematics'; - -export const PRODUCT_FOLDER_NAME = 'product'; -export const PRODUCT_SCSS_FILE_NAME = 'product.scss'; - -export const BULK_PRICING_MODULE_NAME = 'ProductBulkPricing'; -export const BULK_PRICING_FEATURE_NAME_CONSTANT = - 'PRODUCT_BULK_PRICING_FEATURE'; -export const SPARTACUS_BULK_PRICING = `${SPARTACUS_PRODUCT}/bulk-pricing`; -export const SPARTACUS_BULK_PRICING_ROOT = `${SPARTACUS_BULK_PRICING}/root`; -export const SPARTACUS_BULK_PRICING_ASSETS = `${SPARTACUS_BULK_PRICING}/assets`; -export const BULK_PRICING_TRANSLATIONS = 'bulkPricingTranslations'; -export const BULK_PRICING_TRANSLATION_CHUNKS_CONFIG = - 'bulkPricingTranslationChunksConfig'; - -export const VARIANTS_MODULE_NAME = 'ProductVariants'; -export const VARIANTS_FEATURE_NAME_CONSTANT = 'PRODUCT_VARIANTS_FEATURE'; -export const SPARTACUS_VARIANTS = `${SPARTACUS_PRODUCT}/variants`; -export const SPARTACUS_VARIANTS_ROOT = `${SPARTACUS_VARIANTS}/root`; -export const SPARTACUS_VARIANTS_ASSETS = `${SPARTACUS_VARIANTS}/assets`; -export const VARIANTS_TRANSLATIONS = 'productVariantsTranslations'; -export const VARIANTS_TRANSLATION_CHUNKS_CONFIG = - 'productVariantsTranslationChunksConfig'; - -export const IMAGE_ZOOM_MODULE_NAME = 'ProductImageZoom'; -export const IMAGE_ZOOM_FEATURE_NAME_CONSTANT = 'PRODUCT_IMAGE_ZOOM_FEATURE'; -export const SPARTACUS_IMAGE_ZOOM = `${SPARTACUS_PRODUCT}/image-zoom`; -export const SPARTACUS_IMAGE_ZOOM_ROOT = `${SPARTACUS_IMAGE_ZOOM}/root`; -export const SPARTACUS_IMAGE_ZOOM_ASSETS = `${SPARTACUS_IMAGE_ZOOM}/assets`; -export const IMAGE_ZOOM_TRANSLATIONS = 'productImageZoomTranslations'; -export const IMAGE_ZOOM_TRANSLATION_CHUNKS_CONFIG = - 'productImageZoomTranslationChunksConfig'; diff --git a/feature-libs/qualtrics/schematics/add-library/index.ts b/feature-libs/qualtrics/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/qualtrics/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/qualtrics/schematics/add-qualtrics/index.ts b/feature-libs/qualtrics/schematics/add-qualtrics/index.ts index 47b97ad7648..be878efd6c9 100644 --- a/feature-libs/qualtrics/schematics/add-qualtrics/index.ts +++ b/feature-libs/qualtrics/schematics/add-qualtrics/index.ts @@ -1,65 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_QUALTRICS_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusQualtricsOptions, - QUALTRICS_MODULE, - QUALTRICS_ROOT_MODULE, readPackageJson, - shouldAddFeature, - SPARTACUS_QUALTRICS, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - QUALTRICS_EMBEDDED_FEEDBACK_SCSS_FILE_NAME, - QUALTRICS_FEATURE_NAME_CONSTANT, - QUALTRICS_FOLDER_NAME, - QUALTRICS_MODULE_NAME, - SPARTACUS_QUALTRICS_ROOT, -} from '../constants'; export function addQualtricsFeatures(options: SpartacusQualtricsOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_QUALTRICS_FEATURE, options.features) - ? addQualtricsFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addQualtricsFeature(options: SpartacusQualtricsOptions): Rule { - return addLibraryFeature(options, { - folderName: QUALTRICS_FOLDER_NAME, - moduleName: QUALTRICS_MODULE_NAME, - featureModule: { - name: QUALTRICS_MODULE, - importPath: SPARTACUS_QUALTRICS, - }, - rootModule: { - name: QUALTRICS_ROOT_MODULE, - importPath: SPARTACUS_QUALTRICS_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_QUALTRICS_ROOT, - namedImports: [QUALTRICS_FEATURE_NAME_CONSTANT], - }, - styles: { - scssFileName: QUALTRICS_EMBEDDED_FEEDBACK_SCSS_FILE_NAME, - importStyle: SPARTACUS_QUALTRICS, - }, - }); -} diff --git a/feature-libs/qualtrics/schematics/add-qualtrics/index_spec.ts b/feature-libs/qualtrics/schematics/add-qualtrics/index_spec.ts index e3170218510..22fb1c6d90b 100644 --- a/feature-libs/qualtrics/schematics/add-qualtrics/index_spec.ts +++ b/feature-libs/qualtrics/schematics/add-qualtrics/index_spec.ts @@ -10,21 +10,24 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_QUALTRICS_FEATURE, LibraryOptions as SpartacusQualtricsOptions, + qualtricsFeatureModulePath, + QUALTRICS_FEATURE_NAME, SpartacusOptions, + SPARTACUS_QUALTRICS, SPARTACUS_SCHEMATICS, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/qualtrics/qualtrics-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/qualtrics-embedded-feedback.scss'; describe('Spartacus Qualtrics schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_QUALTRICS, + collectionPath + ); let appTree: UnitTestTree; @@ -57,7 +60,7 @@ describe('Spartacus Qualtrics schematics: ng-add', () => { const qualtricsOptions: SpartacusQualtricsOptions = { ...libraryNoFeaturesOptions, - features: [CLI_QUALTRICS_FEATURE], + features: [QUALTRICS_FEATURE_NAME], }; beforeEach(async () => { @@ -99,7 +102,7 @@ describe('Spartacus Qualtrics schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(qualtricsFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -137,7 +140,7 @@ describe('Spartacus Qualtrics schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(qualtricsFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -166,7 +169,7 @@ describe('Spartacus Qualtrics schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(qualtricsFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/qualtrics/schematics/collection.json b/feature-libs/qualtrics/schematics/collection.json index 4d57c2d763c..ead5ea2f84e 100644 --- a/feature-libs/qualtrics/schematics/collection.json +++ b/feature-libs/qualtrics/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-qualtrics/index#addQualtricsFeatures", "description": "Add and configure Spartacus' qualtrics features", diff --git a/feature-libs/qualtrics/schematics/constants.ts b/feature-libs/qualtrics/schematics/constants.ts deleted file mode 100644 index 5a85d7dfce3..00000000000 --- a/feature-libs/qualtrics/schematics/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SPARTACUS_QUALTRICS } from '@spartacus/schematics'; - -export const QUALTRICS_FOLDER_NAME = 'qualtrics'; -export const QUALTRICS_MODULE_NAME = 'Qualtrics'; -export const QUALTRICS_EMBEDDED_FEEDBACK_SCSS_FILE_NAME = - 'qualtrics-embedded-feedback.scss'; - -export const QUALTRICS_FEATURE_NAME_CONSTANT = 'QUALTRICS_FEATURE'; -export const SPARTACUS_QUALTRICS_ROOT = `${SPARTACUS_QUALTRICS}/root`; diff --git a/feature-libs/smartedit/schematics/add-library/index.ts b/feature-libs/smartedit/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/smartedit/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/smartedit/schematics/add-smartedit/__snapshots__/index_spec.ts.snap b/feature-libs/smartedit/schematics/add-smartedit/__snapshots__/index_spec.ts.snap index dccab91ec68..64bee6b28c0 100644 --- a/feature-libs/smartedit/schematics/add-smartedit/__snapshots__/index_spec.ts.snap +++ b/feature-libs/smartedit/schematics/add-smartedit/__snapshots__/index_spec.ts.snap @@ -2,15 +2,22 @@ exports[`Spartacus SmartEdit schematics: ng-add SmartEdit feature eager loading should import appropriate modules 1`] = ` "import { NgModule } from '@angular/core'; +import { provideConfig } from \\"@spartacus/core\\"; import { SmartEditModule } from \\"@spartacus/smartedit\\"; -import { SmartEditRootModule } from \\"@spartacus/smartedit/root\\"; +import { SmartEditConfig, SmartEditRootModule } from \\"@spartacus/smartedit/root\\"; @NgModule({ declarations: [], imports: [ SmartEditRootModule, SmartEditModule - ] + ], + providers: [provideConfig({ + smartEdit: { + storefrontPreviewRoute: 'STOREFRONT_PREVIEW_ROUTE_PLACEHOLDER', + allowOrigin: 'ALLOWED_ORIGIN_PLACEHOLDER', + }, + })] }) export class SmartEditFeatureModule { } " @@ -153,7 +160,7 @@ exports[`Spartacus SmartEdit schematics: ng-add SmartEdit feature general setup exports[`Spartacus SmartEdit schematics: ng-add SmartEdit feature general setup should add the feature using the lazy loading syntax 1`] = ` "import { NgModule } from '@angular/core'; import { CmsConfig, provideConfig } from \\"@spartacus/core\\"; -import { SmartEditRootModule, SMART_EDIT_FEATURE } from \\"@spartacus/smartedit/root\\"; +import { SmartEditConfig, SmartEditRootModule, SMART_EDIT_FEATURE } from \\"@spartacus/smartedit/root\\"; @NgModule({ declarations: [], @@ -167,7 +174,14 @@ import { SmartEditRootModule, SMART_EDIT_FEATURE } from \\"@spartacus/smartedit/ import('@spartacus/smartedit').then((m) => m.SmartEditModule), }, } - })] + }), + provideConfig({ + smartEdit: { + storefrontPreviewRoute: 'STOREFRONT_PREVIEW_ROUTE_PLACEHOLDER', + allowOrigin: 'ALLOWED_ORIGIN_PLACEHOLDER', + }, + }) + ] }) export class SmartEditFeatureModule { } " @@ -193,6 +207,7 @@ import { SmartEditConfig, SmartEditRootModule, SMART_EDIT_FEATURE } from \\"@spa }), provideConfig({ smartEdit: { + storefrontPreviewRoute: 'STOREFRONT_PREVIEW_ROUTE_PLACEHOLDER', allowOrigin: 'localhost:9002', }, }) @@ -223,6 +238,7 @@ import { SmartEditConfig, SmartEditRootModule, SMART_EDIT_FEATURE } from \\"@spa provideConfig({ smartEdit: { storefrontPreviewRoute: 'cx-preview', + allowOrigin: 'ALLOWED_ORIGIN_PLACEHOLDER', }, }) ] diff --git a/feature-libs/smartedit/schematics/add-smartedit/index.ts b/feature-libs/smartedit/schematics/add-smartedit/index.ts index 3c6385e6830..f4e04532d09 100644 --- a/feature-libs/smartedit/schematics/add-smartedit/index.ts +++ b/feature-libs/smartedit/schematics/add-smartedit/index.ts @@ -1,91 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_SMARTEDIT_FEATURE, - CustomConfig, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, readPackageJson, - shouldAddFeature, - SMARTEDIT_MODULE, - SMARTEDIT_ROOT_MODULE, - SMART_EDIT_CONFIG, - SPARTACUS_SMARTEDIT, + SpartacusSmartEditOptions, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - SMARTEDIT_FEATURE_NAME_CONSTANT, - SMARTEDIT_FOLDER_NAME, - SMARTEDIT_MODULE_NAME, - SPARTACUS_SMARTEDIT_ASSETS, - SPARTACUS_SMARTEDIT_ROOT, -} from '../constants'; -import { Schema as SpartacusSmartEditOptions } from './schema'; export function addSmartEditFeatures(options: SpartacusSmartEditOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_SMARTEDIT_FEATURE, options.features) - ? addSmartEditFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addSmartEditFeature(options: SpartacusSmartEditOptions): Rule { - const customConfig: CustomConfig[] = []; - if (options.storefrontPreviewRoute || options.allowOrigin) { - let content = `<${SMART_EDIT_CONFIG}>{ - smartEdit: {\n`; - if (options.storefrontPreviewRoute) { - content += `storefrontPreviewRoute: '${options.storefrontPreviewRoute}',\n`; - } - if (options.allowOrigin) { - content += `allowOrigin: '${options.allowOrigin}',\n`; - } - content += `},\n}`; - - customConfig.push({ - import: [ - { - moduleSpecifier: SPARTACUS_SMARTEDIT_ROOT, - namedImports: [SMART_EDIT_CONFIG], - }, - ], - content, - }); - } - - return addLibraryFeature(options, { - folderName: SMARTEDIT_FOLDER_NAME, - moduleName: SMARTEDIT_MODULE_NAME, - featureModule: { - name: SMARTEDIT_MODULE, - importPath: SPARTACUS_SMARTEDIT, - }, - rootModule: { - name: SMARTEDIT_ROOT_MODULE, - importPath: SPARTACUS_SMARTEDIT_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_SMARTEDIT_ROOT, - namedImports: [SMARTEDIT_FEATURE_NAME_CONSTANT], - }, - customConfig, - assets: { - input: SPARTACUS_SMARTEDIT_ASSETS, - glob: '**/*', - }, - }); -} diff --git a/feature-libs/smartedit/schematics/add-smartedit/index_spec.ts b/feature-libs/smartedit/schematics/add-smartedit/index_spec.ts index 6ea160bd6c6..e4c6e683d59 100644 --- a/feature-libs/smartedit/schematics/add-smartedit/index_spec.ts +++ b/feature-libs/smartedit/schematics/add-smartedit/index_spec.ts @@ -10,20 +10,23 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_SMARTEDIT_FEATURE, + smartEditFeatureModulePath, + SMARTEDIT_FEATURE_NAME, SpartacusOptions, + SpartacusSmartEditOptions, SPARTACUS_SCHEMATICS, + SPARTACUS_SMARTEDIT, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; -import { Schema as SpartacusSmartEditOptions } from './schema'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/smartedit/smart-edit-feature.module.ts'; describe('Spartacus SmartEdit schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SMARTEDIT, + collectionPath + ); let appTree: UnitTestTree; @@ -56,7 +59,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { const smarteditFeatureOptions: SpartacusSmartEditOptions = { ...libraryNoFeaturesOptions, - features: [CLI_SMARTEDIT_FEATURE], + features: [SMARTEDIT_FEATURE_NAME], }; beforeEach(async () => { @@ -98,7 +101,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(smartEditFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -136,7 +139,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(smartEditFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -160,7 +163,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(smartEditFeatureModulePath); expect(module).toMatchSnapshot(); }); }); @@ -180,7 +183,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { }); it('should configure the storefrontPreviewRoute', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(smartEditFeatureModulePath); expect(module).toMatchSnapshot(); }); }); @@ -200,7 +203,7 @@ describe('Spartacus SmartEdit schematics: ng-add', () => { }); it('should configure the allowOrigin', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(smartEditFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/smartedit/schematics/add-smartedit/schema.ts b/feature-libs/smartedit/schematics/add-smartedit/schema.ts deleted file mode 100644 index 741b7473bd3..00000000000 --- a/feature-libs/smartedit/schematics/add-smartedit/schema.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { LibraryOptions } from '@spartacus/schematics'; - -export interface Schema extends LibraryOptions { - storefrontPreviewRoute?: string; - allowOrigin?: string; -} diff --git a/feature-libs/smartedit/schematics/collection.json b/feature-libs/smartedit/schematics/collection.json index 379779c6778..9185e700862 100644 --- a/feature-libs/smartedit/schematics/collection.json +++ b/feature-libs/smartedit/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-smartedit/index#addSmartEditFeatures", "description": "Add and configure Spartacus' SmartEdit features", diff --git a/feature-libs/smartedit/schematics/constants.ts b/feature-libs/smartedit/schematics/constants.ts deleted file mode 100644 index c1f7d4bff09..00000000000 --- a/feature-libs/smartedit/schematics/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SPARTACUS_SMARTEDIT } from '@spartacus/schematics'; - -export const SMARTEDIT_FOLDER_NAME = 'smartedit'; -export const SMARTEDIT_MODULE_NAME = 'SmartEdit'; - -export const SMARTEDIT_FEATURE_NAME_CONSTANT = 'SMART_EDIT_FEATURE'; -export const SPARTACUS_SMARTEDIT_ROOT = `${SPARTACUS_SMARTEDIT}/root`; -export const SPARTACUS_SMARTEDIT_ASSETS = 'smartedit/assets'; diff --git a/feature-libs/storefinder/schematics/add-library/index.ts b/feature-libs/storefinder/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/storefinder/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/storefinder/schematics/add-storefinder/index.ts b/feature-libs/storefinder/schematics/add-storefinder/index.ts index 1afaa30a1c3..2faa623de5f 100644 --- a/feature-libs/storefinder/schematics/add-storefinder/index.ts +++ b/feature-libs/storefinder/schematics/add-storefinder/index.ts @@ -1,33 +1,20 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_STOREFINDER_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusStorefinderOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_STOREFINDER, - STOREFINDER_MODULE, - STOREFINDER_ROOT_MODULE, - STORE_FINDER_SCSS_FILE_NAME, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - SPARTACUS_STOREFINDER_ASSETS, - SPARTACUS_STOREFINDER_ROOT, - STOREFINDER_FEATURE_NAME_CONSTANT, - STOREFINDER_FOLDER_NAME, - STOREFINDER_MODULE_NAME, - STOREFINDER_TRANSLATIONS, - STOREFINDER_TRANSLATION_CHUNKS_CONFIG, -} from '../constants'; export function addStorefinderFeatures( options: SpartacusStorefinderOptions @@ -36,40 +23,17 @@ export function addStorefinderFeatures( const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_STOREFINDER_FEATURE, options.features) - ? addStorefinderFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addStorefinderFeature(options: SpartacusStorefinderOptions): Rule { - return addLibraryFeature(options, { - folderName: STOREFINDER_FOLDER_NAME, - moduleName: STOREFINDER_MODULE_NAME, - featureModule: { - name: STOREFINDER_MODULE, - importPath: SPARTACUS_STOREFINDER, - }, - rootModule: { - name: STOREFINDER_ROOT_MODULE, - importPath: SPARTACUS_STOREFINDER_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_STOREFINDER_ROOT, - namedImports: [STOREFINDER_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: STOREFINDER_TRANSLATIONS, - chunks: STOREFINDER_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_STOREFINDER_ASSETS, - }, - styles: { - scssFileName: STORE_FINDER_SCSS_FILE_NAME, - importStyle: SPARTACUS_STOREFINDER, - }, - }); -} diff --git a/feature-libs/storefinder/schematics/add-storefinder/index_spec.ts b/feature-libs/storefinder/schematics/add-storefinder/index_spec.ts index 47959061b2e..68a9100ce59 100644 --- a/feature-libs/storefinder/schematics/add-storefinder/index_spec.ts +++ b/feature-libs/storefinder/schematics/add-storefinder/index_spec.ts @@ -10,21 +10,24 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_STOREFINDER_FEATURE, LibraryOptions as SpartacusStorefinderOptions, SpartacusOptions, SPARTACUS_SCHEMATICS, + SPARTACUS_STOREFINDER, + storeFinderFeatureModulePath, + STOREFINDER_FEATURE_NAME, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/storefinder/store-finder-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/storefinder.scss'; describe('Spartacus Storefinder schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_STOREFINDER, + collectionPath + ); let appTree: UnitTestTree; @@ -57,7 +60,7 @@ describe('Spartacus Storefinder schematics: ng-add', () => { const storefinderFeatureOptions: SpartacusStorefinderOptions = { ...libraryNoFeaturesOptions, - features: [CLI_STOREFINDER_FEATURE], + features: [STOREFINDER_FEATURE_NAME], }; beforeEach(async () => { @@ -99,7 +102,7 @@ describe('Spartacus Storefinder schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(storeFinderFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -137,7 +140,7 @@ describe('Spartacus Storefinder schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(storeFinderFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -166,7 +169,7 @@ describe('Spartacus Storefinder schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(storeFinderFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/storefinder/schematics/collection.json b/feature-libs/storefinder/schematics/collection.json index c30b2c26cb1..b90bb3b4710 100644 --- a/feature-libs/storefinder/schematics/collection.json +++ b/feature-libs/storefinder/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-storefinder/index#addStorefinderFeatures", "description": "Add and configure Spartacus' storefinder features", diff --git a/feature-libs/storefinder/schematics/constants.ts b/feature-libs/storefinder/schematics/constants.ts deleted file mode 100644 index e1132633b9b..00000000000 --- a/feature-libs/storefinder/schematics/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { SPARTACUS_STOREFINDER } from '@spartacus/schematics'; - -export const STOREFINDER_FOLDER_NAME = 'storefinder'; -export const STOREFINDER_MODULE_NAME = 'StoreFinder'; - -export const STOREFINDER_FEATURE_NAME_CONSTANT = 'STORE_FINDER_FEATURE'; -export const SPARTACUS_STOREFINDER_ROOT = `${SPARTACUS_STOREFINDER}/root`; -export const SPARTACUS_STOREFINDER_ASSETS = `${SPARTACUS_STOREFINDER}/assets`; -export const STOREFINDER_TRANSLATIONS = 'storeFinderTranslations'; -export const STOREFINDER_TRANSLATION_CHUNKS_CONFIG = - 'storeFinderTranslationChunksConfig'; diff --git a/feature-libs/tracking/schematics/add-library/index.ts b/feature-libs/tracking/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/tracking/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/tracking/schematics/add-tracking/index.ts b/feature-libs/tracking/schematics/add-tracking/index.ts index 09c1c2f73d6..655448eb40b 100644 --- a/feature-libs/tracking/schematics/add-tracking/index.ts +++ b/feature-libs/tracking/schematics/add-tracking/index.ts @@ -1,148 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_TRACKING_PERSONALIZATION_FEATURE, - CLI_TRACKING_TMS_AEP_FEATURE, - CLI_TRACKING_TMS_GTM_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusTrackingOptions, - PERSONALIZATION_MODULE, - PERSONALIZATION_ROOT_MODULE, readPackageJson, - shouldAddFeature, - TMS_AEP_MODULE, - TMS_BASE_MODULE, - TMS_GTM_MODULE, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - PERSONALIZATION_FEATURE_NAME_CONSTANT, - PERSONALIZATION_MODULE_NAME, - SPARTACUS_PERSONALIZATION, - SPARTACUS_PERSONALIZATION_ROOT, - SPARTACUS_TMS_AEP, - SPARTACUS_TMS_CORE, - SPARTACUS_TMS_GTM, - TMS_CONFIG, - TMS_MODULE_NAME, - TRACKING_FOLDER_NAME, -} from '../constants'; export function addTrackingFeatures(options: SpartacusTrackingOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); - return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); - shouldAddFeature(CLI_TRACKING_TMS_GTM_FEATURE, options.features) - ? addGtm(options) - : noop(), + return chain([ + analyzeApplication(options, features), - shouldAddFeature(CLI_TRACKING_TMS_AEP_FEATURE, options.features) - ? addAep(options) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_TRACKING_PERSONALIZATION_FEATURE, options.features) - ? addPersonalizationFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addGtm(options: SpartacusTrackingOptions): Rule { - return addLibraryFeature( - { ...options, lazy: false }, // To add feature module in imports (not lazy) - { - folderName: TRACKING_FOLDER_NAME, - moduleName: TMS_MODULE_NAME, - featureModule: { - name: TMS_GTM_MODULE, - importPath: SPARTACUS_TMS_GTM, - }, - rootModule: { - name: TMS_BASE_MODULE, - importPath: SPARTACUS_TMS_CORE, - content: `${TMS_BASE_MODULE}.forRoot()`, - }, - customConfig: { - import: [ - { - moduleSpecifier: SPARTACUS_TMS_GTM, - namedImports: [TMS_GTM_MODULE], - }, - { moduleSpecifier: SPARTACUS_TMS_CORE, namedImports: [TMS_CONFIG] }, - ], - content: `<${TMS_CONFIG}>{ - tagManager: { - gtm: { - events: [], - }, - }, - }`, - }, - } - ); -} - -function addAep(options: SpartacusTrackingOptions): Rule { - return addLibraryFeature( - { ...options, lazy: false }, // To add feature module in imports (not lazy) - { - folderName: TRACKING_FOLDER_NAME, - moduleName: TMS_MODULE_NAME, - featureModule: { - name: TMS_AEP_MODULE, - importPath: SPARTACUS_TMS_AEP, - }, - rootModule: { - name: TMS_BASE_MODULE, - importPath: SPARTACUS_TMS_CORE, - content: `${TMS_BASE_MODULE}.forRoot()`, - }, - customConfig: { - import: [ - { - moduleSpecifier: SPARTACUS_TMS_AEP, - namedImports: [TMS_AEP_MODULE], - }, - { moduleSpecifier: SPARTACUS_TMS_CORE, namedImports: [TMS_CONFIG] }, - ], - content: `<${TMS_CONFIG}>{ - tagManager: { - aep: { - events: [], - }, - }, - }`, - }, - } - ); -} - -function addPersonalizationFeature(options: SpartacusTrackingOptions): Rule { - return addLibraryFeature(options, { - folderName: TRACKING_FOLDER_NAME, - moduleName: PERSONALIZATION_MODULE_NAME, - featureModule: { - name: PERSONALIZATION_MODULE, - importPath: SPARTACUS_PERSONALIZATION, - }, - rootModule: { - name: PERSONALIZATION_ROOT_MODULE, - importPath: SPARTACUS_PERSONALIZATION_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_PERSONALIZATION_ROOT, - namedImports: [PERSONALIZATION_FEATURE_NAME_CONSTANT], - }, - }); -} diff --git a/feature-libs/tracking/schematics/add-tracking/index_spec.ts b/feature-libs/tracking/schematics/add-tracking/index_spec.ts index f77dc3e94eb..02e9e5bbfbb 100644 --- a/feature-libs/tracking/schematics/add-tracking/index_spec.ts +++ b/feature-libs/tracking/schematics/add-tracking/index_spec.ts @@ -10,24 +10,26 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_TRACKING_PERSONALIZATION_FEATURE, - CLI_TRACKING_TMS_AEP_FEATURE, - CLI_TRACKING_TMS_GTM_FEATURE, LibraryOptions as SpartacusTrackingOptions, SpartacusOptions, SPARTACUS_SCHEMATICS, + SPARTACUS_TRACKING, + trackingPersonalizationFeatureModulePath, + trackingTagManagementFeatureModulePath, + TRACKING_PERSONALIZATION_FEATURE_NAME, + TRACKING_TMS_AEP_FEATURE_NAME, + TRACKING_TMS_GTM_FEATURE_NAME, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const personalizationModulePath = - 'src/app/spartacus/features/tracking/personalization-feature.module.ts'; -const tagManagementModulePath = - 'src/app/spartacus/features/tracking/tag-management-feature.module.ts'; describe('Spartacus Tracking schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_TRACKING, + collectionPath + ); let appTree: UnitTestTree; @@ -60,17 +62,17 @@ describe('Spartacus Tracking schematics: ng-add', () => { const personalizationFeatureOptions: SpartacusTrackingOptions = { ...libraryNoFeaturesOptions, - features: [CLI_TRACKING_PERSONALIZATION_FEATURE], + features: [TRACKING_PERSONALIZATION_FEATURE_NAME], }; const gtmFeatureOptions: SpartacusTrackingOptions = { ...libraryNoFeaturesOptions, - features: [CLI_TRACKING_TMS_GTM_FEATURE], + features: [TRACKING_TMS_GTM_FEATURE_NAME], }; const aepFeatureOptions: SpartacusTrackingOptions = { ...libraryNoFeaturesOptions, - features: [CLI_TRACKING_TMS_AEP_FEATURE], + features: [TRACKING_TMS_AEP_FEATURE_NAME], }; beforeEach(async () => { @@ -112,8 +114,12 @@ describe('Spartacus Tracking schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(personalizationModulePath)).toBeFalsy(); - expect(appTree.exists(tagManagementModulePath)).toBeFalsy(); + expect( + appTree.exists(trackingPersonalizationFeatureModulePath) + ).toBeFalsy(); + expect( + appTree.exists(trackingTagManagementFeatureModulePath) + ).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -152,7 +158,7 @@ describe('Spartacus Tracking schematics: ng-add', () => { it('should add the feature using the lazy loading syntax', async () => { const personalizationModule = appTree.readContent( - personalizationModulePath + trackingPersonalizationFeatureModulePath ); expect(personalizationModule).toMatchSnapshot(); }); @@ -173,7 +179,9 @@ describe('Spartacus Tracking schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(personalizationModulePath); + const module = appTree.readContent( + trackingPersonalizationFeatureModulePath + ); expect(module).toMatchSnapshot(); }); }); @@ -189,7 +197,7 @@ describe('Spartacus Tracking schematics: ng-add', () => { describe('general setup', () => { it('should import appropriate modules (without lazy loaded syntax)', async () => { const tagManagementModule = appTree.readContent( - tagManagementModulePath + trackingTagManagementFeatureModulePath ); expect(tagManagementModule).toMatchSnapshot(); }); @@ -206,7 +214,7 @@ describe('Spartacus Tracking schematics: ng-add', () => { describe('general setup', () => { it('should import appropriate modules (without lazy loaded syntax)', async () => { const tagManagementModule = appTree.readContent( - tagManagementModulePath + trackingTagManagementFeatureModulePath ); expect(tagManagementModule).toMatchSnapshot(); }); @@ -221,8 +229,8 @@ describe('Spartacus Tracking schematics: ng-add', () => { { ...libraryNoFeaturesOptions, features: [ - CLI_TRACKING_TMS_GTM_FEATURE, - CLI_TRACKING_TMS_AEP_FEATURE, + TRACKING_TMS_GTM_FEATURE_NAME, + TRACKING_TMS_AEP_FEATURE_NAME, ], }, appTree @@ -233,7 +241,7 @@ describe('Spartacus Tracking schematics: ng-add', () => { describe('general setup', () => { it('should import appropriate modules (without lazy loaded syntax)', async () => { const tagManagementModule = appTree.readContent( - tagManagementModulePath + trackingTagManagementFeatureModulePath ); expect(tagManagementModule).toMatchSnapshot(); }); diff --git a/feature-libs/tracking/schematics/collection.json b/feature-libs/tracking/schematics/collection.json index 8d066241924..be0929b08ad 100644 --- a/feature-libs/tracking/schematics/collection.json +++ b/feature-libs/tracking/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-tracking/index#addTrackingFeatures", "description": "Add and configure Spartacus' tracking features", diff --git a/feature-libs/tracking/schematics/constants.ts b/feature-libs/tracking/schematics/constants.ts deleted file mode 100644 index fec5af43041..00000000000 --- a/feature-libs/tracking/schematics/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { SPARTACUS_TRACKING } from '@spartacus/schematics'; - -export const TRACKING_FOLDER_NAME = 'tracking'; - -export const PERSONALIZATION_MODULE_NAME = 'Personalization'; -export const PERSONALIZATION_FEATURE_NAME_CONSTANT = 'PERSONALIZATION_FEATURE'; -export const SPARTACUS_PERSONALIZATION = `${SPARTACUS_TRACKING}/personalization`; -export const SPARTACUS_PERSONALIZATION_ROOT = `${SPARTACUS_PERSONALIZATION}/root`; - -export const TMS_MODULE_NAME = 'TagManagement'; -export const TMS_CONFIG = 'TmsConfig'; -export const SPARTACUS_TMS_CORE = `${SPARTACUS_TRACKING}/tms/core`; - -export const SPARTACUS_TMS_GTM = `${SPARTACUS_TRACKING}/tms/gtm`; - -export const SPARTACUS_TMS_AEP = `${SPARTACUS_TRACKING}/tms/aep`; diff --git a/feature-libs/user/schematics/add-library/index.ts b/feature-libs/user/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/feature-libs/user/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/feature-libs/user/schematics/add-user/__snapshots__/index_spec.ts.snap b/feature-libs/user/schematics/add-user/__snapshots__/index_spec.ts.snap index 8002e2e141c..aae7270b2e1 100644 --- a/feature-libs/user/schematics/add-user/__snapshots__/index_spec.ts.snap +++ b/feature-libs/user/schematics/add-user/__snapshots__/index_spec.ts.snap @@ -1,22 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Spartacus User schematics: ng-add Account feature eager loading should import appropriate modules 1`] = ` +exports[`Spartacus User schematics: ng-add User Profile feature eager loading should import appropriate modules 1`] = ` "import { NgModule } from '@angular/core'; import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { UserAccountModule } from \\"@spartacus/user/account\\"; -import { userAccountTranslationChunksConfig, userAccountTranslations } from \\"@spartacus/user/account/assets\\"; -import { UserAccountRootModule } from \\"@spartacus/user/account/root\\"; +import { UserProfileModule } from \\"@spartacus/user/profile\\"; +import { userProfileTranslationChunksConfig, userProfileTranslations } from \\"@spartacus/user/profile/assets\\"; +import { UserProfileRootModule } from \\"@spartacus/user/profile/root\\"; @NgModule({ declarations: [], imports: [ - UserAccountRootModule, - UserAccountModule + UserProfileRootModule, + UserProfileModule ], providers: [provideConfig({ i18n: { - resources: userAccountTranslations, - chunks: userAccountTranslationChunksConfig, + resources: userProfileTranslations, + chunks: userProfileTranslationChunksConfig, }, })] }) @@ -24,29 +24,60 @@ export class UserFeatureModule { } " `; -exports[`Spartacus User schematics: ng-add Account feature general setup should add the feature using the lazy loading syntax 1`] = ` +exports[`Spartacus User schematics: ng-add User Profile feature general setup should NOT install the required feature dependencies 1`] = ` "import { NgModule } from '@angular/core'; import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { userAccountTranslationChunksConfig, userAccountTranslations } from \\"@spartacus/user/account/assets\\"; -import { UserAccountRootModule, USER_ACCOUNT_FEATURE } from \\"@spartacus/user/account/root\\"; +import { userProfileTranslationChunksConfig, userProfileTranslations } from \\"@spartacus/user/profile/assets\\"; +import { UserProfileRootModule, USER_PROFILE_FEATURE } from \\"@spartacus/user/profile/root\\"; @NgModule({ declarations: [], imports: [ - UserAccountRootModule + UserProfileRootModule ], providers: [provideConfig({ featureModules: { - [USER_ACCOUNT_FEATURE]: { + [USER_PROFILE_FEATURE]: { module: () => - import('@spartacus/user/account').then((m) => m.UserAccountModule), + import('@spartacus/user/profile').then((m) => m.UserProfileModule), }, } }), provideConfig({ i18n: { - resources: userAccountTranslations, - chunks: userAccountTranslationChunksConfig, + resources: userProfileTranslations, + chunks: userProfileTranslationChunksConfig, + }, + }) + ] +}) +export class UserFeatureModule { } +" +`; + +exports[`Spartacus User schematics: ng-add User Profile feature general setup should add the feature using the lazy loading syntax 1`] = ` +"import { NgModule } from '@angular/core'; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { userProfileTranslationChunksConfig, userProfileTranslations } from \\"@spartacus/user/profile/assets\\"; +import { UserProfileRootModule, USER_PROFILE_FEATURE } from \\"@spartacus/user/profile/root\\"; + +@NgModule({ + declarations: [], + imports: [ + UserProfileRootModule + ], + providers: [provideConfig({ + featureModules: { + [USER_PROFILE_FEATURE]: { + module: () => + import('@spartacus/user/profile').then((m) => m.UserProfileModule), + }, + } + }), + provideConfig({ + i18n: { + resources: userProfileTranslations, + chunks: userProfileTranslationChunksConfig, }, }) ] @@ -55,9 +86,9 @@ export class UserFeatureModule { } " `; -exports[`Spartacus User schematics: ng-add Account feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/user\\";"`; +exports[`Spartacus User schematics: ng-add User Profile feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/user\\";"`; -exports[`Spartacus User schematics: ng-add Account feature general setup styling should update angular.json 1`] = ` +exports[`Spartacus User schematics: ng-add User Profile feature general setup styling should update angular.json 1`] = ` "{ \\"$schema\\": \\"./node_modules/@angular/cli/lib/config/schema.json\\", \\"version\\": 1, @@ -183,23 +214,23 @@ exports[`Spartacus User schematics: ng-add Account feature general setup styling }" `; -exports[`Spartacus User schematics: ng-add Profile feature eager loading should import appropriate modules 1`] = ` +exports[`Spartacus User schematics: ng-add User-Account feature eager loading should import appropriate modules 1`] = ` "import { NgModule } from '@angular/core'; import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { UserProfileModule } from \\"@spartacus/user/profile\\"; -import { userProfileTranslationChunksConfig, userProfileTranslations } from \\"@spartacus/user/profile/assets\\"; -import { UserProfileRootModule } from \\"@spartacus/user/profile/root\\"; +import { UserAccountModule } from \\"@spartacus/user/account\\"; +import { userAccountTranslationChunksConfig, userAccountTranslations } from \\"@spartacus/user/account/assets\\"; +import { UserAccountRootModule } from \\"@spartacus/user/account/root\\"; @NgModule({ declarations: [], imports: [ - UserProfileRootModule, - UserProfileModule + UserAccountRootModule, + UserAccountModule ], providers: [provideConfig({ i18n: { - resources: userProfileTranslations, - chunks: userProfileTranslationChunksConfig, + resources: userAccountTranslations, + chunks: userAccountTranslationChunksConfig, }, })] }) @@ -207,29 +238,29 @@ export class UserFeatureModule { } " `; -exports[`Spartacus User schematics: ng-add Profile feature general setup should add the feature using the lazy loading syntax 1`] = ` +exports[`Spartacus User schematics: ng-add User-Account feature general setup should add the feature using the lazy loading syntax 1`] = ` "import { NgModule } from '@angular/core'; import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { userProfileTranslationChunksConfig, userProfileTranslations } from \\"@spartacus/user/profile/assets\\"; -import { UserProfileRootModule, USER_PROFILE_FEATURE } from \\"@spartacus/user/profile/root\\"; +import { userAccountTranslationChunksConfig, userAccountTranslations } from \\"@spartacus/user/account/assets\\"; +import { UserAccountRootModule, USER_ACCOUNT_FEATURE } from \\"@spartacus/user/account/root\\"; @NgModule({ declarations: [], imports: [ - UserProfileRootModule + UserAccountRootModule ], providers: [provideConfig({ featureModules: { - [USER_PROFILE_FEATURE]: { + [USER_ACCOUNT_FEATURE]: { module: () => - import('@spartacus/user/profile').then((m) => m.UserProfileModule), + import('@spartacus/user/account').then((m) => m.UserAccountModule), }, } }), provideConfig({ i18n: { - resources: userProfileTranslations, - chunks: userProfileTranslationChunksConfig, + resources: userAccountTranslations, + chunks: userAccountTranslationChunksConfig, }, }) ] @@ -238,9 +269,9 @@ export class UserFeatureModule { } " `; -exports[`Spartacus User schematics: ng-add Profile feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/user\\";"`; +exports[`Spartacus User schematics: ng-add User-Account feature general setup styling should create a proper scss file 1`] = `"@import \\"@spartacus/user\\";"`; -exports[`Spartacus User schematics: ng-add Profile feature general setup styling should update angular.json 1`] = ` +exports[`Spartacus User schematics: ng-add User-Account feature general setup styling should update angular.json 1`] = ` "{ \\"$schema\\": \\"./node_modules/@angular/cli/lib/config/schema.json\\", \\"version\\": 1, diff --git a/feature-libs/user/schematics/add-user/index.ts b/feature-libs/user/schematics/add-user/index.ts index 30ba1dac83a..27bd407e350 100644 --- a/feature-libs/user/schematics/add-user/index.ts +++ b/feature-libs/user/schematics/add-user/index.ts @@ -1,121 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_USER_ACCOUNT_FEATURE, - CLI_USER_PROFILE_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusUserOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_USER, - USER_ACCOUNT_MODULE, - USER_ACCOUNT_ROOT_MODULE, - USER_PROFILE_MODULE, - USER_PROFILE_ROOT_MODULE, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - SCSS_FILE_NAME, - SPARTACUS_USER_ACCOUNT, - SPARTACUS_USER_ACCOUNT_ASSETS, - SPARTACUS_USER_ACCOUNT_ROOT, - SPARTACUS_USER_PROFILE, - SPARTACUS_USER_PROFILE_ASSETS, - SPARTACUS_USER_PROFILE_ROOT, - USER_ACCOUNT_FEATURE_NAME_CONSTANT, - USER_ACCOUNT_TRANSLATIONS, - USER_ACCOUNT_TRANSLATION_CHUNKS_CONFIG, - USER_FOLDER_NAME, - USER_MODULE_NAME, - USER_PROFILE_FEATURE_NAME_CONSTANT, - USER_PROFILE_TRANSLATIONS, - USER_PROFILE_TRANSLATION_CHUNKS_CONFIG, -} from '../constants'; export function addUserFeatures(options: SpartacusUserOptions): Rule { return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ - addPackageJsonDependenciesForLibrary(peerDependencies, options), + analyzeApplication(options, features), - shouldAddFeature(CLI_USER_ACCOUNT_FEATURE, options.features) - ? addAccountFeature(options) - : noop(), + addFeatures(options, features), + addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_USER_PROFILE_FEATURE, options.features) - ? addProfileFeature(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addAccountFeature(options: SpartacusUserOptions): Rule { - return addLibraryFeature(options, { - folderName: USER_FOLDER_NAME, - moduleName: USER_MODULE_NAME, - featureModule: { - name: USER_ACCOUNT_MODULE, - importPath: SPARTACUS_USER_ACCOUNT, - }, - rootModule: { - name: USER_ACCOUNT_ROOT_MODULE, - importPath: SPARTACUS_USER_ACCOUNT_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_USER_ACCOUNT_ROOT, - namedImports: [USER_ACCOUNT_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: USER_ACCOUNT_TRANSLATIONS, - chunks: USER_ACCOUNT_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_USER_ACCOUNT_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_USER, - }, - }); -} - -function addProfileFeature(options: SpartacusUserOptions): Rule { - return addLibraryFeature(options, { - folderName: USER_FOLDER_NAME, - moduleName: USER_MODULE_NAME, - featureModule: { - name: USER_PROFILE_MODULE, - importPath: SPARTACUS_USER_PROFILE, - }, - rootModule: { - name: USER_PROFILE_ROOT_MODULE, - importPath: SPARTACUS_USER_PROFILE_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_USER_PROFILE_ROOT, - namedImports: [USER_PROFILE_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: USER_PROFILE_TRANSLATIONS, - chunks: USER_PROFILE_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_USER_PROFILE_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_USER, - }, - dependencyManagement: { - featureName: CLI_USER_PROFILE_FEATURE, - featureDependencies: { - [SPARTACUS_USER]: [CLI_USER_ACCOUNT_FEATURE], - }, - }, - }); -} diff --git a/feature-libs/user/schematics/add-user/index_spec.ts b/feature-libs/user/schematics/add-user/index_spec.ts index 25a446c93cd..9998b427aaf 100644 --- a/feature-libs/user/schematics/add-user/index_spec.ts +++ b/feature-libs/user/schematics/add-user/index_spec.ts @@ -1,6 +1,5 @@ /// -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; import { SchematicTestRunner, UnitTestTree, @@ -11,24 +10,25 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_USER_ACCOUNT_FEATURE, - CLI_USER_PROFILE_FEATURE, - LibraryOptions, LibraryOptions as SpartacusUserOptions, SpartacusOptions, SPARTACUS_SCHEMATICS, SPARTACUS_USER, + userFeatureModulePath, + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/user/user-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/user.scss'; describe('Spartacus User schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_USER, + collectionPath + ); let appTree: UnitTestTree; @@ -61,12 +61,12 @@ describe('Spartacus User schematics: ng-add', () => { const accountFeatureOptions: SpartacusUserOptions = { ...libraryNoFeaturesOptions, - features: [CLI_USER_ACCOUNT_FEATURE], + features: [USER_ACCOUNT_FEATURE_NAME], }; const profileFeatureOptions: SpartacusUserOptions = { ...libraryNoFeaturesOptions, - features: [CLI_USER_PROFILE_FEATURE], + features: [USER_PROFILE_FEATURE_NAME], }; beforeEach(async () => { @@ -108,7 +108,7 @@ describe('Spartacus User schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(userFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -137,7 +137,7 @@ describe('Spartacus User schematics: ng-add', () => { }); }); - describe('Account feature', () => { + describe('User-Account feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner @@ -146,7 +146,7 @@ describe('Spartacus User schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(userFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -175,13 +175,13 @@ describe('Spartacus User schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(userFeatureModulePath); expect(module).toMatchSnapshot(); }); }); }); - describe('Profile feature', () => { + describe('User Profile feature', () => { describe('general setup', () => { beforeEach(async () => { appTree = await schematicRunner @@ -190,7 +190,7 @@ describe('Spartacus User schematics: ng-add', () => { }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(userFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -206,24 +206,9 @@ describe('Spartacus User schematics: ng-add', () => { }); }); - it('should run the proper installation tasks', async () => { - const tasks = schematicRunner.tasks - .filter((task) => task.name === 'run-schematic') - .map( - (task) => task.options as RunSchematicTaskOptions - ); - expect(tasks.length).toEqual(1); - - const userTaskWithSubFeatures = tasks[0]; - expect(userTaskWithSubFeatures).toBeTruthy(); - expect(userTaskWithSubFeatures.name).toEqual('add-spartacus-library'); - expect(userTaskWithSubFeatures.options).toHaveProperty( - 'collection', - SPARTACUS_USER - ); - expect(userTaskWithSubFeatures.options.options?.features).toEqual([ - CLI_USER_ACCOUNT_FEATURE, - ]); + it('should NOT install the required feature dependencies', async () => { + const featureModule = appTree.readContent(userFeatureModulePath); + expect(featureModule).toMatchSnapshot(); }); }); @@ -239,7 +224,7 @@ describe('Spartacus User schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(userFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/feature-libs/user/schematics/add-user/schema.json b/feature-libs/user/schematics/add-user/schema.json index 9ffb0c362ab..aaed8f89f01 100644 --- a/feature-libs/user/schematics/add-user/schema.json +++ b/feature-libs/user/schematics/add-user/schema.json @@ -26,11 +26,11 @@ "type": "array", "uniqueItems": true, "items": { - "enum": ["Account", "Profile"], + "enum": ["User-Account", "User-Profile"], "type": "string" }, - "default": ["Account", "Profile"], - "x-prompt": "Which features would you like to set up from the User library? Please note that for most Spartacus features to be properly configured, the Account feature is required." + "default": ["User-Account", "User-Profile"], + "x-prompt": "Which features would you like to set up from the User library? Please note that for most Spartacus features to be properly configured, the User-Account feature is required." } }, "required": [] diff --git a/feature-libs/user/schematics/collection.json b/feature-libs/user/schematics/collection.json index 04ee7b6b254..4cb0f30c5be 100644 --- a/feature-libs/user/schematics/collection.json +++ b/feature-libs/user/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-user/index#addUserFeatures", "description": "Add and configure Spartacus' user features", diff --git a/feature-libs/user/schematics/constants.ts b/feature-libs/user/schematics/constants.ts deleted file mode 100644 index cbbe00cbeea..00000000000 --- a/feature-libs/user/schematics/constants.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { SPARTACUS_USER } from '@spartacus/schematics'; - -export const USER_FOLDER_NAME = 'user'; -export const SCSS_FILE_NAME = 'user.scss'; -export const USER_MODULE_NAME = 'User'; - -export const USER_ACCOUNT_FEATURE_NAME_CONSTANT = 'USER_ACCOUNT_FEATURE'; -export const SPARTACUS_USER_ACCOUNT = `${SPARTACUS_USER}/account`; -export const SPARTACUS_USER_ACCOUNT_ROOT = `${SPARTACUS_USER_ACCOUNT}/root`; -export const SPARTACUS_USER_ACCOUNT_ASSETS = `${SPARTACUS_USER_ACCOUNT}/assets`; -export const USER_ACCOUNT_TRANSLATIONS = 'userAccountTranslations'; -export const USER_ACCOUNT_TRANSLATION_CHUNKS_CONFIG = - 'userAccountTranslationChunksConfig'; - -export const USER_PROFILE_FEATURE_NAME_CONSTANT = 'USER_PROFILE_FEATURE'; -export const SPARTACUS_USER_PROFILE = `${SPARTACUS_USER}/profile`; -export const SPARTACUS_USER_PROFILE_ROOT = `${SPARTACUS_USER_PROFILE}/root`; -export const SPARTACUS_USER_PROFILE_ASSETS = `${SPARTACUS_USER_PROFILE}/assets`; -export const USER_PROFILE_TRANSLATIONS = 'userProfileTranslations'; -export const USER_PROFILE_TRANSLATION_CHUNKS_CONFIG = - 'userProfileTranslationChunksConfig'; diff --git a/integration-libs/cdc/schematics/add-cdc/__snapshots__/index_spec.ts.snap b/integration-libs/cdc/schematics/add-cdc/__snapshots__/index_spec.ts.snap index 77ac0498a87..bdb3dc6cb2f 100644 --- a/integration-libs/cdc/schematics/add-cdc/__snapshots__/index_spec.ts.snap +++ b/integration-libs/cdc/schematics/add-cdc/__snapshots__/index_spec.ts.snap @@ -91,69 +91,3 @@ import { CmsConfig, provideConfig } from \\"@spartacus/core\\"; export class CdcFeatureModule { } " `; - -exports[`Spartacus CDC schematics: ng-add CDC feature warning for blank jsSDKUrl should set the default JS_SDK_URL_PLACEHOLDER 1`] = ` -"import { NgModule } from '@angular/core'; -import { CdcConfig, CdcRootModule, CDC_FEATURE } from \\"@spartacus/cdc/root\\"; -import { CmsConfig, provideConfig } from \\"@spartacus/core\\"; - -@NgModule({ - declarations: [], - imports: [ - CdcRootModule - ], - providers: [provideConfig({ - featureModules: { - [CDC_FEATURE]: { - module: () => - import('@spartacus/cdc').then((m) => m.CdcModule), - }, - } - }), - provideConfig({ - cdc: [ - { - baseSite: 'electronics-spa', - javascriptUrl: 'JS_SDK_URL_PLACEHOLDER', - sessionExpiration: 3600 - }, - ], - }) - ] -}) -export class CdcFeatureModule { } -" -`; - -exports[`Spartacus CDC schematics: ng-add CDC feature warning for missing jsSDKUrl should set the default JS_SDK_URL_PLACEHOLDER 1`] = ` -"import { NgModule } from '@angular/core'; -import { CdcConfig, CdcRootModule, CDC_FEATURE } from \\"@spartacus/cdc/root\\"; -import { CmsConfig, provideConfig } from \\"@spartacus/core\\"; - -@NgModule({ - declarations: [], - imports: [ - CdcRootModule - ], - providers: [provideConfig({ - featureModules: { - [CDC_FEATURE]: { - module: () => - import('@spartacus/cdc').then((m) => m.CdcModule), - }, - } - }), - provideConfig({ - cdc: [ - { - baseSite: 'electronics-spa', - javascriptUrl: 'JS_SDK_URL_PLACEHOLDER', - sessionExpiration: 3600 - }, - ], - }) - ] -}) -export class CdcFeatureModule { } -" -`; diff --git a/integration-libs/cdc/schematics/add-cdc/index.ts b/integration-libs/cdc/schematics/add-cdc/index.ts index 579aad8fe29..f7fb2d499bd 100644 --- a/integration-libs/cdc/schematics/add-cdc/index.ts +++ b/integration-libs/cdc/schematics/add-cdc/index.ts @@ -1,95 +1,37 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CDC_MODULE, - CDC_ROOT_MODULE, - CLI_CDC_FEATURE, - CLI_USER_PROFILE_FEATURE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, readPackageJson, - shouldAddFeature, - SPARTACUS_CDC, - SPARTACUS_USER, + SpartacusCdcOptions, validateSpartacusInstallation, } from '@spartacus/schematics'; -import { Schema as SpartacusCdcOptions } from './schema'; import { peerDependencies } from '../../package.json'; -import { - CDC_CONFIG, - CDC_FEATURE_CONSTANT, - CDC_FOLDER_NAME, - CDC_MODULE_NAME, - SPARTACUS_CDC_ROOT, -} from '../constants'; export function addCdcFeature(options: SpartacusCdcOptions): Rule { - return (tree: Tree, context: SchematicContext): Rule => { + return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_CDC_FEATURE, options.features) - ? addCdc(options, context) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addCdc(options: SpartacusCdcOptions, context: SchematicContext): Rule { - if (!options.javascriptUrl) { - context.logger.warn( - `CDC JS SDK URL is not provided. Please run the schematic again, or make sure you update the javascriptUrl.` - ); - } - - return addLibraryFeature(options, { - folderName: CDC_FOLDER_NAME, - moduleName: CDC_MODULE_NAME, - rootModule: { - importPath: SPARTACUS_CDC_ROOT, - name: CDC_ROOT_MODULE, - content: `${CDC_ROOT_MODULE}`, - }, - featureModule: { - importPath: SPARTACUS_CDC, - name: CDC_MODULE, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CDC_ROOT, - namedImports: [CDC_FEATURE_CONSTANT], - }, - customConfig: { - import: [ - { - moduleSpecifier: SPARTACUS_CDC_ROOT, - namedImports: [CDC_CONFIG], - }, - ], - content: `<${CDC_CONFIG}>{ - cdc: [ - { - baseSite: '${options.baseSite}', - javascriptUrl: '${ - options.javascriptUrl || 'JS_SDK_URL_PLACEHOLDER' - }', - sessionExpiration: ${options.sessionExpiration} - }, - ], - }`, - }, - dependencyManagement: { - featureName: CLI_CDC_FEATURE, - featureDependencies: { - [SPARTACUS_USER]: [CLI_USER_PROFILE_FEATURE], - }, - }, - }); -} diff --git a/integration-libs/cdc/schematics/add-cdc/index_spec.ts b/integration-libs/cdc/schematics/add-cdc/index_spec.ts index 5f68058d9e1..96fa3615615 100644 --- a/integration-libs/cdc/schematics/add-cdc/index_spec.ts +++ b/integration-libs/cdc/schematics/add-cdc/index_spec.ts @@ -1,6 +1,5 @@ /// -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; import { SchematicTestRunner, UnitTestTree, @@ -11,12 +10,12 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_CDC_FEATURE, - CLI_USER_PROFILE_FEATURE, - LibraryOptions, + cdcFeatureModulePath, + CDC_FEATURE_NAME, LibraryOptions as SpartacusCdcOptions, SpartacusOptions, SPARTACUS_ASM, + SPARTACUS_CDC, SPARTACUS_SCHEMATICS, SPARTACUS_USER, } from '@spartacus/schematics'; @@ -24,11 +23,12 @@ import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/cdc/cdc-feature.module.ts'; describe('Spartacus CDC schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_CDC, + collectionPath + ); let appTree: UnitTestTree; @@ -61,7 +61,7 @@ describe('Spartacus CDC schematics: ng-add', () => { const cdcFeatureOptions: SpartacusCdcOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CDC_FEATURE], + features: [CDC_FEATURE_NAME], }; beforeEach(async () => { @@ -117,67 +117,11 @@ describe('Spartacus CDC schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(cdcFeatureModulePath)).toBeFalsy(); }); }); describe('CDC feature', () => { - describe('warning for missing jsSDKUrl', () => { - let firstMessage: string | undefined; - beforeEach(async () => { - schematicRunner.logger.subscribe((log) => { - if (!firstMessage) { - firstMessage = log.message; - } - }); - - appTree = await schematicRunner - .runSchematicAsync('ng-add', { ...cdcFeatureOptions }, appTree) - .toPromise(); - }); - - it('should show the warning', () => { - expect(firstMessage).toEqual( - `CDC JS SDK URL is not provided. Please run the schematic again, or make sure you update the javascriptUrl.` - ); - }); - - it('should set the default JS_SDK_URL_PLACEHOLDER', async () => { - const module = appTree.readContent(featureModulePath); - expect(module).toMatchSnapshot(); - }); - }); - - describe('warning for blank jsSDKUrl', () => { - let firstMessage: string | undefined; - beforeEach(async () => { - schematicRunner.logger.subscribe((log) => { - if (!firstMessage) { - firstMessage = log.message; - } - }); - - appTree = await schematicRunner - .runSchematicAsync( - 'ng-add', - { ...cdcFeatureOptions, javascriptUrl: '' }, - appTree - ) - .toPromise(); - }); - - it('should show the warning', () => { - expect(firstMessage).toEqual( - `CDC JS SDK URL is not provided. Please run the schematic again, or make sure you update the javascriptUrl.` - ); - }); - - it('should set the default JS_SDK_URL_PLACEHOLDER', async () => { - const module = appTree.readContent(featureModulePath); - expect(module).toMatchSnapshot(); - }); - }); - describe('validation of jsSDKUrl', () => { beforeEach(async () => { appTree = await schematicRunner @@ -190,8 +134,8 @@ describe('Spartacus CDC schematics: ng-add', () => { }); it('should set the given javascriptUrl', async () => { - const module = appTree.readContent(featureModulePath); - expect(module).toMatchSnapshot(); + const featureModule = appTree.readContent(cdcFeatureModulePath); + expect(featureModule).toMatchSnapshot(); }); }); @@ -227,41 +171,9 @@ describe('Spartacus CDC schematics: ng-add', () => { } }); - it('should run the proper installation tasks', async () => { - const tasks = schematicRunner.tasks - .filter((task) => task.name === 'run-schematic') - .map( - (task) => task.options as RunSchematicTaskOptions - ); - expect(tasks.length).toEqual(3); - - const asmTask = tasks[0]; - expect(asmTask).toBeTruthy(); - expect(asmTask.name).toEqual('add-spartacus-library'); - expect(asmTask.options).toHaveProperty('collection', SPARTACUS_ASM); - expect(asmTask.options.options?.features).toEqual([]); - - const userTask = tasks[1]; - expect(userTask).toBeTruthy(); - expect(userTask.name).toEqual('add-spartacus-library'); - expect(userTask.options).toHaveProperty('collection', SPARTACUS_USER); - expect(userTask.options.options?.features).toEqual([]); - - const userTaskWithSubFeatures = tasks[2]; - expect(userTaskWithSubFeatures).toBeTruthy(); - expect(userTaskWithSubFeatures.name).toEqual('add-spartacus-library'); - expect(userTaskWithSubFeatures.options).toHaveProperty( - 'collection', - SPARTACUS_USER - ); - expect(userTaskWithSubFeatures.options.options?.features).toEqual([ - CLI_USER_PROFILE_FEATURE, - ]); - }); - it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); - expect(module).toMatchSnapshot(); + const featureModule = appTree.readContent(cdcFeatureModulePath); + expect(featureModule).toMatchSnapshot(); }); }); @@ -277,8 +189,8 @@ describe('Spartacus CDC schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); - expect(module).toMatchSnapshot(); + const featureModule = appTree.readContent(cdcFeatureModulePath); + expect(featureModule).toMatchSnapshot(); }); }); }); diff --git a/integration-libs/cdc/schematics/add-cdc/schema.ts b/integration-libs/cdc/schematics/add-cdc/schema.ts deleted file mode 100644 index 0c7423a066e..00000000000 --- a/integration-libs/cdc/schematics/add-cdc/schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LibraryOptions } from '@spartacus/schematics'; - -export interface Schema extends LibraryOptions { - baseSite: string; - javascriptUrl: string; - sessionExpiration: number; -} diff --git a/integration-libs/cdc/schematics/add-library/index.ts b/integration-libs/cdc/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/integration-libs/cdc/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/integration-libs/cdc/schematics/collection.json b/integration-libs/cdc/schematics/collection.json index 6ad143828ca..b60185c140c 100644 --- a/integration-libs/cdc/schematics/collection.json +++ b/integration-libs/cdc/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-cdc/index#addCdcFeature", "description": "Add and configure Spartacus' CDC feature", diff --git a/integration-libs/cdc/schematics/constants.ts b/integration-libs/cdc/schematics/constants.ts deleted file mode 100644 index ab55b9eef4d..00000000000 --- a/integration-libs/cdc/schematics/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SPARTACUS_CDC } from '@spartacus/schematics'; - -export const CDC_FOLDER_NAME = 'cdc'; -export const CDC_MODULE_NAME = 'Cdc'; - -export const CDC_FEATURE_CONSTANT = 'CDC_FEATURE'; -export const CDC_CONFIG = 'CdcConfig'; -export const SPARTACUS_CDC_ROOT = `${SPARTACUS_CDC}/root`; diff --git a/integration-libs/cds/src/schematics/add-cds/__snapshots__/index_spec.ts.snap b/integration-libs/cds/src/schematics/add-cds/__snapshots__/index_spec.ts.snap index 81f74f3a510..e1aaf702660 100644 --- a/integration-libs/cds/src/schematics/add-cds/__snapshots__/index_spec.ts.snap +++ b/integration-libs/cds/src/schematics/add-cds/__snapshots__/index_spec.ts.snap @@ -60,7 +60,19 @@ import { provideConfig } from \\"@spartacus/core\\"; defaultCarouselViewportThreshold: 80, }, }, - })] + }), + provideConfig({ + cds: { + profileTag: { + javascriptUrl: + 'PROFILE_TAG_LOAD_URL_PLACEHOLDER', + configUrl: + 'PROFILE_TAG_CONFIG_URL_PLACEHOLDER', + allowInsecureCookies: true, + }, + }, + }) + ] }) export class CdsFeatureModule { } " diff --git a/integration-libs/cds/src/schematics/add-cds/index.ts b/integration-libs/cds/src/schematics/add-cds/index.ts index 896e9da6f77..5c295bd6fd8 100644 --- a/integration-libs/cds/src/schematics/add-cds/index.ts +++ b/integration-libs/cds/src/schematics/add-cds/index.ts @@ -1,136 +1,37 @@ import { chain, - noop, Rule, SchematicContext, - SchematicsException, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CDS_CONFIG, - CDS_MODULE, - CLI_CDS_FEATURE, - CLI_TRACKING_PERSONALIZATION_FEATURE, - CustomConfig, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, readPackageJson, - shouldAddFeature, - SPARTACUS_CDS, - SPARTACUS_TRACKING, + SpartacusCdsOptions, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../../package.json'; -import { CDS_FOLDER_NAME, CDS_MODULE_NAME } from '../constants'; -import { Schema as SpartacusCdsOptions } from './schema'; export function addCdsFeature(options: SpartacusCdsOptions): Rule { - return (tree: Tree, context: SchematicContext) => { + return (tree: Tree, _context: SchematicContext) => { const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_CDS_FEATURE, options.features) - ? addCds(options, context) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addCds(options: SpartacusCdsOptions, context: SchematicContext): Rule { - validateCdsOptions(options, context); - - const customConfig: CustomConfig[] = [ - { - import: [ - { - moduleSpecifier: SPARTACUS_CDS, - namedImports: [CDS_CONFIG], - }, - ], - content: `<${CDS_CONFIG}>{ - cds: { - tenant: '${options.tenant}', - baseUrl: '${options.baseUrl}', - endpoints: { - strategyProducts: '/strategy/\${tenant}/strategies/\${strategyId}/products', - }, - merchandising: { - defaultCarouselViewportThreshold: 80, - }, - }, - }`, - }, - ]; - if (options.profileTagLoadUrl && options.profileTagConfigUrl) { - customConfig.push({ - import: [ - { - moduleSpecifier: SPARTACUS_CDS, - namedImports: [CDS_CONFIG], - }, - ], - content: `<${CDS_CONFIG}>{ - cds: { - profileTag: { - javascriptUrl: - '${options.profileTagLoadUrl}', - configUrl: - '${options.profileTagConfigUrl}', - allowInsecureCookies: true, - }, - }, - }`, - }); - } - - return addLibraryFeature( - { ...options, lazy: false }, - { - folderName: CDS_FOLDER_NAME, - moduleName: CDS_MODULE_NAME, - featureModule: { - importPath: SPARTACUS_CDS, - name: CDS_MODULE, - content: `${CDS_MODULE}.forRoot()`, - }, - customConfig, - dependencyManagement: { - featureName: CLI_CDS_FEATURE, - featureDependencies: { - [SPARTACUS_TRACKING]: [CLI_TRACKING_PERSONALIZATION_FEATURE], - }, - }, - } - ); -} - -function validateCdsOptions( - { - tenant, - baseUrl, - profileTagConfigUrl, - profileTagLoadUrl, - }: SpartacusCdsOptions, - context: SchematicContext -): void { - if (!tenant) { - throw new SchematicsException(`Please specify tenant name.`); - } - if (!baseUrl) { - throw new SchematicsException(`Please specify the base URL.`); - } - - if ( - !( - (profileTagConfigUrl && profileTagLoadUrl) || - (!profileTagConfigUrl && !profileTagLoadUrl) - ) - ) { - context.logger.warn( - `Profile tag will not be added. Please run the schematic again, and make sure you provide both profile tag options.` - ); - } -} diff --git a/integration-libs/cds/src/schematics/add-cds/index_spec.ts b/integration-libs/cds/src/schematics/add-cds/index_spec.ts index d9e51617b0d..4578d13ff98 100644 --- a/integration-libs/cds/src/schematics/add-cds/index_spec.ts +++ b/integration-libs/cds/src/schematics/add-cds/index_spec.ts @@ -1,6 +1,5 @@ /// -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; import { SchematicTestRunner, UnitTestTree, @@ -11,25 +10,25 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_CDS_FEATURE, - CLI_TRACKING_PERSONALIZATION_FEATURE, - LibraryOptions, + cdsFeatureModulePath, + CDS_FEATURE_NAME, + SpartacusCdsOptions, SpartacusOptions, - SPARTACUS_CART, - SPARTACUS_ORDER, + SPARTACUS_CDS, SPARTACUS_SCHEMATICS, - SPARTACUS_TRACKING, + trackingPersonalizationFeatureModulePath, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../../package.json'; -import { Schema as SpartacusCdsOptions } from './schema'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/cds/cds-feature.module.ts'; describe('Spartacus CDS schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_CDS, + collectionPath + ); let appTree: UnitTestTree; @@ -64,7 +63,7 @@ describe('Spartacus CDS schematics: ng-add', () => { const cdsFeatureOptions: SpartacusCdsOptions = { ...libraryNoFeaturesOptions, - features: [CLI_CDS_FEATURE], + features: [CDS_FEATURE_NAME], }; beforeEach(async () => { @@ -106,7 +105,7 @@ describe('Spartacus CDS schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(cdsFeatureModulePath)).toBeFalsy(); }); it('should install necessary Spartacus libraries', () => { @@ -145,79 +144,18 @@ describe('Spartacus CDS schematics: ng-add', () => { }); it('should create the feature module', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(cdsFeatureModulePath); expect(module).toMatchSnapshot(); }); - it('should run the proper installation tasks', async () => { - const tasks = schematicRunner.tasks - .filter((task) => task.name === 'run-schematic') - .map( - (task) => task.options as RunSchematicTaskOptions - ); - expect(tasks.length).toEqual(4); - - const cartTask = tasks[0]; - expect(cartTask).toBeTruthy(); - expect(cartTask.name).toEqual('add-spartacus-library'); - expect(cartTask.options).toHaveProperty('collection', SPARTACUS_CART); - expect(cartTask.options.options?.features).toEqual([]); - - const orderTask = tasks[1]; - expect(orderTask).toBeTruthy(); - expect(orderTask.name).toEqual('add-spartacus-library'); - expect(orderTask.options).toHaveProperty( - 'collection', - SPARTACUS_ORDER - ); - expect(orderTask.options.options?.features).toEqual([]); - - const trackingTask = tasks[2]; - expect(trackingTask).toBeTruthy(); - expect(trackingTask.name).toEqual('add-spartacus-library'); - expect(trackingTask.options).toHaveProperty( - 'collection', - SPARTACUS_TRACKING - ); - expect(trackingTask.options.options?.features).toEqual([]); - - const trackingTaskWithSubFeatures = tasks[3]; - expect(trackingTaskWithSubFeatures).toBeTruthy(); - expect(trackingTaskWithSubFeatures.name).toEqual( - 'add-spartacus-library' - ); - expect(trackingTaskWithSubFeatures.options).toHaveProperty( - 'collection', - SPARTACUS_TRACKING - ); - expect(trackingTaskWithSubFeatures.options.options?.features).toEqual( - [CLI_TRACKING_PERSONALIZATION_FEATURE] - ); - }); - }); - - describe('validation', () => { - let firstMessage: string | undefined; - beforeEach(async () => { - schematicRunner.logger.subscribe((log) => { - if (!firstMessage) { - firstMessage = log.message; - } - }); - - appTree = await schematicRunner - .runSchematicAsync( - 'ng-add', - { ...cdsFeatureOptions, profileTagConfigUrl: 'xxx' }, - appTree - ) - .toPromise(); - }); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); - it('show the warning', () => { - expect(firstMessage).toEqual( - `Profile tag will not be added. Please run the schematic again, and make sure you provide both profile tag options.` + const trackingPersonalizationFeatureModule = appTree.readContent( + trackingPersonalizationFeatureModulePath ); + expect(trackingPersonalizationFeatureModule).toBeFalsy(); }); }); }); @@ -239,9 +177,19 @@ describe('Spartacus CDS schematics: ng-add', () => { describe('general setup', () => { it('should create the feature module', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(cdsFeatureModulePath); expect(module).toMatchSnapshot(); }); + + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); + + const trackingPersonalizationFeatureModule = appTree.readContent( + trackingPersonalizationFeatureModulePath + ); + expect(trackingPersonalizationFeatureModule).toBeFalsy(); + }); }); }); }); diff --git a/integration-libs/cds/src/schematics/add-cds/schema.json b/integration-libs/cds/src/schematics/add-cds/schema.json index f6f7df949e4..c174375e4da 100644 --- a/integration-libs/cds/src/schematics/add-cds/schema.json +++ b/integration-libs/cds/src/schematics/add-cds/schema.json @@ -11,6 +11,16 @@ "$source": "projectName" } }, + "debug": { + "description": "Display additional details during the running process.", + "type": "boolean", + "default": false + }, + "lazy": { + "type": "boolean", + "description": "Lazy load the user features.", + "default": true + }, "features": { "type": "array", "uniqueItems": true, diff --git a/integration-libs/cds/src/schematics/add-cds/schema.ts b/integration-libs/cds/src/schematics/add-cds/schema.ts deleted file mode 100644 index a6ceca1442a..00000000000 --- a/integration-libs/cds/src/schematics/add-cds/schema.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { LibraryOptions } from '@spartacus/schematics'; - -export interface Schema extends LibraryOptions { - tenant: string; - baseUrl: string; - profileTagLoadUrl?: string; - profileTagConfigUrl?: string; -} diff --git a/integration-libs/cds/src/schematics/add-library/index.ts b/integration-libs/cds/src/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/integration-libs/cds/src/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/integration-libs/cds/src/schematics/collection.json b/integration-libs/cds/src/schematics/collection.json index 58d8e08d9ee..1efbdf2333c 100644 --- a/integration-libs/cds/src/schematics/collection.json +++ b/integration-libs/cds/src/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-cds/index#addCdsFeature", "description": "Add and configure Spartacus' CDS feature", diff --git a/integration-libs/cds/src/schematics/constants.ts b/integration-libs/cds/src/schematics/constants.ts deleted file mode 100644 index 28051b73906..00000000000 --- a/integration-libs/cds/src/schematics/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const CDS_FOLDER_NAME = 'cds'; -export const CDS_MODULE_NAME = 'Cds'; diff --git a/integration-libs/digital-payments/schematics/add-digital-payments/__snapshots__/index_spec.ts.snap b/integration-libs/digital-payments/schematics/add-digital-payments/__snapshots__/index_spec.ts.snap index abd467c99c7..085b9188378 100644 --- a/integration-libs/digital-payments/schematics/add-digital-payments/__snapshots__/index_spec.ts.snap +++ b/integration-libs/digital-payments/schematics/add-digital-payments/__snapshots__/index_spec.ts.snap @@ -24,23 +24,14 @@ export class DigitalPaymentsFeatureModule { } exports[`Spartacus Digital-Payments schematics: ng-add Digital-Payments feature general setup should add the feature using the lazy loading syntax 1`] = ` "import { NgModule } from '@angular/core'; -import { CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; -import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; import { dpTranslationChunksConfig, dpTranslations } from \\"@spartacus/digital-payments/assets\\"; @NgModule({ declarations: [], imports: [ ], - providers: [provideConfig({ - featureModules: { - [CHECKOUT_FEATURE]: { - module: () => - import('@spartacus/digital-payments').then((m) => m.DigitalPaymentsModule), - }, - } - }), - provideConfig({ + providers: [provideConfig({ i18n: { resources: dpTranslations, chunks: dpTranslationChunksConfig, @@ -51,3 +42,19 @@ import { dpTranslationChunksConfig, dpTranslations } from \\"@spartacus/digital- export class DigitalPaymentsFeatureModule { } " `; + +exports[`Spartacus Digital-Payments schematics: ng-add Digital-Payments feature general setup should add the feature using the lazy loading syntax 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; + +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + DigitalPaymentsModule + ] +}) +export class CheckoutWrapperModule { } +" +`; diff --git a/integration-libs/digital-payments/schematics/add-digital-payments/index.ts b/integration-libs/digital-payments/schematics/add-digital-payments/index.ts index 6b8ea2b19f1..c7b736389a6 100644 --- a/integration-libs/digital-payments/schematics/add-digital-payments/index.ts +++ b/integration-libs/digital-payments/schematics/add-digital-payments/index.ts @@ -1,33 +1,20 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CHECKOUT_BASE_FEATURE_NAME_CONSTANT, - CLI_CHECKOUT_BASE_FEATURE, - CLI_DIGITAL_PAYMENTS_FEATURE, - DIGITAL_PAYMENTS_MODULE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, LibraryOptions as SpartacusDigitalPaymentsOptions, readPackageJson, - shouldAddFeature, - SPARTACUS_CHECKOUT, - SPARTACUS_CHECKOUT_BASE_ROOT, - SPARTACUS_DIGITAL_PAYMENTS, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - DIGITAL_PAYMENTS_FOLDER_NAME, - DIGITAL_PAYMENTS_MODULE_NAME, - DIGITAL_PAYMENTS_TRANSLATIONS, - DIGITAL_PAYMENTS_TRANSLATION_CHUNKS_CONFIG, - SPARTACUS_DIGITAL_PAYMENTS_ASSETS, -} from '../constants'; export function addDigitalPaymentsFeature( options: SpartacusDigitalPaymentsOptions @@ -36,38 +23,17 @@ export function addDigitalPaymentsFeature( const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_DIGITAL_PAYMENTS_FEATURE, options.features) - ? addDigitalPayments(options) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addDigitalPayments(options: SpartacusDigitalPaymentsOptions): Rule { - return addLibraryFeature(options, { - folderName: DIGITAL_PAYMENTS_FOLDER_NAME, - moduleName: DIGITAL_PAYMENTS_MODULE_NAME, - featureModule: { - name: DIGITAL_PAYMENTS_MODULE, - importPath: SPARTACUS_DIGITAL_PAYMENTS, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_CHECKOUT_BASE_ROOT, - namedImports: [CHECKOUT_BASE_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: DIGITAL_PAYMENTS_TRANSLATIONS, - chunks: DIGITAL_PAYMENTS_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_DIGITAL_PAYMENTS_ASSETS, - }, - dependencyManagement: { - featureName: CLI_DIGITAL_PAYMENTS_FEATURE, - featureDependencies: { - [SPARTACUS_CHECKOUT]: [CLI_CHECKOUT_BASE_FEATURE], - }, - }, - }); -} diff --git a/integration-libs/digital-payments/schematics/add-digital-payments/index_spec.ts b/integration-libs/digital-payments/schematics/add-digital-payments/index_spec.ts index 6104e098ac4..ae1d01357b7 100644 --- a/integration-libs/digital-payments/schematics/add-digital-payments/index_spec.ts +++ b/integration-libs/digital-payments/schematics/add-digital-payments/index_spec.ts @@ -1,6 +1,5 @@ /// -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; import { SchematicTestRunner, UnitTestTree, @@ -11,24 +10,29 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_CHECKOUT_BASE_FEATURE, - CLI_DIGITAL_PAYMENTS_FEATURE, - LibraryOptions, + cartBaseFeatureModulePath, + checkoutWrapperModulePath, + CHECKOUT_BASE_FEATURE_NAME, + digitalPaymentsFeatureModulePath, + DIGITAL_PAYMENTS_FEATURE_NAME, LibraryOptions as SpartacusDigitalPaymentsOptions, + orderFeatureModulePath, SpartacusOptions, - SPARTACUS_CART, SPARTACUS_CHECKOUT, + SPARTACUS_DIGITAL_PAYMENTS, SPARTACUS_SCHEMATICS, + userFeatureModulePath, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/digital-payments/digital-payments-feature.module.ts'; describe('Spartacus Digital-Payments schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_DIGITAL_PAYMENTS, + collectionPath + ); let appTree: UnitTestTree; @@ -59,9 +63,14 @@ describe('Spartacus Digital-Payments schematics: ng-add', () => { features: [], }; + const checkoutFeatureOptions: SpartacusDigitalPaymentsOptions = { + ...libraryNoFeaturesOptions, + features: [CHECKOUT_BASE_FEATURE_NAME], + }; + const digitalPaymentsFeatureOptions: SpartacusDigitalPaymentsOptions = { ...libraryNoFeaturesOptions, - features: [CLI_DIGITAL_PAYMENTS_FEATURE], + features: [DIGITAL_PAYMENTS_FEATURE_NAME], }; beforeEach(async () => { @@ -113,19 +122,22 @@ describe('Spartacus Digital-Payments schematics: ng-add', () => { }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(digitalPaymentsFeatureModulePath)).toBeFalsy(); }); }); describe('Digital-Payments feature', () => { describe('general setup', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync('ng-add', checkoutFeatureOptions, appTree) + .toPromise(); appTree = await schematicRunner .runSchematicAsync('ng-add', digitalPaymentsFeatureOptions, appTree) .toPromise(); }); - it('should install necessary Spartacus libraries', () => { + it('should install necessary Spartacus libraries', async () => { const packageJson = JSON.parse(appTree.readContent('package.json')); let dependencies: Record = {}; dependencies = { ...packageJson.dependencies }; @@ -143,51 +155,37 @@ describe('Spartacus Digital-Payments schematics: ng-add', () => { } }); - it('should run the proper installation tasks', async () => { - const tasks = schematicRunner.tasks - .filter((task) => task.name === 'run-schematic') - .map( - (task) => task.options as RunSchematicTaskOptions - ); - expect(tasks.length).toEqual(3); - - const cartTask = tasks[0]; - expect(cartTask).toBeTruthy(); - expect(cartTask.name).toEqual('add-spartacus-library'); - expect(cartTask.options).toHaveProperty('collection', SPARTACUS_CART); - expect(cartTask.options.options?.features).toEqual([]); - - const checkoutTask = tasks[1]; - expect(checkoutTask).toBeTruthy(); - expect(checkoutTask.name).toEqual('add-spartacus-library'); - expect(checkoutTask.options).toHaveProperty( - 'collection', - SPARTACUS_CHECKOUT - ); - expect(checkoutTask.options.options?.features).toEqual([]); + it('should NOT install the required feature dependencies', async () => { + const userFeatureModule = appTree.readContent(userFeatureModulePath); + expect(userFeatureModule).toBeFalsy(); - const checkoutTaskWithSubFeatures = tasks[2]; - expect(checkoutTaskWithSubFeatures).toBeTruthy(); - expect(checkoutTaskWithSubFeatures.name).toEqual( - 'add-spartacus-library' + const cartFeatureModule = appTree.readContent( + cartBaseFeatureModulePath ); - expect(checkoutTaskWithSubFeatures.options).toHaveProperty( - 'collection', - SPARTACUS_CHECKOUT - ); - expect(checkoutTaskWithSubFeatures.options.options?.features).toEqual([ - CLI_CHECKOUT_BASE_FEATURE, - ]); + expect(cartFeatureModule).toBeFalsy(); + + const orderFeatureModule = appTree.readContent(orderFeatureModulePath); + expect(orderFeatureModule).toBeFalsy(); }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(digitalPaymentsFeatureModulePath); expect(module).toMatchSnapshot(); + + const wrapperModule = appTree.readContent(checkoutWrapperModulePath); + expect(wrapperModule).toMatchSnapshot(); }); }); describe('eager loading', () => { beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { ...checkoutFeatureOptions, lazy: false }, + appTree + ) + .toPromise(); appTree = await schematicRunner .runSchematicAsync( 'ng-add', @@ -198,8 +196,10 @@ describe('Spartacus Digital-Payments schematics: ng-add', () => { }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(digitalPaymentsFeatureModulePath); expect(module).toMatchSnapshot(); + + expect(appTree.readContent(checkoutWrapperModulePath)).toBeFalsy(); }); }); }); diff --git a/integration-libs/digital-payments/schematics/add-library/index.ts b/integration-libs/digital-payments/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/integration-libs/digital-payments/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/integration-libs/digital-payments/schematics/collection.json b/integration-libs/digital-payments/schematics/collection.json index c2efb51071e..0a03ec6d43b 100644 --- a/integration-libs/digital-payments/schematics/collection.json +++ b/integration-libs/digital-payments/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-digital-payments/index#addDigitalPaymentsFeature", "description": "Add and configure Spartacus' Digital Payments feature", diff --git a/integration-libs/digital-payments/schematics/constants.ts b/integration-libs/digital-payments/schematics/constants.ts deleted file mode 100644 index fea4bef9df2..00000000000 --- a/integration-libs/digital-payments/schematics/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SPARTACUS_DIGITAL_PAYMENTS } from '@spartacus/schematics'; - -export const DIGITAL_PAYMENTS_FOLDER_NAME = 'digital-payments'; -export const DIGITAL_PAYMENTS_MODULE_NAME = 'DigitalPayments'; -export const DIGITAL_PAYMENTS_TRANSLATIONS = 'dpTranslations'; -export const DIGITAL_PAYMENTS_TRANSLATION_CHUNKS_CONFIG = - 'dpTranslationChunksConfig'; -export const SPARTACUS_DIGITAL_PAYMENTS_ASSETS = `${SPARTACUS_DIGITAL_PAYMENTS}/assets`; diff --git a/integration-libs/digital-payments/src/digital-payments.module.ts b/integration-libs/digital-payments/src/digital-payments.module.ts index 36885b13670..8102868389a 100644 --- a/integration-libs/digital-payments/src/digital-payments.module.ts +++ b/integration-libs/digital-payments/src/digital-payments.module.ts @@ -1,8 +1,7 @@ import { NgModule } from '@angular/core'; -import { CheckoutModule } from '@spartacus/checkout/base'; import { DpCheckoutModule } from './checkout/dp-checkout.module'; @NgModule({ - imports: [CheckoutModule, DpCheckoutModule], + imports: [DpCheckoutModule], }) export class DigitalPaymentsModule {} diff --git a/integration-libs/epd-visualization/schematics/add-epd-visualization/index.ts b/integration-libs/epd-visualization/schematics/add-epd-visualization/index.ts index 6d414e753f6..2c72c374321 100644 --- a/integration-libs/epd-visualization/schematics/add-epd-visualization/index.ts +++ b/integration-libs/epd-visualization/schematics/add-epd-visualization/index.ts @@ -1,35 +1,20 @@ import { chain, - noop, Rule, SchematicContext, Tree, } from '@angular-devkit/schematics'; import { - addLibraryFeature, + addFeatures, addPackageJsonDependenciesForLibrary, - CLI_EPD_VISUALIZATION_FEATURE, - CustomConfig, - EPD_VISUALIZATION_CONFIG, - EPD_VISUALIZATION_MODULE, - EPD_VISUALIZATION_ROOT_MODULE, + analyzeApplication, + analyzeCrossFeatureDependencies, + finalizeInstallation, readPackageJson, - shouldAddFeature, - SPARTACUS_EPD_VISUALIZATION, + SpartacusEpdVisualizationOptions, validateSpartacusInstallation, } from '@spartacus/schematics'; import { peerDependencies } from '../../package.json'; -import { - EPD_VISUALIZATION_FEATURE_NAME_CONSTANT, - EPD_VISUALIZATION_FOLDER_NAME, - EPD_VISUALIZATION_MODULE_NAME, - EPD_VISUALIZATION_TRANSLATIONS, - EPD_VISUALIZATION_TRANSLATION_CHUNKS_CONFIG, - SCSS_FILE_NAME, - SPARTACUS_EPD_VISUALIZATION_ASSETS, - SPARTACUS_EPD_VISUALIZATION_ROOT, -} from '../constants'; -import { Schema as SpartacusEpdVisualizationOptions } from './schema'; export function addEpdVisualizationFeature( options: SpartacusEpdVisualizationOptions @@ -38,67 +23,17 @@ export function addEpdVisualizationFeature( const packageJson = readPackageJson(tree); validateSpartacusInstallation(packageJson); + const features = analyzeCrossFeatureDependencies( + options.features as string[] + ); + return chain([ + analyzeApplication(options, features), + + addFeatures(options, features), addPackageJsonDependenciesForLibrary(peerDependencies, options), - shouldAddFeature(CLI_EPD_VISUALIZATION_FEATURE, options.features) - ? chain([addEpdVisualization(options)]) - : noop(), + finalizeInstallation(options, features), ]); }; } - -function addEpdVisualization(options: SpartacusEpdVisualizationOptions): Rule { - const customConfig: CustomConfig[] = [ - { - import: [ - { - moduleSpecifier: SPARTACUS_EPD_VISUALIZATION_ROOT, - namedImports: [EPD_VISUALIZATION_CONFIG], - }, - ], - content: `<${EPD_VISUALIZATION_CONFIG}>{ - epdVisualization: { - ui5: { - bootstrapUrl: "https://sapui5.hana.ondemand.com/1.98.0/resources/sap-ui-core.js" - }, - - apis: { - baseUrl: "${options.baseUrl}" - } - } - }`, - }, - ]; - - return addLibraryFeature(options, { - folderName: EPD_VISUALIZATION_FOLDER_NAME, - moduleName: EPD_VISUALIZATION_MODULE_NAME, - featureModule: { - name: EPD_VISUALIZATION_MODULE, - importPath: SPARTACUS_EPD_VISUALIZATION, - }, - rootModule: { - name: EPD_VISUALIZATION_ROOT_MODULE, - importPath: SPARTACUS_EPD_VISUALIZATION_ROOT, - }, - lazyLoadingChunk: { - moduleSpecifier: SPARTACUS_EPD_VISUALIZATION_ROOT, - namedImports: [EPD_VISUALIZATION_FEATURE_NAME_CONSTANT], - }, - i18n: { - resources: EPD_VISUALIZATION_TRANSLATIONS, - chunks: EPD_VISUALIZATION_TRANSLATION_CHUNKS_CONFIG, - importPath: SPARTACUS_EPD_VISUALIZATION_ASSETS, - }, - styles: { - scssFileName: SCSS_FILE_NAME, - importStyle: SPARTACUS_EPD_VISUALIZATION, - }, - customConfig, - dependencyManagement: { - featureName: CLI_EPD_VISUALIZATION_FEATURE, - featureDependencies: {}, - }, - }); -} diff --git a/integration-libs/epd-visualization/schematics/add-epd-visualization/index_spec.ts b/integration-libs/epd-visualization/schematics/add-epd-visualization/index_spec.ts index 8a6f3fa2dca..1fc4ab3d3eb 100644 --- a/integration-libs/epd-visualization/schematics/add-epd-visualization/index_spec.ts +++ b/integration-libs/epd-visualization/schematics/add-epd-visualization/index_spec.ts @@ -11,21 +11,24 @@ import { } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import { - CLI_EPD_VISUALIZATION_FEATURE, + epdFeatureModulePath, + EPD_VISUALIZATION_FEATURE_NAME, + SpartacusEpdVisualizationOptions, SpartacusOptions, + SPARTACUS_EPD_VISUALIZATION, SPARTACUS_SCHEMATICS, } from '@spartacus/schematics'; import * as path from 'path'; import { peerDependencies } from '../../package.json'; -import { Schema as SpartacusEpdVisualizationOptions } from './schema'; const collectionPath = path.join(__dirname, '../collection.json'); -const featureModulePath = - 'src/app/spartacus/features/epd-visualization/epd-visualization-feature.module.ts'; const scssFilePath = 'src/styles/spartacus/epd-visualization.scss'; describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_EPD_VISUALIZATION, + collectionPath + ); let appTree: UnitTestTree; @@ -60,7 +63,7 @@ describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => const visualizationFeatureOptions: SpartacusEpdVisualizationOptions = { ...libraryNoFeaturesOptions, - features: [CLI_EPD_VISUALIZATION_FEATURE], + features: [EPD_VISUALIZATION_FEATURE_NAME], }; beforeEach(async () => { @@ -105,7 +108,7 @@ describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => }); it('should not create any of the feature modules', () => { - expect(appTree.exists(featureModulePath)).toBeFalsy(); + expect(appTree.exists(epdFeatureModulePath)).toBeFalsy(); }); }); @@ -118,7 +121,7 @@ describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => }); it('should add the feature using the lazy loading syntax', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(epdFeatureModulePath); expect(module).toMatchSnapshot(); }); @@ -176,7 +179,7 @@ describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(epdFeatureModulePath); expect(module).toMatchSnapshot(); }); }); @@ -232,7 +235,7 @@ describe('Spartacus SAP EPD Visualization integration schematics: ng-add', () => }); it('should import appropriate modules', async () => { - const module = appTree.readContent(featureModulePath); + const module = appTree.readContent(epdFeatureModulePath); expect(module).toMatchSnapshot(); }); }); diff --git a/integration-libs/epd-visualization/schematics/add-epd-visualization/schema.ts b/integration-libs/epd-visualization/schematics/add-epd-visualization/schema.ts deleted file mode 100644 index aa50e6b1e53..00000000000 --- a/integration-libs/epd-visualization/schematics/add-epd-visualization/schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { LibraryOptions } from '@spartacus/schematics'; - -export interface Schema extends LibraryOptions { - baseUrl: string; -} diff --git a/integration-libs/epd-visualization/schematics/add-library/index.ts b/integration-libs/epd-visualization/schematics/add-library/index.ts deleted file mode 100644 index a53b5176267..00000000000 --- a/integration-libs/epd-visualization/schematics/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '@spartacus/schematics'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/integration-libs/epd-visualization/schematics/collection.json b/integration-libs/epd-visualization/schematics/collection.json index 1c95342cb9e..9c44e2799e0 100644 --- a/integration-libs/epd-visualization/schematics/collection.json +++ b/integration-libs/epd-visualization/schematics/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add": { "factory": "./add-epd-visualization/index#addEpdVisualizationFeature", "description": "Add and configure the SAP Enterprise Product Development Visualization integration", diff --git a/integration-libs/epd-visualization/schematics/constants.ts b/integration-libs/epd-visualization/schematics/constants.ts deleted file mode 100644 index cde096bdecb..00000000000 --- a/integration-libs/epd-visualization/schematics/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const EPD_VISUALIZATION_FEATURE_NAME_CONSTANT = - 'EPD_VISUALIZATION_FEATURE'; - -export const EPD_VISUALIZATION_FOLDER_NAME = 'epd-visualization'; -export const EPD_VISUALIZATION_MODULE_NAME = 'EpdVisualization'; - -export const EPD_VISUALIZATION_TRANSLATIONS = 'epdVisualizationTranslations'; -export const EPD_VISUALIZATION_TRANSLATION_CHUNKS_CONFIG = - 'epdVisualizationTranslationChunksConfig'; - -export const SCSS_FILE_NAME = 'epd-visualization.scss'; - -export const SPARTACUS_EPD_VISUALIZATION_ROOT = - '@spartacus/epd-visualization/root'; -export const SPARTACUS_EPD_VISUALIZATION_ASSETS = - '@spartacus/epd-visualization/assets'; diff --git a/projects/schematics/jest.schematics.config.js b/projects/schematics/jest.schematics.config.js index 29dfffdd03f..cee3b3ccd96 100644 --- a/projects/schematics/jest.schematics.config.js +++ b/projects/schematics/jest.schematics.config.js @@ -18,8 +18,8 @@ module.exports = { coverageThreshold: { global: { statements: 90, - branches: 69, - functions: 88, + branches: 74, + functions: 90, lines: 90, }, }, diff --git a/projects/schematics/src/add-cms-component/index_spec.ts b/projects/schematics/src/add-cms-component/index_spec.ts index 6e77cccabc2..88d7e99121e 100644 --- a/projects/schematics/src/add-cms-component/index_spec.ts +++ b/projects/schematics/src/add-cms-component/index_spec.ts @@ -16,6 +16,7 @@ import { CONFIG_MODULE_CLASS, UTF_8, } from '../shared/constants'; +import { SPARTACUS_SCHEMATICS } from '../shared/libs-constants'; import { commitChanges, getTsSourceFile, @@ -78,7 +79,10 @@ function assertContentDoesNotExist( } describe('add-cms-component', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); let appTree: UnitTestTree; diff --git a/projects/schematics/src/add-library/index.ts b/projects/schematics/src/add-library/index.ts deleted file mode 100644 index 2d00b353608..00000000000 --- a/projects/schematics/src/add-library/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Rule } from '@angular-devkit/schematics'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; -import { - LibraryOptions, - runExternalSpartacusLibrary, -} from '../shared/utils/lib-utils'; - -export function addSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return runExternalSpartacusLibrary(taskOptions); -} diff --git a/projects/schematics/src/add-pwa/index.ts b/projects/schematics/src/add-pwa/index.ts index cfa6e8c2bb8..d60be3cfb42 100644 --- a/projects/schematics/src/add-pwa/index.ts +++ b/projects/schematics/src/add-pwa/index.ts @@ -8,23 +8,18 @@ import { } from '@angular-devkit/schematics'; import { getAppModulePath } from '@schematics/angular/utility/ng-ast-utils'; import { Schema as SpartacusOptions } from '../add-spartacus/schema'; +import { UTF_8 } from '../shared/constants'; import { getLineFromTSFile } from '../shared/utils/file-utils'; import { getProjectTargets } from '../shared/utils/workspace-utils'; -function removeServiceWorkerSetup(host: Tree, modulePath: string) { - const buffer = host.read(modulePath); - - if (!buffer) { - return; - } - - let fileContent = buffer.toString(); +function removeServiceWorkerSetup(host: Tree, modulePath: string): void { + let fileContent = host.read(modulePath)?.toString(UTF_8); const serviceWorkerImport = `import { ServiceWorkerModule } from '@angular/service-worker';`; - const serviceWorkerModuleImport = `ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })`; + const serviceWorkerModuleImport = `ServiceWorkerModule.register('ngsw-worker.js'`; if ( - !fileContent.includes(serviceWorkerModuleImport) || - !fileContent.includes(serviceWorkerImport) + !fileContent?.includes(serviceWorkerModuleImport) || + !fileContent?.includes(serviceWorkerImport) ) { return; } diff --git a/projects/schematics/src/add-pwa/index_spec.ts b/projects/schematics/src/add-pwa/index_spec.ts index 28d34282daa..ed1d5c22b17 100644 --- a/projects/schematics/src/add-pwa/index_spec.ts +++ b/projects/schematics/src/add-pwa/index_spec.ts @@ -9,11 +9,19 @@ import { import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import * as path from 'path'; import { Schema as SpartacusOptions } from '../add-spartacus/schema'; +import { SPARTACUS_SCHEMATICS } from '../shared/libs-constants'; const collectionPath = path.join(__dirname, '../collection.json'); describe('Spartacus Schematics: add-pwa', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); + schematicRunner.registerCollection( + '@angular/pwa', + '../../node_modules/@angular/pwa/collection.json' + ); let appTree: UnitTestTree; @@ -57,18 +65,49 @@ describe('Spartacus Schematics: add-pwa', () => { }); it('Add PWA/ServiceWorker support for your project', async () => { - const tree = await schematicRunner + appTree = await schematicRunner .runSchematicAsync('add-pwa', defaultOptions, appTree) .toPromise(); - const packageJson = tree.readContent('/package.json'); + const packageJson = appTree.readContent('/package.json'); const packageObj = JSON.parse(packageJson); const depPackageList = Object.keys(packageObj.dependencies); expect(depPackageList.includes('@angular/service-worker')).toBe(true); expect( - tree.files.includes('/projects/schematics-test/src/manifest.webmanifest') + appTree.files.includes( + '/projects/schematics-test/src/manifest.webmanifest' + ) + ).toBe(true); + expect( + appTree.files.includes( + '/projects/schematics-test/src/assets/icons/icon-96x96.png' + ) + ).toBe(true); + }); + + it('should remove the existing SW setup and add Spartacus PWA support', async () => { + appTree = await schematicRunner + .runExternalSchematicAsync( + '@angular/pwa', + 'ng-add', + { project: 'schematics-test' }, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync('add-pwa', defaultOptions, appTree) + .toPromise(); + + const packageJson = appTree.readContent('/package.json'); + const packageObj = JSON.parse(packageJson); + const depPackageList = Object.keys(packageObj.dependencies); + expect(depPackageList.includes('@angular/service-worker')).toBe(true); + expect( + appTree.files.includes( + '/projects/schematics-test/src/manifest.webmanifest' + ) ).toBe(true); expect( - tree.files.includes( + appTree.files.includes( '/projects/schematics-test/src/assets/icons/icon-96x96.png' ) ).toBe(true); diff --git a/projects/schematics/src/add-spartacus/configuration.ts b/projects/schematics/src/add-spartacus/configuration.ts index c1c84c17051..2d7f0d8cb33 100644 --- a/projects/schematics/src/add-spartacus/configuration.ts +++ b/projects/schematics/src/add-spartacus/configuration.ts @@ -1,4 +1,9 @@ -import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics'; +import { + Rule, + SchematicContext, + SchematicsException, + Tree, +} from '@angular-devkit/schematics'; import { SourceFile } from 'ts-morph'; import { FEATURES_CONFIG, @@ -21,7 +26,11 @@ import { parseCSV } from '../shared/utils/transform-utils'; import { Schema as SpartacusOptions } from './schema'; export function addSpartacusConfiguration(options: SpartacusOptions): Rule { - return (tree: Tree): Tree => { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Setting up Spartacus configuration module...`); + } + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); if (!buildPaths.length) { @@ -34,6 +43,10 @@ export function addSpartacusConfiguration(options: SpartacusOptions): Rule { for (const tsconfigPath of buildPaths) { addConfiguration(tree, tsconfigPath, basePath, options); } + + if (options.debug) { + context.logger.info(`✅ Spartacus configuration module setup complete.`); + } return tree; }; } @@ -55,7 +68,6 @@ function addConfiguration( addCommonConfiguration(sourceFile, options); saveAndFormat(sourceFile); - break; } } diff --git a/projects/schematics/src/add-spartacus/index.ts b/projects/schematics/src/add-spartacus/index.ts index 5a9e80a1f14..76a231910f3 100644 --- a/projects/schematics/src/add-spartacus/index.ts +++ b/projects/schematics/src/add-spartacus/index.ts @@ -10,14 +10,17 @@ import { NodeDependency } from '@schematics/angular/utility/dependencies'; import { WorkspaceProject } from '@schematics/angular/utility/workspace-models'; import { ANGULAR_HTTP, RXJS } from '../shared/constants'; import { SPARTACUS_STOREFRONTLIB } from '../shared/libs-constants'; +import { + analyzeCrossFeatureDependencies, + analyzeCrossLibraryDependenciesByFeatures, +} from '../shared/utils/dependency-utils'; +import { addFeatures, analyzeApplication } from '../shared/utils/feature-utils'; import { getIndexHtmlPath } from '../shared/utils/file-utils'; import { appendHtmlElementToHead } from '../shared/utils/html-utils'; import { addPackageJsonDependencies, - addSchematicsTasks, - createSpartacusFeatureOptionsForLibrary, - LibraryOptions, - prepareCliPackageAndSubFeature, + finalizeInstallation, + installPackageJsonDependencies, } from '../shared/utils/lib-utils'; import { addModuleImport } from '../shared/utils/new-module-utils'; import { @@ -46,6 +49,10 @@ import { setupStoreModules } from './store'; function installStyles(options: SpartacusOptions): Rule { return (tree: Tree, context: SchematicContext): void => { + if (options.debug) { + context.logger.info(`⌛️ Installing styles...`); + } + const project = getProjectFromWorkspace(tree, options); const rootStyles = getProjectTargets(project)?.build?.options?.styles?.[0]; const styleFilePath = @@ -104,6 +111,10 @@ function installStyles(options: SpartacusOptions): Rule { recorder.insertLeft(htmlContent.length, insertion); tree.commitUpdate(recorder); + + if (options.debug) { + context.logger.info(`✅ Style installation complete.`); + } }; } @@ -112,6 +123,10 @@ function updateMainComponent( options: SpartacusOptions ): Rule { return (host: Tree, context: SchematicContext): Tree | void => { + if (options.debug) { + context.logger.info(`⌛️ Updating main component...`); + } + const filePath = project.sourceRoot + '/app/app.component.html'; const buffer = host.read(filePath); @@ -138,12 +153,19 @@ function updateMainComponent( host.commitUpdate(recorder); + if (options.debug) { + context.logger.info(`✅ Main component update complete.`); + } return host; }; } function updateIndexFile(tree: Tree, options: SpartacusOptions): Rule { - return (host: Tree): Tree => { + return (host: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Updating index file...`); + } + const projectIndexHtmlPath = getIndexHtmlPath(tree); const baseUrl = options.baseUrl || 'OCC_BACKEND_BASE_URL_VALUE'; @@ -156,12 +178,19 @@ function updateIndexFile(tree: Tree, options: SpartacusOptions): Rule { appendHtmlElementToHead(host, projectIndexHtmlPath, metaTag); }); + if (options.debug) { + context.logger.info(`✅ Index file update complete`); + } return host; }; } -function increaseBudgets(): Rule { - return (tree: Tree): Tree => { +function increaseBudgets(options: SpartacusOptions): Rule { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Increasing budgets...`); + } + const { path, workspace: angularJson } = getWorkspace(tree); const projectName = getDefaultProjectNameFromWorkspace(tree); @@ -209,12 +238,20 @@ function increaseBudgets(): Rule { }; tree.overwrite(path, JSON.stringify(updatedAngularJson, null, 2)); + + if (options.debug) { + context.logger.info(`✅ Budget increase complete.`); + } return tree; }; } -function createStylePreprocessorOptions(): Rule { - return (tree: Tree): Tree => { +function createStylePreprocessorOptions(options: SpartacusOptions): Rule { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Updating style preprocessor...`); + } + const { path, workspace: angularJson } = getWorkspace(tree); const projectName = getDefaultProjectNameFromWorkspace(tree); const project = angularJson.projects[projectName]; @@ -262,6 +299,9 @@ function createStylePreprocessorOptions(): Rule { }; tree.overwrite(path, JSON.stringify(updatedAngularJson, null, 2)); + if (options.debug) { + context.logger.info(`✅ Style preprocessor update complete.`); + } return tree; }; } @@ -290,14 +330,29 @@ function createStylePreprocessorOptionsArray(angularJsonStylePreprocessorOptions return angularJsonStylePreprocessorOptions; } -function prepareDependencies(): NodeDependency[] { +function prepareDependencies(features: string[]): NodeDependency[] { const spartacusDependencies = prepareSpartacusDependencies(); - return spartacusDependencies.concat(prepare3rdPartyDependencies()); + + const libraries = analyzeCrossLibraryDependenciesByFeatures(features); + const spartacusVersion = getPrefixedSpartacusSchematicsVersion(); + const spartacusLibraryDependencies = libraries.map((library) => + mapPackageToNodeDependencies(library, spartacusVersion) + ); + + const dependencies: NodeDependency[] = spartacusDependencies + .concat(spartacusLibraryDependencies) + .concat(prepare3rdPartyDependencies()); + + return dependencies; } -function updateAppModule(project: string): Rule { - return (tree: Tree): Tree => { - const { buildPaths } = getProjectTsConfigPaths(tree, project); +function updateAppModule(options: SpartacusOptions): Rule { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Updating AppModule...`); + } + + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); if (!buildPaths.length) { throw new SchematicsException( @@ -329,74 +384,64 @@ function updateAppModule(project: string): Rule { }); saveAndFormat(sourceFile); - break; } } } - return tree; - }; -} -function addSpartacusFeatures(options: SpartacusOptions): Rule { - return (tree: Tree, context: SchematicContext) => { - const cliFeatures = prepareCliPackageAndSubFeature(options.features ?? []); - const libraryOptions: LibraryOptions = { - project: options.project, - lazy: options.lazy, - debug: options.debug, - }; - const featureOptions = createSpartacusFeatureOptionsForLibrary( - libraryOptions, - cliFeatures - ); - addSchematicsTasks(featureOptions, context); - - const packageJson = readPackageJson(tree); - const spartacusVersion = getPrefixedSpartacusSchematicsVersion(); - const dependencies = Object.keys(cliFeatures).map((feature) => - mapPackageToNodeDependencies(feature, spartacusVersion) - ); - return addPackageJsonDependencies(dependencies, packageJson)(tree, context); + if (options.debug) { + context.logger.info(`✅ AppModule update complete.`); + } + return tree; }; } export function addSpartacus(options: SpartacusOptions): Rule { return (tree: Tree, context: SchematicContext) => { - const project = getProjectFromWorkspace(tree, options); - const packageJsonFile = readPackageJson(tree); - const spartacusDependencies = prepareDependencies(); + const features = analyzeCrossFeatureDependencies(options.features ?? []); + const dependencies = prepareDependencies(features); const spartacusRxjsDependency: NodeDependency[] = [ - spartacusDependencies.find((dep) => dep.name === RXJS) as NodeDependency, + dependencies.find((dep) => dep.name === RXJS) as NodeDependency, ]; + const packageJsonFile = readPackageJson(tree); return chain([ - addPackageJsonDependencies(spartacusDependencies, packageJsonFile), - - /** - * Force installing versions of dependencies used by Spartacus. - * E.g. ng13 uses rxjs 7, but Spartacus uses rxjs 6. - */ - updatePackageJsonDependencies(spartacusRxjsDependency, packageJsonFile), + analyzeApplication(options, features), - setupStoreModules(options.project), + setupStoreModules(options), scaffoldStructure(options), - setupSpartacusModule(options.project), + setupSpartacusModule(options), - setupSpartacusFeaturesModule(options.project), + setupSpartacusFeaturesModule(options), addSpartacusConfiguration(options), - updateAppModule(options.project), + updateAppModule(options), installStyles(options), - updateMainComponent(project, options), + updateMainComponent(getProjectFromWorkspace(tree, options), options), options.useMetaTags ? updateIndexFile(tree, options) : noop(), - increaseBudgets(), - createStylePreprocessorOptions(), - addSpartacusFeatures(options), + increaseBudgets(options), + createStylePreprocessorOptions(options), + + addFeatures(options, features), + + chain([ + addPackageJsonDependencies( + prepareDependencies(features), + packageJsonFile + ), + /** + * Force installing versions of dependencies used by Spartacus. + * E.g. ng13 uses rxjs 7, but Spartacus uses rxjs 6. + */ + updatePackageJsonDependencies(spartacusRxjsDependency, packageJsonFile), + installPackageJsonDependencies(), + ]), + + finalizeInstallation(options, features), ])(tree, context); }; } diff --git a/projects/schematics/src/add-spartacus/index_spec.ts b/projects/schematics/src/add-spartacus/index_spec.ts index 432e3a38c86..93aed614486 100644 --- a/projects/schematics/src/add-spartacus/index_spec.ts +++ b/projects/schematics/src/add-spartacus/index_spec.ts @@ -11,15 +11,20 @@ import * as path from 'path'; import { SPARTACUS_CONFIGURATION_MODULE, SPARTACUS_CORE, + SPARTACUS_SCHEMATICS, SPARTACUS_STOREFRONTLIB, SPARTACUS_STYLES, } from '../shared/libs-constants'; +import { spartacusFeaturesModulePath } from '../shared/utils/test-utils'; import { Schema as SpartacusOptions } from './schema'; const collectionPath = path.join(__dirname, '../collection.json'); describe('add-spartacus', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); let appTree: UnitTestTree; @@ -533,7 +538,7 @@ describe('add-spartacus', () => { .toPromise(); const featureModuleContent = appTree.readContent( - '/projects/schematics-test/src/app/spartacus/spartacus-features.module.ts' + `/projects/schematics-test/${spartacusFeaturesModulePath}` ); const importModuleOccurrences = featureModuleContent.match(/AuthModule.forRoot()/gm)?.length ?? -1; diff --git a/projects/schematics/src/add-spartacus/schema.json b/projects/schematics/src/add-spartacus/schema.json index 224bb05f5fb..d9aeb766689 100644 --- a/projects/schematics/src/add-spartacus/schema.json +++ b/projects/schematics/src/add-spartacus/schema.json @@ -44,8 +44,8 @@ "Personalization", "TMS-GTM", "TMS-AEPL", - "Account", - "Profile" + "User-Account", + "User-Profile" ], "type": "string" }, @@ -63,11 +63,11 @@ "SmartEdit", "Store-Finder", "Personalization", - "Account", - "Profile" + "User-Account", + "User-Profile" ], "x-prompt": { - "message": "Which Spartacus features would you like to set up?\nPlease note that for most Spartacus features to be properly configured, the Account feature is required.", + "message": "Which Spartacus features would you like to set up?\nPlease note that for most Spartacus features to be properly configured, the User-Account feature is required.", "type": "list", "items": [ { @@ -100,11 +100,11 @@ }, { "value": "Checkout-B2B", - "label": "Checkout (b2b feature, includes Base Checkout)" + "label": "Checkout B2B (b2b feature, requires Base Checkout)" }, { "value": "Checkout-Scheduled-Replenishment", - "label": "Checkout Scheduled Replenishment (b2b feature, includes Base and B2B Checkout)" + "label": "Checkout Scheduled Replenishment (b2b feature, requires Base and B2B Checkout)" }, { "value": "Cart", @@ -183,11 +183,11 @@ "label": "Tracking - Tag Management System - Adobe Experience Platform Launch" }, { - "value": "Account", + "value": "User-Account", "label": "User - Account" }, { - "value": "Profile", + "value": "User-Profile", "label": "User - Profile" } ] diff --git a/projects/schematics/src/add-spartacus/spartacus-features.ts b/projects/schematics/src/add-spartacus/spartacus-features.ts index fa2d87058c9..acde86eca90 100644 --- a/projects/schematics/src/add-spartacus/spartacus-features.ts +++ b/projects/schematics/src/add-spartacus/spartacus-features.ts @@ -1,4 +1,9 @@ -import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics'; +import { + Rule, + SchematicContext, + SchematicsException, + Tree, +} from '@angular-devkit/schematics'; import { SPARTACUS_CORE, SPARTACUS_FEATURES_MODULE, @@ -8,11 +13,16 @@ import { import { addModuleImport } from '../shared/utils/new-module-utils'; import { createProgram, saveAndFormat } from '../shared/utils/program'; import { getProjectTsConfigPaths } from '../shared/utils/project-tsconfig-paths'; +import { Schema as SpartacusOptions } from './schema'; /** Migration which ensures the spartacus features are being correctly set up */ -export function setupSpartacusFeaturesModule(project: string): Rule { - return (tree: Tree): Tree => { - const { buildPaths } = getProjectTsConfigPaths(tree, project); +export function setupSpartacusFeaturesModule(options: SpartacusOptions): Rule { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Setting up Spartacus features module...`); + } + + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); if (!buildPaths.length) { throw new SchematicsException( @@ -24,6 +34,10 @@ export function setupSpartacusFeaturesModule(project: string): Rule { for (const tsconfigPath of buildPaths) { configureSpartacusModules(tree, tsconfigPath, basePath); } + + if (options.debug) { + context.logger.info(`✅ Spartacus features module setup complete.`); + } return tree; }; } @@ -244,7 +258,6 @@ function configureSpartacusModules( }); saveAndFormat(sourceFile); - break; } } diff --git a/projects/schematics/src/add-spartacus/spartacus.ts b/projects/schematics/src/add-spartacus/spartacus.ts index c44e245632e..0d1caa44d8e 100644 --- a/projects/schematics/src/add-spartacus/spartacus.ts +++ b/projects/schematics/src/add-spartacus/spartacus.ts @@ -1,4 +1,9 @@ -import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics'; +import { + Rule, + SchematicContext, + SchematicsException, + Tree, +} from '@angular-devkit/schematics'; import { BASE_STOREFRONT_MODULE } from '../shared/constants'; import { SPARTACUS_MODULE, @@ -10,11 +15,16 @@ import { } from '../shared/utils/new-module-utils'; import { createProgram, saveAndFormat } from '../shared/utils/program'; import { getProjectTsConfigPaths } from '../shared/utils/project-tsconfig-paths'; +import { Schema as SpartacusOptions } from './schema'; /** Migration which ensures the spartacus is being correctly set up */ -export function setupSpartacusModule(project: string): Rule { - return (tree: Tree): Tree => { - const { buildPaths } = getProjectTsConfigPaths(tree, project); +export function setupSpartacusModule(options: SpartacusOptions): Rule { + return (tree: Tree, context: SchematicContext): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Setting up Spartacus module...`); + } + + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); if (!buildPaths.length) { throw new SchematicsException( @@ -26,6 +36,10 @@ export function setupSpartacusModule(project: string): Rule { for (const tsconfigPath of buildPaths) { configureSpartacusModules(tree, tsconfigPath, basePath); } + + if (options.debug) { + context.logger.info(`✅ Spartacus module setup complete.`); + } return tree; }; } diff --git a/projects/schematics/src/add-spartacus/store.ts b/projects/schematics/src/add-spartacus/store.ts index e53fc6d4509..33aa6f19995 100644 --- a/projects/schematics/src/add-spartacus/store.ts +++ b/projects/schematics/src/add-spartacus/store.ts @@ -3,11 +3,16 @@ import { NGRX_EFFECTS, NGRX_STORE } from '../shared/constants'; import { addModuleImport } from '../shared/utils/new-module-utils'; import { createProgram, saveAndFormat } from '../shared/utils/program'; import { getProjectTsConfigPaths } from '../shared/utils/project-tsconfig-paths'; +import { Schema as SpartacusOptions } from './schema'; /** Migration that ensures that we have correct Store modules set */ -export function setupStoreModules(project: string): Rule { - return (tree: Tree): Tree => { - const { buildPaths } = getProjectTsConfigPaths(tree, project); +export function setupStoreModules(options: SpartacusOptions): Rule { + return (tree: Tree, context): Tree => { + if (options.debug) { + context.logger.info(`⌛️ Setting up store module...`); + } + + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); if (!buildPaths.length) { throw new SchematicsException( @@ -19,6 +24,10 @@ export function setupStoreModules(project: string): Rule { for (const tsconfigPath of buildPaths) { configureStoreModules(tree, tsconfigPath, basePath); } + + if (options.debug) { + context.logger.info(`✅ Store module setup complete`); + } return tree; }; } diff --git a/projects/schematics/src/add-ssr/index_spec.ts b/projects/schematics/src/add-ssr/index_spec.ts index 2fc2dd96408..0f8b0af30f5 100644 --- a/projects/schematics/src/add-ssr/index_spec.ts +++ b/projects/schematics/src/add-ssr/index_spec.ts @@ -10,12 +10,16 @@ import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema import * as path from 'path'; import { Schema as SpartacusOptions } from '../add-spartacus/schema'; import { NGUNIVERSAL_EXPRESS_ENGINE, UTF_8 } from '../shared/constants'; +import { SPARTACUS_SCHEMATICS } from '../shared/libs-constants'; import { getPathResultsForFile } from '../shared/utils/file-utils'; const collectionPath = path.join(__dirname, '../collection.json'); describe('add-ssr', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); let appTree: UnitTestTree; diff --git a/projects/schematics/src/collection.json b/projects/schematics/src/collection.json index f6bc22d5ec8..6f91358cbc2 100644 --- a/projects/schematics/src/collection.json +++ b/projects/schematics/src/collection.json @@ -9,12 +9,6 @@ "hidden": true, "aliases": ["install"] }, - "add-spartacus-library": { - "description": "Install a spartacus library", - "factory": "./add-library/index#addSpartacusLibrary", - "private": true, - "hidden": true - }, "add-spartacus": { "description": "Add spartacus to Angular project.", "factory": "./add-spartacus/index#addSpartacus", @@ -37,6 +31,11 @@ "factory": "./add-cms-component/index#addCmsComponent", "schema": "./add-cms-component/schema.json", "aliases": ["cms"] + }, + "wrapper-module": { + "description": "Generate a feature wrapper module", + "factory": "./wrapper-module/index#generateWrapperModule", + "aliases": ["wrapper"] } } } diff --git a/projects/schematics/src/migrations/3_0/storefinder/storefinder.ts b/projects/schematics/src/migrations/3_0/storefinder/storefinder.ts index e875bd56535..5ece49d4484 100644 --- a/projects/schematics/src/migrations/3_0/storefinder/storefinder.ts +++ b/projects/schematics/src/migrations/3_0/storefinder/storefinder.ts @@ -135,29 +135,3 @@ function addStorefinderPackageJsonDependencies(packageJson: any): Rule { ]; return addPackageJsonDependencies(dependencies, packageJson); } - -// function addStorefinderFeature(): Rule { -// return addLibraryFeature( -// { lazy: false, project: '', features: [CLI_STOREFINDER_FEATURE] }, -// { -// name: STOREFINDER_FEATURE_NAME, -// featureModule: { -// name: STOREFINDER_MODULE, -// importPath: SPARTACUS_STOREFINDER, -// }, -// rootModule: { -// name: STOREFINDER_ROOT_MODULE, -// importPath: SPARTACUS_STOREFINDER_ROOT, -// }, -// i18n: { -// resources: STOREFINDER_TRANSLATIONS, -// chunks: STOREFINDER_TRANSLATION_CHUNKS_CONFIG, -// importPath: SPARTACUS_STOREFINDER_ASSETS, -// }, -// styles: { -// scssFileName: STORE_FINDER_SCSS_FILE_NAME, -// importStyle: SPARTACUS_STOREFINDER, -// }, -// } -// ); -// } diff --git a/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-rulebased-feature.migration.ts b/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-rulebased-feature.migration.ts index 709d14274e1..861756aee52 100644 --- a/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-rulebased-feature.migration.ts +++ b/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-rulebased-feature.migration.ts @@ -1,8 +1,8 @@ import { + PRODUCT_CONFIGURATOR_RULEBASED_FEATURE, PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_OBSOLETE, TODO_SPARTACUS, } from '../../../../shared/constants'; -import { PRODUCT_CONFIGURATOR_RULEBASED_FEATURE } from '../../../../shared/libs-constants'; import { ConfigDeprecation } from '../../../../shared/utils/file-utils'; export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_MIGRATION: ConfigDeprecation = diff --git a/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-textfield-feature.migration.ts b/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-textfield-feature.migration.ts index 5e0c372774c..7c2f93bcbc1 100644 --- a/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-textfield-feature.migration.ts +++ b/projects/schematics/src/migrations/4_0/config-deprecations/data/product-configurator-textfield-feature.migration.ts @@ -1,8 +1,8 @@ import { + PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_OBSOLETE, TODO_SPARTACUS, } from '../../../../shared/constants'; -import { PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE } from '../../../../shared/libs-constants'; import { ConfigDeprecation } from '../../../../shared/utils/file-utils'; export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_MIGRATION: ConfigDeprecation = diff --git a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts index 313471fe292..e5ef412e403 100644 --- a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts +++ b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts @@ -122,9 +122,13 @@ import { VARIANT_STYLE_SELECTOR_MODULE, VIEW_CONFIG_MODULE, } from '../../../shared/constants'; +import { ASM_MODULE } from '../../../shared/lib-configs/asm-schematics-config'; +import { PERSONALIZATION_MODULE } from '../../../shared/lib-configs/tracking-schematics-config'; +import { + USER_ACCOUNT_MODULE, + USER_PROFILE_MODULE, +} from '../../../shared/lib-configs/user-schematics-config'; import { - ASM_MODULE, - PERSONALIZATION_MODULE, SPARTACUS_CART_SAVED_CART_COMPONENTS, SPARTACUS_CORE, SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED, @@ -141,8 +145,6 @@ import { SPARTACUS_USER_PROFILE_COMPONENTS, SPARTACUS_USER_PROFILE_CORE, SPARTACUS_USER_PROFILE_OCC, - USER_ACCOUNT_MODULE, - USER_PROFILE_MODULE, } from '../../../shared/libs-constants'; import { DeprecatedNode } from '../../../shared/utils/file-utils'; import { removedPublicApiDeprecation } from '../../mechanism/removed-public-api-deprecations/removed-public-api-deprecation'; diff --git a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts index 77f68053628..c8e794d521e 100644 --- a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts +++ b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts @@ -64,9 +64,9 @@ import { VARIANT_STYLE_ICONS_COMPONENT, VARIANT_STYLE_ICONS_MODULE, } from '../../../shared/constants'; +import { ASM_MODULE } from '../../../shared/lib-configs/asm-schematics-config'; +import { QUALTRICS_MODULE } from '../../../shared/lib-configs/qualtrics-schematics-config'; import { - ASM_MODULE, - QUALTRICS_MODULE, SPARTACUS_ASM, SPARTACUS_CHECKOUT_OLD_COMPONENTS, SPARTACUS_CHECKOUT_OLD_CORE, diff --git a/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management.ts b/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management.ts index bc13bc15c3e..66d9570cf31 100644 --- a/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management.ts +++ b/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management.ts @@ -8,14 +8,11 @@ import { import { NodeDependency } from '@schematics/angular/utility/dependencies'; import collectedDependencies from '../../../dependencies.json'; import { - CORE_SPARTACUS_SCOPES, SPARTACUS_SCHEMATICS, SPARTACUS_SCOPE, } from '../../../shared/libs-constants'; -import { - getSpartacusLibraries, - installPackageJsonDependencies, -} from '../../../shared/utils/lib-utils'; +import { analyzeCrossLibraryDependenciesByLibraries } from '../../../shared/utils/dependency-utils'; +import { installPackageJsonDependencies } from '../../../shared/utils/lib-utils'; import { createDependencies, readPackageJson, @@ -56,44 +53,19 @@ function collectSpartacusLibraryDependencies(packageJson: any): { spartacusPeerDeps: string[]; } { const dependencies: Record = packageJson.dependencies; - const installedLibs = getSpartacusLibraries(dependencies); + const installedLibs = Object.keys(dependencies).filter((dependency) => + dependency.startsWith(SPARTACUS_SCOPE) + ); - let spartacusPeerDeps: string[] = []; - for (const spartacusLib of installedLibs) { - spartacusPeerDeps = collectSpartacusPeerDeps( - spartacusLib, - spartacusPeerDeps - ); - } + const spartacusPeerDeps = + analyzeCrossLibraryDependenciesByLibraries(installedLibs); - // remove the duplicates - spartacusPeerDeps = Array.from(new Set(spartacusPeerDeps)); return { installedLibs, spartacusPeerDeps, }; } -function collectSpartacusPeerDeps( - name: string, - collectedDeps: string[] -): string[] { - const peerDepsWithVersions = ( - collectedDependencies as Record> - )[name]; - const peerDeps = Object.keys(peerDepsWithVersions) - .filter((d) => d.startsWith(SPARTACUS_SCOPE)) - .filter((d) => !CORE_SPARTACUS_SCOPES.includes(d)) - .filter((d) => !collectedDeps.includes(d)); - - collectedDeps = collectedDeps.concat(peerDeps); - for (const peerDep of peerDeps) { - collectedDeps = collectSpartacusPeerDeps(peerDep, collectedDeps); - } - - return collectedDeps; -} - function createSpartacusLibraryDependencies( allSpartacusLibraries: string[], skipScopes: string[] diff --git a/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management_spec.ts b/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management_spec.ts index 725dc10b2f4..9aba789c87b 100644 --- a/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management_spec.ts +++ b/projects/schematics/src/migrations/mechanism/dependency-management/dependency-management_spec.ts @@ -7,6 +7,20 @@ import { } from '@angular-devkit/schematics/testing'; import * as shx from 'shelljs'; import collectedDependencies from '../../../dependencies.json'; +import { + SPARTACUS_CART, + SPARTACUS_CDS, + SPARTACUS_CHECKOUT, + SPARTACUS_CORE, + SPARTACUS_ORDER, + SPARTACUS_ORGANIZATION, + SPARTACUS_PRODUCT_CONFIGURATOR, + SPARTACUS_QUALTRICS, + SPARTACUS_STOREFRONTLIB, + SPARTACUS_STYLES, + SPARTACUS_TRACKING, + SPARTACUS_USER, +} from '../../../shared/libs-constants'; import { runMigration, writeFile } from '../../../shared/utils/test-utils'; const MIGRATION_SCRIPT_NAME = '02-migration-v4-dependency-management'; @@ -74,7 +88,7 @@ describe('dependency management migrations', () => { name: 'xxx', version: '3.0.0', dependencies: { - '@spartacus/core': '3.0.0', + [SPARTACUS_CORE]: '3.0.0', }, }) ); @@ -85,7 +99,7 @@ describe('dependency management migrations', () => { const packageJson = appTree.readContent('/package.json'); const updatedVersion: string = JSON.parse(packageJson).dependencies.rxjs; expect(updatedVersion).toEqual( - collectedDependencies['@spartacus/core'].rxjs + collectedDependencies[SPARTACUS_CORE].rxjs ); }); }); @@ -100,7 +114,7 @@ describe('dependency management migrations', () => { name: 'xxx', version: '3.0.0', dependencies: { - '@spartacus/styles': '3.0.0', + [SPARTACUS_STYLES]: '3.0.0', bootstrap: '^5.0.0', }, }) @@ -127,7 +141,7 @@ describe('dependency management migrations', () => { name: 'xxx', version: '3.0.0', dependencies: { - '@spartacus/styles': '3.0.0', + [SPARTACUS_STYLES]: '3.0.0', bootstrap: '^3.0.0', }, }) @@ -156,10 +170,10 @@ describe('dependency management migrations', () => { name: 'xxx', version: '3.0.0', dependencies: { - '@spartacus/core': '3.0.0', - '@spartacus/storefront': '3.0.0', - '@spartacus/organization': '3.0.0', - '@spartacus/product-configurator': '3.0.0', + [SPARTACUS_CORE]: '3.0.0', + [SPARTACUS_STOREFRONTLIB]: '3.0.0', + [SPARTACUS_ORGANIZATION]: '3.0.0', + [SPARTACUS_PRODUCT_CONFIGURATOR]: '3.0.0', }, }) ); @@ -169,11 +183,16 @@ describe('dependency management migrations', () => { const packageJson = JSON.parse(appTree.readContent('/package.json')); - expect(packageJson.dependencies['@spartacus/core']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/storefront']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/cart']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/checkout']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/user']).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_USER]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CART]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_ORDER]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CHECKOUT]).toBeTruthy(); + expect( + packageJson.dependencies[SPARTACUS_PRODUCT_CONFIGURATOR] + ).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_ORGANIZATION]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CORE]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_STOREFRONTLIB]).toBeTruthy(); }); }); @@ -186,10 +205,10 @@ describe('dependency management migrations', () => { name: 'xxx', version: '3.0.0', dependencies: { - '@spartacus/core': '3.0.0', - '@spartacus/storefront': '3.0.0', - '@spartacus/cds': '3.0.0', - '@spartacus/qualtrics': '3.0.0', + [SPARTACUS_CORE]: '3.0.0', + [SPARTACUS_STOREFRONTLIB]: '3.0.0', + [SPARTACUS_CDS]: '3.0.0', + [SPARTACUS_QUALTRICS]: '3.0.0', }, }) ); @@ -199,13 +218,13 @@ describe('dependency management migrations', () => { const packageJson = JSON.parse(appTree.readContent('/package.json')); - expect(packageJson.dependencies['@spartacus/core']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/storefront']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/cds']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/tracking']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/cart']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/order']).toBeTruthy(); - expect(packageJson.dependencies['@spartacus/qualtrics']).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CORE]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_STOREFRONTLIB]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CDS]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_TRACKING]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_CART]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_ORDER]).toBeTruthy(); + expect(packageJson.dependencies[SPARTACUS_QUALTRICS]).toBeTruthy(); }); }); }); diff --git a/projects/schematics/src/migrations/mechanism/scaffold-app-structure/scaffold-app-structure_spec.ts b/projects/schematics/src/migrations/mechanism/scaffold-app-structure/scaffold-app-structure_spec.ts index 3c3cce4407f..e014b01fd44 100644 --- a/projects/schematics/src/migrations/mechanism/scaffold-app-structure/scaffold-app-structure_spec.ts +++ b/projects/schematics/src/migrations/mechanism/scaffold-app-structure/scaffold-app-structure_spec.ts @@ -9,6 +9,7 @@ import { Style, } from '@schematics/angular/application/schema'; import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; +import { spartacusFeaturesModulePath } from '../../../shared/utils/test-utils'; import { scaffoldAppStructure } from './scaffold-app-structure'; const spartacusModulePath = 'src/app/spartacus/spartacus.module.ts'; @@ -32,7 +33,6 @@ import { SpartacusFeaturesModule } from './spartacus-features.module'; export class SpartacusModule { } `; -const featuresModulePath = 'src/app/spartacus/spartacus-features.module.ts'; const exampleFeaturesModule = ` import { NgModule } from '@angular/core'; import { AsmFeatureModule } from './features/asm/asm-feature.module'; @@ -104,7 +104,9 @@ describe('scaffold app structure', () => { .callRule(scaffoldAppStructure(), appTree) .toPromise(); - expect(resultTree.read(featuresModulePath)?.toString()).toMatchSnapshot(); + expect( + resultTree.read(spartacusFeaturesModulePath)?.toString() + ).toMatchSnapshot(); expect( resultTree.read(configurationModulePath)?.toString() ).toMatchSnapshot(); @@ -117,7 +119,7 @@ describe('scaffold app structure', () => { describe('When the new app structure is already in place', () => { beforeEach(async () => { appTree.create(spartacusModulePath, exampleSpartacusModule); - appTree.create(featuresModulePath, exampleFeaturesModule); + appTree.create(spartacusFeaturesModulePath, exampleFeaturesModule); appTree.create(configurationModulePath, exampleConfigurationModule); }); @@ -126,7 +128,9 @@ describe('scaffold app structure', () => { .callRule(scaffoldAppStructure(), appTree) .toPromise(); - expect(resultTree.read(featuresModulePath)?.toString()).toMatchSnapshot(); + expect( + resultTree.read(spartacusFeaturesModulePath)?.toString() + ).toMatchSnapshot(); expect( resultTree.read(configurationModulePath)?.toString() ).toMatchSnapshot(); diff --git a/projects/schematics/src/ng-add/index_spec.ts b/projects/schematics/src/ng-add/index_spec.ts index 4ab338e40a4..726a9891df3 100644 --- a/projects/schematics/src/ng-add/index_spec.ts +++ b/projects/schematics/src/ng-add/index_spec.ts @@ -10,12 +10,16 @@ import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema import * as path from 'path'; import { Schema as SpartacusOptions } from '../add-spartacus/schema'; import { NGUNIVERSAL_EXPRESS_ENGINE, UTF_8 } from '../shared/constants'; +import { SPARTACUS_SCHEMATICS } from '../shared/libs-constants'; import { getPathResultsForFile } from '../shared/utils/file-utils'; const collectionPath = path.join(__dirname, '../collection.json'); describe('Spartacus Schematics: ng-add', () => { - const schematicRunner = new SchematicTestRunner('schematics', collectionPath); + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); let appTree: UnitTestTree; diff --git a/projects/schematics/src/shared/constants.ts b/projects/schematics/src/shared/constants.ts index e62bde0b892..4923e32a418 100644 --- a/projects/schematics/src/shared/constants.ts +++ b/projects/schematics/src/shared/constants.ts @@ -714,7 +714,6 @@ export const STORE_FINDER_HEADER_COMPONENT = 'StoreFinderHeaderComponent'; export const STORE_FINDER_PAGINATION_DETAILS_COMPONENT = 'StoreFinderPaginationDetailsComponent'; export const STORE_FINDER_COMPONENT = 'StoreFinderComponent'; -export const STORE_FINDER_SCSS_FILE_NAME = 'storefinder.scss'; export const ON_SUCCESS = 'onSuccess'; @@ -1059,6 +1058,11 @@ export const CONFIGURATOR_IS_IN_VIEWPORT = 'isInViewport'; /***** feature keys start *****/ export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_OBSOLETE = 'rulebased'; export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_OBSOLETE = 'textfield'; + +export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE = + 'productConfiguratorRulebased'; +export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE = + 'productConfiguratorTextfield'; /***** feature keys end *****/ export const TODO_SPARTACUS = 'TODO:Spartacus -'; diff --git a/projects/schematics/src/shared/index.ts b/projects/schematics/src/shared/index.ts index ebc5962b48d..8fc6d59ad47 100644 --- a/projects/schematics/src/shared/index.ts +++ b/projects/schematics/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './constants'; +export * from './lib-configs/index'; export * from './libs-constants'; export * from './utils/index'; diff --git a/projects/schematics/src/shared/lib-configs/asm-schematics-config.ts b/projects/schematics/src/shared/lib-configs/asm-schematics-config.ts new file mode 100644 index 00000000000..e80a9bd1ee2 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/asm-schematics-config.ts @@ -0,0 +1,52 @@ +import { + ASM_FEATURE_NAME, + SPARTACUS_ASM, + SPARTACUS_ASM_ASSETS, + SPARTACUS_ASM_ROOT, + SPARTACUS_USER, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const ASM_FOLDER_NAME = 'asm'; +export const ASM_FEATURE_MODULE_NAME = 'Asm'; + +export const ASM_FEATURE_NAME_CONSTANT = 'ASM_FEATURE'; +export const ASM_MODULE = 'AsmModule'; +export const ASM_ROOT_MODULE = 'AsmRootModule'; +export const ASM_TRANSLATIONS = 'asmTranslations'; +export const ASM_TRANSLATION_CHUNKS_CONFIG = 'asmTranslationChunksConfig'; +export const ASM_SCSS_FILE_NAME = 'asm.scss'; + +export const ASM_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: ASM_FEATURE_NAME, + mainScope: SPARTACUS_ASM, + }, + folderName: ASM_FOLDER_NAME, + moduleName: ASM_FEATURE_MODULE_NAME, + featureModule: { + name: ASM_MODULE, + importPath: SPARTACUS_ASM, + }, + rootModule: { + name: ASM_ROOT_MODULE, + importPath: SPARTACUS_ASM_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_ASM_ROOT, + namedImports: [ASM_FEATURE_NAME_CONSTANT], + }, + styles: { + scssFileName: ASM_SCSS_FILE_NAME, + importStyle: SPARTACUS_ASM, + }, + i18n: { + resources: ASM_TRANSLATIONS, + chunks: ASM_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_ASM_ASSETS, + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/cart-schematics-config.ts b/projects/schematics/src/shared/lib-configs/cart-schematics-config.ts new file mode 100644 index 00000000000..f8ea3499033 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/cart-schematics-config.ts @@ -0,0 +1,270 @@ +import { + ADD_TO_CART_ENTRY_POINT, + ADD_TO_WISHLIST_ENTRY_POINT, + CART_BASE_FEATURE_NAME, + CART_IMPORT_EXPORT_FEATURE_NAME, + CART_QUICK_ORDER_FEATURE_NAME, + CART_SAVED_CART_FEATURE_NAME, + CART_WISHLIST_FEATURE_NAME, + MINI_CART_ENTRY_POINT, + SPARTACUS_CART, + SPARTACUS_CART_BASE, + SPARTACUS_CART_BASE_ASSETS, + SPARTACUS_CART_BASE_ROOT, + SPARTACUS_CART_IMPORT_EXPORT, + SPARTACUS_CART_IMPORT_EXPORT_ASSETS, + SPARTACUS_CART_IMPORT_EXPORT_ROOT, + SPARTACUS_CART_WISHLIST, + SPARTACUS_CART_WISHLIST_ASSETS, + SPARTACUS_CART_WISHLIST_ROOT, + SPARTACUS_QUICK_ORDER, + SPARTACUS_QUICK_ORDER_ASSETS, + SPARTACUS_QUICK_ORDER_ROOT, + SPARTACUS_SAVED_CART, + SPARTACUS_SAVED_CART_ASSETS, + SPARTACUS_SAVED_CART_ROOT, + SPARTACUS_USER, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const CART_FOLDER_NAME = 'cart'; +export const CART_SCSS_FILE_NAME = 'cart.scss'; + +export const CART_BASE_MODULE = 'CartBaseModule'; +export const CART_BASE_ROOT_MODULE = 'CartBaseRootModule'; +export const MINI_CART_MODULE = 'MiniCartModule'; +export const ADD_TO_CART_MODULE = 'AddToCartModule'; +export const CART_BASE_FEATURE_MODULE_NAME = 'CartBase'; +export const CART_BASE_FEATURE_NAME_CONSTANT = 'CART_BASE_FEATURE'; +export const ADD_TO_CART_FEATURE_NAME_CONSTANT = 'ADD_TO_CART_FEATURE'; +export const MINI_CART_FEATURE_NAME_CONSTANT = 'MINI_CART_FEATURE'; +export const CART_BASE_TRANSLATIONS = 'cartBaseTranslations'; +export const CART_BASE_TRANSLATION_CHUNKS_CONFIG = + 'cartBaseTranslationChunksConfig'; + +export const CART_BASE_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CART_BASE_FEATURE_NAME, + mainScope: SPARTACUS_CART, + featureScope: SPARTACUS_CART_BASE, + }, + folderName: CART_FOLDER_NAME, + moduleName: CART_BASE_FEATURE_MODULE_NAME, + featureModule: [ + { + name: CART_BASE_MODULE, + importPath: SPARTACUS_CART_BASE, + }, + { + name: MINI_CART_MODULE, + importPath: MINI_CART_ENTRY_POINT, + }, + { + name: ADD_TO_CART_MODULE, + importPath: ADD_TO_CART_ENTRY_POINT, + }, + ], + rootModule: { + name: CART_BASE_ROOT_MODULE, + importPath: SPARTACUS_CART_BASE_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_CART_BASE_ROOT, + namedImports: [ + CART_BASE_FEATURE_NAME_CONSTANT, + MINI_CART_FEATURE_NAME_CONSTANT, + ADD_TO_CART_FEATURE_NAME_CONSTANT, + ], + }, + i18n: { + resources: CART_BASE_TRANSLATIONS, + chunks: CART_BASE_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CART_BASE_ASSETS, + }, + styles: { + scssFileName: CART_SCSS_FILE_NAME, + importStyle: SPARTACUS_CART, + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; + +export const CART_IMPORT_EXPORT_MODULE = 'ImportExportModule'; +export const CART_IMPORT_EXPORT_ROOT_MODULE = 'ImportExportRootModule'; +export const CART_IMPORT_EXPORT_MODULE_NAME = 'CartImportExport'; +export const CART_IMPORT_EXPORT_FEATURE_NAME_CONSTANT = + 'CART_IMPORT_EXPORT_FEATURE'; +export const CART_IMPORT_EXPORT_TRANSLATIONS = 'importExportTranslations'; +export const CART_IMPORT_EXPORT_TRANSLATION_CHUNKS_CONFIG = + 'importExportTranslationChunksConfig'; + +export const CART_IMPORT_EXPORT_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CART_IMPORT_EXPORT_FEATURE_NAME, + mainScope: SPARTACUS_CART, + featureScope: SPARTACUS_CART_IMPORT_EXPORT, + }, + folderName: CART_FOLDER_NAME, + moduleName: CART_IMPORT_EXPORT_MODULE_NAME, + featureModule: { + name: CART_IMPORT_EXPORT_MODULE, + importPath: SPARTACUS_CART_IMPORT_EXPORT, + }, + rootModule: { + name: CART_IMPORT_EXPORT_ROOT_MODULE, + importPath: SPARTACUS_CART_IMPORT_EXPORT_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_CART_IMPORT_EXPORT_ROOT, + namedImports: [CART_IMPORT_EXPORT_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: CART_IMPORT_EXPORT_TRANSLATIONS, + chunks: CART_IMPORT_EXPORT_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CART_IMPORT_EXPORT_ASSETS, + }, + styles: { + scssFileName: CART_SCSS_FILE_NAME, + importStyle: SPARTACUS_CART, + }, +}; + +export const QUICK_ORDER_MODULE = 'QuickOrderModule'; +export const QUICK_ORDER_ROOT_MODULE = 'QuickOrderRootModule'; +export const CART_QUICK_ORDER_MODULE_NAME = 'CartQuickOrder'; +export const CART_QUICK_ORDER_FEATURE_NAME_CONSTANT = + 'CART_QUICK_ORDER_FEATURE'; +export const QUICK_ORDER_TRANSLATIONS = 'quickOrderTranslations'; +export const QUICK_ORDER_TRANSLATION_CHUNKS_CONFIG = + 'quickOrderTranslationChunksConfig'; + +export const CART_QUICK_ORDER_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CART_QUICK_ORDER_FEATURE_NAME, + mainScope: SPARTACUS_CART, + featureScope: SPARTACUS_QUICK_ORDER, + }, + folderName: CART_FOLDER_NAME, + moduleName: CART_QUICK_ORDER_MODULE_NAME, + featureModule: { + name: QUICK_ORDER_MODULE, + importPath: SPARTACUS_QUICK_ORDER, + }, + rootModule: { + name: QUICK_ORDER_ROOT_MODULE, + importPath: SPARTACUS_QUICK_ORDER_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_QUICK_ORDER_ROOT, + namedImports: [CART_QUICK_ORDER_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: QUICK_ORDER_TRANSLATIONS, + chunks: QUICK_ORDER_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_QUICK_ORDER_ASSETS, + }, + styles: { + scssFileName: CART_SCSS_FILE_NAME, + importStyle: SPARTACUS_CART, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + }, +}; + +export const SAVED_CART_MODULE = 'SavedCartModule'; +export const SAVED_CART_ROOT_MODULE = 'SavedCartRootModule'; +export const CART_SAVED_CART_MODULE_NAME = 'CartSavedCart'; +export const CART_SAVED_CART_FEATURE_NAME_CONSTANT = 'CART_SAVED_CART_FEATURE'; +export const SAVED_CART_TRANSLATIONS = 'savedCartTranslations'; +export const SAVED_CART_TRANSLATION_CHUNKS_CONFIG = + 'savedCartTranslationChunksConfig'; + +export const CART_SAVED_CART_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CART_SAVED_CART_FEATURE_NAME, + mainScope: SPARTACUS_CART, + featureScope: SPARTACUS_SAVED_CART, + }, + folderName: CART_FOLDER_NAME, + moduleName: CART_SAVED_CART_MODULE_NAME, + featureModule: { + name: SAVED_CART_MODULE, + importPath: SPARTACUS_SAVED_CART, + }, + rootModule: { + name: SAVED_CART_ROOT_MODULE, + importPath: SPARTACUS_SAVED_CART_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_SAVED_CART_ROOT, + namedImports: [CART_SAVED_CART_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: SAVED_CART_TRANSLATIONS, + chunks: SAVED_CART_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_SAVED_CART_ASSETS, + }, + styles: { + scssFileName: CART_SCSS_FILE_NAME, + importStyle: SPARTACUS_CART, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + }, +}; + +export const CART_WISHLIST_FEATURE_MODULE_NAME = 'WishList'; +export const CART_WISHLIST_MODULE = 'WishListModule'; +export const ADD_TO_WISHLIST_MODULE = 'AddToWishListModule'; +export const CART_WISHLIST_ROOT_MODULE = 'WishListRootModule'; +export const CART_WISHLIST_FEATURE_NAME_CONSTANT = 'CART_WISH_LIST_FEATURE'; +export const ADD_TO_WISHLIST_FEATURE_NAME_CONSTANT = 'ADD_TO_WISHLIST_FEATURE'; +export const CART_WISHLIST_TRANSLATIONS = 'wishListTranslations'; +export const CART_WISHLIST_TRANSLATION_CHUNKS_CONFIG = + 'wishListTranslationChunksConfig'; + +export const CART_WISHLIST_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CART_WISHLIST_FEATURE_NAME, + mainScope: SPARTACUS_CART, + featureScope: SPARTACUS_CART_WISHLIST, + }, + folderName: CART_FOLDER_NAME, + moduleName: CART_WISHLIST_FEATURE_MODULE_NAME, + featureModule: [ + { + name: CART_WISHLIST_MODULE, + importPath: SPARTACUS_CART_WISHLIST, + }, + { + name: ADD_TO_WISHLIST_MODULE, + importPath: ADD_TO_WISHLIST_ENTRY_POINT, + }, + ], + rootModule: { + name: CART_WISHLIST_ROOT_MODULE, + importPath: SPARTACUS_CART_WISHLIST_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_CART_WISHLIST_ROOT, + namedImports: [ + CART_WISHLIST_FEATURE_NAME_CONSTANT, + ADD_TO_WISHLIST_FEATURE_NAME_CONSTANT, + ], + }, + i18n: { + resources: CART_WISHLIST_TRANSLATIONS, + chunks: CART_WISHLIST_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CART_WISHLIST_ASSETS, + }, + styles: { + scssFileName: CART_SCSS_FILE_NAME, + importStyle: SPARTACUS_CART, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/checkout-schematics-config.ts b/projects/schematics/src/shared/lib-configs/checkout-schematics-config.ts new file mode 100644 index 00000000000..815311d9b35 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/checkout-schematics-config.ts @@ -0,0 +1,149 @@ +import { + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + ORDER_FEATURE_NAME, + SPARTACUS_CHECKOUT, + SPARTACUS_CHECKOUT_B2B, + SPARTACUS_CHECKOUT_B2B_ASSETS, + SPARTACUS_CHECKOUT_B2B_ROOT, + SPARTACUS_CHECKOUT_BASE, + SPARTACUS_CHECKOUT_BASE_ASSETS, + SPARTACUS_CHECKOUT_BASE_ROOT, + SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT, + SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS, + SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT, + SPARTACUS_ORDER, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const CHECKOUT_FOLDER_NAME = 'checkout'; +export const CHECKOUT_SCSS_FILE_NAME = 'checkout.scss'; + +export const CHECKOUT_BASE_FEATURE_NAME_CONSTANT = 'CHECKOUT_FEATURE'; +export const CHECKOUT_BASE_MODULE_NAME = 'Checkout'; +export const CHECKOUT_BASE_MODULE = 'CheckoutModule'; +export const CHECKOUT_BASE_ROOT_MODULE = 'CheckoutRootModule'; +export const CHECKOUT_BASE_TRANSLATIONS = 'checkoutTranslations'; +export const CHECKOUT_BASE_TRANSLATION_CHUNKS_CONFIG = + 'checkoutTranslationChunksConfig'; + +export const CHECKOUT_BASE_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CHECKOUT_BASE_FEATURE_NAME, + mainScope: SPARTACUS_CHECKOUT, + featureScope: SPARTACUS_CHECKOUT_BASE, + }, + folderName: CHECKOUT_FOLDER_NAME, + moduleName: CHECKOUT_BASE_MODULE_NAME, + featureModule: { + name: CHECKOUT_BASE_MODULE, + importPath: SPARTACUS_CHECKOUT_BASE, + }, + rootModule: { + name: CHECKOUT_BASE_ROOT_MODULE, + importPath: SPARTACUS_CHECKOUT_BASE_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_CHECKOUT_BASE_ROOT, + namedImports: [CHECKOUT_BASE_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: CHECKOUT_BASE_TRANSLATIONS, + chunks: CHECKOUT_BASE_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CHECKOUT_BASE_ASSETS, + }, + styles: { + scssFileName: CHECKOUT_SCSS_FILE_NAME, + importStyle: SPARTACUS_CHECKOUT, + }, + dependencyFeatures: { + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + }, +}; + +export const CHECKOUT_B2B_MODULE = 'CheckoutB2BModule'; +export const CHECKOUT_B2B_ROOT_MODULE = 'CheckoutB2BRootModule'; +export const CHECKOUT_B2B_TRANSLATIONS = 'checkoutB2BTranslations'; +export const CHECKOUT_B2B_TRANSLATION_CHUNKS_CONFIG = + 'checkoutB2BTranslationChunksConfig'; + +export const CHECKOUT_B2B_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CHECKOUT_B2B_FEATURE_NAME, + mainScope: SPARTACUS_CHECKOUT, + featureScope: SPARTACUS_CHECKOUT_B2B, + b2b: true, + }, + folderName: CHECKOUT_FOLDER_NAME, + moduleName: CHECKOUT_BASE_MODULE_NAME, + featureModule: { + name: CHECKOUT_B2B_MODULE, + importPath: SPARTACUS_CHECKOUT_B2B, + }, + rootModule: { + name: CHECKOUT_B2B_ROOT_MODULE, + importPath: SPARTACUS_CHECKOUT_B2B_ROOT, + }, + i18n: { + resources: CHECKOUT_B2B_TRANSLATIONS, + chunks: CHECKOUT_B2B_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CHECKOUT_B2B_ASSETS, + }, + styles: { + scssFileName: CHECKOUT_SCSS_FILE_NAME, + importStyle: SPARTACUS_CHECKOUT, + }, + dependencyFeatures: { + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + [SPARTACUS_CHECKOUT]: [CHECKOUT_BASE_FEATURE_NAME], + }, + wrappers: { + [CHECKOUT_BASE_MODULE]: CHECKOUT_B2B_MODULE, + }, +}; + +export const CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE = + 'CheckoutScheduledReplenishmentModule'; +export const CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE = + 'CheckoutScheduledReplenishmentRootModule'; +export const CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATIONS = + 'checkoutScheduledReplenishmentTranslations'; +export const CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATION_CHUNKS_CONFIG = + 'checkoutScheduledReplenishmentTranslationChunksConfig'; + +export const CHECKOUT_SCHEDULED_REPLENISHMENT_SCHEMATICS_CONFIG: SchematicConfig = + { + library: { + featureName: CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + mainScope: SPARTACUS_CHECKOUT, + featureScope: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT, + b2b: true, + }, + folderName: CHECKOUT_FOLDER_NAME, + moduleName: CHECKOUT_BASE_MODULE_NAME, + featureModule: { + name: CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, + importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT, + }, + rootModule: { + name: CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, + importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT, + }, + i18n: { + resources: CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATIONS, + chunks: CHECKOUT_SCHEDULED_REPLENISHMENT_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS, + }, + styles: { + scssFileName: CHECKOUT_SCSS_FILE_NAME, + importStyle: SPARTACUS_CHECKOUT, + }, + dependencyFeatures: { + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + [SPARTACUS_CHECKOUT]: [CHECKOUT_B2B_FEATURE_NAME], + }, + wrappers: { + [CHECKOUT_BASE_MODULE]: CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, + }, + }; diff --git a/projects/schematics/src/shared/lib-configs/index.ts b/projects/schematics/src/shared/lib-configs/index.ts new file mode 100644 index 00000000000..5f639806aa2 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/index.ts @@ -0,0 +1,13 @@ +export * from './asm-schematics-config'; +export * from './cart-schematics-config'; +export * from './checkout-schematics-config'; +export * from './integration-libs/index'; +export * from './order-schematics-config'; +export * from './organization-schematics-config'; +export * from './product-configurator-schematics-config'; +export * from './product-schematics-config'; +export * from './qualtrics-schematics-config'; +export * from './smartedit-schematics-config'; +export * from './storefinder-schematics-config'; +export * from './tracking-schematics-config'; +export * from './user-schematics-config'; diff --git a/projects/schematics/src/shared/lib-configs/integration-libs/cdc-schematics-config.ts b/projects/schematics/src/shared/lib-configs/integration-libs/cdc-schematics-config.ts new file mode 100644 index 00000000000..62311a2e411 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/integration-libs/cdc-schematics-config.ts @@ -0,0 +1,75 @@ +import { + CDC_FEATURE_NAME, + SPARTACUS_CDC, + SPARTACUS_CDC_ROOT, + SPARTACUS_USER, + USER_PROFILE_FEATURE_NAME, +} from '../../libs-constants'; +import { AdditionalFeatureConfiguration } from '../../utils/feature-utils'; +import { LibraryOptions, SchematicConfig } from '../../utils/lib-utils'; + +export interface SpartacusCdcOptions extends LibraryOptions { + baseSite?: string; + javascriptUrl?: string; + sessionExpiration?: number; +} + +export const CDC_FOLDER_NAME = 'cdc'; +export const CDC_MODULE_NAME = 'Cdc'; + +export const CDC_MODULE = 'CdcModule'; +export const CDC_ROOT_MODULE = 'CdcRootModule'; +export const CDC_FEATURE_CONSTANT = 'CDC_FEATURE'; +export const CDC_CONFIG = 'CdcConfig'; + +export const CDC_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CDC_FEATURE_NAME, + mainScope: SPARTACUS_CDC, + }, + folderName: CDC_FOLDER_NAME, + moduleName: CDC_MODULE_NAME, + featureModule: { + importPath: SPARTACUS_CDC, + name: CDC_MODULE, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_CDC_ROOT, + namedImports: [CDC_FEATURE_CONSTANT], + }, + rootModule: { + importPath: SPARTACUS_CDC_ROOT, + name: CDC_ROOT_MODULE, + content: `${CDC_ROOT_MODULE}`, + }, + customConfig: buildCdcConfig, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; + +function buildCdcConfig( + options: SpartacusCdcOptions +): AdditionalFeatureConfiguration { + return { + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_CDC_ROOT, + namedImports: [CDC_CONFIG], + }, + ], + content: `<${CDC_CONFIG}>{ + cdc: [ + { + baseSite: '${options.baseSite || 'BASE_SITE_PLACEHOLDER'}', + javascriptUrl: '${ + options.javascriptUrl || 'JS_SDK_URL_PLACEHOLDER' + }', + sessionExpiration: ${options.sessionExpiration || 3600} + }, + ], + }`, + }, + }; +} diff --git a/projects/schematics/src/shared/lib-configs/integration-libs/cds-schematics-config.ts b/projects/schematics/src/shared/lib-configs/integration-libs/cds-schematics-config.ts new file mode 100644 index 00000000000..1da9e396da4 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/integration-libs/cds-schematics-config.ts @@ -0,0 +1,103 @@ +import { CDS_CONFIG } from '../../constants'; +import { + CDS_FEATURE_NAME, + SPARTACUS_CDS, + SPARTACUS_TRACKING, + TRACKING_PERSONALIZATION_FEATURE_NAME, +} from '../../libs-constants'; +import { + AdditionalFeatureConfiguration, + AdditionalProviders, +} from '../../utils/feature-utils'; +import { LibraryOptions, SchematicConfig } from '../../utils/lib-utils'; + +export interface SpartacusCdsOptions extends LibraryOptions { + tenant?: string; + baseUrl?: string; + profileTagLoadUrl?: string; + profileTagConfigUrl?: string; +} + +export const CDS_FOLDER_NAME = 'cds'; +export const CDS_MODULE_NAME = 'Cds'; + +export const CDS_MODULE = 'CdsModule'; + +export const CDS_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: CDS_FEATURE_NAME, + mainScope: SPARTACUS_CDS, + }, + folderName: CDS_FOLDER_NAME, + moduleName: CDS_MODULE_NAME, + featureModule: { + importPath: SPARTACUS_CDS, + name: CDS_MODULE, + content: `${CDS_MODULE}.forRoot()`, + }, + customConfig: buildCdsConfig, + dependencyFeatures: { + [SPARTACUS_TRACKING]: [TRACKING_PERSONALIZATION_FEATURE_NAME], + }, +}; + +function buildCdsConfig( + options: SpartacusCdsOptions +): AdditionalFeatureConfiguration { + const customConfig: AdditionalProviders[] = [ + { + import: [ + { + moduleSpecifier: SPARTACUS_CDS, + namedImports: [CDS_CONFIG], + }, + ], + content: `<${CDS_CONFIG}>{ + cds: { + tenant: '${options.tenant || 'TENANT_PLACEHOLDER'}', + baseUrl: '${options.baseUrl || 'BASE_URL_PLACEHOLDER'}', + endpoints: { + strategyProducts: '/strategy/\${tenant}/strategies/\${strategyId}/products', + }, + merchandising: { + defaultCarouselViewportThreshold: 80, + }, + }, + }`, + }, + ]; + + customConfig.push({ + import: [ + { + moduleSpecifier: SPARTACUS_CDS, + namedImports: [CDS_CONFIG], + }, + ], + content: `<${CDS_CONFIG}>{ + cds: { + profileTag: { + javascriptUrl: + '${ + options.profileTagLoadUrl || + 'PROFILE_TAG_LOAD_URL_PLACEHOLDER' + }', + configUrl: + '${ + options.profileTagConfigUrl || + 'PROFILE_TAG_CONFIG_URL_PLACEHOLDER' + }', + allowInsecureCookies: true, + }, + }, + }`, + }); + + return { + providers: customConfig, + options: { + ...options, + lazy: false, + }, + }; +} diff --git a/projects/schematics/src/shared/lib-configs/integration-libs/digital-payments-schematics-config.ts b/projects/schematics/src/shared/lib-configs/integration-libs/digital-payments-schematics-config.ts new file mode 100644 index 00000000000..c519b161272 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/integration-libs/digital-payments-schematics-config.ts @@ -0,0 +1,40 @@ +import { + CHECKOUT_BASE_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + SPARTACUS_CHECKOUT, + SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_DIGITAL_PAYMENTS_ASSETS, +} from '../../libs-constants'; +import { SchematicConfig } from '../../utils/lib-utils'; +import { CHECKOUT_BASE_MODULE } from '../checkout-schematics-config'; + +export const DIGITAL_PAYMENTS_FOLDER_NAME = 'digital-payments'; +export const DIGITAL_PAYMENTS_MODULE_NAME = 'DigitalPayments'; +export const DIGITAL_PAYMENTS_MODULE = 'DigitalPaymentsModule'; +export const DIGITAL_PAYMENTS_TRANSLATIONS = 'dpTranslations'; +export const DIGITAL_PAYMENTS_TRANSLATION_CHUNKS_CONFIG = + 'dpTranslationChunksConfig'; + +export const DIGITAL_PAYMENTS_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: DIGITAL_PAYMENTS_FEATURE_NAME, + mainScope: SPARTACUS_DIGITAL_PAYMENTS, + }, + folderName: DIGITAL_PAYMENTS_FOLDER_NAME, + moduleName: DIGITAL_PAYMENTS_MODULE_NAME, + featureModule: { + name: DIGITAL_PAYMENTS_MODULE, + importPath: SPARTACUS_DIGITAL_PAYMENTS, + }, + i18n: { + resources: DIGITAL_PAYMENTS_TRANSLATIONS, + chunks: DIGITAL_PAYMENTS_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_DIGITAL_PAYMENTS_ASSETS, + }, + dependencyFeatures: { + [SPARTACUS_CHECKOUT]: [CHECKOUT_BASE_FEATURE_NAME], + }, + wrappers: { + [CHECKOUT_BASE_MODULE]: DIGITAL_PAYMENTS_MODULE, + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/integration-libs/epd-schematics-config.ts b/projects/schematics/src/shared/lib-configs/integration-libs/epd-schematics-config.ts new file mode 100644 index 00000000000..cca07f02064 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/integration-libs/epd-schematics-config.ts @@ -0,0 +1,82 @@ +import { EPD_VISUALIZATION_CONFIG } from '../../constants'; +import { + EPD_VISUALIZATION_FEATURE_NAME, + SPARTACUS_EPD_VISUALIZATION, + SPARTACUS_EPD_VISUALIZATION_ASSETS, + SPARTACUS_EPD_VISUALIZATION_ROOT, +} from '../../libs-constants'; +import { AdditionalFeatureConfiguration } from '../../utils/feature-utils'; +import { LibraryOptions, SchematicConfig } from '../../utils/lib-utils'; + +export interface SpartacusEpdVisualizationOptions extends LibraryOptions { + baseUrl?: string; +} + +export const EPD_VISUALIZATION_FOLDER_NAME = 'epd-visualization'; +export const EPD_VISUALIZATION_MODULE_NAME = 'EpdVisualization'; +export const EPD_SCSS_FILE_NAME = 'epd-visualization.scss'; + +export const EPD_VISUALIZATION_FEATURE_NAME_CONSTANT = + 'EPD_VISUALIZATION_FEATURE'; +export const EPD_VISUALIZATION_MODULE = 'EpdVisualizationModule'; +export const EPD_VISUALIZATION_ROOT_MODULE = 'EpdVisualizationRootModule'; +export const EPD_VISUALIZATION_TRANSLATIONS = 'epdVisualizationTranslations'; +export const EPD_VISUALIZATION_TRANSLATION_CHUNKS_CONFIG = + 'epdVisualizationTranslationChunksConfig'; + +export const EPD_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: EPD_VISUALIZATION_FEATURE_NAME, + mainScope: SPARTACUS_EPD_VISUALIZATION, + }, + folderName: EPD_VISUALIZATION_FOLDER_NAME, + moduleName: EPD_VISUALIZATION_MODULE_NAME, + featureModule: { + name: EPD_VISUALIZATION_MODULE, + importPath: SPARTACUS_EPD_VISUALIZATION, + }, + rootModule: { + name: EPD_VISUALIZATION_ROOT_MODULE, + importPath: SPARTACUS_EPD_VISUALIZATION_ROOT, + }, + customConfig: buildCdsConfig, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_EPD_VISUALIZATION_ROOT, + namedImports: [EPD_VISUALIZATION_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: EPD_VISUALIZATION_TRANSLATIONS, + chunks: EPD_VISUALIZATION_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_EPD_VISUALIZATION_ASSETS, + }, + styles: { + scssFileName: EPD_SCSS_FILE_NAME, + importStyle: SPARTACUS_EPD_VISUALIZATION, + }, +}; + +function buildCdsConfig( + options: SpartacusEpdVisualizationOptions +): AdditionalFeatureConfiguration { + return { + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_EPD_VISUALIZATION_ROOT, + namedImports: [EPD_VISUALIZATION_CONFIG], + }, + ], + content: `<${EPD_VISUALIZATION_CONFIG}>{ + epdVisualization: { + ui5: { + bootstrapUrl: "https://sapui5.hana.ondemand.com/1.98.0/resources/sap-ui-core.js" + }, + + apis: { + baseUrl: "${options.baseUrl || 'PLACEHOLDER_BASE_URL'}" + } + } + }`, + }, + }; +} diff --git a/projects/schematics/src/shared/lib-configs/integration-libs/index.ts b/projects/schematics/src/shared/lib-configs/integration-libs/index.ts new file mode 100644 index 00000000000..9fcac7ffbd1 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/integration-libs/index.ts @@ -0,0 +1,4 @@ +export * from './cdc-schematics-config'; +export * from './cds-schematics-config'; +export * from './digital-payments-schematics-config'; +export * from './epd-schematics-config'; diff --git a/projects/schematics/src/shared/lib-configs/order-schematics-config.ts b/projects/schematics/src/shared/lib-configs/order-schematics-config.ts new file mode 100644 index 00000000000..8e2d1b3a7af --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/order-schematics-config.ts @@ -0,0 +1,52 @@ +import { + CART_BASE_FEATURE_NAME, + ORDER_FEATURE_NAME, + SPARTACUS_CART, + SPARTACUS_ORDER, + SPARTACUS_ORDER_ASSETS, + SPARTACUS_ORDER_ROOT, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const ORDER_FOLDER_NAME = 'order'; +export const ORDER_MODULE_NAME = 'Order'; +export const ORDER_SCSS_FILE_NAME = 'order.scss'; + +export const ORDER_MODULE = 'OrderModule'; +export const ORDER_ROOT_MODULE = 'OrderRootModule'; +export const ORDER_FEATURE_NAME_CONSTANT = 'ORDER_FEATURE'; +export const ORDER_TRANSLATIONS = 'orderTranslations'; +export const ORDER_TRANSLATION_CHUNKS_CONFIG = 'orderTranslationChunksConfig'; + +export const ORDER_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: ORDER_FEATURE_NAME, + mainScope: SPARTACUS_ORDER, + }, + folderName: ORDER_FOLDER_NAME, + moduleName: ORDER_MODULE_NAME, + featureModule: { + name: ORDER_MODULE, + importPath: SPARTACUS_ORDER, + }, + rootModule: { + name: ORDER_ROOT_MODULE, + importPath: SPARTACUS_ORDER_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_ORDER_ROOT, + namedImports: [ORDER_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: ORDER_TRANSLATIONS, + chunks: ORDER_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_ORDER_ASSETS, + }, + styles: { + scssFileName: ORDER_SCSS_FILE_NAME, + importStyle: SPARTACUS_ORDER, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/organization-schematics-config.ts b/projects/schematics/src/shared/lib-configs/organization-schematics-config.ts new file mode 100644 index 00000000000..57d7c54d644 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/organization-schematics-config.ts @@ -0,0 +1,110 @@ +import { + ORDER_FEATURE_NAME, + ORGANIZATION_ADMINISTRATION_FEATURE_NAME, + ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME, + SPARTACUS_ADMINISTRATION, + SPARTACUS_ORDER, + SPARTACUS_ORGANIZATION, + SPARTACUS_ORGANIZATION_ADMINISTRATION_ASSETS, + SPARTACUS_ORGANIZATION_ADMINISTRATION_ROOT, + SPARTACUS_ORGANIZATION_ORDER_APPROVAL, + SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ASSETS, + SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ROOT, + SPARTACUS_USER, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const ORGANIZATION_FOLDER_NAME = 'organization'; +export const ORGANIZATION_SCSS_FILE_NAME = 'organization.scss'; + +export const ADMINISTRATION_MODULE = 'AdministrationModule'; +export const ADMINISTRATION_ROOT_MODULE = 'AdministrationRootModule'; +export const ORGANIZATION_ADMINISTRATION_MODULE_NAME = + 'OrganizationAdministration'; +export const ORGANIZATION_ADMINISTRATION_FEATURE_NAME_CONSTANT = + 'ORGANIZATION_ADMINISTRATION_FEATURE'; +export const ORGANIZATION_TRANSLATIONS = 'organizationTranslations'; +export const ORGANIZATION_TRANSLATION_CHUNKS_CONFIG = + 'organizationTranslationChunksConfig'; + +export const ORGANIZATION_ADMINISTRATION_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: ORGANIZATION_ADMINISTRATION_FEATURE_NAME, + mainScope: SPARTACUS_ORGANIZATION, + featureScope: SPARTACUS_ADMINISTRATION, + b2b: true, + }, + folderName: ORGANIZATION_FOLDER_NAME, + moduleName: ORGANIZATION_ADMINISTRATION_MODULE_NAME, + featureModule: { + name: ADMINISTRATION_MODULE, + importPath: SPARTACUS_ADMINISTRATION, + }, + rootModule: { + name: ADMINISTRATION_ROOT_MODULE, + importPath: SPARTACUS_ORGANIZATION_ADMINISTRATION_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_ORGANIZATION_ADMINISTRATION_ROOT, + namedImports: [ORGANIZATION_ADMINISTRATION_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: ORGANIZATION_TRANSLATIONS, + chunks: ORGANIZATION_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_ORGANIZATION_ADMINISTRATION_ASSETS, + }, + styles: { + scssFileName: ORGANIZATION_SCSS_FILE_NAME, + importStyle: SPARTACUS_ORGANIZATION, + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; + +export const ORDER_APPROVAL_MODULE = 'OrderApprovalModule'; +export const ORDER_APPROVAL_ROOT_MODULE = 'OrderApprovalRootModule'; +export const ORGANIZATION_ORDER_APPROVAL_MODULE_NAME = + 'OrganizationOrderApproval'; +export const ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME_CONSTANT = + 'ORGANIZATION_ORDER_APPROVAL_FEATURE'; +export const ORDER_APPROVAL_TRANSLATIONS = 'orderApprovalTranslations'; +export const ORDER_APPROVAL_TRANSLATION_CHUNKS_CONFIG = + 'orderApprovalTranslationChunksConfig'; + +export const ORGANIZATION_ORDER_APPROVAL_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME, + mainScope: SPARTACUS_ORGANIZATION, + featureScope: SPARTACUS_ORGANIZATION_ORDER_APPROVAL, + b2b: true, + }, + folderName: ORGANIZATION_FOLDER_NAME, + moduleName: ORGANIZATION_ORDER_APPROVAL_MODULE_NAME, + featureModule: { + name: ORDER_APPROVAL_MODULE, + importPath: SPARTACUS_ORGANIZATION_ORDER_APPROVAL, + }, + rootModule: { + name: ORDER_APPROVAL_ROOT_MODULE, + importPath: SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ROOT, + namedImports: [ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: ORDER_APPROVAL_TRANSLATIONS, + chunks: ORDER_APPROVAL_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ASSETS, + }, + styles: { + scssFileName: ORGANIZATION_SCSS_FILE_NAME, + importStyle: SPARTACUS_ORGANIZATION, + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/product-configurator-schematics-config.ts b/projects/schematics/src/shared/lib-configs/product-configurator-schematics-config.ts new file mode 100644 index 00000000000..f65f4dc4889 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/product-configurator-schematics-config.ts @@ -0,0 +1,149 @@ +import { + CART_BASE_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + ORDER_FEATURE_NAME, + PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME, + PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME, + PRODUCT_CONFIGURATOR_VC_FEATURE_NAME, + SPARTACUS_CART, + SPARTACUS_CHECKOUT, + SPARTACUS_ORDER, + SPARTACUS_PRODUCT_CONFIGURATOR, + SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, + SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED, + SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ, + SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, + SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD, + SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const PRODUCT_CONFIGURATOR_MODULE_NAME = 'ProductConfigurator'; +export const PRODUCT_CONFIGURATOR_FOLDER_NAME = 'product-configurator'; +export const PRODUCT_CONFIGURATOR_SCSS_FILE_NAME = 'product-configurator.scss'; + +export const PRODUCT_CONFIGURATOR_TRANSLATIONS = 'configuratorTranslations'; +export const PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG = + 'configuratorTranslationChunksConfig'; +export const PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE = + 'TextfieldConfiguratorModule'; +export const PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE = + 'TextfieldConfiguratorRootModule'; +export const PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE_NAME = + 'ProductConfiguratorTextfield'; +export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME_CONSTANT = + 'PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE'; + +export const PRODUCT_CONFIGURATOR_TEXTFIELD_SCHEMATICS_CONFIG: SchematicConfig = + { + library: { + featureName: PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT_CONFIGURATOR, + featureScope: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD, + }, + folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, + moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, + featureModule: { + name: PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD, + }, + rootModule: { + name: PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT, + namedImports: [PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: PRODUCT_CONFIGURATOR_TRANSLATIONS, + chunks: PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, + }, + styles: { + scssFileName: PRODUCT_CONFIGURATOR_SCSS_FILE_NAME, + importStyle: SPARTACUS_PRODUCT_CONFIGURATOR, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + [SPARTACUS_CHECKOUT]: [CHECKOUT_BASE_FEATURE_NAME], + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + }, + }; + +export const PRODUCT_CONFIGURATOR_RULEBASED_MODULE_NAME = + 'ProductConfiguratorRulebased'; +export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT = + 'PRODUCT_CONFIGURATOR_RULEBASED_FEATURE'; +export const PRODUCT_CONFIGURATOR_RULEBASED_MODULE = + 'RulebasedConfiguratorModule'; +export const PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE = + 'RulebasedConfiguratorRootModule'; + +export const PRODUCT_CONFIGURATOR_RULEBASED_SCHEMATICS_CONFIG: SchematicConfig = + { + library: { + featureName: PRODUCT_CONFIGURATOR_VC_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT_CONFIGURATOR, + featureScope: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED, + }, + folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, + moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, + featureModule: { + name: PRODUCT_CONFIGURATOR_RULEBASED_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED, + }, + rootModule: { + name: PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, + namedImports: [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: PRODUCT_CONFIGURATOR_TRANSLATIONS, + chunks: PRODUCT_CONFIGURATOR_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS, + }, + styles: { + scssFileName: PRODUCT_CONFIGURATOR_SCSS_FILE_NAME, + importStyle: SPARTACUS_PRODUCT_CONFIGURATOR, + }, + dependencyFeatures: { + [SPARTACUS_CART]: [CART_BASE_FEATURE_NAME], + [SPARTACUS_CHECKOUT]: [CHECKOUT_BASE_FEATURE_NAME], + [SPARTACUS_ORDER]: [ORDER_FEATURE_NAME], + }, + }; + +export const PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE = + 'RulebasedCpqConfiguratorModule'; +export const PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE = + 'CpqConfiguratorRootModule'; + +export const PRODUCT_CONFIGURATOR_CPQ_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT_CONFIGURATOR, + featureScope: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ, + b2b: true, + }, + folderName: PRODUCT_CONFIGURATOR_FOLDER_NAME, + moduleName: PRODUCT_CONFIGURATOR_MODULE_NAME, + featureModule: { + name: PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ, + }, + rootModule: { + name: PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE, + importPath: SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT, + }, + dependencyFeatures: { + [SPARTACUS_PRODUCT_CONFIGURATOR]: [PRODUCT_CONFIGURATOR_VC_FEATURE_NAME], + }, + wrappers: { + [PRODUCT_CONFIGURATOR_RULEBASED_MODULE]: + PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/product-schematics-config.ts b/projects/schematics/src/shared/lib-configs/product-schematics-config.ts new file mode 100644 index 00000000000..097c4c8e97f --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/product-schematics-config.ts @@ -0,0 +1,138 @@ +import { + PRODUCT_BULK_PRICING_FEATURE_NAME, + PRODUCT_IMAGE_ZOOM_FEATURE_NAME, + PRODUCT_VARIANTS_FEATURE_NAME, + SPARTACUS_BULK_PRICING, + SPARTACUS_BULK_PRICING_ASSETS, + SPARTACUS_BULK_PRICING_ROOT, + SPARTACUS_IMAGE_ZOOM, + SPARTACUS_IMAGE_ZOOM_ASSETS, + SPARTACUS_IMAGE_ZOOM_ROOT, + SPARTACUS_PRODUCT, + SPARTACUS_VARIANTS, + SPARTACUS_VARIANTS_ASSETS, + SPARTACUS_VARIANTS_ROOT, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const PRODUCT_FOLDER_NAME = 'product'; +export const PRODUCT_SCSS_FILE_NAME = 'product.scss'; + +export const BULK_PRICING_MODULE = 'BulkPricingModule'; +export const BULK_PRICING_ROOT_MODULE = 'BulkPricingRootModule'; +export const BULK_PRICING_MODULE_NAME = 'ProductBulkPricing'; +export const BULK_PRICING_FEATURE_NAME_CONSTANT = + 'PRODUCT_BULK_PRICING_FEATURE'; +export const BULK_PRICING_TRANSLATIONS = 'bulkPricingTranslations'; +export const BULK_PRICING_TRANSLATION_CHUNKS_CONFIG = + 'bulkPricingTranslationChunksConfig'; + +export const PRODUCT_BULK_PRICING_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: PRODUCT_BULK_PRICING_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT, + featureScope: SPARTACUS_BULK_PRICING, + b2b: true, + }, + folderName: PRODUCT_FOLDER_NAME, + moduleName: BULK_PRICING_MODULE_NAME, + featureModule: { + name: BULK_PRICING_MODULE, + importPath: SPARTACUS_BULK_PRICING, + }, + rootModule: { + name: BULK_PRICING_ROOT_MODULE, + importPath: SPARTACUS_BULK_PRICING_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_BULK_PRICING_ROOT, + namedImports: [BULK_PRICING_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: BULK_PRICING_TRANSLATIONS, + chunks: BULK_PRICING_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_BULK_PRICING_ASSETS, + }, + styles: { + scssFileName: PRODUCT_SCSS_FILE_NAME, + importStyle: SPARTACUS_PRODUCT, + }, +}; + +export const IMAGE_ZOOM_MODULE = 'ProductImageZoomModule'; +export const IMAGE_ZOOM_ROOT_MODULE = 'ProductImageZoomRootModule'; +export const IMAGE_ZOOM_MODULE_NAME = 'ProductImageZoom'; +export const IMAGE_ZOOM_FEATURE_NAME_CONSTANT = 'PRODUCT_IMAGE_ZOOM_FEATURE'; +export const IMAGE_ZOOM_TRANSLATIONS = 'productImageZoomTranslations'; +export const IMAGE_ZOOM_TRANSLATION_CHUNKS_CONFIG = + 'productImageZoomTranslationChunksConfig'; + +export const PRODUCT_IMAGE_ZOOM_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: PRODUCT_IMAGE_ZOOM_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT, + featureScope: SPARTACUS_IMAGE_ZOOM, + }, + folderName: PRODUCT_FOLDER_NAME, + moduleName: IMAGE_ZOOM_MODULE_NAME, + featureModule: { + name: IMAGE_ZOOM_MODULE, + importPath: SPARTACUS_IMAGE_ZOOM, + }, + rootModule: { + name: IMAGE_ZOOM_ROOT_MODULE, + importPath: SPARTACUS_IMAGE_ZOOM_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_IMAGE_ZOOM_ROOT, + namedImports: [IMAGE_ZOOM_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: IMAGE_ZOOM_TRANSLATIONS, + chunks: IMAGE_ZOOM_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_IMAGE_ZOOM_ASSETS, + }, + styles: { + scssFileName: PRODUCT_SCSS_FILE_NAME, + importStyle: SPARTACUS_PRODUCT, + }, +}; + +export const VARIANTS_MODULE = 'ProductVariantsModule'; +export const VARIANTS_ROOT_MODULE = 'ProductVariantsRootModule'; +export const VARIANTS_MODULE_NAME = 'ProductVariants'; +export const VARIANTS_FEATURE_NAME_CONSTANT = 'PRODUCT_VARIANTS_FEATURE'; +export const VARIANTS_TRANSLATIONS = 'productVariantsTranslations'; +export const VARIANTS_TRANSLATION_CHUNKS_CONFIG = + 'productVariantsTranslationChunksConfig'; + +export const PRODUCT_VARIANTS_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: PRODUCT_VARIANTS_FEATURE_NAME, + mainScope: SPARTACUS_PRODUCT, + featureScope: SPARTACUS_VARIANTS, + }, + folderName: PRODUCT_FOLDER_NAME, + moduleName: VARIANTS_MODULE_NAME, + featureModule: { + name: VARIANTS_MODULE, + importPath: SPARTACUS_VARIANTS, + }, + rootModule: { + name: VARIANTS_ROOT_MODULE, + importPath: SPARTACUS_VARIANTS_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_VARIANTS_ROOT, + namedImports: [VARIANTS_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: VARIANTS_TRANSLATIONS, + chunks: VARIANTS_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_VARIANTS_ASSETS, + }, + styles: { + scssFileName: PRODUCT_SCSS_FILE_NAME, + importStyle: SPARTACUS_PRODUCT, + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/qualtrics-schematics-config.ts b/projects/schematics/src/shared/lib-configs/qualtrics-schematics-config.ts new file mode 100644 index 00000000000..43f8ce5ae3c --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/qualtrics-schematics-config.ts @@ -0,0 +1,40 @@ +import { + QUALTRICS_FEATURE_NAME, + SPARTACUS_QUALTRICS, + SPARTACUS_QUALTRICS_ROOT, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const QUALTRICS_FOLDER_NAME = 'qualtrics'; +export const QUALTRICS_MODULE_NAME = 'Qualtrics'; +export const QUALTRICS_EMBEDDED_FEEDBACK_SCSS_FILE_NAME = + 'qualtrics-embedded-feedback.scss'; + +export const QUALTRICS_MODULE = 'QualtricsModule'; +export const QUALTRICS_ROOT_MODULE = 'QualtricsRootModule'; +export const QUALTRICS_FEATURE_NAME_CONSTANT = 'QUALTRICS_FEATURE'; + +export const QUALTRICS_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: QUALTRICS_FEATURE_NAME, + mainScope: SPARTACUS_QUALTRICS, + }, + folderName: QUALTRICS_FOLDER_NAME, + moduleName: QUALTRICS_MODULE_NAME, + featureModule: { + name: QUALTRICS_MODULE, + importPath: SPARTACUS_QUALTRICS, + }, + rootModule: { + name: QUALTRICS_ROOT_MODULE, + importPath: SPARTACUS_QUALTRICS_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_QUALTRICS_ROOT, + namedImports: [QUALTRICS_FEATURE_NAME_CONSTANT], + }, + styles: { + scssFileName: QUALTRICS_EMBEDDED_FEEDBACK_SCSS_FILE_NAME, + importStyle: SPARTACUS_QUALTRICS, + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/smartedit-schematics-config.ts b/projects/schematics/src/shared/lib-configs/smartedit-schematics-config.ts new file mode 100644 index 00000000000..0e14d371142 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/smartedit-schematics-config.ts @@ -0,0 +1,69 @@ +import { SMART_EDIT_CONFIG } from '../constants'; +import { + SMARTEDIT_FEATURE_NAME, + SPARTACUS_SMARTEDIT, + SPARTACUS_SMARTEDIT_ROOT, +} from '../libs-constants'; +import { AdditionalFeatureConfiguration } from '../utils/feature-utils'; +import { LibraryOptions, SchematicConfig } from '../utils/lib-utils'; + +export interface SpartacusSmartEditOptions extends LibraryOptions { + storefrontPreviewRoute?: string; + allowOrigin?: string; +} + +export const SMARTEDIT_FOLDER_NAME = 'smartedit'; +export const SMARTEDIT_MODULE_NAME = 'SmartEdit'; +export const SMARTEDIT_MODULE = 'SmartEditModule'; +export const SMARTEDIT_ROOT_MODULE = 'SmartEditRootModule'; +export const SMARTEDIT_FEATURE_NAME_CONSTANT = 'SMART_EDIT_FEATURE'; +export const SPARTACUS_SMARTEDIT_ASSETS = 'smartedit/assets'; + +export const SMARTEDIT_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: SMARTEDIT_FEATURE_NAME, + mainScope: SPARTACUS_SMARTEDIT, + }, + folderName: SMARTEDIT_FOLDER_NAME, + moduleName: SMARTEDIT_MODULE_NAME, + featureModule: { + name: SMARTEDIT_MODULE, + importPath: SPARTACUS_SMARTEDIT, + }, + rootModule: { + name: SMARTEDIT_ROOT_MODULE, + importPath: SPARTACUS_SMARTEDIT_ROOT, + }, + customConfig: buildSmartEditConfig, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_SMARTEDIT_ROOT, + namedImports: [SMARTEDIT_FEATURE_NAME_CONSTANT], + }, + assets: { + input: SPARTACUS_SMARTEDIT_ASSETS, + glob: '**/*', + }, +}; + +function buildSmartEditConfig( + options: SpartacusSmartEditOptions +): AdditionalFeatureConfiguration { + return { + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_SMARTEDIT_ROOT, + namedImports: [SMART_EDIT_CONFIG], + }, + ], + content: `<${SMART_EDIT_CONFIG}>{ + smartEdit: { + storefrontPreviewRoute: '${ + options.storefrontPreviewRoute || + 'STOREFRONT_PREVIEW_ROUTE_PLACEHOLDER' + }', + allowOrigin: '${options.allowOrigin || 'ALLOWED_ORIGIN_PLACEHOLDER'}', + },}`, + }, + }; +} diff --git a/projects/schematics/src/shared/lib-configs/storefinder-schematics-config.ts b/projects/schematics/src/shared/lib-configs/storefinder-schematics-config.ts new file mode 100644 index 00000000000..8cad869252a --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/storefinder-schematics-config.ts @@ -0,0 +1,48 @@ +import { + SPARTACUS_STOREFINDER, + SPARTACUS_STOREFINDER_ASSETS, + SPARTACUS_STOREFINDER_ROOT, + STOREFINDER_FEATURE_NAME, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const STOREFINDER_FOLDER_NAME = 'storefinder'; +export const STOREFINDER_MODULE_NAME = 'StoreFinder'; +export const STOREFINDER_SCSS_FILE_NAME = 'storefinder.scss'; + +export const STOREFINDER_MODULE = 'StoreFinderModule'; +export const STOREFINDER_ROOT_MODULE = 'StoreFinderRootModule'; +export const STOREFINDER_FEATURE_NAME_CONSTANT = 'STORE_FINDER_FEATURE'; +export const STOREFINDER_TRANSLATIONS = 'storeFinderTranslations'; +export const STOREFINDER_TRANSLATION_CHUNKS_CONFIG = + 'storeFinderTranslationChunksConfig'; + +export const STOREFINDER_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: STOREFINDER_FEATURE_NAME, + mainScope: SPARTACUS_STOREFINDER, + }, + folderName: STOREFINDER_FOLDER_NAME, + moduleName: STOREFINDER_MODULE_NAME, + featureModule: { + name: STOREFINDER_MODULE, + importPath: SPARTACUS_STOREFINDER, + }, + rootModule: { + name: STOREFINDER_ROOT_MODULE, + importPath: SPARTACUS_STOREFINDER_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_STOREFINDER_ROOT, + namedImports: [STOREFINDER_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: STOREFINDER_TRANSLATIONS, + chunks: STOREFINDER_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_STOREFINDER_ASSETS, + }, + styles: { + scssFileName: STOREFINDER_SCSS_FILE_NAME, + importStyle: SPARTACUS_STOREFINDER, + }, +}; diff --git a/projects/schematics/src/shared/lib-configs/tracking-schematics-config.ts b/projects/schematics/src/shared/lib-configs/tracking-schematics-config.ts new file mode 100644 index 00000000000..c1f5e2b2502 --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/tracking-schematics-config.ts @@ -0,0 +1,155 @@ +import { + SPARTACUS_PERSONALIZATION, + SPARTACUS_PERSONALIZATION_ROOT, + SPARTACUS_TMS_AEP, + SPARTACUS_TMS_CORE, + SPARTACUS_TMS_GTM, + SPARTACUS_TRACKING, + SPARTACUS_USER, + TRACKING_PERSONALIZATION_FEATURE_NAME, + TRACKING_TMS_AEP_FEATURE_NAME, + TRACKING_TMS_GTM_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { AdditionalFeatureConfiguration } from '../utils/feature-utils'; +import { LibraryOptions, SchematicConfig } from '../utils/lib-utils'; + +export const TRACKING_FOLDER_NAME = 'tracking'; + +export const PERSONALIZATION_MODULE = 'PersonalizationModule'; +export const PERSONALIZATION_ROOT_MODULE = 'PersonalizationRootModule'; +export const PERSONALIZATION_MODULE_NAME = 'Personalization'; +export const PERSONALIZATION_FEATURE_NAME_CONSTANT = 'PERSONALIZATION_FEATURE'; + +export const TRACKING_PERSONALIZATION_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: TRACKING_PERSONALIZATION_FEATURE_NAME, + mainScope: SPARTACUS_TRACKING, + featureScope: SPARTACUS_PERSONALIZATION, + }, + folderName: TRACKING_FOLDER_NAME, + moduleName: PERSONALIZATION_MODULE_NAME, + featureModule: { + name: PERSONALIZATION_MODULE, + importPath: SPARTACUS_PERSONALIZATION, + }, + rootModule: { + name: PERSONALIZATION_ROOT_MODULE, + importPath: SPARTACUS_PERSONALIZATION_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_PERSONALIZATION_ROOT, + namedImports: [PERSONALIZATION_FEATURE_NAME_CONSTANT], + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; + +export const TMS_MODULE_NAME = 'TagManagement'; +export const TMS_CONFIG = 'TmsConfig'; +export const TMS_BASE_MODULE = 'BaseTmsModule'; +export const TMS_GTM_MODULE = 'GtmModule'; + +export const TRACKING_GTM_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: TRACKING_TMS_GTM_FEATURE_NAME, + mainScope: SPARTACUS_TRACKING, + featureScope: SPARTACUS_TMS_GTM, + }, + folderName: TRACKING_FOLDER_NAME, + moduleName: TMS_MODULE_NAME, + featureModule: { + name: TMS_GTM_MODULE, + importPath: SPARTACUS_TMS_GTM, + }, + rootModule: { + name: TMS_BASE_MODULE, + importPath: SPARTACUS_TMS_CORE, + content: `${TMS_BASE_MODULE}.forRoot()`, + }, + customConfig: buildGtmConfig, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; +function buildGtmConfig( + options: LibraryOptions +): AdditionalFeatureConfiguration { + return { + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_TMS_GTM, + namedImports: [TMS_GTM_MODULE], + }, + { moduleSpecifier: SPARTACUS_TMS_CORE, namedImports: [TMS_CONFIG] }, + ], + content: `<${TMS_CONFIG}>{ + tagManager: { + gtm: { + events: [], + }, + }, + }`, + }, + options: { + // Just import the feature module + ...options, + lazy: false, + }, + }; +} + +export const TMS_AEP_MODULE = 'AepModule'; + +export const TRACKING_AEP_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: TRACKING_TMS_AEP_FEATURE_NAME, + mainScope: SPARTACUS_TRACKING, + featureScope: SPARTACUS_TMS_AEP, + }, + folderName: TRACKING_FOLDER_NAME, + moduleName: TMS_MODULE_NAME, + featureModule: { + name: TMS_AEP_MODULE, + importPath: SPARTACUS_TMS_AEP, + }, + rootModule: { + name: TMS_BASE_MODULE, + importPath: SPARTACUS_TMS_CORE, + content: `${TMS_BASE_MODULE}.forRoot()`, + }, + customConfig: buildAepConfig, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_PROFILE_FEATURE_NAME], + }, +}; + +function buildAepConfig( + options: LibraryOptions +): AdditionalFeatureConfiguration { + return { + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_TMS_AEP, + namedImports: [TMS_AEP_MODULE], + }, + { moduleSpecifier: SPARTACUS_TMS_CORE, namedImports: [TMS_CONFIG] }, + ], + content: `<${TMS_CONFIG}>{ + tagManager: { + aep: { + events: [], + }, + }, + }`, + }, + options: { + // Just import the feature module + ...options, + lazy: false, + }, + }; +} diff --git a/projects/schematics/src/shared/lib-configs/user-schematics-config.ts b/projects/schematics/src/shared/lib-configs/user-schematics-config.ts new file mode 100644 index 00000000000..fd1559c270b --- /dev/null +++ b/projects/schematics/src/shared/lib-configs/user-schematics-config.ts @@ -0,0 +1,95 @@ +import { + SPARTACUS_USER, + SPARTACUS_USER_ACCOUNT, + SPARTACUS_USER_ACCOUNT_ASSETS, + SPARTACUS_USER_ACCOUNT_ROOT, + SPARTACUS_USER_PROFILE, + SPARTACUS_USER_PROFILE_ASSETS, + SPARTACUS_USER_PROFILE_ROOT, + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { SchematicConfig } from '../utils/lib-utils'; + +export const USER_FOLDER_NAME = 'user'; +export const USER_SCSS_FILE_NAME = 'user.scss'; +export const USER_FEATURE_MODULE_NAME = 'User'; + +export const USER_ACCOUNT_FEATURE_NAME_CONSTANT = 'USER_ACCOUNT_FEATURE'; +export const USER_ACCOUNT_MODULE = 'UserAccountModule'; +export const USER_ACCOUNT_ROOT_MODULE = 'UserAccountRootModule'; +export const USER_ACCOUNT_TRANSLATIONS = 'userAccountTranslations'; +export const USER_ACCOUNT_TRANSLATION_CHUNKS_CONFIG = + 'userAccountTranslationChunksConfig'; + +export const USER_ACCOUNT_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: USER_ACCOUNT_FEATURE_NAME, + mainScope: SPARTACUS_USER, + featureScope: SPARTACUS_USER_ACCOUNT, + }, + folderName: USER_FOLDER_NAME, + moduleName: USER_FEATURE_MODULE_NAME, + featureModule: { + name: USER_ACCOUNT_MODULE, + importPath: SPARTACUS_USER_ACCOUNT, + }, + rootModule: { + name: USER_ACCOUNT_ROOT_MODULE, + importPath: SPARTACUS_USER_ACCOUNT_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_USER_ACCOUNT_ROOT, + namedImports: [USER_ACCOUNT_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: USER_ACCOUNT_TRANSLATIONS, + chunks: USER_ACCOUNT_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_USER_ACCOUNT_ASSETS, + }, + styles: { + scssFileName: USER_SCSS_FILE_NAME, + importStyle: SPARTACUS_USER, + }, +}; + +export const USER_PROFILE_FEATURE_NAME_CONSTANT = 'USER_PROFILE_FEATURE'; +export const USER_PROFILE_MODULE = 'UserProfileModule'; +export const USER_PROFILE_ROOT_MODULE = 'UserProfileRootModule'; +export const USER_PROFILE_TRANSLATIONS = 'userProfileTranslations'; +export const USER_PROFILE_TRANSLATION_CHUNKS_CONFIG = + 'userProfileTranslationChunksConfig'; + +export const USER_PROFILE_SCHEMATICS_CONFIG: SchematicConfig = { + library: { + featureName: USER_PROFILE_FEATURE_NAME, + mainScope: SPARTACUS_USER, + featureScope: SPARTACUS_USER_PROFILE, + }, + folderName: USER_FOLDER_NAME, + moduleName: USER_FEATURE_MODULE_NAME, + featureModule: { + name: USER_PROFILE_MODULE, + importPath: SPARTACUS_USER_PROFILE, + }, + rootModule: { + name: USER_PROFILE_ROOT_MODULE, + importPath: SPARTACUS_USER_PROFILE_ROOT, + }, + lazyLoadingChunk: { + moduleSpecifier: SPARTACUS_USER_PROFILE_ROOT, + namedImports: [USER_PROFILE_FEATURE_NAME_CONSTANT], + }, + i18n: { + resources: USER_PROFILE_TRANSLATIONS, + chunks: USER_PROFILE_TRANSLATION_CHUNKS_CONFIG, + importPath: SPARTACUS_USER_PROFILE_ASSETS, + }, + styles: { + scssFileName: USER_SCSS_FILE_NAME, + importStyle: SPARTACUS_USER, + }, + dependencyFeatures: { + [SPARTACUS_USER]: [USER_ACCOUNT_FEATURE_NAME], + }, +}; diff --git a/projects/schematics/src/shared/libs-constants.ts b/projects/schematics/src/shared/libs-constants.ts index c78e8097980..11a0f7bc6bb 100644 --- a/projects/schematics/src/shared/libs-constants.ts +++ b/projects/schematics/src/shared/libs-constants.ts @@ -1,22 +1,17 @@ -export const PRODUCT_CONFIGURATOR_RULEBASED_FEATURE = - 'productConfiguratorRulebased'; -export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE = - 'productConfiguratorTextfield'; - /***** Scopes start *****/ -export const SPARTACUS_SCOPE = '@spartacus/'; +export const SPARTACUS_SCOPE = `@spartacus/`; -export const SPARTACUS_SCHEMATICS = '@spartacus/schematics'; +export const SPARTACUS_SCHEMATICS = `@spartacus/schematics`; -export const SPARTACUS_CORE = '@spartacus/core'; -export const SPARTACUS_STOREFRONTLIB = '@spartacus/storefront'; -export const SPARTACUS_ASSETS = '@spartacus/assets'; -export const SPARTACUS_STYLES = '@spartacus/styles'; +export const SPARTACUS_CORE = `@spartacus/core`; +export const SPARTACUS_STOREFRONTLIB = `@spartacus/storefront`; +export const SPARTACUS_ASSETS = `@spartacus/assets`; +export const SPARTACUS_STYLES = `@spartacus/styles`; -export const SPARTACUS_SETUP = '@spartacus/setup'; -export const SPARTACUS_SETUP_SSR = `${SPARTACUS_SETUP}/ssr`; +export const SPARTACUS_SETUP = `@spartacus/setup`; +export const SPARTACUS_SETUP_SSR = `@spartacus/setup/ssr`; -export const CORE_SPARTACUS_SCOPES = [ +export const CORE_SPARTACUS_SCOPES: string[] = [ SPARTACUS_CORE, SPARTACUS_ASSETS, SPARTACUS_SCHEMATICS, @@ -26,84 +21,141 @@ export const CORE_SPARTACUS_SCOPES = [ ]; export const FEATURES_LIBS_SKIP_SCOPES = [SPARTACUS_SCOPE]; -export const SPARTACUS_ORGANIZATION = '@spartacus/organization'; -export const SPARTACUS_ORGANIZATION_ADMINISTRATION_ROOT = `${SPARTACUS_ORGANIZATION}/administration/root`; -export const SPARTACUS_ORGANIZATION_ADMINISTRATION_CORE = `${SPARTACUS_ORGANIZATION}/administration/core`; -export const SPARTACUS_ORGANIZATION_ADMINISTRATION_COMPONENTS = `${SPARTACUS_ORGANIZATION}/administration/components`; - -export const SPARTACUS_ASM = '@spartacus/asm'; - -export const SPARTACUS_CART = '@spartacus/cart'; -export const SPARTACUS_CART_SAVED_CART_COMPONENTS = `${SPARTACUS_CART}/saved-cart/components`; -export const SPARTACUS_CART_SAVED_CART_CORE = `${SPARTACUS_CART}/saved-cart/core`; -export const SPARTACUS_CART_SAVED_CART_ROOT = `${SPARTACUS_CART}/saved-cart/root`; -export const SPARTACUS_CART_QUICK_ORDER_CORE = `${SPARTACUS_CART}/quick-order/core`; -export const SPARTACUS_CART_QUICK_ORDER_ROOT = `${SPARTACUS_CART}/quick-order/root`; -export const SPARTACUS_CART_QUICK_ORDER_COMPONENTS = `${SPARTACUS_CART}/quick-order/components`; - -export const SPARTACUS_PRODUCT = '@spartacus/product'; -export const SPARTACUS_PRODUCT_VARIANTS_COMPONENTS = `${SPARTACUS_PRODUCT}/variants/components`; -export const SPARTACUS_PRODUCT_VARIANTS_ROOT = `${SPARTACUS_PRODUCT}/variants/root`; - -export const SPARTACUS_SMARTEDIT = '@spartacus/smartedit'; - -export const SPARTACUS_USER = '@spartacus/user'; -export const SPARTACUS_USER_PROFILE = `${SPARTACUS_USER}/profile`; -export const SPARTACUS_USER_PROFILE_OCC = `${SPARTACUS_USER_PROFILE}/occ`; -export const SPARTACUS_USER_PROFILE_CORE = `${SPARTACUS_USER_PROFILE}/core`; -export const SPARTACUS_USER_PROFILE_COMPONENTS = `${SPARTACUS_USER_PROFILE}/components`; -export const SPARTACUS_USER_PROFILE_ROOT = `${SPARTACUS_USER_PROFILE}/root`; -export const SPARTACUS_USER_ACCOUNT = `${SPARTACUS_USER}/account`; -export const SPARTACUS_USER_ACCOUNT_OCC = `${SPARTACUS_USER_ACCOUNT}/occ`; -export const SPARTACUS_USER_ACCOUNT_CORE = `${SPARTACUS_USER_ACCOUNT}/core`; -export const SPARTACUS_USER_ACCOUNT_COMPONENTS = `${SPARTACUS_USER_ACCOUNT}/components`; - -export const SPARTACUS_CHECKOUT = '@spartacus/checkout'; -export const SPARTACUS_CHECKOUT_BASE = `${SPARTACUS_CHECKOUT}/base`; -export const SPARTACUS_CHECKOUT_BASE_ASSETS = `${SPARTACUS_CHECKOUT_BASE}/assets`; -export const SPARTACUS_CHECKOUT_BASE_OCC = `${SPARTACUS_CHECKOUT_BASE}/occ`; -export const SPARTACUS_CHECKOUT_BASE_CORE = `${SPARTACUS_CHECKOUT_BASE}/core`; -export const SPARTACUS_CHECKOUT_BASE_ROOT = `${SPARTACUS_CHECKOUT_BASE}/root`; -export const SPARTACUS_CHECKOUT_BASE_COMPONENTS = `${SPARTACUS_CHECKOUT_BASE}/components`; -export const SPARTACUS_CHECKOUT_B2B = `${SPARTACUS_CHECKOUT}/b2b`; -export const SPARTACUS_CHECKOUT_B2B_ASSETS = `${SPARTACUS_CHECKOUT_B2B}/assets`; -export const SPARTACUS_CHECKOUT_B2B_OCC = `${SPARTACUS_CHECKOUT_B2B}/occ`; -export const SPARTACUS_CHECKOUT_B2B_CORE = `${SPARTACUS_CHECKOUT_B2B}/core`; -export const SPARTACUS_CHECKOUT_B2B_ROOT = `${SPARTACUS_CHECKOUT_B2B}/root`; -export const SPARTACUS_CHECKOUT_B2B_COMPONENTS = `${SPARTACUS_CHECKOUT_B2B}/components`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT = `${SPARTACUS_CHECKOUT}/scheduled-replenishment`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS = `${SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT}/assets`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_OCC = `${SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT}/occ`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_CORE = `${SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT}/core`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT = `${SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT}/root`; -export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_COMPONENTS = `${SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT}/components`; - -export const SPARTACUS_CHECKOUT_OLD_OCC = `${SPARTACUS_CHECKOUT}/occ`; -export const SPARTACUS_CHECKOUT_OLD_CORE = `${SPARTACUS_CHECKOUT}/core`; -export const SPARTACUS_CHECKOUT_OLD_ROOT = `${SPARTACUS_CHECKOUT}/root`; -export const SPARTACUS_CHECKOUT_OLD_COMPONENTS = `${SPARTACUS_CHECKOUT}/components`; - -export const SPARTACUS_ORDER = '@spartacus/order'; -export const SPARTACUS_ORDER_ROOT = '@spartacus/order/root'; - -export const SPARTACUS_QUALTRICS = '@spartacus/qualtrics'; -export const SPARTACUS_QUALTRICS_COMPONENTS = `${SPARTACUS_QUALTRICS}/components`; - -export const SPARTACUS_STOREFINDER = '@spartacus/storefinder'; - -export const SPARTACUS_TRACKING = '@spartacus/tracking'; - -export const SPARTACUS_PRODUCT_CONFIGURATOR = '@spartacus/product-configurator'; -export const SPARTACUS_PRODUCT_CONFIGURATOR_COMMON = `${SPARTACUS_PRODUCT_CONFIGURATOR}/common`; -export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED = `${SPARTACUS_PRODUCT_CONFIGURATOR}/rulebased`; - -export const SPARTACUS_CDS = '@spartacus/cds'; - -export const SPARTACUS_CDC = '@spartacus/cdc'; - -export const SPARTACUS_DIGITAL_PAYMENTS = '@spartacus/digital-payments'; - -export const SPARTACUS_EPD_VISUALIZATION = '@spartacus/epd-visualization'; +export const SPARTACUS_ASM = `@spartacus/asm`; +export const SPARTACUS_ASM_ROOT = `@spartacus/asm/root`; +export const SPARTACUS_ASM_ASSETS = `@spartacus/asm/assets`; + +export const SPARTACUS_CART = `@spartacus/cart`; +export const SPARTACUS_CART_BASE = `@spartacus/cart/base`; +export const SPARTACUS_CART_BASE_ROOT = `@spartacus/cart/base/root`; +export const SPARTACUS_CART_BASE_ASSETS = `@spartacus/cart/base/assets`; +export const MINI_CART_ENTRY_POINT = `@spartacus/cart/base/components/mini-cart`; +export const ADD_TO_CART_ENTRY_POINT = `@spartacus/cart/base/components/add-to-cart`; +export const SPARTACUS_CART_IMPORT_EXPORT = `@spartacus/cart/import-export`; +export const SPARTACUS_CART_IMPORT_EXPORT_ROOT = `@spartacus/cart/import-export/root`; +export const SPARTACUS_CART_IMPORT_EXPORT_ASSETS = `@spartacus/cart/import-export/assets`; +export const SPARTACUS_QUICK_ORDER = `@spartacus/cart/quick-order`; +export const SPARTACUS_CART_QUICK_ORDER_CORE = `@spartacus/cart/quick-order/core`; +export const SPARTACUS_CART_QUICK_ORDER_ROOT = `@spartacus/cart/quick-order/root`; +export const SPARTACUS_CART_QUICK_ORDER_COMPONENTS = `@spartacus/cart/quick-order/components`; +export const SPARTACUS_QUICK_ORDER_ROOT = `@spartacus/cart/quick-order/root`; +export const SPARTACUS_QUICK_ORDER_ASSETS = `@spartacus/cart/quick-order/assets`; +export const SPARTACUS_CART_SAVED_CART_COMPONENTS = `@spartacus/cart/saved-cart/components`; +export const SPARTACUS_CART_SAVED_CART_CORE = `@spartacus/cart/saved-cart/core`; +export const SPARTACUS_CART_SAVED_CART_ROOT = `@spartacus/cart/saved-cart/root`; +export const SPARTACUS_SAVED_CART = `@spartacus/cart/saved-cart`; +export const SPARTACUS_SAVED_CART_ROOT = `@spartacus/cart/saved-cart/root`; +export const SPARTACUS_SAVED_CART_ASSETS = `@spartacus/cart/saved-cart/assets`; +export const SPARTACUS_CART_WISHLIST = `@spartacus/cart/wish-list`; +export const SPARTACUS_CART_WISHLIST_ROOT = `@spartacus/cart/wish-list/root`; +export const SPARTACUS_CART_WISHLIST_ASSETS = `@spartacus/cart/wish-list/assets`; +export const ADD_TO_WISHLIST_ENTRY_POINT = `@spartacus/cart/wish-list/components/add-to-wishlist`; + +export const SPARTACUS_CHECKOUT = `@spartacus/checkout`; +export const SPARTACUS_CHECKOUT_BASE = `@spartacus/checkout/base`; +export const SPARTACUS_CHECKOUT_BASE_ASSETS = `@spartacus/checkout/base/assets`; +export const SPARTACUS_CHECKOUT_BASE_OCC = `@spartacus/checkout/base/occ`; +export const SPARTACUS_CHECKOUT_BASE_CORE = `@spartacus/checkout/base/core`; +export const SPARTACUS_CHECKOUT_BASE_ROOT = `@spartacus/checkout/base/root`; +export const SPARTACUS_CHECKOUT_BASE_COMPONENTS = `@spartacus/checkout/base/components`; +export const SPARTACUS_CHECKOUT_B2B = `@spartacus/checkout/b2b`; +export const SPARTACUS_CHECKOUT_B2B_ASSETS = `@spartacus/checkout/b2b/assets`; +export const SPARTACUS_CHECKOUT_B2B_OCC = `@spartacus/checkout/b2b/occ`; +export const SPARTACUS_CHECKOUT_B2B_CORE = `@spartacus/checkout/b2b/core`; +export const SPARTACUS_CHECKOUT_B2B_ROOT = `@spartacus/checkout/b2b/root`; +export const SPARTACUS_CHECKOUT_B2B_COMPONENTS = `@spartacus/checkout/b2b/components`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT = `@spartacus/checkout/scheduled-replenishment`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ASSETS = `@spartacus/checkout/scheduled-replenishment/assets`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_OCC = `@spartacus/checkout/scheduled-replenishment/occ`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_CORE = `@spartacus/checkout/scheduled-replenishment/core`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT = `@spartacus/checkout/scheduled-replenishment/root`; +export const SPARTACUS_CHECKOUT_SCHEDULED_REPLENISHMENT_COMPONENTS = `@spartacus/checkout/scheduled-replenishment/components`; + +export const SPARTACUS_CHECKOUT_OLD_OCC = `@spartacus/checkout/occ`; +export const SPARTACUS_CHECKOUT_OLD_CORE = `@spartacus/checkout/core`; +export const SPARTACUS_CHECKOUT_OLD_ROOT = `@spartacus/checkout/root`; +export const SPARTACUS_CHECKOUT_OLD_COMPONENTS = `@spartacus/checkout/components`; + +export const SPARTACUS_ORDER = `@spartacus/order`; +export const SPARTACUS_ORDER_ROOT = `@spartacus/order/root`; +export const SPARTACUS_ORDER_ASSETS = `@spartacus/order/assets`; + +export const SPARTACUS_ORGANIZATION = `@spartacus/organization`; +export const SPARTACUS_ADMINISTRATION = `@spartacus/organization/administration`; +export const SPARTACUS_ORGANIZATION_ADMINISTRATION_ROOT = `@spartacus/organization/administration/root`; +export const SPARTACUS_ORGANIZATION_ADMINISTRATION_CORE = `@spartacus/organization/administration/core`; +export const SPARTACUS_ORGANIZATION_ADMINISTRATION_COMPONENTS = `@spartacus/organization/administration/components`; +export const SPARTACUS_ORGANIZATION_ADMINISTRATION_ASSETS = `@spartacus/organization/administration/assets`; +export const SPARTACUS_ORGANIZATION_ORDER_APPROVAL = `@spartacus/organization/order-approval`; +export const SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ROOT = `@spartacus/organization/order-approval/root`; +export const SPARTACUS_ORGANIZATION_ORDER_APPROVAL_ASSETS = `@spartacus/organization/order-approval/assets`; + +export const SPARTACUS_PRODUCT = `@spartacus/product`; +export const SPARTACUS_PRODUCT_VARIANTS_COMPONENTS = `@spartacus/product/variants/components`; +export const SPARTACUS_PRODUCT_VARIANTS_ROOT = `@spartacus/product/variants/root`; +export const SPARTACUS_BULK_PRICING = `@spartacus/product/bulk-pricing`; +export const SPARTACUS_BULK_PRICING_ROOT = `@spartacus/product/bulk-pricing/root`; +export const SPARTACUS_BULK_PRICING_ASSETS = `@spartacus/product/bulk-pricing/assets`; +export const SPARTACUS_IMAGE_ZOOM = `@spartacus/product/image-zoom`; +export const SPARTACUS_IMAGE_ZOOM_ROOT = `@spartacus/product/image-zoom/root`; +export const SPARTACUS_IMAGE_ZOOM_ASSETS = `@spartacus/product/image-zoom/assets`; +export const SPARTACUS_VARIANTS = `@spartacus/product/variants`; +export const SPARTACUS_VARIANTS_ROOT = `@spartacus/product/variants/root`; +export const SPARTACUS_VARIANTS_ASSETS = `@spartacus/product/variants/assets`; + +export const SPARTACUS_PRODUCT_CONFIGURATOR = `@spartacus/product-configurator`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_COMMON = `@spartacus/product-configurator/common`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_ASSETS = `@spartacus/product-configurator/common/assets`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD = `@spartacus/product-configurator/textfield`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED = `@spartacus/product-configurator/rulebased`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT = `@spartacus/product-configurator/textfield/root`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_ROOT = `@spartacus/product-configurator/rulebased/root`; +export const SPARTACUS_PRODUCT_CONFIGURATOR_RULEBASED_CPQ = `@spartacus/product-configurator/rulebased/cpq`; + +export const SPARTACUS_QUALTRICS = `@spartacus/qualtrics`; +export const SPARTACUS_QUALTRICS_COMPONENTS = `@spartacus/qualtrics/components`; +export const SPARTACUS_QUALTRICS_ROOT = `@spartacus/qualtrics/root`; + +export const SPARTACUS_SMARTEDIT = `@spartacus/smartedit`; +export const SPARTACUS_SMARTEDIT_ROOT = `@spartacus/smartedit/root`; + +export const SPARTACUS_STOREFINDER = `@spartacus/storefinder`; +export const SPARTACUS_STOREFINDER_ROOT = `@spartacus/storefinder/root`; +export const SPARTACUS_STOREFINDER_ASSETS = `@spartacus/storefinder/assets`; + +export const SPARTACUS_TRACKING = `@spartacus/tracking`; +export const SPARTACUS_TMS_CORE = `@spartacus/tracking/tms/core`; +export const SPARTACUS_TMS_GTM = `@spartacus/tracking/tms/gtm`; +export const SPARTACUS_TMS_AEP = `@spartacus/tracking/tms/aep`; +export const SPARTACUS_PERSONALIZATION = `@spartacus/tracking/personalization`; +export const SPARTACUS_PERSONALIZATION_ROOT = `@spartacus/tracking/personalization/root`; + +export const SPARTACUS_USER = `@spartacus/user`; +export const SPARTACUS_USER_ACCOUNT = `@spartacus/user/account`; +export const SPARTACUS_USER_ACCOUNT_ASSETS = `@spartacus/user/account/assets`; +export const SPARTACUS_USER_ACCOUNT_OCC = `@spartacus/user/account/occ`; +export const SPARTACUS_USER_ACCOUNT_CORE = `@spartacus/user/account/core`; +export const SPARTACUS_USER_ACCOUNT_ROOT = `@spartacus/user/account/root`; +export const SPARTACUS_USER_ACCOUNT_COMPONENTS = `@spartacus/user/account/components`; +export const SPARTACUS_USER_PROFILE = `@spartacus/user/profile`; +export const SPARTACUS_USER_PROFILE_OCC = `@spartacus/user/profile/occ`; +export const SPARTACUS_USER_PROFILE_CORE = `@spartacus/user/profile/core`; +export const SPARTACUS_USER_PROFILE_COMPONENTS = `@spartacus/user/profile/components`; +export const SPARTACUS_USER_PROFILE_ASSETS = `@spartacus/user/profile/assets`; +export const SPARTACUS_USER_PROFILE_ROOT = `@spartacus/user/profile/root`; + +export const SPARTACUS_CDS = `@spartacus/cds`; + +export const SPARTACUS_CDC = `@spartacus/cdc`; +export const SPARTACUS_CDC_ROOT = `@spartacus/cdc/root`; + +export const SPARTACUS_DIGITAL_PAYMENTS = `@spartacus/digital-payments`; +export const SPARTACUS_DIGITAL_PAYMENTS_ASSETS = `@spartacus/digital-payments/assets`; + +export const SPARTACUS_EPD_VISUALIZATION = `@spartacus/epd-visualization`; +export const SPARTACUS_EPD_VISUALIZATION_ROOT = `@spartacus/epd-visualization/root`; +export const SPARTACUS_EPD_VISUALIZATION_ASSETS = `@spartacus/epd-visualization/assets`; + /***** Scopes end *****/ /***** File structure start *****/ @@ -114,137 +166,52 @@ export const SPARTACUS_FEATURES_NG_MODULE = 'SpartacusFeaturesModule'; export const SPARTACUS_CONFIGURATION_MODULE = 'spartacus-configuration'; /***** File structure end *****/ -/***** CLI start *****/ -export const CLI_ASM_FEATURE = 'ASM'; +/***** Feature name start *****/ +export const ASM_FEATURE_NAME = 'ASM'; -export const CLI_CART_BASE_FEATURE = 'Cart'; -export const CLI_CART_IMPORT_EXPORT_FEATURE = 'Import-Export'; -export const CLI_CART_QUICK_ORDER_FEATURE = 'Quick-Order'; -export const CLI_CART_SAVED_CART_FEATURE = 'Saved-Cart'; -export const CLI_CART_WISHLIST_FEATURE = 'WishList'; +export const CART_BASE_FEATURE_NAME = 'Cart'; +export const CART_IMPORT_EXPORT_FEATURE_NAME = 'Import-Export'; +export const CART_QUICK_ORDER_FEATURE_NAME = 'Quick-Order'; +export const CART_WISHLIST_FEATURE_NAME = 'WishList'; +export const CART_SAVED_CART_FEATURE_NAME = 'Saved-Cart'; -export const CLI_CHECKOUT_BASE_FEATURE = 'Checkout'; -export const CLI_CHECKOUT_B2B_FEATURE = 'Checkout-B2B'; -export const CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE = +export const CHECKOUT_BASE_FEATURE_NAME = 'Checkout'; +export const CHECKOUT_B2B_FEATURE_NAME = 'Checkout-B2B'; +export const CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME = 'Checkout-Scheduled-Replenishment'; -export const CLI_ORDER_FEATURE = 'Order'; +export const ORDER_FEATURE_NAME = 'Order'; -export const CLI_ORGANIZATION_ADMINISTRATION_FEATURE = 'Administration'; -export const CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE = 'Order-Approval'; +export const ORGANIZATION_ADMINISTRATION_FEATURE_NAME = 'Administration'; +export const ORGANIZATION_ORDER_APPROVAL_FEATURE_NAME = 'Order-Approval'; -export const CLI_PRODUCT_BULK_PRICING_FEATURE = 'Bulk-Pricing'; -export const CLI_PRODUCT_IMAGE_ZOOM_FEATURE = 'Image-Zoom'; -export const CLI_PRODUCT_VARIANTS_FEATURE = 'Product-Variants'; +export const PRODUCT_BULK_PRICING_FEATURE_NAME = 'Bulk-Pricing'; +export const PRODUCT_IMAGE_ZOOM_FEATURE_NAME = 'Image-Zoom'; +export const PRODUCT_VARIANTS_FEATURE_NAME = 'Product-Variants'; -export const CLI_PRODUCT_CONFIGURATOR_VC_FEATURE = 'VC-Configurator'; -export const CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE = +export const PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE_NAME = 'Textfield-Configurator'; -export const CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE = 'CPQ-Configurator'; - -export const CLI_QUALTRICS_FEATURE = 'Qualtrics'; - -export const CLI_SMARTEDIT_FEATURE = 'SmartEdit'; - -export const CLI_STOREFINDER_FEATURE = 'Store-Finder'; - -export const CLI_TRACKING_PERSONALIZATION_FEATURE = 'Personalization'; -export const CLI_TRACKING_TMS_GTM_FEATURE = 'TMS-GTM'; -export const CLI_TRACKING_TMS_AEP_FEATURE = 'TMS-AEPL'; - -export const CLI_USER_ACCOUNT_FEATURE = 'Account'; -export const CLI_USER_PROFILE_FEATURE = 'Profile'; - -export const CLI_CDC_FEATURE = 'CDC'; -export const CLI_CDS_FEATURE = 'CDS'; -export const CLI_DIGITAL_PAYMENTS_FEATURE = 'Digital-Payments'; -export const CLI_EPD_VISUALIZATION_FEATURE = 'EPD-Visualization'; -/***** CLI end *****/ - -/***** Feature libs configuration start *****/ -export const ASM_MODULE = 'AsmModule'; -export const ASM_ROOT_MODULE = 'AsmRootModule'; - -export const CART_BASE_MODULE = 'CartBaseModule'; -export const CART_BASE_ROOT_MODULE = 'CartBaseRootModule'; -export const MINI_CART_MODULE = 'MiniCartModule'; -export const ADD_TO_CART_MODULE = 'AddToCartModule'; -export const CART_IMPORT_EXPORT_MODULE = 'ImportExportModule'; -export const CART_IMPORT_EXPORT_ROOT_MODULE = 'ImportExportRootModule'; -export const QUICK_ORDER_MODULE = 'QuickOrderModule'; -export const QUICK_ORDER_ROOT_MODULE = 'QuickOrderRootModule'; -export const SAVED_CART_MODULE = 'SavedCartModule'; -export const SAVED_CART_ROOT_MODULE = 'SavedCartRootModule'; -export const CART_WISHLIST_MODULE = 'WishListModule'; -export const ADD_TO_WISHLIST_MODULE = 'AddToWishListModule'; -export const CART_WISHLIST_ROOT_MODULE = 'WishListRootModule'; - -export const CHECKOUT_BASE_FEATURE_NAME_CONSTANT = 'CHECKOUT_FEATURE'; -export const CHECKOUT_BASE_MODULE_NAME = 'Checkout'; -export const CHECKOUT_BASE_MODULE = 'CheckoutModule'; -export const CHECKOUT_BASE_ROOT_MODULE = 'CheckoutRootModule'; -export const CHECKOUT_B2B_MODULE = 'CheckoutB2BModule'; -export const CHECKOUT_B2B_ROOT_MODULE = 'CheckoutB2BRootModule'; -export const CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE = - 'CheckoutScheduledReplenishmentModule'; -export const CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE = - 'CheckoutScheduledReplenishmentRootModule'; - -export const ORDER_MODULE = 'OrderModule'; -export const ORDER_ROOT_MODULE = 'OrderRootModule'; - -export const ADMINISTRATION_MODULE = 'AdministrationModule'; -export const ADMINISTRATION_ROOT_MODULE = 'AdministrationRootModule'; -export const ORDER_APPROVAL_MODULE = 'OrderApprovalModule'; -export const ORDER_APPROVAL_ROOT_MODULE = 'OrderApprovalRootModule'; - -export const BULK_PRICING_MODULE = 'BulkPricingModule'; -export const BULK_PRICING_ROOT_MODULE = 'BulkPricingRootModule'; -export const IMAGE_ZOOM_MODULE = 'ProductImageZoomModule'; -export const IMAGE_ZOOM_ROOT_MODULE = 'ProductImageZoomRootModule'; -export const VARIANTS_MODULE = 'ProductVariantsModule'; -export const VARIANTS_ROOT_MODULE = 'ProductVariantsRootModule'; - -export const PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE = - 'TextfieldConfiguratorModule'; -export const PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE = - 'TextfieldConfiguratorRootModule'; -export const PRODUCT_CONFIGURATOR_RULEBASED_MODULE = - 'RulebasedConfiguratorModule'; -export const PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE = - 'RulebasedConfiguratorRootModule'; -export const PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE = - 'RulebasedCpqConfiguratorModule'; -export const PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE = - 'CpqConfiguratorRootModule'; - -export const QUALTRICS_MODULE = 'QualtricsModule'; -export const QUALTRICS_ROOT_MODULE = 'QualtricsRootModule'; - -export const SMARTEDIT_MODULE = 'SmartEditModule'; -export const SMARTEDIT_ROOT_MODULE = 'SmartEditRootModule'; - -export const STOREFINDER_MODULE = 'StoreFinderModule'; -export const STOREFINDER_ROOT_MODULE = 'StoreFinderRootModule'; - -export const TMS_BASE_MODULE = 'BaseTmsModule'; -export const TMS_GTM_MODULE = 'GtmModule'; -export const TMS_AEP_MODULE = 'AepModule'; -export const PERSONALIZATION_MODULE = 'PersonalizationModule'; -export const PERSONALIZATION_ROOT_MODULE = 'PersonalizationRootModule'; - -export const USER_ACCOUNT_MODULE = 'UserAccountModule'; -export const USER_ACCOUNT_ROOT_MODULE = 'UserAccountRootModule'; -export const USER_PROFILE_MODULE = 'UserProfileModule'; -export const USER_PROFILE_ROOT_MODULE = 'UserProfileRootModule'; - -export const CDC_MODULE = 'CdcModule'; -export const CDC_ROOT_MODULE = 'CdcRootModule'; - -export const CDS_MODULE = 'CdsModule'; - -export const DIGITAL_PAYMENTS_MODULE = 'DigitalPaymentsModule'; - -export const EPD_VISUALIZATION_MODULE = 'EpdVisualizationModule'; -export const EPD_VISUALIZATION_ROOT_MODULE = 'EpdVisualizationRootModule'; -/***** Feature libs configuration end *****/ +export const PRODUCT_CONFIGURATOR_VC_FEATURE_NAME = 'VC-Configurator'; +export const PRODUCT_CONFIGURATOR_CPQ_FEATURE_NAME = 'CPQ-Configurator'; + +export const QUALTRICS_FEATURE_NAME = 'Qualtrics'; + +export const SMARTEDIT_FEATURE_NAME = 'SmartEdit'; + +export const STOREFINDER_FEATURE_NAME = 'Store-Finder'; + +export const TRACKING_PERSONALIZATION_FEATURE_NAME = 'Personalization'; +export const TRACKING_TMS_GTM_FEATURE_NAME = 'TMS-GTM'; +export const TRACKING_TMS_AEP_FEATURE_NAME = 'TMS-AEPL'; + +export const USER_ACCOUNT_FEATURE_NAME = 'User-Account'; +export const USER_PROFILE_FEATURE_NAME = 'User-Profile'; + +export const CDC_FEATURE_NAME = 'CDC'; + +export const CDS_FEATURE_NAME = 'CDS'; + +export const DIGITAL_PAYMENTS_FEATURE_NAME = 'Digital-Payments'; + +export const EPD_VISUALIZATION_FEATURE_NAME = 'EPD-Visualization'; +/***** Feature name end *****/ diff --git a/projects/schematics/src/shared/schematics-config-mappings.ts b/projects/schematics/src/shared/schematics-config-mappings.ts new file mode 100644 index 00000000000..02bf75b51a9 --- /dev/null +++ b/projects/schematics/src/shared/schematics-config-mappings.ts @@ -0,0 +1,302 @@ +import { SchematicsException } from '@angular-devkit/schematics'; +import { ASM_SCHEMATICS_CONFIG } from './lib-configs/asm-schematics-config'; +import { + CART_BASE_SCHEMATICS_CONFIG, + CART_IMPORT_EXPORT_SCHEMATICS_CONFIG, + CART_QUICK_ORDER_SCHEMATICS_CONFIG, + CART_SAVED_CART_SCHEMATICS_CONFIG, + CART_WISHLIST_SCHEMATICS_CONFIG, +} from './lib-configs/cart-schematics-config'; +import { + CHECKOUT_B2B_SCHEMATICS_CONFIG, + CHECKOUT_BASE_SCHEMATICS_CONFIG, + CHECKOUT_SCHEDULED_REPLENISHMENT_SCHEMATICS_CONFIG, +} from './lib-configs/checkout-schematics-config'; +import { CDC_SCHEMATICS_CONFIG } from './lib-configs/integration-libs/cdc-schematics-config'; +import { CDS_SCHEMATICS_CONFIG } from './lib-configs/integration-libs/cds-schematics-config'; +import { DIGITAL_PAYMENTS_SCHEMATICS_CONFIG } from './lib-configs/integration-libs/digital-payments-schematics-config'; +import { EPD_SCHEMATICS_CONFIG } from './lib-configs/integration-libs/epd-schematics-config'; +import { ORDER_SCHEMATICS_CONFIG } from './lib-configs/order-schematics-config'; +import { + ORGANIZATION_ADMINISTRATION_SCHEMATICS_CONFIG, + ORGANIZATION_ORDER_APPROVAL_SCHEMATICS_CONFIG, +} from './lib-configs/organization-schematics-config'; +import { + PRODUCT_CONFIGURATOR_CPQ_SCHEMATICS_CONFIG, + PRODUCT_CONFIGURATOR_RULEBASED_SCHEMATICS_CONFIG, + PRODUCT_CONFIGURATOR_TEXTFIELD_SCHEMATICS_CONFIG, +} from './lib-configs/product-configurator-schematics-config'; +import { + PRODUCT_BULK_PRICING_SCHEMATICS_CONFIG, + PRODUCT_IMAGE_ZOOM_SCHEMATICS_CONFIG, + PRODUCT_VARIANTS_SCHEMATICS_CONFIG, +} from './lib-configs/product-schematics-config'; +import { QUALTRICS_SCHEMATICS_CONFIG } from './lib-configs/qualtrics-schematics-config'; +import { SMARTEDIT_SCHEMATICS_CONFIG } from './lib-configs/smartedit-schematics-config'; +import { STOREFINDER_SCHEMATICS_CONFIG } from './lib-configs/storefinder-schematics-config'; +import { + TRACKING_AEP_SCHEMATICS_CONFIG, + TRACKING_GTM_SCHEMATICS_CONFIG, + TRACKING_PERSONALIZATION_SCHEMATICS_CONFIG, +} from './lib-configs/tracking-schematics-config'; +import { + USER_ACCOUNT_SCHEMATICS_CONFIG, + USER_PROFILE_SCHEMATICS_CONFIG, +} from './lib-configs/user-schematics-config'; +import { Module, SchematicConfig } from './utils/lib-utils'; + +/** + * A list of all schematics feature configurations. + * _Must_ be updated when adding a new schematics + * library or a feature. + */ +export const SCHEMATICS_CONFIGS: SchematicConfig[] = [ + // feature libraries start + ASM_SCHEMATICS_CONFIG, + + CART_BASE_SCHEMATICS_CONFIG, + CART_IMPORT_EXPORT_SCHEMATICS_CONFIG, + CART_QUICK_ORDER_SCHEMATICS_CONFIG, + CART_WISHLIST_SCHEMATICS_CONFIG, + CART_SAVED_CART_SCHEMATICS_CONFIG, + + CHECKOUT_BASE_SCHEMATICS_CONFIG, + CHECKOUT_B2B_SCHEMATICS_CONFIG, + CHECKOUT_SCHEDULED_REPLENISHMENT_SCHEMATICS_CONFIG, + + ORDER_SCHEMATICS_CONFIG, + + ORGANIZATION_ADMINISTRATION_SCHEMATICS_CONFIG, + ORGANIZATION_ORDER_APPROVAL_SCHEMATICS_CONFIG, + + PRODUCT_CONFIGURATOR_TEXTFIELD_SCHEMATICS_CONFIG, + PRODUCT_CONFIGURATOR_RULEBASED_SCHEMATICS_CONFIG, + PRODUCT_CONFIGURATOR_CPQ_SCHEMATICS_CONFIG, + + PRODUCT_BULK_PRICING_SCHEMATICS_CONFIG, + PRODUCT_IMAGE_ZOOM_SCHEMATICS_CONFIG, + PRODUCT_VARIANTS_SCHEMATICS_CONFIG, + + QUALTRICS_SCHEMATICS_CONFIG, + + SMARTEDIT_SCHEMATICS_CONFIG, + + STOREFINDER_SCHEMATICS_CONFIG, + + TRACKING_PERSONALIZATION_SCHEMATICS_CONFIG, + TRACKING_GTM_SCHEMATICS_CONFIG, + TRACKING_AEP_SCHEMATICS_CONFIG, + + USER_ACCOUNT_SCHEMATICS_CONFIG, + USER_PROFILE_SCHEMATICS_CONFIG, + + // integration libraries start + CDC_SCHEMATICS_CONFIG, + + CDS_SCHEMATICS_CONFIG, + + DIGITAL_PAYMENTS_SCHEMATICS_CONFIG, + + EPD_SCHEMATICS_CONFIG, +]; + +/** + * Maps sub-features to their parent feature. + */ +export const { + /** + * Mapping of features to Spartacus library. + * + * E.g.: + * + * { + * ..., + * '@spartacus/checkout': ['Checkout', 'Checkout-B2B', 'Checkout-Scheduled-Replenishment'], + * ... + * } + */ + libraryFeatureMapping, + /** + * Mapping of feature-modules to the Spartacus library. + * + * E.g.: + * + * { + * ..., + * 'Checkout': ['CheckoutModule'], + * 'Checkout-B2B': ['CheckoutB2BModule'], + * 'Checkout-Scheduled-Replenishment': ['CheckoutScheduledReplenishmentModule'], + * ... + * } + */ + featureFeatureModuleMapping, + /** + * Mapping of root feature-modules to the Spartacus library. + * + * E.g.: + * + * { + * ..., + * 'Checkout': ['CheckoutRootModule'], + * 'Checkout-B2B': ['CheckoutB2BRootModule'], + * 'Checkout-Scheduled-Replenishment': ['CheckoutScheduledReplenishmentRootModule'], + * ... + * } + */ + featureRootModuleMapping, + /** + * Mapping of schematics configurations to the Spartacus features. + * + * E.g.: + * + * { + * ..., + * 'Checkout': [CHECKOUT_BASE_SCHEMATICS_CONFIG], + * 'Checkout-B2B': [CHECKOUT_B2B_SCHEMATICS_CONFIG], + * ... + * } + */ + featureSchematicConfigMapping, +} = generateMappings(); + +/** + * Generates mappings. + */ +export function generateMappings(): { + libraryFeatureMapping: Map; + featureFeatureModuleMapping: Map; + featureRootModuleMapping: Map; + featureSchematicConfigMapping: Map; +} { + const featureMapping: Map = new Map(); + const featureModuleMapping: Map = new Map(); + const rootModuleMapping: Map = new Map(); + const configMapping: Map = new Map(); + + for (const featureConfig of SCHEMATICS_CONFIGS) { + populateFeatureMapping(featureMapping, featureConfig); + populateFeatureModuleMapping(featureModuleMapping, featureConfig); + populateRootModulesMapping(rootModuleMapping, featureConfig); + populateConfigMapping(configMapping, featureConfig); + } + + return { + libraryFeatureMapping: featureMapping, + featureFeatureModuleMapping: featureModuleMapping, + featureRootModuleMapping: rootModuleMapping, + featureSchematicConfigMapping: configMapping, + }; +} + +function populateFeatureMapping( + mapping: Map, + featureConfig: SchematicConfig +): void { + const feature = featureConfig.library.mainScope; + const featureName = featureConfig.library.featureName; + + const existingMapping = mapping.get(feature) ?? []; + // avoid adding duplicates + if (existingMapping.includes(featureName)) { + return; + } + + mapping.set(feature, [...existingMapping, featureName]); +} + +function populateFeatureModuleMapping( + mapping: Map, + featureConfig: SchematicConfig +): void { + const feature = featureConfig.library.featureName; + + const existingMapping = mapping.get(feature) ?? []; + const featureModules = ([] as Module[]) + .concat(featureConfig.featureModule) + .map((fm) => fm.name); + + // avoid adding duplicates + if (existingMapping.some((existing) => featureModules.includes(existing))) { + return; + } + + mapping.set(feature, [...existingMapping, ...featureModules]); +} + +function populateRootModulesMapping( + mapping: Map, + featureConfig: SchematicConfig +): void { + const feature = featureConfig.library.featureName; + + const existingMapping = mapping.get(feature) ?? []; + const rooModules = ([] as Module[]) + .concat(featureConfig.rootModule ?? []) + .map((rm) => rm.name); + + // avoid adding duplicates + if (existingMapping.some((existing) => rooModules.includes(existing))) { + return; + } + + mapping.set(feature, [...existingMapping, ...rooModules]); +} + +function populateConfigMapping( + mapping: Map, + featureConfig: SchematicConfig +): void { + mapping.set(featureConfig.library.featureName, featureConfig); +} + +/** + * Based on the given value, + * it returns the key of the given object. + */ +export function getKeyByMappingValue( + mapping: Map, + value: string +): string | undefined { + try { + return getKeyByMappingValueOrThrow(mapping, value); + } catch (e) { + if (e instanceof SchematicsException) { + return undefined; + } + } + return undefined; +} + +/** + * Based on the given value, + * it returns the key of the given object. + */ +export function getKeyByMappingValueOrThrow( + mapping: Map, + value: string +): string { + for (const key of Array.from(mapping.keys())) { + if ((mapping.get(key) ?? []).includes(value)) { + return key; + } + } + + throw new SchematicsException(`Value ${value} not found in the given map.`); +} + +/** + * Returns the schematics config + * for the given feature. + */ +export function getSchematicsConfigByFeatureOrThrow( + feature: string +): SchematicConfig { + const featureConfig = featureSchematicConfigMapping.get(feature); + if (!featureConfig) { + throw new SchematicsException( + `Config not found for the given feature '${feature}'` + ); + } + + return featureConfig; +} diff --git a/projects/schematics/src/shared/schematics-config-mappings_spec.ts b/projects/schematics/src/shared/schematics-config-mappings_spec.ts new file mode 100644 index 00000000000..6f4a97b1040 --- /dev/null +++ b/projects/schematics/src/shared/schematics-config-mappings_spec.ts @@ -0,0 +1,175 @@ +/// + +import { + ASM_MODULE, + ASM_ROOT_MODULE, + ASM_SCHEMATICS_CONFIG, + CART_BASE_ROOT_MODULE, + CDC_MODULE, + CDC_ROOT_MODULE, + CHECKOUT_B2B_MODULE, + CHECKOUT_B2B_ROOT_MODULE, + CHECKOUT_B2B_SCHEMATICS_CONFIG, + CHECKOUT_BASE_MODULE, + CHECKOUT_BASE_ROOT_MODULE, + CHECKOUT_BASE_SCHEMATICS_CONFIG, + CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, + CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, + CHECKOUT_SCHEDULED_REPLENISHMENT_SCHEMATICS_CONFIG, + MINI_CART_MODULE, + ORDER_MODULE, + ORDER_ROOT_MODULE, +} from './lib-configs'; +import { + ASM_FEATURE_NAME, + CART_BASE_FEATURE_NAME, + CDC_FEATURE_NAME, + CDS_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + ORDER_FEATURE_NAME, + SPARTACUS_ASM, + SPARTACUS_CDC, + SPARTACUS_CHECKOUT, + SPARTACUS_ORDER, +} from './libs-constants'; +import { + generateMappings, + getKeyByMappingValue, + getKeyByMappingValueOrThrow, + getSchematicsConfigByFeatureOrThrow, +} from './schematics-config-mappings'; + +describe('schematics-config-mappings', () => { + describe('libraryFeatureMapping', () => { + it('should generate a correct mapping', () => { + const result = generateMappings().libraryFeatureMapping; + + expect(result.get(SPARTACUS_ASM)).toEqual([ASM_FEATURE_NAME]); + expect(result.get(SPARTACUS_CHECKOUT)).toEqual([ + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + ]); + expect(result.get(SPARTACUS_ORDER)).toEqual([ORDER_FEATURE_NAME]); + expect(result.get(SPARTACUS_CDC)).toEqual([CDC_FEATURE_NAME]); + }); + }); + + describe('featureFeatureModuleMapping', () => { + it('should generate a correct mapping', () => { + const result = generateMappings().featureFeatureModuleMapping; + + expect(result.get(ASM_FEATURE_NAME)).toEqual([ASM_MODULE]); + expect(result.get(CART_BASE_FEATURE_NAME)).toContain(MINI_CART_MODULE); + expect(result.get(CHECKOUT_BASE_FEATURE_NAME)).toEqual([ + CHECKOUT_BASE_MODULE, + ]); + expect(result.get(CHECKOUT_B2B_FEATURE_NAME)).toEqual([ + CHECKOUT_B2B_MODULE, + ]); + expect(result.get(CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME)).toEqual( + [CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE] + ); + expect(result.get(ORDER_FEATURE_NAME)).toEqual([ORDER_MODULE]); + expect(result.get(CDC_FEATURE_NAME)).toEqual([CDC_MODULE]); + }); + }); + + describe('featureRootModuleMapping', () => { + it('should generate a correct mapping', () => { + const result = generateMappings().featureRootModuleMapping; + + expect(result.get(ASM_FEATURE_NAME)).toEqual([ASM_ROOT_MODULE]); + expect(result.get(CART_BASE_FEATURE_NAME)).toContain( + CART_BASE_ROOT_MODULE + ); + expect(result.get(CHECKOUT_BASE_FEATURE_NAME)).toEqual([ + CHECKOUT_BASE_ROOT_MODULE, + ]); + expect(result.get(CHECKOUT_B2B_FEATURE_NAME)).toEqual([ + CHECKOUT_B2B_ROOT_MODULE, + ]); + expect(result.get(CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME)).toEqual( + [CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE] + ); + expect(result.get(ORDER_FEATURE_NAME)).toEqual([ORDER_ROOT_MODULE]); + expect(result.get(CDC_FEATURE_NAME)).toEqual([CDC_ROOT_MODULE]); + expect(result.get(CDS_FEATURE_NAME)).toEqual([]); + expect(result.get(DIGITAL_PAYMENTS_FEATURE_NAME)).toEqual([]); + }); + }); + + describe('featureSchematicConfigMapping', () => { + it('should generate a correct mapping', () => { + const result = generateMappings().featureSchematicConfigMapping; + + expect(result.get(ASM_FEATURE_NAME)).toEqual(ASM_SCHEMATICS_CONFIG); + expect(result.get(CHECKOUT_BASE_FEATURE_NAME)).toEqual( + CHECKOUT_BASE_SCHEMATICS_CONFIG + ); + expect(result.get(CHECKOUT_B2B_FEATURE_NAME)).toEqual( + CHECKOUT_B2B_SCHEMATICS_CONFIG + ); + expect(result.get(CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME)).toEqual( + CHECKOUT_SCHEDULED_REPLENISHMENT_SCHEMATICS_CONFIG + ); + }); + }); + + describe('getKeyByMappingValueOrThrow', () => { + it('should return the key', () => { + const mapping: Map = new Map(); + mapping.set('x', ['1', '2']); + const result = getKeyByMappingValueOrThrow(mapping, '1'); + expect(result).toEqual('x'); + }); + it('should throw if not found', () => { + const mapping: Map = new Map(); + mapping.set('x', ['1', '2']); + try { + getKeyByMappingValueOrThrow(mapping, '3'); + } catch (e) { + expect((e as any).message).toEqual( + `Value 3 not found in the given map.` + ); + } + }); + }); + + describe('getKeyByMappingValue', () => { + it('should return the key', () => { + const mapping: Map = new Map(); + mapping.set('x', ['1', '2']); + const result = getKeyByMappingValue(mapping, '1'); + expect(result).toEqual('x'); + }); + it('should return undefined if not found', () => { + const mapping: Map = new Map(); + mapping.set('x', ['1', '2']); + const result = getKeyByMappingValue(mapping, '3'); + expect(result).toBeFalsy(); + }); + }); + + describe('getSchematicsConfigByFeatureOrThrow', () => { + it('should return the config', () => { + const config = getSchematicsConfigByFeatureOrThrow( + CHECKOUT_BASE_FEATURE_NAME + ); + expect(config).toBeTruthy(); + }); + it('should throw when not found', () => { + const feature = 'xxx'; + try { + getSchematicsConfigByFeatureOrThrow(feature); + } catch (e) { + expect((e as any).message).toEqual( + `Config not found for the given feature '${feature}'` + ); + } + }); + }); +}); diff --git a/projects/schematics/src/shared/updateable-constants.ts b/projects/schematics/src/shared/updateable-constants.ts deleted file mode 100644 index dcaab952169..00000000000 --- a/projects/schematics/src/shared/updateable-constants.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { - ADD_TO_CART_MODULE, - ADD_TO_WISHLIST_MODULE, - ADMINISTRATION_MODULE, - ADMINISTRATION_ROOT_MODULE, - ASM_MODULE, - ASM_ROOT_MODULE, - BULK_PRICING_MODULE, - BULK_PRICING_ROOT_MODULE, - CART_BASE_MODULE, - CART_BASE_ROOT_MODULE, - CART_IMPORT_EXPORT_MODULE, - CART_IMPORT_EXPORT_ROOT_MODULE, - CART_WISHLIST_MODULE, - CART_WISHLIST_ROOT_MODULE, - CDC_MODULE, - CDC_ROOT_MODULE, - CDS_MODULE, - CHECKOUT_B2B_MODULE, - CHECKOUT_B2B_ROOT_MODULE, - CHECKOUT_BASE_MODULE, - CHECKOUT_BASE_ROOT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, - CLI_ASM_FEATURE, - CLI_CART_BASE_FEATURE, - CLI_CART_IMPORT_EXPORT_FEATURE, - CLI_CART_QUICK_ORDER_FEATURE, - CLI_CART_SAVED_CART_FEATURE, - CLI_CART_WISHLIST_FEATURE, - CLI_CDC_FEATURE, - CLI_CDS_FEATURE, - CLI_CHECKOUT_B2B_FEATURE, - CLI_CHECKOUT_BASE_FEATURE, - CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, - CLI_DIGITAL_PAYMENTS_FEATURE, - CLI_EPD_VISUALIZATION_FEATURE, - CLI_ORDER_FEATURE, - CLI_ORGANIZATION_ADMINISTRATION_FEATURE, - CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE, - CLI_PRODUCT_BULK_PRICING_FEATURE, - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, - CLI_PRODUCT_CONFIGURATOR_VC_FEATURE, - CLI_PRODUCT_IMAGE_ZOOM_FEATURE, - CLI_PRODUCT_VARIANTS_FEATURE, - CLI_QUALTRICS_FEATURE, - CLI_SMARTEDIT_FEATURE, - CLI_STOREFINDER_FEATURE, - CLI_TRACKING_PERSONALIZATION_FEATURE, - CLI_TRACKING_TMS_AEP_FEATURE, - CLI_TRACKING_TMS_GTM_FEATURE, - CLI_USER_ACCOUNT_FEATURE, - CLI_USER_PROFILE_FEATURE, - DIGITAL_PAYMENTS_MODULE, - EPD_VISUALIZATION_MODULE, - EPD_VISUALIZATION_ROOT_MODULE, - IMAGE_ZOOM_MODULE, - IMAGE_ZOOM_ROOT_MODULE, - MINI_CART_MODULE, - ORDER_APPROVAL_MODULE, - ORDER_APPROVAL_ROOT_MODULE, - ORDER_MODULE, - ORDER_ROOT_MODULE, - PERSONALIZATION_MODULE, - PERSONALIZATION_ROOT_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE, - PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE, - PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE, - QUALTRICS_MODULE, - QUALTRICS_ROOT_MODULE, - QUICK_ORDER_MODULE, - QUICK_ORDER_ROOT_MODULE, - SAVED_CART_MODULE, - SAVED_CART_ROOT_MODULE, - SMARTEDIT_MODULE, - SMARTEDIT_ROOT_MODULE, - SPARTACUS_ASM, - SPARTACUS_CART, - SPARTACUS_CDC, - SPARTACUS_CDS, - SPARTACUS_CHECKOUT, - SPARTACUS_DIGITAL_PAYMENTS, - SPARTACUS_EPD_VISUALIZATION, - SPARTACUS_ORDER, - SPARTACUS_ORGANIZATION, - SPARTACUS_PRODUCT, - SPARTACUS_PRODUCT_CONFIGURATOR, - SPARTACUS_QUALTRICS, - SPARTACUS_SMARTEDIT, - SPARTACUS_STOREFINDER, - SPARTACUS_TRACKING, - SPARTACUS_USER, - STOREFINDER_MODULE, - STOREFINDER_ROOT_MODULE, - TMS_AEP_MODULE, - TMS_BASE_MODULE, - TMS_GTM_MODULE, - USER_ACCOUNT_MODULE, - USER_ACCOUNT_ROOT_MODULE, - USER_PROFILE_MODULE, - USER_PROFILE_ROOT_MODULE, - VARIANTS_MODULE, - VARIANTS_ROOT_MODULE, -} from './libs-constants'; - -/** - * Maps sub-features to their parent feature. - * E.g. User feature contains sub-features: Account, Profile. - */ -export const packageSubFeaturesMapping: Record = { - /** Feature modules lib start */ - [SPARTACUS_ASM]: [CLI_ASM_FEATURE], - [SPARTACUS_CART]: [ - CLI_CART_BASE_FEATURE, - CLI_CART_WISHLIST_FEATURE, - CLI_CART_IMPORT_EXPORT_FEATURE, - CLI_CART_QUICK_ORDER_FEATURE, - CLI_CART_SAVED_CART_FEATURE, - ], - [SPARTACUS_CHECKOUT]: [ - CLI_CHECKOUT_BASE_FEATURE, - CLI_CHECKOUT_B2B_FEATURE, - CLI_CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE, - ], - [SPARTACUS_ORDER]: [CLI_ORDER_FEATURE], - [SPARTACUS_ORGANIZATION]: [ - CLI_ORGANIZATION_ADMINISTRATION_FEATURE, - CLI_ORGANIZATION_ORDER_APPROVAL_FEATURE, - ], - [SPARTACUS_PRODUCT]: [ - CLI_PRODUCT_BULK_PRICING_FEATURE, - CLI_PRODUCT_VARIANTS_FEATURE, - CLI_PRODUCT_IMAGE_ZOOM_FEATURE, - ], - [SPARTACUS_PRODUCT_CONFIGURATOR]: [ - CLI_PRODUCT_CONFIGURATOR_VC_FEATURE, - CLI_PRODUCT_CONFIGURATOR_TEXTFIELD_FEATURE, - CLI_PRODUCT_CONFIGURATOR_CPQ_FEATURE, - ], - [SPARTACUS_QUALTRICS]: [CLI_QUALTRICS_FEATURE], - [SPARTACUS_SMARTEDIT]: [CLI_SMARTEDIT_FEATURE], - [SPARTACUS_STOREFINDER]: [CLI_STOREFINDER_FEATURE], - [SPARTACUS_TRACKING]: [ - CLI_TRACKING_PERSONALIZATION_FEATURE, - CLI_TRACKING_TMS_GTM_FEATURE, - CLI_TRACKING_TMS_AEP_FEATURE, - ], - [SPARTACUS_USER]: [CLI_USER_ACCOUNT_FEATURE, CLI_USER_PROFILE_FEATURE], - /** Feature modules lib end */ - - /** Integration libs start */ - [SPARTACUS_CDC]: [CLI_CDC_FEATURE], - [SPARTACUS_CDS]: [CLI_CDS_FEATURE], - [SPARTACUS_DIGITAL_PAYMENTS]: [CLI_DIGITAL_PAYMENTS_FEATURE], - [SPARTACUS_EPD_VISUALIZATION]: [CLI_EPD_VISUALIZATION_FEATURE], - /** Integration libs end */ -}; - -/** - * Maps the sub-feature's configurations to its parent feature. - * E.g. User's sub-features contains name configurations for - * Account and Profile: USER_ACCOUNT_MODULE, USER_ACCOUNT_ROOT_MODULE, - * USER_PROFILE_MODULE, USER_PROFILE_ROOT_MODULE, - */ -export const packageFeatureConfigMapping: Record = { - /** Feature modules lib start */ - [SPARTACUS_ASM]: [ASM_MODULE, ASM_ROOT_MODULE], - [SPARTACUS_CART]: [ - CART_BASE_MODULE, - MINI_CART_MODULE, - ADD_TO_CART_MODULE, - CART_BASE_ROOT_MODULE, - CART_WISHLIST_MODULE, - ADD_TO_WISHLIST_MODULE, - CART_WISHLIST_ROOT_MODULE, - SAVED_CART_MODULE, - SAVED_CART_ROOT_MODULE, - QUICK_ORDER_MODULE, - QUICK_ORDER_ROOT_MODULE, - CART_IMPORT_EXPORT_MODULE, - CART_IMPORT_EXPORT_ROOT_MODULE, - ], - [SPARTACUS_CHECKOUT]: [ - CHECKOUT_BASE_MODULE, - CHECKOUT_BASE_ROOT_MODULE, - CHECKOUT_B2B_MODULE, - CHECKOUT_B2B_ROOT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_MODULE, - CHECKOUT_SCHEDULED_REPLENISHMENT_ROOT_MODULE, - ], - [SPARTACUS_ORDER]: [ORDER_MODULE, ORDER_ROOT_MODULE], - [SPARTACUS_ORGANIZATION]: [ - ADMINISTRATION_MODULE, - ADMINISTRATION_ROOT_MODULE, - ORDER_APPROVAL_MODULE, - ORDER_APPROVAL_ROOT_MODULE, - ], - [SPARTACUS_PRODUCT]: [ - BULK_PRICING_MODULE, - BULK_PRICING_ROOT_MODULE, - VARIANTS_MODULE, - VARIANTS_ROOT_MODULE, - IMAGE_ZOOM_MODULE, - IMAGE_ZOOM_ROOT_MODULE, - ], - [SPARTACUS_PRODUCT_CONFIGURATOR]: [ - PRODUCT_CONFIGURATOR_TEXTFIELD_MODULE, - PRODUCT_CONFIGURATOR_TEXTFIELD_ROOT_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_ROOT_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_MODULE, - PRODUCT_CONFIGURATOR_RULEBASED_CPQ_ROOT_MODULE, - ], - [SPARTACUS_QUALTRICS]: [QUALTRICS_MODULE, QUALTRICS_ROOT_MODULE], - [SPARTACUS_SMARTEDIT]: [SMARTEDIT_MODULE, SMARTEDIT_ROOT_MODULE], - [SPARTACUS_STOREFINDER]: [STOREFINDER_MODULE, STOREFINDER_ROOT_MODULE], - [SPARTACUS_TRACKING]: [ - TMS_BASE_MODULE, - TMS_GTM_MODULE, - TMS_AEP_MODULE, - PERSONALIZATION_MODULE, - PERSONALIZATION_ROOT_MODULE, - ], - [SPARTACUS_USER]: [ - USER_ACCOUNT_MODULE, - USER_ACCOUNT_ROOT_MODULE, - USER_PROFILE_MODULE, - USER_PROFILE_ROOT_MODULE, - ], - /** Feature libs end */ - - /** Integration libs start */ - [SPARTACUS_CDC]: [CDC_MODULE, CDC_ROOT_MODULE], - [SPARTACUS_CDS]: [CDS_MODULE], - [SPARTACUS_DIGITAL_PAYMENTS]: [DIGITAL_PAYMENTS_MODULE], - [SPARTACUS_EPD_VISUALIZATION]: [ - EPD_VISUALIZATION_MODULE, - EPD_VISUALIZATION_ROOT_MODULE, - ], - /** Integration libs end */ -}; diff --git a/projects/schematics/src/shared/utils/__snapshots__/feature-utils_spec.ts.snap b/projects/schematics/src/shared/utils/__snapshots__/feature-utils_spec.ts.snap new file mode 100644 index 00000000000..abfd8b8a8fe --- /dev/null +++ b/projects/schematics/src/shared/utils/__snapshots__/feature-utils_spec.ts.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Feature utils addFeatures should generate feature modules for the given array of features 1`] = ` +"import { NgModule } from '@angular/core'; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { userAccountTranslationChunksConfig, userAccountTranslations } from \\"@spartacus/user/account/assets\\"; +import { UserAccountRootModule, USER_ACCOUNT_FEATURE } from \\"@spartacus/user/account/root\\"; + +@NgModule({ + declarations: [], + imports: [ + UserAccountRootModule + ], + providers: [provideConfig({ + featureModules: { + [USER_ACCOUNT_FEATURE]: { + module: () => + import('@spartacus/user/account').then((m) => m.UserAccountModule), + }, + } + }), + provideConfig({ + i18n: { + resources: userAccountTranslations, + chunks: userAccountTranslationChunksConfig, + }, + }) + ] +}) +export class UserFeatureModule { } +" +`; + +exports[`Feature utils analyzeApplication dependent features check when the dependent feature is eagerly configured should succeed 1`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule } from \\"@spartacus/checkout/base/root\\"; +import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule, + CheckoutModule, + DigitalPaymentsModule + ], + providers: [provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + })] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Feature utils analyzeApplication dependent features check when the dependent feature is lazily configured should succeed 1`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + DigitalPaymentsModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Feature utils analyzeApplication dependent features check when the dependent feature is lazily configured should succeed 2`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Feature utils getDynamicallyImportedLocalSourceFile should return the locally referenced source file 1`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; diff --git a/projects/schematics/src/shared/utils/__snapshots__/lib-utils_spec.ts.snap b/projects/schematics/src/shared/utils/__snapshots__/lib-utils_spec.ts.snap index 0ad5cedfb76..4b5c0f499a2 100644 --- a/projects/schematics/src/shared/utils/__snapshots__/lib-utils_spec.ts.snap +++ b/projects/schematics/src/shared/utils/__snapshots__/lib-utils_spec.ts.snap @@ -303,68 +303,6 @@ export class XxxFeatureModule { } " `; -exports[`Lib utils addLibraryFeature recreate option should remove the feature module and recreate it 1`] = ` -"import { NgModule } from '@angular/core'; -import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { translationChunk, translations } from \\"@spartacus/xxx/assets\\"; -import { XxxModuleRoot } from \\"@spartacus/xxx/root\\"; - -@NgModule({ - declarations: [], - imports: [ - XxxModuleRoot - ], - providers: [provideConfig({ - featureModules: { - xxx: { - module: () => - import('@spartacus/xxx').then((m) => m.XxxModule), - }, - } - }), - provideConfig({ - i18n: { - resources: translations, - chunks: translationChunk, - }, - }) - ] -}) -export class XxxFeatureModule { } -" -`; - -exports[`Lib utils addLibraryFeature recreate option should remove the feature module and recreate it 2`] = ` -"import { NgModule } from '@angular/core'; -import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; -import { translationChunk, translations } from \\"@spartacus/xxx/assets\\"; -import { XxxModuleRoot } from \\"@spartacus/xxx/root\\"; - -@NgModule({ - declarations: [], - imports: [ - XxxModuleRoot - ], - providers: [provideConfig({ - featureModules: { - xxx: { - module: () => - import('@spartacus/xxx').then((m) => m.YyyModule), - }, - } - }), - provideConfig({ - i18n: { - resources: translations, - chunks: translationChunk, - }, - }) - ] -}) -export class XxxFeatureModule { } -" -`; - exports[`Lib utils addLibraryFeature should NOT add i18n if the config is not present 1`] = ` "import { NgModule } from '@angular/core'; import { CmsConfig, provideConfig } from \\"@spartacus/core\\"; @@ -473,73 +411,3 @@ import { XxxModuleRoot } from \\"@spartacus/xxx/root\\"; export class XxxFeatureModule { } " `; - -exports[`Lib utils feature ordering should appropriately order the feature modules 1`] = ` -"import { NgModule } from '@angular/core'; -import { AnonymousConsentsModule, AuthModule, CostCenterOccModule, ExternalRoutesModule, ProductModule, ProductOccModule, UserModule, UserOccModule } from \\"@spartacus/core\\"; -import { AddressBookModule, AnonymousConsentManagementBannerModule, AnonymousConsentsDialogModule, BannerCarouselModule, BannerModule, BreadcrumbModule, CategoryNavigationModule, CmsParagraphModule, ConsentManagementModule, FooterNavigationModule, HamburgerMenuModule, HomePageEventModule, LinkModule, LoginRouteModule, LogoutModule, MyCouponsModule, MyInterestsModule, NavigationEventModule, NavigationModule, NotificationPreferenceModule, PageTitleModule, PaymentMethodsModule, ProductCarouselModule, ProductDetailsPageModule, ProductFacetNavigationModule, ProductImagesModule, ProductIntroModule, ProductListingPageModule, ProductListModule, ProductPageEventModule, ProductReferencesModule, ProductSummaryModule, ProductTabsModule, ScrollToTopModule, SearchBoxModule, SiteContextSelectorModule, StockNotificationModule, TabParagraphContainerModule } from \\"@spartacus/storefront\\"; -import { CartFeatureModule } from './features/cart/cart-feature.module'; -import { CheckoutFeatureModule } from './features/checkout/checkout-feature.module'; -import { DigitalPaymentsFeatureModule } from './features/dp/digital-payments-feature.module'; -import { OrderFeatureModule } from './features/order/order-feature.module'; -import { UserProfileFeatureModule } from './features/user/user-profile-feature.module'; -import { XxxFeatureModule } from './features/xxx/xxx-feature.module'; - -@NgModule({ - declarations: [], - imports: [AuthModule.forRoot(), - LogoutModule, - LoginRouteModule, - HamburgerMenuModule, - SiteContextSelectorModule, - LinkModule, - BannerModule, - CmsParagraphModule, - TabParagraphContainerModule, - BannerCarouselModule, - CategoryNavigationModule, - NavigationModule, - FooterNavigationModule, - BreadcrumbModule, - ScrollToTopModule, - PageTitleModule, - UserModule, - UserOccModule, - AddressBookModule, - PaymentMethodsModule, - NotificationPreferenceModule, - MyInterestsModule, - StockNotificationModule, - ConsentManagementModule, - MyCouponsModule, - AnonymousConsentsModule.forRoot(), - AnonymousConsentsDialogModule, - AnonymousConsentManagementBannerModule, - ProductModule.forRoot(), - ProductOccModule, - ProductDetailsPageModule, - ProductListingPageModule, - ProductListModule, - SearchBoxModule, - ProductFacetNavigationModule, - ProductTabsModule, - ProductCarouselModule, - ProductReferencesModule, - ProductImagesModule, - ProductSummaryModule, - ProductIntroModule, - CostCenterOccModule, - NavigationEventModule, - HomePageEventModule, - ProductPageEventModule, - ExternalRoutesModule.forRoot(), - UserProfileFeatureModule, - CartFeatureModule, - OrderFeatureModule, - CheckoutFeatureModule, - DigitalPaymentsFeatureModule, - XxxFeatureModule] -}) -export class SpartacusFeaturesModule { } -" -`; diff --git a/projects/schematics/src/shared/utils/config-utils.ts b/projects/schematics/src/shared/utils/config-utils.ts index 9825d632eea..47d0c1fe038 100644 --- a/projects/schematics/src/shared/utils/config-utils.ts +++ b/projects/schematics/src/shared/utils/config-utils.ts @@ -1,8 +1,15 @@ -import { ArrayLiteralExpression, Expression, Node, SourceFile } from 'ts-morph'; +import { + ArrayLiteralExpression, + Expression, + Node, + ObjectLiteralExpression, + SourceFile, + SyntaxKind, +} from 'ts-morph'; import { PROVIDE_CONFIG_FUNCTION } from '../constants'; import { SPARTACUS_CORE, SPARTACUS_SETUP } from '../libs-constants'; +import { AdditionalProviders } from './feature-utils'; import { isImportedFromSpartacusLibs } from './import-utils'; -import { CustomConfig } from './lib-utils'; import { getModule, getModulePropertyInitializer } from './new-module-utils'; export function getSpartacusProviders( @@ -70,7 +77,44 @@ function normalizeConfiguration(config: string | Node): string { return newConfig; } -export function getB2bConfiguration(): CustomConfig[] { +export function normalizeObject(obj: string): string { + return obj.replace(EMPTY_SPACE_REG_EXP, ''); +} + +/** + * Removes the config for the given property name. + * If the object is empty after removal, the object + * itself is removed. + */ +export function removeProperty( + objectLiteral: ObjectLiteralExpression, + propertyName: string +): void { + const properties = objectLiteral.getProperties(); + for (const property of properties) { + if (!Node.isPropertyAssignment(property)) { + continue; + } + + if (property.getName() === propertyName) { + property.remove(); + return; + } + + const nestedConfigObject = property.getFirstDescendantByKind( + SyntaxKind.ObjectLiteralExpression + ); + if (nestedConfigObject) { + removeProperty(nestedConfigObject, propertyName); + } + + if (normalizeObject(property.getInitializer()?.getText() ?? '') === '{}') { + property.remove(); + } + } +} + +export function getB2bConfiguration(): AdditionalProviders[] { return [ { import: [ diff --git a/projects/schematics/src/shared/utils/config-utils_spec.ts b/projects/schematics/src/shared/utils/config-utils_spec.ts index 9798c3292e2..37c8002bab6 100644 --- a/projects/schematics/src/shared/utils/config-utils_spec.ts +++ b/projects/schematics/src/shared/utils/config-utils_spec.ts @@ -1,13 +1,27 @@ /// -import { InMemoryFileSystemHost, Project, SourceFile } from 'ts-morph'; +import { + InMemoryFileSystemHost, + Project, + SourceFile, + SyntaxKind, +} from 'ts-morph'; import { getSpartacusProviders, isSpartacusConfigDuplicate, + removeProperty, } from './config-utils'; import { getModulePropertyInitializer } from './new-module-utils'; describe('Storefront config utils', () => { + let project: Project; + let sourceFile: SourceFile; + beforeEach(() => { + project = new Project({ + fileSystem: new InMemoryFileSystemHost(), + }); + }); + describe('ts-morph config utils', () => { const configFileContent = ` import { NgModule } from '@angular/core'; @@ -58,12 +72,8 @@ provideConfig({ }) export class TrackingFeatureModule {} `; - let sourceFile: SourceFile; - beforeAll(() => { - const project = new Project({ - fileSystem: new InMemoryFileSystemHost(), - }); + beforeEach(() => { sourceFile = project.createSourceFile('test.ts', configFileContent); }); @@ -111,10 +121,7 @@ export class TrackingFeatureModule {} `; let sourceFile: SourceFile; - beforeAll(() => { - const project = new Project({ - fileSystem: new InMemoryFileSystemHost(), - }); + beforeEach(() => { sourceFile = project.createSourceFile('feature-module.ts', featureModule); }); @@ -129,4 +136,75 @@ featureModules: {[ORDER_FEATURE]: {module: () => import('@spartacus/order').then expect(result).toBeTruthy(); }); }); + + describe('removeProperty', () => { + describe('when the property is not present', () => { + beforeEach(() => { + sourceFile = project.createSourceFile( + 'feature-module.ts', + `const x = {a: 1}` + ); + }); + + it('should not change the object', () => { + const objectLiteral = sourceFile.getDescendantsOfKind( + SyntaxKind.ObjectLiteralExpression + )[0]; + + removeProperty(objectLiteral, 'b'); + expect(objectLiteral.print()).toEqual(`{ a: 1 }`); + }); + }); + describe('when multiple properties are present', () => { + beforeEach(() => { + sourceFile = project.createSourceFile( + 'feature-module.ts', + `const x = {a: 1, b: 2}` + ); + }); + + it('should remove the property, but keep the object', () => { + const objectLiteral = sourceFile.getDescendantsOfKind( + SyntaxKind.ObjectLiteralExpression + )[0]; + + removeProperty(objectLiteral, 'a'); + expect(objectLiteral.print()).toEqual(`{ b: 2 }`); + }); + }); + describe('when single property is present', () => { + beforeEach(() => { + sourceFile = project.createSourceFile( + 'feature-module.ts', + `const x = {a: 1}` + ); + }); + + it('should remove the property, but keep the object', () => { + const objectLiteral = sourceFile.getDescendantsOfKind( + SyntaxKind.ObjectLiteralExpression + )[0]; + + removeProperty(objectLiteral, 'a'); + expect(objectLiteral.print()).toEqual(`{}`); + }); + }); + describe('when the property is nested in the object', () => { + beforeEach(() => { + sourceFile = project.createSourceFile( + 'feature-module.ts', + `const x = {a: {b: {c: 1}}}` + ); + }); + + it('should remove the property, but keep the object', () => { + const objectLiteral = sourceFile.getDescendantsOfKind( + SyntaxKind.ObjectLiteralExpression + )[0]; + + removeProperty(objectLiteral, 'c'); + expect(objectLiteral.print()).toEqual(`{}`); + }); + }); + }); }); diff --git a/projects/schematics/src/shared/utils/dependency-utils.ts b/projects/schematics/src/shared/utils/dependency-utils.ts new file mode 100644 index 00000000000..b08249b7ebb --- /dev/null +++ b/projects/schematics/src/shared/utils/dependency-utils.ts @@ -0,0 +1,130 @@ +import collectedDependencies from '../../dependencies.json'; +import { CORE_SPARTACUS_SCOPES, SPARTACUS_SCOPE } from '../libs-constants'; +import { + getKeyByMappingValueOrThrow, + libraryFeatureMapping, +} from '../schematics-config-mappings'; +import { + calculateCrossFeatureSort, + calculateCrossLibrarySort, +} from './lib-utils'; +import { getConfiguredDependencies } from './schematics-config-utils'; + +/** + * Analyzes cross-feature Spartacus dependencies + * for the given set of features. + * + * E.g. when installing Digital Payments feature, + * the following features will also be configured: + * User-Account, User-Profile, Cart, Order, Checkout + * + * Returns the ordered list, according to the graph. + */ +export function analyzeCrossFeatureDependencies( + startingFeatures: string[] +): string[] { + const result: string[] = []; + + for (const feature of startingFeatures) { + collectCrossFeatureDeps(feature, result); + } + + return result.sort((feature1, feature2) => + calculateCrossFeatureSort(feature1, feature2) + ); +} + +/** + * Collects the cross-feature dependencies for the given feature. + * It recursively collects the dependencies for each of the + * found dependencies. + */ +function collectCrossFeatureDeps(feature: string, result: string[]): void { + // already processed + if (result.includes(feature)) { + return; + } + + result.push(feature); + + const featureDependencies = getConfiguredDependencies(feature); + for (const featureDependency of featureDependencies) { + collectCrossFeatureDeps(featureDependency, result); + } +} + +/** + * Analyzes cross-library Spartacus dependencies + * for the given set of features. + * + * For example, CDC depends on User and ASM features. + * + * Returns the ordered list, according to the features graph. + */ +export function analyzeCrossLibraryDependenciesByFeatures( + startingFeatures: string[] +): string[] { + const startingLibraries: string[] = []; + for (const feature of startingFeatures) { + const library = getKeyByMappingValueOrThrow(libraryFeatureMapping, feature); + startingLibraries.push(library); + } + + return analyzeCrossLibraryDependenciesByLibraries(startingLibraries); +} + +/** + * Analyzes cross-library Spartacus dependencies + * for the given set of libraries. + * + * For example, CDC depends on User and ASM features. + * + * Returns the ordered list, according to the features graph. + */ +export function analyzeCrossLibraryDependenciesByLibraries( + startingLibraries: string[] +): string[] { + let spartacusPeerDeps = [...startingLibraries]; + for (const spartacusLib of startingLibraries) { + collectCrossSpartacusPeerDeps(spartacusLib, spartacusPeerDeps); + } + + // remove the duplicates + spartacusPeerDeps = Array.from(new Set(spartacusPeerDeps)); + // order the libraries + spartacusPeerDeps = spartacusPeerDeps.sort((libraryA, libraryB) => + calculateCrossLibrarySort(libraryA, libraryB) + ); + + return spartacusPeerDeps; +} + +/** + * Recursively collects the cross Spartacus library dependencies for the given library. + */ +export function collectCrossSpartacusPeerDeps( + libraryName: string, + collectedDeps: string[], + processed: string[] = [] +): void { + if (processed.includes(libraryName)) { + return; + } + + const peerDepsWithVersions = + (collectedDependencies as Record>)[ + libraryName + ] ?? {}; + + const peerDeps = Object.keys(peerDepsWithVersions) + .filter((d) => d.startsWith(SPARTACUS_SCOPE)) + .filter((d) => !CORE_SPARTACUS_SCOPES.includes(d)) + .filter((d) => !collectedDeps.includes(d)); + + collectedDeps.push(...peerDeps); + processed.push(libraryName); + + for (const peerDep of peerDeps) { + collectCrossSpartacusPeerDeps(peerDep, collectedDeps); + } +} diff --git a/projects/schematics/src/shared/utils/dependency-utils_spec.ts b/projects/schematics/src/shared/utils/dependency-utils_spec.ts new file mode 100644 index 00000000000..ad663da6041 --- /dev/null +++ b/projects/schematics/src/shared/utils/dependency-utils_spec.ts @@ -0,0 +1,144 @@ +/// + +import { + CART_BASE_FEATURE_NAME, + CDC_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + ORDER_FEATURE_NAME, + SPARTACUS_ASM, + SPARTACUS_CART, + SPARTACUS_CDC, + SPARTACUS_CHECKOUT, + SPARTACUS_CORE, + SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_ORDER, + SPARTACUS_ORGANIZATION, + SPARTACUS_PRODUCT_CONFIGURATOR, + SPARTACUS_STOREFRONTLIB, + SPARTACUS_USER, + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { + analyzeCrossFeatureDependencies, + analyzeCrossLibraryDependenciesByFeatures, + analyzeCrossLibraryDependenciesByLibraries, + collectCrossSpartacusPeerDeps, +} from './dependency-utils'; + +describe('dependency-util', () => { + describe('analyzeCrossFeatureDependencies', () => { + it('DP - should return the correct set of ordered sub-features', () => { + const result = analyzeCrossFeatureDependencies([ + CDC_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + ]); + + expect(result).toEqual([ + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, + CART_BASE_FEATURE_NAME, + ORDER_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + CDC_FEATURE_NAME, + ]); + }); + it('User Profile - should return the correct set of ordered sub-features', () => { + const result = analyzeCrossFeatureDependencies([ + USER_PROFILE_FEATURE_NAME, + ]); + + expect(result).toEqual([ + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, + ]); + }); + }); + + describe('analyzeCrossLibraryDependenciesByFeatures', () => { + it('DP - should return the correct set of ordered libraries', () => { + const result = analyzeCrossLibraryDependenciesByFeatures([ + DIGITAL_PAYMENTS_FEATURE_NAME, + ]); + + expect(result).toEqual([ + SPARTACUS_USER, + SPARTACUS_CART, + SPARTACUS_ORDER, + SPARTACUS_CHECKOUT, + SPARTACUS_DIGITAL_PAYMENTS, + ]); + }); + it('CDC - should return the correct set of ordered libraries', () => { + const result = analyzeCrossLibraryDependenciesByFeatures([ + CDC_FEATURE_NAME, + ]); + + expect(result).toEqual([SPARTACUS_USER, SPARTACUS_ASM, SPARTACUS_CDC]); + }); + }); + + describe('analyzeCrossLibraryDependenciesByLibraries', () => { + it('DP and CDC - should return the correct set of ordered libraries', () => { + const result = analyzeCrossLibraryDependenciesByLibraries([ + SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_CDC, + ]); + + expect(result).toEqual([ + SPARTACUS_USER, + SPARTACUS_CART, + SPARTACUS_ORDER, + SPARTACUS_CHECKOUT, + SPARTACUS_ASM, + SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_CDC, + ]); + }); + + it('CDC - should return the correct set of ordered libraries', () => { + const result = analyzeCrossLibraryDependenciesByLibraries([ + SPARTACUS_CDC, + ]); + + expect(result).toEqual([SPARTACUS_USER, SPARTACUS_ASM, SPARTACUS_CDC]); + }); + + it('Core libs, Organization and Product configurator', () => { + const result = analyzeCrossLibraryDependenciesByLibraries([ + SPARTACUS_CORE, + SPARTACUS_STOREFRONTLIB, + SPARTACUS_ORGANIZATION, + SPARTACUS_PRODUCT_CONFIGURATOR, + ]); + + expect(result).toEqual([ + SPARTACUS_USER, + SPARTACUS_CART, + SPARTACUS_ORDER, + SPARTACUS_CHECKOUT, + SPARTACUS_PRODUCT_CONFIGURATOR, + SPARTACUS_ORGANIZATION, + SPARTACUS_CORE, + SPARTACUS_STOREFRONTLIB, + ]); + }); + }); + + describe('collectCrossSpartacusPeerDeps', () => { + it('should correctly collect all peer deps for the given library and its dependencies', () => { + const result: string[] = []; + collectCrossSpartacusPeerDeps(SPARTACUS_DIGITAL_PAYMENTS, result); + + expect(result).toContain(SPARTACUS_CART); + expect(result).toContain(SPARTACUS_CHECKOUT); + expect(result).toContain(SPARTACUS_ORDER); + expect(result).toContain(SPARTACUS_USER); + }); + }); +}); diff --git a/projects/schematics/src/shared/utils/feature-utils.ts b/projects/schematics/src/shared/utils/feature-utils.ts new file mode 100644 index 00000000000..17778f3887a --- /dev/null +++ b/projects/schematics/src/shared/utils/feature-utils.ts @@ -0,0 +1,485 @@ +import { + chain, + externalSchematic, + noop, + Rule, + SchematicContext, + SchematicsException, + Tree, +} from '@angular-devkit/schematics'; +import { + ArrowFunction, + CallExpression, + Expression, + Identifier, + Node, + SourceFile, + ts as tsMorph, +} from 'ts-morph'; +import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; +import { Schema as SpartacusWrapperOptions } from '../../wrapper-module/schema'; +import { ANGULAR_CORE } from '../constants'; +import { + SPARTACUS_FEATURES_MODULE, + SPARTACUS_FEATURES_NG_MODULE, + SPARTACUS_SCHEMATICS, +} from '../libs-constants'; +import { + featureFeatureModuleMapping, + featureSchematicConfigMapping, + getKeyByMappingValueOrThrow, + getSchematicsConfigByFeatureOrThrow, +} from '../schematics-config-mappings'; +import { crossFeatureInstallationOrder } from './graph-utils'; +import { + findDynamicImport, + getDynamicImportImportPath, + isImportedFrom, + isRelative, + staticImportExists, +} from './import-utils'; +import { + addLibraryFeature, + checkAppStructure, + LibraryOptions, + Module, + SchematicConfig, +} from './lib-utils'; +import { getModulePropertyInitializer, Import } from './new-module-utils'; +import { createProgram } from './program'; +import { getProjectTsConfigPaths } from './project-tsconfig-paths'; + +export interface FeatureModuleImports { + importPath: string; + moduleNode: Expression | Identifier; +} + +/** + * Custom schematics configuration providers. + */ +export interface AdditionalProviders { + import: Import[]; + content: string; +} + +/** + * Additional schematics configurations / overrides. + */ +export interface AdditionalFeatureConfiguration { + /** + * If specified, provides the specified configuration. + */ + providers?: AdditionalProviders | AdditionalProviders[]; + /** + * If specified, overrides the pre-defined schematics options. + */ + options?: T; +} + +/** + * Analysis result of wrapper module configuration. + */ +interface WrapperAnalysisResult { + /** + * Marker name. + */ + markerModuleName: string; + /** + * Options. + */ + wrapperOptions: SpartacusWrapperOptions; +} + +/** + * Configures feature modules for the given array of features. + * + * Optionally, an override can be provided for the default + * schematics options and/or feature-schematics configuration. + */ +export function addFeatures( + options: OPTIONS, + features: string[] +): Rule { + return (_tree: Tree, context: SchematicContext): Rule => { + if (options.debug) { + let message = `\n******************************\n`; + message += `Cross feature graph:\n`; + message += crossFeatureInstallationOrder.join(', '); + message += `\n******************************\n`; + context.logger.info(message); + } + + /** + * In dirty installations, we don't want to + * force-install the dependent features. + */ + const featuresToInstall = options.internal?.dirtyInstallation + ? options.features ?? [] + : features; + + const rules: Rule[] = []; + for (const feature of featuresToInstall) { + const schematicsConfiguration = + featureSchematicConfigMapping.get(feature); + if (!schematicsConfiguration) { + throw new SchematicsException( + `[Internal] No feature config found for ${feature}. ` + + `Please check if the schematics config is added to projects/schematics/src/shared/schematics-config-mappings.ts` + ); + } + + // TODO:#schematics - fix the interactivity for the CDS / ASM, etc. + const libraryOptions = + schematicsConfiguration.customConfig?.(options).options ?? options; + + rules.push(addLibraryFeature(libraryOptions, schematicsConfiguration)); + + const wrappers = analyzeWrappers(schematicsConfiguration, libraryOptions); + for (const { wrapperOptions } of wrappers) { + rules.push( + externalSchematic( + SPARTACUS_SCHEMATICS, + 'wrapper-module', + wrapperOptions + ) + ); + } + } + + return chain(rules); + }; +} + +/** + * Analyzes the given schematics configuration for the wrapper modules. + * It builds the options for the wrapper schematic run, + * including the execution sequence. + */ +function analyzeWrappers( + schematicsConfiguration: SchematicConfig, + options: OPTIONS +): WrapperAnalysisResult[] { + if (!schematicsConfiguration.wrappers) { + return []; + } + + const result: WrapperAnalysisResult[] = []; + for (const markerModuleName in schematicsConfiguration.wrappers) { + if (!schematicsConfiguration.wrappers.hasOwnProperty(markerModuleName)) { + continue; + } + + const featureModuleName = + schematicsConfiguration.wrappers[markerModuleName]; + const wrapperOptions: SpartacusWrapperOptions = { + scope: options.scope, + interactive: options.interactive, + project: options.project, + markerModuleName, + featureModuleName, + debug: options.debug, + }; + + const analysis: WrapperAnalysisResult = { + markerModuleName, + wrapperOptions, + }; + result.push(analysis); + } + + return result; +} + +/** + * If exists, it returns the spartacus-features.module.ts' source. + * Otherwise, it returns undefined. + */ +export function getSpartacusFeaturesModule( + tree: Tree, + basePath: string, + tsconfigPath: string +): SourceFile | undefined { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + for (const sourceFile of appSourceFiles) { + if ( + sourceFile + .getFilePath() + .includes(`${SPARTACUS_FEATURES_MODULE}.module.ts`) + ) { + if (getSpartacusFeaturesNgModuleDecorator(sourceFile)) { + return sourceFile; + } + } + } + return undefined; +} + +/** + * Returns the NgModule decorator, if exists. + */ +function getSpartacusFeaturesNgModuleDecorator( + sourceFile: SourceFile +): CallExpression | undefined { + let spartacusFeaturesModule: CallExpression | undefined; + + function visitor(node: Node) { + if (Node.isCallExpression(node)) { + const expression = node.getExpression(); + if ( + Node.isIdentifier(expression) && + expression.getText() === 'NgModule' && + isImportedFrom(expression, ANGULAR_CORE) + ) { + const classDeclaration = node.getFirstAncestorByKind( + tsMorph.SyntaxKind.ClassDeclaration + ); + if (classDeclaration) { + const identifier = classDeclaration.getNameNode(); + if ( + identifier && + identifier.getText() === SPARTACUS_FEATURES_NG_MODULE + ) { + spartacusFeaturesModule = node; + } + } + } + } + + node.forEachChild(visitor); + } + + sourceFile.forEachChild(visitor); + return spartacusFeaturesModule; +} + +/** + * For the given feature module name, + * returns the module configuration part + * of the given schematics feature config + */ +export function getModuleConfig( + featureModuleName: string, + featureConfig: SchematicConfig +): Module | undefined { + const featureModuleConfigs = ([] as Module[]).concat( + featureConfig.featureModule + ); + for (const featureModuleConfig of featureModuleConfigs) { + if (featureModuleConfig.name === featureModuleName) { + return featureModuleConfig; + } + } + + return undefined; +} + +/** + * Analyzes the customers' application. + * It checks for presence of Spartacus features and + * whether they're configured or present in package.json. + */ +export function analyzeApplication( + options: OPTIONS, + allFeatures: string[] +): Rule { + return (tree: Tree, context: SchematicContext) => { + const spartacusFeatureModuleExists = checkAppStructure( + tree, + options.project + ); + /** + * Mutates the options, and sets the internal properties + * for later usage in other rules. + */ + options.internal = { + ...options.internal, + dirtyInstallation: spartacusFeatureModuleExists, + }; + + if (!options.internal.dirtyInstallation) { + const dependentFeaturesMessage = createDependentFeaturesLog( + options, + allFeatures + ); + if (dependentFeaturesMessage) { + context.logger.info(dependentFeaturesMessage); + } + + return noop(); + } + + if (options.debug) { + context.logger.info(`⌛️ Analyzing application...`); + } + + for (const targetFeature of options.features ?? []) { + const targetFeatureConfig = + getSchematicsConfigByFeatureOrThrow(targetFeature); + if (!targetFeatureConfig.wrappers) { + continue; + } + + const wrappers = analyzeWrappers(targetFeatureConfig, options); + for (const { wrapperOptions } of wrappers) { + const markerFeature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + wrapperOptions.markerModuleName + ); + + const markerFeatureConfig = + getSchematicsConfigByFeatureOrThrow(markerFeature); + const markerModuleConfig = getModuleConfig( + wrapperOptions.markerModuleName, + markerFeatureConfig + ); + if (!markerModuleConfig) { + continue; + } + + if (markerModuleExists(options, tree, markerModuleConfig)) { + continue; + } + + const targetModuleName = wrapperOptions.featureModuleName; + const targetFeature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + targetModuleName + ); + const targetFeatureConfig = + getSchematicsConfigByFeatureOrThrow(targetFeature); + const targetModuleConfig = getModuleConfig( + targetModuleName, + targetFeatureConfig + ); + + let message = `Attempted to append '${targetModuleName}' module `; + message += `from '${targetModuleConfig?.importPath}' after the `; + message += `'${wrapperOptions.markerModuleName}' from '${markerModuleConfig.importPath}', `; + message += `but could not find '${wrapperOptions.markerModuleName}'.`; + message += `\n`; + message += `Please make sure the '${markerFeature}' is installed by running:\n`; + message += `> ng add @spartacus/schematics --features=${markerFeature}`; + + throw new SchematicsException(message); + } + } + + if (options.debug) { + context.logger.info(`✅ Application analysis complete.`); + } + }; +} + +function markerModuleExists( + options: OPTIONS, + tree: Tree, + markerModuleConfig: Module +): boolean { + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + if (findFeatureModule(markerModuleConfig, appSourceFiles)) { + return true; + } + } + + return false; +} + +/** + * Searches through feature modules, + * and looks for either the static or + * dynamic imports. + */ +export function findFeatureModule( + moduleConfig: Module | Module[], + appSourceFiles: SourceFile[] +): SourceFile | undefined { + const moduleConfigs = ([] as Module[]).concat(moduleConfig); + for (const sourceFile of appSourceFiles) { + for (const moduleConfig of moduleConfigs) { + if (isStaticallyImported(sourceFile, moduleConfig)) { + return sourceFile; + } + + if (isDynamicallyImported(sourceFile, moduleConfig)) { + return sourceFile; + } + } + } + + return undefined; +} + +function isStaticallyImported( + sourceFile: SourceFile, + moduleConfig: Module +): boolean { + if ( + !staticImportExists(sourceFile, moduleConfig.importPath, moduleConfig.name) + ) { + false; + } + + const elements = + getModulePropertyInitializer(sourceFile, 'imports', false)?.getElements() ?? + []; + for (const element of elements) { + const moduleName = element.getText().split('.').pop() ?? ''; + + if (moduleName === moduleConfig.name) { + return true; + } + } + + return false; +} + +function isDynamicallyImported( + sourceFile: SourceFile, + moduleConfig: Module +): boolean { + return !!findDynamicImport(sourceFile, { + moduleSpecifier: moduleConfig.importPath, + namedImports: [moduleConfig.name], + }); +} + +/** + * Peeks into the given dynamic import, + * and returns referenced local source file. + */ +export function getDynamicallyImportedLocalSourceFile( + dynamicImport: ArrowFunction +): SourceFile | undefined { + const importPath = getDynamicImportImportPath(dynamicImport) ?? ''; + if (!isRelative(importPath)) { + return; + } + + const wrapperModuleFileName = `${importPath.split('/').pop()}.ts`; + return dynamicImport + .getSourceFile() + .getProject() + .getSourceFile((s) => s.getFilePath().endsWith(wrapperModuleFileName)); +} + +function createDependentFeaturesLog( + options: SpartacusOptions, + features: string[] +): string | undefined { + const selectedFeatures = options.features ?? []; + const notSelectedFeatures = features.filter( + (feature) => !selectedFeatures.includes(feature) + ); + + if (!notSelectedFeatures.length) { + return; + } + + return `\n⚙️ Configuring the dependent features of ${selectedFeatures.join( + ', ' + )}: ${notSelectedFeatures.join(', ')}\n`; +} diff --git a/projects/schematics/src/shared/utils/feature-utils_spec.ts b/projects/schematics/src/shared/utils/feature-utils_spec.ts new file mode 100644 index 00000000000..cfec5d1b220 --- /dev/null +++ b/projects/schematics/src/shared/utils/feature-utils_spec.ts @@ -0,0 +1,318 @@ +import { Tree } from '@angular-devkit/schematics'; +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import { + Schema as ApplicationOptions, + Style, +} from '@schematics/angular/application/schema'; +import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; +import * as path from 'path'; +import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; +import { UTF_8 } from '../constants'; +import { + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + ORDER_FEATURE_NAME, + SPARTACUS_CHECKOUT, + SPARTACUS_SCHEMATICS, + USER_ACCOUNT_FEATURE_NAME, +} from '../libs-constants'; +import { + addFeatures, + getDynamicallyImportedLocalSourceFile, +} from './feature-utils'; +import { collectDynamicImports } from './import-utils'; +import { LibraryOptions } from './lib-utils'; +import { createProgram } from './program'; +import { getProjectTsConfigPaths } from './project-tsconfig-paths'; +import { + checkoutFeatureModulePath, + checkoutWrapperModulePath, + userFeatureModulePath, +} from './test-utils'; + +describe('Feature utils', () => { + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + path.join(__dirname, '../../collection.json') + ); + + let appTree: Tree; + let buildPath: string; + + const workspaceOptions: WorkspaceOptions = { + name: 'workspace', + version: '0.5.0', + }; + + const appOptions: ApplicationOptions = { + name: 'schematics-test', + inlineStyle: false, + inlineTemplate: false, + routing: false, + style: Style.Scss, + skipTests: false, + projectRoot: '', + }; + + const spartacusDefaultOptions: SpartacusOptions = { + project: 'schematics-test', + lazy: true, + features: [], + }; + + const BASE_OPTIONS: LibraryOptions = { + project: 'schematics-test', + lazy: true, + }; + + beforeEach(async () => { + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'workspace', + workspaceOptions + ) + .toPromise(); + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'application', + appOptions, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'add-spartacus', + { ...spartacusDefaultOptions, name: 'schematics-test' }, + appTree + ) + .toPromise(); + + buildPath = getProjectTsConfigPaths(appTree, BASE_OPTIONS.project) + .buildPaths[0]; + }); + + describe('addFeatures', () => { + it('should generate feature modules for the given array of features', async () => { + appTree = await schematicRunner + .callRule( + addFeatures(BASE_OPTIONS, [USER_ACCOUNT_FEATURE_NAME]), + appTree + ) + .toPromise(); + + expect( + appTree.read(userFeatureModulePath)?.toString(UTF_8) + ).toMatchSnapshot(); + }); + }); + + describe('getDynamicallyImportedLocalSourceFile', () => { + it('should return falsy if not local import', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [USER_ACCOUNT_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + const userFeatureModule = program.getSourceFileOrThrow( + userFeatureModulePath + ); + const dynamicImport = collectDynamicImports(userFeatureModule)[0]; + + const result = getDynamicallyImportedLocalSourceFile(dynamicImport); + expect(result).toBeFalsy(); + }); + + it('should return the locally referenced source file', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_BASE_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_B2B_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const dynamicImport = collectDynamicImports(checkoutFeatureModule)[0]; + + const result = getDynamicallyImportedLocalSourceFile(dynamicImport); + expect(result?.print()).toMatchSnapshot(); + }); + }); + + describe('analyzeApplication', () => { + describe('dependent features check', () => { + it('should throw when a feature is not configured during dirty installation', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_BASE_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + try { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + } catch (e) { + expect(e).toBeTruthy(); + } + }); + + it('should throw when a feature is not configured, but library is present in package.json', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [ORDER_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const packageJson = JSON.parse( + appTree.read('package.json')?.toString(UTF_8) ?? '' + ); + packageJson.dependencies[SPARTACUS_CHECKOUT] = '9.9.9'; + appTree.overwrite('package.json', JSON.stringify(packageJson, null, 2)); + + try { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + } catch (e) { + expect(e).toBeTruthy(); + } + }); + + describe('when the dependent feature is eagerly configured', () => { + it('should succeed', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_BASE_FEATURE_NAME], + lazy: false, + }, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [DIGITAL_PAYMENTS_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram( + appTree, + appTree.root.path, + buildPath + ); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + }); + }); + describe('when the dependent feature is lazily configured', () => { + it('should succeed', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [CHECKOUT_BASE_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...spartacusDefaultOptions, + name: 'schematics-test', + features: [DIGITAL_PAYMENTS_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram( + appTree, + appTree.root.path, + buildPath + ); + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + }); + }); + }); + }); +}); diff --git a/projects/schematics/src/shared/utils/file-utils_spec.ts b/projects/schematics/src/shared/utils/file-utils_spec.ts index b75210ad9d2..25deec046f7 100644 --- a/projects/schematics/src/shared/utils/file-utils_spec.ts +++ b/projects/schematics/src/shared/utils/file-utils_spec.ts @@ -31,7 +31,7 @@ import { USER_ADDRESS_SERVICE, UTF_8, } from '../constants'; -import { SPARTACUS_CORE } from '../libs-constants'; +import { SPARTACUS_CORE, SPARTACUS_SCHEMATICS } from '../libs-constants'; import { addConstructorParam, buildSpartacusComment, @@ -294,7 +294,10 @@ export class ServiceNameService extends LaunchDialogService { }`; const collectionPath = path.join(__dirname, '../../collection.json'); -const schematicRunner = new SchematicTestRunner('schematics', collectionPath); +const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath +); describe('File utils', () => { let appTree: UnitTestTree; diff --git a/projects/schematics/src/shared/utils/graph-utils.ts b/projects/schematics/src/shared/utils/graph-utils.ts index 0651d2396e5..b01372fcab1 100644 --- a/projects/schematics/src/shared/utils/graph-utils.ts +++ b/projects/schematics/src/shared/utils/graph-utils.ts @@ -1,3 +1,11 @@ +import collectedDependencies from '../../dependencies.json'; +import { CORE_SPARTACUS_SCOPES, SPARTACUS_SCOPE } from '../libs-constants'; +import { + getKeyByMappingValueOrThrow, + libraryFeatureMapping, +} from '../schematics-config-mappings'; +import { getConfiguredDependencies } from './schematics-config-utils'; + export class Graph { protected adjacentVertices: Record> = {}; @@ -24,6 +32,50 @@ export class Graph { } } +export const crossLibraryDependencyGraph: Graph = + createLibraryDependencyGraph(); +export const crossLibraryInstallationOrder: string[] = kahnsAlgorithm( + crossLibraryDependencyGraph +); + +export const crossFeatureDependencyGraph: Graph = + createCrossFeaturesDependencyGraph(); +export const crossFeatureInstallationOrder: string[] = groupFeatures(); + +function groupFeatures(): string[] { + const order = kahnsAlgorithm(crossFeatureDependencyGraph); + + const auxOrder: { library: string; feature: string }[] = []; + for (const [index, feature] of Array.from(order.entries())) { + const library = getKeyByMappingValueOrThrow(libraryFeatureMapping, feature); + + const lastExistingIndex = getLastLibraryIndex(auxOrder, library); + if (!lastExistingIndex) { + auxOrder.push({ library, feature }); + continue; + } + + auxOrder.splice(lastExistingIndex + 1, 0, { library, feature }); + order.splice(index); + } + + return auxOrder.map(({ feature }) => feature); +} + +function getLastLibraryIndex( + auxOrder: { library: string; feature: string }[], + library: string +): number | undefined { + let lastIndex: number | undefined; + for (const [index, aux] of Array.from(auxOrder.entries())) { + if (aux.library === library) { + lastIndex = index; + } + } + + return lastIndex; +} + /** * Creates the order in which the Spartacus libraries should be installed. * https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm @@ -74,3 +126,51 @@ export function kahnsAlgorithm(graph: Graph): string[] { return Object.keys(topNums).reverse(); } + +function createLibraryDependencyGraph(): Graph { + const skip = CORE_SPARTACUS_SCOPES.concat( + 'storefrontapp-e2e-cypress', + 'storefrontapp' + ); + + const spartacusLibraries = Object.keys(collectedDependencies).filter( + (dependency) => !skip.includes(dependency) + ); + + const graph = new Graph(spartacusLibraries); + for (const spartacusLib of spartacusLibraries) { + const libraryDependencies = ( + collectedDependencies as Record> + )[spartacusLib]; + const spartacusPeerDependencies = Object.keys(libraryDependencies).filter( + (dependency) => dependency.startsWith(SPARTACUS_SCOPE) + ); + for (const spartacusPackage of spartacusPeerDependencies) { + if (skip.includes(spartacusPackage)) { + continue; + } + + graph.createEdge(spartacusLib, spartacusPackage); + } + } + + return graph; +} + +function createCrossFeaturesDependencyGraph(): Graph { + const graph = new Graph(); + + for (const spartacusLib of Array.from(libraryFeatureMapping.keys())) { + const features = libraryFeatureMapping.get(spartacusLib) ?? []; + for (const feature of features) { + graph.addVertex(feature); + + const dependencies = getConfiguredDependencies(feature); + for (const dependency of dependencies) { + graph.createEdge(feature, dependency); + } + } + } + + return graph; +} diff --git a/projects/schematics/src/shared/utils/graph-utils_spec.ts b/projects/schematics/src/shared/utils/graph-utils_spec.ts index 370abeb2c24..febe0d462ad 100644 --- a/projects/schematics/src/shared/utils/graph-utils_spec.ts +++ b/projects/schematics/src/shared/utils/graph-utils_spec.ts @@ -4,19 +4,29 @@ import { SPARTACUS_ASM, SPARTACUS_CART, SPARTACUS_CDC, + SPARTACUS_CDS, SPARTACUS_CHECKOUT, SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_EPD_VISUALIZATION, SPARTACUS_ORDER, + SPARTACUS_ORGANIZATION, SPARTACUS_PRODUCT, + SPARTACUS_PRODUCT_CONFIGURATOR, SPARTACUS_QUALTRICS, SPARTACUS_SMARTEDIT, SPARTACUS_STOREFINDER, + SPARTACUS_TRACKING, SPARTACUS_USER, } from '../libs-constants'; -import { Graph, kahnsAlgorithm } from './graph-utils'; +import { + crossFeatureInstallationOrder, + crossLibraryInstallationOrder, + Graph, + kahnsAlgorithm, +} from './graph-utils'; describe('Graph utils', () => { - describe('graph', () => { + describe('library dependency graph', () => { it('scenario #1 - should be able to find a correct installation order', () => { const graph = new Graph([ SPARTACUS_DIGITAL_PAYMENTS, @@ -108,5 +118,65 @@ describe('Graph utils', () => { expect(e.message).toEqual('Circular dependency detected.'); } }); + + it('should have generated the correct order', () => { + expect(crossLibraryInstallationOrder).toEqual([ + SPARTACUS_USER, + SPARTACUS_CART, + SPARTACUS_ORDER, + SPARTACUS_CHECKOUT, + SPARTACUS_TRACKING, + SPARTACUS_ASM, + SPARTACUS_EPD_VISUALIZATION, + SPARTACUS_DIGITAL_PAYMENTS, + SPARTACUS_CDS, + SPARTACUS_CDC, + SPARTACUS_STOREFINDER, + SPARTACUS_SMARTEDIT, + SPARTACUS_QUALTRICS, + SPARTACUS_PRODUCT_CONFIGURATOR, + SPARTACUS_PRODUCT, + SPARTACUS_ORGANIZATION, + ]); + }); + }); + + describe('feature dependency graph', () => { + it('should generate the correct installation order', () => { + expect(crossFeatureInstallationOrder).toMatchInlineSnapshot(` + Array [ + "User-Account", + "User-Profile", + "Cart", + "Saved-Cart", + "WishList", + "Quick-Order", + "Import-Export", + "Order", + "Checkout", + "Checkout-B2B", + "Checkout-Scheduled-Replenishment", + "Personalization", + "TMS-AEPL", + "TMS-GTM", + "VC-Configurator", + "CPQ-Configurator", + "Textfield-Configurator", + "EPD-Visualization", + "Digital-Payments", + "CDS", + "CDC", + "Store-Finder", + "SmartEdit", + "Qualtrics", + "Product-Variants", + "Image-Zoom", + "Bulk-Pricing", + "Order-Approval", + "Administration", + "ASM", + ] + `); + }); }); }); diff --git a/projects/schematics/src/shared/utils/import-utils.ts b/projects/schematics/src/shared/utils/import-utils.ts index 1793937adcc..083d6978d01 100644 --- a/projects/schematics/src/shared/utils/import-utils.ts +++ b/projects/schematics/src/shared/utils/import-utils.ts @@ -1,12 +1,40 @@ -import { Identifier, ImportDeclaration, ts } from 'ts-morph'; -import { SPARTACUS_SCOPE } from '../libs-constants'; +import { + ArrowFunction, + CallExpression, + Identifier, + ImportDeclaration, + PropertyAccessExpression, + SourceFile, + ts as tsMorph, +} from 'ts-morph'; +import { CORE_SPARTACUS_SCOPES, SPARTACUS_SCOPE } from '../libs-constants'; +import { getSpartacusProviders } from './config-utils'; +import { Import } from './new-module-utils'; +/** + * Checks if the provided import is a Spartacus library. + */ export function isImportedFromSpartacusLibs( node: Identifier | string ): boolean { return isImportedFrom(node, SPARTACUS_SCOPE); } +/** + * Checks if the provided imports is a core Spartacus library. + */ +export function isImportedFromSpartacusCoreLib( + node: Identifier | string +): boolean { + for (const coreScope of CORE_SPARTACUS_SCOPES) { + if (isImportedFrom(node, coreScope)) { + return true; + } + } + + return false; +} + export function isImportedFrom( node: Identifier | string, toCheck: string @@ -36,7 +64,7 @@ export function getImportDeclaration( const references = node.findReferencesAsNodes(); for (const reference of references) { const importDeclaration = reference?.getFirstAncestorByKind( - ts.SyntaxKind.ImportDeclaration + tsMorph.SyntaxKind.ImportDeclaration ); if (importDeclaration) { return importDeclaration; @@ -45,3 +73,146 @@ export function getImportDeclaration( return undefined; } + +/** + * Collects the higher-order arrow functions. + * E.g. `() => import('@spartacus/cart/base/components/add-to-cart').then((m) => m.AddToCartModule)`, + * but not the inner one `(m) => m.AddToCartModule`. + */ +export function collectDynamicImports(source: SourceFile): ArrowFunction[] { + const providers = getSpartacusProviders(source, false); + + let arrowFunctions: ArrowFunction[] = []; + for (const element of providers) { + const higherArrowFunctions = element + .getDescendantsOfKind(tsMorph.SyntaxKind.ArrowFunction) + .filter((arrowFn) => + arrowFn.getParentIfKind(tsMorph.SyntaxKind.PropertyAssignment) + ); + arrowFunctions = arrowFunctions.concat(higherArrowFunctions); + } + + return arrowFunctions; +} + +/** + * Returns the call expression of the dynamic import (if any). + * E.g. for the given `() => import('@spartacus/cart/base').then((m) => m.CartBaseModule)` it returns `import('@spartacus/cart/base')` + */ +export function getDynamicImportCallExpression( + arrowFunction: ArrowFunction +): CallExpression | undefined { + return arrowFunction + .getFirstDescendantByKind(tsMorph.SyntaxKind.ImportKeyword) + ?.getParentIfKind(tsMorph.SyntaxKind.CallExpression); +} + +/** + * Returns the import path, e.g. @spartacus/cart/base + */ +export function getDynamicImportImportPath( + arrowFunction: ArrowFunction +): string | undefined { + return getDynamicImportCallExpression(arrowFunction) + ?.getFirstDescendantByKind(tsMorph.SyntaxKind.StringLiteral) + ?.getLiteralValue(); +} + +/** + * Returns the import module of the dynamic import (if any). + * E.g. for the given `() => import('@spartacus/cart/base').then((m) => m.CartBaseModule)` it returns `m.CartBaseModule` + */ +export function getDynamicImportPropertyAccess( + arrowFunction: ArrowFunction +): PropertyAccessExpression | undefined { + return arrowFunction + .getFirstDescendantByKind(tsMorph.SyntaxKind.ArrowFunction) + ?.getFirstDescendantByKind(tsMorph.SyntaxKind.PropertyAccessExpression); +} + +/** + * Creates the import statement in the given source file. + */ +export function createImports( + sourceFile: SourceFile, + imports: Import | Import[] +): ImportDeclaration[] { + const importDeclarations: ImportDeclaration[] = []; + ([] as Import[]).concat(imports).forEach((specifiedImport) => { + const importDeclaration = sourceFile.addImportDeclaration({ + moduleSpecifier: specifiedImport.moduleSpecifier, + namedImports: specifiedImport.namedImports, + }); + importDeclarations.push(importDeclaration); + }); + + return importDeclarations; +} + +/** + * Searches through the given module's imports + * for the given import path and import name. + */ +export function staticImportExists( + sourceFile: SourceFile, + importPathToFind: string, + moduleNameToFind: string +): boolean { + const importDeclarations = sourceFile.getImportDeclarations(); + for (const importDeclaration of importDeclarations) { + const importPath = importDeclaration.getModuleSpecifierValue(); + if (importPathToFind === importPath) { + const namedImports = + importDeclaration.getImportClause()?.getNamedImports() ?? []; + for (const namedImport of namedImports) { + if (namedImport.getName() === moduleNameToFind) { + return true; + } + } + } + } + + return false; +} + +/** + * Returns true if the given path is relative + */ +export function isRelative(path: string): boolean { + return path.startsWith('./') || path.startsWith('../'); +} + +/** + * Analyzes the dynamic imports of the given module. + * If both dynamic import's import path and module name + * are found in the given config, it returns it. + */ +export function findDynamicImport( + sourceFile: SourceFile, + importToFind: Import +): ArrowFunction | undefined { + const collectedDynamicImports = collectDynamicImports(sourceFile); + + for (const dynamicImport of collectedDynamicImports) { + const importPath = getDynamicImportImportPath(dynamicImport) ?? ''; + if (isRelative(importPath)) { + if (!importPath.includes(importToFind.moduleSpecifier)) { + continue; + } + } else { + if (importPath !== importToFind.moduleSpecifier) { + continue; + } + } + + const importModule = + getDynamicImportPropertyAccess(dynamicImport) + ?.getLastChildByKind(tsMorph.SyntaxKind.Identifier) + ?.getText() ?? ''; + if (importToFind.namedImports.includes(importModule)) { + return dynamicImport; + } + } + + return undefined; +} diff --git a/projects/schematics/src/shared/utils/import-utils_spec.ts b/projects/schematics/src/shared/utils/import-utils_spec.ts new file mode 100644 index 00000000000..14eb0dc0f29 --- /dev/null +++ b/projects/schematics/src/shared/utils/import-utils_spec.ts @@ -0,0 +1,275 @@ +import { Tree } from '@angular-devkit/schematics'; +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import { + Schema as ApplicationOptions, + Style, +} from '@schematics/angular/application/schema'; +import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; +import * as path from 'path'; +import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; +import { NGRX_STORE } from '../constants'; +import { CART_BASE_MODULE } from '../lib-configs/cart-schematics-config'; +import { + CART_BASE_FEATURE_NAME, + SPARTACUS_CART_BASE, + SPARTACUS_CHECKOUT, + SPARTACUS_CORE, + SPARTACUS_SCHEMATICS, + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { addFeatures } from './feature-utils'; +import { + collectDynamicImports, + createImports, + findDynamicImport, + getDynamicImportCallExpression, + getDynamicImportImportPath, + getDynamicImportPropertyAccess, + isImportedFromSpartacusCoreLib, + isImportedFromSpartacusLibs, + isRelative, + staticImportExists, +} from './import-utils'; +import { LibraryOptions } from './lib-utils'; +import { createProgram } from './program'; +import { getProjectTsConfigPaths } from './project-tsconfig-paths'; +import { cartBaseFeatureModulePath } from './test-utils'; + +describe('Import utils', () => { + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + path.join(__dirname, '../../collection.json') + ); + + let tree: Tree; + let buildPath: string; + + const workspaceOptions: WorkspaceOptions = { + name: 'workspace', + version: '0.5.0', + }; + + const appOptions: ApplicationOptions = { + name: 'schematics-test', + inlineStyle: false, + inlineTemplate: false, + routing: false, + style: Style.Scss, + skipTests: false, + projectRoot: '', + }; + + const spartacusDefaultOptions: SpartacusOptions = { + project: 'schematics-test', + lazy: true, + features: [], + }; + + const BASE_OPTIONS: LibraryOptions = { + project: 'schematics-test', + lazy: true, + }; + + beforeEach(async () => { + tree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'workspace', + workspaceOptions + ) + .toPromise(); + tree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'application', + appOptions, + tree + ) + .toPromise(); + tree = await schematicRunner + .runSchematicAsync( + 'add-spartacus', + { ...spartacusDefaultOptions, name: 'schematics-test' }, + tree + ) + .toPromise(); + + buildPath = getProjectTsConfigPaths(tree, BASE_OPTIONS.project) + .buildPaths[0]; + + tree = await schematicRunner + .callRule( + addFeatures(BASE_OPTIONS, [ + USER_ACCOUNT_FEATURE_NAME, + USER_PROFILE_FEATURE_NAME, + CART_BASE_FEATURE_NAME, + ]), + tree + ) + .toPromise(); + }); + + describe('isImportedFromSpartacusLibs', () => { + it('should return true if the provided lib is a spartacus lib', () => { + expect(isImportedFromSpartacusLibs(SPARTACUS_CHECKOUT)).toBeTruthy(); + }); + it('should return false if the provided lib is NOT a spartacus lib', () => { + expect(isImportedFromSpartacusLibs('xxx')).toBeFalsy(); + }); + }); + + describe('isImportedFromSpartacusCoreLib', () => { + it('should return true if the provided lib is a core spartacus lib', () => { + expect(isImportedFromSpartacusCoreLib(SPARTACUS_CORE)).toBeTruthy(); + }); + it('should return false if the provided lib is NOT a core spartacus lib', () => { + expect(isImportedFromSpartacusCoreLib(SPARTACUS_CHECKOUT)).toBeFalsy(); + }); + }); + + describe('collectDynamicImports', () => { + it('should collect all dynamic imports', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const result = collectDynamicImports(cartFeatureModule); + expect(result.length).toBe(3); + expect(result[0].print()).toEqual( + `() => import('@spartacus/cart/base').then((m) => m.CartBaseModule)` + ); + expect(result[1].print()).toEqual( + `() => import('@spartacus/cart/base/components/mini-cart').then((m) => m.MiniCartModule)` + ); + expect(result[2].print()).toEqual( + `() => import('@spartacus/cart/base/components/add-to-cart').then((m) => m.AddToCartModule)` + ); + }); + }); + + describe('getDynamicImportCallExpression', () => { + it('should return the import paths', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const dynamicImports = collectDynamicImports(cartFeatureModule); + const result = getDynamicImportCallExpression(dynamicImports[0]); + expect(result?.print()).toEqual(`import('@spartacus/cart/base')`); + }); + }); + + describe('getDynamicImportImportPath', () => { + it('should return the import path', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const dynamicImports = collectDynamicImports(cartFeatureModule); + const result = getDynamicImportImportPath(dynamicImports[0]); + expect(result).toEqual(`@spartacus/cart/base`); + }); + }); + + describe('getDynamicImportPropertyAccess', () => { + it('should return the module name', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const dynamicImports = collectDynamicImports(cartFeatureModule); + const result = getDynamicImportPropertyAccess(dynamicImports[0]); + expect(result?.print()).toEqual(`m.CartBaseModule`); + }); + }); + + describe('createImports', () => { + it('should create the specified import', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const results = createImports(cartFeatureModule, { + moduleSpecifier: SPARTACUS_CORE, + namedImports: ['xxx'], + }); + expect(results[0].print()).toEqual( + `import { xxx } from "@spartacus/core";` + ); + }); + }); + + describe('importExists', () => { + it('should create the specified import', async () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const result = staticImportExists( + cartFeatureModule, + SPARTACUS_CORE, + 'I18nConfig' + ); + expect(result).toBeTruthy(); + }); + }); + + describe('isRelative', () => { + it('should return true if starts with ./', async () => { + const path = './something'; + const result = isRelative(path); + expect(result).toBeTruthy(); + }); + it('should return true if starts with ../', async () => { + const path = '../../something'; + const result = isRelative(path); + expect(result).toBeTruthy(); + }); + it('should return false if NOT relative', async () => { + const path = 'something'; + const result = isRelative(path); + expect(result).toBeFalsy(); + }); + }); + + describe('findDynamicImport', () => { + it('should be able to find it', () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const result = findDynamicImport(cartFeatureModule, { + moduleSpecifier: SPARTACUS_CART_BASE, + namedImports: [CART_BASE_MODULE], + }); + expect(result?.print()).toMatchInlineSnapshot( + `"() => import('@spartacus/cart/base').then((m) => m.CartBaseModule)"` + ); + }); + it('should return undefined if the import can not be found', () => { + const { program } = createProgram(tree, tree.root.path, buildPath); + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + + const result = findDynamicImport(cartFeatureModule, { + moduleSpecifier: NGRX_STORE, + namedImports: [CART_BASE_MODULE], + }); + expect(result).toBeFalsy(); + }); + }); +}); diff --git a/projects/schematics/src/shared/utils/index.ts b/projects/schematics/src/shared/utils/index.ts index 4a8b4cc82c5..b35c37c5233 100644 --- a/projects/schematics/src/shared/utils/index.ts +++ b/projects/schematics/src/shared/utils/index.ts @@ -1,9 +1,16 @@ export * from './config-utils'; +export * from './dependency-utils'; +export * from './feature-utils'; export * from './file-utils'; +export * from './graph-utils'; +export * from './import-utils'; export * from './lib-utils'; +export * from './logger-utils'; export * from './module-file-utils'; export * from './new-module-utils'; export * from './package-utils'; export * from './program'; export * from './project-tsconfig-paths'; +export * from './schematics-config-utils'; +export * from './test-utils'; export * from './workspace-utils'; diff --git a/projects/schematics/src/shared/utils/lib-utils.ts b/projects/schematics/src/shared/utils/lib-utils.ts index 10147b9e6a1..9e019289088 100644 --- a/projects/schematics/src/shared/utils/lib-utils.ts +++ b/projects/schematics/src/shared/utils/lib-utils.ts @@ -2,7 +2,6 @@ import { dasherize } from '@angular-devkit/core/src/utils/strings'; import { chain, ExecutionOptions, - externalSchematic, noop, Rule, SchematicContext, @@ -10,44 +9,46 @@ import { TaskId, Tree, } from '@angular-devkit/schematics'; -import { - NodePackageInstallTask, - RunSchematicTask, -} from '@angular-devkit/schematics/tasks'; -import { RunSchematicTaskOptions } from '@angular-devkit/schematics/tasks/run-schematic/options'; +import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { addPackageJsonDependency, NodeDependency, NodeDependencyType, } from '@schematics/angular/utility/dependencies'; -import { CallExpression, Node, SourceFile, ts as tsMorph } from 'ts-morph'; -import collectedDependencies from '../../dependencies.json'; import { - ANGULAR_CORE, CMS_CONFIG, I18N_CONFIG, PROVIDE_CONFIG_FUNCTION, UTF_8, } from '../constants'; import { - CORE_SPARTACUS_SCOPES, SPARTACUS_CONFIGURATION_MODULE, SPARTACUS_CORE, SPARTACUS_FEATURES_MODULE, SPARTACUS_FEATURES_NG_MODULE, - SPARTACUS_SCOPE, SPARTACUS_SETUP, } from '../libs-constants'; -import { packageSubFeaturesMapping } from '../updateable-constants'; import { getB2bConfiguration } from './config-utils'; -import { Graph, kahnsAlgorithm } from './graph-utils'; -import { isImportedFrom } from './import-utils'; +import { + AdditionalFeatureConfiguration, + AdditionalProviders, + findFeatureModule, + getSpartacusFeaturesModule, +} from './feature-utils'; +import { + crossFeatureInstallationOrder, + crossLibraryInstallationOrder, +} from './graph-utils'; +import { createImports } from './import-utils'; +import { + debugLogRule, + formatFeatureComplete, + formatFeatureStart, +} from './logger-utils'; import { addModuleImport, addModuleProvider, - collectInstalledModules, ensureModuleExists, - getModulePropertyInitializer, Import, } from './new-module-utils'; import { @@ -73,6 +74,16 @@ export interface LibraryOptions extends Partial { * When enabled, prints the additional logs. */ debug?: boolean; + /** + * Internal options. + * Should not be set by the user. + */ + internal?: { + /** + * If Spartacus is already installed in the app. + */ + dirtyInstallation?: boolean; + }; /** * Meta. * Populated when programmatically invoking @@ -82,7 +93,28 @@ export interface LibraryOptions extends Partial { options?: LibraryOptions; } -export interface FeatureConfig { +export interface SchematicConfig { + /** + * Library options + */ + library: { + /** + * The feature name, e.g. CHECKOUT_BASE_FEATURE + */ + featureName: string; + /** + * Spartacus library scope, e.g. `@spartacus/checkout` + */ + mainScope: string; + /** + * E.g. `@spartacus/checkout/base/b2b` + */ + featureScope?: string; + /** + * If the feature is a b2b feature, it will provide the b2b configuration. + */ + b2b?: boolean; + }; /** * The folder in which we will generate the feature module. E.g. app/spartacus/features/__organization__ (__NOTE__: just the `organization` part should be provided.). */ @@ -118,44 +150,38 @@ export interface FeatureConfig { */ assets?: AssetsConfig; /** - * An optional custom configuration to provide to the generated module. - */ - customConfig?: CustomConfig | CustomConfig[]; - /** - * Configure it if a feature requires another feature - * to be configured before it. + * A function returning the custom configuration. */ - dependencyManagement?: DependencyManagement; - /** - * If set to true, instead of appending the configuration to the existing module, - * it will recreate the feature module with the new configuration. - */ - recreate?: boolean; -} - -/** - * Dependency management for the library - */ -export interface DependencyManagement { - /** - * The name of the feature that's currently being installed. - */ - featureName: string; + customConfig?: ( + options: OPTIONS + ) => AdditionalFeatureConfiguration; /** * Contains the feature dependencies. * The key is a Spartacus scope, while the value is an array of its features. */ - featureDependencies: Record; -} - -export interface CustomConfig { - import: Import[]; - content: string; + dependencyFeatures?: Record; + /** + * Configuration for generating the wrapper modules. + * + * The key is the feature module name for which to search for + * in either the feature module or the wrapper module. + * + * The value is the feature module name which should be added + * to the wrapper module. + */ + wrappers?: Record; } export interface Module { + /** Module name */ name: string; + /** Module import path */ importPath: string; + /** + * The content to specify in ng-module's imports, + * e.g. `AuthModule.forRoot()`. If not specified, + * the `name` is used. + */ content?: string; } @@ -176,9 +202,6 @@ export interface AssetsConfig { glob: string; } -export const dependencyGraph: Graph = createLibraryDependencyGraph(); -export const installationOrder: string[] = kahnsAlgorithm(dependencyGraph); - export function shouldAddFeature( feature: string, features: string[] = [] @@ -186,79 +209,9 @@ export function shouldAddFeature( return features.includes(feature); } -export function prepareCliPackageAndSubFeature( - features: string[] -): Record { - return features.reduce((cliFeatures, subFeature) => { - const packageName = getPackageBySubFeature(subFeature); - const subFeatures = [...(cliFeatures[packageName] ?? []), subFeature]; - - return { ...cliFeatures, [packageName]: subFeatures }; - }, {} as Record); -} - -export function getPackageBySubFeature(subFeature: string): string { - for (const spartacusPackage in packageSubFeaturesMapping) { - if (!packageSubFeaturesMapping.hasOwnProperty(spartacusPackage)) { - continue; - } - - const subFeatures = packageSubFeaturesMapping[spartacusPackage]; - if (subFeatures.includes(subFeature)) { - return spartacusPackage; - } - } - - throw new SchematicsException( - `The given '${subFeature}' doesn't contain a Spartacus package mapping. -Please check 'packageSubFeaturesMapping' in 'projects/schematics/src/shared/updateable-constants.ts'` - ); -} - -function createLibraryDependencyGraph(): Graph { - const skip = CORE_SPARTACUS_SCOPES.concat( - 'storefrontapp-e2e-cypress', - 'storefrontapp' - ); - return createDependencyGraph(collectedDependencies, skip); -} - -function createDependencyGraph( - dependencies: Record>, - skip: string[] = [] -): Graph { - const spartacusLibraries = Object.keys(dependencies).filter( - (dependency) => !skip.includes(dependency) - ); - - const graph = new Graph(spartacusLibraries); - for (const spartacusLib of spartacusLibraries) { - const spartacusPeerDependencies = getSpartacusLibraries( - dependencies[spartacusLib] - ); - for (const spartacusPackage of spartacusPeerDependencies) { - if (skip.includes(spartacusPackage)) { - continue; - } - - graph.createEdge(spartacusLib, spartacusPackage); - } - } - - return graph; -} - -export function getSpartacusLibraries( - dependencies: Record -): string[] { - return Object.keys(dependencies).filter((dependency) => - dependency.startsWith(SPARTACUS_SCOPE) - ); -} - export function addLibraryFeature( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { return (tree: Tree, context: SchematicContext) => { const spartacusFeatureModuleExistsInApp = checkAppStructure( @@ -271,17 +224,23 @@ export function addLibraryFeature( 'Please migrate manually the rest of your feature modules to the new app structure: https://sap.github.io/spartacus-docs/reference-app-structure/' ); } + return chain([ + debugLogRule( + formatFeatureStart(config.library.featureName, `adding...`), + options.debug + ), + spartacusFeatureModuleExistsInApp ? noop() : scaffoldStructure(options), handleFeature(options, config), config.styles ? addLibraryStyles(config.styles, options) : noop(), config.assets ? addLibraryAssets(config.assets, options) : noop(), - config.dependencyManagement - ? installRequiredSpartacusFeatures(config.dependencyManagement, options) - : noop(), - orderInstalledFeatures(options), + debugLogRule( + formatFeatureComplete(config.library.featureName, `added.`), + options.debug + ), ]); }; } @@ -304,84 +263,17 @@ export function checkAppStructure(tree: Tree, project: string): boolean { return false; } -/** - * If exists, it returns the spartacus-features.module.ts' source. - * Otherwise, it returns undefined. - */ -function getSpartacusFeaturesModule( - tree: Tree, - basePath: string, - tsconfigPath: string -): SourceFile | undefined { - const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); - - for (const sourceFile of appSourceFiles) { - if ( - sourceFile - .getFilePath() - .includes(`${SPARTACUS_FEATURES_MODULE}.module.ts`) - ) { - if (getSpartacusFeaturesNgModuleDecorator(sourceFile)) { - return sourceFile; - } - } - } - return undefined; -} - -/** - * Returns the NgModule decorator, if exists. - */ -function getSpartacusFeaturesNgModuleDecorator( - sourceFile: SourceFile -): CallExpression | undefined { - let spartacusFeaturesModule: CallExpression | undefined; - - function visitor(node: Node) { - if (Node.isCallExpression(node)) { - const expression = node.getExpression(); - if ( - Node.isIdentifier(expression) && - expression.getText() === 'NgModule' && - isImportedFrom(expression, ANGULAR_CORE) - ) { - const classDeclaration = node.getFirstAncestorByKind( - tsMorph.SyntaxKind.ClassDeclaration - ); - if (classDeclaration) { - const identifier = classDeclaration.getNameNode(); - if ( - identifier && - identifier.getText() === SPARTACUS_FEATURES_NG_MODULE - ) { - spartacusFeaturesModule = node; - } - } - } - } - - node.forEachChild(visitor); - } - - sourceFile.forEachChild(visitor); - return spartacusFeaturesModule; -} - function handleFeature( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { - return (_tree: Tree, _context: SchematicContext) => { + return (tree: Tree, _context: SchematicContext) => { const rules: Rule[] = []; - if (config.recreate) { - rules.push(deleteFeatureModuleFile(options, config)); - } - rules.push( ensureModuleExists({ - name: `${dasherize(config.moduleName)}-feature`, - path: `app/spartacus/features/${config.folderName}`, + name: createSpartacusFeatureFileName(config.moduleName), + path: createSpartacusFeatureFolderPath(config.folderName), module: SPARTACUS_FEATURES_MODULE, project: options.project, }) @@ -390,40 +282,30 @@ function handleFeature( rules.push(addFeatureModule(options, config)); rules.push(addFeatureTranslations(options, config)); rules.push(addCustomConfig(options, config)); + if (config.library.b2b) { + rules.push(configureB2bFeatures(options, readPackageJson(tree))); + } return chain(rules); }; } -function deleteFeatureModuleFile( - options: T, - config: FeatureConfig -): Rule { - return (tree: Tree): Tree => { - const basePath = process.cwd(); - - const { buildPaths } = getProjectTsConfigPaths(tree, options.project); - for (const tsconfigPath of buildPaths) { - const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); - - const moduleFileName = createModuleFileName(config); - for (const sourceFile of appSourceFiles) { - if (!sourceFile.getFilePath().endsWith('/' + moduleFileName)) { - continue; - } +export function createSpartacusFeatureFolderPath(folderName: string): string { + return `app/spartacus/features/${dasherize(folderName)}`; +} - sourceFile.deleteImmediatelySync(); - break; - } - } +export function createSpartacusFeatureFileName(name: string): string { + return `${dasherize(name)}-feature`; +} - return tree; - }; +export function createSpartacusWrapperModuleFileName(name: string): string { + const normalizedName = name.replace('module', '').replace('Module', ''); + return `${dasherize(normalizedName)}-wrapper`; } function addRootModule( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { return (tree: Tree): Tree => { if (!config.rootModule) { @@ -461,9 +343,9 @@ function addRootModule( function addFeatureModule( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { - return (tree: Tree): Tree => { + return (tree: Tree) => { const basePath = process.cwd(); const moduleFileName = createModuleFileName(config); @@ -477,37 +359,41 @@ function addFeatureModule( } const configFeatures = ([] as Module[]).concat(config.featureModule); + for (let i = 0; i < configFeatures.length; i++) { + const featureModule = configFeatures[i]; + + // if it's already in a wrapper module + if (findFeatureModule(featureModule, appSourceFiles)) { + break; + } - if (options.lazy) { let content = `${PROVIDE_CONFIG_FUNCTION}(<${CMS_CONFIG}>{ featureModules: {`; - for (let i = 0; i < configFeatures.length; i++) { - const featureModule = configFeatures[i]; + if (options.lazy) { let lazyLoadingChunkName = config.moduleName; if (config.lazyLoadingChunk) { const namedImportsContent = config.lazyLoadingChunk.namedImports[i]; lazyLoadingChunkName = `[${namedImportsContent}]`; - sourceFile.addImportDeclaration(config.lazyLoadingChunk); + createImports(sourceFile, config.lazyLoadingChunk); } content = content + `${lazyLoadingChunkName}: { - module: () => - import('${featureModule.importPath}').then((m) => m.${featureModule.name}), - },`; - } - addModuleProvider(sourceFile, { - import: [ - { - moduleSpecifier: SPARTACUS_CORE, - namedImports: [PROVIDE_CONFIG_FUNCTION, CMS_CONFIG], - }, - ], - content: content + `}})`, - }); - } else { - for (let featureModule of configFeatures) { + module: () => + import('${featureModule.importPath}').then((m) => m.${featureModule.name}), + },`; + + addModuleProvider(sourceFile, { + import: [ + { + moduleSpecifier: SPARTACUS_CORE, + namedImports: [PROVIDE_CONFIG_FUNCTION, CMS_CONFIG], + }, + ], + content: content + `}})`, + }); + } else { addModuleImport(sourceFile, { import: { moduleSpecifier: featureModule.importPath, @@ -528,7 +414,7 @@ function addFeatureModule( export function addFeatureTranslations( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { return (tree: Tree): Tree => { if (!config.i18n) { @@ -576,7 +462,7 @@ export function addFeatureTranslations( function addCustomConfig( options: T, - config: FeatureConfig + config: SchematicConfig ): Rule { return (tree: Tree): Tree => { if (!config.customConfig) { @@ -595,8 +481,8 @@ function addCustomConfig( continue; } - const customConfigs = ([] as CustomConfig[]).concat( - config.customConfig + const customConfigs = ([] as AdditionalProviders[]).concat( + config.customConfig(options).providers ?? [] ); customConfigs.forEach((customConfig) => { addModuleProvider(sourceFile, { @@ -819,34 +705,18 @@ export function addPackageJsonDependencies( }; } +/** + * Adds libraries dependencies to package.json + */ export function addPackageJsonDependenciesForLibrary< OPTIONS extends LibraryOptions ->(dependencies: Record, options: OPTIONS): Rule { - return (tree: Tree, context: SchematicContext): Rule => { +>(dependencies: Record, _options: OPTIONS): Rule { + return (tree: Tree, _context: SchematicContext): Rule => { const packageJson = readPackageJson(tree); const spartacusLibraries = createSpartacusDependencies(dependencies); const thirdPartyLibraries = createDependencies(dependencies); const libraries = spartacusLibraries.concat(thirdPartyLibraries); - const cliFeatures = spartacusLibraries - .map((dependency) => dependency.name) - .reduce((previous, current) => { - return { - ...previous, - /** - * Just install the Spartacus library, - * but don't configure any sub-features - */ - [current]: [], - }; - }, {} as Record); - const featureOptions = createSpartacusFeatureOptionsForLibrary( - options, - cliFeatures, - false - ); - addSchematicsTasks(featureOptions, context); - return chain([ addPackageJsonDependencies(libraries, packageJson), installPackageJsonDependencies(), @@ -854,41 +724,6 @@ export function addPackageJsonDependenciesForLibrary< }; } -function installRequiredSpartacusFeatures( - dependencyManagement: DependencyManagement, - options: OPTIONS -): Rule { - return (_tree: Tree, context: SchematicContext): void => { - if (!dependencyManagement) { - return; - } - - logFeatureInstallation(dependencyManagement, context); - const featureOptions = createSpartacusFeatureOptionsForLibrary( - options, - dependencyManagement.featureDependencies - ); - addSchematicsTasks(featureOptions, context); - }; -} - -function logFeatureInstallation( - dependencyManagement: DependencyManagement, - context: SchematicContext -): void { - const cliFeatures = dependencyManagement.featureDependencies; - for (const spartacusScope in cliFeatures) { - if (!cliFeatures.hasOwnProperty(spartacusScope)) { - continue; - } - - const requiredFeatures = cliFeatures[spartacusScope].join(','); - context.logger.info( - `⚙️ ${dependencyManagement.featureName} requires the following features from ${spartacusScope}: ${requiredFeatures}` - ); - } -} - export function dependencyExists( dependency: NodeDependency, packageJson: any @@ -953,166 +788,70 @@ function addB2bProviders(options: T): Rule { }; } -/** - * A helper method that creates the default options for the given Spartacus' libraries. - * - * All `features` options will be set to an empty array, meaning that no features should be installed. - * - * @param spartacusLibraries - * @param options - * @returns - */ -export function createSpartacusFeatureOptionsForLibrary< - OPTIONS extends LibraryOptions ->( - options: OPTIONS, - cliFeatures: Record, - interactive = true -): { - feature: string; - options: LibraryOptions; -}[] { - return Object.keys(cliFeatures).map((spartacusLibrary) => ({ - feature: spartacusLibrary, - options: { - ...options, - // an empty array means that no library features will be installed. - features: cliFeatures[spartacusLibrary] ?? [], - interactive, - }, - })); -} - -export function addSchematicsTasks( - featureOptions: { - feature: string; - options: LibraryOptions; - }[], - context: SchematicContext -): void { - const installationTaskId = createNodePackageInstallationTask(context); - - featureOptions.forEach((featureOption) => { - const runSchematicTaskOptions: RunSchematicTaskOptions = { - collection: featureOption.feature, - name: 'add', - options: featureOption.options, - }; - - context.addTask( - new RunSchematicTask('add-spartacus-library', runSchematicTaskOptions), - [installationTaskId] - ); - }); +function createModuleFileName(config: SchematicConfig): string { + return `${dasherize(config.moduleName)}-feature.module.ts`; } -export function runExternalSpartacusLibrary( - taskOptions: RunSchematicTaskOptions -): Rule { - return (tree: Tree, context: SchematicContext) => { - if (!taskOptions.collection) { - throw new SchematicsException( - `Can't run the Spartacus library schematic, please specify the 'collection' argument.` - ); - } - - const executionOptions: Partial = { - interactive: taskOptions.options.interactive, - }; - - return chain([ - externalSchematic( - taskOptions.collection, - taskOptions.name, - taskOptions.options, - executionOptions - ), - ])(tree, context); - }; +/** + * Used a comparator function when sorting features. + */ +export function calculateCrossFeatureSort( + featureA: string, + featureB: string +): number { + return calculateSortInternal( + featureA, + featureB, + crossFeatureInstallationOrder + ); } -function createModuleFileName(config: FeatureConfig): string { - return `${dasherize(config.moduleName)}-feature.module.ts`; +/** + * Used a comparator function when sorting libraries. + */ +export function calculateCrossLibrarySort( + libraryA: string, + libraryB: string +): number { + return calculateSortInternal( + libraryA, + libraryB, + crossLibraryInstallationOrder + ); } /** - * Used to sort the features in the correct order. + * Used to sort libraries or features in the correct order. */ -function calculateSort(libraryA: string, libraryB: string): number { - const indexA = installationOrder.indexOf(libraryA); - const indexB = installationOrder.indexOf(libraryB); +function calculateSortInternal( + libOrFeatureA: string, + libOrFeatureB: string, + order: string[] +): number { + const indexA = order.indexOf(libOrFeatureA); + const indexB = order.indexOf(libOrFeatureB); /** - * In case a feature module is _not_ found in the `installationOrder`, + * In case a feature module is _not_ found in the `order`, * we want to sort it at the end of the list. */ return (indexA > -1 ? indexA : Infinity) - (indexB > -1 ? indexB : Infinity); } -export function orderInstalledFeatures( - options: T +/** + * Performs the final steps of the installation, + * before Angular schematics mechanism takes over. + */ +export function finalizeInstallation( + options: OPTIONS, + features: string[] ): Rule { - return (tree: Tree, context: SchematicContext): void => { - let message = `Ordering the installed Spartacus features...`; - if (options.debug) { - message = `Sorting the installed Spartacus features according to the dependency graph: ${installationOrder.join( - ', ' - )}`; - } - context.logger.info(message); - - const basePath = process.cwd(); - const { buildPaths } = getProjectTsConfigPaths(tree, options.project); - for (const tsconfigPath of buildPaths) { - const spartacusFeaturesModule = getSpartacusFeaturesModule( - tree, - basePath, - tsconfigPath - ); - if (!spartacusFeaturesModule) { - continue; - } - - const collectedModules = collectInstalledModules(spartacusFeaturesModule); - if (!collectedModules) { - continue; - } - - const spartacusCoreModules = collectedModules.spartacusCoreModules.map( - (spartacusCoreModule) => spartacusCoreModule.getText() - ); - const featureModules = collectedModules.featureModules - .sort((moduleA, moduleB) => - calculateSort(moduleA.spartacusLibrary, moduleB.spartacusLibrary) - ) - .map((featureModule) => featureModule.moduleNode.getText()); - const unrecognizedModules = collectedModules.unrecognizedModules.map( - (unrecognizedModule) => unrecognizedModule.getText() - ); - - const moduleImportsProperty = getModulePropertyInitializer( - spartacusFeaturesModule, - 'imports', - false - ); - if (!moduleImportsProperty) { - continue; - } - - if (collectedModules.warnings.length) { - context.logger.warn( - 'The following modules were not recognized due to various reasons:' - ); - for (const warning of collectedModules.warnings) { - context.logger.warn(warning); - } - } - - const orderedModules: string[] = spartacusCoreModules - .concat(featureModules) - .concat(unrecognizedModules); - moduleImportsProperty.replaceWithText(`[${orderedModules.join(',\n')}]`); - saveAndFormat(spartacusFeaturesModule); + return (_tree: Tree, context: SchematicContext) => { + if (options.internal?.dirtyInstallation) { + let message = `🚨 Detected Spartacus installation. Please make sure the following `; + message += `features are installed, configured and sorted in the correct order:\n`; + message += features.join(', '); + context.logger.warn(message); } }; } diff --git a/projects/schematics/src/shared/utils/lib-utils_spec.ts b/projects/schematics/src/shared/utils/lib-utils_spec.ts index 857d70886c0..c85088e8d60 100644 --- a/projects/schematics/src/shared/utils/lib-utils_spec.ts +++ b/projects/schematics/src/shared/utils/lib-utils_spec.ts @@ -1,9 +1,5 @@ import { Tree } from '@angular-devkit/schematics'; -import { RunSchematicTask } from '@angular-devkit/schematics/tasks'; -import { - SchematicTestRunner, - UnitTestTree, -} from '@angular-devkit/schematics/testing'; +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { Schema as ApplicationOptions, Style, @@ -12,18 +8,21 @@ import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema import * as path from 'path'; import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; import { CDS_CONFIG, UTF_8 } from '../constants'; -import { SPARTACUS_CDS, SPARTACUS_FEATURES_MODULE } from '../libs-constants'; +import { + SPARTACUS_CART, + SPARTACUS_CDS, + SPARTACUS_CHECKOUT, + SPARTACUS_ORDER, +} from '../libs-constants'; import { addLibraryFeature, addPackageJsonDependenciesForLibrary, - FeatureConfig, LibraryOptions, - orderInstalledFeatures, + SchematicConfig, shouldAddFeature, } from './lib-utils'; const xxxFeaturePath = `src/app/spartacus/features/xxx/xxx-feature.module.ts`; -const spartacusFeaturesPath = `src/app/spartacus/${SPARTACUS_FEATURES_MODULE}.module.ts`; describe('Lib utils', () => { const schematicRunner = new SchematicTestRunner( @@ -31,7 +30,7 @@ describe('Lib utils', () => { path.join(__dirname, '../../collection.json') ); - let appTree: UnitTestTree; + let appTree: Tree; const workspaceOptions: WorkspaceOptions = { name: 'workspace', @@ -69,7 +68,11 @@ describe('Lib utils', () => { const scssFilePath = `src/styles/spartacus/${SCSS_FILE_NAME}`; - const BASE_FEATURE_CONFIG: FeatureConfig = { + const BASE_FEATURE_CONFIG: SchematicConfig = { + library: { + featureName: CLI_FEATURE_NAME, + mainScope: FEATURE_MODULE_IMPORT_PATH, + }, folderName: FEATURE_FOLDER_NAME, moduleName: FEATURE_NAME, featureModule: { @@ -97,30 +100,21 @@ describe('Lib utils', () => { lazy: true, }; - const DP_FEATURE_CONFIG: FeatureConfig = { - folderName: 'dp', - moduleName: 'DigitalPayments', - featureModule: { - name: 'DigitalPaymentsModule', - importPath: '@spartacus/digital-payments', + const CHECKOUT_FEATURE_CONFIG: SchematicConfig = { + library: { + featureName: 'checkout', + mainScope: '@spartacus/checkout', + featureScope: '@spartacus/checkout/base', }, - }; - const DP_OPTIONS: LibraryOptions = { - project: 'schematics-test', - features: ['dp-cli'], - lazy: true, - }; - - const CHECKOUT_FEATURE_CONFIG: FeatureConfig = { folderName: 'checkout', moduleName: 'Checkout', featureModule: { name: 'CheckoutModule', - importPath: '@spartacus/Checkout/base', + importPath: '@spartacus/checkout/base', }, rootModule: { name: 'CheckoutRootModule', - importPath: '@spartacus/Checkout/base/root', + importPath: '@spartacus/checkout/base/root', }, }; const CHECKOUT_OPTIONS: LibraryOptions = { @@ -129,60 +123,6 @@ describe('Lib utils', () => { lazy: true, }; - const CART_FEATURE_CONFIG: FeatureConfig = { - folderName: 'cart', - moduleName: 'Cart', - featureModule: { - name: 'CartBaseModule', - importPath: '@spartacus/cart/base', - }, - rootModule: { - name: 'CartBaseRootModule', - importPath: '@spartacus/cart/base/root', - }, - }; - const CART_OPTIONS: LibraryOptions = { - project: 'schematics-test', - features: ['cart-cli'], - lazy: true, - }; - - const USER_PROFILE_FEATURE_CONFIG: FeatureConfig = { - folderName: 'user', - moduleName: 'UserProfile', - featureModule: { - name: 'UserProfileModule', - importPath: '@spartacus/user', - }, - rootModule: { - name: 'UserProfileRootModule', - importPath: '@spartacus/user/root', - }, - }; - const USER_PROFILE_OPTIONS: LibraryOptions = { - project: 'schematics-test', - features: ['user-profile-cli'], - lazy: true, - }; - - const ORDER_FEATURE_CONFIG: FeatureConfig = { - folderName: 'order', - moduleName: 'Order', - featureModule: { - name: 'OrderModule', - importPath: '@spartacus/order', - }, - rootModule: { - name: 'OrderRootModule', - importPath: '@spartacus/order/root', - }, - }; - const ORDER_OPTIONS: LibraryOptions = { - project: 'schematics-test', - features: ['order-cli'], - lazy: true, - }; - beforeEach(async () => { appTree = await schematicRunner .runExternalSchematicAsync( @@ -224,33 +164,33 @@ describe('Lib utils', () => { describe('addLibraryFeature', () => { it('should add i18n config in feature module', async () => { - const tree = await schematicRunner + appTree = await schematicRunner .callRule(addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG), appTree) .toPromise(); - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); }); it('should NOT add i18n if the config is not present', async () => { - const featureConfig: FeatureConfig = { + const featureConfig: SchematicConfig = { ...BASE_FEATURE_CONFIG, i18n: undefined, }; - const tree = await schematicRunner + appTree = await schematicRunner .callRule(addLibraryFeature(BASE_OPTIONS, featureConfig), appTree) .toPromise(); - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); }); describe('when the lazy loading is configured', () => { it('should add it in the lazy loading way', async () => { - const tree = await schematicRunner + appTree = await schematicRunner .callRule( addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG), appTree ) .toPromise(); - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); }); }); describe('when the eager loading is configured', () => { @@ -259,53 +199,24 @@ describe('Lib utils', () => { { ...BASE_OPTIONS, lazy: false }, BASE_FEATURE_CONFIG ); - const tree = await schematicRunner.callRule(rule, appTree).toPromise(); - - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); - }); - }); - describe('recreate option', () => { - it('should remove the feature module and recreate it', async () => { - let tree: Tree; - tree = await schematicRunner - .callRule( - addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG), - appTree - ) - .toPromise(); - - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + appTree = await schematicRunner.callRule(rule, appTree).toPromise(); - tree = await schematicRunner - .callRule( - addLibraryFeature(BASE_OPTIONS, { - ...BASE_FEATURE_CONFIG, - recreate: true, - featureModule: { - ...BASE_FEATURE_CONFIG.featureModule, - // this should change - name: 'YyyModule', - }, - }), - appTree - ) - .toPromise(); - - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); }); }); describe('custom config option', () => { it('should add the custom config when set', async () => { - const featureConfig: FeatureConfig = { + const featureConfig: SchematicConfig = { ...BASE_FEATURE_CONFIG, - customConfig: { - import: [ - { - moduleSpecifier: SPARTACUS_CDS, - namedImports: [CDS_CONFIG], - }, - ], - content: `<${CDS_CONFIG}>{ + customConfig: () => ({ + providers: { + import: [ + { + moduleSpecifier: SPARTACUS_CDS, + namedImports: [CDS_CONFIG], + }, + ], + content: `<${CDS_CONFIG}>{ cds: { profileTag: { javascriptUrl: @@ -316,33 +227,34 @@ describe('Lib utils', () => { }, }, }`, - }, + }, + }), }; - const tree = await schematicRunner + appTree = await schematicRunner .callRule(addLibraryFeature(BASE_OPTIONS, featureConfig), appTree) .toPromise(); - expect(tree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read(xxxFeaturePath)?.toString(UTF_8)).toMatchSnapshot(); }); }); describe('assets options', () => { it('should update angular.json file with assets', async () => { // before - expect(appTree.readContent('angular.json')).toMatchSnapshot(); + expect(appTree.read('angular.json')?.toString(UTF_8)).toMatchSnapshot(); - const featureConfig: FeatureConfig = { + const featureConfig: SchematicConfig = { ...BASE_FEATURE_CONFIG, assets: { input: 'smartedit/assets', glob: '**/*', }, }; - const tree = await schematicRunner + appTree = await schematicRunner .callRule(addLibraryFeature(BASE_OPTIONS, featureConfig), appTree) .toPromise(); // after - expect(tree.read('angular.json')?.toString(UTF_8)).toMatchSnapshot(); + expect(appTree.read('angular.json')?.toString(UTF_8)).toMatchSnapshot(); }); }); describe('style', () => { @@ -350,12 +262,10 @@ describe('Lib utils', () => { describe('and the scss file does NOT exist', () => { it('should add it', async () => { const rule = addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG); - const tree = await schematicRunner - .callRule(rule, appTree) - .toPromise(); + appTree = await schematicRunner.callRule(rule, appTree).toPromise(); - expect(tree.exists(scssFilePath)).toEqual(true); - const content = tree.read(scssFilePath)?.toString(UTF_8); + expect(appTree.exists(scssFilePath)).toEqual(true); + const content = appTree.read(scssFilePath)?.toString(UTF_8); expect(content).toEqual(`@import "${FEATURE_MODULE_IMPORT_PATH}";`); }); }); @@ -368,12 +278,10 @@ describe('Lib utils', () => { }); it('should NOT append it', async () => { const rule = addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG); - const tree = await schematicRunner - .callRule(rule, appTree) - .toPromise(); + appTree = await schematicRunner.callRule(rule, appTree).toPromise(); - expect(tree.exists(scssFilePath)).toEqual(true); - const content = tree.read(scssFilePath)?.toString(UTF_8); + expect(appTree.exists(scssFilePath)).toEqual(true); + const content = appTree.read(scssFilePath)?.toString(UTF_8); expect(content).toEqual(`@import "${FEATURE_MODULE_IMPORT_PATH}";`); }); }); @@ -384,12 +292,10 @@ describe('Lib utils', () => { }); it('should append it', async () => { const rule = addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG); - const tree = await schematicRunner - .callRule(rule, appTree) - .toPromise(); + appTree = await schematicRunner.callRule(rule, appTree).toPromise(); - expect(tree.exists(scssFilePath)).toEqual(true); - const content = tree.read(scssFilePath)?.toString(UTF_8); + expect(appTree.exists(scssFilePath)).toEqual(true); + const content = appTree.read(scssFilePath)?.toString(UTF_8); expect(content).toEqual( `${randomContent}\n@import "${FEATURE_MODULE_IMPORT_PATH}";` ); @@ -402,21 +308,17 @@ describe('Lib utils', () => { ...BASE_FEATURE_CONFIG, styles: undefined, }); - const tree = await schematicRunner - .callRule(rule, appTree) - .toPromise(); + appTree = await schematicRunner.callRule(rule, appTree).toPromise(); - expect(tree.exists(scssFilePath)).toEqual(false); + expect(appTree.exists(scssFilePath)).toEqual(false); }); }); }); }); describe('addPackageJsonDependenciesForLibrary', () => { - let tree: Tree; - beforeEach(async () => { - tree = await schematicRunner + appTree = await schematicRunner .callRule( addLibraryFeature(CHECKOUT_OPTIONS, CHECKOUT_FEATURE_CONFIG), appTree @@ -426,8 +328,9 @@ describe('Lib utils', () => { it('checkout', async () => { const peerDependencies: Record = { - '@spartacus/cart': '4.1.0-next.0', - '@spartacus/checkout': '4.1.0-next.0', + [SPARTACUS_ORDER]: '4.1.0-next.0', + [SPARTACUS_CART]: '4.1.0-next.0', + [SPARTACUS_CHECKOUT]: '4.1.0-next.0', }; await schematicRunner @@ -436,56 +339,17 @@ describe('Lib utils', () => { peerDependencies, CHECKOUT_OPTIONS ), - tree - ) - .toPromise(); - - const tasks = schematicRunner.tasks - .filter((task) => task.name === 'run-schematic') - .map((task) => task.options as RunSchematicTask) - .map((task) => (task as any).options.collection); - - expect(tasks).toEqual(['@spartacus/cart', '@spartacus/checkout']); - }); - }); - - describe('feature ordering', () => { - let tree: Tree; - beforeEach(async () => { - tree = await schematicRunner - .callRule(addLibraryFeature(BASE_OPTIONS, BASE_FEATURE_CONFIG), appTree) - .toPromise(); - tree = await schematicRunner - .callRule(addLibraryFeature(DP_OPTIONS, DP_FEATURE_CONFIG), tree) - .toPromise(); - tree = await schematicRunner - .callRule( - addLibraryFeature(CHECKOUT_OPTIONS, CHECKOUT_FEATURE_CONFIG), - tree - ) - .toPromise(); - tree = await schematicRunner - .callRule(addLibraryFeature(CART_OPTIONS, CART_FEATURE_CONFIG), tree) - .toPromise(); - tree = await schematicRunner - .callRule( - addLibraryFeature(USER_PROFILE_OPTIONS, USER_PROFILE_FEATURE_CONFIG), - tree + appTree ) .toPromise(); - tree = await schematicRunner - .callRule(addLibraryFeature(ORDER_OPTIONS, ORDER_FEATURE_CONFIG), tree) - .toPromise(); - tree = await schematicRunner - .callRule(orderInstalledFeatures(spartacusDefaultOptions), tree) - .toPromise(); - }); + const packageJson = JSON.parse( + appTree.read('package.json')?.toString(UTF_8) ?? '' + ).dependencies as Record; - it('should appropriately order the feature modules', () => { - expect( - tree.read(spartacusFeaturesPath)?.toString(UTF_8) - ).toMatchSnapshot(); + expect(packageJson[SPARTACUS_ORDER]).toBeTruthy(); + expect(packageJson[SPARTACUS_CART]).toBeTruthy(); + expect(packageJson[SPARTACUS_CHECKOUT]).toBeTruthy(); }); }); }); diff --git a/projects/schematics/src/shared/utils/logger-utils.ts b/projects/schematics/src/shared/utils/logger-utils.ts new file mode 100644 index 00000000000..5e20e0c4eab --- /dev/null +++ b/projects/schematics/src/shared/utils/logger-utils.ts @@ -0,0 +1,29 @@ +import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; + +/** + * Logs the provided message if the debug option is set to true. + */ +export function debugLogRule(message: string, debug?: boolean): Rule { + return (_tree: Tree, context: SchematicContext) => { + if (debug) { + context.logger.info(message); + } + }; +} + +/** + * Formats the given message. + */ +export function formatFeatureStart(feature: string, message: string): string { + return `⌛️ ${feature}: ${message}`; +} + +/** + * Formats the given message. + */ +export function formatFeatureComplete( + feature: string, + message: string +): string { + return `✅ ${feature}: ${message}`; +} diff --git a/projects/schematics/src/shared/utils/logger-utils_spec.ts b/projects/schematics/src/shared/utils/logger-utils_spec.ts new file mode 100644 index 00000000000..4c2075b7b08 --- /dev/null +++ b/projects/schematics/src/shared/utils/logger-utils_spec.ts @@ -0,0 +1,104 @@ +import { Tree } from '@angular-devkit/schematics'; +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import { + Schema as ApplicationOptions, + Style, +} from '@schematics/angular/application/schema'; +import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; +import * as path from 'path'; +import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; +import { + SPARTACUS_SCHEMATICS, + USER_PROFILE_FEATURE_NAME, +} from '../libs-constants'; +import { + debugLogRule, + formatFeatureComplete, + formatFeatureStart, +} from './logger-utils'; + +describe('Logger utils', () => { + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + path.join(__dirname, '../../collection.json') + ); + + let appTree: Tree; + + const workspaceOptions: WorkspaceOptions = { + name: 'workspace', + version: '0.5.0', + }; + + const appOptions: ApplicationOptions = { + name: 'schematics-test', + inlineStyle: false, + inlineTemplate: false, + routing: false, + style: Style.Scss, + skipTests: false, + projectRoot: '', + }; + + const spartacusDefaultOptions: SpartacusOptions = { + project: 'schematics-test', + lazy: true, + features: [USER_PROFILE_FEATURE_NAME], + debug: true, + }; + + beforeEach(async () => { + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'workspace', + workspaceOptions + ) + .toPromise(); + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'application', + appOptions, + appTree + ) + .toPromise(); + appTree = await schematicRunner + .runSchematicAsync( + 'add-spartacus', + { ...spartacusDefaultOptions, name: 'schematics-test' }, + appTree + ) + .toPromise(); + }); + + describe('debugLog', () => { + let lastLogMessage: string | undefined; + beforeEach(() => { + schematicRunner.logger.subscribe((log) => { + lastLogMessage = log.message; + }); + }); + + it('should NOT log the message if the debug is false', async () => { + await schematicRunner + .callRule(debugLogRule(`xxx`, false), appTree) + .toPromise(); + expect(lastLogMessage).not.toEqual(`xxx`); + }); + }); + + describe('formatFeatureStart', () => { + it('should format the message', () => { + const message = formatFeatureStart('featurename', 'xxx'); + expect(message).toEqual(`⌛️ featurename: xxx`); + }); + }); + + describe('formatFeatureComplete', () => { + it('should format the message', () => { + const message = formatFeatureComplete('featurename', 'xxx'); + expect(message).toEqual(`✅ featurename: xxx`); + }); + }); +}); diff --git a/projects/schematics/src/shared/utils/module-file-utils_spec.ts b/projects/schematics/src/shared/utils/module-file-utils_spec.ts index 5977e058f00..a7300eb9096 100644 --- a/projects/schematics/src/shared/utils/module-file-utils_spec.ts +++ b/projects/schematics/src/shared/utils/module-file-utils_spec.ts @@ -10,6 +10,7 @@ import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema import * as path from 'path'; import ts from 'typescript'; import { UTF_8 } from '../constants'; +import { SPARTACUS_SCHEMATICS } from '../libs-constants'; import { getPathResultsForFile } from './file-utils'; import { addImport, @@ -21,7 +22,10 @@ import { } from './module-file-utils'; const collectionPath = path.join(__dirname, '../../collection.json'); -const schematicRunner = new SchematicTestRunner('schematics', collectionPath); +const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath +); const TEMPLATE_NAME = 'template.html'; const COMPONENT_TEMPLATE_URL = ` diff --git a/projects/schematics/src/shared/utils/new-module-utils.ts b/projects/schematics/src/shared/utils/new-module-utils.ts index 5a49a9c6455..199f5409a2b 100644 --- a/projects/schematics/src/shared/utils/new-module-utils.ts +++ b/projects/schematics/src/shared/utils/new-module-utils.ts @@ -10,24 +10,15 @@ import { ArrayLiteralExpression, CallExpression, Expression, - Identifier, Node, ObjectLiteralElementLike, SourceFile, ts as tsMorph, } from 'ts-morph'; import { ANGULAR_CORE, ANGULAR_SCHEMATICS } from '../constants'; -import { packageFeatureConfigMapping } from '../updateable-constants'; -import { - getSpartacusProviders, - isSpartacusConfigDuplicate, -} from './config-utils'; +import { isSpartacusConfigDuplicate } from './config-utils'; import { getTsSourceFile } from './file-utils'; -import { - getImportDeclaration, - isImportedFrom, - isImportedFromSpartacusLibs, -} from './import-utils'; +import { createImports, isImportedFrom } from './import-utils'; import { getSourceRoot } from './workspace-utils'; export type ModuleProperty = @@ -41,10 +32,14 @@ export interface Import { } export function ensureModuleExists(options: { + /** module's name */ name: string; + /** path where to create the module */ path: string; - module: string; + /** project name */ project: string; + /** the declaring module */ + module: string; }): Rule { return (host: Tree): Rule => { const modulePath = `${getSourceRoot(host, { project: options.project })}/${ @@ -165,12 +160,7 @@ function addToModuleInternal( } const imports = ([] as Import[]).concat(insertOptions.import); - imports.forEach((specifiedImport) => - sourceFile.addImportDeclaration({ - moduleSpecifier: specifiedImport.moduleSpecifier, - namedImports: specifiedImport.namedImports, - }) - ); + createImports(sourceFile, imports); let createdNode: Expression | undefined; if (insertOptions.order || insertOptions.order === 0) { @@ -286,178 +276,3 @@ function getModuleProperty( return arg.getProperty(propertyName); } - -export function collectInstalledModules(spartacusFeaturesModule: SourceFile): - | { - spartacusCoreModules: (Expression | Identifier)[]; - featureModules: { - spartacusLibrary: string; - moduleNode: Expression | Identifier; - }[]; - unrecognizedModules: (Expression | Identifier)[]; - warnings: string[]; - } - | undefined { - const initializer = getModulePropertyInitializer( - spartacusFeaturesModule, - 'imports', - false - ); - if (!initializer) { - return undefined; - } - - const warnings: string[] = []; - const spartacusCoreModules: (Expression | Identifier)[] = []; - const featureModules: { - spartacusLibrary: string; - moduleNode: Expression | Identifier; - }[] = []; - const unrecognizedModules: (Expression | Identifier)[] = []; - - for (const element of initializer.getElements()) { - const moduleIdentifier = getModuleIdentifier(element); - if (!moduleIdentifier) { - warnings.push( - `Skipping ${element.print()} as it is not recognized as a module.` - ); - continue; - } - - const importDeclaration = getImportDeclaration(moduleIdentifier); - if (!importDeclaration) { - warnings.push( - `Skipping ${element.print()} as there is no import found for it.` - ); - continue; - } - - const importPath = importDeclaration.getModuleSpecifierValue(); - if (isImportedFromSpartacusLibs(importPath)) { - spartacusCoreModules.push(element); - continue; - } - - const potentialFeatureModule = - importDeclaration.getModuleSpecifierSourceFile(); - if (!potentialFeatureModule) { - warnings.push( - `Skipping ${element.print()} as there is no file found for ${importDeclaration.print()}.` - ); - continue; - } - - const spartacusLibrary = recognizeFeatureModule(potentialFeatureModule); - if (spartacusLibrary) { - featureModules.push({ spartacusLibrary, moduleNode: element }); - } else { - unrecognizedModules.push(element); - } - } - - return { - spartacusCoreModules, - featureModules, - unrecognizedModules, - warnings, - }; -} - -function getModuleIdentifier(element: Node): Identifier | undefined { - if (Node.isIdentifier(element)) { - return element; - } - - if (Node.isCallExpression(element)) { - const propertyAccessExpression = element.getFirstChild(); - if (Node.isPropertyAccessExpression(propertyAccessExpression)) { - const firstIdentifier = propertyAccessExpression.getFirstChild(); - if (Node.isIdentifier(firstIdentifier)) { - return firstIdentifier; - } - } - } - - return undefined; -} - -/** - * Function attempts to recognize the given feature module by either: - * - looking at the import path - * - or by looking at the module used in the dynamic import - * - * If the feature module is recognized, the corresponding Spartacus library is returned. - */ -function recognizeFeatureModule(featureModule: SourceFile): string | undefined { - return ( - recognizeFeatureModuleByImports(featureModule) ?? - recognizeFeatureModuleByDynamicImport(featureModule) - ); -} - -function recognizeFeatureModuleByImports( - featureModule: SourceFile -): string | undefined { - const elements = - getModulePropertyInitializer( - featureModule, - 'imports', - false - )?.getElements() ?? []; - - for (const element of elements) { - const moduleName = getModuleIdentifier(element)?.getText() ?? ''; - const spartacusLibrary = getSpartacusLibraryByModule(moduleName); - if (spartacusLibrary) { - return spartacusLibrary; - } - } - - return undefined; -} - -/** - * TODO:#schematics: - * Improvements for dynamic imports detection: - * 1. collect all dynamic imports from the feature module - * 2. filter the dynamic imports that have a relative import - * 3. peek into those relative modules, and check their NgModule imports. - * 4. if it contains the "main" module import, skip the addition of dynamic import that would point to a spartacus lib - */ -function recognizeFeatureModuleByDynamicImport( - featureModule: SourceFile -): string | undefined { - const providers = getSpartacusProviders(featureModule, false); - - for (const element of providers) { - const moduleName = - element - /** e.g.: () => import('@spartacus/digital-payments').then((m) => m.DigitalPaymentsModule) - */ - .getFirstDescendantByKind(tsMorph.SyntaxKind.ArrowFunction) - /** e.g.: (m) => m.DigitalPaymentsModule */ - ?.getFirstDescendantByKind(tsMorph.SyntaxKind.ArrowFunction) - /** e.g.: m.DigitalPaymentsModule */ - ?.getFirstDescendantByKind(tsMorph.SyntaxKind.PropertyAccessExpression) - /** e.g.: DigitalPaymentsModule */ - ?.getLastChildByKind(tsMorph.SyntaxKind.Identifier) - ?.getText() ?? ''; - - const spartacusLibrary = getSpartacusLibraryByModule(moduleName); - if (spartacusLibrary) { - return spartacusLibrary; - } - } - - return undefined; -} - -function getSpartacusLibraryByModule(moduleName: string): string | undefined { - for (const spartacusLibrary of Object.keys(packageFeatureConfigMapping)) { - if (packageFeatureConfigMapping[spartacusLibrary].includes(moduleName)) { - return spartacusLibrary; - } - } - - return undefined; -} diff --git a/projects/schematics/src/shared/utils/package-utils_spec.ts b/projects/schematics/src/shared/utils/package-utils_spec.ts index d352a023bc0..0e50ff37b88 100644 --- a/projects/schematics/src/shared/utils/package-utils_spec.ts +++ b/projects/schematics/src/shared/utils/package-utils_spec.ts @@ -9,6 +9,7 @@ import { import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import * as path from 'path'; import { UTF_8 } from '../constants'; +import { SPARTACUS_SCHEMATICS } from '../libs-constants'; import { getMajorVersionNumber, getSpartacusCurrentFeatureLevel, @@ -17,7 +18,10 @@ import { } from './package-utils'; const collectionPath = path.join(__dirname, '../../collection.json'); -const schematicRunner = new SchematicTestRunner('schematics', collectionPath); +const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath +); describe('Package utils', () => { let appTree: UnitTestTree; diff --git a/projects/schematics/src/shared/utils/schematics-config-utils.ts b/projects/schematics/src/shared/utils/schematics-config-utils.ts new file mode 100644 index 00000000000..b0021bf140f --- /dev/null +++ b/projects/schematics/src/shared/utils/schematics-config-utils.ts @@ -0,0 +1,24 @@ +import { SchematicsException } from '@angular-devkit/schematics'; +import { featureSchematicConfigMapping } from '../schematics-config-mappings'; + +/** + * Returns the configured dependencies for the given feature. + */ +export function getConfiguredDependencies(feature: string): string[] { + const featureConfig = featureSchematicConfigMapping.get(feature); + if (!featureConfig) { + throw new SchematicsException(`No feature config found for ${feature}.`); + } + + const dependencyConfig = featureConfig.dependencyFeatures ?? {}; + const featureDependencies: string[] = []; + for (const key in dependencyConfig) { + if (!dependencyConfig.hasOwnProperty(key)) { + continue; + } + + featureDependencies.push(...(dependencyConfig[key] ?? [])); + } + + return featureDependencies; +} diff --git a/projects/schematics/src/shared/utils/schematics-config-utils_spec.ts b/projects/schematics/src/shared/utils/schematics-config-utils_spec.ts new file mode 100644 index 00000000000..6b719c1d91d --- /dev/null +++ b/projects/schematics/src/shared/utils/schematics-config-utils_spec.ts @@ -0,0 +1,16 @@ +/// + +import { + CHECKOUT_BASE_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, +} from '../libs-constants'; +import { getConfiguredDependencies } from './schematics-config-utils'; + +describe('schematics-config-util', () => { + describe('getConfiguredDependencies', () => { + it('should return the correct feature dependencies for the given feature', () => { + const result = getConfiguredDependencies(DIGITAL_PAYMENTS_FEATURE_NAME); + expect(result).toEqual([CHECKOUT_BASE_FEATURE_NAME]); + }); + }); +}); diff --git a/projects/schematics/src/shared/utils/test-utils.ts b/projects/schematics/src/shared/utils/test-utils.ts index e7ad3da8134..17dad782a73 100644 --- a/projects/schematics/src/shared/utils/test-utils.ts +++ b/projects/schematics/src/shared/utils/test-utils.ts @@ -8,6 +8,65 @@ import { findNodes } from '@schematics/angular/utility/ast-utils'; import ts from 'typescript'; import { findConstructor } from './file-utils'; +export const spartacusFeaturesModulePath = + 'src/app/spartacus/spartacus-features.module.ts'; + +export const asmFeatureModulePath = + 'src/app/spartacus/features/asm/asm-feature.module.ts'; +export const cartBaseFeatureModulePath = + 'src/app/spartacus/features/cart/cart-base-feature.module.ts'; +export const cartWrapperModulePath = + 'src/app/spartacus/features/cart/cart-base-wrapper.module.ts'; +export const importExportFeatureModulePath = + 'src/app/spartacus/features/cart/cart-import-export-feature.module.ts'; +export const quickOrderFeatureModulePath = + 'src/app/spartacus/features/cart/cart-quick-order-feature.module.ts'; +export const wishListFeatureModulePath = + 'src/app/spartacus/features/cart/wish-list-feature.module.ts'; +export const savedCartFeatureModulePath = + 'src/app/spartacus/features/cart/cart-saved-cart-feature.module.ts'; +export const checkoutFeatureModulePath = + 'src/app/spartacus/features/checkout/checkout-feature.module.ts'; +export const checkoutWrapperModulePath = + 'src/app/spartacus/features/checkout/checkout-wrapper.module.ts'; +export const orderFeatureModulePath = + 'src/app/spartacus/features/order/order-feature.module.ts'; +export const organizationAdministrationFeatureModulePath = + 'src/app/spartacus/features/organization/organization-administration-feature.module.ts'; +export const organizationOrderApprovalFeatureModulePath = + 'src/app/spartacus/features/organization/organization-order-approval-feature.module.ts'; +export const productBulkPricingFeatureModulePath = + 'src/app/spartacus/features/product/product-bulk-pricing-feature.module.ts'; +export const productImageZoomFeatureModulePath = + 'src/app/spartacus/features/product/product-image-zoom-feature.module.ts'; +export const productVariantsFeatureModulePath = + 'src/app/spartacus/features/product/product-variants-feature.module.ts'; +export const productConfiguratorFeatureModulePath = + 'src/app/spartacus/features/product-configurator/product-configurator-feature.module.ts'; +export const productConfiguratorRulebasedWrapperModulePath = + 'src/app/spartacus/features/product-configurator/rulebased-configurator-wrapper.module.ts'; +export const qualtricsFeatureModulePath = + 'src/app/spartacus/features/qualtrics/qualtrics-feature.module.ts'; +export const smartEditFeatureModulePath = + 'src/app/spartacus/features/smartedit/smart-edit-feature.module.ts'; +export const storeFinderFeatureModulePath = + 'src/app/spartacus/features/storefinder/store-finder-feature.module.ts'; +export const trackingPersonalizationFeatureModulePath = + 'src/app/spartacus/features/tracking/personalization-feature.module.ts'; +export const trackingTagManagementFeatureModulePath = + 'src/app/spartacus/features/tracking/tag-management-feature.module.ts'; +export const userFeatureModulePath = + 'src/app/spartacus/features/user/user-feature.module.ts'; + +export const cdcFeatureModulePath = + 'src/app/spartacus/features/cdc/cdc-feature.module.ts'; +export const cdsFeatureModulePath = + 'src/app/spartacus/features/cds/cds-feature.module.ts'; +export const digitalPaymentsFeatureModulePath = + 'src/app/spartacus/features/digital-payments/digital-payments-feature.module.ts'; +export const epdFeatureModulePath = + 'src/app/spartacus/features/epd-visualization/epd-visualization-feature.module.ts'; + export function writeFile( host: TempScopedNodeJsSyncHost, filePath: string, diff --git a/projects/schematics/src/shared/utils/workspace-utils.ts b/projects/schematics/src/shared/utils/workspace-utils.ts index e1d32c97fcc..7e6c580d16d 100644 --- a/projects/schematics/src/shared/utils/workspace-utils.ts +++ b/projects/schematics/src/shared/utils/workspace-utils.ts @@ -1,6 +1,7 @@ import { chain, Rule, + SchematicContext, SchematicsException, Tree, } from '@angular-devkit/schematics'; @@ -18,6 +19,7 @@ import { SPARTACUS_FEATURES_MODULE, SPARTACUS_MODULE, } from '../libs-constants'; +import { debugLogRule } from './logger-utils'; import { ensureModuleExists } from './new-module-utils'; const DEFAULT_POSSIBLE_PROJECT_FILES = ['/angular.json', '/.angular.json']; @@ -177,8 +179,13 @@ export function validateSpartacusInstallation(packageJson: any): void { } export function scaffoldStructure(options: SpartacusOptions): Rule { - return (_tree: Tree) => { + return (_tree: Tree, _context: SchematicContext) => { return chain([ + debugLogRule( + `⌛️ Scaffolding Spartacus file structure...`, + options.debug + ), + ensureModuleExists({ name: SPARTACUS_MODULE, path: 'app/spartacus', @@ -197,6 +204,8 @@ export function scaffoldStructure(options: SpartacusOptions): Rule { module: 'spartacus', project: options.project, }), + + debugLogRule(`✅ Spartacus file structure scaffolded.`, options.debug), ]); }; } diff --git a/projects/schematics/src/shared/utils/workspace-utils_spec.ts b/projects/schematics/src/shared/utils/workspace-utils_spec.ts index 78a58f8a53f..984dee736ec 100644 --- a/projects/schematics/src/shared/utils/workspace-utils_spec.ts +++ b/projects/schematics/src/shared/utils/workspace-utils_spec.ts @@ -14,7 +14,7 @@ import { import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; import * as path from 'path'; import { Schema as SpartacusOptions } from '../../add-spartacus/schema'; -import { SPARTACUS_CORE } from '../libs-constants'; +import { SPARTACUS_CORE, SPARTACUS_SCHEMATICS } from '../libs-constants'; import { buildDefaultPath, getAngularJsonFile, @@ -30,7 +30,10 @@ import { } from './workspace-utils'; const collectionPath = path.join(__dirname, '../../collection.json'); -const schematicRunner = new SchematicTestRunner('schematics', collectionPath); +const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath +); describe('Workspace utils', () => { let appTree: UnitTestTree; diff --git a/projects/schematics/src/wrapper-module/__snapshots__/index_spec.ts.snap b/projects/schematics/src/wrapper-module/__snapshots__/index_spec.ts.snap new file mode 100644 index 00000000000..8872d9b22d7 --- /dev/null +++ b/projects/schematics/src/wrapper-module/__snapshots__/index_spec.ts.snap @@ -0,0 +1,545 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout Scheduled Replenishment should create the checkout wrapper module and import Checkout features 1`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; +import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { checkoutScheduledReplenishmentTranslationChunksConfig, checkoutScheduledReplenishmentTranslations } from \\"@spartacus/checkout/scheduled-replenishment/assets\\"; +import { CheckoutScheduledReplenishmentRootModule } from \\"@spartacus/checkout/scheduled-replenishment/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule, + CheckoutB2BRootModule, + CheckoutScheduledReplenishmentRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }), + provideConfig({ + i18n: { + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, + }, + }), + provideConfig({ + i18n: { + resources: checkoutScheduledReplenishmentTranslations, + chunks: checkoutScheduledReplenishmentTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout Scheduled Replenishment should create the checkout wrapper module and import Checkout features 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { CheckoutScheduledReplenishmentModule } from \\"@spartacus/checkout/scheduled-replenishment\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule, + CheckoutScheduledReplenishmentModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout and DP Should order the imports in the wrapper and Spartacus features modules 1`] = ` +"import { NgModule } from '@angular/core'; +import { AnonymousConsentsModule, AuthModule, CostCenterOccModule, ExternalRoutesModule, ProductModule, ProductOccModule, UserModule, UserOccModule } from \\"@spartacus/core\\"; +import { AddressBookModule, AnonymousConsentManagementBannerModule, AnonymousConsentsDialogModule, BannerCarouselModule, BannerModule, BreadcrumbModule, CategoryNavigationModule, CmsParagraphModule, ConsentManagementModule, FooterNavigationModule, HamburgerMenuModule, HomePageEventModule, LinkModule, LoginRouteModule, LogoutModule, MyCouponsModule, MyInterestsModule, NavigationEventModule, NavigationModule, NotificationPreferenceModule, PageTitleModule, PaymentMethodsModule, ProductCarouselModule, ProductDetailsPageModule, ProductFacetNavigationModule, ProductImagesModule, ProductIntroModule, ProductListingPageModule, ProductListModule, ProductPageEventModule, ProductReferencesModule, ProductSummaryModule, ProductTabsModule, ScrollToTopModule, SearchBoxModule, SiteContextSelectorModule, StockNotificationModule, TabParagraphContainerModule } from \\"@spartacus/storefront\\"; +import { UserFeatureModule } from './features/user/user-feature.module'; +import { CartBaseFeatureModule } from './features/cart/cart-base-feature.module'; +import { OrderFeatureModule } from './features/order/order-feature.module'; +import { CheckoutFeatureModule } from './features/checkout/checkout-feature.module'; +import { DigitalPaymentsFeatureModule } from './features/digital-payments/digital-payments-feature.module'; +@NgModule({ + declarations: [], + imports: [ + AuthModule.forRoot(), + LogoutModule, + LoginRouteModule, + HamburgerMenuModule, + SiteContextSelectorModule, + LinkModule, + BannerModule, + CmsParagraphModule, + TabParagraphContainerModule, + BannerCarouselModule, + CategoryNavigationModule, + NavigationModule, + FooterNavigationModule, + BreadcrumbModule, + ScrollToTopModule, + PageTitleModule, + UserModule, + UserOccModule, + AddressBookModule, + PaymentMethodsModule, + NotificationPreferenceModule, + MyInterestsModule, + StockNotificationModule, + ConsentManagementModule, + MyCouponsModule, + AnonymousConsentsModule.forRoot(), + AnonymousConsentsDialogModule, + AnonymousConsentManagementBannerModule, + ProductModule.forRoot(), + ProductOccModule, + ProductDetailsPageModule, + ProductListingPageModule, + ProductListModule, + SearchBoxModule, + ProductFacetNavigationModule, + ProductTabsModule, + ProductCarouselModule, + ProductReferencesModule, + ProductImagesModule, + ProductSummaryModule, + ProductIntroModule, + CostCenterOccModule, + NavigationEventModule, + HomePageEventModule, + ProductPageEventModule, + ExternalRoutesModule.forRoot(), + UserFeatureModule, + CartBaseFeatureModule, + OrderFeatureModule, + CheckoutFeatureModule, + DigitalPaymentsFeatureModule + ] +}) +export class SpartacusFeaturesModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout and DP Should order the imports in the wrapper and Spartacus features modules 2`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; +import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { checkoutScheduledReplenishmentTranslationChunksConfig, checkoutScheduledReplenishmentTranslations } from \\"@spartacus/checkout/scheduled-replenishment/assets\\"; +import { CheckoutScheduledReplenishmentRootModule } from \\"@spartacus/checkout/scheduled-replenishment/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule, + CheckoutB2BRootModule, + CheckoutScheduledReplenishmentRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }), + provideConfig({ + i18n: { + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, + }, + }), + provideConfig({ + i18n: { + resources: checkoutScheduledReplenishmentTranslations, + chunks: checkoutScheduledReplenishmentTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout and DP Should order the imports in the wrapper and Spartacus features modules 3`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { CheckoutScheduledReplenishmentModule } from \\"@spartacus/checkout/scheduled-replenishment\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule, + CheckoutScheduledReplenishmentModule, + DigitalPaymentsModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Checkout and DP Should order the imports in the wrapper and Spartacus features modules 4`] = ` +"import { NgModule } from '@angular/core'; +import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { dpTranslationChunksConfig, dpTranslations } from \\"@spartacus/digital-payments/assets\\"; +@NgModule({ + declarations: [], + imports: [], + providers: [provideConfig({ + i18n: { + resources: dpTranslations, + chunks: dpTranslationChunksConfig, + }, + }) + ] +}) +export class DigitalPaymentsFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Digital Payments should create the checkout wrapper module and import Base Checkout and DP 1`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + DigitalPaymentsModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Digital Payments should create the checkout wrapper module and import Base Checkout and DP 2`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Digital Payments should create the checkout wrapper module and import Base Checkout and DP 3`] = ` +"import { NgModule } from '@angular/core'; +import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; +import { dpTranslationChunksConfig, dpTranslations } from \\"@spartacus/digital-payments/assets\\"; +@NgModule({ + declarations: [], + imports: [], + providers: [provideConfig({ + i18n: { + resources: dpTranslations, + chunks: dpTranslationChunksConfig, + }, + }) + ] +}) +export class DigitalPaymentsFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Double execution should not change anything 1`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Double execution should not change anything 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Multiple dynamic imports in the file should generate appropriate feature module 1`] = ` +"import { NgModule } from '@angular/core'; +import { cartBaseTranslationChunksConfig, cartBaseTranslations } from \\"@spartacus/cart/base/assets\\"; +import { ADD_TO_CART_FEATURE, CartBaseRootModule, CART_BASE_FEATURE, MINI_CART_FEATURE } from \\"@spartacus/cart/base/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CartBaseRootModule + ], + providers: [provideConfig({ + featureModules: { + [CART_BASE_FEATURE]: { + module: () => import('./cart-base-wrapper.module').then((m) => m.CartBaseWrapperModule), + }, + } + }), + provideConfig({ + featureModules: { + [MINI_CART_FEATURE]: { + module: () => import('@spartacus/cart/base/components/mini-cart').then((m) => m.MiniCartModule), + }, + } + }), + provideConfig({ + featureModules: { + [ADD_TO_CART_FEATURE]: { + module: () => import('@spartacus/cart/base/components/add-to-cart').then((m) => m.AddToCartModule), + }, + } + }), + provideConfig({ + i18n: { + resources: cartBaseTranslations, + chunks: cartBaseTranslationChunksConfig, + }, + }) + ] +}) +export class CartBaseFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module Multiple dynamic imports in the file should generate appropriate feature module 2`] = ` +"import { NgModule } from '@angular/core'; +import { CartBaseModule } from \\"@spartacus/cart/base\\"; +@NgModule({ + declarations: [], + imports: [ + CartBaseModule + ] +}) +export class CartBaseWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module One dynamic import in the file should generate appropriate feature module 1`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations } from \\"@spartacus/checkout/b2b/assets\\"; +import { CheckoutB2BRootModule } from \\"@spartacus/checkout/b2b/root\\"; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule, CHECKOUT_FEATURE } from \\"@spartacus/checkout/base/root\\"; +import { CmsConfig, I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule, + CheckoutB2BRootModule + ], + providers: [provideConfig({ + featureModules: { + [CHECKOUT_FEATURE]: { + module: () => import('./checkout-wrapper.module').then((m) => m.CheckoutWrapperModule), + }, + } + }), + provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }), + provideConfig({ + i18n: { + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module One dynamic import in the file should generate appropriate feature module 2`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutB2BModule } from \\"@spartacus/checkout/b2b\\"; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutModule, + CheckoutB2BModule + ] +}) +export class CheckoutWrapperModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module wrapper module already exists should append the feature module after it, and not add a dynamic import to the feature module 1`] = ` +"import { NgModule } from '@angular/core'; +import { CheckoutModule } from \\"@spartacus/checkout/base\\"; +import { AnonymousConsentsModule, AuthModule, CostCenterOccModule, ExternalRoutesModule, ProductModule, ProductOccModule, UserModule, UserOccModule } from \\"@spartacus/core\\"; +import { DigitalPaymentsModule } from \\"@spartacus/digital-payments\\"; +import { AddressBookModule, AnonymousConsentManagementBannerModule, AnonymousConsentsDialogModule, BannerCarouselModule, BannerModule, BreadcrumbModule, CategoryNavigationModule, CmsParagraphModule, ConsentManagementModule, FooterNavigationModule, HamburgerMenuModule, HomePageEventModule, LinkModule, LoginRouteModule, LogoutModule, MyCouponsModule, MyInterestsModule, NavigationEventModule, NavigationModule, NotificationPreferenceModule, PageTitleModule, PaymentMethodsModule, ProductCarouselModule, ProductDetailsPageModule, ProductFacetNavigationModule, ProductImagesModule, ProductIntroModule, ProductListingPageModule, ProductListModule, ProductPageEventModule, ProductReferencesModule, ProductSummaryModule, ProductTabsModule, ScrollToTopModule, SearchBoxModule, SiteContextSelectorModule, StockNotificationModule, TabParagraphContainerModule } from \\"@spartacus/storefront\\"; +import { CartBaseFeatureModule } from './features/cart/cart-base-feature.module'; +import { CheckoutFeatureModule } from './features/checkout/checkout-feature.module'; +import { DigitalPaymentsFeatureModule } from './features/digital-payments/digital-payments-feature.module'; +import { OrderFeatureModule } from './features/order/order-feature.module'; +import { UserFeatureModule } from './features/user/user-feature.module'; +@NgModule({ + declarations: [], + imports: [ + AuthModule.forRoot(), + LogoutModule, + LoginRouteModule, + HamburgerMenuModule, + SiteContextSelectorModule, + LinkModule, + BannerModule, + CmsParagraphModule, + TabParagraphContainerModule, + BannerCarouselModule, + CategoryNavigationModule, + NavigationModule, + FooterNavigationModule, + BreadcrumbModule, + ScrollToTopModule, + PageTitleModule, + UserModule, + UserOccModule, + AddressBookModule, + PaymentMethodsModule, + NotificationPreferenceModule, + MyInterestsModule, + StockNotificationModule, + ConsentManagementModule, + MyCouponsModule, + AnonymousConsentsModule.forRoot(), + AnonymousConsentsDialogModule, + AnonymousConsentManagementBannerModule, + ProductModule.forRoot(), + ProductOccModule, + ProductDetailsPageModule, + ProductListingPageModule, + ProductListModule, + SearchBoxModule, + ProductFacetNavigationModule, + ProductTabsModule, + ProductCarouselModule, + ProductReferencesModule, + ProductImagesModule, + ProductSummaryModule, + ProductIntroModule, + CostCenterOccModule, + NavigationEventModule, + HomePageEventModule, + ProductPageEventModule, + ExternalRoutesModule.forRoot(), + UserFeatureModule, + CartBaseFeatureModule, + OrderFeatureModule, + CheckoutFeatureModule, + CheckoutModule, + DigitalPaymentsFeatureModule, + DigitalPaymentsModule + ] +}) +export class SpartacusFeaturesModule { +} +" +`; + +exports[`Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module wrapper module already exists should append the feature module after it, and not add a dynamic import to the feature module 2`] = ` +"import { NgModule } from '@angular/core'; +import { checkoutTranslationChunksConfig, checkoutTranslations } from \\"@spartacus/checkout/base/assets\\"; +import { CheckoutRootModule } from \\"@spartacus/checkout/base/root\\"; +import { I18nConfig, provideConfig } from \\"@spartacus/core\\"; +@NgModule({ + declarations: [], + imports: [ + CheckoutRootModule + ], + providers: [provideConfig({ + i18n: { + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, + }, + }) + ] +}) +export class CheckoutFeatureModule { +} +" +`; diff --git a/projects/schematics/src/wrapper-module/index.ts b/projects/schematics/src/wrapper-module/index.ts new file mode 100644 index 00000000000..d1b035dfd89 --- /dev/null +++ b/projects/schematics/src/wrapper-module/index.ts @@ -0,0 +1,523 @@ +import { + chain, + noop, + Rule, + SchematicContext, + Tree, +} from '@angular-devkit/schematics'; +import { ArrowFunction, CallExpression, SyntaxKind } from 'ts-morph'; +import { + featureFeatureModuleMapping, + getKeyByMappingValueOrThrow, + getSchematicsConfigByFeatureOrThrow, +} from '../shared/schematics-config-mappings'; +import { normalizeObject, removeProperty } from '../shared/utils/config-utils'; +import { + findFeatureModule, + getModuleConfig, +} from '../shared/utils/feature-utils'; +import { + findDynamicImport, + getDynamicImportCallExpression, + getDynamicImportPropertyAccess, + staticImportExists, +} from '../shared/utils/import-utils'; +import { + createSpartacusFeatureFileName, + createSpartacusFeatureFolderPath, + createSpartacusWrapperModuleFileName, +} from '../shared/utils/lib-utils'; +import { + debugLogRule, + formatFeatureComplete, + formatFeatureStart, +} from '../shared/utils/logger-utils'; +import { + addModuleImport, + ensureModuleExists, + getModulePropertyInitializer, +} from '../shared/utils/new-module-utils'; +import { createProgram, saveAndFormat } from '../shared/utils/program'; +import { getProjectTsConfigPaths } from '../shared/utils/project-tsconfig-paths'; +import { Schema as SpartacusWrapperOptions } from './schema'; + +/** + * If the wrapper module already exists for + * the given `options.markerModuleName`, it + * sets it path to the `options` object. + */ +function checkWrapperModuleExists(options: SpartacusWrapperOptions): Rule { + return (tree: Tree, context: SchematicContext) => { + const feature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + options.markerModuleName + ); + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `checking the wrapper module path for ${options.markerModuleName} ...` + ) + ); + } + + const featureConfig = getSchematicsConfigByFeatureOrThrow(feature); + const moduleConfig = getModuleConfig( + options.markerModuleName, + featureConfig + ); + if (!moduleConfig) { + return noop(); + } + + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + for (const sourceFile of appSourceFiles) { + // check if the wrapper module already exists + if ( + staticImportExists( + sourceFile, + moduleConfig.importPath, + moduleConfig.name + ) + ) { + options.internal = { + ...options.internal, + wrapperModulePath: sourceFile.getFilePath(), + }; + + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `found '${ + options.markerModuleName + }' in the existing wrapper module: ${sourceFile.getFilePath()} .` + ) + ); + } + return noop(); + } + } + } + + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `wrapper module not found, will create a new one.` + ) + ); + } + }; +} + +/** + * Creates the wrapper module using the feature config + * for the given module name. + */ +function createWrapperModule(options: SpartacusWrapperOptions): Rule { + return (tree: Tree, context: SchematicContext) => { + /** + * if the wrapper module path is set, it means + * the wrapper module already exists. + */ + if (options.internal?.wrapperModulePath) { + return noop(); + } + + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + + const feature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + options.markerModuleName + ); + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `creating wrapper module for ${options.markerModuleName} ...` + ) + ); + } + const featureConfig = getSchematicsConfigByFeatureOrThrow(feature); + const moduleConfig = getModuleConfig( + options.markerModuleName, + featureConfig + ); + if (!moduleConfig) { + return noop(); + } + + const path = createSpartacusFeatureFolderPath(featureConfig.folderName); + const name = createSpartacusWrapperModuleFileName(options.markerModuleName); + const wrapperModulePath = `${path}/${name}`; + /** + * Mutates the options by setting + * the wrapperModulePath for the next rules. + */ + options.internal = { + ...options.internal, + wrapperModulePath, + }; + + const rules: Rule[] = []; + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + const featureModule = findFeatureModule( + featureConfig.featureModule, + appSourceFiles + ); + if (!featureModule) { + continue; + } + + rules.push( + ensureModuleExists({ + path, + name, + project: options.project, + /** + * Only temporarily import the wrapper module to the feature module. + * The import will be removed in updateFeatureModule(). + * + * This is a workaround for a weird behavior of the ts-morph library, + * which does not "see" the newly created TS file if it is not + * referenced anywhere. + */ + module: featureModule.getBaseNameWithoutExtension(), + }) + ); + } + + rules.push( + debugLogRule( + formatFeatureComplete( + feature, + `wrapper module created for ${options.markerModuleName} in ${wrapperModulePath} .` + ), + options.debug + ) + ); + return chain(rules); + }; +} + +/** + * Changes the dynamic import to point to the wrapper module. + * E.g. instead of: + * `import('@spartacus/user/profile').then((m) => m.UserProfileModule),` + * it will be changed to: + * `import('./profile-wrapper.module').then((m) => m.ProfileWrapperModule),` + * + * It also removes the temporary static import to the wrapper + * module from the ngModule's array. + */ +function updateFeatureModule(options: SpartacusWrapperOptions): Rule { + return (tree: Tree, context: SchematicContext) => { + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + + const feature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + options.markerModuleName + ); + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `updating feature module for '${options.markerModuleName}' ...` + ) + ); + } + const featureConfig = getSchematicsConfigByFeatureOrThrow(feature); + const featureModuleConfig = getModuleConfig( + options.markerModuleName, + featureConfig + ); + if (!featureModuleConfig) { + return noop(); + } + + const rules: Rule[] = []; + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + const featureModule = findFeatureModule( + featureConfig.featureModule, + appSourceFiles + ); + if (!featureModule) { + continue; + } + + const dynamicImport = findDynamicImport(featureModule, { + moduleSpecifier: featureModuleConfig.importPath, + namedImports: [featureModuleConfig.name], + }); + if (!dynamicImport) { + continue; + } + + for (const wrapperModule of appSourceFiles) { + if ( + !wrapperModule + .getFilePath() + .includes(options.internal?.wrapperModulePath ?? '') + ) { + continue; + } + + const wrapperModuleClassName = + wrapperModule.getClasses()[0].getName() ?? ''; + updateDynamicImportPath( + dynamicImport, + featureModule.getRelativePathAsModuleSpecifierTo( + wrapperModule.getFilePath() + ) + ); + updateDynamicImportModuleName(dynamicImport, wrapperModuleClassName); + + // remove the dummy import + const ngImports = getModulePropertyInitializer( + featureModule, + 'imports', + false + ); + if (!ngImports) { + continue; + } + for (const element of ngImports.getElements()) { + if (element.getText() === wrapperModuleClassName) { + ngImports.removeElement(element); + break; + } + } + + saveAndFormat(featureModule); + break; + } + } + + rules.push( + debugLogRule( + formatFeatureComplete( + feature, + `feature module updated for '${options.markerModuleName}' .` + ), + options.debug + ) + ); + return chain(rules); + }; +} + +/** + * Removes the dynamic imports pointing to the given + * `options.featureModuleName` from the feature module. + */ +function removeLibraryDynamicImport(options: SpartacusWrapperOptions): Rule { + return (tree: Tree, context: SchematicContext) => { + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + + const feature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + options.featureModuleName + ); + const featureConfig = getSchematicsConfigByFeatureOrThrow(feature); + const featureModuleConfig = getModuleConfig( + options.featureModuleName, + featureConfig + ); + if (!featureModuleConfig) { + return noop(); + } + + const path = createSpartacusFeatureFolderPath(featureConfig.folderName); + const name = createSpartacusFeatureFileName(featureConfig.moduleName); + const featureModulePath = `${path}/${name}`; + + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `removing dynamic import in '${featureModulePath}' for '${options.featureModuleName}' ...` + ) + ); + } + + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + for (const featureModule of appSourceFiles) { + if (!featureModule.getFilePath().includes(featureModulePath)) { + continue; + } + + const spartacusProvider = findDynamicImport(featureModule, { + moduleSpecifier: featureModuleConfig.importPath, + namedImports: [featureModuleConfig.name], + })?.getFirstAncestorByKind(SyntaxKind.CallExpression); + if (!spartacusProvider) { + continue; + } + + cleanupConfig(spartacusProvider); + + saveAndFormat(featureModule); + break; + } + } + + if (options.debug) { + context.logger.info( + formatFeatureComplete( + feature, + `dynamic import removed in '${featureModulePath}' for '${options.featureModuleName}' .` + ) + ); + } + }; +} + +/** + * Takes the given spartacus provider, and removes the + * 'module' configuration property from it. + * If the are no other properties left, the whole + * spartacus provider is removed. + */ +export function cleanupConfig(spartacusProvider: CallExpression): void { + const objectLiteral = spartacusProvider.getFirstDescendantByKind( + SyntaxKind.ObjectLiteralExpression + ); + if (!objectLiteral) { + return; + } + + removeProperty(objectLiteral, 'module'); + if (normalizeObject(objectLiteral.getText()) === '{}') { + spartacusProvider + .getParentIfKindOrThrow(SyntaxKind.ArrayLiteralExpression) + .removeElement(spartacusProvider); + } +} + +/** + * Replaces the given dynamic import's path. + * E.g. for the given `() => import('@spartacus/checkout/base')` + * it replaces it with the given path: `() => import('./checkout-wrapper.module')`. + */ +function updateDynamicImportPath( + dynamicImport: ArrowFunction, + path: string +): void { + getDynamicImportCallExpression(dynamicImport) + ?.removeArgument(0) + ?.insertArgument(0, `'${path}'`); +} + +/** + * Replaces the given dynamic import's module name. + * E.g. for the given `(m) => m.CheckoutModule` + * it replaces it with the given module name: `(m) => m.CheckoutWrapperModule`. + */ +function updateDynamicImportModuleName( + dynamicImport: ArrowFunction, + wrapperModuleName: string +): void { + getDynamicImportPropertyAccess(dynamicImport)?.replaceWithText( + `m.${wrapperModuleName}` + ); +} + +/** + * Statically imports the given module. + */ +function updateWrapperModule( + options: SpartacusWrapperOptions, + moduleName: string +): Rule { + return (tree: Tree, context: SchematicContext) => { + const basePath = process.cwd(); + const { buildPaths } = getProjectTsConfigPaths(tree, options.project); + + const feature = getKeyByMappingValueOrThrow( + featureFeatureModuleMapping, + moduleName + ); + const featureConfig = getSchematicsConfigByFeatureOrThrow(feature); + const featureModuleConfig = getModuleConfig(moduleName, featureConfig); + if (!featureModuleConfig) { + return noop(); + } + + const wrapperModulePath = options.internal?.wrapperModulePath ?? ''; + if (options.debug) { + context.logger.info( + formatFeatureStart( + feature, + `importing the '${moduleName}' to the wrapper module ${wrapperModulePath} ...` + ) + ); + } + + const rules: Rule[] = []; + for (const tsconfigPath of buildPaths) { + const { appSourceFiles } = createProgram(tree, basePath, tsconfigPath); + + for (const wrapperModule of appSourceFiles) { + if (!wrapperModule.getFilePath().includes(wrapperModulePath)) { + continue; + } + + addModuleImport(wrapperModule, { + import: { + moduleSpecifier: featureModuleConfig.importPath, + namedImports: [featureModuleConfig.name], + }, + content: featureModuleConfig.name, + }); + + saveAndFormat(wrapperModule); + break; + } + } + + rules.push( + debugLogRule( + formatFeatureComplete( + feature, + `imported the '${moduleName}' to the wrapper module ${options.internal?.wrapperModulePath} .` + ), + options.debug + ) + ); + return chain(rules); + }; +} + +/** + * Generates wrapper modules for the given + * Spartacus feature module. + */ +export function generateWrapperModule(options: SpartacusWrapperOptions): Rule { + return (_tree: Tree, _context: SchematicContext): Rule => { + return chain([ + checkWrapperModuleExists(options), + + createWrapperModule(options), + + updateFeatureModule(options), + removeLibraryDynamicImport(options), + + updateWrapperModule(options, options.markerModuleName), + updateWrapperModule(options, options.featureModuleName), + ]); + }; +} diff --git a/projects/schematics/src/wrapper-module/index_spec.ts b/projects/schematics/src/wrapper-module/index_spec.ts new file mode 100644 index 00000000000..f939bd60c5c --- /dev/null +++ b/projects/schematics/src/wrapper-module/index_spec.ts @@ -0,0 +1,367 @@ +import { Tree } from '@angular-devkit/schematics'; +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import { + Schema as ApplicationOptions, + Style, +} from '@schematics/angular/application/schema'; +import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema'; +import * as path from 'path'; +import { SyntaxKind } from 'ts-morph'; +import { Schema as SpartacusOptions } from '../add-spartacus/schema'; +import { CART_BASE_MODULE } from '../shared/lib-configs/cart-schematics-config'; +import { CHECKOUT_BASE_MODULE } from '../shared/lib-configs/checkout-schematics-config'; +import { + CART_BASE_FEATURE_NAME, + CHECKOUT_B2B_FEATURE_NAME, + CHECKOUT_BASE_FEATURE_NAME, + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + SPARTACUS_CHECKOUT_BASE, + SPARTACUS_SCHEMATICS, +} from '../shared/libs-constants'; +import { findDynamicImport } from '../shared/utils/import-utils'; +import { LibraryOptions } from '../shared/utils/lib-utils'; +import { addModuleImport, Import } from '../shared/utils/new-module-utils'; +import { createProgram, saveAndFormat } from '../shared/utils/program'; +import { getProjectTsConfigPaths } from '../shared/utils/project-tsconfig-paths'; +import { + cartBaseFeatureModulePath, + cartWrapperModulePath, + checkoutFeatureModulePath, + checkoutWrapperModulePath, + digitalPaymentsFeatureModulePath, + spartacusFeaturesModulePath, +} from '../shared/utils/test-utils'; +import { Schema as SpartacusWrapperOptions } from '../wrapper-module/schema'; +import { cleanupConfig } from './index'; + +const collectionPath = path.join(__dirname, '../collection.json'); + +describe('Spartacus Wrapper Module Schematics: ng g @spartacus/schematics:wrapper-module', () => { + const schematicRunner = new SchematicTestRunner( + SPARTACUS_SCHEMATICS, + collectionPath + ); + + let appTree: Tree; + let buildPath: string; + + const workspaceOptions: WorkspaceOptions = { + name: 'workspace', + version: '0.5.0', + }; + + const appOptions: ApplicationOptions = { + name: 'schematics-test', + inlineStyle: false, + inlineTemplate: false, + routing: false, + style: Style.Scss, + skipTests: false, + projectRoot: '', + }; + + const defaultOptions: SpartacusOptions = { + project: 'schematics-test', + lazy: true, + features: [], + }; + + const BASE_OPTIONS: LibraryOptions = { + project: 'schematics-test', + lazy: true, + }; + + beforeEach(async () => { + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'workspace', + workspaceOptions + ) + .toPromise(); + appTree = await schematicRunner + .runExternalSchematicAsync( + '@schematics/angular', + 'application', + appOptions, + appTree + ) + .toPromise(); + + buildPath = getProjectTsConfigPaths(appTree, BASE_OPTIONS.project) + .buildPaths[0]; + }); + + describe('One dynamic import in the file', () => { + it('should generate appropriate feature module', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + features: [CHECKOUT_B2B_FEATURE_NAME], + name: 'schematics-test', + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + }); + }); + + describe('Multiple dynamic imports in the file', () => { + it('should generate appropriate feature module', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + features: [CART_BASE_FEATURE_NAME], + name: 'schematics-test', + }, + appTree + ) + .toPromise(); + const options: SpartacusWrapperOptions = { + project: 'schematics-test', + markerModuleName: CART_BASE_MODULE, + featureModuleName: CART_BASE_MODULE, + }; + appTree = await schematicRunner + .runSchematicAsync('wrapper-module', options, appTree) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const cartFeatureModule = program.getSourceFileOrThrow( + cartBaseFeatureModulePath + ); + const cartWrapperModule = program.getSourceFileOrThrow( + cartWrapperModulePath + ); + + expect(cartFeatureModule.print()).toMatchSnapshot(); + expect(cartWrapperModule.print()).toMatchSnapshot(); + }); + }); + + describe('Double execution', () => { + it('should not change anything', async () => { + // first execution happens under the hood + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + features: [CHECKOUT_BASE_FEATURE_NAME], + name: 'schematics-test', + }, + appTree + ) + .toPromise(); + + const options: SpartacusWrapperOptions = { + project: 'schematics-test', + markerModuleName: CHECKOUT_BASE_MODULE, + featureModuleName: CHECKOUT_BASE_MODULE, + }; + // the second execution + appTree = await schematicRunner + .runSchematicAsync('wrapper-module', options, appTree) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + }); + }); + + describe('Checkout Scheduled Replenishment', () => { + it('should create the checkout wrapper module and import Checkout features', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + name: 'schematics-test', + features: [CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + }); + }); + + describe('Digital Payments', () => { + it('should create the checkout wrapper module and import Base Checkout and DP', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + name: 'schematics-test', + features: [DIGITAL_PAYMENTS_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const dpFeaturesModule = program.getSourceFileOrThrow( + digitalPaymentsFeatureModulePath + ); + + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + expect(dpFeaturesModule.print()).toMatchSnapshot(); + }); + }); + + describe('Checkout and DP', () => { + it('Should order the imports in the wrapper and Spartacus features modules', async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + name: 'schematics-test', + features: [ + CHECKOUT_SCHEDULED_REPLENISHMENT_FEATURE_NAME, + DIGITAL_PAYMENTS_FEATURE_NAME, + ], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const spartacusFeaturesModule = program.getSourceFileOrThrow( + spartacusFeaturesModulePath + ); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const checkoutWrapperModule = program.getSourceFileOrThrow( + checkoutWrapperModulePath + ); + const dpFeaturesModule = program.getSourceFileOrThrow( + digitalPaymentsFeatureModulePath + ); + expect(spartacusFeaturesModule.print()).toMatchSnapshot(); + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + expect(checkoutWrapperModule.print()).toMatchSnapshot(); + expect(dpFeaturesModule.print()).toMatchSnapshot(); + }); + }); + + describe('wrapper module already exists', () => { + beforeEach(async () => { + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + name: 'schematics-test', + features: [CHECKOUT_BASE_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + + const { program } = createProgram(appTree, appTree.root.path, buildPath); + + const spartacusFeaturesModule = program.getSourceFileOrThrow( + spartacusFeaturesModulePath + ); + const checkoutImport: Import = { + moduleSpecifier: SPARTACUS_CHECKOUT_BASE, + namedImports: [CHECKOUT_BASE_MODULE], + }; + // import CheckoutModule statically, making the spartacus-features module a wrapper module + addModuleImport(spartacusFeaturesModule, { + import: checkoutImport, + content: CHECKOUT_BASE_MODULE, + }); + saveAndFormat(spartacusFeaturesModule); + + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + const spartacusProvider = findDynamicImport( + checkoutFeatureModule, + checkoutImport + )?.getFirstAncestorByKindOrThrow(SyntaxKind.CallExpression); + if (!spartacusProvider) { + throw new Error('Could not find the spartacus provider'); + } + // remove the dynamic import + cleanupConfig(spartacusProvider); + saveAndFormat(checkoutFeatureModule); + + appTree = await schematicRunner + .runSchematicAsync( + 'ng-add', + { + ...defaultOptions, + name: 'schematics-test', + features: [DIGITAL_PAYMENTS_FEATURE_NAME], + }, + appTree + ) + .toPromise(); + }); + + it('should append the feature module after it, and not add a dynamic import to the feature module', () => { + const { program } = createProgram(appTree, appTree.root.path, buildPath); + const spartacusFeaturesModule = program.getSourceFileOrThrow( + spartacusFeaturesModulePath + ); + const checkoutFeatureModule = program.getSourceFileOrThrow( + checkoutFeatureModulePath + ); + expect(program.getSourceFile(checkoutWrapperModulePath)).toBeFalsy(); + expect(spartacusFeaturesModule.print()).toMatchSnapshot(); + expect(checkoutFeatureModule.print()).toMatchSnapshot(); + }); + }); +}); diff --git a/projects/schematics/src/wrapper-module/schema.json b/projects/schematics/src/wrapper-module/schema.json new file mode 100644 index 00000000000..623159acb09 --- /dev/null +++ b/projects/schematics/src/wrapper-module/schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "SpartacusSchematics", + "title": "Spartacus Schematics", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "$default": { + "$source": "projectName" + } + }, + "debug": { + "description": "Display additional details during the running process.", + "type": "boolean", + "default": false + }, + "markerModuleName": { + "type": "string", + "description": "The marker module for which to find or generate the wrapper module." + }, + "featureModuleName": { + "type": "string", + "description": "Name of the feature module which to add to the wrapper module." + } + }, + "required": ["markerModuleName", "featureModuleName"] +} diff --git a/projects/schematics/src/wrapper-module/schema.ts b/projects/schematics/src/wrapper-module/schema.ts new file mode 100644 index 00000000000..e818cec870f --- /dev/null +++ b/projects/schematics/src/wrapper-module/schema.ts @@ -0,0 +1,32 @@ +import { ExecutionOptions } from '@angular-devkit/schematics'; + +export interface Schema extends Partial { + /** + * The name of the project in which to execute schematics. + */ + project: string; + /** + * When enabled, prints the additional logs. + */ + debug?: boolean; + /** + * The marker module for which + * to find or generate a new wrapper module. + */ + markerModuleName: string; + /** + * Name of the feature module + * to add to the wrapper module. + */ + featureModuleName: string; + /** + * Internal options. + * Should not be set by the user. + */ + internal?: { + /** + * Path of the wrapper module. + */ + wrapperModulePath?: string; + }; +} diff --git a/projects/storefrontapp/src/app/spartacus/features/asm-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/asm/asm-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/asm-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/asm/asm-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/cart-base-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cart/cart-base-feature.module.ts similarity index 85% rename from projects/storefrontapp/src/app/spartacus/features/cart-base-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cart/cart-base-feature.module.ts index 206189e4f87..ee130a604b0 100644 --- a/projects/storefrontapp/src/app/spartacus/features/cart-base-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/cart/cart-base-feature.module.ts @@ -9,7 +9,7 @@ import { CART_BASE_FEATURE, MINI_CART_FEATURE, } from '@spartacus/cart/base/root'; -import { I18nConfig, provideConfig } from '@spartacus/core'; +import { provideConfig } from '@spartacus/core'; @NgModule({ imports: [CartBaseRootModule], @@ -20,12 +20,20 @@ import { I18nConfig, provideConfig } from '@spartacus/core'; module: () => import('@spartacus/cart/base').then((m) => m.CartBaseModule), }, + }, + }), + provideConfig({ + featureModules: { [MINI_CART_FEATURE]: { module: () => import('@spartacus/cart/base/components/mini-cart').then( (m) => m.MiniCartModule ), }, + }, + }), + provideConfig({ + featureModules: { [ADD_TO_CART_FEATURE]: { module: () => import('@spartacus/cart/base/components/add-to-cart').then( @@ -34,7 +42,7 @@ import { I18nConfig, provideConfig } from '@spartacus/core'; }, }, }), - provideConfig({ + provideConfig({ i18n: { resources: cartBaseTranslations, chunks: cartBaseTranslationChunksConfig, diff --git a/projects/storefrontapp/src/app/spartacus/features/import-export-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cart/cart-import-export-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/import-export-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cart/cart-import-export-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/quick-order-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cart/cart-quick-order-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/quick-order-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cart/cart-quick-order-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/saved-cart-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cart/cart-saved-cart-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/saved-cart-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cart/cart-saved-cart-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/wish-list-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cart/wish-list-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/wish-list-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cart/wish-list-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/cdc-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cdc/cdc-feature.module.ts similarity index 89% rename from projects/storefrontapp/src/app/spartacus/features/cdc-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cdc/cdc-feature.module.ts index 83d3678ed0f..90d936722ad 100644 --- a/projects/storefrontapp/src/app/spartacus/features/cdc-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/cdc/cdc-feature.module.ts @@ -1,10 +1,17 @@ import { NgModule } from '@angular/core'; import { CdcConfig, CdcRootModule, CDC_FEATURE } from '@spartacus/cdc/root'; -import { CmsConfig, provideConfig } from '@spartacus/core'; +import { provideConfig } from '@spartacus/core'; @NgModule({ imports: [CdcRootModule], providers: [ + provideConfig({ + featureModules: { + [CDC_FEATURE]: { + module: () => import('@spartacus/cdc').then((m) => m.CdcModule), + }, + }, + }), provideConfig({ cdc: [ { @@ -20,13 +27,6 @@ import { CmsConfig, provideConfig } from '@spartacus/core'; }, ], }), - provideConfig({ - featureModules: { - [CDC_FEATURE]: { - module: () => import('@spartacus/cdc').then((m) => m.CdcModule), - }, - }, - }), ], }) export class CdcFeatureModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/cds-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/cds/cds-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/cds-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/cds/cds-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/checkout-b2b-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/checkout-b2b-feature.module.ts deleted file mode 100644 index c6bc888948a..00000000000 --- a/projects/storefrontapp/src/app/spartacus/features/checkout-b2b-feature.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NgModule } from '@angular/core'; -import { - checkoutB2BTranslationChunksConfig, - checkoutB2BTranslations, -} from '@spartacus/checkout/b2b/assets'; -import { CheckoutB2BRootModule } from '@spartacus/checkout/b2b/root'; -import { - checkoutTranslationChunksConfig, - checkoutTranslations, -} from '@spartacus/checkout/base/assets'; -import { CHECKOUT_FEATURE } from '@spartacus/checkout/base/root'; -import { provideConfig } from '@spartacus/core'; - -@NgModule({ - imports: [CheckoutB2BRootModule], - providers: [ - provideConfig({ - i18n: { - resources: checkoutTranslations, - chunks: checkoutTranslationChunksConfig, - }, - }), - provideConfig({ - featureModules: { - [CHECKOUT_FEATURE]: { - module: () => - import('@spartacus/checkout/b2b').then((m) => m.CheckoutB2BModule), - }, - }, - i18n: { - resources: checkoutB2BTranslations, - chunks: checkoutB2BTranslationChunksConfig, - }, - }), - ], -}) -export class CheckoutB2BFeatureModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/checkout-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/checkout-feature.module.ts deleted file mode 100644 index cec122eaac7..00000000000 --- a/projects/storefrontapp/src/app/spartacus/features/checkout-feature.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NgModule } from '@angular/core'; -import { - checkoutTranslationChunksConfig, - checkoutTranslations, -} from '@spartacus/checkout/base/assets'; -import { - CheckoutRootModule, - CHECKOUT_FEATURE, -} from '@spartacus/checkout/base/root'; -import { provideConfig } from '@spartacus/core'; - -@NgModule({ - imports: [CheckoutRootModule], - providers: [ - provideConfig({ - featureModules: { - [CHECKOUT_FEATURE]: { - module: () => - import('@spartacus/checkout/base').then((m) => m.CheckoutModule), - }, - }, - i18n: { - resources: checkoutTranslations, - chunks: checkoutTranslationChunksConfig, - }, - }), - ], -}) -export class CheckoutFeatureModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/checkout-scheduled-replenishment-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/checkout/checkout-feature.module.ts similarity index 58% rename from projects/storefrontapp/src/app/spartacus/features/checkout-scheduled-replenishment-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/checkout/checkout-feature.module.ts index 58223534573..b0e6c79b9fb 100644 --- a/projects/storefrontapp/src/app/spartacus/features/checkout-scheduled-replenishment-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/checkout/checkout-feature.module.ts @@ -1,49 +1,72 @@ -import { NgModule } from '@angular/core'; +import { NgModule, Provider, Type } from '@angular/core'; import { checkoutB2BTranslationChunksConfig, checkoutB2BTranslations, } from '@spartacus/checkout/b2b/assets'; +import { CheckoutB2BRootModule } from '@spartacus/checkout/b2b/root'; import { checkoutTranslationChunksConfig, checkoutTranslations, } from '@spartacus/checkout/base/assets'; -import { CHECKOUT_FEATURE } from '@spartacus/checkout/base/root'; +import { + CheckoutRootModule, + CHECKOUT_FEATURE, +} from '@spartacus/checkout/base/root'; import { checkoutScheduledReplenishmentTranslationChunksConfig, checkoutScheduledReplenishmentTranslations, } from '@spartacus/checkout/scheduled-replenishment/assets'; import { CheckoutScheduledReplenishmentRootModule } from '@spartacus/checkout/scheduled-replenishment/root'; import { provideConfig } from '@spartacus/core'; +import { environment } from '../../../../environments/environment'; -@NgModule({ - imports: [CheckoutScheduledReplenishmentRootModule], - providers: [ +const extensionModules: Type[] = []; +const extensionProviders: Provider[] = []; + +if (environment.b2b) { + extensionModules.push( + CheckoutB2BRootModule, + CheckoutScheduledReplenishmentRootModule + ); + + extensionProviders.push( provideConfig({ i18n: { - resources: checkoutTranslations, - chunks: checkoutTranslationChunksConfig, + resources: checkoutB2BTranslations, + chunks: checkoutB2BTranslationChunksConfig, }, - }), + }) + ); + extensionProviders.push( provideConfig({ i18n: { - resources: checkoutB2BTranslations, - chunks: checkoutB2BTranslationChunksConfig, + resources: checkoutScheduledReplenishmentTranslations, + chunks: checkoutScheduledReplenishmentTranslationChunksConfig, }, - }), + }) + ); +} + +@NgModule({ + imports: [CheckoutRootModule, ...extensionModules], + providers: [ provideConfig({ featureModules: { [CHECKOUT_FEATURE]: { module: () => - import('@spartacus/checkout/scheduled-replenishment').then( - (m) => m.CheckoutScheduledReplenishmentModule + import('./checkout-wrapper.module').then( + (m) => m.CheckoutWrapperModule ), }, }, + }), + provideConfig({ i18n: { - resources: checkoutScheduledReplenishmentTranslations, - chunks: checkoutScheduledReplenishmentTranslationChunksConfig, + resources: checkoutTranslations, + chunks: checkoutTranslationChunksConfig, }, }), + ...extensionProviders, ], }) -export class CheckoutScheduledReplenishmentFeatureModule {} +export class CheckoutFeatureModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/checkout/checkout-wrapper.module.ts b/projects/storefrontapp/src/app/spartacus/features/checkout/checkout-wrapper.module.ts new file mode 100644 index 00000000000..926c6e2d9b0 --- /dev/null +++ b/projects/storefrontapp/src/app/spartacus/features/checkout/checkout-wrapper.module.ts @@ -0,0 +1,21 @@ +import { NgModule, Type } from '@angular/core'; +import { CheckoutB2BModule } from '@spartacus/checkout/b2b'; +import { CheckoutModule } from '@spartacus/checkout/base'; +import { CheckoutScheduledReplenishmentModule } from '@spartacus/checkout/scheduled-replenishment'; +import { DigitalPaymentsModule } from '@spartacus/digital-payments'; +import { environment } from '../../../../environments/environment'; + +const extensions: Type[] = []; + +if (environment.b2b) { + extensions.push(CheckoutB2BModule, CheckoutScheduledReplenishmentModule); +} + +if (environment.digitalPayments) { + extensions.push(DigitalPaymentsModule); +} + +@NgModule({ + imports: [CheckoutModule, ...extensions], +}) +export class CheckoutWrapperModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/digital-payments-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/digital-payments/digital-payments-feature.module.ts similarity index 50% rename from projects/storefrontapp/src/app/spartacus/features/digital-payments-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/digital-payments/digital-payments-feature.module.ts index 0431597fc67..adeb2af445e 100644 --- a/projects/storefrontapp/src/app/spartacus/features/digital-payments-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/digital-payments/digital-payments-feature.module.ts @@ -1,22 +1,12 @@ import { NgModule } from '@angular/core'; -import { CHECKOUT_FEATURE } from '@spartacus/checkout/base/root'; -import { CmsConfig, I18nConfig, provideConfig } from '@spartacus/core'; +import { I18nConfig, provideConfig } from '@spartacus/core'; import { dpTranslationChunksConfig, dpTranslations, } from '@spartacus/digital-payments/assets'; + @NgModule({ providers: [ - provideConfig({ - featureModules: { - [CHECKOUT_FEATURE]: { - module: () => - import('@spartacus/digital-payments').then( - (m) => m.DigitalPaymentsModule - ), - }, - }, - }), provideConfig({ i18n: { resources: dpTranslations, diff --git a/projects/storefrontapp/src/app/spartacus/features/epd-visualization-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/epd-visualization/epd-visualization-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/epd-visualization-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/epd-visualization/epd-visualization-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/order-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/order/order-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/order-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/order/order-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/administration-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/organization/organization-administration-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/administration-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/organization/organization-administration-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/order-approval-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/organization/organization-order-approval-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/order-approval-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/organization/organization-order-approval-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts deleted file mode 100644 index ed150247643..00000000000 --- a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-cpq-feature.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { provideConfig } from '@spartacus/core'; -import { configuratorTranslations } from '@spartacus/product-configurator/common/assets'; -import { - CpqConfiguratorRootModule, - PRODUCT_CONFIGURATOR_RULEBASED_FEATURE, - RulebasedConfiguratorRootModule, -} from '@spartacus/product-configurator/rulebased/root'; - -@NgModule({ - imports: [RulebasedConfiguratorRootModule, CpqConfiguratorRootModule], - providers: [ - provideConfig({ - featureModules: { - [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE]: { - module: () => - import('@spartacus/product-configurator/rulebased/cpq').then( - (m) => m.RulebasedCpqConfiguratorModule - ), - }, - }, - i18n: { - resources: configuratorTranslations, - }, - }), - ], -}) -export class ProductConfiguratorRulebasedCpqFeatureModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-rulebased-feature.module.ts similarity index 56% rename from projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-rulebased-feature.module.ts index 50497ecbe64..78de65c91c8 100644 --- a/projects/storefrontapp/src/app/spartacus/features/product-configurator-rulebased-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-rulebased-feature.module.ts @@ -1,23 +1,33 @@ -import { NgModule } from '@angular/core'; +import { NgModule, Type } from '@angular/core'; import { provideConfig } from '@spartacus/core'; import { configuratorTranslations } from '@spartacus/product-configurator/common/assets'; import { + CpqConfiguratorRootModule, PRODUCT_CONFIGURATOR_RULEBASED_FEATURE, RulebasedConfiguratorRootModule, } from '@spartacus/product-configurator/rulebased/root'; +import { environment } from '../../../../environments/environment'; + +const extensions: Type[] = []; + +if (environment.cpq) { + extensions.push(CpqConfiguratorRootModule); +} @NgModule({ - imports: [RulebasedConfiguratorRootModule], + imports: [RulebasedConfiguratorRootModule, ...extensions], providers: [ provideConfig({ featureModules: { [PRODUCT_CONFIGURATOR_RULEBASED_FEATURE]: { module: () => - import('@spartacus/product-configurator/rulebased').then( - (m) => m.RulebasedConfiguratorModule + import('./rulebased-configurator-wrapper.module').then( + (m) => m.RulebasedConfiguratorWrapperModule ), }, }, + }), + provideConfig({ i18n: { resources: configuratorTranslations, }, diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator-textfield-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-textfield-feature.module.ts similarity index 96% rename from projects/storefrontapp/src/app/spartacus/features/product-configurator-textfield-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-textfield-feature.module.ts index 8b8404744c6..581a93eb3ef 100644 --- a/projects/storefrontapp/src/app/spartacus/features/product-configurator-textfield-feature.module.ts +++ b/projects/storefrontapp/src/app/spartacus/features/product-configurator/product-configurator-textfield-feature.module.ts @@ -19,6 +19,8 @@ import { ), }, }, + }), + provideConfig({ i18n: { resources: configuratorTranslations, }, diff --git a/projects/storefrontapp/src/app/spartacus/features/product-configurator/rulebased-configurator-wrapper.module.ts b/projects/storefrontapp/src/app/spartacus/features/product-configurator/rulebased-configurator-wrapper.module.ts new file mode 100644 index 00000000000..837157be51b --- /dev/null +++ b/projects/storefrontapp/src/app/spartacus/features/product-configurator/rulebased-configurator-wrapper.module.ts @@ -0,0 +1,15 @@ +import { NgModule, Type } from '@angular/core'; +import { RulebasedConfiguratorModule } from '@spartacus/product-configurator/rulebased'; +import { RulebasedCpqConfiguratorModule } from '@spartacus/product-configurator/rulebased/cpq'; +import { environment } from '../../../../environments/environment'; + +const extensions: Type[] = []; + +if (environment.cpq) { + extensions.push(RulebasedCpqConfiguratorModule); +} + +@NgModule({ + imports: [RulebasedConfiguratorModule, ...extensions], +}) +export class RulebasedConfiguratorWrapperModule {} diff --git a/projects/storefrontapp/src/app/spartacus/features/bulk-pricing-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product/product-bulk-pricing-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/bulk-pricing-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/product/product-bulk-pricing-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/image-zoom-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product/product-image-zoom-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/image-zoom-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/product/product-image-zoom-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/variants-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/product/product-variants-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/variants-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/product/product-variants-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/qualtrics-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/qualtrics/qualtrics-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/qualtrics-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/qualtrics/qualtrics-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/smartedit-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/smartedit/smartedit-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/smartedit-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/smartedit/smartedit-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/storefinder-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/storefinder/storefinder-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/storefinder-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/storefinder/storefinder-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/tracking-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/tracking/tracking-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/tracking-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/tracking/tracking-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/features/user-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts similarity index 100% rename from projects/storefrontapp/src/app/spartacus/features/user-feature.module.ts rename to projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index db4ceb7c453..36bd58e2603 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -53,32 +53,29 @@ import { VideoModule, } from '@spartacus/storefront'; import { environment } from '../../environments/environment'; -import { AdministrationFeatureModule } from './features/administration-feature.module'; -import { AsmFeatureModule } from './features/asm-feature.module'; -import { BulkPricingFeatureModule } from './features/bulk-pricing-feature.module'; -import { CartBaseFeatureModule } from './features/cart-base-feature.module'; -import { CdcFeatureModule } from './features/cdc-feature.module'; -import { CdsFeatureModule } from './features/cds-feature.module'; -import { CheckoutFeatureModule } from './features/checkout-feature.module'; -import { CheckoutScheduledReplenishmentFeatureModule } from './features/checkout-scheduled-replenishment-feature.module'; -import { DigitalPaymentsFeatureModule } from './features/digital-payments-feature.module'; -import { EpdVisualizationFeatureModule } from './features/epd-visualization-feature.module'; -import { ImageZoomFeatureModule } from './features/image-zoom-feature.module'; -import { ImportExportFeatureModule } from './features/import-export-feature.module'; -import { OrderApprovalFeatureModule } from './features/order-approval-feature.module'; -import { OrderFeatureModule } from './features/order-feature.module'; -import { ProductConfiguratorRulebasedCpqFeatureModule } from './features/product-configurator-rulebased-cpq-feature.module'; -import { ProductConfiguratorRulebasedFeatureModule } from './features/product-configurator-rulebased-feature.module'; -import { ProductConfiguratorTextfieldFeatureModule } from './features/product-configurator-textfield-feature.module'; -import { QualtricsFeatureModule } from './features/qualtrics-feature.module'; -import { QuickOrderFeatureModule } from './features/quick-order-feature.module'; -import { SavedCartFeatureModule } from './features/saved-cart-feature.module'; -import { SmartEditFeatureModule } from './features/smartedit-feature.module'; -import { StorefinderFeatureModule } from './features/storefinder-feature.module'; -import { TrackingFeatureModule } from './features/tracking-feature.module'; -import { UserFeatureModule } from './features/user-feature.module'; -import { VariantsFeatureModule } from './features/variants-feature.module'; -import { WishListFeatureModule } from './features/wish-list-feature.module'; +import { AsmFeatureModule } from './features/asm/asm-feature.module'; +import { CartBaseFeatureModule } from './features/cart/cart-base-feature.module'; +import { ImportExportFeatureModule } from './features/cart/cart-import-export-feature.module'; +import { QuickOrderFeatureModule } from './features/cart/cart-quick-order-feature.module'; +import { SavedCartFeatureModule } from './features/cart/cart-saved-cart-feature.module'; +import { WishListFeatureModule } from './features/cart/wish-list-feature.module'; +import { CdsFeatureModule } from './features/cds/cds-feature.module'; +import { CheckoutFeatureModule } from './features/checkout/checkout-feature.module'; +import { DigitalPaymentsFeatureModule } from './features/digital-payments/digital-payments-feature.module'; +import { EpdVisualizationFeatureModule } from './features/epd-visualization/epd-visualization-feature.module'; +import { OrderFeatureModule } from './features/order/order-feature.module'; +import { AdministrationFeatureModule } from './features/organization/organization-administration-feature.module'; +import { OrderApprovalFeatureModule } from './features/organization/organization-order-approval-feature.module'; +import { ProductConfiguratorRulebasedFeatureModule } from './features/product-configurator/product-configurator-rulebased-feature.module'; +import { ProductConfiguratorTextfieldFeatureModule } from './features/product-configurator/product-configurator-textfield-feature.module'; +import { BulkPricingFeatureModule } from './features/product/product-bulk-pricing-feature.module'; +import { ImageZoomFeatureModule } from './features/product/product-image-zoom-feature.module'; +import { VariantsFeatureModule } from './features/product/product-variants-feature.module'; +import { QualtricsFeatureModule } from './features/qualtrics/qualtrics-feature.module'; +import { SmartEditFeatureModule } from './features/smartedit/smartedit-feature.module'; +import { StorefinderFeatureModule } from './features/storefinder/storefinder-feature.module'; +import { TrackingFeatureModule } from './features/tracking/tracking-feature.module'; +import { UserFeatureModule } from './features/user/user-feature.module'; const featureModules = []; @@ -90,23 +87,10 @@ if (environment.b2b) { ); } -let CheckoutFeature = CheckoutFeatureModule; - -if (environment.b2b) { - CheckoutFeature = CheckoutScheduledReplenishmentFeatureModule; -} - -if (environment.cdc) { - featureModules.push(CdcFeatureModule); -} if (environment.cds) { featureModules.push(CdsFeatureModule); } -if (environment.cpq) { - featureModules.push(ProductConfiguratorRulebasedCpqFeatureModule); -} else { - featureModules.push(ProductConfiguratorRulebasedFeatureModule); -} + if (environment.digitalPayments) { featureModules.push(DigitalPaymentsFeatureModule); } @@ -197,7 +181,7 @@ if (environment.epdVisualization) { OrderFeatureModule, - CheckoutFeature, + CheckoutFeatureModule, TrackingFeatureModule, @@ -210,9 +194,11 @@ if (environment.epdVisualization) { SmartEditFeatureModule, VariantsFeatureModule, - ProductConfiguratorTextfieldFeatureModule, ImageZoomFeatureModule, + ProductConfiguratorTextfieldFeatureModule, + ProductConfiguratorRulebasedFeatureModule, + ...featureModules, ], }) diff --git a/tools/schematics/testing.ts b/tools/schematics/testing.ts index 9504e32d448..d72c906f863 100644 --- a/tools/schematics/testing.ts +++ b/tools/schematics/testing.ts @@ -20,7 +20,12 @@ const featureLibsFolders: string[] = [ 'user', ]; -const integrationLibsFolders: string[] = ['cdc', 'cds', 'digital-payments', 'epd-visualization']; +const integrationLibsFolders: string[] = [ + 'cdc', + 'cds', + 'digital-payments', + 'epd-visualization', +]; const commands = [ 'publish', @@ -132,23 +137,32 @@ function buildSchematicsAndPublish(buildCmd: string): void { } function testAllSchematics(): void { - execSync('yarn --cwd projects/schematics run test --coverage', { - stdio: 'inherit', - }); - - featureLibsFolders.forEach((lib) => - execSync(`yarn --cwd feature-libs/${lib} run test:schematics --coverage`, { + try { + execSync('yarn --cwd projects/schematics run test --coverage', { stdio: 'inherit', - }) - ); - integrationLibsFolders.forEach((lib) => - execSync( - `yarn --cwd integration-libs/${lib} run test:schematics --coverage`, - { - stdio: 'inherit', - } - ) - ); + }); + + featureLibsFolders.forEach((lib) => + execSync( + `yarn --cwd feature-libs/${lib} run test:schematics --coverage`, + { + stdio: 'inherit', + } + ) + ); + integrationLibsFolders.forEach((lib) => + execSync( + `yarn --cwd integration-libs/${lib} run test:schematics --coverage`, + { + stdio: 'inherit', + } + ) + ); + } catch (e) { + console.error(e); + beforeExit(); + process.exit(); + } } async function executeCommand(command: Command): Promise { @@ -210,21 +224,26 @@ async function program(): Promise { executeCommand(response.command); } } catch (e) { - console.log(e); + console.error(e); beforeExit(); process.exit(); } } -program(); - // Handle killing the script process.once('SIGINT', function () { beforeExit(); process.exit(); }); +process.on('SIGHUP', function () { + beforeExit(); + process.exit(); +}); + process.once('SIGTERM', function () { beforeExit(); process.exit(); }); + +program();