diff --git a/.astro/settings.json b/.astro/settings.json index 3cb9b43c3..281b1c71d 100644 --- a/.astro/settings.json +++ b/.astro/settings.json @@ -3,6 +3,6 @@ "enabled": false }, "_variables": { - "lastUpdateCheck": 1724925726721 + "lastUpdateCheck": 1725962974592 } } \ No newline at end of file diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index b9386e7de..e9bb29fef 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -1,24 +1,26 @@ name: Deploy to EC2 + on: - workflow_dispatch: # allow manual run + workflow_dispatch: + jobs: deploy: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v2 + - name: Checkout Repository + uses: actions/checkout@v4 with: fetch-depth: 2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v4 with: node-version: 20 - - uses: pnpm/action-setup@v3.0.0 + - uses: pnpm/action-setup@v4.0.0 with: - version: 8.15.6 + version: 9 - # -------------------- + # ------------------- # Setup configuration - # -------------------- + # ------------------- - name: Prepare configuration files run: | git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/infra-config.git configuration --depth 1 @@ -26,13 +28,14 @@ jobs: run: | cp configuration/dist/github/developer-roadmap.env .env - # -------------------- - # Prepare the build - # -------------------- - - name: Install dependencies + # ----------------- + # Prepare the Build + # ----------------- + - name: Install Dependencies run: | pnpm install - - name: Generate build + + - name: Generate Production Build run: | git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/web-draw.git .temp/web-draw --depth 1 npm run generate-renderer @@ -45,7 +48,7 @@ jobs: - uses: webfactory/ssh-agent@v0.7.0 with: ssh-private-key: ${{ secrets.EC2_PRIVATE_KEY }} - - name: Deploy app to EC2 + - name: Deploy Application to EC2 run: | rsync -apvz --delete --no-times --exclude "configuration" -e "ssh -o StrictHostKeyChecking=no" -p ./ ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/var/www/roadmap.sh/ - name: Restart PM2 @@ -58,9 +61,9 @@ jobs: cd /var/www/roadmap.sh sudo pm2 restart web-roadmap - # -------------------- + # ---------------------- # Clear cloudfront cache - # -------------------- + # ---------------------- - name: Clear Cloudfront Caching run: | curl -L \ diff --git a/.github/workflows/label-issue.yml b/.github/workflows/label-issue.yml index a59b76aac..7250e25ef 100644 --- a/.github/workflows/label-issue.yml +++ b/.github/workflows/label-issue.yml @@ -1,13 +1,15 @@ name: Label Issue + on: issues: types: [ opened, edited ] + jobs: label-topic-change-issue: runs-on: ubuntu-latest steps: - - name: Add roadmap slug to issue as label - uses: actions/github-script@v3 + - name: Add Labels To Issue + uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -18,7 +20,7 @@ jobs: if (issue.labels.some(label => label.name === 'topic-change')) { if (roadmapUrl) { const roadmapSlug = new URL(roadmapUrl[0]).pathname.replace(/\//, ''); - github.issues.addLabels({ + github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, @@ -28,7 +30,7 @@ jobs: // Close the issue if it has no roadmap URL if (!roadmapUrl) { - github.issues.update({ + github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, diff --git a/.github/workflows/refresh-roadmap-content-json.yml b/.github/workflows/refresh-roadmap-content-json.yml index 04ee6235f..8441eabda 100644 --- a/.github/workflows/refresh-roadmap-content-json.yml +++ b/.github/workflows/refresh-roadmap-content-json.yml @@ -1,35 +1,35 @@ -name: Refreshes roadmap content JSON +name: Refresh Roadmap Content JSON on: - workflow_dispatch: # allow manual run + workflow_dispatch: schedule: - - cron: '0 0 * * *' # every day at midnight + - cron: '0 0 * * *' jobs: - upgrade-deps: + refresh-content: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup pnpm@v9 uses: pnpm/action-setup@v4 with: version: 9 run_install: false - - name: Setup Node.js + - name: Setup Node.js Version 20 (LTS) uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 cache: 'pnpm' - - name: Install dependencies and generate content JSON + - name: Install Dependencies and Generate Content JSON run: | pnpm install npm run generate:roadmap-content-json + - name: Create PR - uses: peter-evans/create-pull-request@v4 + uses: peter-evans/create-pull-request@v7 with: delete-branch: false branch: "chore/update-content-json" @@ -37,9 +37,16 @@ jobs: labels: | dependencies automated pr - reviewers: kamranahmedse,arikchakma + reviewers: kamranahmedse commit-message: "chore: update roadmap content json" - title: "Update roadmap content json" + title: "Updated Roadmap Content JSON - Automated" body: | - Updates the roadmap content JSON files in the `public` folder. - Please review the changes and merge if everything looks good. + ## Updated Roadmap Content JSON + + > [!IMPORTANT] + > This PR Updates the Roadmap Content JSON files stored in the `public` directory. + > + > Commit: ${{ github.sha }} + > Workflow Path: ${{ github.workflow_ref }} + + **Please Review the Changes and Merge the PR if everything is fine.** \ No newline at end of file diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml deleted file mode 100644 index c0d1a1e0c..000000000 --- a/.github/workflows/update-deps.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Update dependencies - -on: - workflow_dispatch: # allow manual run - schedule: - - cron: '0 0 * * 0' # every sunday at midnight - -jobs: - upgrade-deps: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 - with: - node-version: 18 - - uses: pnpm/action-setup@v2.2.2 - with: - version: 7.13.4 - - name: Upgrade dependencies - run: | - pnpm install - npm run upgrade - pnpm install --lockfile-only - - name: Create PR - uses: peter-evans/create-pull-request@v4 - with: - delete-branch: false - branch: "update-deps" - base: "master" - labels: | - dependencies - automated pr - reviewers: kamranahmedse - commit-message: "chore: update dependencies to latest" - title: "Upgrade dependencies to latest" - body: | - Updates all dependencies to latest versions. - Please review the changes and merge if everything looks good. diff --git a/.github/workflows/upgrade-dependencies.yml b/.github/workflows/upgrade-dependencies.yml new file mode 100644 index 000000000..3b351766d --- /dev/null +++ b/.github/workflows/upgrade-dependencies.yml @@ -0,0 +1,51 @@ +name: Upgrade Dependencies + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' + +jobs: + upgrade-deps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js Version 20 (LTS) + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Setup pnpm@v9 + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Install & Upgrade Dependencies + run: | + pnpm install + npm run upgrade + pnpm install --lockfile-only + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + delete-branch: false + branch: "update-deps" + base: "master" + labels: | + dependencies + automated pr + reviewers: kamranahmedse + commit-message: "chore: update dependencies to latest" + title: "Upgrade Dependencies To Latest - Automated" + body: | + ## Updated all Dependencies to Latest Versions. + + > [!IMPORTANT] + > This PR Upgrades the Dependencies to the Latest Their Versions. + > + > Commit: ${{ github.sha }} + > Workflow Path: ${{ github.workflow_ref }} + + **Please Review the Changes and Merge the PR if everything is fine.** \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 22a0411a7..e882195d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@resvg/resvg-js": "^2.6.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "astro": "^4.14.6", + "astro": "^4.15.4", "clsx": "^2.1.1", "dayjs": "^1.11.12", "dom-to-image": "^2.6.0", @@ -506,9 +506,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dependencies": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -2266,13 +2266,21 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.14.1.tgz", - "integrity": "sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.16.3.tgz", + "integrity": "sha512-yETIvrETCeC39gSPIiSADmjri9FwKmxz0QvONMtTIUYlKZe90CJkvcjPksayC2VQOtzOJonEiULUa8v8crUQvA==", "dependencies": { - "@types/hast": "^3.0.4" + "@shikijs/vscode-textmate": "^9.2.0", + "@types/hast": "^3.0.4", + "oniguruma-to-js": "0.3.3", + "regex": "4.3.2" } }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz", + "integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==" + }, "node_modules/@shuding/opentype.js": { "version": "1.4.0-beta.0", "resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz", @@ -2900,20 +2908,17 @@ } }, "node_modules/astro": { - "version": "4.14.6", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.14.6.tgz", - "integrity": "sha512-MIDyNhtu3L4uakHvlTprh21eQPehYOtZSuSLtd+r6xZcl3lB+mlBz/hs1W3iHEQAORyJnKArWSY/aVOBKUyflA==", + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.15.4.tgz", + "integrity": "sha512-wqy+m3qygt9DmCSqMsckxyK4ccCUFtti2d/WlLkEpAlqHgyDIg20zRTLHO2v/H4YeSlJ8sAcN0RW2FhOeYbINg==", "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.2.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.5", - "@babel/parser": "^7.25.4", "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/traverse": "^7.25.4", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@oslojs/encoding": "^0.4.1", "@rollup/pluginutils": "^5.1.0", "@types/babel__core": "^7.20.5", @@ -2936,8 +2941,8 @@ "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", - "execa": "^8.0.1", "fast-glob": "^3.3.2", + "fastq": "^1.17.1", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", @@ -2946,6 +2951,7 @@ "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.11", + "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", @@ -2957,14 +2963,15 @@ "prompts": "^2.4.2", "rehype": "^13.0.1", "semver": "^7.6.3", - "shiki": "^1.14.1", + "shiki": "^1.16.1", "string-width": "^7.2.0", "strip-ansi": "^7.1.0", - "tsconfck": "^3.1.1", + "tinyexec": "^0.3.0", + "tsconfck": "^3.1.3", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.2", - "vitefu": "^0.2.5", + "vitefu": "^1.0.2", "which-pm": "^3.0.0", "xxhash-wasm": "^1.0.2", "yargs-parser": "^21.1.1", @@ -4040,28 +4047,6 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4325,17 +4310,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-tsconfig": { "version": "4.7.6", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz", @@ -4720,14 +4694,6 @@ "node": ">= 0.8" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -4927,17 +4893,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-unicode-supported": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", @@ -5252,6 +5207,16 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5526,11 +5491,6 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6118,17 +6078,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -6325,31 +6274,6 @@ "npm": ">=8.12.1" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6397,18 +6321,12 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, + "node_modules/oniguruma-to-js": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.3.3.tgz", + "integrity": "sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/antfu" } }, "node_modules/openai": { @@ -7256,6 +7174,11 @@ "node": ">=8.10.0" } }, + "node_modules/regex": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz", + "integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==" + }, "node_modules/rehype": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.1.tgz", @@ -7792,11 +7715,12 @@ } }, "node_modules/shiki": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.14.1.tgz", - "integrity": "sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.16.3.tgz", + "integrity": "sha512-GypUE+fEd06FqDs63LSAVlmq7WsahhPQU62cgZxGF+TJT5LjD2k7HTxXj4/CKOVuMM3+wWQ1t4Y5oooeJFRRBQ==", "dependencies": { - "@shikijs/core": "1.14.1", + "@shikijs/core": "1.16.3", + "@shikijs/vscode-textmate": "^9.2.0", "@types/hast": "^3.0.4" } }, @@ -8018,17 +7942,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", @@ -8250,6 +8163,11 @@ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -8319,9 +8237,9 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tsconfck": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz", - "integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.3.tgz", + "integrity": "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==", "bin": { "tsconfck": "bin/tsconfck.js" }, @@ -9111,9 +9029,9 @@ } }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.2.tgz", + "integrity": "sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==", "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, diff --git a/package.json b/package.json index b1da03f02..253d1dea9 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@resvg/resvg-js": "^2.6.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "astro": "^4.14.6", + "astro": "^4.15.4", "clsx": "^2.1.1", "dayjs": "^1.11.12", "dom-to-image": "^2.6.0", diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 000000000..3dca3a22c Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/roadmap-content/android.json b/public/roadmap-content/android.json index 56500fc1f..2317745fc 100644 --- a/public/roadmap-content/android.json +++ b/public/roadmap-content/android.json @@ -184,18 +184,36 @@ }, "Dp2DOX10u2xJUjB8Okhzh": { "title": "Frame", - "description": "", - "links": [] + "description": "**FrameLayout** is a simple ViewGroup subclass in Android that is designed to hold a single child view or a stack of overlapping child views. It positions each child in the top-left corner by default and allows them to overlap on top of each other, which makes it useful for situations where you need to layer views on top of one another.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Android developers: Frame Layout", + "url": "https://developer.android.com/reference/android/widget/FrameLayout", + "type": "article" + } + ] }, "U8iMGGOd2EgPxSuwSG39Z": { "title": "Linear", - "description": "", - "links": [] + "description": "**LinearLayout** is a view group that aligns all children in a single directioni, vertically or horizontally. You can specify the layout direction with the `android:orientation` attribute.\n\n**LinearLayout** was commonly used in earlier Android development, but with the introduction of ConstraintLayout, it’s less frequently used in modern apps.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Android developers: Linear Layout", + "url": "https://developer.android.com/develop/ui/views/layout/linear", + "type": "article" + } + ] }, "yE0qAQZiEC9R8WvCdskpr": { "title": "Relative", - "description": "", - "links": [] + "description": "A **RelativeLayout** in Android is a type of ViewGroup that allows you to position child views relative to each other or relative to the parent layout. It's a flexible layout where you can arrange the child views in relation to one another based on certain rules, making it suitable for creating complex UI designs.\n\n**RelativeLayout** was commonly used in earlier Android development, but with the introduction of `ConstraintLayout`, it's less frequently used in modern apps.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Android developers: Relative Layout", + "url": "https://developer.android.com/develop/ui/views/layout/relative", + "type": "article" + } + ] }, "3fFNMhQIuuh-NRzSXYpXO": { "title": "Constraint", diff --git a/public/roadmap-content/angular.json b/public/roadmap-content/angular.json index f76727988..bc4107221 100644 --- a/public/roadmap-content/angular.json +++ b/public/roadmap-content/angular.json @@ -247,8 +247,34 @@ }, "19c7D-fWIJ3vYFT6h8ZfN": { "title": "Communication", - "description": "", - "links": [] + "description": "Angular components can communicate with each other using `@Input()` and `@Output()` decorators. These decorators facilitate data exchange between parent and child components.\n\n* **@Input()**: This decorator allows a parent component to pass data to a child component, enabling the child to receive and use the data.\n* **@Output()**: This decorator allows a child component to emit events to a parent component, enabling the parent to respond to changes or actions within the child component.\n\nAdditionally, **model inputs** are a special type of input that enable two-way data binding. This means that changes in the child component can be propagated back to the parent component, ensuring synchronization between the two. Model inputs automatically create a corresponding output, typically named by appending “Change” to the input’s name, to facilitate this two-way communication.\n\nTo facilitate communication between unrelated components, it’s most effective to trigger events using `EventEmitter` and have the components listen for these events. This approach ensures a decoupled and flexible architecture, allowing components to interact seamlessly without direct dependencies.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Website - Inputs", + "url": "https://angular.dev/guide/components/inputs", + "type": "article" + }, + { + "title": "Angular Official Website - Outputs", + "url": "https://angular.dev/guide/components/outputs", + "type": "article" + }, + { + "title": "Angular Official Docs - Model Inputs", + "url": "https://angular.dev/guide/signals/model", + "type": "article" + }, + { + "title": "Custom events with outputs", + "url": "https://angular.dev/guide/components/outputs", + "type": "article" + }, + { + "title": "Non-Related Component Communication | Angular Component & Directives", + "url": "https://www.youtube.com/watch?v=aIkGXMJFTzM", + "type": "video" + } + ] }, "TDyFjKrIZJnCjEZsojPNQ": { "title": "Parent-Child Interaction", @@ -331,8 +357,34 @@ }, "tC5ETtOuuUcybj1jI4CuG": { "title": "Dynamic Components", - "description": "", - "links": [] + "description": "In addition to using a component directly in a template, you can also dynamically render components. There are two main ways to dynamically render a component: in a template with `NgComponentOutlet`, or in your TypeScript code with `ViewContainerRef`.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Programmatically rendering components", + "url": "https://angular.dev/guide/components/programmatic-rendering", + "type": "article" + }, + { + "title": "New Input Binding for NgComponentOutlet", + "url": "https://medium.com/ngconf/new-input-binding-for-ngcomponentoutlet-cb18a86a739d", + "type": "article" + }, + { + "title": "Render dynamic components in Angular using ViewContainerRef", + "url": "https://dev.to/railsstudent/render-dynamic-components-in-angular-using-viewcontainerref-160h", + "type": "article" + }, + { + "title": "Dynamic Component in Angular (2024)", + "url": "https://www.youtube.com/watch?v=ncbftt3NWVo", + "type": "video" + }, + { + "title": "Mastering ViewContainerRef for dynamic component loading in Angular17", + "url": "https://www.youtube.com/watch?v=Ra4PITCt8m0", + "type": "video" + } + ] }, "b_kdNS9PDupcUftslkf9i": { "title": "Modules", @@ -606,11 +658,6 @@ } ] }, - "Wc2ybRw43uamEtno0FpDv": { - "title": "Template Ref Vars", - "description": "", - "links": [] - }, "VsU6713jeIjAOEZnF6gWx": { "title": "@Input & @Output", "description": "`@Input()` and `@Output()` give a child component a way to communicate with its parent component. `@Input()` lets a parent component update data in the child component. Conversely, `@Output()` lets the child send data to a parent component.\n\nVisit the following resources to learn more:", @@ -640,8 +687,24 @@ }, "VsC7UmE_AumsBP8fC6to1": { "title": "Template Syntax", - "description": "", - "links": [] + "description": "In Angular, a _template_ is a chunk of HTML. Use special syntax within a template to build on many of Angular's features. Extend the HTML vocabulary of your applications with special Angular syntax in your templates. For example, Angular helps you get and set DOM (Document Object Model) values dynamically with features such as built-in template functions, variables, event listening, and data binding.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Template Syntax", + "url": "https://angular.dev/guide/templates", + "type": "article" + }, + { + "title": "An Introduction to Angular Template Syntax", + "url": "https://angularstart.com/modules/basic-angular-concepts/3/", + "type": "article" + }, + { + "title": "Craft Dynamic Templates with Angular's Template Syntax", + "url": "https://www.youtube.com/watch?v=uSnUTcf8adI", + "type": "video" + } + ] }, "U1Zy2T-2ki9pDkXn9hn-I": { "title": "@if", @@ -793,7 +856,7 @@ "links": [ { "title": "Understanding Pipes", - "url": "https://angular.dev/guide/pipes", + "url": "https://angular.dev/tutorials/learn-angular/22-pipes", "type": "article" }, { @@ -1018,7 +1081,7 @@ }, "YF_sG292HqawIX0siWhrv": { "title": "Router Events", - "description": "The Angular Router raises events when it navigates from one route to another route. It raises several events such as `NavigationStart`, `NavigationEnd`, `NavigationCancel`, `NavigationError`, `ResolveStart`, etc. You can listen to these events and find out when the state of the route changes. Some of the useful events are route change start (NavigationStart) and route change end (NavigationEnd).", + "description": "The Angular Router raises events when it navigates from one route to another route. It raises several events such as `NavigationStart`, `NavigationEnd`, `NavigationCancel`, `NavigationError`, `ResolveStart`, etc. You can listen to these events and find out when the state of the route changes. Some of the useful events are route change start (NavigationStart) and route change end (NavigationEnd).\n\nVisit the following resources to learn more:", "links": [ { "title": "Router reference - Router events", @@ -1029,6 +1092,11 @@ "title": "Router event - API", "url": "https://angular.dev/api/router/RouterEvent", "type": "article" + }, + { + "title": "Router events in Angular", + "url": "https://medium.com/@gurunadhpukkalla/router-events-in-angular-3112a3968660", + "type": "article" } ] }, @@ -1149,8 +1217,34 @@ }, "XC_K1Wahl2ySqOXoym4YU": { "title": "Typed Forms", - "description": "", - "links": [] + "description": "Since Angular 14, reactive forms are strictly typed by default. You don't have to define extra custom types or add a ton of type annotations to your form declarations to benefit from this extra type safety, as Angular will infer types from the default value of a form control. Non-typed forms are still supported. To use them, you must import the `Untyped` symbols from `@angular/forms`.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - @let", + "url": "https://angular.dev/api/core/@let", + "type": "article" + }, + { + "title": "Angular Strictly Typed Forms (Complete Guide)", + "url": "https://blog.angular-university.io/angular-typed-forms/", + "type": "article" + }, + { + "title": "Getting started with typed reactive forms in Angular", + "url": "https://www.youtube.com/watch?v=mT3UR0TdDnU", + "type": "video" + }, + { + "title": "Angular TYPED Forms: Are You Using Them Correctly?", + "url": "https://www.youtube.com/watch?v=it2BZoIvBPc", + "type": "video" + }, + { + "title": "Knowing this makes Angular typed forms WAY less awkward", + "url": "https://www.youtube.com/watch?v=xpRlijg6spo", + "type": "video" + } + ] }, "uDx4lPavwsJFBMzdQ70CS": { "title": "Template-driven Forms", @@ -1307,8 +1401,29 @@ }, "lfp7PIjwITU5gBITQdirD": { "title": "RxJS Basics", - "description": "Reactive Extensions for JavaScript, or RxJS, is a reactive library used to implement reactive programming to deal with async implementation, callbacks, and event-based programs.\n\nThe reactive paradigm can be used in many different languages through the use of reactive libraries. These libraries are downloaded APIs that provide functionalities for reactive tools like observers and operators. It can be used in your browser or with Node.js.", - "links": [] + "description": "Reactive Extensions for JavaScript, or RxJS, is a reactive library used to implement reactive programming to deal with async implementation, callbacks, and event-based programs.\n\nThe reactive paradigm can be used in many different languages through the use of reactive libraries. These libraries are downloaded APIs that provide functionalities for reactive tools like observers and operators. It can be used in your browser or with Node.js.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "RxJS Docs", + "url": "https://rxjs.dev/guide/overview", + "type": "article" + }, + { + "title": "Learn RxJS", + "url": "https://www.learnrxjs.io/", + "type": "article" + }, + { + "title": "RxJs and Observables for Beginners: A Beginner Friendly Introduction", + "url": "https://blog.angular-university.io/functional-reactive-programming-for-angular-2-developers-rxjs-and-observables/", + "type": "article" + }, + { + "title": "Beginner's RxJS Tutorial: Dive Deep with RxJS Crash Course!", + "url": "https://www.youtube.com/watch?v=yJdh1_FbtjU", + "type": "video" + } + ] }, "krXA6ua7E3m4IIpFkgQZe": { "title": "Observable Pattern", @@ -1403,8 +1518,13 @@ }, "kdMJHljMzGA3oRlh8Zvos": { "title": "Transformation", - "description": "In RxJS, \"transformation\" refers to the process of modifying or manipulating the data emitted by an Observable. There are a variety of methods available in RxJS that can be used to transform the data emitted by an Observable, including:\n\n* map: applies a function to each item emitted by the Observable and emits the resulting value\n* mergeMap: applies a function to each item emitted by the Observable, and then merges the resulting Observables into a single Observable\n* switchMap: applies a function to each item emitted by the Observable, and then switches to the latest resulting Observable\n* concatMap: applies a function to each item emitted by the Observable, and then concatenates the resulting Observables into a single Observable\n* exhaustMap: applies a function to each item emitted by the Observable, but ignores subsequent emissions until the current Observable completes\n\nThese are just a few examples of the many methods available in RxJS for transforming the data emitted by an Observable. Each method has its own specific use case, and the best method to use will depend on the requirements of your application.\n\nHere are the official documentation links for the RxJS transformation methods:\n\nYou can find more information and examples on these methods in the official RxJS documentation. Additionally, you can find more operators on [https://rxjs.dev/api/operators](https://rxjs.dev/api/operators) and you can also find more information on the library as a whole on [https://rxjs.dev/](https://rxjs.dev/)", + "description": "In RxJS, \"transformation\" refers to the process of modifying or manipulating the data emitted by an Observable. There are a variety of methods available in RxJS that can be used to transform the data emitted by an Observable, including:\n\n* **map**: applies a function to each item emitted by the Observable and emits the resulting value\n* **mergeMap**: applies a function to each item emitted by the Observable, and then merges the resulting Observables into a single Observable\n* **switchMap**: applies a function to each item emitted by the Observable, and then switches to the latest resulting Observable\n* **concatMap**: applies a function to each item emitted by the Observable, and then concatenates the resulting Observables into a single Observable\n* **exhaustMap**: applies a function to each item emitted by the Observable, but ignores subsequent emissions until the current Observable completes\n\nThese are just a few examples of the many methods available in RxJS for transforming the data emitted by an Observable. Each method has its own specific use case, and the best method to use will depend on the requirements of your application.\n\nVisit the following resources to learn more:", "links": [ + { + "title": "RxJs Docs - Operators", + "url": "https://rxjs.dev/api/operators", + "type": "article" + }, { "title": "map", "url": "https://rxjs.dev/api/operators/map", @@ -1663,7 +1783,7 @@ }, "m4WBnx_9h01Jl6Q1sxi4Y": { "title": "Zones", - "description": "Zone.js is a signaling mechanism that Angular uses to detect when an application state might have changed. It captures asynchronous operations like setTimeout, network requests, and event listeners. Angular schedules change detection based on signals from Zone.js.", + "description": "Zone.js is a signaling mechanism that Angular uses to detect when an application state might have changed. It captures asynchronous operations like setTimeout, network requests, and event listeners. Angular schedules change detection based on signals from Zone.js.\n\nVisit the following resources to learn more:", "links": [ { "title": "Resolving zone pollution", @@ -1679,6 +1799,11 @@ "title": "NgZone - API", "url": "https://angular.dev/api/core/NgZone", "type": "article" + }, + { + "title": "WTF is \"Zone.js\" and is it making your app slow?", + "url": "https://www.youtube.com/watch?v=lmrf_gPIOZU", + "type": "video" } ] }, @@ -1720,8 +1845,29 @@ }, "EbJib-XfZFF9bpCtL3aBs": { "title": "Developer Tools", - "description": "", - "links": [] + "description": "Angular offers a suite of powerful developer tools designed to streamline and enhance the development process. These include the Angular CLI for efficient project setup and management, the Angular DevTools extension for advanced debugging and profiling, and the Angular Language Service for improved code editing and completion. Leveraging these tools will significantly improve your ability to write high-quality Angular code.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "VS Code NG Language Service", + "url": "https://github.com/angular/vscode-ng-language-service", + "type": "opensource" + }, + { + "title": "Angular Official Docs - devtools", + "url": "https://angular.dev/tools/devtools", + "type": "article" + }, + { + "title": "Angular Official Docs - CLI", + "url": "https://angular.dev/tools/cli", + "type": "article" + }, + { + "title": "Language Service Docs", + "url": "https://angular.dev/tools/language-service", + "type": "article" + } + ] }, "4YSk6I63Ew--zoXC3xmrC": { "title": "Angular CLI", @@ -1751,18 +1897,106 @@ }, "FVH0lnbIZ2m5EfF2EJ2DW": { "title": "Local Setup", - "description": "", - "links": [] + "description": "To install Angular CLI on your local system, you need to install `Node.js`. Angular requires an active LTS or maintenance LTS version of Node. Angular CLI uses Node and its associated package manager, npm, to install and run JavaScript tools outside the browser. Once you have Node installed, you can run `npm install -g @angular/cli` to install the Angular CLI.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Local set-up", + "url": "https://angular.dev/tools/cli/setup-local", + "type": "article" + }, + { + "title": "Angular Official Docs - Version compatibility guide", + "url": "https://angular.dev/reference/versions", + "type": "article" + }, + { + "title": "How To Install Angular CLI In Windows 10 | In Under 2 Minutes!", + "url": "https://www.youtube.com/watch?v=vjgACKkPENg", + "type": "video" + }, + { + "title": "How to Install Multiple Versions of Angular in Your Development Environment", + "url": "https://www.youtube.com/watch?v=LYNG3kcKRQ8", + "type": "video" + } + ] }, "1fVi9AK6aLjt5QgAFbnGX": { "title": "Deployment", - "description": "", - "links": [] + "description": "The Angular CLI command `ng deploy` executes the deploy CLI builder associated with your project. A number of third-party builders implement deployment capabilities to different platforms. You can add any of them to your project with `ng add`.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "NGX AWS Deploy", + "url": "https://github.com/Jefiozie/ngx-aws-deploy", + "type": "opensource" + }, + { + "title": "Angular CLI GitHub Pages", + "url": "https://github.com/angular-schule/angular-cli-ghpages", + "type": "opensource" + }, + { + "title": "Angular Official Docs - Deployment", + "url": "https://angular.dev/tools/cli/deployment", + "type": "article" + }, + { + "title": "Firebase Hosting", + "url": "https://firebase.google.com/docs/hosting", + "type": "article" + }, + { + "title": "Vercel: Angular Solutions", + "url": "https://vercel.com/solutions/angular", + "type": "article" + }, + { + "title": "Netlify", + "url": "https://docs.netlify.com/frameworks/angular/", + "type": "article" + }, + { + "title": "Cloudflare Pages", + "url": "https://developers.cloudflare.com/pages/framework-guides/deploy-an-angular-site/#create-a-new-project-using-the-create-cloudflare-cli-c3", + "type": "article" + }, + { + "title": "AWS Amplify", + "url": "https://docs.amplify.aws/angular/", + "type": "article" + } + ] }, "yhNGhduk__ow8VTLc6inZ": { "title": "End-to-End Testing", - "description": "", - "links": [] + "description": "End-to-end or (E2E) testing is a form of testing used to assert your entire application works as expected from start to finish or \"end-to-end\". E2E testing differs from unit testing in that it is completely decoupled from the underlying implementation details of your code. It is typically used to validate an application in a way that mimics the way a user would interact with it. The `ng e2e` command will first check your project for the \"e2e\" target. If it can't locate it, the CLI will then prompt you which e2e package you would like to use and walk you through the setup.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - End to End Testing", + "url": "https://angular.dev/tools/cli/end-to-end", + "type": "article" + }, + { + "title": "Cypress Official Docs - Your First Test with Cypress", + "url": "https://docs.cypress.io/guides/end-to-end-testing/writing-your-first-end-to-end-test", + "type": "article" + }, + { + "title": "Nightwatch Official Docs - Writing Tests: Introduction", + "url": "https://nightwatchjs.org/guide/writing-tests/introduction.html", + "type": "article" + }, + { + "title": "Webdriver Official Docs - Getting Started", + "url": "https://webdriver.io/docs/gettingstarted/", + "type": "article" + }, + { + "title": "Puppeteer Angular Schematic", + "url": "https://pptr.dev/guides/ng-schematics/#getting-started", + "type": "article" + } + ] }, "Uvr0pRk_fOzwRwqn0dQ6N": { "title": "Schematics", @@ -1798,8 +2032,24 @@ }, "TeWEy9I-hU6SH02Sy2S2S": { "title": "CLI Builders", - "description": "", - "links": [] + "description": "A number of Angular CLI commands run a complex process on your code, such as building, testing, or serving your application. The commands use an internal tool called `Architect` to run CLI builders, which invoke another tool (bundler, test runner, server) to accomplish the desired task. Custom builders can perform an entirely new task or to change which third-party tool is used by an existing command.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Builders", + "url": "https://github.com/just-jeb/angular-builders", + "type": "opensource" + }, + { + "title": "Angular Official Docs - CLI Builders", + "url": "https://angular.dev/tools/cli/cli-builder", + "type": "article" + }, + { + "title": "Angular Builders – Creating Custom Builder from Scratch", + "url": "https://www.youtube.com/watch?v=QbDkDLnXAZE", + "type": "video" + } + ] }, "MwtM1UAIfj4FJ-Y4CKDsP": { "title": "AoT Compilation", @@ -1877,13 +2127,40 @@ }, "YHV5oFwLwphXf1wJTDZuG": { "title": "Using Libraries", - "description": "", - "links": [] + "description": "Libraries are published as `npm packages`, usually together with schematics that integrate them with the Angular CLI. To integrate reusable library code into an application, you need to install the package and import the provided functionality in the location you use it. For most published Angular libraries, use the `ng add ` Angular CLI command. A published library typically provides a `README` file or other documentation on how to add that library to your application. A library is able to be updated by the publisher, and also has individual dependencies which need to be kept current. To check for updates to your installed libraries, use the `ng update` Angular CLI command.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Using Libraries", + "url": "https://angular.dev/tools/libraries/using-libraries", + "type": "article" + }, + { + "title": "npm", + "url": "https://www.npmjs.com/", + "type": "article" + } + ] }, "A1mYMg7cbcj6p_VkDf-Tz": { "title": "Creating Libraries", - "description": "", - "links": [] + "description": "If you have developed features that are suitable for reuse, you can create your own libraries. These libraries can be used locally in your workspace, or you can publish them as npm packages to share with other projects or other Angular developers. Putting code into a separate library is more complex than simply putting everything in one application. It requires more of an investment in time and thought for managing, maintaining, and updating the library. This complexity can pay off when the library is being used in multiple applications.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "NG Packagr", + "url": "https://github.com/ng-packagr/ng-packagr", + "type": "opensource" + }, + { + "title": "Angular Official Docs - Creating Libraries", + "url": "https://angular.dev/tools/libraries/creating-libraries", + "type": "article" + }, + { + "title": "Angular Official Docs - File Structure: Library project files", + "url": "https://angular.dev/reference/configs/file-structure#library-project-files", + "type": "article" + } + ] }, "jfHaS8TqE4tcAo59K8Nkn": { "title": "SSR", @@ -1908,8 +2185,24 @@ }, "b-0yQ74zHtAxI9aRLBohc": { "title": "SSG", - "description": "SSG (Static Site Generator), helps in building the HTML full website, during the process of building and serving that HTML Page. This method helps to generate the HTML website on the client side before its being served on the server side. Therefore, whenever a user requests a HTML Page, firstly HTML page will be rendered and secondly, the angular app will be rendered. The SSG can be used only if your website is static (or) it's content doesn't changes frequently.", - "links": [] + "description": "SSG (Static Site Generator) helps in building the HTML full website during the process of building and serving that HTML page. This method helps to generate the HTML website on the client side before it's served on the server side. Therefore, whenever a user requests a HTML page, the HTML page will be rendered, and secondly, the Angular app will be rendered. The SSG can be used only if your website is static or its content doesn't change frequently.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Air with Alyssa Nicoll - SSR, SSG, ISR, & SOS", + "url": "https://www.youtube.com/watch?v=b0pUU7RJbBQ", + "type": "podcast" + }, + { + "title": "Angular Official Docs - Prerendering (SSG)", + "url": "https://angular.dev/guide/prerendering", + "type": "article" + }, + { + "title": "Angular 16 Pre Rendering Static Pages - Static Site Generation SSG", + "url": "https://www.youtube.com/watch?v=vmOWJvm3apA", + "type": "video" + } + ] }, "kauQofxCmpktXPcnzid17": { "title": "AnalogJS", @@ -2019,18 +2312,66 @@ }, "xH3RHPhsaqD9zIMms5OmX": { "title": "HTTP Vulnerabilities", - "description": "", - "links": [] + "description": "Angular has built-in support to help prevent two common HTTP vulnerabilities, cross-site request forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily on the server side, but Angular provides helpers to make integration on the client side easier.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Security", + "url": "https://angular.dev/best-practices/security", + "type": "article" + }, + { + "title": "Angular | HackTricks", + "url": "https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/angular", + "type": "article" + } + ] }, "Z1DZBbFI4oU6-KQg3wqMm": { "title": "Cross-site Request Forgery", - "description": "", - "links": [] + "description": "Cross-site request forgery, also known as one-click attack or session riding and abbreviated as CSRF or XSRF, is a type of malicious exploit of a website or web application where unauthorized commands are submitted from a user that the web application trusts. There are many ways in which a malicious website can transmit such commands; specially-crafted image tags, hidden forms, and JavaScript fetch or XMLHttpRequests, for example, can all work without the user's interaction or knowledge. Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user's browser. In a CSRF attack, an innocent end user is tricked by an attacker into submitting a web request that they did not intend. This may cause actions to be performed on the website that can include inadvertent client or server data leakage, change of session state, or manipulation of an end user's account. Angular provides built-in protection against CSRF attacks through the `HttpClientXsrfModule` module. This module automatically adds a token to outgoing requests and validates it on the server side.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Cross Site Request Forgery", + "url": "https://angular.dev/best-practices/security#cross-site-request-forgery", + "type": "article" + }, + { + "title": "HttpClientXsrfModule", + "url": "https://angular.dev/api/common/http/HttpClientXsrfModule", + "type": "article" + }, + { + "title": "Configure the CSRF Protection With Spring Security 6 and Angular", + "url": "https://www.youtube.com/watch?v=tgjLsEmxcuY", + "type": "video" + }, + { + "title": "Angular security - CSRF prevention using Double Submit Cookie", + "url": "https://www.youtube.com/watch?v=lZfF4MOTeNM", + "type": "video" + } + ] }, "m2aw8vb4rz4IjshpoMyNx": { "title": "HttpClient CSRF", - "description": "", - "links": [] + "description": "HttpClient includes a built-in mechanism to prevent XSRF attacks. When making HTTP requests, an interceptor reads a token from a cookie (default name: XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only code running on your domain can read this cookie, the backend can verify that the HTTP request originates from your client application and not from an attacker.\n\nHowever, HttpClient only handles the client-side aspect of XSRF protection. Your backend service must be configured to set the cookie for your page and verify that the header is present on all relevant requests. Without this backend configuration, Angular’s default XSRF protection will not be effective.\n\nVisit the following resources to learn more:", + "links": [ + { + "title": "Angular Official Docs - Security", + "url": "https://angular.dev/best-practices/security#httpclient-xsrf-csrf-security", + "type": "article" + }, + { + "title": "How can you protect Angular Web app from cross site request forgery?", + "url": "https://www.linkedin.com/advice/3/how-can-you-protect-angular-web-app-from-cross-site-pyqwc", + "type": "article" + }, + { + "title": "Cross Site Request Forgery: XSRF protection in Angular", + "url": "https://borstch.com/blog/development/cross-site-request-forgery-xsrf-protection-in-angular", + "type": "article" + } + ] }, "ni00edsphJd7uBLCn7-Vw": { "title": "XSRF protection", @@ -2039,8 +2380,24 @@ }, "zd7YJGlcMFNFbsKUiW_XC": { "title": "Cross-site Script Inclusion", - "description": "", - "links": [] + "description": "Cross-site script inclusion, also known as JSON vulnerability, can allow an attacker's website to read data from a JSON API. The attack works on older browsers by overriding built-in JavaScript object constructors, and then including an API URL using a `