diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 00000000..95edec7b
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,11 @@
+{
+ "extends": [
+ "eslint:recommended",
+ "plugin:react/recommended",
+ "plugin:prettier/recommended"
+ ],
+ "plugins": [
+ "react",
+ "prettier"
+ ]
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..6c99e89f
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,9 @@
+{
+ "semi": false,
+ "singleQuote": true,
+ "endOfLine": "lf",
+ "singleAttributePerLine": true,
+ "bracketSameLine": true,
+ "trailingComma": "none",
+ "arrowParens": "avoid"
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 32dc87a5..5bbb92ea 100644
--- a/README.md
+++ b/README.md
@@ -1,219 +1,70 @@
-# π¬ μν κ²μ
-μ£Όμ΄μ§ APIλ₯Ό νμ©ν΄ '[μμ± μμ](https://stupefied-hodgkin-d9d350.netlify.app/)' μ²λΌ μμ λ‘κ² μν κ²μ κΈ°λ₯μ ꡬνν΄λ³΄μΈμ!
-κ³Όμ μν λ° λ¦¬λ·° κΈ°κ°μ λ³λ 곡μ§λ₯Ό μ°Έκ³ νμΈμ!
+# π¬ OMDb APIλ₯Ό νμ©ν μν κ²μ μ¬μ΄νΈ 'MovieMatrix'
-## κ³Όμ μν λ° μ μΆ λ°©λ²
+## λ°°ν¬
-```
-KDTκΈ°μλ²νΈ_μ΄λ¦ | E.g, KDT0_ParkYoungWoong
-```
+ [MovieMatrix](https://stalwart-nougat-119b80.netlify.app/#/)
-1. νμ¬ μ μ₯μλ₯Ό λ‘컬μ ν΄λ‘ (Clone)ν©λλ€.
-1. μμ μ λ³Έλͺ
μΌλ‘ λΈλμΉλ₯Ό μμ±ν©λλ€.(κ΅¬λΆ κ°λ₯νλλ‘ λ³Έλͺ
μ κΌ νμ€μΉΌμΌμ΄μ€λ‘ νμνμΈμ, `git branch KDT0_ParkYoungWoong`)
-1. μμ μ λ³Έλͺ
λΈλμΉμμ κ³Όμ λ₯Ό μνν©λλ€.
-1. κ³Όμ μνμ΄ μλ£λλ©΄, μμ μ λ³Έλͺ
λΈλμΉλ₯Ό μ격 μ μ₯μμ νΈμ(Push)ν©λλ€.(`main` λΈλμΉμ νΈμνμ§ μλλ‘ κΌ μ£ΌμνμΈμ, `git push origin KDT0_ParkYoungWoong`)
-1. μ μ₯μμμ `main` λΈλμΉλ₯Ό λμμΌλ‘ Pull Request μμ±νλ©΄, κ³Όμ μ μΆμ΄ μλ£λ©λλ€!(E.g, `main` <== `KDT0_ParkYoungWoong`)
+----------
-- `main` νΉμ λ€λ₯Έ μ¬λμ λΈλμΉλ‘ μ λ λ³ν©νμ§ μλλ‘ μ£ΌμνμΈμ!
-- Pull Requestμμ 보μ΄λ μ€λͺ
μ λ€λ₯Έ μ¬λλ€μ΄ μ΄ν΄νκΈ° μ½λλ‘ κΌΌκΌΌνκ² μμ±νμΈμ!
-- Pull Requestμμ κ³Όμ μ μΆ ν μ λ λ³ν©(Merge)νμ§ μλλ‘ μ£ΌμνμΈμ!
-- κ³Όμ μν λ° μ μΆ κ³Όμ μμ λ¬Έμ κ° λ°μν κ²½μ°, λ°λ‘ λ΄λΉ λ©ν λ κ°μ¬μμ μκΈ°νμΈμ!
+## κΈ°μ μ€ν
-## μꡬμ¬ν
-νμ μꡬμ¬νμ κΌ λ¬μ±ν΄μΌ νλ λͺ©νλ‘, μμ /μμ λ λΆκ°νκ³ μΆκ°λ κ°λ₯ν©λλ€.
-μ ν μꡬμ¬νμ λ¨μ μμλ‘, μμ λ‘κ² μΆκ°/μμ /μμ ν΄μ ꡬνν΄λ³΄μΈμ.
-κ° μꡬμ¬νμ λ¬μ± ν λ§ν¬λ€μ΄μμ `- [x]`λ‘ νμνμΈμ.
-### β νμ
+[](https://camo.githubusercontent.com/39a7c00c73ce5a0060e988dbf9721dc0bc2a9032e631656da6582e7519018c48/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f564954452d3634364346463f7374796c653d666c61742d737175617265266c6f676f3d76697465266c6f676f436f6c6f723d7768697465) [](https://camo.githubusercontent.com/afd3a4b22ff275fad5bfb52a091d5933fe213425e4bff924e88ae45f11f794b1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e504d2d4342333833373f7374796c653d666c61742d737175617265266c6f676f3d6e706d266c6f676f436f6c6f723d7768697465)
+
+
+[](https://camo.githubusercontent.com/78dc5835c254ff7423aabdd3a0fb6592c334072417a09e6556f446029395bae8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48544d4c352d4533344632363f7374796c653d666c61742d737175617265266c6f676f3d68746d6c35266c6f676f436f6c6f723d7768697465) [](https://camo.githubusercontent.com/400bc66d72448f5f1fa3ab036333af9578794af175639242723d7f5eac25c1f9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a4156415343524950542d4637444631453f7374796c653d666c61742d737175617265266c6f676f3d6a617661736372697074266c6f676f436f6c6f723d7768697465)
+
+[](https://camo.githubusercontent.com/69139a1fb652b0445950106929ffd6322b3299b73b82d629e720babb9cef1988/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e45544c4946592d3030433742373f7374796c653d666c61742d737175617265266c6f676f3d6e65746c696679266c6f676f436f6c6f723d7768697465)
-- [ ] μν μ λͺ©μΌλ‘ κ²μμ΄ κ°λ₯ν΄μΌ ν©λλ€!
-- [ ] κ²μλ κ²°κ³Όμ μν λͺ©λ‘μ΄ μΆλ ₯λΌμΌ ν©λλ€!
-- [ ] λ¨μΌ μνμ μμΈμ 보(μ λͺ©, κ°λ΄μ°λ, νμ , μ₯λ₯΄, κ°λ
, λ°°μ°, μ€κ±°λ¦¬, ν¬μ€ν° λ±)λ₯Ό λ³Ό μ μμ΄μΌ ν©λλ€!
-- [ ] μ€μ μλΉμ€λ‘ λ°°ν¬νκ³ μ κ·Ό κ°λ₯ν λ§ν¬λ₯Ό μΆκ°ν΄μΌ ν©λλ€.
+----------
+
+## μꡬμ¬ν
+
+### β νμ
+- [X] μν μ λͺ©μΌλ‘ κ²μμ΄ κ°λ₯ν΄μΌ ν©λλ€!
+- [x] κ²μλ κ²°κ³Όμ μν λͺ©λ‘μ΄ μΆλ ₯λΌμΌ ν©λλ€!
+- [x] λ¨μΌ μνμ μμΈμ 보(μ λͺ©, κ°λ΄μ°λ, νμ , μ₯λ₯΄, κ°λ
, λ°°μ°, μ€κ±°λ¦¬, ν¬μ€ν° λ±)λ₯Ό λ³Ό μ μμ΄μΌ ν©λλ€!
+- [x] μ€μ μλΉμ€λ‘ λ°°ν¬νκ³ μ κ·Ό κ°λ₯ν λ§ν¬λ₯Ό μΆκ°ν΄μΌ ν©λλ€.
### β μ ν
-- [ ] ν λ²μ κ²μμΌλ‘ μν λͺ©λ‘μ΄ 20κ° μ΄μ κ²μλλλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] μν κ°λ΄μ°λλ‘ κ²μν μ μλλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] μν λͺ©λ‘μ κ²μνλ λμ λ‘λ© μ λλ©μ΄μ
μ΄ λ³΄μ΄λλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] 무ν μ€ν¬λ‘€ κΈ°λ₯μ μΆκ°ν΄μ μΆκ° μν λͺ©λ‘μ λ³Ό μ μλλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] μν ν¬μ€ν°κ° μμ κ²½μ° λ체 μ΄λ―Έμ§λ₯Ό μΆλ ₯νλλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] μν μμΈμ λ³΄κ° μΆλ ₯λκΈ° μ μ λ‘λ© μ λλ©μ΄μ
μ΄ λ³΄μ΄λλ‘ λ§λ€μ΄λ³΄μΈμ.
-- [ ] μν μμΈμ 보 ν¬μ€ν°λ₯Ό κ³ ν΄μλλ‘ μΆλ ₯ν΄λ³΄μΈμ. (μ€μκ° μ΄λ―Έμ§ 리μ¬μ΄μ§)
-- [ ] μ°¨λ³νκ° κ°λ₯νλλ‘ νλ‘μ νΈλ₯Ό μ΅λν μμκ² λ§λ€μ΄λ³΄μΈμ.
-- [ ] μνμ κ΄λ ¨λ κΈ°ν κΈ°λ₯λ κ³ λ €ν΄λ³΄μΈμ.
-
-## API κΈ°λ³Έ μ¬μ©λ²
-
-```curl
-curl https://omdbapi.com/?apikey=7035c60c
- \ -X 'GET'
-```
-
-## μν λͺ©λ‘ κ²μ
-
-μν λͺ©λ‘μ ν λ²μ μ΅λ 10κ°κΉμ§ κ²μν μ μμ΅λλ€.
-
-νλΌλ―Έν° | μ€λͺ
| κΈ°λ³Έκ°
----|----------------------|---
-`s` | κ²μν μν μ λͺ©(νμ!) | -
-`y` | κ²μν κ°λ΄μ°λ, λΉ κ°μ μ 체 κ²μ | -
-`page` | κ²μν νμ΄μ§ λ²νΈ | `1`
-
-μμ² μ½λ μμ:
-
-```js
-async function getMovies(title, year = '', page = 1) {
- const s = `&s=${title}`
- const y = `&y=${year}`
- const p = `&page=${page}`
- try {
- const res = await fetch(`https://omdbapi.com/?apikey=7035c60c${s}${y}${p}`)
- const json = await res.json()
- if (json.Response === 'True') {
- const { Search: movies, totalResults } = json
- return {
- movies,
- totalResults
- }
- }
- return json.Error
- } catch (error) {
- console.log(error)
- }
-}
-```
-
-μλ΅ λ°μ΄ν° νμ
λ° μμ:
-
-```ts
-interface ResponseValue {
- Search: Movie[] // κ²μλ μν λͺ©λ‘, μ΅λ 10κ°
- totalResults: string // κ²μλ μν κ°μ
- Response: 'True' | 'False' // μμ² μ±κ³΅ μ¬λΆ
-}
-interface Movie {
- Title: string // μν μ λͺ©
- Year: string // μν κ°λ΄μ°λ
- imdbID: string // μν κ³ μ ID
- Type: string // μν νμ
- Poster: string // μν ν¬μ€ν° μ΄λ―Έμ§ URL
-}
-```
-
-```json
-{
- "Search": [
- {
- "Title": "Frozen",
- "Year": "2013",
- "imdbID": "tt2294629",
- "Type": "movie",
- "Poster": "https://m.media-amazon.com/images/M/MV5BMTQ1MjQwMTE5OF5BMl5BanBnXkFtZTgwNjk3MTcyMDE@._V1_SX300.jpg"
- },
- {
- "Title": "Frozen II",
- "Year": "2019",
- "imdbID": "tt4520988",
- "Type": "movie",
- "Poster": "https://m.media-amazon.com/images/M/MV5BMjA0YjYyZGMtN2U0Ni00YmY4LWJkZTItYTMyMjY3NGYyMTJkXkEyXkFqcGdeQXVyNDg4NjY5OTQ@._V1_SX300.jpg"
- }
- ],
- "totalResults": "338",
- "Response": "True"
-}
-```
-
-## μν μμ μ 보 κ²μ
-
-λ¨μΌ μνμ μμ μ 보λ₯Ό κ²μν©λλ€.
-
-νλΌλ―Έν° | μ€λͺ
| κΈ°λ³Έκ°
----|---|---
-`i` | κ²μν μν ID(νμ!) |
-`plot` | μ€κ±°λ¦¬ κΈΈμ΄ | `short`
-
-μμ² μ½λ μμ:
-
-```js
-async function getMovie(id) {
- const res = await fetch(`https://omdbapi.com/?apikey=7035c60c&i=${id}&plot=full`)
- const json = await res.json()
- if (json.Response === 'True') {
- return json
- }
- return json.Error
-}
-```
-
-μλ΅ λ°μ΄ν° νμ
λ° μμ:
-
-```ts
-interface ResponseValue {
- Title: string // μν μ λͺ©
- Year: string // μν κ°λ΄μ°λ
- Rated: string // μν λ±κΈ
- Released: string // μν κ°λ΄μΌ
- Runtime: string // μν μμμκ°
- Genre: string // μν μ₯λ₯΄
- Director: string // μν κ°λ
- Writer: string // μν μκ°
- Actors: string // μν μΆμ°μ§
- Plot: string // μν μ€κ±°λ¦¬
- Language: string // μν μΈμ΄
- Country: string // μν μ μ κ΅κ°
- Awards: string // μν μμ λ΄μ
- Poster: string // μν ν¬μ€ν° μ΄λ―Έμ§ URL
- Ratings: Rating[] // μν νμ μ 보
- Metascore: string // μν λ©νμ€μ½μ΄
- imdbRating: string // μν IMDB νμ
- imdbVotes: string // μν IMDB ν¬ν μ
- imdbID: string // μν κ³ μ ID
- Type: string // μν νμ
- DVD: string // μν DVD μΆμμΌ
- BoxOffice: string // μν λ°μ€μ€νΌμ€
- Production: string // μν μ μμ¬
- Website: string // μν 곡μ μΉμ¬μ΄νΈ
- Response: string // μμ² μ±κ³΅ μ¬λΆ
-}
-interface Rating { // μν νμ μ 보
- Source: string // νμ μ 곡 μ¬μ΄νΈ
- Value: string // νμ
-}
-```
-
-```json
-{
- "Title": "Frozen",
- "Year": "2013",
- "Rated": "PG",
- "Released": "27 Nov 2013",
- "Runtime": "102 min",
- "Genre": "Animation, Adventure, Comedy",
- "Director": "Chris Buck, Jennifer Lee",
- "Writer": "Jennifer Lee, Hans Christian Andersen, Chris Buck",
- "Actors": "Kristen Bell, Idina Menzel, Jonathan Groff",
- "Plot": "When the newly crowned Queen Elsa accidentally uses her power to turn things into ice to curse her home in infinite winter, her sister Anna teams up with a mountain man, his playful reindeer, and a snowman to change the weather co...",
- "Language": "English, Norwegian",
- "Country": "United States",
- "Awards": "Won 2 Oscars. 82 wins & 60 nominations total",
- "Poster": "https://m.media-amazon.com/images/M/MV5BMTQ1MjQwMTE5OF5BMl5BanBnXkFtZTgwNjk3MTcyMDE@._V1_SX300.jpg",
- "Ratings": [
- { "Source": "Internet Movie Database", "Value": "7.4/10" },
- { "Source": "Rotten Tomatoes", "Value": "90%" },
- { "Source": "Metacritic", "Value": "75/100" }
- ],
- "Metascore": "75",
- "imdbRating": "7.4",
- "imdbVotes": "620,489",
- "imdbID": "tt2294629",
- "Type": "movie",
- "DVD": "18 Mar 2014",
- "BoxOffice": "$400,953,009",
- "Production": "N/A",
- "Website": "N/A",
- "Response": "True"
-}
-```
+- [X] ν λ²μ κ²μμΌλ‘ μν λͺ©λ‘μ΄ 20κ° μ΄μ κ²μλλλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] μν κ°λ΄μ°λλ‘ κ²μν μ μλλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] μν λͺ©λ‘μ κ²μνλ λμ λ‘λ© μ λλ©μ΄μ
μ΄ λ³΄μ΄λλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] 무ν μ€ν¬λ‘€ κΈ°λ₯μ μΆκ°ν΄μ μΆκ° μν λͺ©λ‘μ λ³Ό μ μλλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] μν ν¬μ€ν°κ° μμ κ²½μ° λ체 μ΄λ―Έμ§λ₯Ό μΆλ ₯νλλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] μν μμΈμ λ³΄κ° μΆλ ₯λκΈ° μ μ λ‘λ© μ λλ©μ΄μ
μ΄ λ³΄μ΄λλ‘ λ§λ€μ΄λ³΄μΈμ.
+- [x] μν μμΈμ 보 ν¬μ€ν°λ₯Ό κ³ ν΄μλλ‘ μΆλ ₯ν΄λ³΄μΈμ. (μ€μκ° μ΄λ―Έμ§ 리μ¬μ΄μ§)
+- [x] μ°¨λ³νκ° κ°λ₯νλλ‘ νλ‘μ νΈλ₯Ό μ΅λν μμκ² λ§λ€μ΄λ³΄μΈμ.
+- [x] μνμ κ΄λ ¨λ **κΈ°νκΈ°λ₯**λ κ³ λ €ν΄λ³΄μΈμ.
+
+----------
+
+## κΈ°νκΈ°λ₯
+
+### My Movie (λ§μ΄ 무λΉ)
+
+- μνλ₯Ό κ²μ ν λ§μμ λλ μνμ μμΈ νμ΄μ§μμ ννΈ λ²νΌμ ν΄λ¦ν κ²½μ° LocalStorageμ ν΄λΉ μνμ μ λ³΄κ° μ μ₯λμ΄ μ΄ν 'My Movie' νμ΄μ§μμ μ μ₯λ μν 리μ€νΈλ₯Ό νμΈν μ μλλ‘ κ΅¬ννμμ΅λλ€.
+
+----------
+
+## νλ‘μ νΈ λ¦¬λ·°
+
+### μΈμ¬μ΄νΈ
+
+- μλ°μ€ν¬λ¦½νΈλ₯Ό νμ©νμ¬ κ°κ°μ κ°λ³ μ»΄ν¬λνΈλ₯Ό λλκ³ UIμ κΈ°λ₯μ λ°λΌ νμν μ»΄ν¬λνΈλ₯Ό μν¬νΈνμ¬ μ¬μ©νλ λ°©λ²μ λ°°μΈ μ μμμ΅λλ€.
+
+- APIλ₯Ό νμ©νμ¬ μλ²λ‘λΆν° νμν λ°μ΄ν°λ₯Ό μμ²νκ³ λΆλ¬μ¨ λ°μ΄ν°λ₯Ό μ°μμ λ°λΌ μλ°μ€ν¬λ¦½νΈλ‘ κ°κ³΅ λ° νμ©νλ λ°©λ²μ λ°°μΈ μ μμμ΅λλ€.
+
+- λΌμ°ν°λ₯Ό νμ©νμ¬ URLμ λ°λΌ νμ΄μ§λ₯Ό λλκ³ μμΈ μ²λ¦¬λ₯Ό ν΅ν΄ 404 νμ΄μ§λ₯Ό ꡬννλ λ°©λ²μ λ°°μ μ΅λλ€.
+
+- μ€ν μ΄ κΈ°λ₯μ ν΅ν΄ νμν λ°μ΄ν°λ₯Ό 곡ν΅μΌλ‘ κ΄λ¦¬νκ³ κ°κ°μ μ»΄ν¬λνΈμμ νμ©νλ λ°©λ²μ λ°°μ μ΅λλ€.
+
+
+### λ¬Έμ μ
+
+- λ―Έλμ΄ μΏΌλ¦¬ μ μ© : PC λΉμ¨μ λ§κ² μ μλ CSSλ₯Ό λ―Έλμ΄ μΏΌλ¦¬λ₯Ό ν΅ν΄ λͺ¨λ°μΌ λ μ΄μμμ ꡬμ±νλ κ³Όμ μμ μΌλΆ νλͺ©μ΄ μ λλ‘ μ μ©λμ§ μλ λ¬Έμ κ° μμμ΅λλ€. ν₯ν νλ‘μ νΈμ λ―Έλμ΄ μΏΌλ¦¬λ₯Ό μ μ©ν κ²½μ° λͺ¨λ°μΌ λ μ΄μμμ λ¨Όμ ꡬννλ μμλ‘ μ§νν΄μΌ ν κ² κ°μ΅λλ€!
+
+- λ€λ‘κ°κΈ° λ²νΌ ꡬν : μν μμΈ νμ΄μ§μμ μ΄μ νλ©΄(κ²μ νμ΄μ§)μΌλ‘ μ΄λνκΈ° μν΄ 'λ€λ‘κ°κΈ°' λ²νΌμ ꡬννμμΌλ, λΈλΌμ°μ μ λ€λ‘κ°κΈ°μ κ°μ κΈ°λ³Έ λ°©μμΌλ‘, μ΄μ νμ΄μ§κ° κ²μ νμ΄μ§κ° μλ κ²½μ° κΈ°λ₯μ΄ μ λλ‘ μλνμ§ μλ λ¬Έμ κ° μμ΅λλ€. (μ΄μ νμ΄μ§μλ μκ΄ μμ΄ νμ¬ κ²μ λ΄μ νμ΄μ§λ‘ μ΄λν μ μλ λ°©λ²μ΄ μμκΉμ?)
diff --git a/counter.js b/counter.js
new file mode 100644
index 00000000..881e2d7a
--- /dev/null
+++ b/counter.js
@@ -0,0 +1,9 @@
+export function setupCounter(element) {
+ let counter = 0
+ const setCounter = (count) => {
+ counter = count
+ element.innerHTML = `count is ${counter}`
+ }
+ element.addEventListener('click', () => setCounter(counter + 1))
+ setCounter(0)
+}
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..ad0c6ed2
--- /dev/null
+++ b/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+ MOVIE MATRIX
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/javascript.svg b/javascript.svg
new file mode 100644
index 00000000..f9abb2b7
--- /dev/null
+++ b/javascript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 00000000..b400b4e3
--- /dev/null
+++ b/main.js
@@ -0,0 +1,24 @@
+import './style.css'
+import javascriptLogo from './javascript.svg'
+import viteLogo from '/vite.svg'
+import { setupCounter } from './counter.js'
+
+document.querySelector('#app').innerHTML = `
+
+
+
+
+
+
+
+
Hello Vite!
+
+
+
+
+ Click on the Vite logo to learn more
+
+
+`
+
+setupCounter(document.querySelector('#counter'))
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..219599e1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1730 @@
+{
+ "name": "movetomovie",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "movetomovie",
+ "version": "0.0.0",
+ "dependencies": {
+ "compass": "^0.1.1"
+ },
+ "devDependencies": {
+ "eslint": "^8.39.0",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "prettier": "^2.8.8",
+ "vite": "^4.3.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
+ "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
+ "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
+ "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
+ "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
+ "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
+ "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
+ "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
+ "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
+ "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
+ "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
+ "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
+ "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
+ "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
+ "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
+ "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
+ "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
+ "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
+ "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
+ "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
+ "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
+ "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
+ "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
+ "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
+ "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.5.1",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
+ "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+ "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "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==",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/compass": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/compass/-/compass-0.1.1.tgz",
+ "integrity": "sha512-9z+RjcAGig6/9uAFeCbWajTvrye1u61ca5QrqN6JTrHRYOOgAKs33Xv/3/fODYthxWC2C97AE0e45PLETNRicg=="
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.17.18",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
+ "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.18",
+ "@esbuild/android-arm64": "0.17.18",
+ "@esbuild/android-x64": "0.17.18",
+ "@esbuild/darwin-arm64": "0.17.18",
+ "@esbuild/darwin-x64": "0.17.18",
+ "@esbuild/freebsd-arm64": "0.17.18",
+ "@esbuild/freebsd-x64": "0.17.18",
+ "@esbuild/linux-arm": "0.17.18",
+ "@esbuild/linux-arm64": "0.17.18",
+ "@esbuild/linux-ia32": "0.17.18",
+ "@esbuild/linux-loong64": "0.17.18",
+ "@esbuild/linux-mips64el": "0.17.18",
+ "@esbuild/linux-ppc64": "0.17.18",
+ "@esbuild/linux-riscv64": "0.17.18",
+ "@esbuild/linux-s390x": "0.17.18",
+ "@esbuild/linux-x64": "0.17.18",
+ "@esbuild/netbsd-x64": "0.17.18",
+ "@esbuild/openbsd-x64": "0.17.18",
+ "@esbuild/sunos-x64": "0.17.18",
+ "@esbuild/win32-arm64": "0.17.18",
+ "@esbuild/win32-ia32": "0.17.18",
+ "@esbuild/win32-x64": "0.17.18"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
+ "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.4.0",
+ "@eslint/eslintrc": "^2.0.2",
+ "@eslint/js": "8.39.0",
+ "@humanwhocodes/config-array": "^0.11.8",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.0",
+ "eslint-visitor-keys": "^3.4.0",
+ "espree": "^9.5.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz",
+ "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
+ "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
+ "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.5.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
+ "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "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==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "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": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+ "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-sdsl": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
+ "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/postcss": {
+ "version": "8.4.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
+ "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.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==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz",
+ "integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==",
+ "dev": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.1.tgz",
+ "integrity": "sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.17.5",
+ "postcss": "^8.4.21",
+ "rollup": "^3.20.2"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..af7b8536
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "movetomovie",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "eslint": "^8.39.0",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "prettier": "^2.8.8",
+ "vite": "^4.3.0"
+ },
+ "dependencies": {
+ "compass": "^0.1.1"
+ }
+}
diff --git a/public/vite.svg b/public/vite.svg
new file mode 100644
index 00000000..e7b8dfb1
--- /dev/null
+++ b/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/App.js b/src/App.js
new file mode 100644
index 00000000..e1330db4
--- /dev/null
+++ b/src/App.js
@@ -0,0 +1,12 @@
+import Component from './core/Component.js'
+import TheHeader from './components/TheHeader.js'
+import TheFooter from './components/TheFooter.js'
+
+export default class App extends Component {
+ render() {
+ const routerView = document.createElement('router-view')
+ const header = new TheHeader().el
+ const footer = new TheFooter().el
+ this.el.append(header, routerView, footer)
+ }
+}
diff --git a/src/components/Headline.js b/src/components/Headline.js
new file mode 100644
index 00000000..2d408551
--- /dev/null
+++ b/src/components/Headline.js
@@ -0,0 +1,12 @@
+import Component from '../core/Component'
+
+export default class Headline extends Component {
+ render() {
+ this.el.classList.add('headline')
+ this.el.innerHTML = `
+
+

+ MOVIE
MATRIX
+ `
+ }
+}
diff --git a/src/components/MovieItem.js b/src/components/MovieItem.js
new file mode 100644
index 00000000..526be2a7
--- /dev/null
+++ b/src/components/MovieItem.js
@@ -0,0 +1,24 @@
+import Component from '../core/Component'
+
+export default class MovieItem extends Component {
+ constructor(props) {
+ super({
+ props,
+ tagName: 'a'
+ })
+ }
+ render() {
+ const { movie } = this.props
+
+ this.el.setAttribute('href', `#/movie?id=${movie.imdbID}`)
+ this.el.classList.add('movie')
+ this.el.style.backgroundImage = `url(${movie.Poster})`
+ this.el.innerHTML = /*html*/ `
+
+
${movie.Type.toUpperCase()}
+
${movie.Year}
+
${movie.Title}
+
+ `
+ }
+}
diff --git a/src/components/MovieList.js b/src/components/MovieList.js
new file mode 100644
index 00000000..660d38ed
--- /dev/null
+++ b/src/components/MovieList.js
@@ -0,0 +1,54 @@
+import Component from '../core/Component'
+import movieStore from '../store/movie'
+import MovieItem from './MovieItem'
+import { searchMovies } from '../store/movie'
+
+export default class MovieList extends Component {
+ constructor() {
+ super()
+
+ this.sortBy = 'latest'
+ this.filterBy = 'all'
+
+ movieStore.subscribe('movies', () => {
+ // ꡬλ
+ this.render()
+ // μν λͺ©λ‘μ΄ λ³κ²½λλ©΄ render λ©μλλ₯Ό νΈμΆνμ¬ λ³κ²½λ μν λͺ©λ‘μ νλ©΄μ μΆλ ₯
+ })
+ movieStore.subscribe('loading', () => {
+ this.render()
+ })
+ movieStore.subscribe('message', () => {
+ this.render()
+ })
+ }
+
+ render() {
+ this.el.classList.add('movie-List')
+ this.el.innerHTML = /*html*/ `
+ ${
+ movieStore.state.message
+ ? `${movieStore.state.message}
`
+ : ''
+ }
+
+ `
+
+ const moviesEl = this.el.querySelector('.movies')
+ moviesEl?.append(
+ // μ΅μ
λ 체μ΄λ, μν λͺ©λ‘μ΄ μμ κ²½μ° append λ©μλλ₯Ό νΈμΆνμ§ μμ
+ ...movieStore.state.movies.map(movie => {
+ // μ κ° μ°μ°μ
+ // movieStore.state.moviesμ μλ μν λͺ©λ‘μ μννλ©°
+ return new MovieItem({ movie }).el // μν μμ΄ν
μ λ°°μ΄λ‘ λ°ν
+ })
+ )
+
+ const loaderEl = this.el.querySelector('.the-loader')
+ movieStore.state.loading
+ ? loaderEl.classList.remove('hide')
+ : loaderEl.classList.add('hide')
+
+ console.log('MovieList.render', movieStore)
+ }
+}
diff --git a/src/components/MovieListMore.js b/src/components/MovieListMore.js
new file mode 100644
index 00000000..d8d0b32b
--- /dev/null
+++ b/src/components/MovieListMore.js
@@ -0,0 +1,41 @@
+import Component from '../core/Component'
+import movieStore, { searchMovies } from '../store/movie'
+
+export default class MovieListMore extends Component {
+ constructor() {
+ super({
+ tagName: 'button'
+ })
+ movieStore.subscribe('pageMax', () => {
+ // movieStore.state.page < movieStore.state.pageMax
+ const { page, pageMax } = movieStore.state
+ if (page < pageMax) {
+ this.el.classList.remove('hide')
+ } else {
+ this.el.classList.add('hide')
+ }
+ })
+ window.addEventListener('scroll', () => {
+ // λ μ΄μ λ‘λν μνκ° μλ κ²½μ° MovieListMoreλ₯Ό μ¨κΉλλ€.
+ if (movieStore.state.page >= movieStore.state.pageMax) {
+ return
+ }
+ const { scrollTop, clientHeight, scrollHeight } = document.documentElement
+ if (scrollTop + clientHeight >= scrollHeight - 100 && !this.isLoading) {
+ // scrolltopμ μ€ν¬λ‘€μ΄ μΌλ§λ λ΄λ €κ°λμ§λ₯Ό λνλ΄λ μμ±
+ // clientHeightλ λΈλΌμ°μ νλ©΄μ λμ΄
+ // scrollHeightλ μ€ν¬λ‘€μ΄ μλ μμμ μ 체 λμ΄
+ // μ€ν¬λ‘€μ΄ 맨 μλμ μμΉνκ³ μκ³ , λ‘λ© μ€μ΄ μλ λ
+ this.isLoading = true
+ searchMovies(movieStore.state.page + 1).then(() => {
+ this.isLoading = false
+ })
+ }
+ })
+ }
+ render() {
+ this.el.classList.add('btn', 'btn-more', 'hide')
+ this.el.textContent = 'β'
+ this.isLoading = false
+ }
+}
diff --git a/src/components/MyList.js b/src/components/MyList.js
new file mode 100644
index 00000000..3fcedbbc
--- /dev/null
+++ b/src/components/MyList.js
@@ -0,0 +1,16 @@
+import Component from '../core/Component'
+import movieStore from '../store/movie'
+import MovieItem from './MovieItem'
+import { searchMovies } from '../store/movie'
+
+export default class MovieList extends Component {
+ constructor() {
+ super()
+ }
+ render() {
+ this.el.classList.add('movie-List')
+ this.el.innerHTML = /*html*/ `
+ γΉγ
γ΄γΉγ
γ΄γΉγ
γ΄
'
+ `
+ }
+}
diff --git a/src/components/NextBtn.js b/src/components/NextBtn.js
new file mode 100644
index 00000000..a6e76299
--- /dev/null
+++ b/src/components/NextBtn.js
@@ -0,0 +1,27 @@
+import Component from '../core/Component'
+import movieStore from '../store/movie'
+
+export default class NextBtn extends Component {
+ render() {
+ const currentMovie = movieStore.state.currentMovie
+ if (!currentMovie) return null // check if currentMovie is defined
+
+ const currentImdbId = currentMovie.imdbID // νμ¬ λ³΄κ³ μλ μνμ imdbID κ°
+ const movies = movieStore.state.movies // μ 체 μν κ°μ²΄ λ°μ΄ν°
+
+ // νμ¬ λ³΄κ³ μλ μνμ μΈλ±μ€λ₯Ό μ°Ύμ΅λλ€.
+ const currentIndex = movies.findIndex(
+ movie => movie.imdbID === currentImdbId
+ )
+
+ if (currentIndex !== -1 && currentIndex < movies.length - 1) {
+ // λ€μ μν κ°μ²΄ λ°μ΄ν°μ imdbID κ°μ κ°μ Έμ΅λλ€.
+ const nextMovieImdbId = movies[currentIndex + 1].imdbID
+
+ // λ€μ μνλ₯Ό 보기 μν URLμ μμ±ν©λλ€.
+ const nextMovieUrl = `${window.location.pathname}?imdbID=${nextMovieImdbId}`
+ } else {
+ return null // return null if the current movie is the last one
+ }
+ }
+}
diff --git a/src/components/Search.js b/src/components/Search.js
new file mode 100644
index 00000000..53967500
--- /dev/null
+++ b/src/components/Search.js
@@ -0,0 +1,47 @@
+import Component from '../core/Component'
+import movieStore, { searchMovies } from '../store/movie'
+
+export default class Search extends Component {
+ render() {
+ this.el.classList.add('search')
+ this.el.innerHTML = /*html*/ `
+
+
+
+ `
+
+ const inputTextEl = this.el.querySelector('.searchText')
+ const inputYearEl = this.el.querySelector('.searchYear')
+
+ inputTextEl.addEventListener('input', () => {
+ movieStore.state.searchText = inputTextEl.value
+ })
+ inputTextEl.addEventListener('keydown', event => {
+ if (event.key === 'Enter' && movieStore.state.searchText.trim()) {
+ searchMovies(1)
+ searchMovies(2)
+ }
+ })
+
+ inputYearEl.addEventListener('input', () => {
+ movieStore.state.searchYear = inputYearEl.value
+ })
+
+ inputYearEl.addEventListener('keydown', event => {
+ if (event.key === 'Enter') {
+ searchMovies(1)
+ searchMovies(2)
+ }
+ })
+
+ const buttonEl = this.el.querySelector('button')
+ buttonEl.addEventListener('click', () => {
+ if (movieStore.state.searchText.trim()) {
+ searchMovies(1)
+ searchMovies(2)
+ }
+ })
+ }
+}
diff --git a/src/components/TheFooter.js b/src/components/TheFooter.js
new file mode 100644
index 00000000..78e32c0c
--- /dev/null
+++ b/src/components/TheFooter.js
@@ -0,0 +1,22 @@
+import Component from '../core/Component'
+import aboutStore from '../store/about'
+
+export default class TheFooter extends Component {
+ constructor() {
+ super({
+ tagName: 'footer'
+ })
+ }
+ render() {
+ const { github, blog } = aboutStore.state
+ this.el.innerHTML = /*html*/ `
+
+
+ `
+ this.el.classList.add('footer')
+ }
+}
diff --git a/src/components/TheHeader.js b/src/components/TheHeader.js
new file mode 100644
index 00000000..6fe48490
--- /dev/null
+++ b/src/components/TheHeader.js
@@ -0,0 +1,59 @@
+import Component from '../core/Component'
+import Search from '../components/Search'
+
+export default class TheHeader extends Component {
+ constructor() {
+ super({
+ tagName: 'header',
+ state: {
+ menus: [
+ {
+ name: 'SEARCH',
+ href: '#/'
+ },
+ {
+ name: 'MY MOVIE',
+ href: '#/mymovie'
+ },
+ {
+ name: 'ABOUT',
+ href: '#/about'
+ }
+ ]
+ }
+ })
+ window.addEventListener('popstate', () => {
+ this.render()
+ })
+ }
+ render() {
+ this.el.innerHTML = /*html*/ `
+
+
+ `
+ this.el.classList.add('header')
+ }
+}
diff --git a/src/core/Component.js b/src/core/Component.js
new file mode 100644
index 00000000..f33d7744
--- /dev/null
+++ b/src/core/Component.js
@@ -0,0 +1,12 @@
+export default class Component {
+ constructor(payload = {}) {
+ const { tagName = 'div', state = {}, props = {} } = payload
+ this.el = document.createElement(tagName)
+ this.state = state // payloadλ₯Ό ν΅ν΄ μ λ¬λ°μ stateλ₯Ό this.stateμ μ μ₯
+ this.props = props // payloadλ₯Ό ν΅ν΄ μ λ¬λ°μ propsλ₯Ό this.propsμ μ μ₯
+ this.render()
+ }
+ render() {
+ // νμ₯μ©
+ }
+}
diff --git a/src/core/Store.js b/src/core/Store.js
new file mode 100644
index 00000000..17148dc0
--- /dev/null
+++ b/src/core/Store.js
@@ -0,0 +1,35 @@
+///// Store /////
+export default class Store {
+ constructor(state) {
+ this.state = {} // μν(λ°μ΄ν°)
+ this.observers = {}
+ for (const key in state) {
+ // κ° μνμ λν λ³κ²½ κ°μ(Setter) μ€μ !
+ Object.defineProperty(this.state, key, {
+ // Getter
+ get: () => state[key],
+ // Setter
+ set: val => {
+ state[key] = val
+ if (Array.isArray(this.observers[key])) {
+ // νΈμΆν μ½λ°±μ΄ μλ κ²½μ°!
+ this.observers[key].forEach(observer => observer(val))
+ }
+ }
+ })
+ }
+ }
+ // μν λ³κ²½ ꡬλ
!
+ subscribe(key, cb) {
+ Array.isArray(this.observers[key]) // μ΄λ―Έ λ±λ‘λ μ½λ°±μ΄ μλμ§ νμΈ!
+ ? this.observers[key].push(cb) // μμΌλ©΄ μλ‘μ΄ μ½λ°± λ°μ΄λ£κΈ°!
+ : (this.observers[key] = [cb]) // μμΌλ©΄ μ½λ°± λ°°μ΄λ‘ ν λΉ!
+
+ // μμ)
+ // observers = {
+ // ꡬλ
ν μνμ΄λ¦: [μ€νν μ½λ°±1, μ€νν μ½λ°±2]
+ // movies: [cb, cb, cb],
+ // message: [cb]
+ // }
+ }
+}
diff --git a/src/core/donghae.js b/src/core/donghae.js
new file mode 100644
index 00000000..c2e9018c
--- /dev/null
+++ b/src/core/donghae.js
@@ -0,0 +1,60 @@
+import routes from '../routes/index.js'
+import Component from '../core/Component.js'
+import Store from './Store.js'
+
+// ///// Component /////
+// export class Component {
+// constructor(payload = {}) {
+// const {
+// tagName = 'div', // μ΅μμ μμμ νκ·Έ μ΄λ¦
+// props = {},
+// state = {}
+// } = payload
+// this.el = document.createElement(tagName) // μ»΄ν¬λνΈμ μ΅μμ μμ
+// this.props = props // μ»΄ν¬λνΈκ° μ¬μ©λ λ λΆλͺ¨ μ»΄ν¬λνΈμμ λ°λ λ°μ΄ν°
+// this.state = state // μ»΄ν¬λνΈ μμμ μ¬μ©ν λ°μ΄ν°
+// this.render()
+// }
+// render() {
+// // μ»΄ν¬λνΈλ₯Ό λ λλ§νλ ν¨μ
+// // ...
+// }
+// }
+
+///// Router /////
+// νμ΄μ§ λ λλ§!
+function routeRender(routes) {
+ // μ μν λ ν΄μ λͺ¨λκ° μλλ©΄(ν΄μκ° μμΌλ©΄) /#/λ‘ λ¦¬λ€μ΄λ νΈ!
+ if (!location.hash) {
+ history.replaceState(null, '', '/#/') // (μν, μ λͺ©, μ£Όμ)
+ }
+ const routerView = document.querySelector('router-view')
+ const [hash, queryString = ''] = location.hash.split('?') // λ¬Όμνλ₯Ό κΈ°μ€μΌλ‘ ν΄μ μ 보μ 쿼리μ€νΈλ§μ ꡬλΆ
+
+ // 1) 쿼리μ€νΈλ§μ κ°μ²΄λ‘ λ³νν΄ νμ€ν 리μ μνμ μ μ₯!
+ const query = queryString.split('&').reduce((acc, cur) => {
+ const [key, value] = cur.split('=')
+ acc[key] = value
+ return acc
+ }, {})
+ history.replaceState(query, '') // (μν, μ λͺ©)
+
+ // 2) νμ¬ λΌμ°νΈ μ 보λ₯Ό μ°Ύμμ λ λλ§!
+ const currentRoute = routes.find(route =>
+ new RegExp(`${route.path}/?$`).test(hash)
+ )
+ routerView.innerHTML = ''
+ routerView.append(new currentRoute.component().el)
+
+ // 3) νλ©΄ μΆλ ₯ ν μ€ν¬λ‘€ μμΉ λ³΅κ΅¬!
+ window.scrollTo(0, 0)
+}
+export function createRouter(routes) {
+ // μνλ(νμν) κ³³μμ νΈμΆν μ μλλ‘ ν¨μ λ°μ΄ν°λ₯Ό λ°ν!
+ return function () {
+ window.addEventListener('popstate', () => {
+ routeRender(routes)
+ })
+ routeRender(routes)
+ }
+}
diff --git a/src/defaultPoster.png b/src/defaultPoster.png
new file mode 100644
index 00000000..a9a34da7
Binary files /dev/null and b/src/defaultPoster.png differ
diff --git a/src/main.css b/src/main.css
new file mode 100644
index 00000000..b87fe135
--- /dev/null
+++ b/src/main.css
@@ -0,0 +1,953 @@
+html {
+ --color-universe: #0d1117;
+ --color-white: #fff;
+ --color-white-50: rgba(255, 255, 255, .5);
+ --color-white-30: rgba(255, 255, 255, .3);
+ --color-white-20: rgba(255, 255, 255, .2);
+ --color-white-10: rgba(255, 255, 255, .1);
+ --color-white-5: rgba(255, 255, 255, .05);
+ --color-black: #000;
+ --color-green: #7ee787;
+ --color-purple: #939aff;
+ --color-sky: #a5d6ff;
+ --color-beige: #efe2d5ff;
+ --color-red: #ffa28b;
+ --width: 200px;
+}
+
+body {
+ background-color: var(--color-universe);
+ font-family: 'Roboto', sans-serif;
+ line-height: 1.4;
+ color: var(--color-white);
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+.canvas {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: transparent;
+ z-index: -1;
+}
+
+
+.container {
+ max-width: 1120px;
+ margin: 0 auto;
+ padding: 40px 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+
+.header {
+ padding: 20px 0px 20px;
+ background-color: var(--color-white-5);
+ position: sticky;
+ top: 0;
+ z-index: 9;
+ display: flex;
+ align-items: center;
+ transition: .5s;
+ translate: 0px 0px;
+}
+
+@media (max-width: 844px) {
+
+ .container {
+ display: block;
+ width: 100%;
+ }
+
+ .header.header {
+ width: auto;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ flex-direction: row;
+ }
+
+ .header nav ul {
+ min-width: 1px;
+ /* flex-grow: 0;
+ padding-left: 30px;
+ margin-left: -30px;
+ gap: 40px; */
+ }
+
+ .header {}
+
+ .header .user {
+ display: block;
+
+ }
+
+
+ .headline {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ margin-top: 50px;
+ width: 100%;
+ transform: translate(-60px, 0px);
+ }
+
+ .container .headline h1 {
+ text-align: center;
+ font-size: 55px;
+ }
+
+ .container .headline .logo {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100px;
+ height: 100px;
+ margin-bottom: 30px;
+ }
+
+ .container .headline .logo img {
+ width: 120px;
+ display: block;
+ translate: 0px 0px;
+ }
+
+ .container .search {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 50px;
+ }
+
+ .container .search input {
+ width: 100px;
+ }
+
+ .container .search .searchYear {
+ width: 38px;
+ }
+
+ .container .movie-List {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .container .movie-List .message {
+ width: 300px;
+ background-color: var(--color-white-5);
+ }
+
+ .container .movies a {
+ width: 150px;
+ height: 210px;
+ }
+
+ .container .search button {}
+
+ .the-movie {}
+
+ .the-movie .btn-back {}
+
+ .the-movie .posterinner {
+ flex-direction: column;
+ }
+
+ .the-movie .inner {
+ display: flex;
+ width: 100%;
+ translate: -30px 0px;
+ }
+
+ .the-movie .inner .posterbox .poster {
+ gap: 10px;
+ width: 300px;
+ height: 450px;
+ background-repeat: no-repeat;
+ background-size: cover;
+ }
+
+ .the-movie .posterinner {
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ }
+
+ .the-movie .details {
+ width: auto;
+ }
+
+ .the-movie .inner .posterbox .type {
+ display: none;
+ }
+
+ .the-movie .inner .btn-back {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: static;
+ text-align: center;
+ }
+
+ footer.footer {
+ padding-left: 0px;
+ padding-right: 0px;
+ }
+
+ .about.about {
+ margin: 30px 0px 30px 0px;
+ padding: 60px 0px 60px 0px;
+ width: 100%;
+ height: 100%;
+ }
+
+ router-view inner {
+ margin-left: 0;
+ margin-right: 0;
+ width: 100%;
+ height: 100%;
+ gap: 5px;
+ flex-direction: row;
+ padding: 20px 0px 20px 0px;
+ }
+
+ router-view inner .movie .poster a img {
+ width: 150px;
+ height: 210px;
+ }
+
+ router-view inner .movie .delete {
+ width: 150px;
+ }
+
+ .the-movie .btns {
+ justify-content: center;
+ padding-right: 30px;
+ }
+
+}
+
+.header:hover {
+ background-color: var(--color-white-10);
+}
+
+.header .logo {
+ width: 50px;
+ height: 20px;
+ background-image: url("");
+}
+
+.header nav ul {
+ /* min-width: 400px; */
+ display: flex;
+ gap: 20px;
+}
+
+.header nav ul li a {
+ text-decoration: none;
+ color: var(--color-white-30);
+ transition: .5s;
+}
+
+.header nav ul li a.active {
+ color: var(--color-green);
+}
+
+.header nav ul li a:hover {
+ color: var(--color-green);
+}
+
+.header .user {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 40px;
+ transition: .3s;
+}
+
+.header .user:hover {
+ transform: scale(1.1);
+}
+
+.header .user img {
+ margin-top: 10px;
+ border-radius: 50%;
+ width: 100%;
+}
+
+.footer:hover {
+ background-color: var(--color-white-10);
+}
+
+.footer {
+ background-color: var(--color-white-5);
+ position: relative;
+ bottom: 0;
+ z-index: 9;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ text-align: center;
+ padding: 20px 30px 20px;
+ transition: .5s;
+}
+
+.footer a {
+ color: var(--color-white-30);
+ text-decoration: none;
+}
+
+.footer a:hover {
+ color: var(--color-green);
+}
+
+
+.headline {
+ margin-bottom: 30px;
+ position: relative;
+ translate: 60px;
+}
+
+.headline .logo img {
+ width: 130px;
+ position: absolute;
+ translate: -135px 10px;
+}
+
+.headline span {
+ color: var(--color-green);
+}
+
+.headline h1 {
+ font-family: 'Righteous', cursive;
+ font-size: 80px;
+ font-weight: 600;
+ line-height: 1;
+ margin-bottom: 20px;
+ text-align: center;
+}
+
+.btn {
+ height: 40px;
+ padding: 0 20px;
+ border: none;
+ outline: none;
+ border-radius: 5px;
+ font-size: 14px;
+ font-weight: 700;
+ color: var(--color-white);
+ background-color: var(--color-green);
+ cursor: pointer;
+ transition: .5s;
+}
+
+.btn:hover {
+ background-color: var(--color-red);
+ scale: 1.1;
+}
+
+
+.movie-list {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.movie-List .movies a .info {}
+
+.movie-List .movies a .info .year {}
+
+.movie-List .movies a .info .type {
+ color: #7ee787;
+ translate: 50px 0px;
+}
+
+.message {
+ margin: 20px;
+ box-sizing: border-box;
+ display: block;
+ border-radius: 50px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ width: 440px;
+ height: 50px;
+ color: var(--color-white-50);
+ font-size: 14px;
+ background-color: var(--color-white-5);
+ transition: 0.5s;
+}
+
+.message:hover {
+ background-color: var(--color-white-20);
+}
+
+.movies {
+ max-width: 1200px;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ gap: 30px;
+ margin-bottom: 30px;
+}
+
+.movies .movie {
+ width: var(--width);
+ height: calc(var(--width) * 1.5);
+ border-radius: 30px;
+ background-color: var(--color-white-20);
+ background-size: cover;
+ overflow: hidden;
+ position: relative;
+ cursor: pointer;
+ transition: 0.5s;
+ overflow: hidden;
+ position: relative;
+}
+
+.movies .movie:hover::after {
+ content: "";
+ position: absolute;
+ border-top: 5px solid var(--color-red);
+ border-bottom: 5px solid var(--color-red);
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+.movies .movie:hover {
+ transform: scale(1.1);
+ border-radius: 30px;
+}
+
+.movies .movie .info {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 50px;
+ padding: 14px;
+ box-sizing: border-box;
+ font-size: 14px;
+ text-align: center;
+ position: absolute;
+ background-color: rgba(0, 0, 0, .5);
+ /* backdrop-filter: blur(10px); */
+ transition: .3s;
+}
+
+.movies .movie .info .year {
+ display: block;
+ position: absolute;
+ top: 15px;
+ left: 30px;
+ color: var(--color-white);
+}
+
+.movies .movie .info .title {
+ padding: 5px;
+ width: var(--width);
+ height: 50px;
+ box-sizing: border-box;
+ background-color: rgba(0, 0, 0, .5);
+ /* text-shadow: 2px 2px 0 var(--color-black); */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ bottom: -250px;
+ font-size: 15px;
+ color: var(--color-white)
+}
+
+.search {
+ display: flex;
+ gap: 15px;
+ margin-bottom: 60px;
+}
+
+.search input {
+ /* flex-grow: 1; */
+ width: 200px;
+ height: 40px;
+ padding: 0 20px;
+ border: 1px solid var(--color-white-20);
+ outline: none;
+ border-radius: 30px;
+ font-size: 14px;
+ color: var(--color-white);
+ background-color: var(--color-white-10);
+ transition: .5s;
+}
+
+.search input:hover {
+ background-color: var(--color-white-20);
+}
+
+.search input:focus {
+ background-color: var(--color-white);
+ color: #0d1117;
+ scale: 1.1;
+}
+
+.search input::placeholder {
+ color: var(--color-white-50);
+}
+
+.search input:focus::placeholder {
+ color: var(--color-universe)
+}
+
+.search .searchYear {
+ width: 80px;
+}
+
+.search .searchYear::placeholder {
+ color: var(--color-white-10);
+}
+
+.search .searchYear:focus::placeholder {
+ color: var(--color-universe)
+}
+
+.search .btn {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 30px;
+ flex-grow: 1;
+ max-width: 30px;
+}
+
+.search .btn {}
+
+.btn-more {
+ font-size: 25px;
+ font-weight: 400;
+ width: 100%;
+ height: 60px;
+ flex-grow: 1;
+ margin: 20px auto;
+ display: block;
+ border-radius: 30px;
+ font-weight: 900;
+}
+
+.btn-more.hide {
+ display: none;
+}
+
+
+.the-loader {
+ width: 30px;
+ height: 30px;
+ margin: 30px auto;
+ border-radius: 50%;
+ border: 5px solid var(--color-green);
+ border-top-color: transparent;
+ animation: spinner 0.5s linear infinite;
+}
+
+@keyframes spinner {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+.the-loader.hide {
+ display: none;
+}
+
+.the-movie {
+ position: relative;
+}
+
+.the-movie .inner {
+ max-width: 900px;
+ background-color: var(--color-white-10);
+ border-radius: 30px;
+ display: flex;
+ justify-content: start;
+ align-items: center;
+ flex-wrap: wrap;
+ margin: 30px;
+ padding-bottom: 40px;
+}
+
+.the-movie .posterbox {
+ margin: 30px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 30px;
+}
+
+.the-movie .posterbox .poster {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ width: 300px;
+ height: 400px;
+ /* min-width: calc(var(--width)*1.5);
+ max-height: calc(var(--width)*2); */
+ border-radius: 30px;
+ background-repeat: no-repeat;
+ background-size: cover;
+}
+
+.the-movie .posterbox .type {
+ display: block;
+ position: absolute;
+ font-family: 'Codystar', cursive;
+ bottom: 110px;
+ left: 230px;
+ font-size: 40px;
+ font-weight: 900;
+ color: var(--color-green);
+}
+
+.the-movie .posterbox .boxoffice {
+ padding: 10px 0 10px 0;
+ width: 300px;
+ text-align: center;
+ border: 3px dotted var(--color-green);
+ font-size: 18px;
+ color: var(--color-green);
+ border-radius: 15px;
+}
+
+.the-movie .posterbox .boxoffice.skeleton {
+ border: none;
+ height: 50px;
+}
+
+.the-movie .posterbox .grade {
+ padding: 10px 0 10px 0;
+ width: 300px;
+ text-align: center;
+ border: 3px dotted var(--color-green);
+ font-size: 18px;
+ color: var(--color-green);
+ border-radius: 15px;
+}
+
+.the-movie .posterbox .grade.skeleton {
+ border: none;
+ height: 50px;
+}
+
+.the-movie .details {
+ margin: 0 0 0px;
+ display: block;
+ max-width: 500px;
+}
+
+.the-movie .posterinner {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.the-movie .btns {
+ display: flex;
+ /* justify-content: start; */
+ align-items: center;
+ gap: 20px;
+ width: 100%;
+ padding-left: 30px;
+}
+
+.the-movie .details .title {
+ font-family: 'Righteous', cursive;
+ display: block;
+ margin: 30px 30px 20px 30px;
+ font-size: 40px;
+ max-width: 500px;
+ height: auto;
+ line-height: 1.4;
+}
+
+.the-movie .details .title.skeleton {
+ margin: 30px 30px 20px 30px;
+ max-width: 500px;
+ height: 100px;
+}
+
+
+.the-movie .details .plot {
+ margin: 30px 30px 20px 30px;
+ display: block;
+}
+
+.the-movie .details .plot span {
+ color: var(--color-green);
+}
+
+.the-movie .details .plot.skeleton {
+ display: block;
+ margin: 30px 30px 20px 30px;
+ width: 440px;
+ height: 100px;
+}
+
+.the-movie .details .sub-content {
+ margin: 30px 30px 20px 30px;
+ display: block;
+ width: auto;
+ background-color: var();
+}
+
+.the-movie .details .sub-content .info {
+ margin-bottom: 30px;
+}
+
+.the-movie .details .sub-content .info.skeleton {
+ margin-bottom: 30px;
+ width: 440px;
+ height: 320px;
+}
+
+.the-movie .btn-favorit {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 500px;
+ background-color: var(--color-purple);
+ font-size: 25px;
+ font-weight: 900;
+ height: 60px;
+}
+
+.the-movie .btn-favorit:hover {
+ background-color: var(--color-red);
+ color: var(--color-white);
+}
+
+.the-movie .btn-back {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 500px;
+ background-color: var(--color-green);
+ font-size: 20px;
+ height: 60px;
+}
+
+
+.the-movie .btn-back:hover {
+ background-color: var(--color-red);
+ color: var(--color-white);
+}
+
+.the-movie .btn-back.skeleton {
+ width: 80px;
+ height: 80px;
+ background-color: var(--color-universe);
+}
+
+.about {
+ margin: 30px;
+ padding: 100px;
+ background-color: var(--color-white-5);
+ border-radius: 30px;
+ transition: .5s;
+}
+
+.about:hover {
+ background-color: var(--color-white-10);
+}
+
+.about .inner {}
+
+.about .photo {
+ width: 230px;
+ height: 230px;
+ margin: 0 auto 20px;
+ border-radius: 200px;
+ background-size: cover;
+}
+
+.about .name {
+ font-size: 40px;
+ font-family: 'Righteous', cursive;
+ margin-bottom: 20px;
+}
+
+.about p {
+ line-height: 1.5;
+ text-align: center;
+ margin-bottom: 4px;
+}
+
+.about a {
+ color: var(--color-green);
+ text-decoration: none;
+}
+
+.about a:hover {
+ text-decoration: underline;
+}
+
+
+
+.photo {
+ display: block;
+ width: 100px;
+ height: 100px;
+}
+
+.skeleton {
+ position: relative;
+ overflow: hidden;
+ border-radius: 10px;
+ background-color: var(--color-universe);
+}
+
+.skeleton::after {
+ content: '';
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-image: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ transform: translateX(-100%);
+ animation: skeleton 1.5s infinite;
+}
+
+@keyframes skeleton {
+ 0% {
+ transform: translateX(-100%);
+ }
+
+ 100% {
+ transform: translateX(100%);
+ }
+}
+
+
+.skeleton {
+ position: relative;
+ overflow: hidden;
+ border-radius: 10px;
+ background-color: var(--color-universe);
+}
+
+.skeleton::after {
+ content: '';
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-image: linear-gradient(90deg,
+ rgba(126, 231, 135, 0),
+ rgba(126, 231, 135, .1),
+ rgba(126, 231, 135, 0));
+ transform: translateX(-100%);
+ animation: skeleton 1.5s infinite;
+}
+
+@keyframes skeleton {
+ 0% {
+ transform: translateX(-100%);
+ }
+
+ 100% {
+ transform: translateX(100%);
+ }
+}
+
+.not-found {
+ margin: 30px;
+ font-size: 50px;
+ font-weight: 700;
+ color: var(--color-white-20);
+ text-align: center;
+}
+
+router-view {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+
+inner {
+ max-width: 1200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap;
+ margin: 30px 30px 30px 30px;
+ /* padding: 40px; */
+ gap: 20px;
+ background-color: var(--color-white-5);
+ border-radius: 30px;
+ padding: 30px 30px 30px 30px;
+}
+
+inner .movie {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ overflow: hidden;
+ margin: 10px;
+ position: relative;
+}
+
+inner .movie .poster a img {
+ width: 200px;
+ height: 300px;
+ border-radius: 30px;
+ background-size: cover;
+}
+
+inner .movie .title {}
+
+inner .movie .delete {
+ border: none;
+ outline: none;
+ font-size: 20px;
+ font-weight: 900;
+ color: var(--color-green);
+ border: 2px dotted var(--color-green);
+ font-family: 'Codystar', cursive;
+ background-color: transparent;
+ border-radius: 30px;
+ width: 200px;
+ height: 40px;
+ transition: 0.5s;
+}
+
+inner .movie .delete:hover {
+ color: var(--color-red);
+ border: 2px dotted var(--color-red);
+}
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 00000000..bb8f1e02
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,7 @@
+import App from './App.js'
+import router from './routes/index.js'
+
+const root = document.querySelector('#root')
+root.append(new App().el)
+
+router()
diff --git a/src/routes/About.js b/src/routes/About.js
new file mode 100644
index 00000000..40aa5181
--- /dev/null
+++ b/src/routes/About.js
@@ -0,0 +1,19 @@
+import Component from '../core/Component'
+import aboutStore from '../store/about'
+
+export default class About extends Component {
+ render() {
+ const { photo, name, email, github, blog } = aboutStore.state
+ this.el.classList.add('about')
+ this.el.innerHTML = /*html*/ `
+
+ `
+ }
+}
diff --git a/src/routes/Home.js b/src/routes/Home.js
new file mode 100644
index 00000000..6f9849a4
--- /dev/null
+++ b/src/routes/Home.js
@@ -0,0 +1,17 @@
+import Component from '../core/Component.js'
+import Headline from '../components/Headline.js'
+import Search from '../components/Search.js'
+import MovieList from '../components/MovieList.js'
+import MovieListMore from '../components/MovieListMore.js'
+
+export default class Home extends Component {
+ render() {
+ const headline = new Headline().el
+ const search = new Search().el
+ const movieList = new MovieList().el
+ const movieListMore = new MovieListMore().el
+ this.el.classList.add('container')
+
+ this.el.append(headline, search, movieList, movieListMore)
+ }
+}
diff --git a/src/routes/Movie.js b/src/routes/Movie.js
new file mode 100644
index 00000000..1c08d346
--- /dev/null
+++ b/src/routes/Movie.js
@@ -0,0 +1,220 @@
+import Component from '../core/Component'
+import movieStore, { getMovieDetails } from '../store/movie'
+
+export default class Movie extends Component {
+ constructor() {
+ super({
+ state: {
+ FM: []
+ }
+ })
+ }
+
+ async render() {
+ this.el.classList.add('container', 'the-movie')
+ this.el.innerHTML = /*html*/ `
+
+ `
+
+ await getMovieDetails(history.state.id)
+ const { movie } = movieStore.state
+ const bigPoster = movie.Poster.replace('SX300', 'SX700') // ν¬μ€ν° μ΄λ―Έμ§λ₯Ό λ ν¬κ² 보μ¬μ£ΌκΈ° μν΄ SX300μ SX700μΌλ‘ λ³κ²½
+
+ let averageRate
+ if (movie.Ratings.length === 3) {
+ if (
+ movie.Ratings[1].Source === 'Rotten Tomatoes' &&
+ movie.Ratings[2].Source === 'Metacritic'
+ ) {
+ averageRate = (
+ (Number(movie.imdbRating) +
+ Number(movie.Ratings[1].Value.split('%')[0]) / 10 +
+ Number(movie.Ratings[2].Value.split('/')[0]) / 10) /
+ 3
+ ).toFixed(1)
+ } else if (
+ movie.Ratings[1].Source === 'Metacritic' &&
+ movie.Ratings[2].Source === 'Rotten Tomatoes'
+ ) {
+ averageRate = (
+ (Number(movie.imdbRating) +
+ Number(movie.Ratings[1].Value.split('/')[0]) / 10 +
+ Number(movie.Ratings[2].Value.split('%')[0]) / 10) /
+ 3
+ ).toFixed(1)
+ }
+ } else if (movie.Ratings.length === 2) {
+ if (
+ movie.Ratings[0].Source === 'Internet Movie Database' &&
+ movie.Ratings[1].Source === 'Rotten Tomatoes'
+ ) {
+ averageRate = (
+ (Number(movie.imdbRating) +
+ Number(movie.Ratings[1].Value.split('%')[0]) / 10) /
+ 2
+ ).toFixed(1)
+ } else if (
+ movie.Ratings[0].Source === 'Internet Movie Database' &&
+ movie.Ratings[1].Source === 'Metacritic'
+ ) {
+ averageRate = (
+ (Number(movie.imdbRating) +
+ Number(movie.Ratings[1].Value.split('/')[0]) / 10) /
+ 2
+ ).toFixed(1)
+ }
+ } else if (movie.Ratings.length === 1) {
+ if (movie.Ratings[0].Source === 'Metacritic') {
+ averageRate = Number(movie.Ratings[0].Value.split('/')[0]) / 10
+ } else if (movie.Ratings[0].Source === 'Rotten Tomatoes') {
+ averageRate = Number(movie.Ratings[0].Value.split('%')[0]) / 10
+ } else if (movie.Ratings[0].Source === 'Internet Movie Database') {
+ averageRate = Number(movie.Ratings[0].Value.split('/')[0])
+ }
+ } else {
+ averageRate = 'N/A'
+ }
+
+ this.el.innerHTML = /*html*/ `
+
+
+
+
+
+
BoxOffice : ${movie.BoxOffice}
+
+
+
Average Ratings : ${averageRate}
+
+
+
+
+
${movie.Title}
+
+
+
SUMMARY
+ ${movie.Plot}
+
+
+
Release Date : ${movie.Released}
+
Runtime : ${movie.Runtime}
+
Country : ${movie.Country}
+
+
Awards : ${movie.Awards}
+ Age Rating : ${movie.Rated}
+ Genre : ${movie.Genre}
+ Director : ${movie.Director}
+ Writer : ${movie.Writer}
+ Actors : ${movie.Actors}
+
+
+
+
+
+
+
+
+
+
+ `
+ const btnFavorit = this.el.querySelector('.btn-favorit')
+ const addMovie = this.el.querySelector('#addmovie')
+ let isFavorit = false
+
+ btnFavorit.addEventListener('click', () => {
+ const { movie } = movieStore.state
+ let storedMovies = JSON.parse(localStorage.getItem('favoritMovies')) || []
+
+ if (!isFavorit) {
+ // μνλ₯Ό μΆκ°νλ κ²½μ°
+ storedMovies.push(movie)
+ localStorage.setItem('favoritMovies', JSON.stringify(storedMovies))
+ addMovie.textContent = 'heart_check'
+ isFavorit = true
+ } else {
+ // μνλ₯Ό μμ νλ κ²½μ°
+ const updatedStoredMovies = storedMovies.filter(
+ storedMovie => storedMovie.title !== movie.title
+ )
+ localStorage.setItem(
+ 'favoritMovies',
+ JSON.stringify(updatedStoredMovies)
+ )
+ addMovie.textContent = 'favorite'
+ isFavorit = false
+ }
+ })
+
+ // ratings μμκ° μ‘΄μ¬νλ κ²½μ°μλ§ νκ° μ μ HTML μΆκ°
+ const ratings = this.el.querySelector('.grade') // ratings μμ μ ν
+ if (ratings) {
+ // ratings μμκ° μ‘΄μ¬νλ κ²½μ°μλ§ νκ° μ μ HTML μΆκ°
+ movie.Ratings.forEach(rating => {
+ if (rating?.Value) {
+ if (rating.Source === 'Rotten Tomatoes') {
+ ratings.innerHTML += `- Rotten Tomatoes : ${
+ Number(rating.Value.split('%')[0]) / 10
+ } / 10
`
+ } else if (rating.Source === 'Metacritic') {
+ ratings.innerHTML += `- Metacritic: ${
+ Number(rating.Value.split('/')[0]) / 10
+ } / 10
`
+ } else {
+ ratings.innerHTML += `- IMDB: ${Number(
+ rating.Value.split('/')[0]
+ )} / 10
`
+ }
+ }
+ })
+ }
+ console.log(movie.averageRate)
+ const boxOfficeEl = this.el.querySelector('.boxoffice')
+ if (
+ movie.BoxOffice === undefined ||
+ movie.BoxOffice === null ||
+ movie.BoxOffice === 'N/A'
+ ) {
+ boxOfficeEl.style.display = 'none'
+ }
+ const gradeEl = this.el.querySelector('.grade')
+ if (averageRate === 'N/A') {
+ gradeEl.style.display = 'none'
+ }
+ const noPoster = this.el.querySelector('.poster')
+ if (noPoster.style.backgroundImage === 'url("N/A")') {
+ noPoster.style.backgroundImage =
+ 'url("https://static-00.iconduck.com/assets.00/flying-saucer-emoji-2048x1837-wqaxl6sz.png")'
+ } else {
+ noPoster.style.backgroundImage = `url(${bigPoster})`
+ }
+ }
+}
diff --git a/src/routes/MyList.js b/src/routes/MyList.js
new file mode 100644
index 00000000..f7c3c69c
--- /dev/null
+++ b/src/routes/MyList.js
@@ -0,0 +1,44 @@
+import Component from '../core/Component'
+import movieStore from '../store/movie'
+import MovieItem from '../components/MovieItem'
+import { searchMovies } from '../store/movie'
+
+export default class MovieList extends Component {
+ constructor() {
+ super({
+ tagName: 'inner'
+ })
+ }
+ render() {
+ // λ‘컬 μ€ν 리μ§μμ favoritMovies ν€ λ°μ΄ν° κ°μ Έμ€κΈ°
+ const favoriteMovies = JSON.parse(localStorage.getItem('favoritMovies'))
+
+ // favoriteMovies κ°μ²΄μ μμ±(νλ‘νΌν°) μ€ 'Title'κ³Ό 'Poster' μΆλ ₯νκΈ°
+ favoriteMovies.forEach(movie => {
+ const posterSrc = movie.Poster
+ ? movie.Poster
+ : 'https://static-00.iconduck.com/assets.00/flying-saucer-emoji-2048x1837-wqaxl6sz.png'
+ const movieElement = document.createElement('section')
+ movieElement.innerHTML = /*html*/ `
+
+
+
+
+
+
+ `
+
+ movieElement.querySelector('.delete').addEventListener('click', event => {
+ const title = event.target.dataset.title
+ const index = favoriteMovies.findIndex(movie => movie.Title === title)
+ if (index > -1) {
+ favoriteMovies.splice(index, 1)
+ localStorage.setItem('favoritMovies', JSON.stringify(favoriteMovies))
+ event.target.closest('.movie').remove()
+ }
+ })
+ movieElement.classList.add('movie')
+ this.el.appendChild(movieElement)
+ })
+ }
+}
diff --git a/src/routes/NotFound.js b/src/routes/NotFound.js
new file mode 100644
index 00000000..be0e0a98
--- /dev/null
+++ b/src/routes/NotFound.js
@@ -0,0 +1,13 @@
+import Component from '../core/Component'
+
+export default class NotFound extends Component {
+ render() {
+ this.el.classList.add('not-found')
+ this.el.innerHTML = /*html*/ `
+
+
SORRY..
+
Page Not Found.
+
+ `
+ }
+}
diff --git a/src/routes/index.js b/src/routes/index.js
new file mode 100644
index 00000000..b0c0987f
--- /dev/null
+++ b/src/routes/index.js
@@ -0,0 +1,29 @@
+import { createRouter } from '../core/donghae.js'
+import Home from '../routes//Home.js'
+import Movie from '../routes/Movie.js'
+import About from '../routes/About.js'
+import NotFound from '../routes/NotFound.js'
+import MyList from '../routes/MyList.js'
+
+export default createRouter([
+ {
+ path: '#/',
+ component: Home
+ },
+ {
+ path: '#/movie',
+ component: Movie
+ },
+ {
+ path: '#/mymovie',
+ component: MyList
+ },
+ {
+ path: '#/about',
+ component: About
+ },
+ {
+ path: '.*',
+ component: NotFound
+ }
+])
diff --git a/src/stars.js b/src/stars.js
new file mode 100644
index 00000000..f6781c50
--- /dev/null
+++ b/src/stars.js
@@ -0,0 +1,74 @@
+const sin = Math.sin
+const cos = Math.cos
+const PI = Math.PI
+const fov = 150
+
+class Dot {
+ constructor(x, y, z) {
+ this.x = x
+ this.y = y
+ this.z = z
+ }
+}
+
+let canvas
+let context
+let tempx, tempy, tempz
+let dots = []
+let dotsLength = innerWidth + innerHeight
+
+function setSize() {
+ canvas.width = innerWidth
+ canvas.height = innerHeight
+ initDots()
+ context.fillStyle = '#ffffff'
+ if (innerWidth < 800) {
+ context.globalAlpha = 0.3
+ } else {
+ context.globalAlpha = 0.8
+ }
+}
+
+function initDots() {
+ dots = []
+ dotsLength = (innerWidth + innerHeight) / 20
+ let x, y, z
+ for (let i = 0; i < dotsLength; i++) {
+ x = Math.random() * innerWidth - innerWidth / 2
+ y = Math.random() * innerHeight - innerHeight / 2
+ z = Math.random() * innerWidth - innerWidth / 2
+ dots.push(new Dot(x, y, z))
+ }
+}
+
+function drawDots(dot) {
+ let scale, x2d, y2d
+ scale = fov / (fov + dot.z)
+ x2d = dot.x * scale + innerWidth / 2
+ y2d = dot.y * scale + innerHeight / 2
+ context.fillRect(x2d, y2d, scale * 4, scale * 3)
+}
+
+function render() {
+ context.clearRect(0, 0, canvas.width, canvas.height)
+ let dot
+ for (let i = 0; i < dots.length; i++) {
+ dot = dots[i]
+ dot.z -= 1
+ if (dot.z < -fov) {
+ dot.z += (innerWidth + innerHeight) / 2
+ }
+ drawDots(dot)
+ }
+ requestAnimationFrame(render)
+}
+
+function init() {
+ canvas = document.querySelector('.canvas')
+ context = canvas.getContext('2d')
+ setSize()
+ render()
+}
+
+addEventListener('resize', setSize)
+init()
diff --git a/src/store/about.js b/src/store/about.js
new file mode 100644
index 00000000..a135b1ec
--- /dev/null
+++ b/src/store/about.js
@@ -0,0 +1,11 @@
+import Store from '../core/Store.js'
+
+export default new Store({
+ photo:
+ 'https://tistory1.daumcdn.net/tistory/6081640/attach/3f3363ce559e4391a84004653065d4a4',
+ name: 'THEEASTSEA
DONGHAE',
+ email: 'todonghae@gmail.com',
+ blog: 'https://theeastsea.xyz/',
+ github: 'https://github.com/theeastsea',
+ repository: 'https://github.com/theeastsea/vanillajs-movie-matrix'
+})
diff --git a/src/store/movie.js b/src/store/movie.js
new file mode 100644
index 00000000..f6b016ce
--- /dev/null
+++ b/src/store/movie.js
@@ -0,0 +1,62 @@
+import Store from '../core/Store.js'
+
+const store = new Store({
+ searchText: '',
+ searchYear: '',
+ page: 1,
+ pageMax: 1,
+ movies: [],
+ movie: {},
+ loading: false,
+ message: 'SEARCH FOR THE MOVIE TITLE!',
+ FM: []
+})
+
+export default store
+
+export const searchMovies = async page => {
+ store.state.loading = true
+ store.state.page = page
+ if (page === 1) {
+ // μλ‘μ΄ κ²μ, κΈ°μ‘΄ κ²μ κ²°κ³Ό μ΄κΈ°ν
+ store.state.movies = []
+ store.state.message = ''
+ }
+ const defaultPosterUrl =
+ 'https://static-00.iconduck.com/assets.00/flying-saucer-emoji-2048x1837-wqaxl6sz.png'
+
+ const res = await fetch(
+ `https://www.omdbapi.com/?apikey=7035c60c&s=${store.state.searchText}&y=${store.state.searchYear}&page=${page}`
+ )
+ try {
+ const { Search, totalResults, Response, Error } = await res.json()
+ if (Response === 'True') {
+ store.state.movies = [
+ ...store.state.movies.filter(movie => movie != null), // nullμ΄λ undefinedλ₯Ό κ±Έλ¬λ
+ ...(Search || []).map(movie => ({
+ ...movie,
+ Poster: movie.Poster !== 'N/A' ? movie.Poster : defaultPosterUrl
+ // ν¬μ€ν° μ΄λ―Έμ§κ° μμ κ²½μ° κΈ°λ³Έ μ΄λ―Έμ§λ‘ λ체
+ }))
+ ]
+ store.state.pageMax = Math.ceil(Number(totalResults) / 10)
+ } else {
+ store.state.message = Error
+ }
+ } catch {
+ console.log('searchMovies error', error)
+ } finally {
+ store.state.loading = false
+ }
+}
+
+export const getMovieDetails = async id => {
+ try {
+ const res = await fetch(
+ `https://www.omdbapi.com/?apikey=7035c60c&i=${id}&plot=short`
+ )
+ store.state.movie = await res.json()
+ } catch (error) {
+ console.log('getMovieDetails error:', error)
+ }
+}
diff --git a/style.css b/style.css
new file mode 100644
index 00000000..abf9d15f
--- /dev/null
+++ b/style.css
@@ -0,0 +1,97 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #f7df1eaa);
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}