mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(codegen): validate starting indent level (#25550)
## Summary Validate `startingIndentLevel` before printing to prevent unbounded indentation-cache growth. - Reject non-finite, fractional, negative, and unsafe integer values with a `RangeError`. - Document the option as a non-negative integer.
This commit is contained in:
1 parent
b2f8fdf230
commit
59e8895162
4 files changed
+42
-4
No files matched your search
@@ -47,7 +47,7 @@ Returns a string containing the code for the AST `node`.
|
||||
| Option | Type | Default | Description |
|
||||
| :-------------------- | :------------------- | :------ | :---------------------------------------------- |
|
||||
| `indent` | `string` | `"\t"` | Indentation - spaces and/or tabs only |
|
||||
| `startingIndentLevel` | `number` | `0` | Indent level to start from |
|
||||
| `startingIndentLevel` | `number` | `0` | Starting indent level, from `0` to `1000` |
|
||||
| `jsx` | `boolean` | `false` | `.tsx` mode - lone type parameters print `<T,>` |
|
||||
| `ts` | `boolean` | `false` | AST may contain TypeScript syntax |
|
||||
| `sourceMap` | `SourceMapGenerator` | - | If present, source mappings are emitted into it |
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface Options {
|
||||
indent?: string;
|
||||
|
||||
/**
|
||||
* Indent level to start from, defaults to `0`.
|
||||
* Non-negative integer indent level to start from, from `0` to `1000`. Defaults to `0`.
|
||||
*/
|
||||
startingIndentLevel?: number;
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ const indents = [""];
|
||||
*/
|
||||
const INDENT_REGEX = /^[ \t]+$/;
|
||||
|
||||
/** Upper bound for the process-wide indentation cache. */
|
||||
const MAX_STARTING_INDENT_LEVEL = 1_000;
|
||||
|
||||
export class State {
|
||||
// Current output.
|
||||
// A string which is appended to as the printing process proceeds.
|
||||
@@ -85,8 +88,18 @@ export class State {
|
||||
constructor(options: Options) {
|
||||
this.output = "";
|
||||
|
||||
let indentLevel = options.startingIndentLevel;
|
||||
if (typeof indentLevel !== "number") indentLevel = 0;
|
||||
let { startingIndentLevel: indentLevel } = options;
|
||||
if (indentLevel === undefined) {
|
||||
indentLevel = 0;
|
||||
} else if (
|
||||
!Number.isSafeInteger(indentLevel) ||
|
||||
indentLevel < 0 ||
|
||||
indentLevel > MAX_STARTING_INDENT_LEVEL
|
||||
) {
|
||||
throw new RangeError(
|
||||
"`startingIndentLevel` must be a non-negative safe integer no greater than 1000",
|
||||
);
|
||||
}
|
||||
this.indentLevel = indentLevel;
|
||||
|
||||
// The `indent` option is validated here, not in the printer, and changing of it discards
|
||||
|
||||
@@ -107,6 +107,31 @@ function checkCases(cases: Case[]): void {
|
||||
|
||||
// --- Tests ----------------------------------------------------------------------------------
|
||||
|
||||
describe("starting indent level", () => {
|
||||
const ast = e(id("x"));
|
||||
|
||||
test("indents from a valid level", () => {
|
||||
expect(printSync(ast, { startingIndentLevel: 1 })).toBe("\tx;\n");
|
||||
});
|
||||
|
||||
test("accepts the maximum level", () => {
|
||||
expect(printSync(ast, { startingIndentLevel: 1000 })).toBe(`${"\t".repeat(1000)}x;\n`);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["infinity", Infinity],
|
||||
["negative infinity", -Infinity],
|
||||
["not a number", NaN],
|
||||
["fraction", 0.5],
|
||||
["negative", -1],
|
||||
["above the maximum", 1001],
|
||||
])("rejects %s", (_name, startingIndentLevel) => {
|
||||
expect(() => printSync(ast, { startingIndentLevel })).toThrow(
|
||||
"`startingIndentLevel` must be a non-negative safe integer no greater than 1000",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// A numeric literal followed by `.` needs a space when the number is plain digits, because
|
||||
// `0.toExponential()` would lex the `.` as part of the number. This is the `needSpaceBeforeDot`
|
||||
// path, and every case below is unreachable from a parsed fixture in at least one respect.
|
||||
|
||||
Reference in new issue
Block a user