diff --git a/README.md b/README.md
index 4a522a048..88d94affc 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,8 @@ Maven template that creates a minimal, best-practices-based Adobe Experience Man
* **Responsive Layout:** On templates or individual pages, [define how the elements reflow](https://docs.adobe.com/content/help/en/experience-manager-65/authoring/siteandpage/responsive-layout.html) for the defined breakpoints.
* **Header and Footer:** Assemble and localize them without code, using the [localization features of the components](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/get-started/localization.html).
* **Style System:** Avoid building custom components by allowing authors to [apply different styles](https://docs.adobe.com/content/help/en/experience-manager-learn/getting-started-wknd-tutorial-develop/style-system.html) to them.
-* **Front-End Build:** Front-end devs can [mock AEM pages](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend.html#webpack-dev-server) and [build client libraries](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend.html) with Webpack, TypeScript, and SASS.
+* **Front-End Build:** Front-end devs can [mock AEM pages](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend.html#webpack-dev-server) and [build client libraries](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend.html) with Webpack, TypeScript, and SASS.
+* **Decoupled Front-End:** When chosing the frontend module to be decoupled, the project is preconfigured to use the [AEMaaCS Frontend Pipeline](https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/sites/administering/site-creation/enable-front-end-pipeline.html). See [the AEM React SPA](https://github.com/adobe/aem-react-spa) template for more details how to get started with a decoupled frontend module using React.
* **WebApp-Ready:** For sites using [React](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend-react.html) or [Angular](https://docs.adobe.com/content/help/en/experience-manager-core-components/using/developing/archetype/uifrontend-angular.html), use the [SPA SDK](https://docs.adobe.com/content/help/en/experience-manager-64/developing/headless/spas/spa-architecture.html) to retain [in-context authoring of the app](https://docs.adobe.com/content/help/en/experience-manager-learn/sites/spa-editor/spa-editor-framework-feature-video-use.html).
* **Commerce Enabled:** For projects that want to use Commerce Integration Framework ([CIF](https://github.com/adobe/aem-core-cif-components)) to integrate with commerce solutions like Magento.
* **Forms Enabled:** For projects that want to use ([Forms](https://github.com/adobe/aem-core-forms-components)).
@@ -76,7 +77,7 @@ Name | Default | Description
`aemVersion` | `cloud` | Target AEM version (can be `cloud` for [AEM as a Cloud Service](https://docs.adobe.com/content/help/en/experience-manager-cloud-service/landing/home.html); or `6.5.5` for [Adobe Managed Services](https://github.com/adobe/aem-project-archetype/tree/master/src/main/archetype/dispatcher.ams) or on-premise).
`sdkVersion` | `latest` | When `aemVersion=cloud` an [SDK](https://docs.adobe.com/content/help/en/experience-manager-cloud-service/implementing/developing/aem-as-a-cloud-service-sdk.html) version can be specified (e.g. `2020.02.2265.20200217T222518Z-200130`).
`includeDispatcherConfig` | `y` | Includes a dispatcher configuration either for cloud or for AMS/on-premise, depending of the value of `aemVersion` (can be `y` or `n`).
-`frontendModule` | `general` | Includes a Webpack frontend build module that generates the client libraries (can be `general` or `none` for regular sites; can be `angular` or `react` for a Single Page App that implements the [SPA Editor](https://docs.adobe.com/content/help/en/experience-manager-65/developing/headless/spas/spa-overview.html)).
+`frontendModule` | `general` | Includes a Webpack frontend build module that generates the client libraries (can be `general` or `none` for regular sites; can be `angular`, `react` or `decoupled` for a Single Page App that implements the [SPA Editor](https://docs.adobe.com/content/help/en/experience-manager-65/developing/headless/spas/spa-overview.html). In the later case the project will be preconfigured to use the [AEM as a Cloud Service Frontend Pipeline](https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/sites/administering/site-creation/enable-front-end-pipeline.html)).
`language` | `en` | Language code (ISO 639-1) to create the content structure from (e.g. `en`, `deu`).
`country` | `us` | Country code (ISO 3166-1) to create the content structure from (e.g. `US`).
`singleCountry` | `y` | Includes a language-master content structure (can be `y`, or `n`).
diff --git a/src/main/archetype/pom.xml b/src/main/archetype/pom.xml
index 05d173a46..5017088ec 100644
--- a/src/main/archetype/pom.xml
+++ b/src/main/archetype/pom.xml
@@ -24,7 +24,7 @@
#set( $symbol_escape = '\' )
#set( $startbrace = "{" )
#set( $endbrace = "}" )
-#set( $isSpaProject = $frontendModule == "angular" || $frontendModule == "react" )
+#set( $isSpaProject = $frontendModule == "angular" || $frontendModule == "react" || $frontendModule == "decoupled" )
#set( $includeClassifierOnUberJar = $aemVersion.matches("6\.5\.[0-5]") )
4.0.0
@@ -38,7 +38,7 @@
all
core
-#if ( $frontendModule != "none" )
+#if ( $frontendModule != "none" && $frontendModule != "decoupled" )
ui.frontend
#end
#if ( $includeFormsheadless == "y" )
@@ -89,7 +89,7 @@
UTF-8
UTF-8
#if ( $isSpaProject )
- 1.3.12
+ 1.3.16
#end
#if ( $frontendModule == "angular" )
+#if ( $frontendModule != "decoupled" )
-
+#end
+
+
diff --git a/src/main/archetype/ui.apps/src/main/content/jcr_root/apps/__appId__/components/xfpage/customheaderlibs.html b/src/main/archetype/ui.apps/src/main/content/jcr_root/apps/__appId__/components/xfpage/customheaderlibs.html
index ded265079..e45510b38 100644
--- a/src/main/archetype/ui.apps/src/main/content/jcr_root/apps/__appId__/components/xfpage/customheaderlibs.html
+++ b/src/main/archetype/ui.apps/src/main/content/jcr_root/apps/__appId__/components/xfpage/customheaderlibs.html
@@ -31,8 +31,16 @@
+#if ( $frontentModule != "decoupled" )
+#end
+
+
+
+
#else
@@ -45,4 +53,12 @@
-#end
\ No newline at end of file
+
+
+
+
+#end
+
diff --git a/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/_sling_configs/.content.xml b/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/_sling_configs/.content.xml
index dc27b4f7b..47db4c560 100644
--- a/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/_sling_configs/.content.xml
+++ b/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/_sling_configs/.content.xml
@@ -1,5 +1,5 @@
-
+#if ( $frontendModule == 'decoupled')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#end
\ No newline at end of file
diff --git a/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/settings/wcm/policies/.content.xml b/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/settings/wcm/policies/.content.xml
index 8305357e3..d43e4110b 100644
--- a/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/settings/wcm/policies/.content.xml
+++ b/src/main/archetype/ui.content/src/main/content/jcr_root/conf/__appId__/settings/wcm/policies/.content.xml
@@ -423,8 +423,10 @@
+#if ( $frontendModule != "decoupled" )
+ clientlibs="[${appId}.${frontendModule}]"
+#end
+ sling:resourceType="wcm/core/components/policy/policy">
#end
@@ -435,7 +437,9 @@
jcr:primaryType="nt:unstructured"
jcr:title="${appTitle} App Policy"
sling:resourceType="wcm/core/components/policy/policy"
+#if ( $frontendModule != "decoupled" )
clientlibs="[${appId}.${frontendModule}]"
+#end
isRoot="{Boolean}true"
structureDepth="3"
/>
@@ -611,7 +615,7 @@
jcr:primaryType="nt:unstructured"
jcr:title="SPA Content"
sling:resourceType="wcm/core/components/policy/policy"
- components="[/libs/wcm/foundation/components/responsivegrid,/apps/${appId}/components/text]">
+ components="[group:${appTitle} - Content]">
#if ( ($includeForms == "y" or $includeFormsenrollment == "y" or $includeFormscommunications == "y") and $aemVersion == "cloud")
diff --git a/src/main/archetype/ui.content/src/main/content/jcr_root/content/__appId__/.content.xml b/src/main/archetype/ui.content/src/main/content/jcr_root/content/__appId__/.content.xml
index c8c86a961..111178b55 100644
--- a/src/main/archetype/ui.content/src/main/content/jcr_root/content/__appId__/.content.xml
+++ b/src/main/archetype/ui.content/src/main/content/jcr_root/content/__appId__/.content.xml
@@ -4,10 +4,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/META-INF/archetype-post-generate.groovy b/src/main/resources/META-INF/archetype-post-generate.groovy
index f49678753..749110c91 100644
--- a/src/main/resources/META-INF/archetype-post-generate.groovy
+++ b/src/main/resources/META-INF/archetype-post-generate.groovy
@@ -86,13 +86,15 @@ if (aemVersion == "cloud") {
buildContentSkeleton(uiContentPackage, uiAppsPackage, singleCountry, appId, language, country)
cleanUpFrontendModule(frontendModules, frontendModule, rootPom, rootDir, appsFolder, confFolder, configFolder, contentFolder,enableSSR, includeCommerce)
-if ( includeDispatcherConfig == "n"){
+if (includeDispatcherConfig == "n") {
// remove the unneeded config file
+ def rrfConfig;
if (aemVersion.startsWith("6.4")) {
- assert new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.config").delete()
+ rrfConfig = new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.config")
} else {
- assert new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.cfg.json").delete()
+ rrfConfig = new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.cfg.json")
}
+ assert !rrfConfig.exists() || rrfConfig.delete();
} else {
def source;
if (aemVersion == 'cloud') {
@@ -275,12 +277,12 @@ def cleanUpFrontendModule(frontendModules, optionFrontendModule, rootPom, rootDi
}
// Rename selected frontend module (e.g. "ui.frontend.angular" -> "ui.frontend")
- if (optionFrontendModule != "none") {
+ if (optionFrontendModule != "none" && optionFrontendModule != "decoupled") {
assert new File(rootDir, "ui.frontend.$optionFrontendModule").renameTo(new File(rootDir, "ui.frontend"))
}
// Not generating SPA: Delete SPA-specific files
- if (optionFrontendModule != "angular" && optionFrontendModule != "react") {
+ if (optionFrontendModule != "angular" && optionFrontendModule != "react" && optionFrontendModule != "decoupled") {
// Delete app component
assert new File("$appsFolder/components/structure/spa").deleteDir()
assert new File("$appsFolder/components/xfpage/body.html").delete()
@@ -298,6 +300,7 @@ def cleanUpFrontendModule(frontendModules, optionFrontendModule, rootPom, rootDi
assert new File("$confFolder/settings/wcm/template-types/remote-page").deleteDir()
// Delete SPA content
+ assert new File("$contentFolder/language-masters/en/home").deleteDir()
assert new File("$contentFolder/us/en/home").deleteDir()
}else{
@@ -312,13 +315,21 @@ def cleanUpFrontendModule(frontendModules, optionFrontendModule, rootPom, rootDi
}
// Generating SPA: Delete non-SPA specific files
- if (optionFrontendModule == "angular" || optionFrontendModule == "react") {
+ if (optionFrontendModule == "angular" || optionFrontendModule == "react" || optionFrontendModule == "decoupled") {
assert new File("$confFolder/settings/wcm/templates/page-content").deleteDir()
assert new File("$confFolder/settings/wcm/template-types/page").deleteDir()
- if(enableSSR == "n"){
+ // remove JcrResourceResolverFactoryImpl configuration as Sling Mappings do not work with SPA yet
+ for (def rrfConfig in [
+ new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.cfg.json"),
+ new File("$configFolder/config.publish/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.config")
+ ]) {
+ assert !rrfConfig.exists() || rrfConfig.delete()
+ }
+
+ if (enableSSR == "n") {
- if(optionFrontendModule == "react"){
+ if (optionFrontendModule == "react") {
//cleanup IO runtime related files from react module
assert new File(rootDir, "ui.frontend/webpack.config.express.js").delete();
assert new File(rootDir, "ui.frontend/webpack.config.adobeio.js").delete();
@@ -326,7 +337,7 @@ def cleanUpFrontendModule(frontendModules, optionFrontendModule, rootPom, rootDi
assert new File(rootDir, "ui.frontend/src/server").deleteDir();
assert new File(rootDir, "ui.frontend/actions").deleteDir();
assert new File(rootDir, "ui.frontend/scripts").deleteDir();
- }else if(optionFrontendModule == "angular"){
+ } else if (optionFrontendModule == "angular") {
assert new File(rootDir, "ui.frontend/server.ts").delete();
assert new File(rootDir, "ui.frontend/serverless.ts").delete();
assert new File(rootDir, "ui.frontend/manifest.yml").delete();
@@ -337,6 +348,11 @@ def cleanUpFrontendModule(frontendModules, optionFrontendModule, rootPom, rootDi
}
}
+
+ if (optionFrontendModule == "decoupled") {
+ // remove clientlibs for decoupled frontend
+ assert new File("$appsFolder/clientlibs").deleteDir();
+ }
}
}
diff --git a/src/test/resources/projects/frontend-decoupled/archetype.properties b/src/test/resources/projects/frontend-decoupled/archetype.properties
new file mode 100644
index 000000000..284959929
--- /dev/null
+++ b/src/test/resources/projects/frontend-decoupled/archetype.properties
@@ -0,0 +1,27 @@
+groupId=archetype.it
+artifactId=testing-frontend-decoupled
+appId=testing-frontend-decoupled
+package=it.pkg
+version=0.1-SNAPSHOT
+appTitle=Test General Decoupled Project
+aemVersion=cloud
+sdkVersion=latest
+language=en
+country=us
+singleCountry=n
+frontendModule=decoupled
+includeExamples=n
+includeErrorHandler=n
+includeDispatcherConfig=n
+includeCommerce=n
+commerceEndpoint=https://hostname.com/grapql
+includeForms=n
+includeFormsenrollment=n
+includeFormscommunications=n
+sdkFormsVersion=latest
+datalayer=${datalayer}
+amp=${amp}
+enableDynamicMedia=y
+enableSSR=n
+precompiledScripts=n
+includeFormsheadless=n
\ No newline at end of file
diff --git a/src/test/resources/projects/frontend-decoupled/goal.txt b/src/test/resources/projects/frontend-decoupled/goal.txt
new file mode 100644
index 000000000..7c32f5598
--- /dev/null
+++ b/src/test/resources/projects/frontend-decoupled/goal.txt
@@ -0,0 +1 @@
+install