diff --git a/.internals/icons/inkscape-banner_1200x1200.svg b/.internals/icons/inkscape-banner_1200x1200.svg new file mode 100644 index 00000000..57024df2 --- /dev/null +++ b/.internals/icons/inkscape-banner_1200x1200.svg @@ -0,0 +1,225 @@ + +(Holloway) Chew, Kean Ho's AutomataCI Banner(Holloway) Chew, Kean Ho's AutomataCI BannerSun, 27 Oct 2024 09:42:34 +0000(Holloway) Chew, Kean HoCC-BY-ND-4.0(Holloway) Chew, Kean Hochewkeanho-automatacihttps://github.com/ChewKeanHo/AutomataCI/ApplicationEnglishbannerautomatacicibanner, automataci, ciThe banner promoting (Holloway) Chew, Kean Ho's AutomataCI. diff --git a/.internals/icons/inkscape-banner_1200x630.svg b/.internals/icons/inkscape-banner_1200x630.svg new file mode 100644 index 00000000..a4d7fb31 --- /dev/null +++ b/.internals/icons/inkscape-banner_1200x630.svg @@ -0,0 +1,225 @@ + +(Holloway) Chew, Kean Ho's AutomataCI Banner(Holloway) Chew, Kean Ho's AutomataCI BannerSun, 27 Oct 2024 09:42:34 +0000(Holloway) Chew, Kean HoCC-BY-ND-4.0(Holloway) Chew, Kean Hochewkeanho-automatacihttps://github.com/ChewKeanHo/AutomataCI/ApplicationEnglishbannerautomatacicibanner, automataci, ciThe banner promoting (Holloway) Chew, Kean Ho's AutomataCI. diff --git a/.internals/icons/inkscape-banner_630x1200.svg b/.internals/icons/inkscape-banner_630x1200.svg new file mode 100644 index 00000000..2a0eb46e --- /dev/null +++ b/.internals/icons/inkscape-banner_630x1200.svg @@ -0,0 +1,225 @@ + +(Holloway) Chew, Kean Ho's AutomataCI Banner(Holloway) Chew, Kean Ho's AutomataCI BannerSun, 27 Oct 2024 09:42:34 +0000(Holloway) Chew, Kean HoCC-BY-ND-4.0(Holloway) Chew, Kean Hochewkeanho-automatacihttps://github.com/ChewKeanHo/AutomataCI/ApplicationEnglishbannerautomatacicibanner, automataci, ciThe banner promoting (Holloway) Chew, Kean Ho's AutomataCI. diff --git a/Angular/.ci/build_unix-any.sh b/Angular/.ci/build_unix-any.sh index 0e5571ef..5bbae10a 100644 --- a/Angular/.ci/build_unix-any.sh +++ b/Angular/.ci/build_unix-any.sh @@ -1,6 +1,7 @@ #!/bin/sh # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at: @@ -46,7 +47,7 @@ fi I18N_Build "$PROJECT_ANGULAR" __current_path="$PWD" && cd "${PROJECT_PATH_ROOT}/${PROJECT_ANGULAR}" -ANGULAR_Build +./build.sh.ps1 ___process=$? cd "$__current_path" && unset __current_path if [ $___process -ne 0 ]; then diff --git a/Angular/.ci/build_windows-any.ps1 b/Angular/.ci/build_windows-any.ps1 index 34318d58..0e3da71c 100644 --- a/Angular/.ci/build_windows-any.ps1 +++ b/Angular/.ci/build_windows-any.ps1 @@ -1,5 +1,6 @@ # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at: @@ -20,6 +21,7 @@ if (-not (Test-Path -Path $env:PROJECT_PATH_ROOT)) { } . "${env:LIBS_AUTOMATACI}\services\io\fs.ps1" +. "${env:LIBS_AUTOMATACI}\services\io\os.ps1" . "${env:LIBS_AUTOMATACI}\services\i18n\translations.ps1" . "${env:LIBS_AUTOMATACI}\services\compilers\angular.ps1" @@ -46,7 +48,7 @@ if ($___process -ne 0) { $null = I18N-Build "${env:PROJECT_ANGULAR}" $__current_path = Get-Location $null = Set-Location "${env:PROJECT_PATH_ROOT}\${env:PROJECT_ANGULAR}" -$___process = ANGULAR-Build +$___process = OS-Exec "./build.sh.ps1" $null = Set-Location "${__current_path}" $null = Remove-Variable __current_path if ($___process -ne 0) { diff --git a/Angular/.ci/clean_unix-any.sh b/Angular/.ci/clean_unix-any.sh new file mode 100644 index 00000000..ed970d5f --- /dev/null +++ b/Angular/.ci/clean_unix-any.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Copyright 2024 (Holloway) Chew, Kean Ho +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at: +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + + + + +# initialize +if [ "$PROJECT_PATH_ROOT" = "" ]; then + >&2 printf "[ ERROR ] - Please run from automataCI/ci.sh.ps1 instead!\n" + return 1 +fi + +. "${LIBS_AUTOMATACI}/services/io/fs.sh" + + + + +# execute +__current_path="$PWD" +cd "${PROJECT_PATH_ROOT}/${PROJECT_ANGULAR}" + +FS_Remove_Silently "dist" +FS_Remove_Silently "node_modules" + +cd "$__current_path" +unset __current_path + + + + +# report status +return 0 diff --git a/Angular/.ci/clean_windows-any.ps1 b/Angular/.ci/clean_windows-any.ps1 new file mode 100644 index 00000000..ebce945f --- /dev/null +++ b/Angular/.ci/clean_windows-any.ps1 @@ -0,0 +1,42 @@ +# Copyright 2024 (Holloway) Chew, Kean Ho +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at: +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + + + +# initialize +if (-not (Test-Path -Path $env:PROJECT_PATH_ROOT)) { + Write-Error "[ ERROR ] - Please run from automataCI\ci.sh.ps1 instead!`n" + return 1 +} + +. "${env:LIBS_AUTOMATACI}\services\io\fs.ps1" + + + + +# execute +$__current_path = Get-Location +$null = Set-Location "${env:PROJECT_PATH_ROOT}\${env:PROJECT_ANGULAR}" + +$null = FS-Remove-Silently "dist" +$null = FS-Remove-Silently "node_modules" + +$null = Set-Location "${__current_path}" +$null = Remove-Variable __current_path + + + + +# report status +return 0 diff --git a/Angular/.ci/materialize_unix-any.sh b/Angular/.ci/materialize_unix-any.sh index 2a67a691..5ccbc69d 100644 --- a/Angular/.ci/materialize_unix-any.sh +++ b/Angular/.ci/materialize_unix-any.sh @@ -1,6 +1,7 @@ #!/bin/sh # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at: @@ -38,7 +39,7 @@ fi I18N_Build "$PROJECT_ANGULAR" __current_path="$PWD" && cd "${PROJECT_PATH_ROOT}/${PROJECT_ANGULAR}" -ANGULAR_Build +./build.sh.ps1 ___process=$? cd "$__current_path" && unset __current_path if [ $___process -ne 0 ]; then diff --git a/Angular/.ci/materialize_windows-any.ps1 b/Angular/.ci/materialize_windows-any.ps1 index fc9501c7..ea895b3d 100644 --- a/Angular/.ci/materialize_windows-any.ps1 +++ b/Angular/.ci/materialize_windows-any.ps1 @@ -1,5 +1,6 @@ # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at: @@ -39,7 +40,7 @@ if ($___process -ne 0) { $null = I18N-Build "${env:PROJECT_ANGULAR}" $__current_path = Get-Location $null = Set-Location "${env:PROJECT_PATH_ROOT}\${env:PROJECT_ANGULAR}" -$___process = Angular-Build +$___process = OS-Exec "./build.sh.ps1" $null = Set-Location "${__current_path}" $null = Remove-Variable __current_path if ($___process -ne 0) { diff --git a/Angular/.ci/prepare_unix-any.sh b/Angular/.ci/prepare_unix-any.sh index 95cf2688..cbd1631e 100644 --- a/Angular/.ci/prepare_unix-any.sh +++ b/Angular/.ci/prepare_unix-any.sh @@ -1,6 +1,7 @@ #!/bin/sh # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at: diff --git a/Angular/.ci/prepare_windows-any.ps1 b/Angular/.ci/prepare_windows-any.ps1 index 712970f6..48c28f27 100644 --- a/Angular/.ci/prepare_windows-any.ps1 +++ b/Angular/.ci/prepare_windows-any.ps1 @@ -1,5 +1,6 @@ # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at: diff --git a/Angular/.ci/test_unix-any.sh b/Angular/.ci/test_unix-any.sh index 27796a0f..10d5306f 100644 --- a/Angular/.ci/test_unix-any.sh +++ b/Angular/.ci/test_unix-any.sh @@ -1,6 +1,7 @@ #!/bin/sh # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at: @@ -50,7 +51,7 @@ else return 1 fi - CHROME_BIN="${___browser}" ANGULAR_Test + CHROME_BIN="${___browser}" ./test.sh.ps1 if [ $? -ne 0 ]; then I18N_Run_Failed return 1 diff --git a/Angular/.ci/test_windows-any.ps1 b/Angular/.ci/test_windows-any.ps1 index 53b5c1a9..e7966bb5 100644 --- a/Angular/.ci/test_windows-any.ps1 +++ b/Angular/.ci/test_windows-any.ps1 @@ -1,5 +1,6 @@ # Copyright 2023 (Holloway) Chew, Kean Ho # +# # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at: @@ -51,7 +52,7 @@ if ($(OS-Is-Run-Simulated) -eq 0) { } $env:CHROME_BIN = $___browser - $___process = ANGULAR-Test + $___process = OS-Exec "./test.sh.ps1" if ($___process -ne 0) { $null = I18N-Run-Failed return 1 diff --git a/Angular/README.md b/Angular/README.md index 42442fc9..422d2d30 100644 --- a/Angular/README.md +++ b/Angular/README.md @@ -1,29 +1,79 @@ # AutomataCI Static Site Generator Angular Setup -This document is mainly for Angular developer to understand what is going on -with the organization here. Most of the resources are the same. +This document is mainly for the project developers including junior role to +understand what is going on in the workspace and how to operate it. +Most of the Angular resources should be the same. ## Directory Structures -Unlike what was recommended by Angular, AutomataCI prepares 2 separate +Unlike what was recommended by Angular, AutomataCI prepared 2 clearly separated components directories: 1. `contents/` - organize the pages 2. `services/` - where your libraries and components stays +3. `assets/` - any static files located at the root of the site. -The `contents/` structures the website page hirarchy and imports the `services/` -libraries to construct the page. +The root directory is programmed to be here, where all the `app.*.ts` and +`main.*.ts` are located. + +The `contents/` component directory structures the website page hirarchy. +Each page imports component libraries from the `services/` directory for +constructing its content. All components in both directories are both using +Angular Components to operate seamlessly. + +For internationalization (i18n), it is best to keep it as service libraries +while retaining the `contents/` directory as the page rendering template. You +can pass the language code using the `app.routes.ts` routing mechanism and +have them rendered accordingly. + +To avoid path conflicts, you should always check the `assets/` availability +before creating the content inside `content/` directory. + +For better separation of concerns: + +1. `content/` components is recommended to have `PAGE_` prefix for clarity +2. `service/` components can remain to be anything but is best to be organized + into directory for later module construction and etc. + + + + +## Operations + +Due to Angular's limitation (dating Angular 18), You are **strongly advised** +to use the prepared Polygot shell for commands: + +``` +$ cd workspace/ # get into your Angular workspace as current directory + +$ # run any of the following matching your intention: +$ ./serve.sh.ps1 # for development +$ ./test.sh.ps1 # for test run +$ ./build.sh.ps1 # for build +``` + +This is mainly due to Angular does not have any pre-initialization function +that can setup the workspace's critical data files dynamically +(example: `assets/manifest.webmanifest`, `assets/CNAME`, and etc) before the +actual start-up. To workaround this challenge, a 2-steps execution is done +inside these scripts where the `init.sh.ps1` (sourced by others) is +responsible for building and updating these critical data files using the +Angular server-side-rendering capability. + +These are Polygot scripts which means it should work for both UNIX and Windows +operating system natively. ## Server-Side Rendering (SSR) or Static Site Generation (SSG) First -There is a high chance this project is likely being used to generate JAM Stack. -Hence, AutomataCI prioritizes the SSR and SSG (pre-rendering) facilities. +AutomataCI prioritizes the SSR and SSG (pre-rendering) facilities over other +modes. There is a high chance this project is likely being used to generate +JAM stack static website. @@ -31,49 +81,218 @@ Hence, AutomataCI prioritizes the SSR and SSG (pre-rendering) facilities. ## Setting website Base URL To set the Base URL, make sure you update the `baseHref` and `deployUrl` data -inside `angular.json`: +inside the `angular.json` as follows: ``` -diff --git a/angular/angular.json b/angular/angular.json -index 5379d9e..fb2e8fa 100644 ---- a/angular/angular.json -+++ b/angular/angular.json -@@ -46,6 +46,8 @@ - }, - "configurations": { - "production": { -+ "baseHref": "https://www.example.com/", -+ "deployUrl": "https://www.example.com/", - "budgets": [ - { - "type": "initial", +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "app": { + ... + "architect": { + "build": { + ... + "configurations": { + "production": { + "baseHref": "https://www.example.com/", + "deployUrl": "https://www.example.com/", + ... + ... +} ``` -Then make sure you edit `assets/CNAME` and add only the domain name inside. This -is for GitHub or GitLab authentication use. +`baseHref` is for the website base URL while `deployUrl` is for the asset-only +base URL. + +**DO NOT PASS ANY OF THEM IN VIA ARGUMENTS**. The workspace initialization is +sourced `angular.json` only. Failure can cause unknown and time-consuming +concequences. + + + + +## PWA Web Manifest & CNAME + +By default, AutomataCI prepared the workspace to generate their critical data +files dynamically using the 2-steps operational workaround. + +To modify them, look for these generator files and modify accordingly: + +* `init.pwa.ts` - for generating `assets/manifest.webmanifest` file. +* `init.cname.ts` - for generating the `assets/CNAME` file. + + + + +## Site-Level Metadata + +By default, AutomataCI prepared a site-level metadata file for default values +rendering purposes located at: + +* `init.metadata.ts` - a constant object holding the site-level data. + +This affects a lot of built-in facilities like `assets/manifest.webmanifest` and +page rendering. + + + + +## Site Logos & Favicons + +This workspace comes with a default placement for logos & favicons +pre-programmed located at `assets/logos/` directory. Updating all the graphic +files will update the project entirely. + +These logos are reusable outside of favicon usage especially those `.svg` +format. + +To add/remove a logo, look for: + +1. `init.pwa.ts` web manifest generator. +2. `app.html` head section. + + + + +## Update Screenshots + +Baseline PWA requires 3 basic screenshots located inside `assets/screenshots/` +for: + +* `wide` form factor (recommended `1200x630` size) +* `narrow` form factor (recommended `630x1200` size) +* any form factor (recommended `1200x1200` size) + +To add/remove a logo, look for `init.pwa.ts` web manifest generator. For more +technical specifications, please refer to the following: + +1. https://web.dev/articles/add-manifest +2. https://developer.mozilla.org/en-US/docs/Web/Manifest/screenshots + + + + +## Open-Graph Metadata + +By default, AutomataCI setup the default image thumbnails located inside the +`assets/thumbnails/` directory. These images serve as the fall back images +in case the page-level ones failed. + +By practice, these Open-Graph metadata must be updated at page-level. + +Recommended media dimension would be: + +1. `1200x630` - horizontal widescreen presentation +2. `1200x1200` - square presentation +3. `480x480` - fallback presentation + +You are free to alter the thumbnails located in `app.html` head section. + +For image, please use: + +``` + + + + + +``` + +For video, please use: + +``` + + + + + +``` + +For audio, please use: + +``` + + +``` + +For more technical specification, please refer to the following: + +1. https://ogp.me/ + + + + +## LD+JSON Search Engine Optimization Schematic Data + +By default, AutomataCI only setup the default empty tag in the `app.html`. + +These LD+JSON SEO schematic data must be updated at the page-level rendering. +For more technical specifications, please refer to the following: + +1. https://schema.org/docs/full.html + + + + +## Sitemaps & `Robots.txt` Search Engine Optimization Configurations + +By default, AutomataCI generates the following automatically: + +* 1 index sitemap (`assets/sitemap.xml`) +* all the pages' sitemap inside `assets/sitemaps/` directory +* 1 robot text file (`assets/robots.txt`) + +The sitemaps are complying to the 50,000 entries limit. + +This is done by: + +1. parsing the `prerender-routes.txt` url list +2. add from `url_add` specific url list +3. remove from `url_remove` specific url list + +To add/remove urls or modify the `robots.txt` policy: + +* `init.sitemaps.ts` - the generator script. + +Look for: + +1. `url_add` - add additional URLs after `prerender-routes.txt` +2. `url_remove` - remove URLs right before writing. +3. `robots_policy` - the `robots.txt` without the `Sitemap: ` field (added + automatically whenever available). + -Then update the `assets/manifest.webmanifest` for PWA settings. Specifically, -look for `start_url` and etc. +## `browserconfig.xml` fallback configuration +While deemed obselete, this file is generated for backward compatibilities and +silencing those automated requests. AutomataCI automatically generate this +config file into `assets/browserconfig.xml` based on the site-level metadata +information. -## For development -Everything is the same as Angular: `$ ng serve` +## `.nojekyll` configuration file +Specific to GitHub, it is required to generate this configuration file for +static site generator not using [Jekyll](https://jekyllrb.com/). -## For Tests -Everything is the same as Angular: `$ ng test` OR `$ ng e2e` +## `CNAME` configuration file +Specific to GitHub, AutomataCI generates the `assets/CNAME` configuration file +as requested for custom domain implementations. By default, this file is not +generated and will be hosted on GitHub Pages. -## For Production +To set the domain, please refer to: -Everything is the same as Angular: `$ ng build` +* `init.cname.ts` - generator file -For full automation, use: `$ ./automataCI/ci.sh.ps1 build` +Look for `const data = ''; // override the content here` and specify the domain +value into it. diff --git a/Angular/app.html b/Angular/app.html index 44f7ea9b..5f91b72c 100644 --- a/Angular/app.html +++ b/Angular/app.html @@ -1,15 +1,20 @@ - - Website + + TITLE - + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Angular/app.routes.ts b/Angular/app.routes.ts index 88719dd8..9a370472 100644 --- a/Angular/app.routes.ts +++ b/Angular/app.routes.ts @@ -11,10 +11,29 @@ import { Page_Lang } from "./contents/lang/page"; export const routes: Routes = [ - { path: '', component: Page_Root }, - { path: 'en', component: Page_Lang, data: { lang: 'en' } }, + // landing page + { + path: '', + component: Page_Root, + data: { + lang: '', + }, + }, + + + // main page by language + { + path: 'en', + component: Page_Lang, + data: { + lang: 'en', + }, + }, // catch all - { path: '**', component: Page_404 }, + { + path: '**', + component: Page_404, + }, ]; diff --git a/Angular/assets/CNAME b/Angular/assets/CNAME deleted file mode 100644 index 396d471f..00000000 --- a/Angular/assets/CNAME +++ /dev/null @@ -1 +0,0 @@ -www.example.com diff --git a/Angular/assets/browserconfig.xml b/Angular/assets/browserconfig.xml index 9c8fceb5..f63e6aa2 100644 --- a/Angular/assets/browserconfig.xml +++ b/Angular/assets/browserconfig.xml @@ -2,11 +2,11 @@ - - - - - + + + + + #021B79 diff --git a/Angular/assets/logos/icon_180x180.png b/Angular/assets/logos/icon_180x180.png new file mode 100644 index 00000000..1c4b730c Binary files /dev/null and b/Angular/assets/logos/icon_180x180.png differ diff --git a/Angular/assets/logos/icon_76x76.png b/Angular/assets/logos/icon_76x76.png new file mode 100644 index 00000000..06b76431 Binary files /dev/null and b/Angular/assets/logos/icon_76x76.png differ diff --git a/Angular/assets/manifest.webmanifest b/Angular/assets/manifest.webmanifest index 0c451c8f..c18b6ecf 100644 --- a/Angular/assets/manifest.webmanifest +++ b/Angular/assets/manifest.webmanifest @@ -1,32 +1,31 @@ { - "name": "Brand Name", - "short_name": "brand", + "name": "Your Site-Level Website Name Here", + "short_name": "brand-name-here", "lang": "en", - "description": "description here", + "description": "Your site-level description here", "categories": [ - "CAT 1", - "CAT 2", - "CAT 3" + "keyword 1", + "keyword 2" ], - "id": "#0000FF", - "theme_color": "#0000FF", - "background_color": "#021B79", + "id": "/", + "start_url": "/", + "scope": "/", "display": "standalone", "display_override": [ "standalone", "minimal-ui", "browser" ], - "scope": "./", - "start_url": "./", "orientation": "any", - "prefer_related_applications": false, + "theme_color": "#0000FF", + "background_color": "#021B79", "protocol_handlers": [ { "protocol": "web+brand", "url": "/?query=%s" } ], + "prefer_related_applications": false, "related_applications": [ { "id": "/", @@ -36,140 +35,174 @@ ], "screenshots": [ { - "label": "Brand Name", - "platform": "wide", + "label": "Welcome", + "form_factor": "wide", "sizes": "1200x630", - "src": "/logos/icon_512x512.png", + "src": "https://www.example.com/screenshots/screenshot-welcome_1200x630.png", + "type": "image/png" + }, + { + "label": "Welcome", + "form_factor": "narrow", + "sizes": "630x1200", + "src": "https://www.example.com/screenshots/screenshot-welcome_630x1200.png", + "type": "image/png" + }, + { + "label": "Welcome", + "sizes": "1200x1200", + "src": "https://www.example.com/screenshots/screenshot-welcome_1200x1200.png", "type": "image/png" } ], "shortcuts": [ { - "description": "To Main Dashboard", + "description": "Dashboard", "icons": [ { "purpose": "any", "sizes": "1200x1200", - "src": "/logos/icon_1200x1200.png", + "src": "https://www.example.com/logos/icon_1200x1200.png", "type": "image/png" } ], - "name": "Home \\u0026 Dashboard", + "name": "Dashboard", "short_name": "Dashboard", "url": "/" } ], "icons": [ { - "src": "/logos/icon_57x57.png", + "src": "https://www.example.com/logos/icon_57x57.png", "sizes": "57x57", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_60x60.png", + }, + { + "src": "https://www.example.com/logos/icon_60x60.png", "sizes": "60x60", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_70x70.png", + }, + { + "src": "https://www.example.com/logos/icon_70x70.png", "sizes": "70x70", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_72x72.png", + }, + { + "src": "https://www.example.com/logos/icon_72x72.png", "sizes": "72x72", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_76x76.png", + }, + { + "src": "https://www.example.com/logos/icon_76x76.png", "sizes": "76x76", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_96x96.png", + }, + { + "src": "https://www.example.com/logos/icon_96x96.png", "sizes": "96x96", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_114x114.png", + }, + { + "src": "https://www.example.com/logos/icon_114x114.png", "sizes": "114x114", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_120x120.png", - "sizes": "96x96", + }, + { + "src": "https://www.example.com/logos/icon_120x120.png", + "sizes": "120x120", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_128x128.png", + }, + { + "src": "https://www.example.com/logos/icon_128x128.png", "sizes": "128x128", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_144x144.png", + }, + { + "src": "https://www.example.com/logos/icon_144x144.png", "sizes": "144x144", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_150x150.png", + }, + { + "src": "https://www.example.com/logos/icon_150x150.png", "sizes": "150x150", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_152x152.png", + }, + { + "src": "https://www.example.com/logos/icon_152x152.png", "sizes": "152x152", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_180x180.png", - "sizes": "152x152", + }, + { + "src": "https://www.example.com/logos/icon_180x180.png", + "sizes": "180x180", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_192x192.png", + }, + { + "src": "https://www.example.com/logos/icon_192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_310x310.png", + }, + { + "src": "https://www.example.com/logos/icon_310x310.png", "sizes": "310x310", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_384x384.png", + }, + { + "src": "https://www.example.com/logos/icon_384x384.png", "sizes": "384x384", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_480x480.png", + }, + { + "src": "https://www.example.com/logos/icon_480x480.png", "sizes": "480x480", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_512x512.png", + }, + { + "src": "https://www.example.com/logos/icon_512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_1024x1024.png", + }, + { + "src": "https://www.example.com/logos/icon_1024x1024.png", "sizes": "1024x1024", "type": "image/png", "purpose": "any" - }, { - "src": "/logos/icon_1200x1200.svg", + }, + { + "src": "https://www.example.com/logos/icon_1200x1200.svg", "sizes": "1200x1200", "type": "image/svg+xml", "purpose": "any" - }, { - "src": "/logos/icon_1200x1200.png", + }, + { + "src": "https://www.example.com/logos/icon_1200x1200.png", "sizes": "1200x1200", "type": "image/png", "purpose": "any" - }, { - "purpose": "maskable any", + }, + { + "src": "https://www.example.com/logos/icon-monochrome_1200x1200.svg", "sizes": "1200x1200", - "src": "/logos/icon-monochrome_1200x1200.svg", - "type": "image/svg+xml" + "type": "image/svg+xml", + "purpose": "maskable" } ] -} +} \ No newline at end of file diff --git a/Angular/assets/robots.txt b/Angular/assets/robots.txt index c2a49f4f..7864a4f3 100644 --- a/Angular/assets/robots.txt +++ b/Angular/assets/robots.txt @@ -1,2 +1,3 @@ +Sitemap: https://www.example.com/sitemap.xml User-agent: * Allow: / diff --git a/Angular/assets/screenshots/screenshot-welcome_1200x1200.png b/Angular/assets/screenshots/screenshot-welcome_1200x1200.png new file mode 100644 index 00000000..871d70c9 Binary files /dev/null and b/Angular/assets/screenshots/screenshot-welcome_1200x1200.png differ diff --git a/Angular/assets/screenshots/screenshot-welcome_1200x630.png b/Angular/assets/screenshots/screenshot-welcome_1200x630.png new file mode 100644 index 00000000..b5226a40 Binary files /dev/null and b/Angular/assets/screenshots/screenshot-welcome_1200x630.png differ diff --git a/Angular/assets/screenshots/screenshot-welcome_630x1200.png b/Angular/assets/screenshots/screenshot-welcome_630x1200.png new file mode 100644 index 00000000..1a75e7c8 Binary files /dev/null and b/Angular/assets/screenshots/screenshot-welcome_630x1200.png differ diff --git a/Angular/assets/sitemap.xml b/Angular/assets/sitemap.xml new file mode 100644 index 00000000..823a5ad1 --- /dev/null +++ b/Angular/assets/sitemap.xml @@ -0,0 +1,7 @@ + + + + https://www.example.com/sitemaps/sitemap-page-1.xml + 2024-10-29T13:29:38+08:00 + + diff --git a/Angular/assets/sitemaps/sitemap-page-1.xml b/Angular/assets/sitemaps/sitemap-page-1.xml new file mode 100644 index 00000000..eadfa8c9 --- /dev/null +++ b/Angular/assets/sitemaps/sitemap-page-1.xml @@ -0,0 +1,11 @@ + + + + https://www.example.com/ + 2024-10-29T13:29:38+08:00 + + + https://www.example.com/en + 2024-10-29T13:29:38+08:00 + + diff --git a/Angular/assets/thumbnails/default_1200x1200.png b/Angular/assets/thumbnails/default_1200x1200.png new file mode 100644 index 00000000..871d70c9 Binary files /dev/null and b/Angular/assets/thumbnails/default_1200x1200.png differ diff --git a/Angular/assets/thumbnails/default_1200x630.png b/Angular/assets/thumbnails/default_1200x630.png new file mode 100644 index 00000000..b5226a40 Binary files /dev/null and b/Angular/assets/thumbnails/default_1200x630.png differ diff --git a/Angular/assets/thumbnails/default_480x480.png b/Angular/assets/thumbnails/default_480x480.png new file mode 100644 index 00000000..886c70b4 Binary files /dev/null and b/Angular/assets/thumbnails/default_480x480.png differ diff --git a/Angular/build.sh.ps1 b/Angular/build.sh.ps1 new file mode 100755 index 00000000..b4266a7b --- /dev/null +++ b/Angular/build.sh.ps1 @@ -0,0 +1,52 @@ +echo \" <<'RUN_AS_BATCH' >/dev/null ">NUL "\" \`" <#" +@ECHO OFF +REM LICENSE CLAUSES HERE +REM ---------------------------------------------------------------------------- + + + + +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +echo "[ ERROR ] --> powershell.exe !!!" +exit /b 1 +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +RUN_AS_BATCH +#> | Out-Null + + + + +echo \" <<'RUN_AS_POWERSHELL' >/dev/null # " | Out-Null +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +# execute +$null = . ".\init.sh.ps1" +$null = Clear-Host +$null = ng build --configuration production +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +exit +<# +RUN_AS_POWERSHELL + + + + +################################################################################ +# Unix Main Codes # +################################################################################ +# execute +. "./init.sh.ps1" +clear +ng build --configuration production +################################################################################ +# Unix Main Codes # +################################################################################ +exit $? +#> diff --git a/Angular/init.browserconfig-xml.ts b/Angular/init.browserconfig-xml.ts new file mode 100644 index 00000000..61279829 --- /dev/null +++ b/Angular/init.browserconfig-xml.ts @@ -0,0 +1,52 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import { Get_Base_URL, METADATA_SITE, Yield_URL } from './init.metadata'; +import * as fs from 'fs'; + + + + +/* exported function for creating the browserconfig.xml file */ +export function Create_Browser_Config_XML() { + const dir_build = 'assets'; + const filepath = dir_build + '/' + 'browserconfig.xml'; + + + /* bail if file exists */ + try { + if (fs.existsSync(filepath)) { + return; + } + } catch { + ; + } + + + /* create directory */ + if (!fs.existsSync(dir_build)) { + fs.mkdirSync(dir_build, { recursive: true }); + } + + + /* create new file */ + let data = Get_Base_URL(); + fs.writeFileSync(filepath,` + + + + + + + + + ${METADATA_SITE.Color_Theme_Background} + + + +`); + + + /* report status */ + console.log(`✓ ${filepath} generated successfully.`); +} diff --git a/Angular/init.cname.ts b/Angular/init.cname.ts new file mode 100644 index 00000000..b7de6bda --- /dev/null +++ b/Angular/init.cname.ts @@ -0,0 +1,45 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import { Get_Base_URL, METADATA_SITE } from './init.metadata'; +import * as fs from 'fs'; + + + + +const data = ''; // override the content here + + + + +/* exported function for creating the CNAME configuration file */ +export function Create_CNAME() { + const dir_build = 'assets'; + const filepath = dir_build + '/' + 'CNAME'; + + + /* bail if file exists */ + try { + if (fs.existsSync(filepath)) { + return; + } + } catch { + ; + } + + + /* create directory */ + if (!fs.existsSync(dir_build)) { + fs.mkdirSync(dir_build, { recursive: true }); + } + + + /* create new file */ + if (data) { + fs.writeFileSync(filepath, data); + } + + + /* report status */ + console.log(`✓ ${filepath} generated successfully.`); +} diff --git a/Angular/init.metadata.ts b/Angular/init.metadata.ts new file mode 100644 index 00000000..30ee0b53 --- /dev/null +++ b/Angular/init.metadata.ts @@ -0,0 +1,106 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import * as fs from 'fs'; +import { join } from 'path'; + + + + +export const METADATA_SITE = { + Name: { + "en": "Your Site-Level Website Name Here", + }, + SKU: "brand-name-here", + ID: "", // example: com.example.www.project + Description: { + "en": "Your site-level description here", + }, + Keywords: { + "en": [ + "keyword 1", + "keyword 2", + ], + }, + Color_Theme_Foreground: "#0000FF", + Color_Theme_Background: "#021B79", + Owner: [{ + "en": { + Call_Sign: "any call sign or nickname", + Title: "any given recongized title", + Family_Name: "first name", + Given_Name: "last name", + Brand: "any associated trademark or brand", + Description: "internal description here", + Slogan: "the owner's tagline", + Contacts: { + Email: "name@domain.tld", + Facebook: "https://www.facebook.com/USERNAME", + BlueSky: "https://bsky.app/profile/USERNAME", + Instagram: "https://www.instagram.com/USERNAME", + Mastodon: "https://[SERVER]/@USERNAME", + Call: "tel:123456789", + WhatsApp: "https://wa.me/123456789", + Telegram: "https://t.me/[LINK_OR_PHONE_NUMBER]" + }, + Roles: [ + "Creator", + "Contact", + "Artwork", + "Knowledge", + "Editor", + "Developer", + "Maintainer", + "Owner", + "Producer", + "Provider", + "Publisher", + "Reviewer", + "Funder", + "Sponsor" + ], + } + }], +}; + + + + +export function Get_Base_URL(): string { + try { + // Read angular.json file + const filepath = join(process.cwd(), 'angular.json'); + const file_content = fs.readFileSync(filepath, 'utf-8'); + + // Parse JSON safely + const json = JSON.parse(file_content); + + // Get first project key if project name not specified + const keys = Object.keys(json.projects || {}); + if (keys.length === 0) { + console.warn('No projects found in angular.json'); + return ''; + } + const project_key = keys[0]; + const project = json.projects[project_key]; + + // Safely navigate the object structure + const url = project?.architect?.build?.configurations?.production?.baseHref; + + // make sure the base url is the correct type and without tailing slash + if (typeof url === 'string') { + return url.replace(/\/$/, ""); + } + } catch (error) { + console.warn('Error reading baseHref from angular.json:', error); + } + + return ''; +} + + + + +export function Yield_URL(path: string, base_url: string): string { + return base_url.replace(/\/$/, "") + '/' + path.replace(/^\//, ""); +} diff --git a/Angular/init.no-jekyll.ts b/Angular/init.no-jekyll.ts new file mode 100644 index 00000000..1be8d615 --- /dev/null +++ b/Angular/init.no-jekyll.ts @@ -0,0 +1,37 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import * as fs from 'fs'; + + + + +/* exported function for creating the .nojekyll file */ +export function Create_No_Jekyll() { + const dir_build = 'assets'; + const filepath = dir_build + '/' + '.nojekyll'; + + + /* bail if file exists */ + try { + if (fs.existsSync(filepath)) { + return; + } + } catch { + ; + } + + + /* create directory */ + if (!fs.existsSync(dir_build)) { + fs.mkdirSync(dir_build, { recursive: true }); + } + + + /* create new file */ + fs.writeFileSync(filepath, 'No Jekyll'); + + + /* report status */ + console.log(`✓ ${filepath} generated successfully.`); +} diff --git a/Angular/init.pwa.ts b/Angular/init.pwa.ts new file mode 100644 index 00000000..853abec8 --- /dev/null +++ b/Angular/init.pwa.ts @@ -0,0 +1,267 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import { Get_Base_URL, METADATA_SITE, Yield_URL } from './init.metadata'; +import * as fs from 'fs'; + + + + +/* generate manifest.webmanifest with site data */ +function generate_web_manifest(): any { + const base = Get_Base_URL(); + + + /* create default settings */ + var manifest = { + name: "TITLE", + short_name: "BRAND", + lang: "en", + description: "DESCRIPTION", + categories: [ + "website", + "webapp", + "app", + ], + id: "/", + start_url: "/", + scope: "/", + display: "standalone", + display_override: [ + "standalone", + "minimal-ui", + "browser" + ], + orientation: "any", + theme_color: "#0000FF", + background_color: "#021B79", + protocol_handlers: [ + { protocol: "web+brand", url: "/?query=%s" }, + ], + prefer_related_applications: false, + related_applications: [{ + id: "/", + platform: "webapp", + url: "/" + }], + screenshots: [{ + label: "Welcome", + form_factor: "wide", + sizes: "1200x630", + src: Yield_URL("/screenshots/screenshot-welcome_1200x630.png", base), + type: "image/png" + }, { + label: "Welcome", + form_factor: "narrow", + sizes: "630x1200", + src: Yield_URL("/screenshots/screenshot-welcome_630x1200.png", base), + type: "image/png" + }, { + label: "Welcome", + sizes: "1200x1200", + src: Yield_URL("/screenshots/screenshot-welcome_1200x1200.png", base), + type: "image/png" + }], + shortcuts: [{ + description: "Dashboard", + icons: [{ + purpose: "any", + sizes: "1200x1200", + src: Yield_URL("/logos/icon_1200x1200.png", base), + type: "image/png" + }], + name: "Dashboard", + short_name: "Dashboard", + url: "/" + }], + icons: [{ + src: Yield_URL("/logos/icon_57x57.png", base), + sizes: "57x57", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_60x60.png", base), + sizes: "60x60", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_70x70.png", base), + sizes: "70x70", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_72x72.png", base), + sizes: "72x72", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_76x76.png", base), + sizes: "76x76", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_96x96.png", base), + sizes: "96x96", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_114x114.png", base), + sizes: "114x114", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_120x120.png", base), + sizes: "120x120", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_128x128.png", base), + sizes: "128x128", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_144x144.png", base), + sizes: "144x144", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_150x150.png", base), + sizes: "150x150", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_152x152.png", base), + sizes: "152x152", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_180x180.png", base), + sizes: "180x180", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_192x192.png", base), + sizes: "192x192", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_310x310.png", base), + sizes: "310x310", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_384x384.png", base), + sizes: "384x384", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_480x480.png", base), + sizes: "480x480", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_512x512.png", base), + sizes: "512x512", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_1024x1024.png", base), + sizes: "1024x1024", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_1200x1200.svg", base), + sizes: "1200x1200", + type: "image/svg+xml", + purpose: "any" + }, { + src: Yield_URL("/logos/icon_1200x1200.png", base), + sizes: "1200x1200", + type: "image/png", + purpose: "any" + }, { + src: Yield_URL("/logos/icon-monochrome_1200x1200.svg", base), + sizes: "1200x1200", + type: "image/svg+xml", + purpose: "maskable" + }] + } + + + /* check and overrides */ + if (!METADATA_SITE) { + return manifest; + } + + if (METADATA_SITE.Name) { + if (METADATA_SITE.Name.en) { + manifest.name = METADATA_SITE.Name.en; + } + } + + if (METADATA_SITE.SKU) { + manifest.short_name = METADATA_SITE.SKU; + } + + if (METADATA_SITE.Description) { + if (METADATA_SITE.Description.en) { + manifest.description = METADATA_SITE.Description.en; + } + } + + if (METADATA_SITE.Keywords) { + if (METADATA_SITE.Keywords.en) { + manifest.categories = METADATA_SITE.Keywords.en; + } + } + + if (METADATA_SITE.Color_Theme_Foreground) { + manifest.theme_color = METADATA_SITE.Color_Theme_Foreground; + } + + if (METADATA_SITE.Color_Theme_Background) { + manifest.background_color = METADATA_SITE.Color_Theme_Background; + } + + if (METADATA_SITE.ID) { + manifest.id = METADATA_SITE.ID; + } + + + /* return output */ + return manifest; +} + + + + +/* exported function for creating the web manifest file */ +export function Create_Web_Manifest() { + const dir_build = 'assets'; + const filepath = dir_build + '/' + 'manifest.webmanifest'; + + + /* bail if file exists */ + try { + if (fs.existsSync(filepath)) { + return; + } + } catch { + ; + } + + + /* create directory */ + if (!fs.existsSync(dir_build)) { + fs.mkdirSync(dir_build, { recursive: true }); + } + + + /* create new file */ + const data = generate_web_manifest(); + fs.writeFileSync(filepath, JSON.stringify(data, null, 2)); + + + /* report status */ + console.log(`✓ ${filepath} generated successfully.`); +} diff --git a/Angular/init.seo.ts b/Angular/init.seo.ts new file mode 100644 index 00000000..f91bb154 --- /dev/null +++ b/Angular/init.seo.ts @@ -0,0 +1,304 @@ +/* + * COPYRIGHT LICENSE NOTICE HERE + */ +import { Get_Base_URL, Yield_URL } from './init.metadata'; +import * as fs from 'fs'; +import * as path from 'path'; + + + + +// add your url here +const url_add: string[] = [ +]; + +// remove your url here +const url_remove: string[] = [ + '/404', +]; + +// robots.txt policy here +// NOTICE: DO NOT add 'Sitemap:'. It will be added automatically when available. +const robots_policy = ` +User-agent: * +Allow: / +` + + + +function time_pad(n: number) { + return n < 10 ? '0' + n : n; +} + + + + +function time_timezone_offset(offset: number) { + if (offset === 0) { + return 'Z'; +} + + const sign = offset > 0 ? '-' : '+'; + offset = Math.abs(offset); + + return sign + time_pad(Math.floor(offset / 60)) + ':' + time_pad(offset % 60); +} + + + + +function time_get_current_RFC3339(date: Date): string { + return ( + date.getFullYear() + + '-' + + time_pad(date.getMonth() + 1) + + '-' + + time_pad(date.getDate()) + + 'T' + + time_pad(date.getHours()) + + ':' + + time_pad(date.getMinutes()) + + ':' + + time_pad(date.getSeconds()) + + time_timezone_offset(date.getTimezoneOffset()) + ); +} + + + + +const sitemap_capacity_limit = 50000; +var time_updated = time_get_current_RFC3339(new Date()); + + + +function create_sitemaps_page(directory: string, url_base: string, list: string[]): string[] { + // filename: sitemap-page-%d.xml + const filename = 'sitemap-page-'; + var count_sitemap = 0; + const extension = '.xml'; + + var count_url = sitemap_capacity_limit; // note: to spin immediately + var url_path = ''; + var filepath = ''; + var list_sitemaps: string[] = []; + + const header = ` + +`; + const seal = `\n` + + + // validate before start + if (list.length <= 0) { + return list_sitemaps; + } + + + // loop through urls + for (var i = 0; i < list.length; i++) { + if (count_url >= sitemap_capacity_limit) { + // seal the existing sitemap + if (count_sitemap > 0) { + fs.appendFileSync(filepath, seal); + } + + + // spin a new sitemap + count_sitemap++; + count_url = 0; + filepath = filename + String(count_sitemap) + extension; + url_path = Yield_URL(filepath, url_base); + filepath = path.join(directory, filepath); + + + // write the header + fs.appendFileSync(filepath, header); + + + // register into sitemaps list + list_sitemaps.push(url_path); + } + + + // append url entry + fs.appendFileSync(filepath, +` + ${list[i]} + ${time_updated} + +`); + } + + // seal the last sitemap + fs.appendFileSync(filepath, seal); + + + // report status + return list_sitemaps; +} + + + + +function create_sitemaps_index(filepath: string, list: string[]) { + const header = ` + +`; + const seal = `\n` + + + // validate before start + if (list.length <= 0) { + return; + } + + + // write the header + fs.appendFileSync(filepath, header); + + + // loop through urls + for (var i = 0; i < list.length; i++) { + fs.appendFileSync(filepath, +` + ${list[i]} + ${time_updated} + +`); + } + + + // seal the last sitemap + fs.appendFileSync(filepath, seal); +} + + + + +function create_robots(filepath: string, sitemap_url: string) { + // write sitemap if available + if (sitemap_url) { + fs.appendFileSync(filepath, `Sitemap: ${sitemap_url}`); + } + + // append the policy + fs.appendFileSync(filepath, robots_policy); +} + + + + +/* exported function for creating the seo configuration files */ +export function Create_SEO() { + const source_file = 'prerender-routes.txt'; + const filename_sitemap = 'sitemap.xml'; + const filename_robots = 'robots.txt'; + + const dir_sitemaps = 'sitemaps'; + const dir_sitemaps_build = `assets/${dir_sitemaps}`; + + const filepath_sitemap = `assets/${filename_sitemap}`; + const filepath_robots = `assets/${filename_robots}`; + + + /* bail if source file is missing */ + try { + if (!fs.existsSync(source_file)) { + return; + } + } catch { + ; + } + + + /* bail if file exists */ + try { + if (fs.existsSync(dir_sitemaps_build)) { + return; + } + + if (fs.existsSync(filepath_sitemap)) { + return; + } + + if (fs.existsSync(filepath_robots)) { + return; + } + } catch { + ; + } + + + /* parse urls from prerender-routes.txt */ + var base = Get_Base_URL(); + var sources = fs.readFileSync(source_file).toString().split("\n"); + var list: string[] = []; + for (var i = 0; i < sources.length; i++) { + if (!sources[i]) { + continue + } + + list[i] = Yield_URL(sources[i], base); + } + + + /* add urls from url_add */ + for (var i = 0; i < url_add.length; i++) { + if (!url_add[i]) { + continue + } + + list.push(Yield_URL(sources[i], base)); + } + + + /* remove duplicated urls */ + list.filter((x, i, a) => a.indexOf(x) == i); + + + /* remove urls from url_remove */ + for (var i = 0; i < url_remove.length; i++) { + if (!url_remove[i]) { + continue + } + + let target = Yield_URL(url_remove[i], base); + list = list.filter((sample) => sample != target); + } + + + /* validate list before proceeding */ + if (list.length <= 0) { + create_robots(filepath_robots, ''); + return; /* no url - bail */ + } + + + /* update sitemap baseline url */ + let url_sitemap = Yield_URL(filename_sitemap, base); + base = Yield_URL(`/${dir_sitemaps}`, base); + + + /* create directory */ + if (!fs.existsSync(dir_sitemaps_build)) { + fs.mkdirSync(dir_sitemaps_build, { recursive: true }); + } + + + /* create page sitemaps */ + var sitemaps = create_sitemaps_page(dir_sitemaps_build, base, list); + + + /* create index sitemaps */ + create_sitemaps_index(filepath_sitemap, sitemaps); + + + /* create robots.txt */ + create_robots(filepath_robots, url_sitemap); + + + /* report status */ + console.log(`✓ ${filepath_sitemap} generated successfully.`); + console.log(`✓ ${filepath_robots} generated successfully.`); +} diff --git a/Angular/init.sh.ps1 b/Angular/init.sh.ps1 new file mode 100644 index 00000000..635e89a0 --- /dev/null +++ b/Angular/init.sh.ps1 @@ -0,0 +1,76 @@ +echo \" <<'RUN_AS_BATCH' >/dev/null ">NUL "\" \`" <#" +@ECHO OFF +REM LICENSE CLAUSES HERE +REM ---------------------------------------------------------------------------- + + + + +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +echo "[ ERROR ] --> powershell.exe !!!" +exit /b 1 +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +RUN_AS_BATCH +#> | Out-Null + + + + +echo \" <<'RUN_AS_POWERSHELL' >/dev/null # " | Out-Null +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +# execute +$null = Write-Host "clean-ing up existing configuration files..." +$null = Remove-Item -Force ".\assets\manifest.webmanifest" -ErrorAction SilentlyContinue +$null = Remove-Item -Force ".\assets\CNAME" -ErrorAction SilentlyContinue +$null = Remove-Item -Recursive -Force ".\assets\sitemaps" -ErrorAction SilentlyContinue +$null = Remove-Item -Force ".\assets\sitemap.xml" -ErrorAction SilentlyContinue +$null = Remove-Item -Force ".\assets\robots.txt" -ErrorAction SilentlyContinue +$null = Remove-Item -Force ".\assets\browserconfig.xml" -ErrorAction SilentlyContinue +$null = Remove-Item -Force ".\assets\.nojekyll" -ErrorAction SilentlyContinue + + +$null = Write-Host "initializing the repository..." +$null = ng build --aot --configuration production | Out-Null +$null = Remove-Item -Recurse -Force ".\dist" -ErrorAction SilentlyContinue +$null = [System.IO.File]::FlushAll() +return +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +exit +<# +RUN_AS_POWERSHELL + + + + +################################################################################ +# Unix Main Codes # +################################################################################ +# execute +1>&2 printf -- "%s\n" "clean-ing up existing configuration files..." +rm "./assets/manifest.webmanifest" &> /dev/null +rm "./assets/CNAME" &> /dev/null +rm -r "./assets/sitemaps/" &> /dev/null +rm "./assets/sitemap.xml" &> /dev/null +rm "./assets/robots.txt" &> /dev/null +rm "./assets/browserconfig.xml" &> /dev/null +rm "./assets/.nojekyll" &> /dev/null + + +1>&2 printf -- "%s\n" "initializing the repository..." +ng build --aot --configuration production --server main.server.ts &> /dev/null +rm -rf "./dist/" &> /dev/null +sync +return 0 +################################################################################ +# Unix Main Codes # +################################################################################ +exit $? +#> diff --git a/Angular/main.server.ts b/Angular/main.server.ts index 2d9c2a45..d6d345e0 100644 --- a/Angular/main.server.ts +++ b/Angular/main.server.ts @@ -4,13 +4,25 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { config } from './app.config.server'; import { App } from './app'; +import { Create_Web_Manifest } from './init.pwa'; +import { Create_CNAME } from './init.cname'; +import { Create_SEO } from './init.seo'; +import { Create_Browser_Config_XML } from './init.browserconfig-xml'; +import { Create_No_Jekyll } from './init.no-jekyll'; -const bootstrap = () => bootstrapApplication(App, config); +/* initializing project's dynamic configurations */ +Create_CNAME(); +Create_Web_Manifest(); +Create_SEO(); +Create_Browser_Config_XML(); +Create_No_Jekyll(); +/* main code */ +const bootstrap = () => bootstrapApplication(App, config); export default bootstrap; diff --git a/Angular/main.ts b/Angular/main.ts index 7ea9e48d..1658f6f3 100644 --- a/Angular/main.ts +++ b/Angular/main.ts @@ -8,5 +8,6 @@ import { App } from './app'; +/* main code */ bootstrapApplication(App, appConfig) .catch((err) => console.error(err)); diff --git a/Angular/ngsw-config.json b/Angular/ngsw-config.json index 4d80eea7..60176243 100644 --- a/Angular/ngsw-config.json +++ b/Angular/ngsw-config.json @@ -7,9 +7,13 @@ "installMode": "prefetch", "resources": { "files": [ - "/favicon.ico", "/index.html", "/manifest.webmanifest", + "/logos/**", + "/css/**", + "/js/**", + "/thumbnails/**", + "/screenshots/**", "/*.css", "/*.js" ] @@ -21,8 +25,7 @@ "updateMode": "prefetch", "resources": { "files": [ - "/assets/**", - "/media/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" + "/**" ] } } diff --git a/Angular/package.json b/Angular/package.json index c92a0195..d715d46a 100644 --- a/Angular/package.json +++ b/Angular/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "start": "clear && ng serve", - "build": "clear && ng build", - "watch": "clear && ng build --watch --configuration development", - "test": "clear && ng test --no-watch --code-coverage --browsers ChromeHeadless", + "build": "./build.sh.ps1", + "start": "./serve.sh.ps1", + "test": "./test.sh.ps1", + "watch": "./watch.sh.ps1", "serve:ssr:app": "node dist/app/server/server.mjs" }, "private": true, diff --git a/Angular/serve.sh.ps1 b/Angular/serve.sh.ps1 new file mode 100755 index 00000000..2e816926 --- /dev/null +++ b/Angular/serve.sh.ps1 @@ -0,0 +1,52 @@ +echo \" <<'RUN_AS_BATCH' >/dev/null ">NUL "\" \`" <#" +@ECHO OFF +REM LICENSE CLAUSES HERE +REM ---------------------------------------------------------------------------- + + + + +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +echo "[ ERROR ] --> powershell.exe !!!" +exit /b 1 +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +RUN_AS_BATCH +#> | Out-Null + + + + +echo \" <<'RUN_AS_POWERSHELL' >/dev/null # " | Out-Null +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +# execute +$null = . ".\init.sh.ps1" +$null = Clear-Host +$null = ng serve --configuration production +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +exit +<# +RUN_AS_POWERSHELL + + + + +################################################################################ +# Unix Main Codes # +################################################################################ +# execute +. "./init.sh.ps1" +clear +ng serve --configuration production +################################################################################ +# Unix Main Codes # +################################################################################ +exit $? +#> diff --git a/Angular/test.sh.ps1 b/Angular/test.sh.ps1 new file mode 100755 index 00000000..47642757 --- /dev/null +++ b/Angular/test.sh.ps1 @@ -0,0 +1,52 @@ +echo \" <<'RUN_AS_BATCH' >/dev/null ">NUL "\" \`" <#" +@ECHO OFF +REM LICENSE CLAUSES HERE +REM ---------------------------------------------------------------------------- + + + + +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +echo "[ ERROR ] --> powershell.exe !!!" +exit /b 1 +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +RUN_AS_BATCH +#> | Out-Null + + + + +echo \" <<'RUN_AS_POWERSHELL' >/dev/null # " | Out-Null +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +# execute +$null = . ".\init.sh.ps1" +$null = Clear-Host +$null = ng test --no-watch --code-coverage --browsers ChromeHeadless +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +exit +<# +RUN_AS_POWERSHELL + + + + +################################################################################ +# Unix Main Codes # +################################################################################ +# execute +. "./init.sh.ps1" +clear +ng test --no-watch --code-coverage --browsers ChromeHeadless +################################################################################ +# Unix Main Codes # +################################################################################ +exit $? +#> diff --git a/Angular/watch.sh.ps1 b/Angular/watch.sh.ps1 new file mode 100755 index 00000000..64a43606 --- /dev/null +++ b/Angular/watch.sh.ps1 @@ -0,0 +1,52 @@ +echo \" <<'RUN_AS_BATCH' >/dev/null ">NUL "\" \`" <#" +@ECHO OFF +REM LICENSE CLAUSES HERE +REM ---------------------------------------------------------------------------- + + + + +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +echo "[ ERROR ] --> powershell.exe !!!" +exit /b 1 +REM ############################################################################ +REM # Windows BATCH Codes # +REM ############################################################################ +RUN_AS_BATCH +#> | Out-Null + + + + +echo \" <<'RUN_AS_POWERSHELL' >/dev/null # " | Out-Null +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +# execute +$null = . ".\init.sh.ps1" +$null = Clear-Host +$null = ng build --watch --configuration development +################################################################################ +# Windows POWERSHELL Codes # +################################################################################ +exit +<# +RUN_AS_POWERSHELL + + + + +################################################################################ +# Unix Main Codes # +################################################################################ +# execute +. "./init.sh.ps1" +clear +ng build --watch --configuration development +################################################################################ +# Unix Main Codes # +################################################################################ +exit $? +#>