mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-10 12:53:03 -05:00
Merge branch 'main' into answer29-links
This commit is contained in:
@@ -3,7 +3,9 @@
|
|||||||
"projectOwner": "tomalaforge",
|
"projectOwner": "tomalaforge",
|
||||||
"repoType": "github",
|
"repoType": "github",
|
||||||
"repoHost": "https://github.com",
|
"repoHost": "https://github.com",
|
||||||
"files": ["README.md"],
|
"files": [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
"imageSize": 100,
|
"imageSize": 100,
|
||||||
"commit": true,
|
"commit": true,
|
||||||
"commitConvention": "angular",
|
"commitConvention": "angular",
|
||||||
@@ -13,31 +15,63 @@
|
|||||||
"name": "Laforge Thomas",
|
"name": "Laforge Thomas",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/30832608?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/30832608?v=4",
|
||||||
"profile": "https://thomaslaforge.dev/home",
|
"profile": "https://thomaslaforge.dev/home",
|
||||||
"contributions": ["code", "doc", "content", "ideas", "design"]
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc",
|
||||||
|
"content",
|
||||||
|
"ideas",
|
||||||
|
"design"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"login": "alan-bio",
|
"login": "alan-bio",
|
||||||
"name": "Alan Dragicevich",
|
"name": "Alan Dragicevich",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/31838230?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/31838230?v=4",
|
||||||
"profile": "https://github.com/alan-bio",
|
"profile": "https://github.com/alan-bio",
|
||||||
"contributions": ["doc"]
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"login": "edimitchel",
|
"login": "edimitchel",
|
||||||
"name": "Michel EDIGHOFFER",
|
"name": "Michel EDIGHOFFER",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/2922851?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/2922851?v=4",
|
||||||
"profile": "https://github.com/edimitchel",
|
"profile": "https://github.com/edimitchel",
|
||||||
"contributions": ["doc"]
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"login": "gsgonzalez88",
|
"login": "gsgonzalez88",
|
||||||
"name": "Gerardo Sebastian Gonzalez",
|
"name": "Gerardo Sebastian Gonzalez",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/39884678?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/39884678?v=4",
|
||||||
"profile": "https://github.com/gsgonzalez88",
|
"profile": "https://github.com/gsgonzalez88",
|
||||||
"contributions": ["doc"]
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "marryday",
|
||||||
|
"name": "Evseev Yuriy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/57489315?v=4",
|
||||||
|
"profile": "https://github.com/marryday",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "tomer953",
|
||||||
|
"name": "Tomer953",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1807493?v=4",
|
||||||
|
"profile": "https://github.com/tomer953",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"doc",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
"linkToUsage": true
|
"linkToUsage": true,
|
||||||
|
"commitType": "docs"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,3 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
> Thank you for considering contributing to this project. Your help is very much appreciated!
|
Learn how to contribute [here](https://angular-challenges.vercel.app/guides/contribute/)
|
||||||
|
|
||||||
When contributing, it's better to first explain the challenge/exercice you are thinking about in the issue tab.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
Please follow those step in order to succesfully make your contribution to this repository.
|
|
||||||
|
|
||||||
1. Fork the project
|
|
||||||
2. Install **Nx Console**, this will help you work with this repository
|
|
||||||
3. Run `npm ci` to install all dependencies
|
|
||||||
4. Generate a new app with Nx Console > Right Click on apps folder > `Nx Generate Application`
|
|
||||||
5. Copy/Paste **example.README.md** and fill it up.
|
|
||||||
6. Link the main **README** with your new challenge
|
|
||||||
7. Few days later, create a PR with your answer.
|
|
||||||
8. Optional: write a blog post explaining your Challenge and the solution you came up with.
|
|
||||||
|
|
||||||
## Pull Request Process
|
|
||||||
|
|
||||||
1. We follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/)
|
|
||||||
in our commit messages, i.e. `feat(core): improve typing`
|
|
||||||
2. When you are ready, create Pull Request of your fork into original repository with the title starting with **NEW CHALLENGE**
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f
|
|||||||
|
|
||||||
## Challenges
|
## Challenges
|
||||||
|
|
||||||
Check [all 35 challenges](https://angular-challenges.vercel.app/)
|
Check [all 36 challenges](https://angular-challenges.vercel.app/)
|
||||||
|
|
||||||
## Contributors ✨
|
## Contributors ✨
|
||||||
|
|
||||||
@@ -38,6 +38,8 @@ Check [all 35 challenges](https://angular-challenges.vercel.app/)
|
|||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alan-bio"><img src="https://avatars.githubusercontent.com/u/31838230?v=4?s=100" width="100px;" alt="Alan Dragicevich"/><br /><sub><b>Alan Dragicevich</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=alan-bio" title="Documentation">📖</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alan-bio"><img src="https://avatars.githubusercontent.com/u/31838230?v=4?s=100" width="100px;" alt="Alan Dragicevich"/><br /><sub><b>Alan Dragicevich</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=alan-bio" title="Documentation">📖</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/edimitchel"><img src="https://avatars.githubusercontent.com/u/2922851?v=4?s=100" width="100px;" alt="Michel EDIGHOFFER"/><br /><sub><b>Michel EDIGHOFFER</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=edimitchel" title="Documentation">📖</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/edimitchel"><img src="https://avatars.githubusercontent.com/u/2922851?v=4?s=100" width="100px;" alt="Michel EDIGHOFFER"/><br /><sub><b>Michel EDIGHOFFER</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=edimitchel" title="Documentation">📖</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gsgonzalez88"><img src="https://avatars.githubusercontent.com/u/39884678?v=4?s=100" width="100px;" alt="Gerardo Sebastian Gonzalez"/><br /><sub><b>Gerardo Sebastian Gonzalez</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=gsgonzalez88" title="Documentation">📖</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gsgonzalez88"><img src="https://avatars.githubusercontent.com/u/39884678?v=4?s=100" width="100px;" alt="Gerardo Sebastian Gonzalez"/><br /><sub><b>Gerardo Sebastian Gonzalez</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/commits?author=gsgonzalez88" title="Documentation">📖</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marryday"><img src="https://avatars.githubusercontent.com/u/57489315?v=4?s=100" width="100px;" alt="Evseev Yuriy"/><br /><sub><b>Evseev Yuriy</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/issues?q=author%3Amarryday" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomer953"><img src="https://avatars.githubusercontent.com/u/1807493?v=4?s=100" width="100px;" alt="Tomer953"/><br /><sub><b>Tomer953</b></sub></a><br /><a href="https://github.com/tomalaforge/angular-challenges/issues?q=author%3Atomer953" title="Bug reports">🐛</a> <a href="https://github.com/tomalaforge/angular-challenges/commits?author=tomer953" title="Documentation">📖</a> <a href="https://github.com/tomalaforge/angular-challenges/commits?author=tomer953" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
<p align='center'>
|
|
||||||
<img src='./logo/angular-challenge.png' height="150px"/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align='center' style='font-weight:bold'>Collection of Angular TESTING challenges</p>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Intro
|
|
||||||
|
|
||||||
This project has been created with two purposes:
|
|
||||||
|
|
||||||
- The first purpose is to assist you in becoming better at Testing using **Testing Library** and **Cypress Component Testing**. 💪
|
|
||||||
- The second purpose is to share best practices and different implementations of the same problem to gain diverse perspectives. 📖
|
|
||||||
|
|
||||||
Thanks to all these challenges, you will learn how to create **Integration Tests** to test your UI. The goal is to test your component/application as a black box, just as an end user or developer would do.
|
|
||||||
|
|
||||||
> **Learning by reading is good but learning by doing is better.**
|
|
||||||
|
|
||||||
## Testing Challenges
|
|
||||||
|
|
||||||
> Click the following badges to join your next challenge.
|
|
||||||
>
|
|
||||||
> <img src="https://img.shields.io/badge/Easy--green" alt="Easy challenge"/>
|
|
||||||
> <img src="https://img.shields.io/badge/Intermediate--orange" alt="Easy challenge"/>
|
|
||||||
> <img src="https://img.shields.io/badge/Advanced--red" alt="Easy challenge"/>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<img src="https://img.shields.io/badge/Testing--gray" alt="testing"/>
|
|
||||||
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-router-outlet/README.md"><img src="https://img.shields.io/badge/17-Router Testing-orange" alt="router outlet Testing"/></a>Testing a small application with a router</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-nested/README.md"><img src="https://img.shields.io/badge/18-Nested Comp Testing-orange" alt="nested component Testing"/></a> Testing a parent component with Child components</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-input-output/README.md"><img src="https://img.shields.io/badge/19-Input Output Testing-orange" alt="input output Testing"/></a>Testing a presentational component by setting Inputs and listening to the Outputs</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-modal/README.md"><img src="https://img.shields.io/badge/20-Modal Testing-orange" alt="modal Testing"/></a>Testing a modal</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-harness/README.md"><img src="https://img.shields.io/badge/23-Harness Testing-green" alt="harness Testing"/></a>Testing using Component Harnesses</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/create-harness/README.md"><img src="https://img.shields.io/badge/24-Create Harness-orange" alt="Create harness"/></a>Creating a custom Component Harness and Testing your application by using this harness</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-checkbox/README.md"><img src="https://img.shields.io/badge/28-Checkbox Testing-green" alt="Test a simple checkbox"/></a>Testing a very simple checkbox</span>
|
|
||||||
<span style="display:flex;gap: 5px"><a href="./apps/testing-todos-list/README.md"><img src="https://img.shields.io/badge/29-Real application Testing-red" alt="Test a real application"/></a>Testing a real-life application by creating test double on Http request and handling all asynchronous part</span>
|
|
||||||
@@ -1,32 +1,13 @@
|
|||||||
<h1>memoized function</h1>
|
# Memoization
|
||||||
|
|
||||||
> Author: Thomas Laforge
|
> Author: Thomas Laforge
|
||||||
|
|
||||||
<!-- TODO: add Information/Statement/Rules/Constraint/Steps -->
|
### Run Application
|
||||||
|
|
||||||
## Information
|
```bash
|
||||||
|
npx nx serve performance-memoized
|
||||||
|
```
|
||||||
|
|
||||||
## Statement
|
### Documentation and Instruction
|
||||||
|
|
||||||
### Step 1
|
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular-performance/35-memoize/).
|
||||||
|
|
||||||
### Step 2
|
|
||||||
|
|
||||||
### Constraints:
|
|
||||||
|
|
||||||
### Submitting your work
|
|
||||||
|
|
||||||
1. Fork the project
|
|
||||||
2. clone it
|
|
||||||
3. npm ci
|
|
||||||
4. `npx nx serve memoized`
|
|
||||||
5. _...work on it_
|
|
||||||
6. Commit your work
|
|
||||||
7. Submit a PR with a title beginning with **Answer:35** that I will review and other dev can review.
|
|
||||||
|
|
||||||
<a href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A35+label%3Aanswer"><img src="https://img.shields.io/badge/-Solutions-green" alt="memoized"/></a>
|
|
||||||
<a href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A35+label%3A"answer+author"'><img src="https://img.shields.io/badge/-Author solution-important" alt="memoized solution author"/></a>
|
|
||||||
|
|
||||||
<!-- <a href="{Blog post url}" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/-Blog post explanation-blue" alt="memoized blog article"/></a> -->
|
|
||||||
|
|
||||||
_You can ask any question on_ <a href="https://twitter.com/laforge_toma" target="_blank" rel="noopener noreferrer"><img src="./../../logo/twitter.svg" height=20px alt="twitter"/></a>
|
|
||||||
|
|||||||
36
apps/performance/ngfor-optimize/.eslintrc.json
Normal file
36
apps/performance/ngfor-optimize/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts"],
|
||||||
|
"rules": {
|
||||||
|
"@angular-eslint/directive-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"type": "attribute",
|
||||||
|
"prefix": "app",
|
||||||
|
"style": "camelCase"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@angular-eslint/component-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"prefix": "app",
|
||||||
|
"style": "kebab-case"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:@nx/angular",
|
||||||
|
"plugin:@angular-eslint/template/process-inline-templates"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.html"],
|
||||||
|
"extends": ["plugin:@nx/angular-template"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
13
apps/performance/ngfor-optimize/README.md
Normal file
13
apps/performance/ngfor-optimize/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# NgFor Optimization
|
||||||
|
|
||||||
|
> Author: Thomas Laforge
|
||||||
|
|
||||||
|
### Run Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx nx serve performance-ngfor-optimize
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation and Instruction
|
||||||
|
|
||||||
|
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular-performance/36-ngfor-optimize/).
|
||||||
22
apps/performance/ngfor-optimize/jest.config.ts
Normal file
22
apps/performance/ngfor-optimize/jest.config.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'performance-ngfor-optimize',
|
||||||
|
preset: '../../../jest.preset.js',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||||
|
coverageDirectory: '../../../coverage/apps/performance/ngfor-optimize',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|mjs|js|html)$': [
|
||||||
|
'jest-preset-angular',
|
||||||
|
{
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||||
|
snapshotSerializers: [
|
||||||
|
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||||
|
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||||
|
'jest-preset-angular/build/serializers/html-comment',
|
||||||
|
],
|
||||||
|
};
|
||||||
99
apps/performance/ngfor-optimize/project.json
Normal file
99
apps/performance/ngfor-optimize/project.json
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"name": "performance-ngfor-optimize",
|
||||||
|
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"projectType": "application",
|
||||||
|
"prefix": "app",
|
||||||
|
"sourceRoot": "apps/performance/ngfor-optimize/src",
|
||||||
|
"tags": [],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@angular-devkit/build-angular:browser",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/apps/performance/ngfor-optimize",
|
||||||
|
"index": "apps/performance/ngfor-optimize/src/index.html",
|
||||||
|
"main": "apps/performance/ngfor-optimize/src/main.ts",
|
||||||
|
"polyfills": ["zone.js"],
|
||||||
|
"tsConfig": "apps/performance/ngfor-optimize/tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"apps/performance/ngfor-optimize/src/favicon.ico",
|
||||||
|
"apps/performance/ngfor-optimize/src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"apps/performance/ngfor-optimize/src/styles.scss",
|
||||||
|
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"allowedCommonJsDependencies": ["seedrandom"]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "500kb",
|
||||||
|
"maximumError": "1mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "2kb",
|
||||||
|
"maximumError": "4kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "performance-ngfor-optimize:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "performance-ngfor-optimize:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "performance-ngfor-optimize:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nx/linter:eslint",
|
||||||
|
"outputs": ["{options.outputFile}"],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"apps/performance/ngfor-optimize/**/*.ts",
|
||||||
|
"apps/performance/ngfor-optimize/**/*.html"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nx/jest:jest",
|
||||||
|
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "apps/performance/ngfor-optimize/jest.config.ts",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"ci": {
|
||||||
|
"ci": true,
|
||||||
|
"codeCoverage": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
apps/performance/ngfor-optimize/src/app/app.component.ts
Normal file
58
apps/performance/ngfor-optimize/src/app/app.component.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Component, OnInit, inject } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { PersonService } from './list.service';
|
||||||
|
import { PersonListComponent } from './person-list.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
PersonListComponent,
|
||||||
|
FormsModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
],
|
||||||
|
providers: [PersonService],
|
||||||
|
selector: 'app-root',
|
||||||
|
template: `
|
||||||
|
<h1 class="font-semibold text-center text-3xl" title="Title">
|
||||||
|
List of Persons
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<mat-form-field class="w-3/4">
|
||||||
|
<input
|
||||||
|
placeholder="Add one member to the list"
|
||||||
|
matInput
|
||||||
|
type="text"
|
||||||
|
[(ngModel)]="label"
|
||||||
|
(keydown)="handleKey($event)" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<app-person-list
|
||||||
|
class="max-w-2xl w-3/4"
|
||||||
|
[persons]="persons()"
|
||||||
|
(delete)="personService.deletePerson($event)"
|
||||||
|
(update)="personService.updatePerson($event)" />
|
||||||
|
`,
|
||||||
|
host: {
|
||||||
|
class: 'flex items-center flex-col gap-5',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class AppComponent implements OnInit {
|
||||||
|
readonly personService = inject(PersonService);
|
||||||
|
readonly persons = this.personService.persons;
|
||||||
|
|
||||||
|
label = '';
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.personService.loadPersons();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKey(event: any) {
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
this.personService.addPerson(this.label);
|
||||||
|
this.label = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
apps/performance/ngfor-optimize/src/app/app.config.ts
Normal file
6
apps/performance/ngfor-optimize/src/app/app.config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [provideAnimations()],
|
||||||
|
};
|
||||||
15
apps/performance/ngfor-optimize/src/app/generateList.ts
Normal file
15
apps/performance/ngfor-optimize/src/app/generateList.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { randEmail, randFirstName } from '@ngneat/falso';
|
||||||
|
import { Person } from './person.model';
|
||||||
|
|
||||||
|
export function generateList() {
|
||||||
|
const arr: Person[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
arr.push({
|
||||||
|
email: randEmail(),
|
||||||
|
name: randFirstName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
44
apps/performance/ngfor-optimize/src/app/list.service.ts
Normal file
44
apps/performance/ngfor-optimize/src/app/list.service.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable, inject, signal } from '@angular/core';
|
||||||
|
import { randEmail, randFirstName } from '@ngneat/falso';
|
||||||
|
import { generateList } from './generateList';
|
||||||
|
import { Person } from './person.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PersonService {
|
||||||
|
private readonly fakeBackend = inject(FakeBackendService);
|
||||||
|
readonly persons = signal<Person[]>([]);
|
||||||
|
|
||||||
|
loadPersons() {
|
||||||
|
this.persons.set(generateList());
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePerson(email: string) {
|
||||||
|
this.persons.set(
|
||||||
|
this.fakeBackend
|
||||||
|
.returnNewList(this.persons())
|
||||||
|
.filter((p) => p.email !== email)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePerson(email: string) {
|
||||||
|
this.persons.set(
|
||||||
|
this.fakeBackend
|
||||||
|
.returnNewList(this.persons())
|
||||||
|
.map((p) => (p.email === email ? { email, name: randFirstName() } : p))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPerson(name: string) {
|
||||||
|
this.persons.set([
|
||||||
|
{ email: randEmail(), name },
|
||||||
|
...this.fakeBackend.returnNewList(this.persons()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class FakeBackendService {
|
||||||
|
returnNewList = (input: Person[]): Person[] => [
|
||||||
|
...input.map((i) => ({ ...i })),
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { Person } from './person.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-person-list',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `
|
||||||
|
<div
|
||||||
|
*ngFor="let person of persons"
|
||||||
|
class="flex justify-between items-center border-b">
|
||||||
|
<h3>{{ person.name }}</h3>
|
||||||
|
<div class="flex gap-10 py-1">
|
||||||
|
<button
|
||||||
|
class="border rounded-md p-2 bg-blue-500 text-white"
|
||||||
|
(click)="update.emit(person.email)">
|
||||||
|
UPDATE
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="border rounded-md p-2 bg-red-500 text-white"
|
||||||
|
(click)="delete.emit(person.email)">
|
||||||
|
DELETE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
host: {
|
||||||
|
class: 'w-full flex flex-col',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class PersonListComponent {
|
||||||
|
@Input() persons: Person[] = [];
|
||||||
|
@Output() delete = new EventEmitter<string>();
|
||||||
|
@Output() update = new EventEmitter<string>();
|
||||||
|
}
|
||||||
4
apps/performance/ngfor-optimize/src/app/person.model.ts
Normal file
4
apps/performance/ngfor-optimize/src/app/person.model.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface Person {
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
0
apps/performance/ngfor-optimize/src/assets/.gitkeep
Normal file
0
apps/performance/ngfor-optimize/src/assets/.gitkeep
Normal file
BIN
apps/performance/ngfor-optimize/src/favicon.ico
Normal file
BIN
apps/performance/ngfor-optimize/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
13
apps/performance/ngfor-optimize/src/index.html
Normal file
13
apps/performance/ngfor-optimize/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>performance-ngfor-optimize</title>
|
||||||
|
<base href="/" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
7
apps/performance/ngfor-optimize/src/main.ts
Normal file
7
apps/performance/ngfor-optimize/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
|
import { appConfig } from './app/app.config';
|
||||||
|
import { AppComponent } from './app/app.component';
|
||||||
|
|
||||||
|
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||||
|
console.error(err)
|
||||||
|
);
|
||||||
5
apps/performance/ngfor-optimize/src/styles.scss
Normal file
5
apps/performance/ngfor-optimize/src/styles.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
2
apps/performance/ngfor-optimize/src/test-setup.ts
Normal file
2
apps/performance/ngfor-optimize/src/test-setup.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import 'jest-preset-angular/setup-jest';
|
||||||
14
apps/performance/ngfor-optimize/tailwind.config.js
Normal file
14
apps/performance/ngfor-optimize/tailwind.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
|
||||||
|
...createGlobPatternsForDependencies(__dirname),
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
10
apps/performance/ngfor-optimize/tsconfig.app.json
Normal file
10
apps/performance/ngfor-optimize/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../../dist/out-tsc",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"files": ["src/main.ts"],
|
||||||
|
"include": ["src/**/*.d.ts"],
|
||||||
|
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||||
|
}
|
||||||
7
apps/performance/ngfor-optimize/tsconfig.editor.json
Normal file
7
apps/performance/ngfor-optimize/tsconfig.editor.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
}
|
||||||
|
}
|
||||||
32
apps/performance/ngfor-optimize/tsconfig.json
Normal file
32
apps/performance/ngfor-optimize/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.editor.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
15
apps/performance/ngfor-optimize/tsconfig.spec.json
Normal file
15
apps/performance/ngfor-optimize/tsconfig.spec.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||||
|
},
|
||||||
|
"files": ["src/test-setup.ts"],
|
||||||
|
"include": [
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ export const randStudent = (): Student => ({
|
|||||||
id: factoryStudent(),
|
id: factoryStudent(),
|
||||||
firstname: randFirstName(),
|
firstname: randFirstName(),
|
||||||
lastname: randLastName(),
|
lastname: randLastName(),
|
||||||
mainTeacher: teachers[randNumber({ max: teachers.length })],
|
mainTeacher: teachers[randNumber({ max: teachers.length - 1 })],
|
||||||
school: randWord(),
|
school: randWord(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"total": 35
|
"total": 36,
|
||||||
|
"🟢": 13,
|
||||||
|
"🟠": 116,
|
||||||
|
"🔴": 207
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
import starlight from '@astrojs/starlight';
|
import starlight from '@astrojs/starlight';
|
||||||
import vercel from '@astrojs/vercel/static';
|
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: 'static',
|
|
||||||
adapter: vercel({
|
|
||||||
webAnalytics: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
integrations: [
|
integrations: [
|
||||||
starlight({
|
starlight({
|
||||||
title: 'Angular Challenges',
|
title: 'Angular Challenges',
|
||||||
@@ -19,7 +12,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
favicon: './angular-challenge.ico',
|
favicon: './angular-challenge.ico',
|
||||||
social: {
|
social: {
|
||||||
github: 'https://github.com/withastro/starlight',
|
github: 'https://github.com/tomalaforge/angular-challenges',
|
||||||
linkedin: 'https://www.linkedin.com/in/thomas-laforge-2b05a945/',
|
linkedin: 'https://www.linkedin.com/in/thomas-laforge-2b05a945/',
|
||||||
twitter: 'https://twitter.com/laforge_toma',
|
twitter: 'https://twitter.com/laforge_toma',
|
||||||
},
|
},
|
||||||
|
|||||||
589
docs/package-lock.json
generated
589
docs/package-lock.json
generated
@@ -9,7 +9,6 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.10.0",
|
"@astrojs/starlight": "^0.10.0",
|
||||||
"@astrojs/vercel": "^5.0.1",
|
|
||||||
"@fontsource/ibm-plex-serif": "^5.0.8",
|
"@fontsource/ibm-plex-serif": "^5.0.8",
|
||||||
"astro": "^3.0.6",
|
"astro": "^3.0.6",
|
||||||
"sharp": "^0.32.5"
|
"sharp": "^0.32.5"
|
||||||
@@ -152,23 +151,6 @@
|
|||||||
"node": ">=18.14.1"
|
"node": ">=18.14.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@astrojs/vercel": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@astrojs/vercel/-/vercel-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-qDXTBSRSzTRourx90QcB/uARUcABDRLfrDggNtyiyyh6WoJPK7BuhKDYh0Bd372Mrt2mm15ZSGaxHJLmbpv2wg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@astrojs/internal-helpers": "0.2.0",
|
|
||||||
"@vercel/analytics": "^1.0.2",
|
|
||||||
"@vercel/nft": "^0.23.1",
|
|
||||||
"esbuild": "^0.19.2",
|
|
||||||
"fast-glob": "^3.3.1",
|
|
||||||
"set-cookie-parser": "^2.6.0",
|
|
||||||
"web-vitals": "^3.4.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"astro": "^3.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.22.13",
|
"version": "7.22.13",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||||
@@ -881,25 +863,6 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mapbox/node-pre-gyp": {
|
|
||||||
"version": "1.0.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
|
||||||
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"detect-libc": "^2.0.0",
|
|
||||||
"https-proxy-agent": "^5.0.0",
|
|
||||||
"make-dir": "^3.1.0",
|
|
||||||
"node-fetch": "^2.6.7",
|
|
||||||
"nopt": "^5.0.0",
|
|
||||||
"npmlog": "^5.0.1",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"semver": "^7.3.5",
|
|
||||||
"tar": "^6.1.11"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"node-pre-gyp": "bin/node-pre-gyp"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mdx-js/mdx": {
|
"node_modules/@mdx-js/mdx": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz",
|
||||||
@@ -1025,23 +988,6 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
|
||||||
"version": "4.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
|
|
||||||
"integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"estree-walker": "^2.0.1",
|
|
||||||
"picomatch": "^2.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/acorn": {
|
"node_modules/@types/acorn": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
||||||
@@ -1175,45 +1121,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz",
|
||||||
"integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw=="
|
"integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw=="
|
||||||
},
|
},
|
||||||
"node_modules/@vercel/analytics": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-BZFxVrv24VbNNl5xMxqUojQIegEeXMI6rX3rg1uVLYUEXsuKNBSAEQf4BWEcjQDp/8aYJOj6m8V4PUA3x/cxgg=="
|
|
||||||
},
|
|
||||||
"node_modules/@vercel/nft": {
|
|
||||||
"version": "0.23.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.23.1.tgz",
|
|
||||||
"integrity": "sha512-NE0xSmGWVhgHF1OIoir71XAd0W0C1UE3nzFyhpFiMr3rVhetww7NvM1kc41trBsPG37Bh+dE5FYCTMzM/gBu0w==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mapbox/node-pre-gyp": "^1.0.5",
|
|
||||||
"@rollup/pluginutils": "^4.0.0",
|
|
||||||
"acorn": "^8.6.0",
|
|
||||||
"async-sema": "^3.1.1",
|
|
||||||
"bindings": "^1.4.0",
|
|
||||||
"estree-walker": "2.0.2",
|
|
||||||
"glob": "^7.1.3",
|
|
||||||
"graceful-fs": "^4.2.9",
|
|
||||||
"micromatch": "^4.0.2",
|
|
||||||
"node-gyp-build": "^4.2.2",
|
|
||||||
"resolve-from": "^5.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"nft": "out/cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@vercel/nft/node_modules/estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"node_modules/abbrev": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
|
||||||
},
|
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.10.0",
|
"version": "8.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||||
@@ -1233,17 +1140,6 @@
|
|||||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/agent-base": {
|
|
||||||
"version": "6.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
|
||||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align": {
|
"node_modules/ansi-align": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
@@ -1328,23 +1224,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aproba": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
|
|
||||||
},
|
|
||||||
"node_modules/are-we-there-yet": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
|
||||||
"dependencies": {
|
|
||||||
"delegates": "^1.0.0",
|
|
||||||
"readable-stream": "^3.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@@ -1497,11 +1376,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/async-sema": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="
|
|
||||||
},
|
|
||||||
"node_modules/b4a": {
|
"node_modules/b4a": {
|
||||||
"version": "1.6.4",
|
"version": "1.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
|
||||||
@@ -1516,11 +1390,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/balanced-match": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
|
||||||
},
|
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -1571,14 +1440,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bindings": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
|
||||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"file-uri-to-path": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bl": {
|
"node_modules/bl": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
|
||||||
@@ -1647,15 +1508,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
@@ -1943,14 +1795,6 @@
|
|||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/color-support": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
|
||||||
"bin": {
|
|
||||||
"color-support": "bin.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color/node_modules/color-convert": {
|
"node_modules/color/node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -1981,16 +1825,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz",
|
||||||
"integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="
|
"integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="
|
||||||
},
|
},
|
||||||
"node_modules/concat-map": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
|
||||||
},
|
|
||||||
"node_modules/console-control-strings": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
|
|
||||||
},
|
|
||||||
"node_modules/convert-source-map": {
|
"node_modules/convert-source-map": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||||
@@ -2072,11 +1906,6 @@
|
|||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/delegates": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
|
||||||
},
|
|
||||||
"node_modules/dequal": {
|
"node_modules/dequal": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
@@ -2367,11 +2196,6 @@
|
|||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/file-uri-to-path": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
|
||||||
},
|
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -2412,38 +2236,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"node_modules/fs-minipass": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs-minipass/node_modules/minipass": {
|
|
||||||
"version": "3.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
|
||||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs-minipass/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
},
|
|
||||||
"node_modules/fs.realpath": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
|
||||||
},
|
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -2462,62 +2254,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
},
|
},
|
||||||
"node_modules/gauge": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"aproba": "^1.0.3 || ^2.0.0",
|
|
||||||
"color-support": "^1.1.2",
|
|
||||||
"console-control-strings": "^1.0.0",
|
|
||||||
"has-unicode": "^2.0.1",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"signal-exit": "^3.0.0",
|
|
||||||
"string-width": "^4.2.3",
|
|
||||||
"strip-ansi": "^6.0.1",
|
|
||||||
"wide-align": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -2547,25 +2283,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
|
||||||
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
|
||||||
"version": "7.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
||||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.1.1",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
@@ -2643,11 +2360,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-unicode": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
|
||||||
},
|
|
||||||
"node_modules/hast-util-from-parse5": {
|
"node_modules/hast-util-from-parse5": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
|
||||||
@@ -2856,18 +2568,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||||
},
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
|
||||||
"dependencies": {
|
|
||||||
"agent-base": "6",
|
|
||||||
"debug": "4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/human-signals": {
|
"node_modules/human-signals": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
||||||
@@ -2915,15 +2615,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/inflight": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
|
||||||
"dependencies": {
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"wrappy": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/inherits": {
|
"node_modules/inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
@@ -3347,28 +3038,6 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/make-dir": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
|
||||||
"dependencies": {
|
|
||||||
"semver": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/make-dir/node_modules/semver": {
|
|
||||||
"version": "6.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
|
||||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/markdown-extensions": {
|
"node_modules/markdown-extensions": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
||||||
@@ -4513,17 +4182,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minimist": {
|
"node_modules/minimist": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
@@ -4532,53 +4190,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minipass": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minizlib": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^3.0.0",
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minizlib/node_modules/minipass": {
|
|
||||||
"version": "3.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
|
||||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minizlib/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
},
|
|
||||||
"node_modules/mkdirp": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
|
||||||
"bin": {
|
|
||||||
"mkdirp": "bin/cmd.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mkdirp-classic": {
|
"node_modules/mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
@@ -4671,54 +4282,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
|
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch": {
|
|
||||||
"version": "2.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
|
||||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": "^5.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "4.x || >=6.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"encoding": "^0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"encoding": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-gyp-build": {
|
|
||||||
"version": "4.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz",
|
|
||||||
"integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==",
|
|
||||||
"bin": {
|
|
||||||
"node-gyp-build": "bin.js",
|
|
||||||
"node-gyp-build-optional": "optional.js",
|
|
||||||
"node-gyp-build-test": "build-test.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.13",
|
"version": "2.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||||
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
|
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
|
||||||
},
|
},
|
||||||
"node_modules/nopt": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"abbrev": "1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"nopt": "bin/nopt.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -4757,17 +4325,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npmlog": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
|
||||||
"dependencies": {
|
|
||||||
"are-we-there-yet": "^2.0.0",
|
|
||||||
"console-control-strings": "^1.1.0",
|
|
||||||
"gauge": "^3.0.0",
|
|
||||||
"set-blocking": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nth-check": {
|
"node_modules/nth-check": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
@@ -4779,14 +4336,6 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -4964,14 +4513,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-is-absolute": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-key": {
|
"node_modules/path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
@@ -5497,14 +5038,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/resolve-from": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/restore-cursor": {
|
"node_modules/restore-cursor": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
||||||
@@ -5610,20 +5143,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rimraf": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
|
||||||
"dependencies": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"rimraf": "bin.js"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.29.2",
|
"version": "3.29.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz",
|
||||||
@@ -5748,16 +5267,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
|
||||||
"integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="
|
"integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="
|
||||||
},
|
},
|
||||||
"node_modules/set-blocking": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
|
||||||
},
|
|
||||||
"node_modules/set-cookie-parser": {
|
|
||||||
"version": "2.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
|
||||||
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
|
||||||
},
|
|
||||||
"node_modules/sharp": {
|
"node_modules/sharp": {
|
||||||
"version": "0.32.5",
|
"version": "0.32.5",
|
||||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.5.tgz",
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.5.tgz",
|
||||||
@@ -6087,22 +5596,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
|
||||||
"version": "6.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
|
|
||||||
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"chownr": "^2.0.0",
|
|
||||||
"fs-minipass": "^2.0.0",
|
|
||||||
"minipass": "^5.0.0",
|
|
||||||
"minizlib": "^2.1.1",
|
|
||||||
"mkdirp": "^1.0.3",
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tar-fs": {
|
"node_modules/tar-fs": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
||||||
@@ -6123,19 +5616,6 @@
|
|||||||
"streamx": "^2.15.0"
|
"streamx": "^2.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar/node_modules/chownr": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tar/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
},
|
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
@@ -6155,11 +5635,6 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tr46": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
|
||||||
},
|
|
||||||
"node_modules/trim-lines": {
|
"node_modules/trim-lines": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||||
@@ -6945,25 +6420,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-vitals": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-n9fZ5/bG1oeDkyxLWyep0eahrNcPDF6bFqoyispt7xkW0xhDzpUBTgyDKqWDi1twT0MgH4HvvqzpUyh0ZxZV4A=="
|
|
||||||
},
|
|
||||||
"node_modules/webidl-conversions": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-url": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
|
||||||
"dependencies": {
|
|
||||||
"tr46": "~0.0.3",
|
|
||||||
"webidl-conversions": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -6998,51 +6454,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wide-align": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wide-align/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wide-align/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
|
||||||
},
|
|
||||||
"node_modules/wide-align/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wide-align/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/widest-line": {
|
"node_modules/widest-line": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.10.0",
|
"@astrojs/starlight": "^0.10.0",
|
||||||
"@astrojs/vercel": "^5.0.1",
|
|
||||||
"@fontsource/ibm-plex-serif": "^5.0.8",
|
"@fontsource/ibm-plex-serif": "^5.0.8",
|
||||||
"astro": "^3.0.6",
|
"astro": "^3.0.6",
|
||||||
"sharp": "^0.32.5"
|
"sharp": "^0.32.5"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
BIN
docs/src/assets/angular-performance/35/memoize-profiler.png
Normal file
BIN
docs/src/assets/angular-performance/35/memoize-profiler.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
@@ -2,35 +2,39 @@
|
|||||||
title: 🟠 Optimize Change Detection
|
title: 🟠 Optimize Change Detection
|
||||||
description: Challenge 12 about optimizing the number of change detection cycle while scrolling
|
description: Challenge 12 about optimizing the number of change detection cycle while scrolling
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 12
|
order: 107
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #12</div>
|
<div class="chip">Challenge #12</div>
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
In this challenge, you will need to optimize the change detection cycles run by Angular.
|
In Angular, there is a library called <b>Zone.js</b> that performs a lot of magic to simplify a developer's life. Zone.js monkey patches all DOM events so that it will recheck and rerender the view when something has changed inside the application. The developer doesn't have to manually trigger change detection.
|
||||||
|
|
||||||
Zone.js triggers a change detection cycle each time a scroll event is dispatched. However we only want to show or hide a button at a specific scroll position. Therefore, we only want to refresh our application once.
|
However, sometimes Zone.js triggers a lot more change detection than needed. For example, when you are listening to a scroll event, each scroll event will dispatch a new change detection cycle.
|
||||||
|
|
||||||
> You can vizualise how many times CD is triggered by installing the [Angular chrome devTool](https://chrome.google.com/webstore/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh) and starting a new recording on the profiler tab.
|
In this challenge, we only need to refresh the view at a specific scroll position to display or hide a button. All other cycles are unnecessary.
|
||||||
|
|
||||||
The following video will explain what is the goal of this challenge.
|
To have a better visualization of the problem, profile your application with Angular Dev Tools.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If you don't know how to use it, read [the performance introduction page](/challenges/angular-performance/) first and come back after.
|
||||||
|
:::
|
||||||
|
|
||||||
|
You can learn more details about zone pollution and how to resolve it [here](https://angular.io/guide/change-detection-zone-pollution).
|
||||||
|
|
||||||
|
The following video will explain more in-depth the issue of this application.
|
||||||
|
|
||||||
<video controls src="https://user-images.githubusercontent.com/30832608/209819211-58d9ddcf-e1ad-4a78-8a7a-2be9d729e3f1.mov">
|
<video controls src="https://user-images.githubusercontent.com/30832608/209819211-58d9ddcf-e1ad-4a78-8a7a-2be9d729e3f1.mov">
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
## Statement
|
## Statement
|
||||||
|
|
||||||
Your goal for this challenge is to avoid all unnecessary change detection cycles and trigger a CD only when needed.
|
Your goal for this challenge is to avoid all unnecessary change detection cycles and trigger a change detection only when needed.
|
||||||
|
|
||||||
## Constraint:
|
## Constraint:
|
||||||
|
|
||||||
You cannot opt-out of zone.js. If this code is part of a large project and you opt out of zone.js, you will break many things within your application.
|
You cannot opt-out of Zone.js globally. If this code is part of a large project and you opt out of Zone.js, you will break your application without any doubt.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -49,7 +53,7 @@ Your PR title must start with <b>Answer:12</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A{challenge number}+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A{challenge number}+label%3A"answer+author"'
|
||||||
alt="Optimize Change Detection solution author">
|
alt="Optimize Change Detection solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,36 +2,40 @@
|
|||||||
title: 🟢 Default vs OnPush
|
title: 🟢 Default vs OnPush
|
||||||
description: Challenge 34 is about learning the difference between Default and OnPush Change Detection Strategy.
|
description: Challenge 34 is about learning the difference between Default and OnPush Change Detection Strategy.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 34
|
order: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #34</div>
|
<div class="chip">Challenge #34</div>
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
In this series of challenges, you will learn how to optimize and enhance the performance of your Angular Application.
|
In this challenge, we will explore the differences and impacts of using `ChangeDetectionStrategy.Default` versus `ChangeDetectionStrategy.OnPush`.
|
||||||
|
|
||||||
The first step is to download the [Angular DevTools Chrome extention](https://chrome.google.com/webstore/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh) if you haven't already done so. This extension allows you to profile your application and detect performance issues.
|
You can read the [Angular documentation](https://angular.io/guide/change-detection-skipping-subtrees) to learn more about the differences between these strategies.
|
||||||
|
|
||||||
In this challenge, we will explore the differences and impacts of using `ChangeDetectionStrategy.Default` versus `ChangeDetectionStrategy.OnPush`. To provide a clearer demonstration, I have added color enlightment to each component and each row in our application. However, in real-world scenarios, you will not have such visualization. This is where the Angular DevTool profiler comes to the rescue.
|
In this challenge, all components start with the `Default` strategy. When you type letters inside the input field, you will notice that all components are highlighted in orange.
|
||||||
|
|
||||||
Start by serving this application by running: `npx nx serve performance-default-onpush` inside your terminal. Then open Chrome DevTool by pressing **F12** and switch to the Angular Tab. From there you can select the Profiler tab as shown below.
|
:::note
|
||||||
|
I added color highlighting to each component and each row to provide a better visualization of when a component is rerendered.
|
||||||
|
:::
|
||||||
|
|
||||||

|
As you can see, each letter triggers a new change detection cycle, and all components are rerendered, causing performance issues.
|
||||||
|
|
||||||
Start profiling your application and type some letters inside the input field. You will notice that each element of your application will flash at each change detection cycle and the profiler will show you a bar for each change detection cycle.
|
Let's use the <b>Angular DevTool</b> to profile our application and understand how this tool can help us understand what is happening inside our application.
|
||||||
|
|
||||||
If you click on one of the bars (indicated by the yellow arrow on the picture below), you can see that `PersonListComponent`, `RandomComponent` and all the `MatListItem` are impacted by the change detection cycle, even when we only interact with the input field.
|
:::note
|
||||||
|
If you don't know how to use it, read [the performance introduction page](/challenges/angular-performance/) first and come back after.
|
||||||
|
:::
|
||||||
|
|
||||||

|
Now, start profiling your application and type some letters inside the input field to trigger some change detection cycles.
|
||||||
|
|
||||||
|
If you click on one of the bars (indicated by the yellow arrow in the picture below), you can see that `PersonListComponent`, `RandomComponent`, and all the `MatListItem` are impacted by the change detection cycle, even when we only interact with the input field.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Statement
|
## Statement
|
||||||
|
|
||||||
The goal of this challenge is to improve the clustering of change detection within the application.
|
The goal of this challenge is to improve the clustering of change detection within the application using the `OnPush` change detection strategy, but not only...
|
||||||
|
|
||||||
## Hints:
|
## Hints:
|
||||||
|
|
||||||
@@ -66,7 +70,7 @@ Your PR title must start with <b>Answer:34</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A34+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A34+label%3A"answer+author"'
|
||||||
alt="Default vs OnPush solution author">
|
alt="Default vs OnPush solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
title: 🟢 Memoization
|
||||||
|
description: Challenge 35 is about learning
|
||||||
|
sidebar:
|
||||||
|
order: 8
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="chip">Challenge #35</div>
|
||||||
|
|
||||||
|
## Information
|
||||||
|
|
||||||
|
In Angular, <b>pure Pipes</b> are very powerful because the value is memoized, which means if the input value doesn't change, the `transform` function of the pipe is not recomputed, and the cached value is outputted.
|
||||||
|
|
||||||
|
You can learn more about pipes in the [Angular documentation](https://angular.io/guide/pipes) and inside this [deep dive article](https://medium.com/ngconf/deep-dive-into-angular-pipes-c040588cd15d).
|
||||||
|
|
||||||
|
In this challenge, we start with a button to load a list of people. Each person is associated with a number, and we will use the Fibonacci calculation to create a heavy computation that will slow down the application.
|
||||||
|
|
||||||
|
Once the list is loaded, try typing some letters inside the input field. You will notice that the application is very slow, even though you are only performing very basic typing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
We will not focus on the initial loading of the list in this challenge.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Let's use the <b>Angular DevTool</b> to profile our application and understand how this tool can help us understand what is happening inside our application.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If you don't know how to use it, read [the performance introduction page](/challenges/angular-performance/) first and come back after.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Now, start profiling your application and type some letters inside the input field. You will see some red bars showing up inside the profiler panel.
|
||||||
|
|
||||||
|
If you click on one of the bars (indicated by the yellow arrow in the picture below), you will see that the change detection cycle is taking more than 3s in `PersonListComponent`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Statement
|
||||||
|
|
||||||
|
The goal of this challenge is to understand what is causing this latency and to improve it.
|
||||||
|
|
||||||
|
## Hints:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Hint 1</summary>
|
||||||
|
|
||||||
|
Use `Pipes` to memoize the Fibonnaci computation.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Start the project by running: `npx nx serve performance-memoized`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip[Reminder]
|
||||||
|
Your PR title must start with <b>Answer:35</b>.
|
||||||
|
:::
|
||||||
|
|
||||||
|
<div class="article-footer">
|
||||||
|
<a
|
||||||
|
href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A35+label%3Aanswer"
|
||||||
|
alt="Memoization community solutions">
|
||||||
|
❖ Community Answers
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A35+label%3A"answer+author"'
|
||||||
|
alt="Memoization solution author">
|
||||||
|
▶︎ Author Answer
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
title: 🟢 NgFor Optimization
|
||||||
|
description: Challenge 36 is about ...
|
||||||
|
sidebar:
|
||||||
|
order: 13
|
||||||
|
badge: New
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="chip">Challenge #36</div>
|
||||||
|
|
||||||
|
## Information
|
||||||
|
|
||||||
|
In this application, we have a list of individuals that we can add, delete or update. If you open the developer Chrome panel by pressing **F12**, go to he <b>source</b> tab, and expand the element to see the list, you will notice that each time, you add, delete or update a list item, the entire DOM elements are destroyed and initialized again. (See video below).
|
||||||
|
|
||||||
|
<video controls src="https://github.com/tomalaforge/angular-challenges/assets/30832608/71b90307-3ee3-42c0-a532-b67ce4f20bf6">
|
||||||
|
</video>
|
||||||
|
|
||||||
|
We can also use the <b>Angular DevTool</b> to profile our application and understand what is happening inside our application. I will show you how to do it inside the following video.
|
||||||
|
|
||||||
|
<video controls src="https://github.com/tomalaforge/angular-challenges/assets/30832608/dd8108c6-1d89-4b05-9aa5-e760bd6f7f11">
|
||||||
|
</video>
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If you don't know how to use it, read [the performance introduction page](/challenges/angular-performance/) first and come back after.
|
||||||
|
:::
|
||||||
|
|
||||||
|
If you need more information about `NgFor`, I invite you to read the [documentation](https://angular.io/api/common/NgFor) first.
|
||||||
|
|
||||||
|
## Statement
|
||||||
|
|
||||||
|
The goal of this challenge is to understand what is causing this DOM refresh and to solve it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Start the project by running: `npx nx serve ngfor-optimize`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip[Reminder]
|
||||||
|
Your PR title must start with <b>Answer:36</b>.
|
||||||
|
:::
|
||||||
|
|
||||||
|
<div class="article-footer">
|
||||||
|
<a
|
||||||
|
href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A36+label%3Aanswer"
|
||||||
|
alt="NgFor Optimization community solutions">
|
||||||
|
❖ Community Answers
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A36+label%3A"answer+author"'
|
||||||
|
alt="NgFor Optimization solution author">
|
||||||
|
▶︎ Author Answer
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: Angular Performance
|
||||||
|
prev: false
|
||||||
|
next: false
|
||||||
|
description: Learn how to use the Angular Devtool chrome extension.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { LinkCard } from '@astrojs/starlight/components';
|
||||||
|
|
||||||
|
In this series of challenges about performance, you will learn how to optimize and enhance the performance of your Angular application.
|
||||||
|
|
||||||
|
Before starting to resolve any challenge, I invite you to download the [Angular DevTools Chrome extention](https://chrome.google.com/webstore/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh) if you haven't already done so.
|
||||||
|
|
||||||
|
This extension allows you to profile your application and detect performance issues, which is very useful for understanding where performance issues can occur.
|
||||||
|
|
||||||
|
## How to use it
|
||||||
|
|
||||||
|
When you serve an Angular application, you can inspect a page by pressing <b>F12</b>, which will open the <b>Chrome developer tools</b>. Then navigate to the <b>Angular tab</b>. From there, you can select the <b>Profiler tab</b> as shown below.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You can now profile your application by clicking on the record button. You can play with your application and see when change detection is triggered and which components are rerendered.
|
||||||
|
|
||||||
|
:::tip[Learn more]
|
||||||
|
You can learn more on the [documentation page](https://angular.io/guide/devtools).
|
||||||
|
:::
|
||||||
|
|
||||||
|
Now that you know how to use the <b>Angular DevTool</b>, you can choose a challenge, profile it, and resolve it.
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Optimize Change Detection"
|
||||||
|
description="Learn how to remove zone pollution."
|
||||||
|
href="/challenges/angular-performance/12-scroll-cd/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟢 Default vs OnPush"
|
||||||
|
description="Learn the difference between Default and OnPush change detection strategies."
|
||||||
|
href="/challenges/angular-performance/34-default-onpush/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟢 Memoization"
|
||||||
|
description="Learn the power of pure pipes."
|
||||||
|
href="/challenges/angular-performance/35-memoize/"
|
||||||
|
/>
|
||||||
@@ -56,7 +56,16 @@ Your PR title must start with <b>Answer:1</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Projection blog article">
|
alt="Projection blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://www.youtube.com/watch?v=npyEyUZxoIw&ab_channel=ArthurLannelucq"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
alt="Projection video by Arthur Lannelucq">
|
||||||
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M23.5 6.2A3 3 0 0 0 21.4 4c-1.9-.5-9.4-.5-9.4-.5s-7.5 0-9.4.5A3 3 0 0 0 .5 6.3C0 8 0 12 0 12s0 4 .5 5.8A3 3 0 0 0 2.6 20c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 0 0 2.1-2c.5-2 .5-5.9.5-5.9s0-4-.5-5.8zm-14 9.4V8.4l6.3 3.6-6.3 3.6z"></path></svg>
|
||||||
|
Video
|
||||||
|
<span class="flag">🇫🇷<span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Utility Wrapper Pipe
|
title: 🔴 Utility Wrapper Pipe
|
||||||
description: Challenge 10 is about creating a pipe to wrap utilities
|
description: Challenge 10 is about creating a pipe to wrap utilities
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 10
|
order: 202
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -40,7 +40,7 @@ Your PR title must start with <b>Answer:10</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A10+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A10+label%3A"answer+author"'
|
||||||
alt="Utility Wrapper Pipe solution author">
|
alt="Utility Wrapper Pipe solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Highly Customizable CSS
|
title: 🟠 Highly Customizable CSS
|
||||||
description: Challenge 13 is about creating highly customizable CSS styles
|
description: Challenge 13 is about creating highly customizable CSS styles
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 13
|
order: 104
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -42,7 +42,7 @@ Your PR title must start with <b>Answer:13</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A13+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A13+label%3A"answer+author"'
|
||||||
alt="Highly Customizable CSS solution author">
|
alt="Highly Customizable CSS solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Master Dependancy Injection
|
title: 🔴 Master Dependancy Injection
|
||||||
description: Challenge 16 is about masjering how dependancy injection works
|
description: Challenge 16 is about masjering how dependancy injection works
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 16
|
order: 203
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -45,7 +45,7 @@ Your PR title must start with <b>Answer:16</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A16+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A16+label%3A"answer+author"'
|
||||||
alt="Master Dependancy Injection solution author">
|
alt="Master Dependancy Injection solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Anchor Navigation
|
title: 🟢 Anchor Navigation
|
||||||
description: Challenge 21 is about navigating inside the page with anchor
|
description: Challenge 21 is about navigating inside the page with anchor
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 21
|
order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -37,7 +37,7 @@ Your PR title must start with <b>Answer:21</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A21+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A21+label%3A"answer+author"'
|
||||||
alt="Anchor Navigation solution author">
|
alt="Anchor Navigation solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 @RouterInput()
|
title: 🟢 @RouterInput()
|
||||||
description: Challenge 22 is about using the @Input decorator to retreive router params.
|
description: Challenge 22 is about using the @Input decorator to retreive router params.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 22
|
order: 5
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -32,7 +32,7 @@ Your PR title must start with <b>Answer:22</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A22+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A22+label%3A"answer+author"'
|
||||||
alt="@RouterInput() solution author">
|
alt="@RouterInput() solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -41,7 +41,7 @@ Your PR title must start with <b>Answer:22</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="@RouterInput() blog article">
|
alt="@RouterInput() blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Directive Enhancement
|
title: 🟠 Directive Enhancement
|
||||||
description: Challenge 3 is about enhancing a built-in directive
|
description: Challenge 3 is about enhancing a built-in directive
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 3
|
order: 101
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -60,7 +60,7 @@ Your PR title must start with <b>Answer:3</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A3+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A3+label%3A"answer+author"'
|
||||||
alt="Directive Enhancement solution author">
|
alt="Directive Enhancement solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -69,7 +69,7 @@ Your PR title must start with <b>Answer:3</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Directive Enhancement blog article">
|
alt="Directive Enhancement blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Interoperability Rxjs/Signal
|
title: 🔴 Interoperability Rxjs/Signal
|
||||||
description: Challenge 30 is about learning how to mix signal with Rxjs
|
description: Challenge 30 is about learning how to mix signal with Rxjs
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 30
|
order: 204
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -34,7 +34,7 @@ Your PR title must start with <b>Answer:30</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A30+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A30+label%3A"answer+author"'
|
||||||
alt="Interoperability Rxjs/Signal solution author">
|
alt="Interoperability Rxjs/Signal solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Module to Standalone
|
title: 🟢 Module to Standalone
|
||||||
description: Challenge 31 is about migrating a module based application to a standalone application.
|
description: Challenge 31 is about migrating a module based application to a standalone application.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 31
|
order: 6
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -42,7 +42,7 @@ Your PR title must start with <b>Answer:31</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A31+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A31+label%3A"answer+author"'
|
||||||
alt="Module to Standalone solution author">
|
alt="Module to Standalone solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Change Detection Bug
|
title: 🟠 Change Detection Bug
|
||||||
description: Challenge 32 is about debugging an application that has issue when change detection is triggered
|
description: Challenge 32 is about debugging an application that has issue when change detection is triggered
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 32
|
order: 105
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -11,11 +11,13 @@ WIP: The following documentation will be reviewed and improved. However, you can
|
|||||||
|
|
||||||
<div class="chip">Challenge #32</div>
|
<div class="chip">Challenge #32</div>
|
||||||
|
|
||||||
|
:::note
|
||||||
This challenge is inspired by a real-life example that I simplified to create this nice challenge.
|
This challenge is inspired by a real-life example that I simplified to create this nice challenge.
|
||||||
|
:::
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
In this small application, we have a navigation menu to route our application to either `barComponent` or `FooComponent`. However our application is not loading and no errors are displayed inside the console.
|
In this small application, we have a navigation menu to route our application to either `BarComponent` or `FooComponent`. However our application is not loading and no errors are displayed inside the console.
|
||||||
|
|
||||||
## Statement
|
## Statement
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ Your PR title must start with <b>Answer:32</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A32+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A32+label%3A"answer+author"'
|
||||||
alt="Change Detection Bug solution author">
|
alt="Change Detection Bug solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Decoupling Components
|
title: 🟠 Decoupling Components
|
||||||
description: Challenge 33 is about decoupling two strongly coupled components using Injection Token
|
description: Challenge 33 is about decoupling two strongly coupled components using Injection Token
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 33
|
order: 106
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -50,7 +50,7 @@ Your PR title must start with <b>Answer:33</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A33+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A33+label%3A"answer+author"'
|
||||||
alt="Decoupling Components solution author">
|
alt="Decoupling Components solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Typed ContextOutlet
|
title: 🔴 Typed ContextOutlet
|
||||||
description: Challenge 4 is about strongly typing ngContextOutlet directives
|
description: Challenge 4 is about strongly typing ngContextOutlet directives
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 4
|
order: 201
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -58,7 +58,7 @@ Your PR title must start with <b>Answer:4</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A4+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A4+label%3A"answer+author"'
|
||||||
alt="Typed ContextOutlet solution author">
|
alt="Typed ContextOutlet solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -67,7 +67,7 @@ Your PR title must start with <b>Answer:4</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Typed ContextOutlet blog article">
|
alt="Typed ContextOutlet blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Crud application
|
title: 🟢 Crud application
|
||||||
description: Challenge 5 is about refactoring a crud application
|
description: Challenge 5 is about refactoring a crud application
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 5
|
order: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -67,12 +67,12 @@ Your PR title must start with <b>Answer:5</b>.
|
|||||||
|
|
||||||
<div class="article-footer">
|
<div class="article-footer">
|
||||||
<a
|
<a
|
||||||
href="https://jsonplaceholder.typicode.com/"
|
href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A5+label%3Aanswer"
|
||||||
alt="Crud application community solutions">
|
alt="Crud application community solutions">
|
||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A5+label%3Aanswer'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A5+label%3A"answer+author"'
|
||||||
alt="Crud application solution author">
|
alt="Crud application solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Structural Directive
|
title: 🟠 Structural Directive
|
||||||
description: Challenge 6 is about creating a structural directive to handle permissions
|
description: Challenge 6 is about creating a structural directive to handle permissions
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 6
|
order: 102
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -73,7 +73,7 @@ Your PR title must start with <b>Answer:6</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A6+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A6+label%3A"answer+author"'
|
||||||
alt="Structural Directive solution author">
|
alt="Structural Directive solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -82,7 +82,7 @@ Your PR title must start with <b>Answer:6</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Structural Directive blog article">
|
alt="Structural Directive blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Pure Pipe
|
title: 🟢 Pure Pipe
|
||||||
description: Challenge 8 is about creating a pure pipe
|
description: Challenge 8 is about creating a pure pipe
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 8
|
order: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -40,7 +40,7 @@ Your PR title must start with <b>Answer:8</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A8+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A8+label%3A"answer+author"'
|
||||||
alt="Pure Pipe solution author">
|
alt="Pure Pipe solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -49,7 +49,7 @@ Your PR title must start with <b>Answer:8</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Pure Pipe blog article">
|
alt="Pure Pipe blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Wrap Function Pipe
|
title: 🟠 Wrap Function Pipe
|
||||||
description: Challenge 9 is about creating a pipe to wrap component fonctions
|
description: Challenge 9 is about creating a pipe to wrap component fonctions
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 9
|
order: 103
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -41,7 +41,7 @@ Your PR title must start with <b>Answer:9</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A9+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A9+label%3A"answer+author"'
|
||||||
alt="Wrap Function Pipe solution author">
|
alt="Wrap Function Pipe solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -50,7 +50,7 @@ Your PR title must start with <b>Answer:9</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Wrap Function Pipe blog article">
|
alt="Wrap Function Pipe blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Effect vs Selector
|
title: 🟠 Effect vs Selector
|
||||||
description: Challenge 2 is about learning the difference between effects and selectors in NgRx
|
description: Challenge 2 is about learning the difference between effects and selectors in NgRx
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 2
|
order: 113
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -48,7 +48,7 @@ Your PR title must start with <b>Answer:2</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A2+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A2+label%3A"answer+author"'
|
||||||
alt="Effect vs Selector solution author">
|
alt="Effect vs Selector solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -57,7 +57,7 @@ Your PR title must start with <b>Answer:2</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Effect vs Selector blog article">
|
alt="Effect vs Selector blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Power of Effect
|
title: 🔴 Power of Effect
|
||||||
description: Challenge 7 is about creating an Ngrx effect with another Rxjs Hot observable
|
description: Challenge 7 is about creating an Ngrx effect with another Rxjs Hot observable
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 7
|
order: 206
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -51,7 +51,7 @@ Your PR title must start with <b>Answer:7</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A7+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A7+label%3A"answer+author"'
|
||||||
alt="Power of Effect solution author">
|
alt="Power of Effect solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🔴 Extend Lib Generator
|
title: 🔴 Extend Lib Generator
|
||||||
description: Challenge 25 is about creating a Nx generator to extend the built-in Library Generator
|
description: Challenge 25 is about creating a Nx generator to extend the built-in Library Generator
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 25
|
order: 207
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -67,7 +67,7 @@ Your PR title must start with <b>Answer:25</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A25+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A25+label%3A"answer+author"'
|
||||||
alt="Extend Lib Generator solution author">
|
alt="Extend Lib Generator solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Component Generator
|
title: 🟠 Component Generator
|
||||||
description: Challenge 26 is about creating a Nx generator to create a custom component
|
description: Challenge 26 is about creating a Nx generator to create a custom component
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 26
|
order: 116
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -155,7 +155,7 @@ Your PR title must start with <b>Answer:26</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A26+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A26+label%3A"answer+author"'
|
||||||
alt="Component Generator solution author">
|
alt="Component Generator solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Custom Eslint Rule
|
title: 🟢 Custom Eslint Rule
|
||||||
description: Challenge 27 is about creating a custom Eslint Rule to forbid enums
|
description: Challenge 27 is about creating a custom Eslint Rule to forbid enums
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 27
|
order: 12
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -36,7 +36,7 @@ Your PR title must start with <b>Answer:27</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A27+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A27+label%3A"answer+author"'
|
||||||
alt="Custom Eslint Rule solution author">
|
alt="Custom Eslint Rule solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 High Order Operator Bug
|
title: 🟠 High Order Operator Bug
|
||||||
description: Challenge 11 is about resolving a Rxjs bug because of high order operators
|
description: Challenge 11 is about resolving a Rxjs bug because of high order operators
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 11
|
order: 114
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -48,7 +48,7 @@ Your PR title must start with <b>Answer:11</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A11+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A11+label%3A"answer+author"'
|
||||||
alt="High Order Operator Bug solution author">
|
alt="High Order Operator Bug solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Race Condition
|
title: 🟢 Race Condition
|
||||||
description: Challenge 14 is about race condition in Rxjs
|
description: Challenge 14 is about race condition in Rxjs
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 14
|
order: 11
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -46,7 +46,7 @@ Your PR title must start with <b>Answer:14</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A14+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A14+label%3A"answer+author"'
|
||||||
alt="Race Condition solution author">
|
alt="Race Condition solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,34 +2,26 @@
|
|||||||
title: 🟠 Router
|
title: 🟠 Router
|
||||||
description: Challenge 17 is about testing the router
|
description: Challenge 17 is about testing the router
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 17
|
order: 108
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #17</div>
|
<div class="chip">Challenge #17</div>
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
Testing is a crucial step in building scalable, maintainable, and trustworthy applications.
|
We have a functional application that lists available books for borrowing inside a library. If the book you searched for is available, you will be directed to the corresponding book(s), otherwise, you will end up on an error page.
|
||||||
Testing should never be avoided, even in the face of short deadlines or strong pressure from the product team.
|
|
||||||
Nowadays, there are numerous awesome tools available that make it easy to test your code and provide a great developer experience.
|
|
||||||
|
|
||||||
In this series of testing exercises, we will learn and master [Testing Library](https://testing-library.com/docs/) and [Cypress Component Testing](https://docs.cypress.io/guides/component-testing/angular/overview) that simplifies DOM manipulation for testing any Angular component.
|
The file named `app.component.spec.ts` will let you test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-router-outlet`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
||||||
|
|
||||||
## Statement:
|
|
||||||
|
|
||||||
We have a functional application that lists available books for borrowing inside a library. If the book you searched is available, you will be directed to the corresponding book(s), otherwise, you will end up on an error page.
|
|
||||||
|
|
||||||
The goal is to test this behavior with Testing library and Cypress
|
|
||||||
|
|
||||||
The file named `app.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-router-outlet`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
|
||||||
|
|
||||||
For testing cypress, you will execute your test inside the `app.component.cy.ts` and run `npx nx component-test testing-router-outlet` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
For testing cypress, you will execute your test inside the `app.component.cy.ts` and run `npx nx component-test testing-router-outlet` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
||||||
|
|
||||||
I created some `it` blocks but feel free to add more test if you like to.
|
# Statement
|
||||||
|
|
||||||
|
The goal is to test multiple behaviors of the application described in each test file using Testing library and Cypress Component Testing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
I have created some `it` blocks but feel free to add more tests if you want.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -48,7 +40,7 @@ Your PR title must start with <b>Answer:17</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A17+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A17+label%3A"answer+author"'
|
||||||
alt="Router solution author">
|
alt="Router solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,28 +2,30 @@
|
|||||||
title: 🟠 Nested Components
|
title: 🟠 Nested Components
|
||||||
description: Challenge 18 is about testing nested components
|
description: Challenge 18 is about testing nested components
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 18
|
order: 109
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #18</div>
|
<div class="chip">Challenge #18</div>
|
||||||
|
|
||||||
## Statement:
|
## Information
|
||||||
|
|
||||||
|
We have a small application that sends a title, typed into an input to a fake backend.
|
||||||
|
If the title is correctly typed, you can send the request otherwise you receive an error and the request is not sent.
|
||||||
|
The application is created with <b>nested components</b>. `ChildComponent` is the container that includes four components: `ResultComponent`, `ButtonComponent`, `InputComponent` and `ErrorComponent`. However since we are testing our component as a black box, the architecture of our components doesn't change anything. You can create your test, change how the components are structured, and your tests should still pass. That's the goal of integration tests. <b>Never test internal implementation details!!!</b>.
|
||||||
|
|
||||||
We have a small application that send a title to a fake backend that you type inside a input.
|
|
||||||
If the title is correctly typed, you can send the request otherwise you get a nice error and the request is not sent.
|
|
||||||
You can play with it by running : `npx nx serve testing-nested`.
|
You can play with it by running : `npx nx serve testing-nested`.
|
||||||
|
|
||||||
The goal is to test this behavior with Testing library and Cypress
|
|
||||||
|
|
||||||
The file named `child.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-nested`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
The file named `child.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-nested`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
||||||
|
|
||||||
For testing cypress, you will execute your test inside the `child.component.cy.ts` and run `npx nx component-test testing-nested` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
For testing cypress, you will execute your test inside the `child.component.cy.ts` and run `npx nx component-test testing-nested` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
||||||
|
|
||||||
I created some `it` blocks but feel free to add more test if you like to.
|
# Statement
|
||||||
|
|
||||||
|
The goal is to test multiple behaviors of the application describe inside each test files using Testing library and Cypress Component Testing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
I have created some `it` blocks but feel free to add more tests if you want.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ Your PR title must start with <b>Answer:18</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A18+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A18+label%3A"answer+author"'
|
||||||
alt="Nested Components solution author">
|
alt="Nested Components solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,27 +2,28 @@
|
|||||||
title: 🟠 Input Output
|
title: 🟠 Input Output
|
||||||
description: Challenge 19 is about testing inputs and ouputs
|
description: Challenge 19 is about testing inputs and ouputs
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 19
|
order: 110
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #19</div>
|
<div class="chip">Challenge #19</div>
|
||||||
|
|
||||||
## Statement:
|
## Information:
|
||||||
|
|
||||||
|
We have a small counter application that increments or decrements a number. The `CounterComponent` takes an initial value as an `@Input` and emits the result of the counter as an `@Output` when we click on the **Send** button. Since we are testing our component as a black box, we only have access to our inputs and listen to the output values. <b>We should not rely on any internal implementation details!!!</b>
|
||||||
|
|
||||||
We have a small counter application that increment or decrement a number.
|
|
||||||
You can play with it by running : `npx nx serve testing-input-output`.
|
You can play with it by running : `npx nx serve testing-input-output`.
|
||||||
|
|
||||||
The goal is to test `CounterComponent` with Testing library and Cypress
|
The file named `counter.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-input-output`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
||||||
|
|
||||||
The file named `counter.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-nested`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
For testing cypress, you will execute your test inside the `child.component.cy.ts` and run `npx nx component-test testing-input-output` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
||||||
|
|
||||||
For testing cypress, you will execute your test inside the `counter.component.cy.ts` and run `npx nx component-test testing-nested` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
# Statement
|
||||||
|
|
||||||
I created some `it` blocks but feel free to add more test if you like to.
|
The goal is to test multiple behaviors of the application describe inside each test files using Testing library and Cypress Component Testing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
I have created some `it` blocks but feel free to add more tests if you want.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ Your PR title must start with <b>Answer:19</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A19+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A19+label%3A"answer+author"'
|
||||||
alt="Input Output solution author">
|
alt="Input Output solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,30 +2,32 @@
|
|||||||
title: 🟠 Modal
|
title: 🟠 Modal
|
||||||
description: Challenge 20 is about testing modals
|
description: Challenge 20 is about testing modals
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 20
|
order: 111
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #20</div>
|
<div class="chip">Challenge #20</div>
|
||||||
|
|
||||||
## Statement:
|
## Information:
|
||||||
|
|
||||||
The goal of this challenge is to test dialogs inside your application.
|
In this small application, you have an input prompting you to enter a name, and a **Confirm** button to submit your form.
|
||||||
Within this program, you will get an error modal if the user doesn't input a name, while a confirmation modal will appear in all other cases.
|
If you enter a name, a confirmation modal will appear; otherwise an error modal will be displayed.
|
||||||
In the confirmation modal, if you click the "confirm" button, a message confirming the submission of the form will appear. Otherwise, if the user clicks on "Cancel", an error message will be displayed.
|
In the confirmation modal, if you click the **Confirm** button, a message confirming the submission of the form will appear. If the user clicks on **Cancel**, an error message will be displayed.
|
||||||
|
|
||||||
|
The goal of this challenge is to test the dialogs inside your application. To do so, we will test the full application like an end-to-end test will do. This means, we will test the `AppComponent` as a black box and react to events on the page. <b>No internal details should be tested</b>. The difference between an e2e test and integration test is that we will mock all API calls. _(All http requests are faked inside this application, but this would not be the case in a real entreprice application.)_
|
||||||
|
|
||||||
You can play with it by running : `npx nx serve testing-modal`.
|
You can play with it by running : `npx nx serve testing-modal`.
|
||||||
|
|
||||||
The goal is to test this behavior with Testing library and Cypress
|
|
||||||
|
|
||||||
The file named `app.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-modal`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
The file named `app.component.spec.ts` will let test your application using Testing Library. To run the test suits, you need to run `npx nx test testing-modal`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
||||||
|
|
||||||
For testing cypress, you will execute your test inside the `app.component.cy.ts` and run `npx nx component-test testing-modal` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
For testing cypress, you will execute your test inside the `app.component.cy.ts` and run `npx nx component-test testing-modal` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
||||||
|
|
||||||
I created some `it` blocks but feel free to add more test if you like to.
|
# Statement
|
||||||
|
|
||||||
|
The goal is to test multiple behaviors of the application describe inside each test files using Testing library and Cypress Component Testing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
I have created some `it` blocks but feel free to add more tests if you want.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ Your PR title must start with <b>Answer:20</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A20+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A20+label%3A"answer+author"'
|
||||||
alt="Modal solution author">
|
alt="Modal solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟢 Harness
|
title: 🟢 Harness
|
||||||
description: Challenge 23 is about testing with component harnesses
|
description: Challenge 23 is about testing with component harnesses
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 23
|
order: 9
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -39,7 +39,7 @@ Your PR title must start with <b>Answer:23</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A23+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A23+label%3A"answer+author"'
|
||||||
alt="Harness solution author">
|
alt="Harness solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Harness Creation
|
title: 🟠 Harness Creation
|
||||||
description: Challenge 24 is about creating a component harness.
|
description: Challenge 24 is about creating a component harness.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 24
|
order: 112
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -63,7 +63,7 @@ Your PR title must start with <b>Answer:24</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A24+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A24+label%3A"answer+author"'
|
||||||
alt="Harness Creation solution author">
|
alt="Harness Creation solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
title: 🟢 Checkbox
|
title: 🟢 Checkbox
|
||||||
description: Challenge 28 is about testing a simple checkbox
|
description: Challenge 28 is about testing a simple checkbox
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 28
|
order: 10
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #28</div>
|
<div class="chip">Challenge #28</div>
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
This is the perfect example to get started with `Testing Library`.
|
This application is very simple. It consists of a checkbox that enables or disables a button. The primary goal of this application is to become familiar with the debug API of Testing Library. Knowing how to debug your tests is a crucial tool you need to have in your toolkit.
|
||||||
|
|
||||||
You will need to only check if the button gets enabled when clicking on the checkbox
|
You can find the documentation about debugging in Testing Library [here](https://testing-library.com/docs/dom-testing-library/api-debugging#screenlogtestingplaygroundurl).
|
||||||
|
|
||||||
You can look into debug function to get full power of `Testing Library` like:
|
The main functions to remember are as follows:
|
||||||
|
|
||||||
- logRoles(...);
|
- `logRoles(myDOMElement)`: prints out all ARIA roles within the tree of the given DOM element. ARIA roles are the primary selectors you should reach for in the first place.
|
||||||
- screen.debug(); // you can pass a element as input
|
- `screen.debug()` or `screen.debug(myDOMElement)`: prints the DOM inside the console.
|
||||||
- screen.logTestingPlaygroundURL(); // you can pass a element as input
|
- `screen.logTestingPlaygroundURL()` or `screen.logTestingPlaygroundURL(myDOMElement)`: this function is very powerful. It will create a playground to expose all elements, and you can interact with it to see the selectors you should choose for a DOM element.
|
||||||
|
|
||||||
|
## Statement
|
||||||
|
|
||||||
|
The goal of this challenge is not to submit an answer, but you can if you want. It's more about using the debugging API to play around. These tools will be of great help for the upcoming testing challenges.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ Your PR title must start with <b>Answer:28</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A28+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A28+label%3A"answer+author"'
|
||||||
alt="Checkbox solution author">
|
alt="Checkbox solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,25 +2,34 @@
|
|||||||
title: 🔴 Real-life Application
|
title: 🔴 Real-life Application
|
||||||
description: Challenge 29 is about testing a real-life application
|
description: Challenge 29 is about testing a real-life application
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 29
|
order: 205
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
|
||||||
WIP: The following documentation will be reviewed and improved. However, you can still take on the challenge. If you don't understand a certain part, please feel free to reach out or create an issue.
|
|
||||||
:::
|
|
||||||
|
|
||||||
<div class="chip">Challenge #29</div>
|
<div class="chip">Challenge #29</div>
|
||||||
|
|
||||||
## Statement:
|
## Information:
|
||||||
|
|
||||||
I built this more real life application to create more real life test cases.
|
This application presents a greater challenge because it closely resembles a real-life application that you might encounter in your day-to-day activities as an Angular developer. What makes it more difficult is the need to handle asynchronous tasks and create appropriate mocks.
|
||||||
In this application, you can search for tickets, you can assign or finish them. You can also create new tickets.
|
|
||||||
|
|
||||||
This is a very simple application, but it will let you deal with asynchronous task and mocks
|
The application is a typical todo list application. You can filter tickets, create new ones, assign each ticket, close others, and navigate to the details of each ticket.
|
||||||
|
|
||||||
The goal of this challenge is to write all test cases of `ticket.store` , `list.component` and `row.component` with Testing Library.
|
In this challenge, you will write tests for the `ListComponent`, which represents the global view, and the `RowComponent`, which represents a specific ticket. Additionally, you will need to write unit tests for the `TicketStoreService` using Testing Library. _This library allows you to test services effectively._
|
||||||
|
|
||||||
You can also do it with cypress.
|
Handling asynchronous tasks will be particularly challenging. It's important not to introduce any explicit <b>waits</b> in your tests, as this would introduce unnecessary delays. Instead, it's better to look for an element that needs to appear or disappear from the DOM. In this case, the test will naturally wait for the correct period of time, as the waits are already implemented within both libraries. Take advantage of these built-in functionalities to create efficient and reliable tests.
|
||||||
|
|
||||||
|
You can play with it by running : `npx nx serve testing-todos-list`.
|
||||||
|
|
||||||
|
To run Testing Library test suits, you need to run `npx nx test testing-input-output`. You can also install [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) to execute your test by clicking on the `Run` button above each `describe` or `it` blocks.
|
||||||
|
|
||||||
|
For testing cypress, you will execute your test inside the `child.component.cy.ts` and run `npx nx component-test testing-input-output` to execute your test suits. You can add the `--watch` flag to execute your test in watch mode.
|
||||||
|
|
||||||
|
# Statement
|
||||||
|
|
||||||
|
The goal is to test multiple behaviors of the application describe inside each test files using Testing library and Cypress Component Testing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
I have created some `it` blocks but feel free to add more tests if you want.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -39,7 +48,7 @@ Your PR title must start with <b>Answer:29</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A29+label%3A%22answer+author%22'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A29+label%3A"answer+author"'
|
||||||
alt="Real-life Application solution author">
|
alt="Real-life Application solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
70
docs/src/content/docs/challenges/testing/index.mdx
Normal file
70
docs/src/content/docs/challenges/testing/index.mdx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
title: Testing
|
||||||
|
prev: false
|
||||||
|
next: false
|
||||||
|
description: Introduction to testing challenges.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { LinkCard } from '@astrojs/starlight/components';
|
||||||
|
|
||||||
|
Testing is a crucial step in building scalable, maintainable, and trustworthy applications.
|
||||||
|
Testing should never be avoided, even in the face of short deadlines or strong pressure from the product team.
|
||||||
|
Nowadays, there are numerous awesome tools available that make it easy to test your code and provide a great developer experience.
|
||||||
|
|
||||||
|
In this series of testing exercises, we will learn and master [Testing Library](https://testing-library.com/docs/) and [Cypress Component Testing](https://docs.cypress.io/guides/component-testing/angular/overview) that simplifies DOM manipulation for testing any Angular component.
|
||||||
|
|
||||||
|
The benefits of using <b>Testing Library</b> or <b>Cypress Component Testing</b> are to test your component as a black box. You will only interact with what the user can do on the UI. However, the difference with end-to-end tests is that the backend is mocked, which makes the tests faster and more maintainable.
|
||||||
|
The goal is to mock as little as possible to test your component at a higher level than unit testing, which will make refactoring easier.
|
||||||
|
Within a real application, integration tests are the tests you will write the most. Learning how to write them will make your application more robust and more maintainable.
|
||||||
|
|
||||||
|
Here is a series of 8 challenges that you can take in any order.
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟢 Harness"
|
||||||
|
description="Learn how to test using Angular CDK Component harnesses"
|
||||||
|
href="/challenges/testing/23-harness/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟢 Checkbox"
|
||||||
|
description="Learn how to debug your tests using Testing Library on a simple checkbox application"
|
||||||
|
href="/challenges/testing/28-checkbox/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Router"
|
||||||
|
description="Learn how to test the routed components"
|
||||||
|
href="/challenges/testing/17-router/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Nested Components"
|
||||||
|
description="Learn how to test nested components"
|
||||||
|
href="/challenges/testing/18-nested-comp/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Input Output"
|
||||||
|
description="Learn how to test inputs and outputs"
|
||||||
|
href="/challenges/testing/19-input-output/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Modal"
|
||||||
|
description="Learn how to test a modal component"
|
||||||
|
href="/challenges/testing/20-modal/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🟠 Harness Creation"
|
||||||
|
description="Learn how to create harness on your own components"
|
||||||
|
href="/challenges/testing/24-harness-creation/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
title="🔴 Real-life Application"
|
||||||
|
description="Learn how to write a series of test for a real-life applications"
|
||||||
|
href="/challenges/testing/29-real-application/"
|
||||||
|
/>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
title: 🟠 Function Overload
|
title: 🟠 Function Overload
|
||||||
description: Challenge 15 is about creating overload functions
|
description: Challenge 15 is about creating overload functions
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 15
|
order: 115
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@@ -42,7 +42,7 @@ Your PR title must start with <b>Answer:15</b>.
|
|||||||
❖ Community Answers
|
❖ Community Answers
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A15+label%3A'
|
href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A15+label%3A"answer+author"'
|
||||||
alt="Function Overload solution author">
|
alt="Function Overload solution author">
|
||||||
▶︎ Author Answer
|
▶︎ Author Answer
|
||||||
</a>
|
</a>
|
||||||
@@ -51,7 +51,7 @@ Your PR title must start with <b>Answer:15</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="Function Overload blog article">
|
alt="Function Overload blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ sidebar:
|
|||||||
order: 4
|
order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
You can contribute to this repository in many ways:
|
||||||
WIP:
|
|
||||||
:::
|
🔥 Create a new challenge by following the intructions [here](/guides/create-challenge).
|
||||||
|
|
||||||
|
🔥 Answer challenges and submit the results. (guide [here](/guides/resolve-challenge)).
|
||||||
|
|
||||||
|
🔥 Comment on other's solutions by providing constructive and caring feedback.
|
||||||
|
|
||||||
|
🔥 Correct typos or English mistakes within the documentation.
|
||||||
|
|
||||||
|
🔥 File an issue to suggest new challenge ideas or report a bug.
|
||||||
|
|
||||||
|
🔥 Sponsor the project [here](https://github.com/sponsors/tomalaforge)
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ hero:
|
|||||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||||
|
|
||||||
<CardGrid>
|
<CardGrid>
|
||||||
<Card title="35 Challenges">
|
<Card title="36 Challenges">
|
||||||
This repository gathers 35 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
|
This repository gathers 36 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
|
||||||
These challenges resolve around real-life issues or specific features to elevate your skills.
|
These challenges resolve around real-life issues or specific features to elevate your skills.
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -56,7 +56,10 @@ import { Card, CardGrid } from '@astrojs/starlight/components';
|
|||||||
---
|
---
|
||||||
|
|
||||||
<div class="article-footer">
|
<div class="article-footer">
|
||||||
<a href="https://github.com/sponsors/tomalaforge" alt="Sponsor link">
|
<a
|
||||||
|
href="https://github.com/sponsors/tomalaforge"
|
||||||
|
alt="Sponsor link"
|
||||||
|
class="primary action astro-yjy4zhro">
|
||||||
🤍 Sponsor the Project
|
🤍 Sponsor the Project
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,18 +2,30 @@
|
|||||||
--sl-color-black: #1b1b1d;
|
--sl-color-black: #1b1b1d;
|
||||||
--sl-color-gray-6: #242526;
|
--sl-color-gray-6: #242526;
|
||||||
--sl-color-accent-high: #f10023;
|
--sl-color-accent-high: #f10023;
|
||||||
/* --sl-color-black: white; */
|
|
||||||
--sl-icon-color: #fff;
|
--sl-icon-color: #fff;
|
||||||
--sl-color-text-invert: #fff;
|
--sl-color-text-invert: #fff;
|
||||||
--primary-color: var(--sl-color-bg-nav) !important;
|
--primary-color: var(--sl-color-bg-nav) !important;
|
||||||
/* --sl-color-text-invert: #212121; */
|
|
||||||
--cardBgColor: #242526;
|
--cardBgColor: #242526;
|
||||||
/* --sl-font: 'IBM Plex Serif', serif; */
|
|
||||||
|
|
||||||
--sl-hue-purple: 41;
|
--sl-hue-purple: 41;
|
||||||
--sl-color-purple-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
--sl-color-purple-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
||||||
--sl-color-purple: hsl(var(--sl-hue-orange), 82%, 63%);
|
--sl-color-purple: hsl(var(--sl-hue-orange), 82%, 63%);
|
||||||
--sl-color-purple-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
--sl-color-purple-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
||||||
|
|
||||||
|
--color-btn: var(--sl-color-white);
|
||||||
|
--color-chip: rgb(35, 38, 47);
|
||||||
|
--color-chip-border: rgba(240, 246, 252, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='light'],
|
||||||
|
[data-theme='light'] {
|
||||||
|
--sl-color-accent: #f10023;
|
||||||
|
--sl-color-accent-high: #f10023;
|
||||||
|
--sl-icon-color: rgb(35, 38, 47);
|
||||||
|
|
||||||
|
--color-btn: white;
|
||||||
|
--color-chip: white;
|
||||||
|
--color-chip-border: rgb(33, 38, 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.github-success-btn {
|
.github-success-btn {
|
||||||
@@ -21,7 +33,7 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
background-color: rgb(35, 134, 54);
|
background-color: rgb(35, 134, 54);
|
||||||
color: var(--sl-color-white);
|
color: var(-color-btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
.github-neutral-btn {
|
.github-neutral-btn {
|
||||||
@@ -38,12 +50,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chip {
|
.chip {
|
||||||
border: 1px solid rgba(240, 246, 252, 0.1);
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--color-chip-border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
background-color: rgb(33, 38, 45);
|
background-color: var(--color-chip);
|
||||||
color: rgb(201, 209, 217);
|
color: var(--sl-color-text);
|
||||||
fill: rgb(201, 209, 217);
|
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,14 +67,14 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-footer > a {
|
.article-footer > a {
|
||||||
border: 1px solid var(--sl-color-accent-high);
|
border: 1px solid var(--sl-color-accent-high);
|
||||||
border-radius: 999rem;
|
border-radius: 999rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--sl-color-gray-2);
|
color: var(--color-btn);
|
||||||
box-shadow: var(--sl-shadow-md);
|
box-shadow: var(--sl-shadow-md);
|
||||||
color: var(--sl-color-white);
|
|
||||||
font-size: var(--sl-text-lg);
|
font-size: var(--sl-text-lg);
|
||||||
line-height: var(--sl-line-height-headings);
|
line-height: var(--sl-line-height-headings);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -72,14 +85,19 @@
|
|||||||
background-color: var(--sl-color-accent-high);
|
background-color: var(--sl-color-accent-high);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.primary {
|
a.primary,
|
||||||
color: var(--sl-color-white) !important;
|
a.primary > svg {
|
||||||
|
color: var(--color-btn) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
b {
|
b {
|
||||||
color: var(--sl-color-accent-high);
|
color: var(--sl-color-accent-high);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.starlight-aside--tip b {
|
||||||
|
color: var(--sl-color-asides-text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
.main-page-footer {
|
.main-page-footer {
|
||||||
margin-top: 2rem !important;
|
margin-top: 2rem !important;
|
||||||
font-size: var(--sl-text-sm);
|
font-size: var(--sl-text-sm);
|
||||||
@@ -96,3 +114,9 @@ b {
|
|||||||
starlight-menu-button svg {
|
starlight-menu-button svg {
|
||||||
color: #1c1a1d;
|
color: #1c1a1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flag {
|
||||||
|
background-color: white;
|
||||||
|
padding: 1px 2px;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
<!-- TODO: add title of your exercice -->
|
|
||||||
<h1>{Title of your exercice}</h1>
|
|
||||||
|
|
||||||
<!-- TODO: Add your name -->
|
|
||||||
|
|
||||||
> Author: {Your name}
|
|
||||||
|
|
||||||
<!-- TODO: add Information/Statement/Rules/Constraint/Steps -->
|
|
||||||
|
|
||||||
## Information
|
|
||||||
|
|
||||||
## Statement
|
|
||||||
|
|
||||||
### Step 1
|
|
||||||
|
|
||||||
### Step 2
|
|
||||||
|
|
||||||
### Constraints:
|
|
||||||
|
|
||||||
### Submitting your work
|
|
||||||
|
|
||||||
<!-- To replace
|
|
||||||
{Project name} by your project name
|
|
||||||
{challenge number} by the following number (To be find inside the main readme) -->
|
|
||||||
|
|
||||||
1. Fork the project
|
|
||||||
2. clone it
|
|
||||||
3. npm ci
|
|
||||||
<!-- TODO: add you project app name directory -->
|
|
||||||
4. `npx nx serve {project app name}`
|
|
||||||
5. _...work on it_
|
|
||||||
6. Commit your work
|
|
||||||
<!-- TODO: add your challenge number -->
|
|
||||||
7. Submit a PR with a title beginning with **Answer:{challenge number}** that I will review and other dev can review.
|
|
||||||
|
|
||||||
<!-- TODO: add challenge number and project Name -->
|
|
||||||
|
|
||||||
<a href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A{challenge number}+label%3Aanswer"><img src="https://img.shields.io/badge/-Solutions-green" alt="{Project name}"/></a>
|
|
||||||
|
|
||||||
<!-- TODO: uncomment when done late -->
|
|
||||||
<!-- <a href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A{challenge number}+label%3A"answer+author"'><img src="https://img.shields.io/badge/-Author solution-important" alt="{Project name} solution author"/></a>
|
|
||||||
<a href="{Blog post url}" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/-Blog post explanation-blue" alt="{Project name} blog article"/></a> -->
|
|
||||||
|
|
||||||
<!-- TODO: you can add your twitter or anything else if you wish -->
|
|
||||||
<!-- _You can ask any question on_ <a href="https://twitter.com/laforge_toma" target="_blank" rel="noopener noreferrer"><img src="./../../logo/twitter.svg" height=20px alt="twitter"/></a> -->
|
|
||||||
@@ -3,7 +3,7 @@ import { Component } from '@angular/core';
|
|||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [],
|
||||||
selector: 'lib-root',
|
selector: 'app-root',
|
||||||
template: ``,
|
template: ``,
|
||||||
styles: [''],
|
styles: [''],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
title: <%= difficulty %> <%= title %>
|
title: <%= difficulty %> <%= title %>
|
||||||
description: Challenge <%= challengeNumber %> is about ...
|
description: Challenge <%= challengeNumber %> is about ...
|
||||||
sidebar:
|
sidebar:
|
||||||
order: <%= challengeNumber %>
|
order: <%= order %>
|
||||||
|
badge: New
|
||||||
---
|
---
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|||||||
@@ -12,16 +12,44 @@ import {
|
|||||||
updateJson,
|
updateJson,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { Linter } from '@nx/linter';
|
import { Linter } from '@nx/linter';
|
||||||
import { readFile, writeFile } from 'fs/promises';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { getProjectDir } from '../../utils/normalize';
|
import { getProjectDir } from '../../utils/normalize';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
|
function findPreviousChallengeFilePath(tree, path, number) {
|
||||||
|
if (tree.isFile(path) && path.startsWith(`${number}-`)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchingChild = tree
|
||||||
|
.children(path)
|
||||||
|
.find((child) => child.startsWith(`${number}-`));
|
||||||
|
|
||||||
|
if (matchingChild) {
|
||||||
|
const fullPath = path + '/' + matchingChild;
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of tree.children(path)) {
|
||||||
|
const childPath = path + '/' + child;
|
||||||
|
const result = findPreviousChallengeFilePath(tree, childPath, number);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function challengeGenerator(tree: Tree, options: Schema) {
|
export async function challengeGenerator(tree: Tree, options: Schema) {
|
||||||
const { appDirectory } = getProjectDir(options.name, options.directory);
|
const { appDirectory } = getProjectDir(options.name, options.directory);
|
||||||
|
|
||||||
|
const difficulty = options.challengeDifficulty;
|
||||||
|
|
||||||
|
// read json file with the total challanges and display order
|
||||||
const challengeNumberPath = 'challenge-number.json';
|
const challengeNumberPath = 'challenge-number.json';
|
||||||
const challengeNumber = readJsonFile(challengeNumberPath).total;
|
const challangeNumberJson = readJsonFile(challengeNumberPath);
|
||||||
|
const challengeNumber = challangeNumberJson.total + 1;
|
||||||
|
const order = challangeNumberJson[difficulty] + 1;
|
||||||
|
|
||||||
await applicationGenerator(tree, {
|
await applicationGenerator(tree, {
|
||||||
...options,
|
...options,
|
||||||
@@ -60,7 +88,8 @@ export async function challengeGenerator(tree: Tree, options: Schema) {
|
|||||||
projectName: names(options.name).name,
|
projectName: names(options.name).name,
|
||||||
title: options.title,
|
title: options.title,
|
||||||
challengeNumber,
|
challengeNumber,
|
||||||
difficulty: options.challengeDifficulty,
|
difficulty,
|
||||||
|
order,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -70,27 +99,40 @@ export async function challengeGenerator(tree: Tree, options: Schema) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const readme = await readFile('./README.md', { encoding: 'utf-8' });
|
const readme = tree.read('./README.md').toString();
|
||||||
|
|
||||||
const readmeRegex = new RegExp(`all ${challengeNumber} challenges`);
|
const readmeRegex = new RegExp(`all ${challengeNumber - 1} challenges`);
|
||||||
const readmeReplace = readme.replace(
|
const readmeReplace = readme.replace(
|
||||||
readmeRegex,
|
readmeRegex,
|
||||||
`all ${challengeNumber + 1} challenges`
|
`all ${challengeNumber} challenges`
|
||||||
);
|
);
|
||||||
|
|
||||||
await writeFile('./README.md', readmeReplace, 'utf-8');
|
tree.write('./README.md', readmeReplace);
|
||||||
|
|
||||||
const docs = await readFile('./docs/src/content/docs/index.mdx', {
|
const docs = tree.read('./docs/src/content/docs/index.mdx').toString();
|
||||||
encoding: 'utf-8',
|
|
||||||
});
|
|
||||||
|
|
||||||
const regex = new RegExp(`${challengeNumber} Challenges`, 'gi');
|
const regex = new RegExp(`${challengeNumber - 1} Challenges`, 'gi');
|
||||||
const replaced = docs.replace(regex, `${challengeNumber + 1} Challenges`);
|
const replaced = docs.replace(regex, `${challengeNumber} Challenges`);
|
||||||
|
|
||||||
await writeFile('./docs/src/content/docs/index.mdx', replaced, 'utf-8');
|
tree.write('./docs/src/content/docs/index.mdx', replaced);
|
||||||
|
|
||||||
|
const previousChallengeFilePath = findPreviousChallengeFilePath(
|
||||||
|
tree,
|
||||||
|
`./docs/src/content/docs/challenges`,
|
||||||
|
String(challengeNumber - 1)
|
||||||
|
);
|
||||||
|
console.log(`restul`, previousChallengeFilePath);
|
||||||
|
|
||||||
|
const previousChallenge = tree.read(previousChallengeFilePath).toString();
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
previousChallengeFilePath,
|
||||||
|
previousChallenge.replace(`badge: New`, ``)
|
||||||
|
);
|
||||||
|
|
||||||
updateJson(tree, challengeNumberPath, (json) => {
|
updateJson(tree, challengeNumberPath, (json) => {
|
||||||
json.total = json.total + 1;
|
json.total += 1;
|
||||||
|
json[difficulty] += 1;
|
||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Tree, formatFiles } from '@nx/devkit';
|
import { Tree, formatFiles } from '@nx/devkit';
|
||||||
import { readFile, writeFile } from 'fs/promises';
|
|
||||||
|
|
||||||
const README_FILENAME = 'README.md';
|
const README_FILENAME = 'README.md';
|
||||||
const OMIT = ['memoized', 'projection', 'testing-table', 'testing-forms'];
|
const OMIT = ['memoized', 'projection', 'testing-table', 'testing-forms'];
|
||||||
@@ -46,10 +45,10 @@ function findHref(href) {
|
|||||||
|
|
||||||
async function rewriteFile(tree: Tree, file: string) {
|
async function rewriteFile(tree: Tree, file: string) {
|
||||||
console.log('Current file', file);
|
console.log('Current file', file);
|
||||||
const buffer = await readFile(file, { encoding: 'utf-8' });
|
const buffer = tree.read(file);
|
||||||
|
|
||||||
const regex = new RegExp(/Answer:(\d+)/);
|
const regex = new RegExp(/Answer:(\d+)/);
|
||||||
const match = buffer.match(regex);
|
const match = buffer.toString().match(regex);
|
||||||
|
|
||||||
if (!match) throw new Error('NO MATCH');
|
if (!match) throw new Error('NO MATCH');
|
||||||
|
|
||||||
@@ -69,19 +68,19 @@ async function rewriteFile(tree: Tree, file: string) {
|
|||||||
-2
|
-2
|
||||||
)}/${pathElts.at(-1)}/`;
|
)}/${pathElts.at(-1)}/`;
|
||||||
|
|
||||||
const doc = await readFile(docFile, { encoding: 'utf-8' });
|
const doc = tree.read(docFile);
|
||||||
|
|
||||||
const regexTitle = new RegExp(/title:\s(🟢|🟠|🔴)\s(.+?)\n/);
|
const regexTitle = new RegExp(/title:\s(🟢|🟠|🔴)\s(.+?)\n/);
|
||||||
const matchTitle = doc.match(regexTitle);
|
const matchTitle = doc.toString().match(regexTitle);
|
||||||
const title = matchTitle[2];
|
const title = matchTitle[2];
|
||||||
|
|
||||||
const regexCommand = new RegExp(/npx nx serve\s(.+?)`\s/);
|
const regexCommand = new RegExp(/npx nx serve\s(.+?)`\s/);
|
||||||
const matchCommand = buffer.match(regexCommand);
|
const matchCommand = buffer.toString().match(regexCommand);
|
||||||
|
|
||||||
let command = '';
|
let command = '';
|
||||||
if (!matchCommand) {
|
if (!matchCommand) {
|
||||||
const regexOldCommand = new RegExp(/nx serve\s(.+?)\*/);
|
const regexOldCommand = new RegExp(/nx serve\s(.+?)\*/);
|
||||||
command = buffer.match(regexOldCommand)[1];
|
command = buffer.toString().match(regexOldCommand)[1];
|
||||||
} else {
|
} else {
|
||||||
command = matchCommand[1];
|
command = matchCommand[1];
|
||||||
}
|
}
|
||||||
@@ -103,12 +102,12 @@ npx nx serve ${command}
|
|||||||
Challenge documentation is [here](${link}).
|
Challenge documentation is [here](${link}).
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await writeFile(file, finalText, { encoding: 'utf-8' });
|
tree.write(file, finalText);
|
||||||
|
|
||||||
///**** */
|
///**** */
|
||||||
|
|
||||||
const regexHref = new RegExp(/<a href=("|')(.+?)("|')/, 'g');
|
const regexHref = new RegExp(/<a href=("|')(.+?)("|')/, 'g');
|
||||||
const href = buffer.match(regexHref).map(findHref);
|
const href = buffer.toString().match(regexHref).map(findHref);
|
||||||
|
|
||||||
console.log('HREF', href);
|
console.log('HREF', href);
|
||||||
|
|
||||||
@@ -139,7 +138,7 @@ Your PR title must start with <b>Answer:${number}</b>.
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
alt="${title} blog article">
|
alt="${title} blog article">
|
||||||
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
<svg aria-hidden="true" class="astro-yzt5nm4y astro-lq7oo3uf" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M9 10h1a1 1 0 1 0 0-2H9a1 1 0 0 0 0 2Zm0 2a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9Zm11-3.06a1.3 1.3 0 0 0-.06-.27v-.09c-.05-.1-.11-.2-.19-.28l-6-6a1.07 1.07 0 0 0-.28-.19h-.09a.88.88 0 0 0-.33-.11H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V8.94Zm-6-3.53L16.59 8H15a1 1 0 0 1-1-1V5.41ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v3a3 3 0 0 0 3 3h3v9Zm-3-3H9a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2Z"></path></svg>
|
||||||
Blog Post
|
Blog Post
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,21 +149,21 @@ Your PR title must start with <b>Answer:${number}</b>.
|
|||||||
}
|
}
|
||||||
|
|
||||||
const regexHeader = new RegExp(/([\s\S]*?)\s:::note/);
|
const regexHeader = new RegExp(/([\s\S]*?)\s:::note/);
|
||||||
const header = doc.match(regexHeader)[1];
|
const header = doc.toString().match(regexHeader)[1];
|
||||||
|
|
||||||
console.log('header', header);
|
console.log('header', header);
|
||||||
|
|
||||||
const regexContent = new RegExp(
|
const regexContent = new RegExp(
|
||||||
/Author: Thomas Laforge([\s\S]*?)### Submitting your work/
|
/Author: Thomas Laforge([\s\S]*?)### Submitting your work/
|
||||||
);
|
);
|
||||||
const matchContent = buffer.match(regexContent);
|
const matchContent = buffer.toString().match(regexContent);
|
||||||
|
|
||||||
let content = '';
|
let content = '';
|
||||||
if (!matchContent) {
|
if (!matchContent) {
|
||||||
const regexOldContent = new RegExp(
|
const regexOldContent = new RegExp(
|
||||||
/Author: Thomas Laforge([\s\S]*?)## Submitting your work/
|
/Author: Thomas Laforge([\s\S]*?)## Submitting your work/
|
||||||
);
|
);
|
||||||
content = buffer.match(regexOldContent)[1];
|
content = buffer.toString().match(regexOldContent)[1];
|
||||||
} else {
|
} else {
|
||||||
content = matchContent[1];
|
content = matchContent[1];
|
||||||
}
|
}
|
||||||
@@ -184,7 +183,7 @@ ${content}
|
|||||||
${footerText}
|
${footerText}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await writeFile(docFile, fullDocText, { encoding: 'utf-8' });
|
tree.write(docFile, fullDocText);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readmeGenerator(tree: Tree) {
|
export async function readmeGenerator(tree: Tree) {
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export * from './lib/cd-flashing.directive';
|
export * from './lib/cd-flashing.directive';
|
||||||
|
export { NgForTrackByModule } from './lib/track-by.directive';
|
||||||
|
|||||||
51
libs/shared/directives/src/lib/track-by.directive.ts
Normal file
51
libs/shared/directives/src/lib/track-by.directive.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* eslint-disable @angular-eslint/directive-selector */
|
||||||
|
import { NgFor, NgForOf } from '@angular/common';
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
Input,
|
||||||
|
NgIterable,
|
||||||
|
NgModule,
|
||||||
|
Provider,
|
||||||
|
inject,
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ngForTrackByProp]',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class NgForTrackByPropDirective<T> {
|
||||||
|
@Input() ngForOf!: NgIterable<T>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set ngForTrackByProp(ngForTrackBy: keyof T) {
|
||||||
|
// setter
|
||||||
|
this.ngFor.ngForTrackBy = (index: number, item: T) => item[ngForTrackBy];
|
||||||
|
}
|
||||||
|
|
||||||
|
private ngFor = inject(NgForOf<T>, { self: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ngForTrackById]',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class NgForTrackByIdDirective<T extends { id: string | number }> {
|
||||||
|
@Input() ngForOf!: NgIterable<T>; // 2
|
||||||
|
|
||||||
|
private ngFor = inject(NgForOf<T>, { self: true }); // 3
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.ngFor.ngForTrackBy = (index: number, item: T) => item.id; // 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NgForTrackByDirective: Provider[] = [
|
||||||
|
NgForTrackByIdDirective,
|
||||||
|
NgForTrackByPropDirective,
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [NgFor, NgForTrackByDirective],
|
||||||
|
exports: [NgFor, NgForTrackByDirective],
|
||||||
|
})
|
||||||
|
export class NgForTrackByModule {}
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
"start": "nx serve",
|
"start": "nx serve",
|
||||||
"build": "nx build",
|
"build": "nx build",
|
||||||
"test": "nx test",
|
"test": "nx test",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install",
|
||||||
|
"doc:dev": "cd docs && npm run dev"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user