diff --git a/.dockerignore b/.dockerignore new file mode 100755 index 00000000..27d2dae2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +*/node_modules +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 00000000..d369844d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:8.11.4 + +WORKDIR /app/website + +EXPOSE 3000 35729 +COPY ./docs /app/docs +COPY ./website /app/website +RUN yarn install + +CMD ["yarn", "start"] diff --git a/_site/.github/ISSUE_TEMPLATE/bug/index.html b/_site/.github/ISSUE_TEMPLATE/bug/index.html new file mode 100644 index 00000000..8071ac1b --- /dev/null +++ b/_site/.github/ISSUE_TEMPLATE/bug/index.html @@ -0,0 +1,18 @@ +

๐Ÿ› Bug Report

+

A clear and concise description of what the bug is.

+

To Reproduce

+

Steps to reproduce the behavior:

+

Expected behavior

+

A clear and concise description of what you expected to happen.

+

Link to repro (highly encouraged)

+

+ Please provide either a + CodeSandbox demo or a + minimal repository on GitHub. +

+

Environment

+ diff --git a/_site/.github/ISSUE_TEMPLATE/feature/index.html b/_site/.github/ISSUE_TEMPLATE/feature/index.html new file mode 100644 index 00000000..46b20c33 --- /dev/null +++ b/_site/.github/ISSUE_TEMPLATE/feature/index.html @@ -0,0 +1,6 @@ +

๐Ÿš€ Feature Proposal

+

A clear and concise description of what the feature is.

+

Motivation

+

Please outline the motivation for the proposal.

+

Example

+

Please provide an example for how this feature would be used.

diff --git a/_site/readme/index.html b/_site/readme/index.html new file mode 100644 index 00000000..f14478f5 --- /dev/null +++ b/_site/readme/index.html @@ -0,0 +1,1190 @@ + +

Immer

+

+ npm + Build Status + Coverage Status + Minzipped size + code style: prettier + Donate +

+

+ Create the next immutable state tree by simply modifying the current + tree +

+

+ Winner of the "Breakthrough of the year" + React open source award and + "Most impactful contribution" + JavaScript open source award in + 2019 +

+

Release notes

+

+ Did Immer make a difference to your project? Consider buying me a coffee!
Buy Me A Coffee +

+
+ +
+ +

+ Immer (German for: always) is a tiny package that allows you to work with + immutable state in a more convenient way. It is based on the + copy-on-write + mechanism. +

+

+ The basic idea is that you will apply all your changes to a temporary + draftState, which is a proxy of the currentState. Once all + your mutations are completed, Immer will produce the nextState based + on the mutations to the draft state. This means that you can interact with + your data by simply modifying it while keeping all the benefits of immutable + data. +

+

immer-hd.png

+

+ Using Immer is like having a personal assistant; he takes a letter (the + current state) and gives you a copy (draft) to jot changes onto. Once you are + done, the assistant will take your draft and produce the real immutable, final + letter for you (the next state). +

+

+ A mindful reader might notice that this is quite similar to + withMutations of ImmutableJS. It is indeed, but generalized and + applied to plain, native JavaScript data structures (arrays and objects) + without further needing any library. +

+

External resources

+ +

API

+

The Immer package exposes a default function that does all the work.

+

+ produce(currentState, producer: (draftState) => void): nextState +

+

+ There is also a curried overload that is explained + below. +

+

Example

+
import produce from "immer"
+
+const baseState = [
+	{
+		todo: "Learn typescript",
+		done: true
+	},
+	{
+		todo: "Try immer",
+		done: false
+	}
+]
+
+const nextState = produce(baseState, draftState => {
+	draftState.push({todo: "Tweet about it"})
+	draftState[1].done = true
+})
+
+

+ The interesting thing about Immer is that the baseState will be + untouched, but the nextState will reflect all changes made to + draftState. +

+
// the new item is only added to the next state,
+// base state is unmodified
+expect(baseState.length).toBe(2)
+expect(nextState.length).toBe(3)
+
+// same for the changed 'done' prop
+expect(baseState[1].done).toBe(false)
+expect(nextState[1].done).toBe(true)
+
+// unchanged data is structurally shared
+expect(nextState[0]).toBe(baseState[0])
+// changed data not (dรปh)
+expect(nextState[1]).not.toBe(baseState[1])
+
+

Benefits

+ +

Read further to see all these benefits explained.

+

Reducer Example

+

+ Here is a simple example of the difference that Immer could make in practice. +

+
// Redux reducer
+// Shortened, based on: https://github.com/reactjs/redux/blob/master/examples/shopping-cart/src/reducers/products.js
+const byId = (state, action) => {
+	switch (action.type) {
+		case RECEIVE_PRODUCTS:
+			return {
+				...state,
+				...action.products.reduce((obj, product) => {
+					obj[product.id] = product
+					return obj
+				}, {})
+			}
+		default:
+			return state
+	}
+}
+
+

After using Immer, that simply becomes:

+
import produce from "immer"
+
+const byId = (state, action) =>
+	produce(state, draft => {
+		switch (action.type) {
+			case RECEIVE_PRODUCTS:
+				action.products.forEach(product => {
+					draft[product.id] = product
+				})
+		}
+	})
+
+

+ Notice that it is not needed to handle the default case, a producer that + doesn't do anything will simply return the original state. +

+

+ Creating Redux reducer is just a sample application of the Immer package. + Immer is not just designed to simplify Redux reducers. It can be used in any + context where you have an immutable data tree that you want to clone and + modify (with structural sharing). +

+

+ Note: it might be tempting after using producers for a while, to just place + produce in your root reducer and then pass the draft to each + reducer and work directly over such draft. Don't do that. It kills the point + of Redux where each reducer is testable as pure reducer. Immer is best used + when applying it to small individual pieces of logic. +

+

React.setState example

+

+ Deep updates in the state of React components can be greatly simplified as + well by using immer. Take for example the following onClick handlers (Try in + codesandbox): +

+
/**
+ * Classic React.setState with a deep merge
+ */
+onBirthDayClick1 = () => {
+	this.setState(prevState => ({
+		user: {
+			...prevState.user,
+			age: prevState.user.age + 1
+		}
+	}))
+}
+
+/**
+ * ...But, since setState accepts functions,
+ * we can just create a curried producer and further simplify!
+ */
+onBirthDayClick2 = () => {
+	this.setState(
+		produce(draft => {
+			draft.user.age += 1
+		})
+	)
+}
+
+

Currying

+

+ Passing a function as the first argument to produce is intended + to be used for currying. This means that you get a pre-bound producer that + only needs a state to produce the value from. The producer function gets + passed in the draft and any further arguments that were passed to the curried + function. +

+

For example:

+
// mapper will be of signature (state, index) => state
+const mapper = produce((draft, index) => {
+	draft.index = index
+})
+
+// example usage
+console.dir([{}, {}, {}].map(mapper))
+//[{index: 0}, {index: 1}, {index: 2}])
+
+

+ This mechanism can also nicely be leveraged to further simplify our example + reducer: +

+
import produce from "immer"
+
+const byId = produce((draft, action) => {
+	switch (action.type) {
+		case RECEIVE_PRODUCTS:
+			action.products.forEach(product => {
+				draft[product.id] = product
+			})
+			return
+	}
+})
+
+

+ Note that state is now factored out (the created reducer will + accept a state, and invoke the bound producer with it). +

+

+ If you want to initialize an uninitialized state using this construction, you + can do so by passing the initial state as second argument to + produce: +

+
import produce from "immer"
+
+const byId = produce(
+	(draft, action) => {
+		switch (action.type) {
+			case RECEIVE_PRODUCTS:
+				action.products.forEach(product => {
+					draft[product.id] = product
+				})
+				return
+		}
+	},
+	{
+		1: {id: 1, name: "product-1"}
+	}
+)
+
+
Fun with currying
+

+ A random fun example just for inspiration: a neat trick is to turn + Object.assign into a producer to create a "spread" + function that is smarter than the normal spread operator, as it doesn't + produce a new state if the result doesn't actually change (details & explanation). Quick example: +

+
import produce from "immer"
+const spread = produce(Object.assign)
+
+const base = {x: 1, y: 1}
+
+console.log({...base, y: 1} === base) // false
+console.log(spread(base, {y: 1}) === base) // true! base is recycled as no actual new value was produced
+console.log(spread(base, {y: 2}) === base) // false, produced a new object as it should
+
+

Patches

+

+ During the run of a producer, Immer can record all the patches that would + replay the changes made by the reducer. This is a very powerful tool if you + want to fork your state temporarily and replay the changes to the original. +

+

Patches are useful in few scenarios:

+ +

+ To help with replaying patches, applyPatches comes in handy. Here + is an example how patches could be used to record the incremental updates and + (inverse) apply them: +

+
import produce, {applyPatches} from "immer"
+
+let state = {
+	name: "Micheal",
+	age: 32
+}
+
+// Let's assume the user is in a wizard, and we don't know whether
+// his changes should end up in the base state ultimately or not...
+let fork = state
+// all the changes the user made in the wizard
+let changes = []
+// the inverse of all the changes made in the wizard
+let inverseChanges = []
+
+fork = produce(
+	fork,
+	draft => {
+		draft.age = 33
+	},
+	// The third argument to produce is a callback to which the patches will be fed
+	(patches, inversePatches) => {
+		changes.push(...patches)
+		inverseChanges.push(...inversePatches)
+	}
+)
+
+// In the meantime, our original state is replaced, as, for example,
+// some changes were received from the server
+state = produce(state, draft => {
+	draft.name = "Michel"
+})
+
+// When the wizard finishes (successfully) we can replay the changes that were in the fork onto the *new* state!
+state = applyPatches(state, changes)
+
+// state now contains the changes from both code paths!
+expect(state).toEqual({
+	name: "Michel", // changed by the server
+	age: 33 // changed by the wizard
+})
+
+// Finally, even after finishing the wizard, the user might change his mind and undo his changes...
+state = applyPatches(state, inverseChanges)
+expect(state).toEqual({
+	name: "Michel", // Not reverted
+	age: 32 // Reverted
+})
+
+

+ The generated patches are similar (but not the same) to the + RFC-6902 JSON patch standard, + except that the path property is an array, rather than a string. + This makes processing patches easier. If you want to normalize to the official + specification, patch.path = patch.path.join("/") should + do the trick. Anyway, this is what a bunch of patches and their inverse could + look like: +

+
[
+	{
+		"op": "replace",
+		"path": ["profile"],
+		"value": {"name": "Veria", "age": 5}
+	},
+	{"op": "remove", "path": ["tags", 3]}
+]
+
+
[
+	{"op": "replace", "path": ["profile"], "value": {"name": "Noa", "age": 6}},
+	{"op": "add", "path": ["tags", 3], "value": "kiddo"}
+]
+
+

produceWithPatches

+

+ Instead of setting up a patch listener, an easier way to obtain the patches is + to use produceWithPatches, which has the same signature as + produce, except that it doesn't return just the next state, but a + tuple consisting of [nextState, patches, inversePatches]. Like + produce, produceWithPatches supports currying as + well. +

+
import {produceWithPatches} from "immer"
+
+const [nextState, patches, inversePatches] = produceWithPatches(
+	{
+		age: 33
+	},
+	draft => {
+		draft.age++
+	}
+)
+
+

Which produces:

+
;[
+	{
+		age: 34
+	},
+	[
+		{
+			op: "replace",
+			path: ["age"],
+			value: 34
+		}
+	],
+	[
+		{
+			op: "replace",
+			path: ["age"],
+			value: 33
+		}
+	]
+]
+
+

+ For a more in-depth study, see + Distributing patches and rebasing actions using Immer +

+

+ Tip: Check this trick to + compress patches + produced over time. +

+

Async producers

+

+ It is allowed to return Promise objects from recipes. Or, in other words, to + use async / await. This can be pretty useful for long running + processes, that only produce the new object once the promise chain resolves. + Note that produce itself (even in the curried form) will return a + promise if the producer is async. Example: +

+
import produce from "immer"
+
+const user = {
+	name: "michel",
+	todos: []
+}
+
+const loadedUser = await produce(user, async function(draft) {
+	draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
+})
+
+

+ Warning: please note that the draft shouldn't be 'leaked' from the async + process and stored else where. The draft will still be revoked as soon as + the async process completes. +

+

createDraft and finishDraft

+

+ createDraft and finishDraft are two low-level + functions that are mostly useful for libraries that build abstractions on top + of immer. It avoids the need to always create a function in order to work with + drafts. Instead, one can create a draft, modify it, and at some time in the + future finish the draft, in which case the next immutable state will be + produced. We could for example rewrite our above example as: +

+
import {createDraft, finishDraft} from "immer"
+
+const user = {
+	name: "michel",
+	todos: []
+}
+
+const draft = createDraft(user)
+draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
+const loadedUser = finishDraft(draft)
+
+

+ Note: finishDraft takes a patchListener as second + argument, which can be used to record the patches, similarly to + produce. +

+

+ Warning: in general, we recommend to use produce instead of + the createDraft / finishDraft combo, + produce is less error prone in usage, and more clearly + separates the concepts of mutability and immutability in your code base. +

+

Returning data from producers

+

+ It is not needed to return anything from a producer, as Immer will return the + (finalized) version of the draft anyway. However, it is allowed + to just return draft. +

+

+ It is also allowed to return arbitrarily other data from the producer + function. But only if you didn't modify the draft. This can be useful + to produce an entirely new state. Some examples: +

+
const userReducer = produce((draft, action) => {
+	switch (action.type) {
+		case "renameUser":
+			// OK: we modify the current state
+			draft.users[action.payload.id].name = action.payload.name
+			return draft // same as just 'return'
+		case "loadUsers":
+			// OK: we return an entirely new state
+			return action.payload
+		case "adduser-1":
+			// NOT OK: This doesn't do change the draft nor return a new state!
+			// It doesn't modify the draft (it just redeclares it)
+			// In fact, this just doesn't do anything at all
+			draft = {users: [...draft.users, action.payload]}
+			return
+		case "adduser-2":
+			// NOT OK: modifying draft *and* returning a new state
+			draft.userCount += 1
+			return {users: [...draft.users, action.payload]}
+		case "adduser-3":
+			// OK: returning a new state. But, unnecessary complex and expensive
+			return {
+				userCount: draft.userCount + 1,
+				users: [...draft.users, action.payload]
+			}
+		case "adduser-4":
+			// OK: the immer way
+			draft.userCount += 1
+			draft.users.push(action.payload)
+			return
+	}
+})
+
+

+ Note: It is not possible to return undefined this way, as it + is indistinguishable from not updating the draft! Read on... +

+

Producing undefined using nothing

+

+ So, in general, one can replace the current state by just + returning a new value from the producer, rather than modifying + the draft. There is a subtle edge case however: if you try to write a producer + that wants to replace the current state with undefined: +

+
produce({}, draft => {
+	// don't do anything
+})
+
+

Versus:

+
produce({}, draft => {
+	// Try to return undefined from the producer
+	return undefined
+})
+
+

+ The problem is that in JavaScript a function that doesn't return anything also + returns undefined! So immer cannot differentiate between those + different cases. So, by default, Immer will assume that any producer that + returns undefined just tried to modify the draft. +

+

+ However, to make it clear to Immer that you intentionally want to produce the + value undefined, you can return the built-in token + nothing: +

+
import produce, {nothing} from "immer"
+
+const state = {
+	hello: "world"
+}
+
+produce(state, draft => {})
+produce(state, draft => undefined)
+// Both return the original state: { hello: "world"}
+
+produce(state, draft => nothing)
+// Produces a new state, 'undefined'
+
+

+ N.B. Note that this problem is specific for the undefined value, + any other value, including null, doesn't suffer from this issue. +

+

Inline shortcuts using void

+

+ Draft mutations in Immer usually warrant a code block, since a return denotes + an overwrite. Sometimes that can stretch code a little more than you might be + comfortable with. +

+

+ In such cases, you can use javascripts + void + operator, which evaluates expressions and returns undefined. +

+
// Single mutation
+produce(draft => void (draft.user.age += 1))
+
+// Multiple mutations
+produce(draft => void ((draft.user.age += 1), (draft.user.height = 186)))
+
+

+ Code style is highly personal, but for code bases that are to be understood by + many, we recommend to stick to the classic + draft => { draft.user.age += 1} to avoid cognitive overhead. +

+

Extracting the original object from a proxied instance

+

+ Immer exposes a named export original that will get the original + object from the proxied instance inside produce (or return + undefined for unproxied values). A good example of when this can + be useful is when searching for nodes in a tree-like state using strict + equality. +

+
import {original} from "immer"
+
+const baseState = {users: [{name: "Richie"}]}
+const nextState = produce(baseState, draftState => {
+	original(draftState.users) // is === baseState.users
+})
+
+

+ Just want to know if a value is a proxied instance? Use the + isDraft function! +

+
import {isDraft} from "immer"
+
+const baseState = {users: [{name: "Bobby"}]}
+const nextState = produce(baseState, draft => {
+	isDraft(draft) // => true
+	isDraft(draft.users) // => true
+	isDraft(draft.users[0]) // => true
+})
+isDraft(nextState) // => false
+
+

Auto freezing

+

+ Immer automatically freezes any state trees that are modified using + produce. This protects against accidental modifications of the + state tree outside of a producer. This comes with a performance impact, so it + is recommended to disable this option in production. It is by default enabled. + By default, it is turned on during local development and turned off in + production. Use setAutoFreeze(true / false) to explicitly turn + this feature on or off. +

+

+ โš ๏ธ If auto freezing is enabled, recipes are not entirely side-effect free: + Any plain object or array that ends up in the produced result, will be + frozen, even when these objects were not frozen before the start of the + producer! โš ๏ธ +

+

Immer on older JavaScript environments?

+

+ By default produce tries to use proxies for optimal performance. + However, on older JavaScript engines Proxy is not available. For + example, when running Microsoft Internet Explorer or React Native (< v0.59) + on Android. In such cases, Immer will fallback to an ES5 compatible + implementation which works identical, but is a bit slower. +

+

Importing immer

+

+ produce is exposed as the default export, but optionally it can + be used as name import as well, as this benefits some older project setups. So + the following imports are all correct, where the first is recommended: +

+
import produce from "immer"
+import {produce} from "immer"
+
+const {produce} = require("immer")
+const produce = require("immer").produce
+const produce = require("immer").default
+
+import unleashTheMagic from "immer"
+import {produce as unleashTheMagic} from "immer"
+
+

Supported object types

+

Plain objects and arrays are always drafted by Immer.

+

+ Every other object must use the immerable symbol to mark itself + as compatible with Immer. When one of these objects is mutated within a + producer, its prototype is preserved between copies. +

+
import {immerable} from "immer"
+
+class Foo {
+	[immerable] = true // Option 1
+
+	constructor() {
+		this[immerable] = true // Option 2
+	}
+}
+
+Foo[immerable] = true // Option 3
+
+

+ For arrays, only numeric properties and the length property can + be mutated. Custom properties are not preserved on arrays. +

+

+ When working with Date objects, you should always create a new + Date instance instead of mutating an existing + Date object. +

+

+ Built-in classes like Map and Set are not supported. + As a workaround, you should clone them before mutating them in a producer: +

+
const state = {
+	set: new Set(),
+	map: new Map()
+}
+const nextState = produce(state, draft => {
+	// Don't use any Set methods, as that mutates the instance!
+	draft.set.add("foo") // โŒ
+
+	// 1. Instead, clone the set (just once)
+	const newSet = new Set(draft.set) // โœ…
+
+	// 2. Mutate the clone (just in this producer)
+	newSet.add("foo")
+
+	// 3. Update the draft with the new set
+	draft.set = newSet
+
+	// Similarly, don't use any Map methods.
+	draft.map.set("foo", "bar") // โŒ
+
+	// 1. Instead, clone the map (just once)
+	const newMap = new Map(draft.map) // โœ…
+
+	// 2. Mutate it
+	newMap.set("foo", "bar")
+
+	// 3. Update the draft
+	draft.map = newMap
+})
+
+

TypeScript or Flow

+

+ The Immer package ships with type definitions inside the package, which should + be picked up by TypeScript and Flow out of the box and without further + configuration. +

+

+ The TypeScript typings automatically remove readonly modifiers + from your draft types and return a value that matches your original type. See + this practical example: +

+
import produce from "immer"
+
+interface State {
+	readonly x: number
+}
+
+// `x` cannot be modified here
+const state: State = {
+	x: 0
+}
+
+const newState = produce(state, draft => {
+	// `x` can be modified here
+	draft.x++
+})
+
+// `newState.x` cannot be modified here
+
+

+ This ensures that the only place you can modify your state is in your produce + callbacks. It even works recursively and with ReadonlyArrays! +

+

+ For curried reducers, the type is inferred from the first argument of recipe + function, so make sure to type it. The Draft utility type can be + used if the state argument type is immutable: +

+
import produce, {Draft} from "immer"
+
+interface State {
+	readonly x: number
+}
+
+// `x` cannot be modified here
+const state: State = {
+	x: 0
+}
+
+const increment = produce((draft: Draft<State>, inc: number) => {
+	// `x` can be modified here
+	draft.x += inc
+})
+
+const newState = increment(state, 2)
+// `newState.x` cannot be modified here
+
+

Note: Immer v1.9+ supports TypeScript v3.1+ only.

+

Note: Immer v3.0+ supports TypeScript v3.4+ only.

+

Pitfalls

+
    +
  1. + Don't redefine draft like, draft = myCoolNewState. Instead, + either modify the draft or return a new state. See + Returning data from producers. +
  2. +
  3. + Immer assumes your state to be a unidirectional tree. That is, no object + should appear twice in the tree, and there should be no circular references. +
  4. +
  5. + Since Immer uses proxies, reading huge amounts of data from state comes with + an overhead (especially in the ES5 implementation). If this ever becomes an + issue (measure before you optimize!), do the current state analysis before + entering the producer function or read from the + currentState rather than the draftState. Also, + realize that immer is opt-in everywhere, so it is perfectly fine to manually + write super performance critical reducers, and use immer for all the normal + ones. Also note that original can be used to get the original + state of an object, which is cheaper to read. +
  6. +
  7. + Always try to pull produce 'up', for example + for (let x of y) produce(base, d => d.push(x)) is + exponentially slower than + produce(base, d => { for (let x of y) d.push(x)}) +
  8. +
  9. + It is possible to return values from producers, except, it is not possible + to return undefined that way, as it is indistinguishable from + not updating the draft at all! If you want to replace the draft with + undefined, just return nothing from the producer. +
  10. +
+

Cool things built with immer

+ +

How does Immer work?

+

+ Read the (second part of the) + introduction blog. +

+

Example patterns.

+

For those who have to go back to thinking in object updates :-)

+
import produce from "immer"
+
+// object mutations
+const todosObj = {
+	id1: {done: false, body: "Take out the trash"},
+	id2: {done: false, body: "Check Email"}
+}
+
+// add
+const addedTodosObj = produce(todosObj, draft => {
+	draft["id3"] = {done: false, body: "Buy bananas"}
+})
+
+// delete
+const deletedTodosObj = produce(todosObj, draft => {
+	delete draft["id1"]
+})
+
+// update
+const updatedTodosObj = produce(todosObj, draft => {
+	draft["id1"].done = true
+})
+
+// array mutations
+const todosArray = [
+	{id: "id1", done: false, body: "Take out the trash"},
+	{id: "id2", done: false, body: "Check Email"}
+]
+
+// add
+const addedTodosArray = produce(todosArray, draft => {
+	draft.push({id: "id3", done: false, body: "Buy bananas"})
+})
+
+// delete
+const deletedTodosArray = produce(todosArray, draft => {
+	draft.splice(draft.findIndex(todo => todo.id === "id1"), 1)
+	// or (slower):
+	// return draft.filter(todo => todo.id !== "id1")
+})
+
+// update
+const updatedTodosArray = produce(todosArray, draft => {
+	draft[draft.findIndex(todo => todo.id === "id1")].done = true
+})
+
+

Performance

+

+ Here is a simple benchmark on the + performance of Immer. This test takes 50,000 todo items and updates 5,000 of + them. Freeze indicates that the state tree has been frozen after + producing it. This is a development best practice, as it prevents + developers from accidentally modifying the state tree. +

+

+ These tests were executed on Node 9.3.0. Use yarn test:perf to + reproduce them locally. +

+

performance.png

+

Most important observation:

+ +

Migration

+

Immer 2.* -> 3.0

+

+ In your producers, make sure you're not treating this as the + draft. (see here: https://github.com/immerjs/immer/issues/308) +

+

Upgrade to typescript@^3.4 if you're a TypeScript user.

+

Immer 1.* -> 2.0

+

+ Make sure you don't return any promises as state, because + produce will actually invoke the promise and wait until it + settles. +

+

Immer 2.1 -> 2.2

+

+ When using TypeScript, for curried reducers that are typed in the form + produce<Type>((arg) => { }), rewrite this to + produce((arg: Type) => { }) or + produce((arg: Draft<Type>) => { }) for correct + inference. +

+

FAQ

+

(for those who skimmed the above instead of actually reading)

+

+ Q: Does Immer use structural sharing? So that my selectors can be memoized + and such? +

+

A: Yes

+

Q: Does Immer support deep updates?

+

A: Yes

+

+ Q: I can't rely on Proxies being present on my target environments. Can I + use Immer? +

+

A: Yes

+

Q: Can I typecheck my data structures when using Immer?

+

A: Yes

+

+ Q: Can I store Date objects, functions etc in my state tree + when using Immer? +

+

A: Yes

+

Q: Is it fast?

+

A: Yes

+

Q: Idea! Can Immer freeze the state for me?

+

A: Yes

+

Credits

+

+ Special thanks to @Mendix, which supports its employees to experiment + completely freely two full days a month, which formed the kick-start for this + project. +

+

Donations

+

+ A significant part of my OSS work is unpaid. So + donations are greatly + appreciated :) +

diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 00000000..6711192a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3" + +services: + docusaurus: + build: . + ports: + - 3000:3000 + - 35729:35729 + volumes: + - ./docs:/app/docs + - ./website/blog:/app/website/blog + - ./website/core:/app/website/core + - ./website/i18n:/app/website/i18n + - ./website/pages:/app/website/pages + - ./website/static:/app/website/static + - ./website/sidebars.json:/app/website/sidebars.json + - ./website/siteConfig.js:/app/website/siteConfig.js + working_dir: /app/website diff --git a/docs/doc1.md b/docs/doc1.md new file mode 100755 index 00000000..37fa73f8 --- /dev/null +++ b/docs/doc1.md @@ -0,0 +1,29 @@ +--- +id: doc1 +title: Latin-ish +sidebar_label: Example Page +--- + +Check the [documentation](https://docusaurus.io) for how to use Docusaurus. + +## Lorem + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + +## Mauris In Code + +``` +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. +``` + +## Nulla + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +## Orci + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +## Phasellus + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/docs/doc2.md b/docs/doc2.md new file mode 100755 index 00000000..20c0c95a --- /dev/null +++ b/docs/doc2.md @@ -0,0 +1,7 @@ +--- +id: doc2 +title: document number 2 +--- + +This is a link to [another document.](doc3.md) +This is a link to an [external page.](http://www.example.com) diff --git a/docs/doc3.md b/docs/doc3.md new file mode 100755 index 00000000..2cc6183c --- /dev/null +++ b/docs/doc3.md @@ -0,0 +1,14 @@ +--- +id: doc3 +title: This is document number 3 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique. + +Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis. + +Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor. + +Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur. diff --git a/docs/exampledoc4.md b/docs/exampledoc4.md new file mode 100755 index 00000000..6f94ffe9 --- /dev/null +++ b/docs/exampledoc4.md @@ -0,0 +1,6 @@ +--- +id: doc4 +title: Other Document +--- + +this is another document diff --git a/docs/exampledoc5.md b/docs/exampledoc5.md new file mode 100755 index 00000000..92e2d0d5 --- /dev/null +++ b/docs/exampledoc5.md @@ -0,0 +1,6 @@ +--- +id: doc5 +title: Fifth Document +--- + +Another one diff --git a/website/README.md b/website/README.md new file mode 100755 index 00000000..05668619 --- /dev/null +++ b/website/README.md @@ -0,0 +1,198 @@ +This website was created with [Docusaurus](https://docusaurus.io/). + +# What's In This Document + +- [Get Started in 5 Minutes](#get-started-in-5-minutes) +- [Directory Structure](#directory-structure) +- [Editing Content](#editing-content) +- [Adding Content](#adding-content) +- [Full Documentation](#full-documentation) + +# Get Started in 5 Minutes + +1. Make sure all the dependencies for the website are installed: + +```sh +# Install dependencies +$ yarn +``` + +2. Run your dev server: + +```sh +# Start the site +$ yarn start +``` + +## Directory Structure + +Your project file structure should look something like this + +``` +my-docusaurus/ + docs/ + doc-1.md + doc-2.md + doc-3.md + website/ + blog/ + 2016-3-11-oldest-post.md + 2017-10-24-newest-post.md + core/ + node_modules/ + pages/ + static/ + css/ + img/ + package.json + sidebar.json + siteConfig.js +``` + +# Editing Content + +## Editing an existing docs page + +Edit docs by navigating to `docs/` and editing the corresponding document: + +`docs/doc-to-be-edited.md` + +```markdown +--- +id: page-needs-edit +title: This Doc Needs To Be Edited +--- + +Edit me... +``` + +For more information about docs, click [here](https://docusaurus.io/docs/en/navigation) + +## Editing an existing blog post + +Edit blog posts by navigating to `website/blog` and editing the corresponding post: + +`website/blog/post-to-be-edited.md` + +```markdown +--- +id: post-needs-edit +title: This Blog Post Needs To Be Edited +--- + +Edit me... +``` + +For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) + +# Adding Content + +## Adding a new docs page to an existing sidebar + +1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`: + +```md +--- +id: newly-created-doc +title: This Doc Needs To Be Edited +--- + +My new content here.. +``` + +1. Refer to that doc's ID in an existing sidebar in `website/sidebar.json`: + +```javascript +// Add newly-created-doc to the Getting Started category of docs +{ + "docs": { + "Getting Started": [ + "quick-start", + "newly-created-doc" // new doc here + ], + ... + }, + ... +} +``` + +For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation) + +## Adding a new blog post + +1. Make sure there is a header link to your blog in `website/siteConfig.js`: + +`website/siteConfig.js` + +```javascript +headerLinks: [ + ... + { blog: true, label: 'Blog' }, + ... +] +``` + +2. Create the blog post with the format `YYYY-MM-DD-My-Blog-Post-Title.md` in `website/blog`: + +`website/blog/2018-05-21-New-Blog-Post.md` + +```markdown +--- +author: Frank Li +authorURL: https://twitter.com/foobarbaz +authorFBID: 503283835 +title: New Blog Post +--- + +Lorem Ipsum... +``` + +For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) + +## Adding items to your site's top navigation bar + +1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`: + +`website/siteConfig.js` + +```javascript +{ + headerLinks: [ + ... + /* you can add docs */ + { doc: 'my-examples', label: 'Examples' }, + /* you can add custom pages */ + { page: 'help', label: 'Help' }, + /* you can add external links */ + { href: 'https://github.com/facebook/Docusaurus', label: 'GitHub' }, + ... + ], + ... +} +``` + +For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation) + +## Adding custom pages + +1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`: +1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element: + +`website/siteConfig.js` + +```javascript +{ + headerLinks: [ + ... + { page: 'my-new-custom-page', label: 'My New Custom Page' }, + ... + ], + ... +} +``` + +For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages). + +# Full Documentation + +Full documentation can be found on the [website](https://docusaurus.io/). diff --git a/website/blog/2016-03-11-blog-post.md b/website/blog/2016-03-11-blog-post.md new file mode 100755 index 00000000..cf2ba296 --- /dev/null +++ b/website/blog/2016-03-11-blog-post.md @@ -0,0 +1,18 @@ +--- +title: Blog Title +author: Blog Author +authorURL: http://twitter.com/ +authorFBID: 100002976521003 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + + + +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/website/blog/2017-04-10-blog-post-two.md b/website/blog/2017-04-10-blog-post-two.md new file mode 100755 index 00000000..3ab4637b --- /dev/null +++ b/website/blog/2017-04-10-blog-post-two.md @@ -0,0 +1,18 @@ +--- +title: New Blog Post +author: Blog Author +authorURL: http://twitter.com/ +authorFBID: 100002976521003 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + + + +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/website/blog/2017-09-25-testing-rss.md b/website/blog/2017-09-25-testing-rss.md new file mode 100755 index 00000000..13a42f76 --- /dev/null +++ b/website/blog/2017-09-25-testing-rss.md @@ -0,0 +1,14 @@ +--- +title: Adding RSS Support - RSS Truncation Test +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- + +1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 + +This should be truncated. + + + +This line should never render in XML. diff --git a/website/blog/2017-09-26-adding-rss.md b/website/blog/2017-09-26-adding-rss.md new file mode 100755 index 00000000..eeb4f047 --- /dev/null +++ b/website/blog/2017-09-26-adding-rss.md @@ -0,0 +1,10 @@ +--- +title: Adding RSS Support +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- + +This is a test post. + +A whole bunch of other information. diff --git a/website/blog/2017-10-24-new-version-1.0.0.md b/website/blog/2017-10-24-new-version-1.0.0.md new file mode 100755 index 00000000..60761c02 --- /dev/null +++ b/website/blog/2017-10-24-new-version-1.0.0.md @@ -0,0 +1,8 @@ +--- +title: New Version 1.0.0 +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- + +This blog post will test file name parsing issues when periods are present. diff --git a/website/core/Footer.js b/website/core/Footer.js new file mode 100755 index 00000000..fdfc74b8 --- /dev/null +++ b/website/core/Footer.js @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require("react") + +class Footer extends React.Component { + docUrl(doc, language) { + const baseUrl = this.props.config.baseUrl + const docsUrl = this.props.config.docsUrl + const docsPart = `${docsUrl ? `${docsUrl}/` : ""}` + const langPart = `${language ? `${language}/` : ""}` + return `${baseUrl}${docsPart}${langPart}${doc}` + } + + pageUrl(doc, language) { + const baseUrl = this.props.config.baseUrl + return baseUrl + (language ? `${language}/` : "") + doc + } + + render() { + return ( + + ) + } +} + +module.exports = Footer diff --git a/website/i18n/en.json b/website/i18n/en.json new file mode 100644 index 00000000..9bfd49c7 --- /dev/null +++ b/website/i18n/en.json @@ -0,0 +1,42 @@ +{ + "_comment": "This file is auto-generated by write-translations.js", + "localized-strings": { + "next": "Next", + "previous": "Previous", + "tagline": "A website for testing", + "docs": { + "doc1": { + "title": "Latin-ish", + "sidebar_label": "Example Page" + }, + "doc2": { + "title": "document number 2" + }, + "doc3": { + "title": "This is document number 3" + }, + "doc4": { + "title": "Other Document" + }, + "doc5": { + "title": "Fifth Document" + } + }, + "links": { + "Docs": "Docs", + "API": "API", + "Help": "Help", + "Blog": "Blog" + }, + "categories": { + "Docusaurus": "Docusaurus", + "First Category": "First Category", + "Second Category": "Second Category" + } + }, + "pages-strings": { + "Help Translate|recruit community translators for your project": "Help Translate", + "Edit this Doc|recruitment message asking to edit the doc source": "Edit", + "Translate this Doc|recruitment message asking to translate the docs": "Translate" + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 00000000..9d1c98ff --- /dev/null +++ b/website/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "examples": "docusaurus-examples", + "start": "docusaurus-start", + "build": "docusaurus-build", + "publish-gh-pages": "docusaurus-publish", + "write-translations": "docusaurus-write-translations", + "version": "docusaurus-version", + "rename-version": "docusaurus-rename-version" + }, + "devDependencies": { + "docusaurus": "^1.12.0" + } +} diff --git a/website/pages/en/help.js b/website/pages/en/help.js new file mode 100755 index 00000000..2eaf7f5e --- /dev/null +++ b/website/pages/en/help.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require("react") + +const CompLibrary = require("../../core/CompLibrary.js") + +const Container = CompLibrary.Container +const GridBlock = CompLibrary.GridBlock + +function Help(props) { + const {config: siteConfig, language = ""} = props + const {baseUrl, docsUrl} = siteConfig + const docsPart = `${docsUrl ? `${docsUrl}/` : ""}` + const langPart = `${language ? `${language}/` : ""}` + const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}` + + const supportLinks = [ + { + content: `Learn more using the [documentation on this site.](${docUrl( + "doc1.html" + )})`, + title: "Browse Docs" + }, + { + content: "Ask questions about the documentation and project", + title: "Join the community" + }, + { + content: "Find out what's new with this project", + title: "Stay up to date" + } + ] + + return ( +
+ +
+
+

Need help?

+
+

This project is maintained by a dedicated group of people.

+ +
+
+
+ ) +} + +module.exports = Help diff --git a/website/pages/en/index.js b/website/pages/en/index.js new file mode 100755 index 00000000..3b3fee29 --- /dev/null +++ b/website/pages/en/index.js @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require("react") + +const CompLibrary = require("../../core/CompLibrary.js") + +const MarkdownBlock = CompLibrary.MarkdownBlock /* Used to read markdown */ +const Container = CompLibrary.Container +const GridBlock = CompLibrary.GridBlock + +class HomeSplash extends React.Component { + render() { + const {siteConfig, language = ""} = this.props + const {baseUrl, docsUrl} = siteConfig + const docsPart = `${docsUrl ? `${docsUrl}/` : ""}` + const langPart = `${language ? `${language}/` : ""}` + const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}` + + const SplashContainer = props => ( +
+
+
{props.children}
+
+
+ ) + + const Logo = props => ( +
+ Project Logo +
+ ) + + const ProjectTitle = () => ( +

+ {siteConfig.title} + {siteConfig.tagline} +

+ ) + + const PromoSection = props => ( +
+
+
{props.children}
+
+
+ ) + + const Button = props => ( +
+ + {props.children} + +
+ ) + + return ( + + +
+ + + + + + +
+
+ ) + } +} + +class Index extends React.Component { + render() { + const {config: siteConfig, language = ""} = this.props + const {baseUrl} = siteConfig + + const Block = props => ( + + + + ) + + const FeatureCallout = () => ( +
+

Feature Callout

+ These are features of this project +
+ ) + + const TryOut = () => ( + + {[ + { + content: + "To make your landing page more attractive, use illustrations! Check out " + + "[**unDraw**](https://undraw.co/) which provides you with customizable illustrations which are free to use. " + + "The illustrations you see on this page are from unDraw.", + image: `${baseUrl}img/undraw_code_review.svg`, + imageAlign: "left", + title: "Wonderful SVG Illustrations" + } + ]} + + ) + + const Description = () => ( + + {[ + { + content: + "This is another description of how this project is useful", + image: `${baseUrl}img/undraw_note_list.svg`, + imageAlign: "right", + title: "Description" + } + ]} + + ) + + const LearnHow = () => ( + + {[ + { + content: + "Each new Docusaurus project has **randomly-generated** theme colors.", + image: `${baseUrl}img/undraw_youtube_tutorial.svg`, + imageAlign: "right", + title: "Randomly Generated Theme Colors" + } + ]} + + ) + + const Features = () => ( + + {[ + { + content: "This is the content of my feature", + image: `${baseUrl}img/undraw_react.svg`, + imageAlign: "top", + title: "Feature One" + }, + { + content: "The content of my second feature", + image: `${baseUrl}img/undraw_operating_system.svg`, + imageAlign: "top", + title: "Feature Two" + } + ]} + + ) + + const Showcase = () => { + if ((siteConfig.users || []).length === 0) { + return null + } + + const showcase = siteConfig.users + .filter(user => user.pinned) + .map(user => ( + + {user.caption} + + )) + + const pageUrl = page => baseUrl + (language ? `${language}/` : "") + page + + return ( +
+

Who is Using This?

+

This project is used by all these people

+
{showcase}
+
+ + More {siteConfig.title} Users + +
+
+ ) + } + + return ( +
+ +
+ + + + + + +
+
+ ) + } +} + +module.exports = Index diff --git a/website/pages/en/users.js b/website/pages/en/users.js new file mode 100755 index 00000000..9c2284d4 --- /dev/null +++ b/website/pages/en/users.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require("react") + +const CompLibrary = require("../../core/CompLibrary.js") + +const Container = CompLibrary.Container + +class Users extends React.Component { + render() { + const {config: siteConfig} = this.props + if ((siteConfig.users || []).length === 0) { + return null + } + + const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js` + const showcase = siteConfig.users.map(user => ( + + {user.caption} + + )) + + return ( +
+ +
+
+

Who is Using This?

+

This project is used by many folks

+
+
{showcase}
+

Are you using this project?

+ + Add your company + +
+
+
+ ) + } +} + +module.exports = Users diff --git a/website/sidebars.json b/website/sidebars.json new file mode 100755 index 00000000..fa12db3e --- /dev/null +++ b/website/sidebars.json @@ -0,0 +1,10 @@ +{ + "docs": { + "Docusaurus": ["doc1"], + "First Category": ["doc2"], + "Second Category": ["doc3"] + }, + "docs-other": { + "First Category": ["doc4", "doc5"] + } +} diff --git a/website/siteConfig.js b/website/siteConfig.js new file mode 100644 index 00000000..2e586410 --- /dev/null +++ b/website/siteConfig.js @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// See https://docusaurus.io/docs/site-config for all the possible +// site configuration options. + +// List of projects/orgs using your project for the users page. +const users = [ + { + caption: "User1", + // You will need to prepend the image path with your baseUrl + // if it is not '/', like: '/test-site/img/image.jpg'. + image: "/img/undraw_open_source.svg", + infoLink: "https://www.facebook.com", + pinned: true + } +] + +const siteConfig = { + title: "Test Site", // Title for your website. + tagline: "A website for testing", + url: "https://your-docusaurus-test-site.com", // Your website URL + baseUrl: "/", // Base URL for your project */ + // For github.io type URLs, you would set the url and baseUrl like: + // url: 'https://facebook.github.io', + // baseUrl: '/test-site/', + + // Used for publishing and more + projectName: "test-site", + organizationName: "facebook", + // For top-level user or org sites, the organization is still the same. + // e.g., for the https://JoelMarcey.github.io site, it would be set like... + // organizationName: 'JoelMarcey' + + // For no header links in the top nav bar -> headerLinks: [], + headerLinks: [ + {doc: "doc1", label: "Docs"}, + {doc: "doc4", label: "API"}, + {page: "help", label: "Help"}, + {blog: true, label: "Blog"} + ], + + // If you have users set above, you add it here: + users, + + /* path to images for header/footer */ + headerIcon: "img/favicon.ico", + footerIcon: "img/favicon.ico", + favicon: "img/favicon.ico", + + /* Colors for website */ + colors: { + primaryColor: "#11220f", + secondaryColor: "#0b170a" + }, + + /* Custom fonts for website */ + /* + fonts: { + myFont: [ + "Times New Roman", + "Serif" + ], + myOtherFont: [ + "-apple-system", + "system-ui" + ] + }, + */ + + // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds. + copyright: `Copyright ยฉ ${new Date().getFullYear()} Your Name or Your Company Name`, + + highlight: { + // Highlight.js theme to use for syntax highlighting in code blocks. + theme: "default" + }, + + // Add custom scripts here that would be placed in