feat: add jest cheatsheet.
				
					
				
			This commit is contained in:
		@@ -3,9 +3,7 @@ Quick Reference
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
为开发人员分享快速参考备忘单(主要是方便自己),在看到 [Reference](https://github.com/Randy8080/reference) 快速参考备忘单,感觉非常简单,造轮子使命感突然来了,造个中文版本的,为了方便自己的技术栈查阅,立马撸起来 :)。
 | 
					为开发人员分享快速参考备忘单(主要是方便自己),在看到 [Reference](https://github.com/Randy8080/reference) 快速参考备忘单,感觉非常简单,造轮子使命感突然来了,造个中文版本的,为了方便自己的技术栈查阅,立马撸起来 :)。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
如果您发现此处的备忘单不合适,您可以通过提交 PR 来修复它或提供更好的备忘清单,只针对【中文】用户。
 | 
					如果您发现此处的备忘单不合适,您可以通过提交 PR 来修复它或提供更好的备忘清单,只针对【中文】用户。以下是开源天使提供的一些备忘清单和快速参考 :)。
 | 
				
			||||||
 | 
					 | 
				
			||||||
以下是开源天使提供的一些备忘清单和快速参考 :)。
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 编程
 | 
					## 编程
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,14 +12,18 @@ Quick Reference
 | 
				
			|||||||
[JavaScript](./docs/javascript.md)
 | 
					[JavaScript](./docs/javascript.md)
 | 
				
			||||||
[JSON](./docs/json.md)
 | 
					[JSON](./docs/json.md)
 | 
				
			||||||
[Markdown](./docs/markdown.md)
 | 
					[Markdown](./docs/markdown.md)
 | 
				
			||||||
 | 
					<!--rehype:class=home-card-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 工具包
 | 
					## 工具包
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[VSCode](./docs/vscode.md)
 | 
					[VSCode](./docs/vscode.md)
 | 
				
			||||||
 | 
					[Jest](./docs/jest.md)
 | 
				
			||||||
 | 
					<!--rehype:class=home-card-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Linux 命令
 | 
					## Linux 命令
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Cron](./docs/cron.md)
 | 
					[Cron](./docs/cron.md)
 | 
				
			||||||
 | 
					<!--rehype:class=home-card-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## License
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,6 @@ var a;
 | 
				
			|||||||
console.log(a); // => undefined
 | 
					console.log(a); // => undefined
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
### 字符串
 | 
					### 字符串
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
@@ -95,7 +94,6 @@ console.log(single.length);
 | 
				
			|||||||
10 % 5 = 0     // 取模 Modulo
 | 
					10 % 5 = 0     // 取模 Modulo
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
### 注释
 | 
					### 注释
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										681
									
								
								docs/jest.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										681
									
								
								docs/jest.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,681 @@
 | 
				
			|||||||
 | 
					Jest 备忘清单
 | 
				
			||||||
 | 
					===
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Jest 是一款优雅、简洁的 JavaScript 测试框架。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					入门
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 介绍
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Jest](https://jestjs.io/) 是一款优雅、简洁的 JavaScript 测试框架。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 无需配置,大多数 JS 项目中即装即用,无需配置
 | 
				
			||||||
 | 
					- 优秀接口,从 it 到 expect - Jest 将工具包整合在一处。文档齐全、不断维护,非常不错。
 | 
				
			||||||
 | 
					- 隔离的,并行进行测试,发挥每一丝算力。
 | 
				
			||||||
 | 
					- 快照, 轻松编写持续追踪大型对象的测试,并在测试旁或代码内显示实时快照。
 | 
				
			||||||
 | 
					- 代码覆盖, 无需其他操作,您仅需添加 --coverage 参数来生成代码覆盖率报告。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 测试结构
 | 
				
			||||||
 | 
					<!--rehype:warp-class=col-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					describe('makePoniesPink', () => {
 | 
				
			||||||
 | 
					  beforeAll(() => {
 | 
				
			||||||
 | 
					    /* 在所有测试之前运行 */
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  afterAll(() => {
 | 
				
			||||||
 | 
					    /* 在所有测试后运行 */
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    /* 在每次测试之前运行 */
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  afterEach(() => {
 | 
				
			||||||
 | 
					    /* 每次测试后运行 */
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  test('make each pony pink', () => {
 | 
				
			||||||
 | 
					    const actual = fn(['Alice', 'Bob', 'Eve'])
 | 
				
			||||||
 | 
					    expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve'])
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					匹配器
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 基本匹配器
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					expect(42).toBe(42)    // 严格相等 (===)
 | 
				
			||||||
 | 
					expect(42).not.toBe(3) // 严格相等 (!==)
 | 
				
			||||||
 | 
					expect([1, 2]).toEqual([1, 2]) // 深度相等
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 深度相等
 | 
				
			||||||
 | 
					expect({ a: undefined, b: 2 })
 | 
				
			||||||
 | 
					  .toEqual({ b: 2 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 严格相等 (Jest 23+)
 | 
				
			||||||
 | 
					expect({ a: undefined, b: 2 })
 | 
				
			||||||
 | 
					  .not.toStrictEqual({ b: 2 })
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Using matchers](http://jestjs.io/docs/en/using-matchers), [matchers docs](https://jestjs.io/docs/en/expect)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 真实性
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 匹配 if 语句视为 true 的任何内容
 | 
				
			||||||
 | 
					// (not false、0、''、null、undefined、NaN)
 | 
				
			||||||
 | 
					expect('foo').toBeTruthy()
 | 
				
			||||||
 | 
					// 匹配 if 语句视为 false 的任何内容
 | 
				
			||||||
 | 
					// (false、0、''、null、undefined、NaN)
 | 
				
			||||||
 | 
					expect('').toBeFalsy()
 | 
				
			||||||
 | 
					// 仅匹配 null
 | 
				
			||||||
 | 
					expect(null).toBeNull()
 | 
				
			||||||
 | 
					// 仅匹配未定义
 | 
				
			||||||
 | 
					expect(undefined).toBeUndefined()
 | 
				
			||||||
 | 
					// toBeUndefined 的反义词
 | 
				
			||||||
 | 
					expect(7).toBeDefined()
 | 
				
			||||||
 | 
					// 匹配真假
 | 
				
			||||||
 | 
					expect(true).toEqual(expect.any(Boolean))
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 数字
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 大于
 | 
				
			||||||
 | 
					expect(2).toBeGreaterThan(1)
 | 
				
			||||||
 | 
					// 大于或等于
 | 
				
			||||||
 | 
					expect(1).toBeGreaterThanOrEqual(1)
 | 
				
			||||||
 | 
					// 小于
 | 
				
			||||||
 | 
					expect(1).toBeLessThan(2)
 | 
				
			||||||
 | 
					// 小于或等于
 | 
				
			||||||
 | 
					expect(1).toBeLessThanOrEqual(1)
 | 
				
			||||||
 | 
					// 接近于
 | 
				
			||||||
 | 
					expect(0.2 + 0.1).toBeCloseTo(0.3, 5)
 | 
				
			||||||
 | 
					// 原始值的传递类型
 | 
				
			||||||
 | 
					expect(NaN).toEqual(expect.any(Number))
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 字符串
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 检查字符串是否与正则表达式匹配。
 | 
				
			||||||
 | 
					expect('long string').toMatch('str')
 | 
				
			||||||
 | 
					expect('string').toEqual(expect.any(String))
 | 
				
			||||||
 | 
					expect('coffee').toMatch(/ff/)
 | 
				
			||||||
 | 
					expect('pizza').not.toMatch('coffee')
 | 
				
			||||||
 | 
					expect(['pizza', 'coffee']).toEqual(
 | 
				
			||||||
 | 
					  [
 | 
				
			||||||
 | 
					    expect.stringContaining('zz'), 
 | 
				
			||||||
 | 
					    expect.stringMatching(/ff/)
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 数组
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					expect([]).toEqual(expect.any(Array))
 | 
				
			||||||
 | 
					const exampleArray = [
 | 
				
			||||||
 | 
					  'Alice', 'Bob', 'Eve'
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					expect(exampleArray).toHaveLength(3)
 | 
				
			||||||
 | 
					expect(exampleArray).toContain('Alice')
 | 
				
			||||||
 | 
					expect(exampleArray).toEqual(
 | 
				
			||||||
 | 
					  expect.arrayContaining(['Alice', 'Bob'])
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					expect([{ a: 1 }, { a: 2 }])
 | 
				
			||||||
 | 
					    .toContainEqual({ a: 1 }) // 包含相等
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 对象
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					expect({ a:1 }).toHaveProperty('a')
 | 
				
			||||||
 | 
					expect({ a:1 }).toHaveProperty('a', 1)
 | 
				
			||||||
 | 
					expect({ a: {b:1} }).toHaveProperty('a.b')
 | 
				
			||||||
 | 
					expect({ a:1, b:2 }).toMatchObject({a:1})
 | 
				
			||||||
 | 
					expect({ a:1, b:2 }).toMatchObject({
 | 
				
			||||||
 | 
					  a: expect.any(Number),
 | 
				
			||||||
 | 
					  b: expect.any(Number),
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					expect([{ a: 1 }, { b: 2 }]).toEqual([
 | 
				
			||||||
 | 
					  expect.objectContaining(
 | 
				
			||||||
 | 
					    { a: expect.any(Number) }
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  expect.anything(),
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 模拟函数
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// const fn = jest.fn()
 | 
				
			||||||
 | 
					// const fn = jest.fn().mockName('Unicorn') -- 命名为 mock, Jest 22+
 | 
				
			||||||
 | 
					// 函数被调用
 | 
				
			||||||
 | 
					expect(fn).toBeCalled()
 | 
				
			||||||
 | 
					// 函数*未*调用
 | 
				
			||||||
 | 
					expect(fn).not.toBeCalled()
 | 
				
			||||||
 | 
					// 函数只被调用一次
 | 
				
			||||||
 | 
					expect(fn).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					// 任何执行都带有这些参数
 | 
				
			||||||
 | 
					expect(fn).toBeCalledWith(arg1, arg2)
 | 
				
			||||||
 | 
					// 最后一个执行是用这些参数
 | 
				
			||||||
 | 
					expect(fn).toHaveBeenLastCalledWith(arg1, arg2)
 | 
				
			||||||
 | 
					// 第 N 个执行带有这些参数(Jest 23+)
 | 
				
			||||||
 | 
					expect(fn).toHaveBeenNthCalledWith(callNumber, args)
 | 
				
			||||||
 | 
					// 函数返回没有抛出错误(Jest 23+)
 | 
				
			||||||
 | 
					expect(fn).toHaveReturnedTimes(2)
 | 
				
			||||||
 | 
					// 函数返回一个值(Jest 23+)
 | 
				
			||||||
 | 
					expect(fn).toHaveReturnedWith(value)
 | 
				
			||||||
 | 
					// 最后一个函数调用返回一个值(Jest 23+)
 | 
				
			||||||
 | 
					expect(fn).toHaveLastReturnedWith(value)
 | 
				
			||||||
 | 
					// 第 N 个函数调用返回一个值(Jest 23+)
 | 
				
			||||||
 | 
					expect(fn).toHaveNthReturnedWith(value)
 | 
				
			||||||
 | 
					expect(fn.mock.calls).toEqual([
 | 
				
			||||||
 | 
					  ['first', 'call', 'args'],
 | 
				
			||||||
 | 
					  ['second', 'call', 'args'],
 | 
				
			||||||
 | 
					]) // 多次调用
 | 
				
			||||||
 | 
					// fn.mock.calls[0][0] — 第一次调用的第一个参数
 | 
				
			||||||
 | 
					expect(fn.mock.calls[0][0]).toBe(2)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### 别名
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `toBeCalled` → `toHaveBeenCalled`
 | 
				
			||||||
 | 
					- `toBeCalledWith` → `toHaveBeenCalledWith`
 | 
				
			||||||
 | 
					- `lastCalledWith` → `toHaveBeenLastCalledWith`
 | 
				
			||||||
 | 
					- `nthCalledWith` → `toHaveBeenNthCalledWith`
 | 
				
			||||||
 | 
					- `toReturnTimes` → `toHaveReturnedTimes`
 | 
				
			||||||
 | 
					- `toReturnWith` → `toHaveReturnedWith`
 | 
				
			||||||
 | 
					- `lastReturnedWith` → `toHaveLastReturnedWith`
 | 
				
			||||||
 | 
					- `nthReturnedWith` → `toHaveNthReturnedWith`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 杂项
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 检查对象是否是类的实例。
 | 
				
			||||||
 | 
					expect(new A()).toBeInstanceOf(A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 检查对象是否是函数的实例。
 | 
				
			||||||
 | 
					expect(() => {}).toEqual(
 | 
				
			||||||
 | 
					  expect.any(Function)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 匹配除 null 或 undefined 之外的任何内容
 | 
				
			||||||
 | 
					expect('pizza').toEqual(expect.anything())
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 快照
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 这可确保某个值与最近的快照匹配。
 | 
				
			||||||
 | 
					expect(node).toMatchSnapshot()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Jest 23+
 | 
				
			||||||
 | 
					expect(user).toMatchSnapshot({
 | 
				
			||||||
 | 
					  date: expect.any(Date),
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 确保值与最近的快照匹配。
 | 
				
			||||||
 | 
					expect(user).toMatchInlineSnapshot()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Promise 匹配器(Jest 20+)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('resolve to lemon', () => {
 | 
				
			||||||
 | 
					  // 验证在测试期间是否调用了一定数量的断言。
 | 
				
			||||||
 | 
					  expect.assertions(1)
 | 
				
			||||||
 | 
					  // 确保添加return语句
 | 
				
			||||||
 | 
					  return expect(Promise.resolve('lemon'))
 | 
				
			||||||
 | 
					            .resolves.toBe('lemon')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return expect(Promise.reject('octopus'))
 | 
				
			||||||
 | 
					            .rejects.toBeDefined()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return expect(Promise.reject(
 | 
				
			||||||
 | 
					    Error('pizza')
 | 
				
			||||||
 | 
					  )).rejects.toThrow()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					或者使用 async/await:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('resolve to lemon', async () => {
 | 
				
			||||||
 | 
					  expect.assertions(2)
 | 
				
			||||||
 | 
					  await expect(Promise.resolve('lemon'))
 | 
				
			||||||
 | 
					          .resolves.toBe('lemon')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await expect(Promise.resolve('lemon'))
 | 
				
			||||||
 | 
					          .resolves.not.toBe('octopus')
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[resolves 文档](https://jestjs.io/docs/en/expect#resolves)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 例外
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// const fn = () => {
 | 
				
			||||||
 | 
					//    throw new Error('Out of cheese!')
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expect(fn).toThrow()
 | 
				
			||||||
 | 
					expect(fn).toThrow('Out of cheese')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 测试错误消息在某处说“cheese”:这些是等价的
 | 
				
			||||||
 | 
					expect(fn).toThrowError(/cheese/);
 | 
				
			||||||
 | 
					expect(fn).toThrowError('cheese');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 测试准确的错误信息
 | 
				
			||||||
 | 
					expect(fn).toThrowError(
 | 
				
			||||||
 | 
					  /^Out of cheese!$/
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					expect(fn).toThrowError(
 | 
				
			||||||
 | 
					  new Error('Out of cheese!')
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 测试函数在调用时是否抛出与最新快照匹配的错误。
 | 
				
			||||||
 | 
					expect(fn).toThrowErrorMatchingSnapshot()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### 别名
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `toThrowError` → `toThrow`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					异步测试
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 实例
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					请参阅 Jest 文档中的 [更多示例](https://jestjs.io/docs/en/tutorial-async)。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在异步测试中指定一些预期的断言是一个很好的做法,所以如果你的断言根本没有被调用,测试将会失败。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('async test', () => {
 | 
				
			||||||
 | 
					  // 在测试期间恰好调用了三个断言
 | 
				
			||||||
 | 
					  expect.assertions(3) 
 | 
				
			||||||
 | 
					  // 或者 - 在测试期间至少调用一个断言
 | 
				
			||||||
 | 
					  expect.hasAssertions()
 | 
				
			||||||
 | 
					  // 你的异步测试
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					请注意,您也可以在任何 `describe` 和 `test` 之外对每个文件执行此操作:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					beforeEach(expect.hasAssertions)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					这将验证每个测试用例至少存在一个断言。 它还可以与更具体的 `expect.assertions(3)` 声明配合使用。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### async/await
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('async test', async () => {
 | 
				
			||||||
 | 
					  expect.assertions(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const result = await runAsyncOperation()
 | 
				
			||||||
 | 
					  expect(result).toBe(true)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### done() 回调
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('async test', (done) => {
 | 
				
			||||||
 | 
					  expect.assertions(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  runAsyncOperation()
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  setTimeout(() => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const result = getAsyncOperationResult()
 | 
				
			||||||
 | 
					      expect(result).toBe(true)
 | 
				
			||||||
 | 
					      done()
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      done.fail(err)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					将断言包装在 try/catch 块中,否则 Jest 将忽略失败
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Promises
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('async test', () => {
 | 
				
			||||||
 | 
					  expect.assertions(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return runAsyncOperation().then((result) => {
 | 
				
			||||||
 | 
					    expect(result).toBe(true)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					从你的测试中 _返回_ 一个 Promise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 模拟
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 模拟函数
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('call the callback', () => {
 | 
				
			||||||
 | 
					  const callback = jest.fn()
 | 
				
			||||||
 | 
					  fn(callback)
 | 
				
			||||||
 | 
					  expect(callback).toBeCalled()
 | 
				
			||||||
 | 
					  expect(callback.mock.calls[0][1].baz).toBe('pizza') // 第一次调用的第二个参数
 | 
				
			||||||
 | 
					  // 匹配第一个和最后一个参数,但忽略第二个参数
 | 
				
			||||||
 | 
					  expect(callback).toHaveBeenLastCalledWith('meal', expect.anything(), 'margarita')
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					您还可以使用快照:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test('call the callback', () => {
 | 
				
			||||||
 | 
					  // mockName 在 Jest 22+ 中可用
 | 
				
			||||||
 | 
					  const callback = jest.fn().mockName('Unicorn') 
 | 
				
			||||||
 | 
					  fn(callback)
 | 
				
			||||||
 | 
					  expect(callback).toMatchSnapshot()
 | 
				
			||||||
 | 
					  // ->
 | 
				
			||||||
 | 
					  // [MockFunction Unicorn] {
 | 
				
			||||||
 | 
					  //   "calls": Array [
 | 
				
			||||||
 | 
					  // ...
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					并将实现传递给 `jest.fn` 函数:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const callback = jest.fn(() => true)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[模拟函数文档](https://jestjs.io/docs/en/mock-function-api)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 返回、解析和拒绝值
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					您的模拟可以返回值:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const callback
 | 
				
			||||||
 | 
					    = jest.fn().mockReturnValue(true)
 | 
				
			||||||
 | 
					const callbackOnce
 | 
				
			||||||
 | 
					    = jest.fn().mockReturnValueOnce(true)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					或解析值:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const promise 
 | 
				
			||||||
 | 
					    = jest.fn().mockResolvedValue(true)
 | 
				
			||||||
 | 
					const promiseOnce 
 | 
				
			||||||
 | 
					    = jest.fn().mockResolvedValueOnce(true)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					他们甚至可以拒绝值:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const failedPromise
 | 
				
			||||||
 | 
					    = jest.fn().mockRejectedValue('Error')
 | 
				
			||||||
 | 
					const failedPromiseOnce
 | 
				
			||||||
 | 
					    = jest.fn().mockRejectedValueOnce('Error')
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					你甚至可以结合这些:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const callback
 | 
				
			||||||
 | 
					    = jest.fn().mockReturnValueOnce(false).mockReturnValue(true)
 | 
				
			||||||
 | 
					// ->
 | 
				
			||||||
 | 
					//  call 1: false
 | 
				
			||||||
 | 
					//  call 2+: true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用 `jest.mock` 方法模拟模块
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					jest.mock('lodash/memoize', () => (a) => a) // The original lodash/memoize should exist
 | 
				
			||||||
 | 
					jest.mock('lodash/memoize', () => (a) => a, { virtual: true }) // The original lodash/memoize isn’t required
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[jest.mock docs](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> 注意:当使用 `babel-jest` 时,对 `jest.mock` 的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 `jest.doMock`。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用模拟文件模拟模块
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					创建一个类似 `__mocks__/lodash/memoize.js` 的文件:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					module.exports = (a) => a
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					添加到您的测试中:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					jest.mock('lodash/memoize')
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					注意:当使用 `babel-jest` 时,对 `jest.mock` 的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 `jest.doMock`。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[手动模拟文档](https://jestjs.io/docs/en/manual-mocks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 模拟对象方法
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const spy = jest.spyOn(console, 'log').mockImplementation(() => {})
 | 
				
			||||||
 | 
					expect(console.log.mock.calls).toEqual([['dope'], ['nope']])
 | 
				
			||||||
 | 
					spy.mockRestore()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const spy = jest.spyOn(ajax, 'request').mockImplementation(() => Promise.resolve({ success: true }))
 | 
				
			||||||
 | 
					expect(spy).toHaveBeenCalled()
 | 
				
			||||||
 | 
					spy.mockRestore()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 模拟 getter 和 setter (Jest 22.1.0+)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const location = {}
 | 
				
			||||||
 | 
					const getTitle = jest
 | 
				
			||||||
 | 
					    .spyOn(location, 'title', 'get')
 | 
				
			||||||
 | 
					    .mockImplementation(() => 'pizza')
 | 
				
			||||||
 | 
					const setTitle = jest
 | 
				
			||||||
 | 
					    .spyOn(location, 'title', 'set')
 | 
				
			||||||
 | 
					    .mockImplementation(() => {})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 定时器模拟
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-3-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					为使用本机计时器函数(`setTimeout`、`setInterval`、`clearTimeout`、`clearInterval`)的代码编写同步测试。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 启用假计时器
 | 
				
			||||||
 | 
					jest.useFakeTimers()
 | 
				
			||||||
 | 
					test('kill the time', () => {
 | 
				
			||||||
 | 
					  const callback = jest.fn()
 | 
				
			||||||
 | 
					  // 运行一些使用 setTimeout 或 setInterval 的代码
 | 
				
			||||||
 | 
					  const actual = someFunctionThatUseTimers(callback)
 | 
				
			||||||
 | 
					  // 快进直到所有定时器都执行完毕
 | 
				
			||||||
 | 
					  jest.runAllTimers()
 | 
				
			||||||
 | 
					  // 同步检查结果
 | 
				
			||||||
 | 
					  expect(callback).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					或者使用 [advanceTimersByTime()](https://jestjs.io/docs/en/timer-mocks#advance-timers-by-time) 按时间调整计时器:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 启用假计时器
 | 
				
			||||||
 | 
					jest.useFakeTimers()
 | 
				
			||||||
 | 
					test('kill the time', () => {
 | 
				
			||||||
 | 
					  const callback = jest.fn()
 | 
				
			||||||
 | 
					  // 运行一些使用 setTimeout 或 setInterval 的代码
 | 
				
			||||||
 | 
					  const actual = someFunctionThatUseTimers(callback)
 | 
				
			||||||
 | 
					  // 快进 250 毫秒
 | 
				
			||||||
 | 
					  jest.advanceTimersByTime(250)
 | 
				
			||||||
 | 
					  // 同步检查结果
 | 
				
			||||||
 | 
					  expect(callback).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					对于特殊情况,请使用 [jest.runOnlyPendingTimers()](https://jestjs.io/docs/en/timer-mocks#run-pending-timers)。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**注意:** 您应该在测试用例中调用 `jest.useFakeTimers()` 以使用其他假计时器方法。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 模拟 getters 和 setters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const getTitle = jest.fn(() => 'pizza')
 | 
				
			||||||
 | 
					const setTitle = jest.fn()
 | 
				
			||||||
 | 
					const location = {}
 | 
				
			||||||
 | 
					Object.defineProperty(location, 'title', {
 | 
				
			||||||
 | 
					  get: getTitle,
 | 
				
			||||||
 | 
					  set: setTitle,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 清除和恢复模拟
 | 
				
			||||||
 | 
					<!--rehype:warp-class=row-span-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--rehype:-->
 | 
				
			||||||
 | 
					对于一个模拟
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 清除模拟使用日期
 | 
				
			||||||
 | 
					// (fn.mock.calls、fn.mock.instances)
 | 
				
			||||||
 | 
					fn.mockClear()
 | 
				
			||||||
 | 
					// 清除并删除任何模拟的返回值或实现
 | 
				
			||||||
 | 
					fn.mockReset()
 | 
				
			||||||
 | 
					// 重置并恢复初始实现
 | 
				
			||||||
 | 
					fn.mockRestore()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> 注意:`mockRestore` 仅适用于由`jest.spyOn` 创建的模拟。对于所有模拟:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// 清除所有 mock 的 
 | 
				
			||||||
 | 
					// mock.calls、mock.instances、
 | 
				
			||||||
 | 
					// mock.contexts 和 mock.results 属性。
 | 
				
			||||||
 | 
					jest.clearAllMocks()
 | 
				
			||||||
 | 
					// 重置所有模拟的状态。
 | 
				
			||||||
 | 
					jest.resetAllMocks()
 | 
				
			||||||
 | 
					// 将所有模拟恢复到其原始值。
 | 
				
			||||||
 | 
					jest.restoreAllMocks()
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用模拟时访问原始模块
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					jest.mock('fs')
 | 
				
			||||||
 | 
					// 模拟模块
 | 
				
			||||||
 | 
					const fs = require('fs')
 | 
				
			||||||
 | 
					// 原始模块
 | 
				
			||||||
 | 
					const fs = require.requireActual('fs')
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					数据驱动测试(Jest 23+)
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用不同的数据运行相同的测试
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test.each([
 | 
				
			||||||
 | 
					  [1, 1, 2],
 | 
				
			||||||
 | 
					  [1, 2, 3],
 | 
				
			||||||
 | 
					  [2, 1, 3],
 | 
				
			||||||
 | 
					])('.add(%s, %s)', (a, b, expected) => {
 | 
				
			||||||
 | 
					  expect(a + b).toBe(expected)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用模板文字相同
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					test.each`
 | 
				
			||||||
 | 
					  a    | b    | expected
 | 
				
			||||||
 | 
					  ${1} | ${1} | ${2}
 | 
				
			||||||
 | 
					  ${1} | ${2} | ${3}
 | 
				
			||||||
 | 
					  ${2} | ${1} | ${3}
 | 
				
			||||||
 | 
					`('returns $expected when $a is added $b', ({ a, b, expected }) => {
 | 
				
			||||||
 | 
					  expect(a + b).toBe(expected)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 或在“describe”级别
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					describe.each([
 | 
				
			||||||
 | 
					  ['mobile'], ['tablet'], ['desktop']
 | 
				
			||||||
 | 
					])('checkout flow on %s', (viewport) => {
 | 
				
			||||||
 | 
					  test('displays success page', () => {
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[describe.each() 文档](https://jestjs.io/docs/en/api#describeeachtablename-fn-timeout)、[test.each() 文档](https://jestjs.io/docs/en/api#testeachtablename-fn-timeout),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					跳过测试
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<!--rehype:body-class=cols-2-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 不要运行这些测试
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					describe.skip('makePoniesPink'...
 | 
				
			||||||
 | 
					tests.skip('make each pony pink'...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 仅运行以下测试
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					describe.only('makePoniesPink'...
 | 
				
			||||||
 | 
					tests.only('make each pony pink'...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					测试有副作用的模块
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<!--rehype:body-class=cols-1-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 实例
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					const modulePath = '../module-to-test'
 | 
				
			||||||
 | 
					afterEach(() => {
 | 
				
			||||||
 | 
					  jest.resetModules()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					test('第一次测试', () => {
 | 
				
			||||||
 | 
					  // 准备第一次测试的条件
 | 
				
			||||||
 | 
					  const result = require(modulePath)
 | 
				
			||||||
 | 
					  expect(result).toMatchSnapshot()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					test('第二个文本', () => {
 | 
				
			||||||
 | 
					  // 准备第二次测试的条件
 | 
				
			||||||
 | 
					  const fn = () => require(modulePath)
 | 
				
			||||||
 | 
					  expect(fn).toThrow()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Node.js 和 Jest 会缓存你需要的模块。 要测试具有副作用的模块,您需要在测试之间重置模块注册表
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
JSON
 | 
					JSON 备忘清单
 | 
				
			||||||
===
 | 
					===
 | 
				
			||||||
 | 
					
 | 
				
			||||||
这是理解和编写 JSON 格式配置文件的快速参考备忘单。
 | 
					这是理解和编写 JSON 格式配置文件的快速参考备忘单。
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,8 @@
 | 
				
			|||||||
  "description": "为开发人员分享快速参考备忘单(主要是方便自己)。",
 | 
					  "description": "为开发人员分享快速参考备忘单(主要是方便自己)。",
 | 
				
			||||||
  "private": false,
 | 
					  "private": false,
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "build": "node scripts/build.mjs"
 | 
					    "build": "node scripts/build.mjs",
 | 
				
			||||||
 | 
					    "start": "node scripts/build.mjs"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
    "type": "git",
 | 
					    "type": "git",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import rehypeFormat from 'rehype-format';
 | 
				
			|||||||
import { rehypeUrls } from './nodes/rehypeUrls.mjs';
 | 
					import { rehypeUrls } from './nodes/rehypeUrls.mjs';
 | 
				
			||||||
import { htmlTagAddAttri } from './nodes/htmlTagAddAttri.mjs';
 | 
					import { htmlTagAddAttri } from './nodes/htmlTagAddAttri.mjs';
 | 
				
			||||||
import { footer } from './nodes/footer.mjs';
 | 
					import { footer } from './nodes/footer.mjs';
 | 
				
			||||||
 | 
					import { header } from './nodes/header.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 标记 Number */
 | 
					/** 标记 Number */
 | 
				
			||||||
function panelAddNumber(arr = [], result = []) {
 | 
					function panelAddNumber(arr = [], result = []) {
 | 
				
			||||||
@@ -113,25 +114,30 @@ export function getTocsTree(arr = [], result = []) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function create(str = '', options = {}) {
 | 
					export function create(str = '', options = {}) {
 | 
				
			||||||
 | 
					  let title = str.match(/[^===]+(?=[===])/g);
 | 
				
			||||||
 | 
					  let description = str.match(/\n==={1,}\n+([\s\S]*?)\n/g);
 | 
				
			||||||
 | 
					  title = title[0] || '';
 | 
				
			||||||
 | 
					  description = (description[0] || '').replace(/^\n[=\n]+/, '').replace(/\[([\s\S]*?)?\]\(([\s\S]*?)?\)/g, '$1').replace(/\n/, '');
 | 
				
			||||||
  const mdOptions = {
 | 
					  const mdOptions = {
 | 
				
			||||||
    hastNode: false,
 | 
					    hastNode: false,
 | 
				
			||||||
    remarkPlugins: [],
 | 
					    remarkPlugins: [],
 | 
				
			||||||
    rehypePlugins: [
 | 
					    rehypePlugins: [
 | 
				
			||||||
      rehypeFormat,
 | 
					      rehypeFormat,
 | 
				
			||||||
      [rehypeDocument, {
 | 
					      [rehypeDocument, {
 | 
				
			||||||
        title: "Quick Reference",
 | 
					        title: `${title ? `${title} & ` : ''} Quick Reference`,
 | 
				
			||||||
        css: [ ...options.css ],
 | 
					        css: [ ...options.css ],
 | 
				
			||||||
        meta: [
 | 
					        meta: [
 | 
				
			||||||
          { description: '为开发人员分享快速参考备忘单。' },
 | 
					          { description: `${description}为开发人员分享快速参考备忘单。` },
 | 
				
			||||||
          { keywords: 'Quick,Reference' }
 | 
					          { keywords: 'Quick,Reference' }
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }],
 | 
					      }],
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    rewrite: (node, index, parent) => {
 | 
					    rewrite: (node, index, parent) => {
 | 
				
			||||||
      htmlTagAddAttri(node);
 | 
					      htmlTagAddAttri(node, options);
 | 
				
			||||||
      rehypeUrls(node);
 | 
					      rehypeUrls(node);
 | 
				
			||||||
      if (node.type === 'element' && node.tagName === 'body') {
 | 
					      if (node.type === 'element' && node.tagName === 'body') {
 | 
				
			||||||
        node.children = getTocsTree([ ...node.children ]);
 | 
					        node.children = getTocsTree([ ...node.children ]);
 | 
				
			||||||
 | 
					        node.children.unshift(header(options));
 | 
				
			||||||
        node.children.push(footer());
 | 
					        node.children.push(footer());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,13 +15,18 @@ async function createHTML(files = [], num = 0) {
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ++num;
 | 
					  ++num;
 | 
				
			||||||
 | 
					  const githubURL = `https://github.com/jaywcjlove/reference/blob/main/${path.relative(process.cwd(), dataFile.path).replace(path.sep, '/')}`;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  const mdstr = await fs.readFile(dataFile.path);
 | 
					  const mdstr = await fs.readFile(dataFile.path);
 | 
				
			||||||
  const htmlPath = path.relative(DOCS, dataFile.path);
 | 
					  const htmlPath = path.relative(DOCS, dataFile.path);
 | 
				
			||||||
  const outputHTMLPath = path.resolve(OUTOUT, 'docs', htmlPath).replace(/README.md$/i, 'index.html').replace(/.md$/, '.html');
 | 
					  const outputHTMLPath = path.resolve(OUTOUT, 'docs', htmlPath).replace(/README.md$/i, 'index.html').replace(/.md$/, '.html');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await fs.ensureDir(path.dirname(outputHTMLPath));
 | 
					  await fs.ensureDir(path.dirname(outputHTMLPath));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const html = create(mdstr.toString(), {
 | 
					  const html = create(mdstr.toString(), {
 | 
				
			||||||
 | 
					    isHome: /README.md$/.test(path.relative(process.cwd(), dataFile.path)),
 | 
				
			||||||
 | 
					    githubURL,
 | 
				
			||||||
 | 
					    homePath: path.relative(path.dirname(outputHTMLPath), path.resolve(OUTOUT, 'index.html')),
 | 
				
			||||||
    css: [path.relative(path.dirname(outputHTMLPath), CSS_OUTPUT_PATH)]
 | 
					    css: [path.relative(path.dirname(outputHTMLPath), CSS_OUTPUT_PATH)]
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  await fs.writeFile(outputHTMLPath, html);
 | 
					  await fs.writeFile(outputHTMLPath, html);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										82
									
								
								scripts/nodes/header.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								scripts/nodes/header.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					import { logo, github, editor } from './logo.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function header({ homePath, githubURL = '' }) {
 | 
				
			||||||
 | 
					  const data = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      href: githubURL,
 | 
				
			||||||
 | 
					      target: '__blank',
 | 
				
			||||||
 | 
					      label: '编辑',
 | 
				
			||||||
 | 
					      children: [editor]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      href: 'https://github.com/jaywcjlove/reference',
 | 
				
			||||||
 | 
					      target: '__blank',
 | 
				
			||||||
 | 
					      children: [github]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: 'element',
 | 
				
			||||||
 | 
					    tagName: 'nav',
 | 
				
			||||||
 | 
					    properties: {
 | 
				
			||||||
 | 
					      class: 'header-nav',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    children: [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        type: 'element',
 | 
				
			||||||
 | 
					        tagName: 'div',
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					          class: ['max-container'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            type: 'element',
 | 
				
			||||||
 | 
					            tagName: 'a',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              href: homePath,
 | 
				
			||||||
 | 
					              class: ['logo'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            children: logo,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            type: 'element',
 | 
				
			||||||
 | 
					            tagName: 'div',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              class: ['menu'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            children: data.map(({ href, label, children = [], ...props }) => {
 | 
				
			||||||
 | 
					              const childs = {
 | 
				
			||||||
 | 
					                type: 'element',
 | 
				
			||||||
 | 
					                tagName: 'a',
 | 
				
			||||||
 | 
					                properties: { href, class: [], ...props },
 | 
				
			||||||
 | 
					                children: [
 | 
				
			||||||
 | 
					                  ...children,
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    type: 'element',
 | 
				
			||||||
 | 
					                    tagName: 'span',
 | 
				
			||||||
 | 
					                    properties: {},
 | 
				
			||||||
 | 
					                    children: label ? [
 | 
				
			||||||
 | 
					                      { type: 'text', value: label }
 | 
				
			||||||
 | 
					                    ] : []
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              if (label) {
 | 
				
			||||||
 | 
					                childs.children = [...children, {
 | 
				
			||||||
 | 
					                  type: 'element',
 | 
				
			||||||
 | 
					                  tagName: 'span',
 | 
				
			||||||
 | 
					                  properties: {},
 | 
				
			||||||
 | 
					                  children: [
 | 
				
			||||||
 | 
					                    { type: 'text', value: label }
 | 
				
			||||||
 | 
					                  ]
 | 
				
			||||||
 | 
					                }];
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                childs.children = children;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return childs
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
export function htmlTagAddAttri(node) {
 | 
					export function htmlTagAddAttri(node, { isHome }) {
 | 
				
			||||||
  if (node && node.tagName === 'html') {
 | 
					  if (node && node.tagName === 'html') {
 | 
				
			||||||
    node.properties['data-color-mode'] = 'dark';
 | 
					    node.properties['data-color-mode'] = 'dark';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (node && node.tagName === 'body' && isHome) {
 | 
				
			||||||
 | 
					    node.properties.class = ['home'];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										99
									
								
								scripts/nodes/logo.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								scripts/nodes/logo.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					export const logo = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    type: 'element',
 | 
				
			||||||
 | 
					    tagName: 'svg',
 | 
				
			||||||
 | 
					    properties: {
 | 
				
			||||||
 | 
					      viewBox: "0 0 24 24",
 | 
				
			||||||
 | 
					      fill: "none",
 | 
				
			||||||
 | 
					      xmlns: "http://www.w3.org/2000/svg",
 | 
				
			||||||
 | 
					      height: "1em",
 | 
				
			||||||
 | 
					      width: "1em"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    children: [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        type: 'element',
 | 
				
			||||||
 | 
					        tagName: 'path',
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					          opacity: ".5",
 | 
				
			||||||
 | 
					          d: "m21.66 10.44-.98 4.18c-.84 3.61-2.5 5.07-5.62 4.77-.5-.04-1.04-.13-1.62-.27l-1.68-.4c-4.17-.99-5.46-3.05-4.48-7.23l.98-4.19c.2-.85.44-1.59.74-2.2 1.17-2.42 3.16-3.07 6.5-2.28l1.67.39c4.19.98 5.47 3.05 4.49 7.23Z",
 | 
				
			||||||
 | 
					          fill: "white"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        type: 'element',
 | 
				
			||||||
 | 
					        tagName: 'path',
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					          d: "M15.06 19.39c-.62.42-1.4.77-2.35 1.08l-1.58.52c-3.97 1.28-6.06.21-7.35-3.76L2.5 13.28c-1.28-3.97-.22-6.07 3.75-7.35l1.58-.52c.41-.13.8-.24 1.17-.31-.3.61-.54 1.35-.74 2.2l-.98 4.19c-.98 4.18.31 6.24 4.48 7.23l1.68.4c.58.14 1.12.23 1.62.27Zm2.43-8.88c-.06 0-.12-.01-.19-.02l-4.85-1.23a.75.75 0 0 1 .37-1.45l4.85 1.23a.748.748 0 0 1-.18 1.47Z" ,
 | 
				
			||||||
 | 
					          fill: "#cbd5e1"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        type: 'element',
 | 
				
			||||||
 | 
					        tagName: 'path',
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					          d: "M14.56 13.89c-.06 0-.12-.01-.19-.02l-2.91-.74a.75.75 0 0 1 .37-1.45l2.91.74c.4.1.64.51.54.91-.08.34-.38.56-.72.56Z",
 | 
				
			||||||
 | 
					          fill: "#292D32"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    type: 'element',
 | 
				
			||||||
 | 
					    tagName: 'span',
 | 
				
			||||||
 | 
					    properties: {
 | 
				
			||||||
 | 
					      class: ['title'],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    children: [
 | 
				
			||||||
 | 
					      { type: 'text', value: 'Quick Reference' }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const github = {
 | 
				
			||||||
 | 
					  type: 'element',
 | 
				
			||||||
 | 
					  tagName: 'svg',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    viewBox: "0 0 16 16",
 | 
				
			||||||
 | 
					    fill: "currentColor",
 | 
				
			||||||
 | 
					    height: "1em",
 | 
				
			||||||
 | 
					    width: "1em"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  children: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'element',
 | 
				
			||||||
 | 
					      tagName: 'path',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const editor = {
 | 
				
			||||||
 | 
					  type: 'element',
 | 
				
			||||||
 | 
					  tagName: 'svg',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    viewBox: "0 0 36 36",
 | 
				
			||||||
 | 
					    fill: "currentColor",
 | 
				
			||||||
 | 
					    height: "1em",
 | 
				
			||||||
 | 
					    width: "1em"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  children: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'element',
 | 
				
			||||||
 | 
					      tagName: 'path',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        d: "m33 6.4-3.7-3.7a1.71 1.71 0 0 0-2.36 0L23.65 6H6a2 2 0 0 0-2 2v22a2 2 0 0 0 2 2h22a2 2 0 0 0 2-2V11.76l3-3a1.67 1.67 0 0 0 0-2.36ZM18.83 20.13l-4.19.93 1-4.15 9.55-9.57 3.23 3.23ZM29.5 9.43 26.27 6.2l1.85-1.85 3.23 3.23Z",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'element',
 | 
				
			||||||
 | 
					      tagName: 'path',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        fill: 'none',
 | 
				
			||||||
 | 
					        d: "M0 0h36v36H0z",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
export function rehypeUrls(node) {
 | 
					export function rehypeUrls(node) {
 | 
				
			||||||
  if (node.type === 'element' && node.properties.href && /.md/.test(node.properties.href)) {
 | 
					  if (node.type === 'element' && node.properties.href && /.md/.test(node.properties.href) && !/^(https?:\/\/)/.test(node.properties.href)) {
 | 
				
			||||||
    let href = node.properties.href;
 | 
					    let href = node.properties.href;
 | 
				
			||||||
    node.properties.href = href.replace(/([^\.\/\\]+)\.(md|markdown)/gi, '$1.html');
 | 
					    node.properties.href = href.replace(/([^\.\/\\]+)\.(md|markdown)/gi, '$1.html');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,10 +45,115 @@ blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre {
 | 
				
			|||||||
  padding: 0.75rem;
 | 
					  padding: 0.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body.home .h1warp-body, body.home .h1warp .warp-body {
 | 
				
			||||||
 | 
					  max-width: 940px;
 | 
				
			||||||
 | 
					  margin-left: auto;
 | 
				
			||||||
 | 
					  margin-right: auto;
 | 
				
			||||||
 | 
					  padding: 0.75rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					body.home .h2warp > h2 {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  padding-right: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body.home .h2warp > h2::after {
 | 
				
			||||||
 | 
					  content: ' ';
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  height: 3px;
 | 
				
			||||||
 | 
					  width: 110%;
 | 
				
			||||||
 | 
					  margin-top: 0.5rem;
 | 
				
			||||||
 | 
					  --bg-opacity: 1;
 | 
				
			||||||
 | 
					  background-color: rgb(30 41 59/var(--bg-opacity));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					body.home .h1warp .warp-body p + p {
 | 
				
			||||||
 | 
					  margin-top: 1.6rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					body.home .h1warp .warp-body p {
 | 
				
			||||||
 | 
					  text-indent: 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body.home .h1warp p {
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.home-card {
 | 
				
			||||||
 | 
					  display: grid;
 | 
				
			||||||
 | 
					  gap: 2rem;
 | 
				
			||||||
 | 
					  grid-template-columns: repeat(4,minmax(0,1fr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.home-card a {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  border-radius: 0.5rem;
 | 
				
			||||||
 | 
					  padding: 1rem;
 | 
				
			||||||
 | 
					  color: rgb(30 41 59/1);
 | 
				
			||||||
 | 
					  box-shadow: 0 0 #0000,0 0 #0000,0 1px 2px 0 rgba(0,0,0,0.05);
 | 
				
			||||||
 | 
					  color: rgb(203 213 225/1);
 | 
				
			||||||
 | 
					  --bg-opacity: 0.5;
 | 
				
			||||||
 | 
					  background-color: rgb(62 69 72/var(--bg-opacity));
 | 
				
			||||||
 | 
					  transition: all .3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.home-card a:hover {
 | 
				
			||||||
 | 
					  --bg-opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .max-container {
 | 
				
			||||||
 | 
					  padding-top: 1.8rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .max-container, .header-nav .logo, .header-nav .menu, .header-nav .menu a {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .logo {
 | 
				
			||||||
 | 
					  gap: 0.5rem;
 | 
				
			||||||
 | 
					  color: currentColor;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .title {
 | 
				
			||||||
 | 
					  font-size: 1.8rem;
 | 
				
			||||||
 | 
					  line-height: 2rem;
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .logo svg {
 | 
				
			||||||
 | 
					  font-size: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .menu a:hover {
 | 
				
			||||||
 | 
					  background-color: rgb(30 41 59/1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .menu a {
 | 
				
			||||||
 | 
					  padding-left: 0.75rem;
 | 
				
			||||||
 | 
					  padding-right: 0.75rem;
 | 
				
			||||||
 | 
					  padding-top: 0.5rem;
 | 
				
			||||||
 | 
					  padding-bottom: 0.5rem;
 | 
				
			||||||
 | 
					  border-radius: 9999px;
 | 
				
			||||||
 | 
					  transition: all .3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav .menu {
 | 
				
			||||||
 | 
					  gap: 0.2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.header-nav .menu a > span {
 | 
				
			||||||
 | 
					  font-size: 0.9rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header-nav a, .header-nav a:visited {
 | 
				
			||||||
 | 
					  color: rgb(209 213 219/1);
 | 
				
			||||||
 | 
					  line-height: 1.2;
 | 
				
			||||||
 | 
					  gap: 0.3rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.warp-header.h1warp {
 | 
					.warp-header.h1warp {
 | 
				
			||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
  margin-top: 2rem;
 | 
					  margin-top: 4.6rem;
 | 
				
			||||||
  margin-bottom: 2rem;
 | 
					  margin-bottom: 5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.warp-header.h1warp .warp-body {
 | 
					.warp-header.h1warp .warp-body {
 | 
				
			||||||
@@ -58,7 +163,7 @@ blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre {
 | 
				
			|||||||
.warp-header.h1warp > h1 {
 | 
					.warp-header.h1warp > h1 {
 | 
				
			||||||
  font-size: 3rem;
 | 
					  font-size: 3rem;
 | 
				
			||||||
  line-height: 1;
 | 
					  line-height: 1;
 | 
				
			||||||
  margin-bottom: 1rem;
 | 
					  margin-bottom: 3rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h1warp-body {
 | 
					.h1warp-body {
 | 
				
			||||||
@@ -145,8 +250,6 @@ blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  margin: 0px;
 | 
					  margin: 0px;
 | 
				
			||||||
  border-top-width: 1px;
 | 
					  border-top-width: 1px;
 | 
				
			||||||
  /* border-color: rgb(226 232 240/1);
 | 
					 | 
				
			||||||
  background-color: rgb(241 245 249/1); */
 | 
					 | 
				
			||||||
  padding-left: 1rem;
 | 
					  padding-left: 1rem;
 | 
				
			||||||
  padding-right: 1rem;
 | 
					  padding-right: 1rem;
 | 
				
			||||||
  padding-top: 0.25rem;
 | 
					  padding-top: 0.25rem;
 | 
				
			||||||
@@ -174,21 +277,16 @@ ol, ul, menu {
 | 
				
			|||||||
  grid-template-columns: repeat(1,minmax(0,1fr));
 | 
					  grid-template-columns: repeat(1,minmax(0,1fr));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h4warp > .warp-body ul,
 | 
					.h2warp-body ul li,
 | 
				
			||||||
.h4warp > .warp-body ol,
 | 
					.h2warp-body ol li,
 | 
				
			||||||
.h4warp > .warp-body dl,
 | 
					.h2warp-body dl li {
 | 
				
			||||||
 | 
					 | 
				
			||||||
.h3warp > .warp-body ul li,
 | 
					 | 
				
			||||||
.h3warp > .warp-body ol li,
 | 
					 | 
				
			||||||
.h3warp > .warp-body dl li {
 | 
					 | 
				
			||||||
  padding: 9px;
 | 
					  padding: 9px;
 | 
				
			||||||
  padding-left: 26px;
 | 
					  padding-left: 26px;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  border-bottom: solid 1px rgb(51 65 85/0.5);
 | 
					  border-bottom: solid 1px rgb(51 65 85/0.5);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h4warp>.warp-body ul:not(.style-none)>li::before,
 | 
					.h2warp-body ul:not(.style-none)>li::before {
 | 
				
			||||||
.h3warp>.warp-body ul:not(.style-none)>li::before {
 | 
					 | 
				
			||||||
  content: '';
 | 
					  content: '';
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
@@ -196,10 +294,24 @@ ol, ul, menu {
 | 
				
			|||||||
  height: 4px;
 | 
					  height: 4px;
 | 
				
			||||||
  background: #556677;
 | 
					  background: #556677;
 | 
				
			||||||
  border-radius: 50%;
 | 
					  border-radius: 50%;
 | 
				
			||||||
  left: 14px;
 | 
					  left: 13px;
 | 
				
			||||||
  top: 18px;
 | 
					  top: 18px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.h2warp-body ul li,
 | 
				
			||||||
 | 
					.h2warp-body ol li,
 | 
				
			||||||
 | 
					.h2warp-body dl li {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.warp-body ul:last-child {
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.warp-body ul:last-child li:last-child {
 | 
				
			||||||
 | 
					  border-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h3warp hr {
 | 
					.h3warp hr {
 | 
				
			||||||
  border-bottom: 1px solid #475060;
 | 
					  border-bottom: 1px solid #475060;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -301,6 +413,9 @@ pre {
 | 
				
			|||||||
  overflow-x: auto;
 | 
					  overflow-x: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.cols-1 {
 | 
				
			||||||
 | 
					  grid-template-columns: repeat(1,minmax(0,1fr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.cols-2 {
 | 
					.cols-2 {
 | 
				
			||||||
  grid-template-columns: repeat(2,minmax(0,1fr));
 | 
					  grid-template-columns: repeat(2,minmax(0,1fr));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user