Commit Graph

157 Commits

Author SHA1 Message Date
Boshen fb181ccdff fix(semantic): error on using at top level in scripts (#18090)
## Summary

- Add check to error on `using` declarations at the top level of scripts
- `using` declarations require block scope semantics and are not allowed
at the top level of scripts

`using` declarations are allowed:
- At the top level of ES modules
- At the top level of CommonJS modules (files are wrapped in a function)
- Inside any block scope in scripts

But NOT at the top level of scripts.

Closes #11862

## Test plan

- [x] Added test case
`tasks/coverage/misc/fail/script-top-level-using.js` for invalid usage
- [x] Added test case
`tasks/coverage/misc/pass/script-using-in-block.js` for valid usage in
block
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:18:26 +08:00
Boshen 08dad63678 feat(span): add sourceType: 'commonjs' support (#18089)
## Summary

- Add `ModuleKind::CommonJS` variant to support CommonJS module type in the parser
- Enable top-level `return` statements (CommonJS files are wrapped in a function)
- Enable top-level `new.target` (allowed in CommonJS function wrapper)
- Add proper errors for `import`/`export` statements in CommonJS
- Add proper errors for `import.meta` in CommonJS

The CommonJS source type can be set via:
- `SourceType::with_commonjs(true)` method
- Babel's `sourceType: "commonjs"` option

Closes #16200

## Test plan

- [x] Added test cases in `tasks/coverage/misc/pass/` for valid CommonJS features
- [x] Added test cases in `tasks/coverage/misc/fail/` for invalid CommonJS usage
- [x] Enabled Babel conformance tests for `sourceType: "commonjs"`
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-16 16:08:27 +00:00
Dunqing 4d9582d762 fix(semantic): allow arguments/eval as binding identifier names and identifier reference names in .d.ts (#17910)
Fixes: https://github.com/oxc-project/oxc/issues/17776
2026-01-12 10:03:42 +00:00
Copilot 59a6228ef9 fix(parser): detect TS1363 error for type-only imports with mixed default and named/namespace bindings (#17712)
The parser was not detecting `import type foo, {} from 'bar';` and `import type A, * as NS from 'mod';` as errors. TypeScript's TS1363 requires that type-only imports specify either a default import or named/namespace bindings, but not both—even when the named bindings list is empty.

## Changes

- **Parser**: Added TS1363 check in `parse_import_declaration_specifiers` when encountering `LCurly` or
`Star` token after a type-only default import
- **Module record**: Removed redundant TS1363 validation that couldn't detect empty `{}` cases
- **Error span**: Now points at the default specifier instead of the entire import statement

## Examples

The following patterns now correctly error with TS1363:

```typescript
// Empty named bindings
import type foo, {} from 'bar';

// Named bindings with specifiers
import type foo, { bar } from 'baz';

// Namespace import
import type A, * as NS from 'mod';
```

The parser-level check is comprehensive and catches all cases including
empty braces and namespace imports, which weren't being detected
previously.


- Fixes oxc-project/oxc#17684

---------

Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: camc314 <18101008+camc314@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-01-06 14:45:14 +00:00
camc314 d99134d70c feat(parser): improve diagnostic message for phase imports (#17092)
We now produce the same error codes as typescript
2025-12-21 08:14:16 +00:00
sapphi-red cb515fac28 feat(parser): improve error message for yield as identifier usage (#16950)
Improved error message for `yield` as identifier usage.

**Before**
```
  × Cannot use `yield` as an identifier in a generator context
    ╭─[test262/test/language/expressions/generators/yield-weak-binding.js:17:33]
 16 │
 17 │ var g = function*() { yield 3 + yield 4; };
    ·                                 ─────
    ╰────
```
**After**
```
  × Cannot use `yield` as an identifier in a generator context
    ╭─[test262/test/language/expressions/generators/yield-weak-binding.js:17:33]
 16 │
 17 │ var g = function*() { yield 3 + yield 4; };
    ·                                 ─────
    ╰────
  help: Wrap this in parentheses if you want to use a `yield` expression here
```
2025-12-16 11:30:44 +00:00
overlookmotel 75f9800166 test(parser): combine test cases for let as identifier (#16861)
Follow-on after #16840 and #16857. We had a few test cases relating to the same thing, and one of them called the undescriptive `oxc.js`. Combine them, and rename the test case files with a more accurate name.
2025-12-14 21:30:55 +00:00
Copilot d3208dc969 fix(parser): parse let?.x (#16840)
Fixes #16839.

Make parser parse `let?.x` successfully.

There's a failure in codegen idempotency tests, but that's an unrelated
and pre-existing bug, so am leaving that to another PR.

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
2025-12-14 19:26:57 +00:00
sapphi-red da87812ac7 feat(semantic): add TS2309 error for export assignment with other exports (#15992)
Added TS2309 error check.
2025-12-06 08:30:06 +00:00
copilot-swe-agent 74cf5720bc feat(ast)!: Make source field of TSImportType a StringLiteral (#16114)
- [x] Change `source` field type in `TSImportType` from `TSType<'a>` to `StringLiteral<'a>` in `crates/oxc_ast/src/ast/ts.rs`
- [x] Remove `#[estree(via = TSImportTypeSource)]` attribute from the field
- [x] Run `just ast` to regenerate all derived code
- [x] Update the parser in `crates/oxc_parser/src/ts/types.rs` to parse a `StringLiteral` directly and emit errors for non-string literals
- [x] Delete the dead code `TSImportTypeSource` serializer from `crates/oxc_ast/src/serialize/ts.rs`
- [x] Add `ts_string_literal_expected` diagnostic (TS1141)
- [x] Update semantic snapshot for `import-type-with-type-params.snap`
- [x] Add test case for invalid import type source
- [x] Verify all tests pass
- [x] Simplify error recovery - just report error and create dummy string literal (per review feedback)
- [x] Fix spurious "Expected ')'" error by skipping invalid tokens in error recovery

<!-- START COPILOT CODING AGENT SUFFIX -->

<details>

<summary>Original prompt</summary>

>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>AST: Make `source` field of `TSImportType` a `StringLiteral`</issue_title>
> <issue_description>Continuation of oxc-project/oxc#16074.
>
> The expression inside `import(...)` in TS context (`TSImportType`) cannot legally be anything other than a string literal.
>
> Our parser is too liberal. We currently accept all kind of crazy stuff like:
>
> ```ts
> type A = typeof import(`react`);
> type B = typeof import(A);
> type C = typeof import(`${A} ${B}`);
> type D = typeof import(typeof import('react'));
> ```
>
> [Playground](https://playground.oxc.rs/#eNptU9tOGzEQ/ZXIQqJIqUSRKqFUfQjQSJVooaUqL31g1jsbDF7bHc/molX+nfEmdkDhyfaZ+5njXmk1UbwOOJqOvo7SxTcj0wZP/OGBEDQ/nHz55waPiwOPabFdHkYf9dPN6Ki/2OwzXB14vX0dDxWPT1KAGiuvJr2izqXDGsdq0oCNOFaNpxaYkQrCBC4muCAmeguM9RVqCwRsvIvFuHw0jDGAxgK14OZ2/9S+DYRxHxK1D2Jm6tJj3Vbe5pdu5ju3zVgFoJga6xWuGF2UuoniKPOAtX75G7kjd9NxNDXOOqdTZzlTKom0wFsgCc3oEPf3/LtjMpJQF0NE6ZqN/kbkaYdKC4mroQW5v6KqV13EP1DtZ2Ko7k3Nj2pyNlbo6pvm2jgZUtlG2g1SjXf281OpZhJDvzrPe5qe4uruHfh/et2SD1JLQfzoEGusJafsyUh780vftpBs1goqc5gyrMyyzPOLfQnrxF1FoJ+R72RnEp6dMwotbjvf1ffVE2q+JwiSI5OaCg29TlmIrDrpEOlNGK4CkmnRMdgbuQJ7uvXRbFeUGFJCadHallQGmqNoU2E8O/30WaoIzVfYSOKZp0sLMc4M2rps7XUVUacfyuytreGC/kCGGliY2q32fVX3KspEQQSC5EB0mcWofY1zHD6QS0Iokn2KtZevv6PcOc9DroxYnGffIUv+C0lR+aPI5D5c4wJL0mfE8FNWkQU2hIpovZ2JgFPEAqnyMROe5nFpUclUbhuB64G+BJfbJuGJZoisNi/WFa5W)
>
> TypeScript says these are all errors: [TS Playground](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAglC8VSQPYDMoEsC2YUCdgAKAA3wgEMBjYEgSgG4AoZaAIQSXAnS1wOIxGLblADCnVrxx5CpACQBvGAF8oStivrNWUACKTu0-nKkYZAogHJy1YFbrCgA)
>
> ### AST change
>
> We should alter the type of the `source` field from `TSType<'a>` to `StringLiteral<'a>`:
>
> https://github.com/oxc-project/oxc/blob/35d0ee491cbcd271ebceaac7e25cff0490ded5c4/crates/oxc_ast/src/ast/ts.rs#L1382-L1389
>
> ```diff
> pub struct TSImportType<'a> {
>     pub span: Span,
> -   #[estree(via = TSImportTypeSource)]
> -   pub source: TSType<'a>,
> +   pub source: StringLiteral<'a>,
>     pub options: Option<Box<'a, ObjectExpression<'a>>>,
>     pub qualifier: Option<TSImportTypeQualifier<'a>>,
>     pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
> }
> ```
>
> ### Parser changes
>
> If we change the AST first and run `just ast` to regenerate traits code, then all the places which rely on the type of `source` field will become apparent. We'll then need to alter the parser to throw an error if it encounters something that isn't a string literal.
>
> ### `ESTree` serialization
>
> Currently there's a custom deserializer to alter the AST on JS side from `TSType` to `StringLiteral`:
>
> https://github.com/oxc-project/oxc/blob/91eb3f2be509cef11cffeff51fac61a3a4fbd12c/crates/oxc_ast/src/serialize/ts.rs#L506-L538
>
> Once we've changed the field type in AST, this code can be deleted - it's dead code once `#[estree(via = TSImportTypeSource)]` attribute is removed from the `source` field in AST struct.</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>

</details>

- Fixes oxc-project/oxc#16111

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
2025-11-25 18:51:16 +00:00
camc314 1199cee3ae fix(parser): reject invalid modifiers on parameter properties with binding patterns (#16083)
Fixes #16077
2025-11-25 09:43:09 +00:00
sapphi-red 2191ae9f8d fix(semantic): allow reserved keywords in typescript ambient contexts (#15495)
Reserved keywords are allowed as an identifier in ambient contexts.
```ts
declare const private: number
```
This is allowed.
2025-11-18 08:11:22 +00:00
sapphi-red d60ca818e7 fix(parser): reject import something 'source' (#15746)
```js
import something 'source'
```
was not rejected but this should be rejected.
https://tc39.es/ecma262/2025/multipage/ecmascript-language-scripts-and-modules.html#prod-ImportDeclaration
2025-11-17 02:58:51 +00:00
sapphi-red 9f54a36de3 fix(semantic): error on \00 in strict mode (#15743)
`\00` matches LegacyOctalEscapeSequence and should be an error in strict mode.
https://tc39.es/ecma262/2025/multipage/ecmascript-language-lexical-grammar.html#sec-string-literals-early-errors
2025-11-16 09:25:13 +00:00
sapphi-red 77efb7698d feat(parser): improve error message for invalid switch clauses (#15728)
Improved error message for invalid switch clauses.

**Before**
```
  x Unexpected token
   ,-[:2:3]
 1 | switch (foo) {
 2 |   caze 1:
   :   ^^^^
 3 | }
   `----
```

**After**
```
  × Expected switch clause
   ╭─[misc/fail/switch-invalid-clause.js:2:3]
 1 │ switch (foo) {
 2 │   caze 1:
   ·   ──┬─
   ·     ╰── `case` or `default` clause expected here
 3 │ }
   ╰────
  help: If this is intended to be the condition for the switch statement, add `case` before it.
```
2025-11-16 02:57:35 +00:00
sapphi-red 56917278ca feat(parser): improve import source from error message (#15727)
Improved error message for `import source` + something + `from`.

**Before**
```
  x Unexpected token
   ,-[:1:19]
 1 | import source foo bar from 'module';
   :                   ^^^
   `----
```

**After**
```
  × Expected `from` but found `Identifier`
   ╭─[misc/fail/import-source-non-from.js:1:19]
 1 │ import source foo bar from 'module';
   ·                   ─┬─
   ·                    ╰── `from` expected
   ╰────
```
2025-11-16 02:57:35 +00:00
sapphi-red 71c2fb05bc feat(parser): improve error message when JSX is found while not enabled (#15725)
Improved the error message when JSX syntax is found while not enabled.

The idea came from esbuild's error message ([esbuild try](https://esbuild.github.io/try/#dAAwLjI3LjAAAGV4cG9ydCBjb25zdCBmb28gPSA8Rm9vIC8+Ow)).

**Before**
```
  x Unexpected token
   ,-[:1:20]
 1 | export const foo = <Foo />;
   :                    ^
   `----
```

**After**
```
  × Unexpected JSX expression
   ╭─[misc/fail/jsx-in-js.js:1:20]
 1 │ export const foo = <Foo />;
   ·                    ───────
   ╰────
  help: JSX syntax is disabled and should be enabled via the parser options
```
2025-11-16 02:57:34 +00:00
camc314 8951953008 feat(parser): improve diagnostic messages for merge conflicts (#15443)
Adds detection and improved error messages for version control merge conflicts encountered in the parser.

This is based upon rustc's implementation.
2025-11-08 06:53:50 +00:00
sapphi-red d4f6545078 feat(parser): improve error message for missing closing tokens (#15269)
Improves error message for imbalanced tokens.
```
 × Expected `,` or `]` but found `const`
   ╭─[misc/fail/imbalanced-array-expr.js:2:1]
 1 │ const foo = [0, 1
   ·             ┬
   ·             ╰── Opened here
 2 │ const bar = 2
   · ──┬──
   ·   ╰── `,` or `]` expected
   ╰────
```

The error message for this case was:
```
  x Expected `,` but found `const`
   ,-[:2:1]
 1 | const foo = [0,1
 2 | const bar = 2
   : ^^|^^
   :   `-- `,` expected
   `----
```

This change makes it clear why the parser is expecting `,` and also tell that it can be fixed by adding the closing token somewhere.
2025-11-04 13:13:00 +00:00
sapphi-red 2ef8f01624 feat(parser): improve error message for missing conditional alternative (#15268)
Improve error message for missing alternative for conditional expressions.
```
 × Expected `:` but found `EOF`
   ╭─[misc/fail/missing-conditional-alternative-type.ts:2:1]
 1 │ type A = 1 extends 2 ? 3
   ·                      ┬
   ·                      ╰── Conditional starts here
   ╰────

  × Expected `:` but found `EOF`
   ╭─[misc/fail/missing-conditional-alternative.js:2:1]
 1 │ const foo = 1 ? 2
   ·               ┬
   ·               ╰── Conditional starts here
   ╰────
```
Without this PR, the error mesage is a simple ``Expected `:` but found `EOF` `` only.
2025-11-04 13:12:59 +00:00
sapphi-red c28807b32c fix(parser): reject async await => {} in scripts (#15222)
`async await => {}` was rejected in modules but should be rejected in scripts as well.
2025-11-02 12:08:56 +00:00
camc314 384ea3c7be fix(parser): report err on missing function body in expression (#14946)
partially addresses #14944
2025-10-27 11:04:22 +00:00
Ulrich Stark 81a28d4bff fix(parser): forbid abstract class members with implementation in parser instead of semantic (#14325)
The following four cases are now errors in parser instead of semantic
errors.
[TS
Playground](https://www.typescriptlang.org/play/?#code/IYIwzgLgTsDGEAJYBthjAgYge2wg3gFAIKiQzwIAOU2VCAvAgMwDchxp40ciAtgFMIAC2wATABQBKAggC+HEmR6UA5kITqIEAVGkFOJKEICuUAHYIAjOxILOyiojAaX23RIBuwZCYEAuBHMTPhBdGSISEk4FOSA)
for reference.
Closes #14013 

```typescript
abstract class Foo {
  abstract prop = 3;

  abstract method() { }

  abstract get getter() {
    return 1;
  }

  abstract set setter(value: number) {
    
  }
}
```
2025-10-05 09:11:58 +08:00
Don Isaac f4d6790d27 fix(parser): forbid accessors named constructor (#14017)
Re-creation of #14015. Closes #14014.
2025-09-23 12:56:23 +08:00
Karan Kiri db33196f16 feat(parser): adds typescript rule for empty argument list (#13730)
Co-authored-by: Boshen <boshenc@gmail.com>
2025-09-13 21:27:09 +08:00
overlookmotel 9fa551a6ce fix(semantic): handle edge cases checking super (#13499)
Fixes #13284.

Fix semantic's checking of `super()` and `super.prop`.

This fixes various TS test cases, notably correctly identifying that these are all illegal:

```js
class C {
  prop = function() { super.foo };
  [super.foo]() {}
  @super.foo method() {}
};
```

It also fixes various edge cases related to classes nested within other classes that aren't covered by existing conformance tests.

It's pretty much a complete re-write. I've combined the 2 paths for in a class and not in a class into one, to avoid repeated code.

This PR includes pass + fail test fixtures which include `super` in every conceivable position I could think of. There are so many errors in the fail test fixtures, that I had to write a script to check the snapshot includes them all!

Hopefully this solves this issue for once and for all!

Note: The large pass fixture seems to be revealing bugs somewhere in transformer - there's a mismatch, as well as lots of wrong scopes. But that's incidental to this PR, and can be addressed separately.
2025-09-10 03:23:41 +00:00
Ulrich Stark f795d69ce0 fix(parser): improve diagnostics around modifier checks (#13526)
Follow up to #13368.
Closes #11713 by fixing five more cases mentioned in
https://github.com/oxc-project/oxc/issues/11713#issuecomment-3239687597.

Other changes:
- Added TS(1024) diagnostic
- Fixed the broken oxc-11713-13.ts test file
- Made TS(1070) not report redundantly if it's actually the
responsibility of TS(1071)
- Branch off the more specific TS(1028) diagnostic from the general
TS(1030) diagnostic like TypeScript does
- Remove incorrect code to parse modifiers on typescript this parameter
2025-09-09 09:28:39 +08:00
Ulrich Stark 65aca9eeb9 fix(parser): reset popped lexer errors when rewinding (#13494)
Closes #12816.
Alternative to #13460.

This PR is the slightly cleaner way to fix the issue compared to the
other PR, but comes at a potential performance cost because now instead
of remembering how many errors there were, we are storing a clone of all
errors and re-assign them when rewinding.

This makes it possible to fix the issue because now even errors before a
checkpoint, that were popped/removed after the checkpoint, are properly
rewinded.
2025-09-01 22:54:27 +08:00
Boshen dc9645f98f fix(semantic): allow super in object literal method inside a class (#13463)
fixes #13284
2025-08-31 21:06:40 +00:00
overlookmotel fd3233cf95 fix(semantic): flag super in function inside ObjectExpression (#13325)
Fix #13323.

Make semantic raise errors for the following illegal `super` uses:

```js
o = {
  foo: function() {
    super.foo;
  },

  bar() {
    return function() {
      super.bar;
    };
  },

  [ function() { super.qux; } ]() {},

  get [ function() { super.bing; } ]() {},

  set [ function() { super.bong; } ](v) {},
};
```
2025-08-29 13:10:08 +00:00
Ulrich Stark 903a150bf8 feat(parser): report more invalid modifier locations (#13368)
### Changes

- Added all 22 examples of #11713 to `tasks/coverage/misc/fail`
- Added `Default` and `Export` to modifiers
- Added three new TS diagnostics: 1029, 1031, and 1070
- Split off `FunctionKind::Constructor` from `FunctionKind::ClassMethod`
to differentiate when verifying modifiers of formal parameter
- Added multiple checks to verify modifiers

### Results
- "Negative Passed" percentage of `parser_babel.snap` and
`parser_typescript.snap` increased
- Better diagnostic messages in both snapshots
- All new failing examples of original issue do really fail
- Closes #11713

---------

Co-authored-by: Boshen <boshenc@gmail.com>
2025-08-29 11:33:21 +08:00
Boshen 8a43068a27 fix(semantic): allow super in object method in js but not in ts (#13318)
closes #13284

Either TypeScript implemented the spec incorrectly or made it more
strict.

```js
class C {
  foo() {
    return {
      bar() {
        super.bar(); // syntax error in ts but not in js
      },
    };
  }
}
```
2025-08-27 02:20:08 +00:00
Li Wei 724837e75f fix(semantic): label redeclaration check for crossing boundary on arrow function (#12679) (#12948)
In the previous implementation, check label cross boundary on function
or static block, but ignore arrow function

fixes #12679
2025-08-10 15:17:07 +08:00
Cameron e836e552f3 fix(parser): prevent panic when parsing invalid extends clause (#12551)
fixes #12546
2025-08-01 16:19:30 +08:00
camc314 ce5876d5b5 fix(parser): validate inner expression of type assertions in assignment targets (#12614)
fixes #12612
2025-08-01 04:42:56 +00:00
camc314 6fa23ad24e test(semantic): add failing tests for arguments and eval use in strict mode (#12298) 2025-07-16 07:49:38 +00:00
camc314 089f7b10a3 fix(semantic): allow arguments/eval as name of TSTypeAliasDeclaration, TSInterfaceDeclaration (#12291)
this stack should fix #12286
2025-07-16 07:49:37 +00:00
camc314 1bfd44feba fix(semantic): allow arguments/eval as spread argument name inside TSMethodSignature, TSFunctionType (#12290) 2025-07-16 07:49:37 +00:00
camc314 a740f3f3e8 fix(semantic): allow arguments/eval as argument name inside TSMethodSignature (#12289) 2025-07-16 07:49:37 +00:00
camc314 e82f758ff9 fix(semantic): allow arguments/eval as argument name inside TSFunctionType (#12288) 2025-07-16 01:45:12 +00:00
camc314 6c9c580989 fix(parser): panic when parsing interface with missing implements (#11898) 2025-06-25 12:42:32 +00:00
Boshen 4e40089741 fix(parser): parse TSTypePredicate correctly (#11666)
fixses #11659
2025-06-13 10:16:19 +00:00
Boshen eb55d839c1 fix(parser): parse using() correctly (#11664)
closes #11658
2025-06-13 07:44:52 +00:00
Boshen e0ae6b240c feat(parser): produce syntax error for decorator in class methods in js (#11624)
closes #11485
2025-06-12 03:58:17 +00:00
Boshen cdf7cdcac7 feat(parser): produce syntax error for decorators in non-class methods (#11614)
part of #11485

I searched around, there are no test cases in babel nor ts.
2025-06-11 17:15:56 +00:00
Boshen b7b0dc3b79 refactor(parser): improve TSModuleDeclaration parsing (#11605)
closes #11592
2025-06-11 12:28:00 +00:00
Boshen e9a8832ce6 refactor(parser): rewrite decorator parsing (#11604)
fixes #11593

Parse decorators and pass them around explicitly instead of using a state to store them.
2025-06-11 11:11:58 +00:00
camc314 cb17dae916 fix(parser): report error on malformed template expressions (#11540)
fixes #11538
2025-06-08 11:46:56 +00:00
camchenry 551cd2a790 fix(parser): fix parsing of import source and import defer (#11537)
- closes https://github.com/oxc-project/oxc/issues/11532
2025-06-07 03:57:33 +00:00
Cam McHenry 8d9919fc49 fix(parser): parse import type from ... correctly in JS files (#11506)
- fixes https://github.com/oxc-project/oxc/issues/11505

I forgot to add a `self.is_ts` check for parsing `import type from ...`.
In JS files, this is just a normal default import.

Co-authored-by: Cameron McHenry <camchenry@lovelace.localdomain>
2025-06-06 15:26:35 +08:00