Commit graph

1615 commits

Author SHA1 Message Date
camchenry
2b17003e0b perf(linter, prettier, diagnostics): use FxHashMap instead of std::collections::HashMap (#5993)
Using `FxHashMap` is faster than `HashMap` in many cases, especially for hashing-heavy workloads. This change improves the performance of the linter, prettier, and diagnostics crates by using `FxHashMap` instead of `std::collections::HashMap`.
2024-09-23 16:29:05 +00:00
Boshen
4771492b6a fix(linter): fix import/no_cycle with ignoreTypes (#5995)
related https://github.com/toeverything/AFFiNE/issues/7580

`iter().all` produces `true` on an empty list.
2024-09-23 15:10:53 +00:00
oxc-bot
859227e457
release(oxlint): v0.9.7 (#5988)
## [0.9.7] - 2024-09-23

### Features

- d24985e linter: Add `oxc-security/api-keys` (#5906) (DonIsaac)
- f9b44c5 linter: Add unicode sets support to `no-useless-escape` rule
(#5974) (camchenry)
- 0f19848 linter: Implement `no-unexpected-multiline` rule (#5911)
(camchenry)
- 16fe383 linter: Implement `no-extend-native` rule (#5867) (Cam
McHenry)

### Bug Fixes

- eed9ac7 linter: Include actual span size in `no-regex-spaces`
diagnostic (#5957) (camchenry)
- 40c89c2 linter: Move `promise/avoid-new` to style category (#5961)
(DonIsaac)

### Performance

- 608d637 linter: Use `aho-corasick` instead of `regex` for string
matching in `jsx-a11y/img-redundant-alt` (#5892) (camchenry)
- 3148d4b linter: Check file path after checking node kind for
`nextjs/no-head-element` (#5868) (Cam McHenry)

### Refactor

- 0a5a4a9 linter: Use parsed patterns for `unicorn/no-hex-escape`
(#5985) (camchenry)
- 2cf2edd linter: Use parsed patterns in `no-empty-character-class` rule
(#5980) (camchenry)
- a9a8e2a linter: Use regex parser in `eslint/no-regex-spaces` (#5952)
(camchenry)
- 05f592b linter: Use parsed patterns in
`unicorn/prefer-string-starts-ends-with` (#5949) (camchenry)
- 3273b64 linter: Use parsed patterns for
`unicorn/prefer-string-replace-all` rule (#5943) (camchenry)
- ba7b01f linter: Add `LinterBuilder` (#5714) (DonIsaac)
- db4f16a semantic: Call `with_trivias` before `build_with_jsdoc`
(#5875) (Boshen)
- 3d13c6d semantic: Impl `IntoIterator` for `&AstNodes` (#5873)
(DonIsaac)

### Testing

- b681c9a linter: Import test cases for `no-empty-character-class`
(#5981) (camchenry)
- 767602b linter: Add regression test for #5227 (#5975) (camchenry)

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2024-09-23 15:10:33 +08:00
camchenry
0a5a4a9812 refactor(linter): use parsed patterns for unicorn/no-hex-escape (#5985)
- part of https://github.com/oxc-project/oxc/issues/5416

This pull request includes significant improvements to the `no_hex_escape` rule in the `oxc_linter` crate. The changes enhance the detection and replacement of hexadecimal escapes within regular expressions by introducing a more comprehensive AST traversal.

  - Implemented a new `visit_terms` function and its helper functions to traverse the regex AST and apply checks on individual terms.
  - Introduced the `check_character` function to replace hexadecimal escapes with Unicode escapes within regex patterns.
  - Updated snapshots to reflect the new diagnostic messages and replacements for hexadecimal escapes in regex patterns.
2024-09-23 01:11:02 +00:00
camchenry
b681c9a12c test(linter): import test cases for no-empty-character-class (#5981)
improved test coverage for this rule by importing the newer test cases from 27383226b8/tests/lib/rules/no-empty-character-class.js (L4)
2024-09-23 00:33:29 +00:00
camchenry
2cf2edd6bf refactor(linter): use parsed patterns in no-empty-character-class rule (#5980)
- part of https://github.com/oxc-project/oxc/issues/5416

Uses the parsed regular expression patterns for detecting empty character classes. This is more robust than the handwritten pattern matching from before and allows us to provide more accurate diagnostics and actually point to the empty character class in the literal.
2024-09-23 00:33:29 +00:00
DonIsaac
d24985ed51 feat(linter): add oxc-security/api-keys (#5906)
## What This PR does
Adds a new `oxc-security/api-key` rule that scans for hard-coded API keys.

It is broken up into "secret rules", where each one is responsible for finding a different kind of key. It is architecturally identical to how lint rules themselves. This PR also includes the first of these rules, for AWS access key IDs.

Logic and rules are based on [keyhunter](https://github.com/Donisaac/keyhunter). I've licensed that repo under GNU GPLv3, but it's my code and I can do what I want with it 😈 (read: I'm fine with it being MIT for oxc).

This PR is a complete feature in its own right, but does not represent the end of this work. See https://github.com/oxc-project/backlog/issues/116 to track overall progress.
2024-09-22 22:39:56 +00:00
camchenry
767602b56d test(linter): add regression test for #5227 (#5975)
- closes https://github.com/oxc-project/oxc/issues/5227
2024-09-22 22:25:25 +00:00
camchenry
f9b44c5738 feat(linter): add unicode sets support to no-useless-escape rule (#5974)
- part of https://github.com/oxc-project/oxc/issues/5416

Replaces the handwritten regex parsing logic with the `oxc_regular_expression` parser, which should be more accurate and enables support for unicode sets.
2024-09-22 21:19:15 +00:00
camchenry
eed9ac7c46 fix(linter): include actual span size in no-regex-spaces diagnostic (#5957) 2024-09-22 04:45:40 +00:00
camchenry
a9a8e2ad41 refactor(linter): use regex parser in eslint/no-regex-spaces (#5952)
- part of https://github.com/oxc-project/oxc/issues/5416

Use the `oxc_regular_expression` parser to make these checks more robust. a few snapshots are updated because they now output more accurate diagnostics based on the regex AST. for example, `/   ?/` now correctly only highlights two spaces rather than three (because the last one is part of a quantifier)
2024-09-22 04:33:46 +00:00
DonIsaac
40c89c2c5f fix(linter): move promise/avoid-new to style category (#5961) 2024-09-22 04:25:48 +00:00
camchenry
05f592b834 refactor(linter): Use parsed patterns in unicorn/prefer-string-starts-ends-with (#5949)
- part of https://github.com/oxc-project/oxc/issues/5416

This change enhances the accuracy of the `prefer_string_starts_ends_with` rule by using the parsed regex patterns for analysis. It allows for more precise detection of patterns that can be replaced with `startsWith()` and `endsWith()` methods, reducing false positives and improving the overall effectiveness of the linter.

### What changed?

- Replaced the simple string-based regex analysis with a more robust AST-based approach.
- Removed the `is_simple_string` function as it's no longer needed.
2024-09-21 18:15:07 +00:00
camchenry
3273b64a0f refactor(linter): Use parsed patterns for unicorn/prefer-string-replace-all rule (#5943)
- part of https://github.com/oxc-project/oxc/issues/5416

Replaces the `is_simple_string` method with a more robust check against the parsed terms from the regular expression.
2024-09-21 18:02:59 +00:00
camchenry
0f19848263 feat(linter): Implement no-unexpected-multiline rule (#5911)
- part of https://github.com/oxc-project/oxc/issues/479

The bulk of this rule is closely based on the logic from the original ESLint rule. One major difference between this implementation and the original though is the lack of a tokenizer. ESLint uses a proper tokenizer to find identifers, parentheses, brackets, and other tokens. For this rule, I opted to just manually search for the characters we might expect to find. I'm not sure how this will hold up in the real world, I expect it could lead to some missing cases potentially, but it at least works for all of the given test cases.
2024-09-20 23:05:02 +00:00
DonIsaac
ba7b01fbdf refactor(linter): Add LinterBuilder (#5714)
This will replace `OxlintOptions` in an upstream PR. This also adds `plugins` to
`Oxlintrc`. This field gets respected by the builder, but not by
`OxlintOptions`.
2024-09-20 11:58:37 +00:00
camchenry
608d637e4d perf(linter): use aho-corasick instead of regex for string matching in jsx-a11y/img-redundant-alt (#5892)
hypothesis: profiling shows that Regex creation takes a decent amount of time. the `regex` crate uses `aho-corasick` internally for string matching, which is all we need in some cases. in theory, we could save time by using the lib directly and not needing the full regex syntax.
2024-09-19 22:40:01 +00:00
Boshen
db4f16a98f refactor(semantic): call with_trivias before build_with_jsdoc (#5875) 2024-09-19 04:12:57 +00:00
DonIsaac
3d13c6d1f9 refactor(semantic): impl IntoIterator for &AstNodes (#5873)
Simple quality-of-life change.
2024-09-19 03:22:29 +00:00
Cam McHenry
16fe3837d9
feat(linter): implement no-extend-native rule (#5867)
- part of https://github.com/oxc-project/oxc/issues/479

This implements the `no-extend-native` rule from the ESLint core
ruleset. This was translated based on the rule source code from the
ESLint codebase. That means some oddity, such as treating any
capitalized identifier (like `Math`) as a global object, is preserved.
So even though `Math.prototype` doesn't exist in reality, we still
report it as an error. This is probably okay given that such code
doesn't make sense anyway.
2024-09-18 20:44:54 -04:00
Cam McHenry
3148d4b654
perf(linter): Check file path after checking node kind for nextjs/no-head-element (#5868) 2024-09-18 18:31:18 -04:00
oxc-bot
65a17346cd
release(oxlint): v0.9.6 (#5843)
## [0.9.6] - 2024-09-18

### Features

- 3bf7b24 linter: Make `typescript/no-duplicate-enum-values` a
`correctness` rule (#5810) (DonIsaac)
- 7799c06 linter/react: Implement `no-danger-with-children` rule (#5420)
(Cam McHenry)

### Bug Fixes

- f942485 linter: Remove all* remaining "Disallow <foo>" messages
(#5812) (DonIsaac)
- b5ad518 linter: Improve diagnostic messages for various lint rules
(#5808) (DonIsaac)
- 858f7af linter: Plugin prefix name for eslint-plugin-node (#5807)
(DonIsaac)
- 737ba1d linter: Fix some cases on ```AssignmentExpression``` for
```unicorn/consistent-function-scoping``` (#5675) (Arian94)
- 148c7a8 linter: Replace bitwise AND (&) with logical AND (&&) in
explici… (#5780) (kaykdm)
- b4ed564 linter/no-unused-vars: Writes to members triggering false
positive (#5744) (Dunqing)
- e9c084a linter/no-unused-vars: False positive when a variable used as
a computed member property (#5722) (Dunqing)

### Performance

- 3725d5d linter: Make all rules share a diagnostics vec (#5806)
(DonIsaac)
- e978567 linter: Shrink size of `DisableDirectives` (#5798) (DonIsaac)
- 1bfa515 linter: Remove redundant clone of diagnostics in context
(#5797) (DonIsaac)
- e413cad linter: Move shared context info to `ContextHost` (#5795)
(DonIsaac)

### Refactor

- 6dd6f7c ast: Change `Comment` struct (#5783) (Boshen)
- 7caae5b codegen: Add `GetSpan` requirement to `Gen` trait (#5772)
(Boshen)
- 026ee6a linter: Decouple module resolution from import plugin (#5829)
(dalaoshu)
- 50834bc linter: Move `override_rule` to `OxlintRules` (#5708)
(DonIsaac)
- a438743 linter: Move `OxlintConfig` to `Oxlintrc` (#5707) (DonIsaac)
- f61e8b5 linter: Impl serde and schemars traits for `LintPlugins`
(#5706) (DonIsaac)
- 20a7861 linter: Shorten `Option` syntax (#5735) (overlookmotel)
- d8b612c oxc_linter: Prefer pass Enum instead of str `no_plus_plus`
(#5730) (IWANABETHATGUY)
- cc0408b semantic: S/AstNodeId/NodeId (#5740) (Boshen)

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2024-09-18 12:02:09 +08:00
dalaoshu
026ee6a7e8
refactor(linter): decouple module resolution from import plugin (#5829)
closes #5815
2024-09-17 15:48:03 -04:00
DonIsaac
f9424857df fix(linter): remove all* remaining "Disallow <foo>" messages (#5812)
Cleans diagnostic messages and help texts for (almost) all remaining rules.
There were two rules I couldn' find good replacements for.

I also made some logical improvements to a few rules, and added `pending` fixers
to others.
2024-09-17 05:46:16 +00:00
DonIsaac
3bf7b24621 feat(linter): make typescript/no-duplicate-enum-values a correctness rule (#5810)
This rule is enabled by default in typescript-eslint's recommended config, so we
should enable it by default too. I checked this change with `oxlint-ecosystem-ci` locally and saw no problems.

This PR also improves diagnostic messages for this rule.
2024-09-17 05:46:16 +00:00
DonIsaac
b5ad518621 fix(linter): improve diagnostic messages for various lint rules (#5808)
This is the first of a few PRs to remove "Disallow <x>" from all diagnostic
messages. More to come.
2024-09-17 05:46:15 +00:00
DonIsaac
858f7af2b5 fix(linter): plugin prefix name for eslint-plugin-node (#5807) 2024-09-17 05:46:15 +00:00
DonIsaac
3725d5d44e perf(linter): make all rules share a diagnostics vec (#5806)
## What This PR Does

Each time a lint rule is run on a file, it will now store diagnostics in a
shared vec instead of having its own list. This is done by moving `diagnostics`
from `LintContext` to `ContextHost`.

The motivating line of code can be found in `Linter::run` in
`oxc_linter/src/lib.rs#145`

```rust
rules.into_iter().flat_map(|(_, ctx)| ctx.into_message()).collect::<Vec<_>>()
```

Each `LintContext` allocates a new vec, and pushes diagnostics into it. After
all run-related methods have been run, a new vec is created and those
diagnostics are copied into it. This is `O(n+1)` allocations and `O(n)` copies,
not to mention that allocations for the original vecs cannot be re-used since
those vecs aren't dropped until after the new one is created.
2024-09-17 05:46:15 +00:00
DonIsaac
50834bc6d2 refactor(linter): move override_rule to OxlintRules (#5708) 2024-09-17 05:15:38 +00:00
DonIsaac
a438743191 refactor(linter): move OxlintConfig to Oxlintrc (#5707) 2024-09-17 05:15:37 +00:00
Cam McHenry
7799c06c1b
feat(linter/react): Implement no-danger-with-children rule (#5420)
- Part of https://github.com/oxc-project/oxc/issues/1022

This implements the [recommended rule
`no-danger-with-children`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md)
from the `eslint-plugin-react` package.

This rule proved to be a little bit trickier to implement than I
anticipated, as it required searching up through all of the scopes
recursively in order to resolve object properties. This would be easier
with something like a type-check system, but this will work for now.

(Sidenote: this is my first real attempt at Rust programming in ~5
years, so any feedback on making this more idiomatic is welcome.)
2024-09-16 13:33:00 -04:00
DonIsaac
e978567659 perf(linter): shrink size of DisableDirectives (#5798)
Use boxed slices instead of vecs, shrinking `DisableDirectives` from 136 to 120
bytes.
2024-09-16 04:22:08 +00:00
DonIsaac
1bfa515088 perf(linter): remove redundant clone of diagnostics in context (#5797) 2024-09-16 04:22:07 +00:00
DonIsaac
e413cad0d7 perf(linter): move shared context info to ContextHost (#5795)
> Related to #5770

## What This PR Does

Moves state that is constant over a linted file out of `LintContext` and into a shared `ContextHost` struct, turning `LintContext` into a [flyweight](https://en.wikipedia.org/wiki/Flyweight_pattern).

This brings `LintContext` from 144 bytes down to 96. `Linter::run` iterates over `(rule, ctx)` pairs in a very tight loop, and each rule instance gets its own clone of `ctx`. A smaller `LintContext` means better cache locality and a smaller heap allocation for this vec. I'm hoping to eventually get it small enough to fit in a single cache line.

I made a PR a while ago that did something similar to this one, but instead of using an `Rc`, each `LintContext` stored a read-only reference. This added an extra lifetime parameter, making it slightly unwieldy, but I saw up to 15%/25% perf improvements on local benchmarks. I'll dig around for it and add a link shortly.

### Architecture
![image](https://github.com/user-attachments/assets/9e8352ae-a581-46a3-a578-9eb855d4ebaf)
----
![image](https://github.com/user-attachments/assets/49213cd9-3c31-40dc-97ad-ddf010705ab6)
2024-09-16 04:22:07 +00:00
Arian94
737ba1d9d8
fix(linter): fix some cases on ``AssignmentExpression` for `unicorn/consistent-function-scoping`` (#5675)
This is to fix the cases mentioned in the comment section of #5365.

In short, it will report these as PASS test cases:

```js
let inner;

function foo1() {
  inner = function() {}
}
function foo2() {
  inner = function() {}
}
```

```js
let inner;

function outer() {
  inner = function() {}
}
```

And report these below as FAIL test cases:

```js
let inner;

function foo1() {
  inner = function smth() {}
}
function foo2() {
  inner = function bar() {}
}
```

```js
let inner;

function outer() {
  inner = function inner() {}
}
```

The case below was already done in #5312 but mentioned in #5365. It is a
FAIL case as well:
```js
function outer() {
  const inner = function inner() {}
}
```

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Don Isaac <donald.isaac@gmail.com>
2024-09-16 00:09:59 -04:00
Arian94
d1d54a6ef3
fix(eslint-plugin-jsx-a11y(anchor-is-valid)): make the rule work correctly and improve error grammar (#5781)
fixes #5746.
2024-09-15 23:23:33 -04:00
kaykdm
148c7a8b82
fix(linter): replace bitwise AND (&) with logical AND (&&) in explici… (#5780)
…t_function_return_type rule

This PR fixes an issue where the Bitwise AND (&) operator was mistakenly
used in a conditional statement, instead of the Logical AND (&&)
operator.
2024-09-15 19:36:13 +08:00
Boshen
6dd6f7ca26 refactor(ast): change Comment struct (#5783) 2024-09-15 09:22:58 +00:00
DonIsaac
f61e8b53dd refactor(linter): impl serde and schemars traits for LintPlugins (#5706) 2024-09-14 11:16:44 +00:00
Boshen
7caae5bca7 refactor(codegen): add GetSpan requirement to Gen trait (#5772) 2024-09-14 11:13:12 +00:00
Dunqing
b4ed564a35 fix(linter/no-unused-vars): writes to members triggering false positive (#5744)
close: #5246

similar to #5722
2024-09-13 06:00:48 +00:00
Boshen
cc0408b5d4 refactor(semantic): s/AstNodeId/NodeId (#5740)
part of #5689
2024-09-13 03:00:00 +00:00
overlookmotel
20a7861838 refactor(linter): shorten Option syntax (#5735)
Use `Some` instead of `Option::Some`.
2024-09-12 18:42:51 +00:00
IWANABETHATGUY
d8b612c038
refactor(oxc_linter): prefer pass Enum instead of str no_plus_plus (#5730)
1. remove redundant branch.
2024-09-12 11:43:24 -04:00
Dunqing
e9c084a705 fix(linter/no-unused-vars): false positive when a variable used as a computed member property (#5722)
close: #5671
2024-09-12 11:23:01 +00:00
oxc-bot
1dad9fa301
release(oxlint): v0.9.5 (#5724)
## [0.9.5] - 2024-09-12

### Features

- 4b04f65 linter: Implement `no-plusplus` rule (#5570) (Cam McHenry)

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2024-09-12 16:59:25 +08:00
Cam McHenry
4b04f65275
feat(linter): implement no-plusplus rule (#5570)
- part of https://github.com/oxc-project/oxc/issues/479

This PR implements the `no-plusplus` rule and is more-or-less a direct
port of the ESLint version of the rule.

---------

Co-authored-by: Don Isaac <donald.isaac@gmail.com>
2024-09-12 16:50:19 +08:00
oxc-bot
d06bab6260
release(oxlint): v0.9.4 (#5718)
## [0.9.4] - 2024-09-12

### Features

- 9ca2593 linter: Eslint/sort-keys  (#4845) (Na'aman Hirschfeld)
- 023c160 linter: Impl `Serialize` for `OxlintConfig` (#5594) (DonIsaac)
- 24d6a47 linter: Implement `eslint/no-invalid-regexp` (#5443) (Boshen)

### Bug Fixes

- af6d240 linter: Panic in consistent-function-scoping (#5613)
(DonIsaac)
- 54e2e76 linter: `react/no_set_state` + `react/no_string_refs` rules
find correct parent (#5615) (overlookmotel)
- 3b87ac4 linter: Fix no_unused_vars panic when encountering unicode
(#5582) (Boshen)

### Performance

- bfe9186 linter: Use `cow_replace` instead of `replace` (#5643)
(dalaoshu)
- e3ae5db linter: Use cow_to_ascii_lowercase/uppercase (#5637) (heygsc)
- a0370bf linter: Use cow_utils in no_script_url (#5633) (heygsc)
- 37e922c linter: `eslint/no_shadow_restricted_names` use
`run_on_symbol` (#5618) (overlookmotel)
- 0b7fccf linter: `react/no_set_state` + `react/no_string_refs` rules
reduce iteration over ancestors (#5616) (overlookmotel)
- 2c3f3fe linter: Make `jsx_key` slightly faster (#5585) (Boshen)
- cd81d12 linter: Add `should_run` to check path only once to
nextjs/no_typos (#5584) (Boshen)
- d18c896 rust: Use `cow_utils` instead (#5664) (dalaoshu)

### Documentation

- 64f9575 linter: Add plugin usage to example with configuration
(Boshen)
- 8c9179d linter: Fix typos (#5591) (Brian Donovan)

### Refactor

- 9e9435f linter: Add `LintFilter` (#5685) (DonIsaac)
- 4f70fe5 linter: Start internal/external split of LintPluginOptions
(#5660) (DonIsaac)
- 5ae9b48 linter: Start internal/external split of `OxlintOptions`
(#5659) (DonIsaac)
- c8bc6f0 linter: Use `std::ptr::eq` (#5649) (overlookmotel)
- a37c064 linter: Use `ContentHash` for `no_duplicate_case`; remove
`calculate_hash` (#5648) (Boshen)
- 0b3c1d7 linter: Start internal/external split of `OxlintConfig`
(#5595) (DonIsaac)
- 89bdf55 linter: Inline `Rule` trait default methods (#5619)
(overlookmotel)
- afea8d5 linter: Rename `Rule` trait method params (#5617)
(overlookmotel)
- 4e748b5 linter: Replace ast "compare by hash" to "compare by content"
(#5602) (dalaoshu)
- bac03e3 linter: Make fields of `LintServiceOptions` private (#5593)
(DonIsaac)
- 2661d8b linter: Jest prefer_strict_equal (#5588) (IWANABETHATGUY)
- 20d0068 oxlint: Move cli-related exports to `cli` module (#5139)
(DonIsaac)
- 067f9b5 semantic: Introduce `IsGlobalReference` trait (#5672)
(Boshen)- 26d9235 Enable clippy::ref_as_ptr (#5577) (夕舞八弦)

### Testing

- 8e79f8d linter: Add class method test cases for `oxc/no-async-await`
(#5550) (DonIsaac)
- 3835189 linter: Add test case for no_unused_vars in 3b87ac4 (Boshen)
- 5f27551 linter: Add a passing case to no_undef (#5580) (Boshen)

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2024-09-12 13:56:38 +08:00
Na'aman Hirschfeld
9ca2593aaa
feat(linter): eslint/sort-keys (#4845)
This PR adds the `eslint/sort-keys` implementation as part of #479 

Note: two tests are commented out. I was not able to model this behavior
properly. I would be happy to have some assistance there.
2024-09-11 13:10:55 -04:00
Boshen
945d2744ae
chore(linter: fix snapshot 2024-09-11 21:07:23 +08:00