diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20fccdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local diff --git a/components/ContentPage.tsx b/components/ContentPage.tsx new file mode 100644 index 0000000..757fac0 --- /dev/null +++ b/components/ContentPage.tsx @@ -0,0 +1,52 @@ +// import React, { useRef, useState, useEffect } from "react"; +// import { Octokit } from "@octokit/rest"; + +// const ContentPage: React.FunctionComponent = (props) => { +// const octokit = useRef( +// new Octokit({ +// auth: "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", +// }).rest.markdown +// ); +// const [text, setText] = useState(""); +// const [htmlDoc, setHtmlDoc] = useState(null); + +// useEffect(() => { +// if (htmlDoc !== null) { +// return; +// } + +// fetch( +// "https://raw.githubusercontent.com/prakharporwal/bank-server/master/README.md" +// ) +// .then((res) => res.text()) +// .then((data) => { +// console.log(data); +// setText(data); + +// octokit.current +// .render({ text: data }) +// .then((val) => val.data) +// .then((htmlDoc) => { +// setHtmlDoc(htmlDoc); +// console.log(htmlDoc); +// }); +// }) +// .catch((err) => { +// console.log(err); +// }); +// }, []); + +// return ( +//
+// ); +// }; +// export default ContentPage; diff --git a/components/CourseItem.tsx b/components/CourseItem.tsx new file mode 100644 index 0000000..5969c87 --- /dev/null +++ b/components/CourseItem.tsx @@ -0,0 +1,37 @@ +import Link from 'next/link'; + +const CourseItem: React.FunctionComponent = (props) => { + return ( + +
+

{props.course_name}

+
+ {props.tags.split(",").map((tag) => { + return {tag}; + })} +
+
+ + ); +}; + +export default CourseItem; diff --git a/components/Layout.tsx b/components/Layout.tsx new file mode 100644 index 0000000..614b779 --- /dev/null +++ b/components/Layout.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import Link from "next/link"; + +type Metadata = { + data: { + title : string + date : string + } + dangerousInnerHTML: string +} + +const Layout: React.FunctionComponent = (props) => { + const {title, date} = props.data + + return ( +
+
+

{title}

+

{date}

+
+ + Go back +
+
+ ); +}; + +export default Layout; diff --git a/markdown/class_based_components.md b/markdown/class_based_components.md new file mode 100644 index 0000000..2eaeee3 --- /dev/null +++ b/markdown/class_based_components.md @@ -0,0 +1,289 @@ +### Introduction + +All the components by far, have been functional in style and syntax. This is common now, but you will see a different `class` based syntax too. In this lesson, we explore how a class-based component is written and how concepts like props and state are used in one. + +### Lesson Overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Learn the structure of a class component and how they are written. +- How to use props and state in class components. +- Highlight the uses of `this` in class components. + +### The What's And Whys + +In your previous lessons, you have already been introduced to functional components, and the basic patterns in which components get written now a days. However, React components did not look this way when React was introduced. + +If you look into any older React codebase, you'll notice a lot of classes. These are known as class-based components. Prior to February 2019, functional components were also called state-less, as there was no way to manage state in them. This was changed when hooks were introduced, leading to less verbose and 'neater' components. + +In your career, chances are, you will be dealing with legacy code, so there will be days where you would be dealing with class components. Let's peek into the intricacies of a class-based component, and how they work. + +### Building A Class Component + +As we already know about functional components, let us build a class-based component from a functional one. Usually, you will want to divide the contents of a component, like the one we use, into smaller, re-usable components, but for the purposes of this exercise, we stick to one component. Below, we have a sample functional component: + +~~~jsx +import React, { useState } from "react"; + +const FunctionalInput = ({ name }) => { + const [todos, setTodos] = useState(["Just some demo tasks", "As an example"]); + const [inputVal, setInputVal] = useState(""); + + const handleInputChange = (e) => { + setInputVal(e.target.value); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + setTodos((todo) => [...todo, inputVal]); + setInputVal(""); + }; + + return ( +
+

{name}

+
+ + + +
+

All the tasks!

+
    + {todos.map((todo) => ( +
  • {todo}
  • + ))} +
+
+ ); +}; + +export default FunctionalInput; +~~~ + +That was a solid chunk of code. Take a while, sip some water and read it a couple of times. + +#### The Start Of A Class-Based Component + +Now, let's try to recreate it as a class-based component. The first thing it should have is, _drumroll_, a class! But it cannot be just another class, it will need to have certain properties that qualifies it as a React component. React provides us with all those properties on a class called `Component`, and we can write our components by simply extending the given class, as shown below: + +~~~jsx +import React, { Component } from "react"; + +class ClassInput extends Component { + // Some code goes here +} + +/* + This can also be written as: + + import React from 'react'; + class ClassInput extends React.Component {} + export default ClassInput; + + instead of destructuring the `Component` during import +*/ + +export default ClassInput; +~~~ + +#### The Use Of A Constructor And Props + +A class is generally incomplete without a constructor, so let's add one. + +The props, that get passed into this component, gets passed into the class's `constructor`. This, along with the `super` method, allows you to use the props in context to `this`, which, in _this_ case, refers to the component. If you’re really curious about what `super` actually does, [check out the docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super). + +If your component doesn't have any props, it is fine to leave the `constructor` and the `super` with no arguments. + +~~~jsx +import React, { Component } from "react"; + +class ClassInput extends Component { + constructor(props) { + super(props); + } + // Some more code goes here +} + +export default ClassInput; +~~~ + +#### Way To Render JSX + +Now that the props can be accessed inside of the class component, the next issue is to find a way to render the JSX. + +Well, you can do that by returning your JSX from a `render` method! You can use the props that you declared in the constructor too! + +~~~jsx +import React, { Component } from "react"; + +class ClassInput extends Component { + constructor(props) { + super(props); + } + // Some more code goes here + + render() { + return ( +
+

{this.props.name}

+ {/* The input field to enter To-Do's */} +
+ + + +
+

All the tasks!

+ {/* The list of all the To-Do's, displayed */} +
    +
    + ); + } +} + +export default ClassInput; +~~~ + +Notice how the props get provided by `this`, unlike the functional component that we saw, initially? + +#### How To Use State And Manage Context + +Next comes the state. In a class-based component, the state gets initialized as a part of the constructor. + +~~~jsx +import React, { Component } from "react"; + +class ClassInput extends Component { + constructor(props) { + super(props); + + this.state = { + todos: [], + inputVal: "", + }; + } + // Some more code goes here + + render() { + return ( +
    +

    {this.props.name}

    +
    + + + +
    +

    All the tasks!

    +
      +
      + ); + } +} + +export default ClassInput; +~~~ + +The pre-defined `setState` method can be used to set it again! Remember, state must not be mutated, so a new state must be set, every time. + +Now, it is time to finish it off by adding all the functionality! It is nearly the same, except for a single difference. Whenever a method is declared, you must `bind` the `this` of the method to that of the class in order to work with it, as by default, the methods in a class are not bound to it. Usually, you do this inside the constructor and not at runtime [in the render method]. + +~~~jsx +import React, { Component } from "react"; + +class ClassInput extends Component { + constructor(props) { + super(props); + + this.state = { + todos: [], + inputVal: "", + }; + + this.handleInputChange = this.handleInputChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleInputChange(e) { + this.setState((state) => ({ + ...state, + inputVal: e.target.value, + })); + } + + handleSubmit(e) { + e.preventDefault(); + this.setState((state) => ({ + todos: state.todos.concat(state.inputVal), + inputVal: "", + })); + } + + render() { + return ( +
      +

      {this.props.name}

      +
      + + + +
      +

      All the tasks!

      +
        + {this.state.todos.map((todo) => ( +
      • {todo}
      • + ))} +
      +
      + ); + } +} + +export default ClassInput; +~~~ + +And there we go, we have successfully made our first class-based component, as easy as that! + +### Assignment + +
      + +For the purposes of this assignment, we take the simple class-based component that we built. There is an interactive example provided at the end, where you can work on. + +1. Implement a delete button for each task. The delete button should remove that specific task from the state array, thus deleting the task itself! Styling isn't a priority at this moment, but the button tag should be styled by default. + +2. Implement a new class component, `Count` that displays the count of the number of todos, at any given time. + +3. Implement an edit button for each task. It should replace the todo with an input field, and change the button itself to 'Resubmit', so the edits can be saved. This is a comparatively harder task, kudos for when you finish it! + + + +
      + +### Knowledge Check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- How do props get used in a class-based component? +- How does JSX get displayed? +- How do we deal with state in a class-based component? +- How do you restore the context of `this` in a method? + +### Additional Resources + +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +- In general, the React documentation on [thinking in react](https://reactjs.org/docs/thinking-in-react.html) is a really good place to look into, if you want more practice with class-based components, from scratch. diff --git a/markdown/component_lifecycle_methods.md b/markdown/component_lifecycle_methods.md new file mode 100644 index 0000000..a634d01 --- /dev/null +++ b/markdown/component_lifecycle_methods.md @@ -0,0 +1,76 @@ +### Introduction + +In the previous section, you've learned about a component's lifecycle. In functional components, we've primarily used the `useEffect` hook to perform tasks throughout the lifecycle, but in class components we have to use specific lifecycle methods instead. + +We'll start with a short recap of what a components lifecycle is. There are three stages to a component's life: mounting, updating, and unmounting. Each of these have a method assigned to them within class components, which is what we are going to cover in this lesson. + +### Lesson Overview + +This section contains a general overview of topics that you will learn in this lesson. + +* How to use lifecycle methods in a class component + +### render() + +The render function is the most used lifecycle method, and one that you've come across in the last class components lesson. It is the only required lifecycle method in a class component. It runs on mount and update of a component. Render should be pure, meaning it doesn't modify component state, returns the same thing each time it's called (given the same inputs), and doesn't directly interact with the browser. + +### componentDidMount() + +This method is run after the component is mounted (inserted in the DOM tree). You should make any calls to fetch data that is needed for the component here. It is also a good place to do anything that is reliant on the component, such as drawing on a canvas element that you just rendered. + +### componentDidUpdate() + +This method is run after a component re-renders. Because of this, you have to be careful about what you update in this method, as if you're updating state indiscriminately, a re-render is caused, and you'll end up in an endless loop. You can avoid this issue by using conditional statements about the equality of previous and current props when updating state. + +In this method you should be updating anything that needs to be changed in response to either the DOM changing, or any states that you might want to act on upon change. For example, you'd want to refetch user data if the user changes. + +### componentWillUnmount() + +This is the last lifecycle method, which is called before a component is unmounted and destroyed. In this method you should be performing cleanup actions, so that would be cancelling network requests, clearing timers, etc. + +### How useEffect() combines the Lifecycle Methods + +Now that we've learnt about class lifecycle methods, it's useful to understand that the `useEffect` hook used in functional components is essentially a combination of `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`. Which method/methods it relates to varies based on it's dependency array, and if it returns anything. + +* An empty dependency array would be equivalent to `componentDidMount`. +* A dependency array with a value/values in it would be a combination of `componentDidMount` and `componentDidUpdate`, but only updating when dependencies change. +* No dependency array would be equivalent to `componentDidMount` and `componentDidUpdate` combined. +* A return function inside of a `useEffect()` hook would be equivalent to `componentWillUnmount`. + +For example: + +~~~js + useEffect(() => { + placeholderFunction() + return () => cleanupFunction() + }, []) +~~~ + +In this snippet, the useEffect contains the functionality of `componentDidMount`, and `componentWillUnmount` via the return function. This example doesn't have the `componentDidUpdate` functionality because of an empty dependency array. + +### Assignment + +
      + +1. Check out this [lifecycle diagram](https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/) to see a good visual representation of a components lifecycle methods. + +2. Read this [component lifecycle documentation](https://reactjs.org/docs/react-component.html#the-component-lifecycle) by the people who made react! + * You only have to worry about the methods in bold, but you can read up on the others if you're curious - they're uncommon and you likely won't need them in + most of your apps. +
      + +### Knowledge Check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +* What is the only required lifecycle method? +* What lifecycle method should you use for initial data fetching? +* When you want to act upon change of the DOM, or of state, what lifecycle method would you use? +* When performing cleanup actions, what lifecycle method should be used? +* How does the useEffect hook combine some of the lifecycle methods? + +### Additional Resources + +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +* It looks like this lesson doesn't have any additional resources yet. Help us expand this section by contributing to our curriculum. diff --git a/markdown/databases.md b/markdown/databases.md new file mode 100644 index 0000000..d178ec4 --- /dev/null +++ b/markdown/databases.md @@ -0,0 +1,48 @@ +### Introduction + +We've talked about the client-side and the server-side but how do we keep ahold of all our user's data? Who remembers that your login password is `CatLover1985` so you can sign into the website? The bottom layer of any web application is the database and it handles all the remembering for you (we'll cover caching much later). It can be relatively simple, like an excel spreadsheet, or incredibly complex and split into many giant pieces like Facebook's. + +Databases are kind of hidden in the back of the web application so people treat them with a sort of suspicion and awe. **That's nonsense and you should get over it** -- your database and you are going to become very good friends (or at least frenemies). By the end of this curriculum, you're going to understand what's going on with your databases and be able to interact with them like a pro (and probably better than some people you'll work with). This lesson is a teaser for that. + +Compared to a normal programming language like you've already learned, SQL (Structured Query Language), which is used to query databases, is a very simple syntax... there are only a small handful of verbs to learn. What trips people up is that you need to be able to visualize in your head what it's going to be doing. We'll spend a fair bit of time on SQL and databases because they're so fundamental, but for now we'll just cover enough to get you familiar with what's going on in there. + +### Learning Overview +This section contains a general overview of topics that you will learn in this lesson. + +- What a database is. +- What relational databases are. +- In what way relational databases are different from XML. +- What SQL is. +- What SQL is used for. +- How to get all the records from a table in SQL. +- How to insert a record in SQL. + +### Assignment + +
      + + 1. Check out this [introduction](https://launchschool.com/books/sql/read/introduction) of how SQL can be used to organise and manage an overwhelming amount of data. + - You do not need to go any further than the first page on introductions. + + 2. Watch this [short video introduction to relational databases](http://www.youtube.com/watch?v=z2kbsG8zsLM) to get a feel for why this stuff is useful and some more exposure to the terminology we'll use. + 3. Go through this [Khan Academy tutorial](https://www.khanacademy.org/computing/hour-of-code/hour-of-sql/v/welcome-to-sql), to get a feel for actually creating and manipulating databases. + +
      + +### Knowledge Check +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is a database?](https://launchschool.com/books/sql/read/introduction#structureddata) +- [What are relational databases?](https://launchschool.com/books/sql/read/introduction#rdbms) +- [What is a Primary Key?](https://youtu.be/z2kbsG8zsLM?t=200) +- [What is SQL?](https://launchschool.com/books/sql/read/introduction#sql) +- [How do you get all the records from a table in SQL?](https://www.khanacademy.org/computing/hour-of-code/hour-of-code-lessons/hour-of-sql/pt/querying-the-table) +- [How do you insert a record in SQL?](https://www.khanacademy.org/computing/hour-of-code/hour-of-code-lessons/hour-of-sql/pt/creating-a-table-and-inserting-data) + +### Additional Resources +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +- [What is a Relational Database?](http://computer.howstuffworks.com/question599.htm) from HowStuffWorks.com +- A brief [Simple Wiki article describing relational databases](http://simple.wikipedia.org/wiki/Relational_database) +- Hunter Ducharme created [an e-book](https://hunter-ducharme.gitbook.io/sql-basics/) which is a great documentation on how to do all the basics in SQL. +- Relational databases aren't the only way to store data. Non-relational (aka NoSQL) databases have emerged over the last couple decades. Check out [this article](https://circleci.com/blog/SQL-vs-NoSQL-databases/) to learn the difference between SQL and NoSQL. diff --git a/markdown/databases_and_sql.md b/markdown/databases_and_sql.md new file mode 100644 index 0000000..a258356 --- /dev/null +++ b/markdown/databases_and_sql.md @@ -0,0 +1,161 @@ +### Introduction + +Data is the core of any good web app and a good working knowledge of SQL will take you a long way. That knowledge lets you not just understand what's going on behind the scenes with your ORM tool (e.g. Active Record) but also to feel comfortable asking more complicated questions of your data. And that's really what SQL is all about -- asking questions of your database and occasionally also adding or changing things in it. Querying can be incredibly useful for you. + +In more straightforward cases, you might want to display all users who signed up in December via the promotion code "FREESTUFF". You might want to display all comments created by the current user and sorted by topic and creation date. In more complex cases, you may want to show a list of all the orders shipped to states with more than 1,000 users by quantity and total order value. Or, for internal reasons, you might ask marketing analysis questions like which promotion channels produce users who meet your specified engagement criteria of reading 5 articles per work week. + +All of those examples involve you engaging with your database. Luckily, many databases (the ones we'll be focusing on) speak SQL and luckily SQL really only has a dozen or so words that you'll consistently use out of a few dozen total. So it's not a large language, it's much more about the concepts behind it. + +You will start with the questions like the ones above and then have to figure out how to ask them properly of your database, which likely has a bunch of different tables in it. Everyone probably visualizes it a bit differently, but finding a way to visualize what's going on when you do SQL queries is pretty important. We actually think of Excel tables moving in our head and combining with each other and reshuffling as necessary. To each their own. + +We'll move beyond just the simple `SELECT "users".* FROM "users" LIMIT 1` queries and into more dynamic topics like joining tables together, performing calculations on the results, and grouping results together in new ways. + +All this stuff is being used by Rails behind the scenes so understanding it will make you much better at writing queries in Rails. This is why we're going over databases before learning Rails. + +#### A Note on Resources + +SQL is one of those topics that's been stored away in dusty old technical manuals and 90's style websites. Even the best books out there can make it seem oddly complicated because they tend to write for the database engineer who actually does need to know all the nitty gritty details. + +Though the prevalence of web applications these days has grown the demand among new users to focus on understanding the *concepts* of SQL, the learning tools haven't really caught up. We'll do our best to impart those concepts using the tools available. + +### Lesson Overview +This section contains a general overview of topics that you will learn in this lesson. + +- What a Primary Key is. +- What Foreign Keys are. +- What a Schema is. +- How to use various SQL statements like `SELECT`, `CREATE TABLE`, `UPDATE`, `DELETE` and more. +- How to use various SQL clauses like `WHERE`, `LIKE`, `DISTINCT` and more. +- How to use various SQL functions like `AVG`, `COUNT`, `SUM` and more. +- What Indexes are good for. +- What the difference between `WHERE` and `HAVING` is. + +### The World's Fastest Semi-Complete Explanation of SQL + +This is a very brief soup-to-nuts explanation of SQL. It won't do a particularly good job teaching you specific new tactics but should present a general overview to have going into the reading assignment. Here we go... + +SQL is the language used to talk to many relational databases. These databases use lots of tables to store different types of data (e.g. "users" and "posts" tables). Tables are long lists like spreadsheets where each row is a different record (or object, e.g. a single user) and each column is one of that record's attributes (like name, email, etc). The one column that all tables include is an "ID" column, which gives the unique row numbers, and is called the record's "primary key". + +You can "link" tables together by making one of the columns in one table point to the ID of another table, for instance a row in the "posts" table might include the author's ID under the column called "user_id". Because the "posts" table has the ID of another table in it, that column is called a "foreign key". + +#### Setting Stuff Up + +SQL lets you do everything. The first category of commands are for setting up the database (`CREATE DATABASE`), setting up an individual table (`CREATE TABLE`), and similar commands for altering or destroying them. The setup information for your database is stored in a special file called the "Schema", and this is updated whenever you make changes to the structure of your database. Think of the schema as saying "here's our database and it's got a couple tables. The first table is 'users' and it's got columns for 'ID' (which is an integer), 'name' (which is a bunch of characters), 'email' (which is a bunch of characters) ..." + +In addition to setting up tables, you can tell your database to only allow unique values in a particular column (e.g. for usernames) or to index a column for faster searching later with `CREATE INDEX`. Create indexes, which basically do all the hard work of sorting your table ahead of time, for columns that you'll likely be using to search on later (like username)... it will make your database much faster. + +SQL likes semicolons at the end of lines and using single quotes (') instead of double quotes("). + +#### Mucking Around with Data + +Once your database is set up and you've got empty tables to work with, you use SQL's statements to start populating it. The main actions you want to do are CRUD (which we've seen before) -- Create, Read, Update, and Destroy. Most of the commands you run will fall under the "Read" category, since you'll spend lots of time asking questions of your data and trying to display it. + +Every CRUDdy command in SQL contains a few parts -- the action ("statement"), the table it should run on, and the conditions ("clauses"). If you just do an action on a table without specifying conditions, it will apply to the whole database and you'll probably break something. + +For "Destroy" queries, the classic mistake is typing `DELETE FROM users` without a `WHERE` clause, which removes all your users from the table. You probably needed to delete just one user, who you would specify based on some (hopefully unique) attribute like "name" or "id" as part of your condition clause, e.g. `DELETE FROM users WHERE users.id = 1`. You can do all kinds of common sense things like using `>`, `<`, `<=` etc. comparison operators to specify groups of rows to run commands on or logical operators like `AND`, `OR`, `NOT` etc to chain multiple clauses together, e.g. `DELETE FROM users WHERE id > 12 AND name = 'foo'`. + +"Create" queries use `INSERT INTO` and you'll need to specify which columns to insert stuff into and then which values to put in those columns, which looks something like `INSERT INTO users (name, email) VALUES ('foobar','foo@bar.com');`. This is one of the few queries that you don't need to be careful about which rows you've selected since you're actually just adding new ones into the table. + +"Update" queries use `UPDATE` and you'll need to tell it what data to `SET` (using key="value" pairs) and which rows to do those updates for. Be careful because if your `WHERE` clause finds multiple rows (e.g. if you've searched based on a common first name), they'll all get updated. A standard query for updating a user's email may look something like the following (though in the real world you'd search on ID because it's always unique): + +~~~sql + UPDATE users + SET name='barfoo', email='bar@foo.com' + WHERE email='foo@bar.com'; +~~~ + +"Read" queries, which use `SELECT`, are the most common, e.g. `SELECT * FROM users WHERE created_at < '2013-12-11 15:35:59 -0800'`. The `*` you see just says "all the columns". Specify a column using both the table name and the column name. You can get away with just the column name for simple queries but as soon as there are more than one table involved, SQL will yell at you so just always specify the table name: `SELECT users.id, users.name FROM users`. + +A close cousin of `SELECT`, for if you only want unique values of a column, is `SELECT DISTINCT`. Say you want a list of all the different names of your users without any duplicates... try `SELECT DISTINCT users.name FROM users`. + +#### Mashing Tables Together + +If you want to get all the posts created by a given user, you need to tell SQL which columns it should use to zip the tables together with the `ON` clause. Perform the "zipping" with the `JOIN` command. But wait, if you mash two tables together where the data doesn't perfectly match up (e.g. there are multiple posts for one user), which rows do you actually keep? There are four different possibilities: + +_(__note:__ the "left" table is the original table (the one that the `FROM` clause was `ON`), e.g. "users" in examples below.)_ + +*See ["A Visual Explanation of SQL Joins"](http://blog.codinghorror.com/a-visual-explanation-of-sql-joins) by Jeff Atwood for good visuals.* + +1. `INNER JOIN`, aka `JOIN` -- Your best friend and 95% of what you'll use. Keeps only the rows from both tables where they match up. If you asked for all the posts for all users (`SELECT * FROM users JOIN posts ON users.id = posts.user_id`), it would return only the users who have actually written posts and only posts which have specified their author in the `user_id` column. If an author has written multiple posts, there will be multiple rows returned (but the columns containing the user data will just be repeated). +2. `LEFT OUTER JOIN` -- keep all the rows from the left table and add on any rows from the right table which match up to the left table's. Set any empty cells this produces to `NULL`. E.g. return all the users whether they have written posts or not. If they do have posts, list those posts as above. If not, set the columns we asked for from the "posts" table to `NULL`. +3. `RIGHT OUTER JOIN` -- the opposite... keep all rows in the right table. +4. `FULL OUTER JOIN` -- Keep all rows from all tables, even if there are mismatches between them. Set any mismatched cells to `NULL`. + +Joins naturally let you specify conditions too, like if you only want the posts from a specific user: `SELECT * FROM users JOIN posts ON users.id = posts.user_id WHERE users.id = 42`. + +Read through [W3 Schools' Joins lesson](http://www.w3schools.com/sql/sql_join.asp) for a better explanation. + +#### Using Functions to Aggregate Your Data + +When you run a vanilla SQL query, you often get back a bunch of rows. Sometimes you want to just return a single relevant value that aggregates a column, like the `COUNT` of posts a user has written. In this case, just use one of the helpful "aggregate" functions offered by SQL (most of which you'd expect to be there -- functions like `SUM` and `MIN` and `MAX` etc). You include the function as a part of the `SELECT` statement, like `SELECT MAX(users.age) FROM users`. The function will operate on just a single column unless you specify `*`, which only works for some functions like `COUNT` (because how would you `MAX` a column for "name"?). + +You often see aliases (`AS`) used to rename columns or aggregate functions so you can call them by that alias later, e.g. `SELECT MAX(users.age) AS highest_age FROM users` will return a column called `highest_age` with the maximum age in it. + +Now we're getting into the fun stuff. Aggregate functions like `COUNT` which return just a single value for your whole dataset are nice, but they become really useful when you want to use them on very specific chunks of your data and then group them together, e.g. displaying the `COUNT` of posts for EACH user (as opposed to the count of all posts by all users). That would look like: + +~~~sql + SELECT users.id, users.name, COUNT(posts.id) AS posts_written + FROM users + JOIN posts ON users.id = posts.user_id + GROUP BY users.id; +~~~ + +See [W3 Schools' article](http://www.w3schools.com/sql/trysql.asp?filename=trysql_select_groupby) and play around with the SQL in the window (try deleting the `GROUP BY` line) for an interactive visual. + +The last nifty trick is if you want to only display a subset of your data. In a normal situation, you'd use a `WHERE` clause to narrow it down. But if you've used an aggregate function like `COUNT` (say to get the count of posts written for each user in the example above), `WHERE` won't work anymore. So to conditionally retrieve records based on aggregate functions, you use the `HAVING` function, which is essentially the `WHERE` for aggregates. So say you only want to display users who have written more than 10 posts: + +~~~sql + SELECT users.id, users.name, COUNT(posts.id) AS posts_written + FROM users + JOIN posts ON users.id = posts.user_id + GROUP BY users.id + HAVING posts_written >= 10; +~~~ + +Try going back to [the W3 Schools' example](http://www.w3schools.com/sql/trysql.asp?filename=trysql_select_groupby) and joining the `Customers` and the `Orders` tables to get the number of orders in each country and adding the line `HAVING COUNT(*) > 10;` after `GROUP BY` (and delete the extra semicolon in the previous line). + +You probably got lost somewhere in the above explanation and that's just fine... it's covering way more stuff than anyone can pick up in 10 minutes. The assigned reading will do a better job of explaining things but, more importantly, you'll get plenty of opportunities to solidify your understanding by applying it in the project. If you've still got blind spots, check out the Additional Resources section below. Fear not and stick with it! + +### SQL is faster than Ruby! + +Learning this stuff is particularly relevant because it's MUCH faster for you to build queries that use SQL intelligently than to just grab a whole bunch of data out of your database and then use Ruby to process it. For instance, if you want all the unique names of your users, you COULD just grab the whole list from your database using SQL like `SELECT users.name FROM users` (which Active Record will do for you with `User.select(:name)`) then remove duplicates using Ruby's `#uniq` method, e.g. `User.select(:name).uniq`... but that requires you to pull all that data out of your database and then put it into memory and then iterate through it using Ruby. Use `SELECT DISTINCT users.name FROM users` instead to have SQL do it all in one step. + +SQL is built to be fast. It has a special query optimizer which takes a look at the whole query you're about to run and it figures out exactly which tables it needs to join together and how it can most quickly execute the query. The difference between using `SELECT` and `SELECT DISTINCT` is negligible compared to the time cost of doing it in Ruby. Learning your SQL will help you write Active Record queries that can do more which will make your app much faster. + + +### Assignment + +
      + 1. Go through this interactive SQL tutorial from [SQL Teaching](https://www.sqlteaching.com/) + 2. Go through this more in-depth interactive SQL tutorial from [SQL Bolt](http://sqlbolt.com) + 3. Go through the basics at [Part 1](https://www.sqlcourse.com/beginner-course/) and the advanced at [Part 2](https://www.sqlcourse.com/advanced-course/) of SQL Course +
      + +### Conclusion + +SQL can be a tricky set of concepts to wrap your head around, particularly when it comes to conditionally displaying and grouping the results of multiple joins. We've emphasized that this stuff is useful for understanding what's going on behind the scenes with Rails and you'll get a chance to apply it in the project. Everything up to vanilla joins and vanilla aggregate functions is core knowledge that you should really make an effort to assimilate. + +If you never quite get to the point where you're comfortable with the really advanced concepts, you'll luckily not need to use them except in a small fraction of situations in your future. It's good to learn it all up front but you'll probably find yourself Googling for how to perform certain advanced queries when the time comes anyway. + +The next step, once you've had a chance to practice this all in the project, is to apply it to Rails with Active Record. You'll quickly find that Active Record makes your life much, much, much better. Just don't forget about ol' SQL when you've moved onto those better and brighter things, okay? + +### Knowledge Checks +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is the difference between a foreign key and a primary key?](#foreign-key) +- [Where is the setup information for your database stored?](#schema) +- [What are the important parts of a SQL command?](#command-parts) +- [Which SQL statement is associated with "Read" from the CRUD acronym?](#sql-read) +- [Which `JOIN` statement keeps only the rows from both tables where they match up?](#inner-join) +- [How do you use an aggregate function?](#aggregate-function) +- [In which situation would you use the `HAVING` function?](#having-function) +- [Why can't I just use Ruby to process my database data?](#sql-is-faster-than-ruby) + +### Additional Resources +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +- Odinite Hunter D made his excellent notes into a [Git Book on SQL](https://hunter-ducharme.gitbook.io/sql-basics) which you should totally check out if you want a decent resource. +- [SQL "tutorial" from tutorialspoint](http://www.tutorialspoint.com/sql/index.htm)... doesn't really give much guidance, but can be a useful reference for the language. +- [A Beginners Guide to SQL](http://www.sohamkamani.com/blog/2016/07/07/a-beginners-guide-to-sql/) by Soham Kamani. +- [SQL Flashcards](https://flashcards.github.io/sql/introduction.html) by flashcards.github.io. +- If you feel like doing more SQL exercises, make sure to check out [SQL Exercises](http://www.sql-ex.com/). diff --git a/markdown/javascript.md b/markdown/javascript.md new file mode 100644 index 0000000..7471bc6 --- /dev/null +++ b/markdown/javascript.md @@ -0,0 +1,16 @@ +--- + +title: "hello" + +--- + +# Javascript the language + +## Functional Programming +- Function Context +- What to do +- How to go about doing it + +### Function in javascript vs Arrow functions + +- [ x ] Dance diff --git a/markdown/mark.md b/markdown/mark.md new file mode 100644 index 0000000..49a6541 --- /dev/null +++ b/markdown/mark.md @@ -0,0 +1,8 @@ +--- + +title: "Mark" +date: "28 April 2023" + +--- + +# mark md \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7d79a31 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1701 @@ +{ + "name": "learn-by-doing-app", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "gray-matter": "^4.0.3", + "next": "latest", + "react": "18.2.0", + "react-dom": "18.2.0", + "remark": "^14.0.2", + "remark-html": "^15.0.2" + }, + "devDependencies": { + "@types/node": "20.1.1", + "@types/react": "18.2.6", + "typescript": "5.0.4" + } + }, + "node_modules/@next/env": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.1.tgz", + "integrity": "sha512-eD6WCBMFjLFooLM19SIhSkWBHtaFrZFfg2Cxnyl3vS3DAdFRfnx5TY2RxlkuKXdIRCC0ySbtK9JXXt8qLCqzZg==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.1.tgz", + "integrity": "sha512-eF8ARHtYfnoYtDa6xFHriUKA/Mfj/cCbmKb3NofeKhMccs65G6/loZ15a6wYCCx4rPAd6x4t1WmVYtri7EdeBg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.1.tgz", + "integrity": "sha512-7cmDgF9tGWTgn5Gw+vP17miJbH4wcraMHDCOHTYWkO/VeKT73dUWG23TNRLfgtCNSPgH4V5B4uLHoZTanx9bAw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.1.tgz", + "integrity": "sha512-qwJqmCri2ie8aTtE5gjTSr8S6O8B67KCYgVZhv9gKH44yvc/zXbAY8u23QGULsYOyh1islWE5sWfQNLOj9iryg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.1.tgz", + "integrity": "sha512-qcC54tWNGDv/VVIFkazxhqH1Bnagjfs4enzELVRlUOoJPD2BGJTPI7z08pQPbbgxLtRiu8gl2mXvpB8WlOkMeA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.1.tgz", + "integrity": "sha512-9TeWFlpLsBosZ+tsm/rWBaMwt5It9tPH8m3nawZqFUUrZyGRfGcI67js774vtx0k3rL9qbyY6+3pw9BCVpaYUA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.1.tgz", + "integrity": "sha512-sNDGaWmSqTS4QRUzw61wl4mVPeSqNIr1OOjLlQTRuyInxMxtqImRqdvzDvFTlDfdeUMU/DZhWGYoHrXLlZXe6A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.1.tgz", + "integrity": "sha512-+CXZC7u1iXdLRudecoUYbhbsXpglYv8KFYsFxKBPn7kg+bk7eJo738wAA4jXIl8grTF2mPdmO93JOQym+BlYGA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.1.tgz", + "integrity": "sha512-vIoXVVc7UYO68VwVMDKwJC2+HqAZQtCYiVlApyKEeIPIQpz2gpufzGxk1z3/gwrJt/kJ5CDZjlhYDCzd3hdz+g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.1.tgz", + "integrity": "sha512-n8V5ImLQZibKTu10UUdI3nIeTLkliEXe628qxqW9v8My3BAH2a7H0SaCqkV2OgqFnn8sG1wxKYw9/SNJ632kSA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.1.tgz", + "integrity": "sha512-uKBEevTNb+l6/aCQaKVnUModfEMjAl98lw2Si9P5y4hLu9tm6AlX2ZIoXZX6Wh9lJueYPrGPKk5WMCNHg/u6/A==", + "dev": true + }, + "node_modules/@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.6.tgz", + "integrity": "sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001486", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz", + "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", + "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "hastscript": "^7.0.0", + "property-information": "^6.0.0", + "vfile": "^5.0.0", + "vfile-location": "^4.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "dependencies": { + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", + "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/parse5": "^6.0.0", + "hast-util-from-parse5": "^7.0.0", + "hast-util-to-parse5": "^7.0.0", + "html-void-elements": "^2.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-sanitize": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-4.1.0.tgz", + "integrity": "sha512-Hd9tU0ltknMGRDv+d6Ro/4XKzBqQnP/EZrpiTbpFYfXv/uOhWeKc+2uajcbEvAEH98VZd7eII2PiXm13RihnLw==", + "dependencies": { + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz", + "integrity": "sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-raw": "^7.0.0", + "hast-util-whitespace": "^2.0.0", + "html-void-elements": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", + "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz", + "integrity": "sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "dependencies": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", + "integrity": "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", + "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", + "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", + "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", + "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", + "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", + "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", + "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", + "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", + "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", + "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", + "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", + "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", + "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", + "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", + "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz", + "integrity": "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", + "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", + "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", + "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "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==" + }, + "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==", + "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/next": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.1.tgz", + "integrity": "sha512-JBw2kAIyhKDpjhEWvNVoFeIzNp9xNxg8wrthDOtMctfn3EpqGCmW0FSviNyGgOSOSn6zDaX48pmvbdf6X2W9xA==", + "dependencies": { + "@next/env": "13.4.1", + "@swc/helpers": "0.5.1", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1", + "zod": "3.21.4" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=16.8.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "13.4.1", + "@next/swc-darwin-x64": "13.4.1", + "@next/swc-linux-arm64-gnu": "13.4.1", + "@next/swc-linux-arm64-musl": "13.4.1", + "@next/swc-linux-x64-gnu": "13.4.1", + "@next/swc-linux-x64-musl": "13.4.1", + "@next/swc-win32-arm64-msvc": "13.4.1", + "@next/swc-win32-ia32-msvc": "13.4.1", + "@next/swc-win32-x64-msvc": "13.4.1" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "fibers": ">= 3.1.0", + "node-sass": "^6.0.0 || ^7.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/remark": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz", + "integrity": "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "remark-parse": "^10.0.0", + "remark-stringify": "^10.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-html": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-15.0.2.tgz", + "integrity": "sha512-/CIOI7wzHJzsh48AiuIyIe1clxVkUtreul73zcCXLub0FmnevQE0UMFDQm7NUx8/3rl/4zCshlMfqBdWScQthw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "hast-util-sanitize": "^4.0.0", + "hast-util-to-html": "^8.0.0", + "mdast-util-to-hast": "^12.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", + "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz", + "integrity": "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", + "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", + "dependencies": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..169f8be --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "gray-matter": "^4.0.3", + "next": "latest", + "react": "18.2.0", + "react-dom": "18.2.0", + "remark": "^14.0.2", + "remark-html": "^15.0.2" + }, + "devDependencies": { + "@types/node": "20.1.1", + "@types/react": "18.2.6", + "typescript": "5.0.4" + } +} diff --git a/pages/api/_make.ts b/pages/api/_make.ts new file mode 100644 index 0000000..0b558a1 --- /dev/null +++ b/pages/api/_make.ts @@ -0,0 +1,50 @@ +import {readdirSync, readFileSync, writeFileSync} from 'fs'; +import path from 'path'; +import { remark } from 'remark'; +import html from 'remark-html'; +import matter from 'gray-matter'; + +const markdownDirectory = './markdown'; +const pagesDirectory = './pages/sample'; + +async function generateNextjsPages() { + const markdownFiles = readdirSync(markdownDirectory); + + for (const file of markdownFiles) { + const filePath = path.join(markdownDirectory, file); + const fileContent = readFileSync(filePath, 'utf-8'); + const { data, content } = matter(fileContent); + + const processedContent = await remark() + .use(html) + .process(content); + + const contentHtml = processedContent; + + const pageSlug = file.replace('.md', ''); + const pagePath = path.join(pagesDirectory, `${pageSlug}.js`); + + const componentName = pageSlug.charAt(0).toUpperCase() + pageSlug.substring(1).toLowerCase() + // Generate the Next.js page file + writeFileSync(pagePath, generatePageTemplate(data, contentHtml, componentName)); + } +} + +function generatePageTemplate(metadata, html, componentName) { + + return ` + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function ${componentName}() { + return ( + + + ); + } + `; + +} + +export {generateNextjsPages} \ No newline at end of file diff --git a/pages/api/create.ts b/pages/api/create.ts new file mode 100644 index 0000000..b529433 --- /dev/null +++ b/pages/api/create.ts @@ -0,0 +1,8 @@ +import { generateNextjsPages } from "./_make" + +export default async function route(req,res){ + + await generateNextjsPages(); + + res.status(200).json("hello") +} \ No newline at end of file diff --git a/pages/index.js b/pages/index.js new file mode 100644 index 0000000..d0b94c8 --- /dev/null +++ b/pages/index.js @@ -0,0 +1,15 @@ +import Head from "next/head"; +import styles from "../styles/Home.module.css"; +import Link from "next/link"; + +export default function Home() { + + return ( +
      +

      Learn By Doing App

      + Go back + Go back + Go back +
      + ); +} diff --git a/pages/sample/class_based_components.js b/pages/sample/class_based_components.js new file mode 100644 index 0000000..6844e1a --- /dev/null +++ b/pages/sample/class_based_components.js @@ -0,0 +1,255 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Class_based_components() { + return ( + Introduction +

      All the components by far, have been functional in style and syntax. This is common now, but you will see a different class based syntax too. In this lesson, we explore how a class-based component is written and how concepts like props and state are used in one.

      +

      Lesson Overview

      +

      This section contains a general overview of topics that you will learn in this lesson.

      +
        +
      • Learn the structure of a class component and how they are written.
      • +
      • How to use props and state in class components.
      • +
      • Highlight the uses of this in class components.
      • +
      +

      The What's And Whys

      +

      In your previous lessons, you have already been introduced to functional components, and the basic patterns in which components get written now a days. However, React components did not look this way when React was introduced.

      +

      If you look into any older React codebase, you'll notice a lot of classes. These are known as class-based components. Prior to February 2019, functional components were also called state-less, as there was no way to manage state in them. This was changed when hooks were introduced, leading to less verbose and 'neater' components.

      +

      In your career, chances are, you will be dealing with legacy code, so there will be days where you would be dealing with class components. Let's peek into the intricacies of a class-based component, and how they work.

      +

      Building A Class Component

      +

      As we already know about functional components, let us build a class-based component from a functional one. Usually, you will want to divide the contents of a component, like the one we use, into smaller, re-usable components, but for the purposes of this exercise, we stick to one component. Below, we have a sample functional component:

      +
      import React, { useState } from "react";
      +
      +const FunctionalInput = ({ name }) => {
      +  const [todos, setTodos] = useState(["Just some demo tasks", "As an example"]);
      +  const [inputVal, setInputVal] = useState("");
      +
      +  const handleInputChange = (e) => {
      +    setInputVal(e.target.value);
      +  };
      +
      +  const handleSubmit = (e) => {
      +    e.preventDefault();
      +    setTodos((todo) => [...todo, inputVal]);
      +    setInputVal("");
      +  };
      +
      +  return (
      +    <section>
      +      <h3>{name}</h3>
      +      <form onSubmit={handleSubmit}>
      +        <label htmlFor="task-entry">Enter a task: </label>
      +        <input
      +          type="text"
      +          name="task-entry"
      +          value={inputVal}
      +          onChange={handleInputChange}
      +        />
      +        <button type="submit">Submit</button>
      +      </form>
      +      <h4>All the tasks!</h4>
      +      <ul>
      +        {todos.map((todo) => (
      +          <li key={todo}>{todo}</li>
      +        ))}
      +      </ul>
      +    </section>
      +  );
      +};
      +
      +export default FunctionalInput;
      +
      +

      That was a solid chunk of code. Take a while, sip some water and read it a couple of times.

      +

      The Start Of A Class-Based Component

      +

      Now, let's try to recreate it as a class-based component. The first thing it should have is, drumroll, a class! But it cannot be just another class, it will need to have certain properties that qualifies it as a React component. React provides us with all those properties on a class called Component, and we can write our components by simply extending the given class, as shown below:

      +
      import React, { Component } from "react";
      +
      +class ClassInput extends Component {
      +  // Some code goes here
      +}
      +
      +/*
      +  This can also be written as:
      +
      +  import React from 'react';
      +  class ClassInput extends React.Component {}
      +  export default ClassInput;
      +
      +  instead of destructuring the `Component` during import
      +*/
      +
      +export default ClassInput;
      +
      +

      The Use Of A Constructor And Props

      +

      A class is generally incomplete without a constructor, so let's add one.

      +

      The props, that get passed into this component, gets passed into the class's constructor. This, along with the super method, allows you to use the props in context to this, which, in this case, refers to the component. If you’re really curious about what super actually does, check out the docs.

      +

      If your component doesn't have any props, it is fine to leave the constructor and the super with no arguments.

      +
      import React, { Component } from "react";
      +
      +class ClassInput extends Component {
      +  constructor(props) {
      +    super(props);
      +  }
      +  // Some more code goes here
      +}
      +
      +export default ClassInput;
      +
      +

      Way To Render JSX

      +

      Now that the props can be accessed inside of the class component, the next issue is to find a way to render the JSX.

      +

      Well, you can do that by returning your JSX from a render method! You can use the props that you declared in the constructor too!

      +
      import React, { Component } from "react";
      +
      +class ClassInput extends Component {
      +  constructor(props) {
      +    super(props);
      +  }
      +  // Some more code goes here
      +
      +  render() {
      +    return (
      +      <section>
      +        <h3>{this.props.name}</h3>
      +        {/* The input field to enter To-Do's */}
      +        <form>
      +          <label htmlFor="task-entry">Enter a task: </label>
      +          <input type="text" name="task-entry" />
      +          <button type="submit">Submit</button>
      +        </form>
      +        <h4>All the tasks!</h4>
      +        {/* The list of all the To-Do's, displayed */}
      +        <ul></ul>
      +      </section>
      +    );
      +  }
      +}
      +
      +export default ClassInput;
      +
      +

      Notice how the props get provided by this, unlike the functional component that we saw, initially?

      +

      How To Use State And Manage Context

      +

      Next comes the state. In a class-based component, the state gets initialized as a part of the constructor.

      +
      import React, { Component } from "react";
      +
      +class ClassInput extends Component {
      +  constructor(props) {
      +    super(props);
      +
      +    this.state = {
      +      todos: [],
      +      inputVal: "",
      +    };
      +  }
      +  // Some more code goes here
      +
      +  render() {
      +    return (
      +      <section>
      +        <h3>{this.props.name}</h3>
      +        <form>
      +          <label htmlFor="task-entry">Enter a task: </label>
      +          <input type="text" name="task-entry" />
      +          <button type="submit">Submit</button>
      +        </form>
      +        <h4>All the tasks!</h4>
      +        <ul></ul>
      +      </section>
      +    );
      +  }
      +}
      +
      +export default ClassInput;
      +
      +

      The pre-defined setState method can be used to set it again! Remember, state must not be mutated, so a new state must be set, every time.

      +

      Now, it is time to finish it off by adding all the functionality! It is nearly the same, except for a single difference. Whenever a method is declared, you must bind the this of the method to that of the class in order to work with it, as by default, the methods in a class are not bound to it. Usually, you do this inside the constructor and not at runtime [in the render method].

      +
      import React, { Component } from "react";
      +
      +class ClassInput extends Component {
      +  constructor(props) {
      +    super(props);
      +
      +    this.state = {
      +      todos: [],
      +      inputVal: "",
      +    };
      +
      +    this.handleInputChange = this.handleInputChange.bind(this);
      +    this.handleSubmit = this.handleSubmit.bind(this);
      +  }
      +
      +  handleInputChange(e) {
      +    this.setState((state) => ({
      +      ...state,
      +      inputVal: e.target.value,
      +    }));
      +  }
      +
      +  handleSubmit(e) {
      +    e.preventDefault();
      +    this.setState((state) => ({
      +      todos: state.todos.concat(state.inputVal),
      +      inputVal: "",
      +    }));
      +  }
      +
      +  render() {
      +    return (
      +      <section>
      +        <h3>{this.props.name}</h3>
      +        <form onSubmit={this.handleSubmit}>
      +          <label htmlFor="task-entry">Enter a task: </label>
      +          <input
      +            type="text"
      +            name="task-entry"
      +            value={this.state.inputVal}
      +            onChange={this.handleInputChange}
      +          />
      +          <button type="submit">Submit</button>
      +        </form>
      +        <h4>All the tasks!</h4>
      +        <ul>
      +          {this.state.todos.map((todo) => (
      +            <li key={todo}>{todo}</li>
      +          ))}
      +        </ul>
      +      </section>
      +    );
      +  }
      +}
      +
      +export default ClassInput;
      +
      +

      And there we go, we have successfully made our first class-based component, as easy as that!

      +

      Assignment

      +

      For the purposes of this assignment, we take the simple class-based component that we built. There is an interactive example provided at the end, where you can work on.

      +
        +
      1. +

        Implement a delete button for each task. The delete button should remove that specific task from the state array, thus deleting the task itself! Styling isn't a priority at this moment, but the button tag should be styled by default.

        +
      2. +
      3. +

        Implement a new class component, Count that displays the count of the number of todos, at any given time.

        +
      4. +
      5. +

        Implement an edit button for each task. It should replace the todo with an input field, and change the button itself to 'Resubmit', so the edits can be saved. This is a comparatively harder task, kudos for when you finish it!

        +
      6. +
      +

      Knowledge Check

      +

      This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to.

      +
        +
      • How do props get used in a class-based component?
      • +
      • How does JSX get displayed?
      • +
      • How do we deal with state in a class-based component?
      • +
      • How do you restore the context of this in a method?
      • +
      +

      Additional Resources

      +

      This section contains helpful links to related content. It isn’t required, so consider it supplemental.

      +
        +
      • In general, the React documentation on thinking in react is a really good place to look into, if you want more practice with class-based components, from scratch.
      • +
      +`}> +
      + ); + } + \ No newline at end of file diff --git a/pages/sample/component_lifecycle_methods.js b/pages/sample/component_lifecycle_methods.js new file mode 100644 index 0000000..e2261f4 --- /dev/null +++ b/pages/sample/component_lifecycle_methods.js @@ -0,0 +1,71 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Component_lifecycle_methods() { + return ( + Introduction +

      In the previous section, you've learned about a component's lifecycle. In functional components, we've primarily used the useEffect hook to perform tasks throughout the lifecycle, but in class components we have to use specific lifecycle methods instead.

      +

      We'll start with a short recap of what a components lifecycle is. There are three stages to a component's life: mounting, updating, and unmounting. Each of these have a method assigned to them within class components, which is what we are going to cover in this lesson.

      +

      Lesson Overview

      +

      This section contains a general overview of topics that you will learn in this lesson.

      +
        +
      • How to use lifecycle methods in a class component
      • +
      +

      render()

      +

      The render function is the most used lifecycle method, and one that you've come across in the last class components lesson. It is the only required lifecycle method in a class component. It runs on mount and update of a component. Render should be pure, meaning it doesn't modify component state, returns the same thing each time it's called (given the same inputs), and doesn't directly interact with the browser.

      +

      componentDidMount()

      +

      This method is run after the component is mounted (inserted in the DOM tree). You should make any calls to fetch data that is needed for the component here. It is also a good place to do anything that is reliant on the component, such as drawing on a canvas element that you just rendered.

      +

      componentDidUpdate()

      +

      This method is run after a component re-renders. Because of this, you have to be careful about what you update in this method, as if you're updating state indiscriminately, a re-render is caused, and you'll end up in an endless loop. You can avoid this issue by using conditional statements about the equality of previous and current props when updating state.

      +

      In this method you should be updating anything that needs to be changed in response to either the DOM changing, or any states that you might want to act on upon change. For example, you'd want to refetch user data if the user changes.

      +

      componentWillUnmount()

      +

      This is the last lifecycle method, which is called before a component is unmounted and destroyed. In this method you should be performing cleanup actions, so that would be cancelling network requests, clearing timers, etc.

      +

      How useEffect() combines the Lifecycle Methods

      +

      Now that we've learnt about class lifecycle methods, it's useful to understand that the useEffect hook used in functional components is essentially a combination of componentDidMount, componentDidUpdate, and componentWillUnmount. Which method/methods it relates to varies based on it's dependency array, and if it returns anything.

      +
        +
      • An empty dependency array would be equivalent to componentDidMount.
      • +
      • A dependency array with a value/values in it would be a combination of componentDidMount and componentDidUpdate, but only updating when dependencies change.
      • +
      • No dependency array would be equivalent to componentDidMount and componentDidUpdate combined.
      • +
      • A return function inside of a useEffect() hook would be equivalent to componentWillUnmount.
      • +
      +

      For example:

      +
          useEffect(() => {
      +        placeholderFunction()
      +        return () => cleanupFunction()
      +    }, [])
      +
      +

      In this snippet, the useEffect contains the functionality of componentDidMount, and componentWillUnmount via the return function. This example doesn't have the componentDidUpdate functionality because of an empty dependency array.

      +

      Assignment

      +
        +
      1. +

        Check out this lifecycle diagram to see a good visual representation of a components lifecycle methods.

        +
      2. +
      3. +

        Read this component lifecycle documentation by the people who made react!

        +
          +
        • You only have to worry about the methods in bold, but you can read up on the others if you're curious - they're uncommon and you likely won't need them in +most of your apps.
        • +
        +
      4. +
      +

      Knowledge Check

      +

      This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to.

      +
        +
      • What is the only required lifecycle method?
      • +
      • What lifecycle method should you use for initial data fetching?
      • +
      • When you want to act upon change of the DOM, or of state, what lifecycle method would you use?
      • +
      • When performing cleanup actions, what lifecycle method should be used?
      • +
      • How does the useEffect hook combine some of the lifecycle methods?
      • +
      +

      Additional Resources

      +

      This section contains helpful links to related content. It isn’t required, so consider it supplemental.

      +
        +
      • It looks like this lesson doesn't have any additional resources yet. Help us expand this section by contributing to our curriculum.
      • +
      +`}> +
      + ); + } + \ No newline at end of file diff --git a/pages/sample/databases.js b/pages/sample/databases.js new file mode 100644 index 0000000..4ce9c98 --- /dev/null +++ b/pages/sample/databases.js @@ -0,0 +1,60 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Databases() { + return ( + Introduction +

      We've talked about the client-side and the server-side but how do we keep ahold of all our user's data? Who remembers that your login password is CatLover1985 so you can sign into the website? The bottom layer of any web application is the database and it handles all the remembering for you (we'll cover caching much later). It can be relatively simple, like an excel spreadsheet, or incredibly complex and split into many giant pieces like Facebook's.

      +

      Databases are kind of hidden in the back of the web application so people treat them with a sort of suspicion and awe. That's nonsense and you should get over it -- your database and you are going to become very good friends (or at least frenemies). By the end of this curriculum, you're going to understand what's going on with your databases and be able to interact with them like a pro (and probably better than some people you'll work with). This lesson is a teaser for that.

      +

      Compared to a normal programming language like you've already learned, SQL (Structured Query Language), which is used to query databases, is a very simple syntax... there are only a small handful of verbs to learn. What trips people up is that you need to be able to visualize in your head what it's going to be doing. We'll spend a fair bit of time on SQL and databases because they're so fundamental, but for now we'll just cover enough to get you familiar with what's going on in there.

      +

      Learning Overview

      +

      This section contains a general overview of topics that you will learn in this lesson.

      +
        +
      • What a database is.
      • +
      • What relational databases are.
      • +
      • In what way relational databases are different from XML.
      • +
      • What SQL is.
      • +
      • What SQL is used for.
      • +
      • How to get all the records from a table in SQL.
      • +
      • How to insert a record in SQL.
      • +
      +

      Assignment

      +
        +
      1. +

        Check out this introduction of how SQL can be used to organise and manage an overwhelming amount of data.

        +
          +
        • You do not need to go any further than the first page on introductions.
        • +
        +
      2. +
      3. +

        Watch this short video introduction to relational databases to get a feel for why this stuff is useful and some more exposure to the terminology we'll use.

        +
      4. +
      5. +

        Go through this Khan Academy tutorial, to get a feel for actually creating and manipulating databases.

        +
      6. +
      +

      Knowledge Check

      +

      This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to.

      + +

      Additional Resources

      +

      This section contains helpful links to related content. It isn’t required, so consider it supplemental.

      + +`}> +
      + ); + } + \ No newline at end of file diff --git a/pages/sample/databases_and_sql.js b/pages/sample/databases_and_sql.js new file mode 100644 index 0000000..1ac2d25 --- /dev/null +++ b/pages/sample/databases_and_sql.js @@ -0,0 +1,114 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Databases_and_sql() { + return ( + Introduction +

      Data is the core of any good web app and a good working knowledge of SQL will take you a long way. That knowledge lets you not just understand what's going on behind the scenes with your ORM tool (e.g. Active Record) but also to feel comfortable asking more complicated questions of your data. And that's really what SQL is all about -- asking questions of your database and occasionally also adding or changing things in it. Querying can be incredibly useful for you.

      +

      In more straightforward cases, you might want to display all users who signed up in December via the promotion code "FREESTUFF". You might want to display all comments created by the current user and sorted by topic and creation date. In more complex cases, you may want to show a list of all the orders shipped to states with more than 1,000 users by quantity and total order value. Or, for internal reasons, you might ask marketing analysis questions like which promotion channels produce users who meet your specified engagement criteria of reading 5 articles per work week.

      +

      All of those examples involve you engaging with your database. Luckily, many databases (the ones we'll be focusing on) speak SQL and luckily SQL really only has a dozen or so words that you'll consistently use out of a few dozen total. So it's not a large language, it's much more about the concepts behind it.

      +

      You will start with the questions like the ones above and then have to figure out how to ask them properly of your database, which likely has a bunch of different tables in it. Everyone probably visualizes it a bit differently, but finding a way to visualize what's going on when you do SQL queries is pretty important. We actually think of Excel tables moving in our head and combining with each other and reshuffling as necessary. To each their own.

      +

      We'll move beyond just the simple SELECT "users".* FROM "users" LIMIT 1 queries and into more dynamic topics like joining tables together, performing calculations on the results, and grouping results together in new ways.

      +

      All this stuff is being used by Rails behind the scenes so understanding it will make you much better at writing queries in Rails. This is why we're going over databases before learning Rails.

      +

      A Note on Resources

      +

      SQL is one of those topics that's been stored away in dusty old technical manuals and 90's style websites. Even the best books out there can make it seem oddly complicated because they tend to write for the database engineer who actually does need to know all the nitty gritty details.

      +

      Though the prevalence of web applications these days has grown the demand among new users to focus on understanding the concepts of SQL, the learning tools haven't really caught up. We'll do our best to impart those concepts using the tools available.

      +

      Lesson Overview

      +

      This section contains a general overview of topics that you will learn in this lesson.

      +
        +
      • What a Primary Key is.
      • +
      • What Foreign Keys are.
      • +
      • What a Schema is.
      • +
      • How to use various SQL statements like SELECT, CREATE TABLE, UPDATE, DELETE and more.
      • +
      • How to use various SQL clauses like WHERE, LIKE, DISTINCT and more.
      • +
      • How to use various SQL functions like AVG, COUNT, SUM and more.
      • +
      • What Indexes are good for.
      • +
      • What the difference between WHERE and HAVING is.
      • +
      +

      The World's Fastest Semi-Complete Explanation of SQL

      +

      This is a very brief soup-to-nuts explanation of SQL. It won't do a particularly good job teaching you specific new tactics but should present a general overview to have going into the reading assignment. Here we go...

      +

      SQL is the language used to talk to many relational databases. These databases use lots of tables to store different types of data (e.g. "users" and "posts" tables). Tables are long lists like spreadsheets where each row is a different record (or object, e.g. a single user) and each column is one of that record's attributes (like name, email, etc). The one column that all tables include is an "ID" column, which gives the unique row numbers, and is called the record's "primary key".

      +

      You can "link" tables together by making one of the columns in one table point to the ID of another table, for instance a row in the "posts" table might include the author's ID under the column called "user_id". Because the "posts" table has the ID of another table in it, that column is called a "foreign key".

      +

      Setting Stuff Up

      +

      SQL lets you do everything. The first category of commands are for setting up the database (CREATE DATABASE), setting up an individual table (CREATE TABLE), and similar commands for altering or destroying them. The setup information for your database is stored in a special file called the "Schema", and this is updated whenever you make changes to the structure of your database. Think of the schema as saying "here's our database and it's got a couple tables. The first table is 'users' and it's got columns for 'ID' (which is an integer), 'name' (which is a bunch of characters), 'email' (which is a bunch of characters) ..."

      +

      In addition to setting up tables, you can tell your database to only allow unique values in a particular column (e.g. for usernames) or to index a column for faster searching later with CREATE INDEX. Create indexes, which basically do all the hard work of sorting your table ahead of time, for columns that you'll likely be using to search on later (like username)... it will make your database much faster.

      +

      SQL likes semicolons at the end of lines and using single quotes (') instead of double quotes(").

      +

      Mucking Around with Data

      +

      Once your database is set up and you've got empty tables to work with, you use SQL's statements to start populating it. The main actions you want to do are CRUD (which we've seen before) -- Create, Read, Update, and Destroy. Most of the commands you run will fall under the "Read" category, since you'll spend lots of time asking questions of your data and trying to display it.

      +

      Every CRUDdy command in SQL contains a few parts -- the action ("statement"), the table it should run on, and the conditions ("clauses"). If you just do an action on a table without specifying conditions, it will apply to the whole database and you'll probably break something.

      +

      For "Destroy" queries, the classic mistake is typing DELETE FROM users without a WHERE clause, which removes all your users from the table. You probably needed to delete just one user, who you would specify based on some (hopefully unique) attribute like "name" or "id" as part of your condition clause, e.g. DELETE FROM users WHERE users.id = 1. You can do all kinds of common sense things like using >, <, <= etc. comparison operators to specify groups of rows to run commands on or logical operators like AND, OR, NOT etc to chain multiple clauses together, e.g. DELETE FROM users WHERE id > 12 AND name = 'foo'.

      +

      "Create" queries use INSERT INTO and you'll need to specify which columns to insert stuff into and then which values to put in those columns, which looks something like INSERT INTO users (name, email) VALUES ('foobar','foo@bar.com');. This is one of the few queries that you don't need to be careful about which rows you've selected since you're actually just adding new ones into the table.

      +

      "Update" queries use UPDATE and you'll need to tell it what data to SET (using key="value" pairs) and which rows to do those updates for. Be careful because if your WHERE clause finds multiple rows (e.g. if you've searched based on a common first name), they'll all get updated. A standard query for updating a user's email may look something like the following (though in the real world you'd search on ID because it's always unique):

      +
        UPDATE users
      +  SET name='barfoo', email='bar@foo.com'
      +  WHERE email='foo@bar.com';
      +
      +

      "Read" queries, which use SELECT, are the most common, e.g. SELECT * FROM users WHERE created_at < '2013-12-11 15:35:59 -0800'. The * you see just says "all the columns". Specify a column using both the table name and the column name. You can get away with just the column name for simple queries but as soon as there are more than one table involved, SQL will yell at you so just always specify the table name: SELECT users.id, users.name FROM users.

      +

      A close cousin of SELECT, for if you only want unique values of a column, is SELECT DISTINCT. Say you want a list of all the different names of your users without any duplicates... try SELECT DISTINCT users.name FROM users.

      +

      Mashing Tables Together

      +

      If you want to get all the posts created by a given user, you need to tell SQL which columns it should use to zip the tables together with the ON clause. Perform the "zipping" with the JOIN command. But wait, if you mash two tables together where the data doesn't perfectly match up (e.g. there are multiple posts for one user), which rows do you actually keep? There are four different possibilities:

      +

      (note: the "left" table is the original table (the one that the FROM clause was ON), e.g. "users" in examples below.)

      +

      See "A Visual Explanation of SQL Joins" by Jeff Atwood for good visuals.

      +
        +
      1. INNER JOIN, aka JOIN -- Your best friend and 95% of what you'll use. Keeps only the rows from both tables where they match up. If you asked for all the posts for all users (SELECT * FROM users JOIN posts ON users.id = posts.user_id), it would return only the users who have actually written posts and only posts which have specified their author in the user_id column. If an author has written multiple posts, there will be multiple rows returned (but the columns containing the user data will just be repeated).
      2. +
      3. LEFT OUTER JOIN -- keep all the rows from the left table and add on any rows from the right table which match up to the left table's. Set any empty cells this produces to NULL. E.g. return all the users whether they have written posts or not. If they do have posts, list those posts as above. If not, set the columns we asked for from the "posts" table to NULL.
      4. +
      5. RIGHT OUTER JOIN -- the opposite... keep all rows in the right table.
      6. +
      7. FULL OUTER JOIN -- Keep all rows from all tables, even if there are mismatches between them. Set any mismatched cells to NULL.
      8. +
      +

      Joins naturally let you specify conditions too, like if you only want the posts from a specific user: SELECT * FROM users JOIN posts ON users.id = posts.user_id WHERE users.id = 42.

      +

      Read through W3 Schools' Joins lesson for a better explanation.

      +

      Using Functions to Aggregate Your Data

      +

      When you run a vanilla SQL query, you often get back a bunch of rows. Sometimes you want to just return a single relevant value that aggregates a column, like the COUNT of posts a user has written. In this case, just use one of the helpful "aggregate" functions offered by SQL (most of which you'd expect to be there -- functions like SUM and MIN and MAX etc). You include the function as a part of the SELECT statement, like SELECT MAX(users.age) FROM users. The function will operate on just a single column unless you specify *, which only works for some functions like COUNT (because how would you MAX a column for "name"?).

      +

      You often see aliases (AS) used to rename columns or aggregate functions so you can call them by that alias later, e.g. SELECT MAX(users.age) AS highest_age FROM users will return a column called highest_age with the maximum age in it.

      +

      Now we're getting into the fun stuff. Aggregate functions like COUNT which return just a single value for your whole dataset are nice, but they become really useful when you want to use them on very specific chunks of your data and then group them together, e.g. displaying the COUNT of posts for EACH user (as opposed to the count of all posts by all users). That would look like:

      +
        SELECT users.id, users.name, COUNT(posts.id) AS posts_written
      +  FROM users
      +  JOIN posts ON users.id = posts.user_id
      +  GROUP BY users.id;
      +
      +

      See W3 Schools' article and play around with the SQL in the window (try deleting the GROUP BY line) for an interactive visual.

      +

      The last nifty trick is if you want to only display a subset of your data. In a normal situation, you'd use a WHERE clause to narrow it down. But if you've used an aggregate function like COUNT (say to get the count of posts written for each user in the example above), WHERE won't work anymore. So to conditionally retrieve records based on aggregate functions, you use the HAVING function, which is essentially the WHERE for aggregates. So say you only want to display users who have written more than 10 posts:

      +
        SELECT users.id, users.name, COUNT(posts.id) AS posts_written
      +  FROM users
      +  JOIN posts ON users.id = posts.user_id
      +  GROUP BY users.id
      +  HAVING posts_written >= 10;
      +
      +

      Try going back to the W3 Schools' example and joining the Customers and the Orders tables to get the number of orders in each country and adding the line HAVING COUNT(*) > 10; after GROUP BY (and delete the extra semicolon in the previous line).

      +

      You probably got lost somewhere in the above explanation and that's just fine... it's covering way more stuff than anyone can pick up in 10 minutes. The assigned reading will do a better job of explaining things but, more importantly, you'll get plenty of opportunities to solidify your understanding by applying it in the project. If you've still got blind spots, check out the Additional Resources section below. Fear not and stick with it!

      +

      SQL is faster than Ruby!

      +

      Learning this stuff is particularly relevant because it's MUCH faster for you to build queries that use SQL intelligently than to just grab a whole bunch of data out of your database and then use Ruby to process it. For instance, if you want all the unique names of your users, you COULD just grab the whole list from your database using SQL like SELECT users.name FROM users (which Active Record will do for you with User.select(:name)) then remove duplicates using Ruby's #uniq method, e.g. User.select(:name).uniq... but that requires you to pull all that data out of your database and then put it into memory and then iterate through it using Ruby. Use SELECT DISTINCT users.name FROM users instead to have SQL do it all in one step.

      +

      SQL is built to be fast. It has a special query optimizer which takes a look at the whole query you're about to run and it figures out exactly which tables it needs to join together and how it can most quickly execute the query. The difference between using SELECT and SELECT DISTINCT is negligible compared to the time cost of doing it in Ruby. Learning your SQL will help you write Active Record queries that can do more which will make your app much faster.

      +

      Assignment

      +

      Conclusion

      +

      SQL can be a tricky set of concepts to wrap your head around, particularly when it comes to conditionally displaying and grouping the results of multiple joins. We've emphasized that this stuff is useful for understanding what's going on behind the scenes with Rails and you'll get a chance to apply it in the project. Everything up to vanilla joins and vanilla aggregate functions is core knowledge that you should really make an effort to assimilate.

      +

      If you never quite get to the point where you're comfortable with the really advanced concepts, you'll luckily not need to use them except in a small fraction of situations in your future. It's good to learn it all up front but you'll probably find yourself Googling for how to perform certain advanced queries when the time comes anyway.

      +

      The next step, once you've had a chance to practice this all in the project, is to apply it to Rails with Active Record. You'll quickly find that Active Record makes your life much, much, much better. Just don't forget about ol' SQL when you've moved onto those better and brighter things, okay?

      +

      Knowledge Checks

      +

      This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to.

      + +

      Additional Resources

      +

      This section contains helpful links to related content. It isn’t required, so consider it supplemental.

      + +`}> +
      + ); + } + \ No newline at end of file diff --git a/pages/sample/javascript.js b/pages/sample/javascript.js new file mode 100644 index 0000000..b175512 --- /dev/null +++ b/pages/sample/javascript.js @@ -0,0 +1,23 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Javascript() { + return ( + Javascript the language +

      Functional Programming

      +
        +
      • Function Context
      • +
      • What to do
      • +
      • How to go about doing it
      • +
      +

      Function in javascript vs Arrow functions

      +
        +
      • [ x ] Dance
      • +
      +`}> +
      + ); + } + \ No newline at end of file diff --git a/pages/sample/mark.js b/pages/sample/mark.js new file mode 100644 index 0000000..4d2d61f --- /dev/null +++ b/pages/sample/mark.js @@ -0,0 +1,13 @@ + + // GENERATED FILE : CHANGES MADE MIGHT BE LOST ON BUILDING + + import Layout from '../../components/Layout'; + + export default function Mark() { + return ( + mark md +`}> + + ); + } + \ No newline at end of file diff --git a/pages/track/javascript/course_content.md b/pages/track/javascript/course_content.md new file mode 100644 index 0000000..1a1f28c --- /dev/null +++ b/pages/track/javascript/course_content.md @@ -0,0 +1,18 @@ +-- +title: "Javascript" +date: "28 April 2023" +-- + +# Javascript Internals + +## Event Loop + As you might already know javascript is single threaded, so how does it handle asynchronous task. + It uses a concept of event loop. + Event Loop constantly keeps looking for a new task inside the `task queue`. When we make a web api call like on setTimeout or setInterval method. + + - Then it goes to the Web API section. which then is moved to `task queue`. + - Event loop picks from the `task queue` and moves it to the `execution stack`. + +## Note +> Event loop is a very important concept and questions are frequently asking from this concept. +> So don't skip it understand it properly and frame your language properly so that it is presentable. \ No newline at end of file diff --git a/pages/track/javascript/eventloop.js b/pages/track/javascript/eventloop.js new file mode 100644 index 0000000..335eee1 --- /dev/null +++ b/pages/track/javascript/eventloop.js @@ -0,0 +1,9 @@ +import { useEffect, useState, useRef, useMemo } from "react"; +import ContentPage from "../../../components/ContentPage"; +export default function() { + return ( +
      + +
      + ) +} diff --git a/pages/track/javascript/index.js b/pages/track/javascript/index.js new file mode 100644 index 0000000..4d124d4 --- /dev/null +++ b/pages/track/javascript/index.js @@ -0,0 +1,51 @@ +import CourseItem from "../../../components/CourseItem"; + +const CoursePage = () => { + const trackCourses = [ + { + course_name: "event loop", + tags: "js, basics, important", + route:"/track/javascript/eventloop" + }, + { + course_name: "Web API and Promises", + tags: "js, basics, important, confusing", + route:"/track/javascript/webapi" + }, + ]; + + return ( +
      +
      +

      + course heading +

      +

      + The Only Course on Javascript from the basics +

      +
      + {trackCourses.map((item, key) => ( + + ))} +
      + ); +}; + +export default CoursePage; diff --git a/pages/user/hello.js b/pages/user/hello.js new file mode 100644 index 0000000..8f07ed9 --- /dev/null +++ b/pages/user/hello.js @@ -0,0 +1,5 @@ +export default function UserPage(){ + return (
      +

      Hello

      +
      ); +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..4965832 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..fbf0e25 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/styles/Home.module.css b/styles/Home.module.css new file mode 100644 index 0000000..b825756 --- /dev/null +++ b/styles/Home.module.css @@ -0,0 +1,91 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0 0 1rem; + line-height: 1.15; + font-size: 3.6rem; +} + +.title { + text-align: center; +} + +.title, +.description { + text-align: center; +} + + +.description { + line-height: 1.5; + font-size: 1.5rem; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + + max-width: 800px; + margin-top: 3rem; +} + +.card { + margin: 1rem; + flex-basis: 45%; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h3 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} diff --git a/styles/globals.css b/styles/globals.css new file mode 100644 index 0000000..54ef947 --- /dev/null +++ b/styles/globals.css @@ -0,0 +1,38 @@ +html, +body { + padding: 0; + margin: 0; + font-family: Lato, sans-serif, Roboto, Inter, -apple-system, BlinkMacSystemFont, Segoe UI, + Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, ; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} + +img { + max-width: 100%; + height: auto; +} + +h1, +h2, +p, +ul { + margin: 0; +} + +ul { + padding: 0; + list-style: none; +} + +button { + padding: 0.5rem 1rem; + font-weight: bold; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..801915e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" +, "pages/track/javascript/index.js" ], + "exclude": [ + "node_modules" + ] +}