Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
23
Frontend-Learner/node_modules/oxc-walker/LICENCE
generated
vendored
Normal file
23
Frontend-Learner/node_modules/oxc-walker/LICENCE
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Daniel Roe
|
||||
Copyright (c) 2024 Matej Černý
|
||||
Copyright (c) 2015-2020 [estree-walker contributors] (https://github.com/Rich-Harris/estree-walker/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
225
Frontend-Learner/node_modules/oxc-walker/README.md
generated
vendored
Normal file
225
Frontend-Learner/node_modules/oxc-walker/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
# oxc-walker
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||
[![Github Actions][github-actions-src]][github-actions-href]
|
||||
[![Codecov][codecov-src]][codecov-href]
|
||||
|
||||
A strongly-typed ESTree AST walker built on top of [oxc-parser](https://github.com/oxc-project/oxc).
|
||||
|
||||
## Usage
|
||||
|
||||
Install package:
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npm install oxc-walker
|
||||
|
||||
# pnpm
|
||||
pnpm install oxc-walker
|
||||
```
|
||||
|
||||
### Walk a parsed AST
|
||||
```ts
|
||||
import { parseSync } from 'oxc-parser'
|
||||
import { walk } from 'oxc-walker'
|
||||
|
||||
const ast = parseSync('example.js', 'const x = 1')
|
||||
|
||||
walk(ast.program, {
|
||||
enter(node, parent, ctx) {
|
||||
// ...
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Parse and walk directly
|
||||
|
||||
```js
|
||||
import { parseAndWalk } from 'oxc-walker'
|
||||
|
||||
parseAndWalk('const x = 1', 'example.js', (node, parent, ctx) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
## ⚙️ API
|
||||
|
||||
### `walk(ast, options)`
|
||||
Walk an AST.
|
||||
```ts
|
||||
// options
|
||||
interface WalkOptions {
|
||||
/**
|
||||
* The function to be called when entering a node.
|
||||
*/
|
||||
enter?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
|
||||
/**
|
||||
* The function to be called when leaving a node.
|
||||
*/
|
||||
leave?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
|
||||
/**
|
||||
* The instance of `ScopeTracker` to use for tracking declarations and references.
|
||||
*/
|
||||
scopeTracker?: ScopeTracker
|
||||
}
|
||||
|
||||
interface CallbackContext {
|
||||
/**
|
||||
* The key of the current node within its parent node object, if applicable.
|
||||
*/
|
||||
key: string | number | symbol | null | undefined
|
||||
/**
|
||||
* The zero-based index of the current node within its parent's children array, if applicable.
|
||||
*/
|
||||
index: number | null
|
||||
/**
|
||||
* The full Abstract Syntax Tree (AST) that is being walked, starting from the root node.
|
||||
*/
|
||||
ast: Program | Node
|
||||
}
|
||||
```
|
||||
|
||||
#### `this.skip()`
|
||||
When called inside an `enter` callback, prevents the node's children from being walked.
|
||||
It is not available in `leave`.
|
||||
|
||||
#### `this.replace(newNode)`
|
||||
Replaces the current node with `newNode`. When called inside `enter`, the **new node's children** will be walked.
|
||||
The leave callback will still be called with the original node.
|
||||
|
||||
> ⚠️ When a `ScopeTracker` is provided, calling `this.replace()` will not update its declarations.
|
||||
|
||||
#### `this.remove()`
|
||||
Removes the current node from its parent. When called inside `enter`, the removed node's children
|
||||
will not be walked.
|
||||
|
||||
_This has a higher precedence than `this.replace()`, so if both are called, the node will be removed._
|
||||
|
||||
> ⚠️ When a `ScopeTracker` is provided, calling `this.remove()` will not update its declarations.
|
||||
|
||||
### `parseAndWalk(source, filename, callback, options?)`
|
||||
Parse the source code using `oxc-parser`, walk the resulting AST and return the `ParseResult`.
|
||||
|
||||
Overloads:
|
||||
- `parseAndWalk(code, filename, enter)`
|
||||
- `parseAndWalk(code, filename, options)`
|
||||
|
||||
```ts
|
||||
interface ParseAndWalkOptions {
|
||||
/**
|
||||
* The function to be called when entering a node.
|
||||
*/
|
||||
enter?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
|
||||
/**
|
||||
* The function to be called when leaving a node.
|
||||
*/
|
||||
leave?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
|
||||
/**
|
||||
* The instance of `ScopeTracker` to use for tracking declarations and references.
|
||||
*/
|
||||
scopeTracker?: ScopeTracker
|
||||
/**
|
||||
* The options for `oxc-parser` to use when parsing the code.
|
||||
*/
|
||||
parseOptions?: ParserOptions
|
||||
}
|
||||
```
|
||||
|
||||
### `ScopeTracker`
|
||||
A utility to track scopes and declarations while walking an AST. It is designed to be used with the `walk`
|
||||
function from this library.
|
||||
|
||||
```ts
|
||||
interface ScopeTrackerOptions {
|
||||
/**
|
||||
* If true, the scope tracker will preserve exited scopes in memory.
|
||||
* @default false
|
||||
*/
|
||||
preserveExitedScopes?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
#### Example usage:
|
||||
```ts
|
||||
import { parseAndWalk, ScopeTracker } from 'oxc-walker'
|
||||
|
||||
const scopeTracker = new ScopeTracker()
|
||||
|
||||
parseAndWalk('const x = 1; function foo() { console.log(x) }', 'example.js', {
|
||||
scopeTracker,
|
||||
enter(node, parent) {
|
||||
if (node.type === 'Identifier' && node.name === 'x' && parent?.type === 'CallExpression') {
|
||||
const declaration = scopeTracker.getDeclaration(node.name)
|
||||
console.log(declaration) // ScopeTrackerVariable
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
```ts
|
||||
import { parseAndWalk, ScopeTracker, walk } from 'oxc-walker'
|
||||
|
||||
const code = `
|
||||
function foo() {
|
||||
console.log(a)
|
||||
}
|
||||
|
||||
const a = 1
|
||||
`
|
||||
|
||||
const scopeTracker = new ScopeTracker({
|
||||
preserveExitedScopes: true,
|
||||
})
|
||||
|
||||
// pre-pass to collect hoisted declarations
|
||||
const { program } = parseAndWalk(code, 'example.js', {
|
||||
scopeTracker,
|
||||
})
|
||||
|
||||
// freeze the scope tracker to prevent further modifications
|
||||
// and prepare it for second pass
|
||||
scopeTracker.freeze()
|
||||
|
||||
// main pass to analyze references
|
||||
walk(program, {
|
||||
scopeTracker,
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression' /* ... */) {
|
||||
const declaration = scopeTracker.getDeclaration('a')
|
||||
console.log(declaration) // ScopeTrackerVariable; would be `null` without the pre-pass
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Helpers:
|
||||
- `scopeTracker.isDeclared(name: string): boolean` - check if an identifier is declared in reference to the current scope
|
||||
- `scopeTracker.getDeclaration(name: string): ScopeTrackerNode | null` - get the scope tracker node with metadata for a given identifier name in reference to the current scope
|
||||
- `scopeTracker.freeze()` - freeze the scope tracker to prevent further modifications and prepare for second pass (useful for multi-pass analysis)
|
||||
- `scopeTracker.getCurrentScope(): string` - get the key of the current scope (a unique identifier for the scope, do not rely on its format)
|
||||
- `scopeTracker.isCurrentScopeUnder(scopeKey: string): boolean` - check if the current scope is a child of the given scope key
|
||||
|
||||
## 💻 Development
|
||||
|
||||
- Clone this repository
|
||||
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
|
||||
- Install dependencies using `pnpm install`
|
||||
- Run interactive tests using `pnpm dev`
|
||||
|
||||
## License
|
||||
|
||||
Made with ❤️
|
||||
|
||||
Published under [MIT License](./LICENCE).
|
||||
|
||||
<!-- Badges -->
|
||||
|
||||
[npm-version-src]: https://img.shields.io/npm/v/oxc-walker?style=flat-square
|
||||
[npm-version-href]: https://npmjs.com/package/oxc-walker
|
||||
[npm-downloads-src]: https://img.shields.io/npm/dm/oxc-walker?style=flat-square
|
||||
[npm-downloads-href]: https://npm.chart.dev/oxc-walker
|
||||
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/danielroe/oxc-walker/ci.yml?branch=main&style=flat-square
|
||||
[github-actions-href]: https://github.com/danielroe/oxc-walker/actions?query=workflow%3Aci
|
||||
[codecov-src]: https://img.shields.io/codecov/c/gh/danielroe/oxc-walker/main?style=flat-square
|
||||
[codecov-href]: https://codecov.io/gh/danielroe/oxc-walker
|
||||
323
Frontend-Learner/node_modules/oxc-walker/dist/index.d.mts
generated
vendored
Normal file
323
Frontend-Learner/node_modules/oxc-walker/dist/index.d.mts
generated
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
import { Node, Program, IdentifierName, IdentifierReference, BindingIdentifier, LabelIdentifier, TSIndexSignatureName, ParseResult, ParserOptions, Function, ArrowFunctionExpression, VariableDeclaration, ImportDeclarationSpecifier, ImportDeclaration, CatchClause } from 'oxc-parser';
|
||||
|
||||
interface WalkerCallbackContext {
|
||||
/**
|
||||
* The key of the current node within its parent node object, if applicable.
|
||||
*
|
||||
* For instance, when processing a `VariableDeclarator` node, this would be the `declarations` key of the parent `VariableDeclaration` node.
|
||||
* @example
|
||||
* {
|
||||
* type: 'VariableDeclaration',
|
||||
* declarations: [[Object]],
|
||||
* // ...
|
||||
* },
|
||||
* { // <-- when processing this, the key would be 'declarations'
|
||||
* type: 'VariableDeclarator',
|
||||
* // ...
|
||||
* },
|
||||
*/
|
||||
key: string | number | symbol | null | undefined;
|
||||
/**
|
||||
* The zero-based index of the current node within its parent's children array, if applicable.
|
||||
* For instance, when processing a `VariableDeclarator` node,
|
||||
* this would be the index of the current `VariableDeclarator` node within the `declarations` array.
|
||||
*
|
||||
* This is `null` when the node is not part of an array.
|
||||
*
|
||||
* @example
|
||||
* {
|
||||
* type: 'VariableDeclaration',
|
||||
* declarations: [[Object]],
|
||||
* // ...
|
||||
* },
|
||||
* { // <-- when processing this, the index would be 0
|
||||
* type: 'VariableDeclarator',
|
||||
* // ...
|
||||
* },
|
||||
*/
|
||||
index: number | null;
|
||||
/**
|
||||
* The full Abstract Syntax Tree (AST) that is being walked, starting from the root node.
|
||||
*/
|
||||
ast: Program | Node;
|
||||
}
|
||||
interface WalkerThisContextLeave {
|
||||
/**
|
||||
* Remove the current node from the AST.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node removal
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
remove: () => void;
|
||||
/**
|
||||
* Replace the current node with another node.
|
||||
* After replacement, the walker will continue with the next sibling of the replaced node.
|
||||
*
|
||||
* In case the current node was removed in the `enter` phase, this will put the new node in
|
||||
* the place of the removed node - essentially undoing the removal.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node replacement
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
replace: (node: Node) => void;
|
||||
}
|
||||
interface WalkerThisContextEnter extends WalkerThisContextLeave {
|
||||
/**
|
||||
* Skip traversing the child nodes of the current node.
|
||||
*/
|
||||
skip: () => void;
|
||||
/**
|
||||
* Remove the current node and all of its children from the AST.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node removal
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
remove: () => void;
|
||||
/**
|
||||
* Replace the current node with another node.
|
||||
* After replacement, the walker will continue to traverse the children of the new node.
|
||||
*
|
||||
* If you want to replace the current node and skip traversing its children, call `this.skip()` after calling `this.replace(newNode)`.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node replacement
|
||||
* @see this.skip
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
replace: (node: Node) => void;
|
||||
}
|
||||
type WalkerCallback<T extends WalkerThisContextLeave> = (this: T, node: Node, parent: Node | null, ctx: WalkerCallbackContext) => void;
|
||||
type WalkerEnter = WalkerCallback<WalkerThisContextEnter>;
|
||||
type WalkerLeave = WalkerCallback<WalkerThisContextLeave>;
|
||||
|
||||
interface _WalkOptions {
|
||||
/**
|
||||
* The instance of `ScopeTracker` to use for tracking declarations and references.
|
||||
* @see ScopeTracker
|
||||
* @default undefined
|
||||
*/
|
||||
scopeTracker: ScopeTracker;
|
||||
}
|
||||
interface WalkOptions extends Partial<_WalkOptions> {
|
||||
/**
|
||||
* The function to be called when entering a node.
|
||||
*/
|
||||
enter: WalkerEnter;
|
||||
/**
|
||||
* The function to be called when leaving a node.
|
||||
*/
|
||||
leave: WalkerLeave;
|
||||
}
|
||||
|
||||
type Identifier = IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier | TSIndexSignatureName;
|
||||
/**
|
||||
* Walk the AST with the given options.
|
||||
* @param input The AST to walk.
|
||||
* @param options The options to be used when walking the AST. Here you can specify the callbacks for entering and leaving nodes, as well as other options.
|
||||
*/
|
||||
declare function walk(input: Program | Node, options: Partial<WalkOptions>): Node | null;
|
||||
interface ParseAndWalkOptions extends WalkOptions {
|
||||
/**
|
||||
* The options for `oxc-parser` to use when parsing the code.
|
||||
*/
|
||||
parseOptions: ParserOptions;
|
||||
}
|
||||
/**
|
||||
* Parse the code and walk the AST with the given callback, which is called when entering a node.
|
||||
* @param code The string with the code to parse and walk. This can be JavaScript, TypeScript, jsx, or tsx.
|
||||
* @param sourceFilename The filename of the source code. This is used to determine the language of the code, unless
|
||||
* it is specified in the parse options.
|
||||
* @param callback The callback to be called when entering a node.
|
||||
*/
|
||||
declare function parseAndWalk(code: string, sourceFilename: string, callback: WalkerEnter): ParseResult;
|
||||
/**
|
||||
* Parse the code and walk the AST with the given callback(s).
|
||||
* @param code The string with the code to parse and walk. This can be JavaScript, TypeScript, jsx, or tsx.
|
||||
* @param sourceFilename The filename of the source code. This is used to determine the language of the code, unless
|
||||
* it is specified in the parse options.
|
||||
* @param options The options to be used when walking the AST. Here you can specify the callbacks for entering and leaving nodes, as well as other options.
|
||||
*/
|
||||
declare function parseAndWalk(code: string, sourceFilename: string, options: Partial<ParseAndWalkOptions>): ParseResult;
|
||||
|
||||
interface ScopeTrackerProtected {
|
||||
processNodeEnter: (node: Node) => void;
|
||||
processNodeLeave: (node: Node) => void;
|
||||
}
|
||||
/**
|
||||
* Tracks variable scopes and identifier declarations within a JavaScript AST.
|
||||
*
|
||||
* Maintains a stack of scopes, each represented as a map from identifier names to their declaration nodes,
|
||||
* enabling efficient lookup of the declaration.
|
||||
*
|
||||
* The ScopeTracker is designed to integrate with the `walk` function,
|
||||
* it automatically manages scope creation and identifier tracking,
|
||||
* so only query and inspection methods are exposed for external use.
|
||||
*
|
||||
* ### Scope tracking
|
||||
* A new scope is created when entering blocks, function parameters, loop variables, etc.
|
||||
* Note that this representation may split a single JavaScript lexical scope into multiple internal scopes,
|
||||
* meaning it doesn't mirror JavaScript’s scoping 1:1.
|
||||
*
|
||||
* Scopes are represented using a string-based index like `"0-1-2"`, which tracks depth and ancestry.
|
||||
*
|
||||
* #### Root scope
|
||||
* The root scope is represented by an empty string `""`.
|
||||
*
|
||||
* #### Scope key format
|
||||
* Scope keys are hierarchical strings that uniquely identify each scope and its position in the tree.
|
||||
* They are constructed using a depth-based indexing scheme, where:
|
||||
*
|
||||
* - the root scope is represented by an empty string `""`.
|
||||
* - the first child scope is `"0"`.
|
||||
* - a parallel sibling of `"0"` becomes `"1"`, `"2"`, etc.
|
||||
* - a nested scope under `"0"` is `"0-0"`, then its sibling is `"0-1"`, and so on.
|
||||
*
|
||||
* Each segment in the key corresponds to the zero-based index of the scope at that depth level in
|
||||
* the order of AST traversal.
|
||||
*
|
||||
* ### Additional features
|
||||
* - supports freezing the tracker to allow for second passes through the AST without modifying the scope data
|
||||
* (useful for doing a pre-pass to collect all identifiers before walking).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const scopeTracker = new ScopeTracker()
|
||||
* walk(code, {
|
||||
* scopeTracker,
|
||||
* enter(node) {
|
||||
* // ...
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @see parseAndWalk
|
||||
* @see walk
|
||||
*/
|
||||
declare class ScopeTracker {
|
||||
protected scopeIndexStack: number[];
|
||||
protected scopeIndexKey: string;
|
||||
protected scopes: Map<string, Map<string, ScopeTrackerNode>>;
|
||||
protected options: Partial<ScopeTrackerOptions>;
|
||||
protected isFrozen: boolean;
|
||||
constructor(options?: ScopeTrackerOptions);
|
||||
protected updateScopeIndexKey(): void;
|
||||
protected pushScope(): void;
|
||||
protected popScope(): void;
|
||||
protected declareIdentifier(name: string, data: ScopeTrackerNode): void;
|
||||
protected declareFunctionParameter(param: Node, fn: Function | ArrowFunctionExpression): void;
|
||||
protected declarePattern(pattern: Node, parent: VariableDeclaration | ArrowFunctionExpression | CatchClause | Function): void;
|
||||
protected processNodeEnter: ScopeTrackerProtected["processNodeEnter"];
|
||||
protected processNodeLeave: ScopeTrackerProtected["processNodeLeave"];
|
||||
/**
|
||||
* Check if an identifier is declared in the current scope or any parent scope.
|
||||
* @param name the identifier name to check
|
||||
*/
|
||||
isDeclared(name: string): boolean;
|
||||
/**
|
||||
* Get the declaration node for a given identifier name.
|
||||
* @param name the identifier name to look up
|
||||
*/
|
||||
getDeclaration(name: string): ScopeTrackerNode | null;
|
||||
/**
|
||||
* Get the current scope key.
|
||||
*/
|
||||
getCurrentScope(): string;
|
||||
/**
|
||||
* Check if the current scope is a child of a specific scope.
|
||||
* @example
|
||||
* ```ts
|
||||
* // current scope is 0-1
|
||||
* isCurrentScopeUnder('0') // true
|
||||
* isCurrentScopeUnder('0-1') // false
|
||||
* ```
|
||||
*
|
||||
* @param scope the parent scope key to check against
|
||||
* @returns `true` if the current scope is a child of the specified scope, `false` otherwise (also when they are the same)
|
||||
*/
|
||||
isCurrentScopeUnder(scope: string): boolean;
|
||||
/**
|
||||
* Freezes the ScopeTracker, preventing further modifications to its state.
|
||||
* It also resets the scope index stack to its initial state so that the tracker can be reused.
|
||||
*
|
||||
* This is useful for second passes through the AST.
|
||||
*/
|
||||
freeze(): void;
|
||||
}
|
||||
declare function isBindingIdentifier(node: Node, parent: Node | null): boolean;
|
||||
declare function getUndeclaredIdentifiersInFunction(node: Function | ArrowFunctionExpression): string[];
|
||||
declare abstract class BaseNode<T extends Node = Node> {
|
||||
abstract type: string;
|
||||
readonly scope: string;
|
||||
node: T;
|
||||
constructor(node: T, scope: string);
|
||||
/**
|
||||
* The starting position of the entire node relevant for code transformation.
|
||||
* For instance, for a reference to a variable (ScopeTrackerVariable -> Identifier), this would refer to the start of the VariableDeclaration.
|
||||
*/
|
||||
abstract get start(): number;
|
||||
/**
|
||||
* The ending position of the entire node relevant for code transformation.
|
||||
* For instance, for a reference to a variable (ScopeTrackerVariable -> Identifier), this would refer to the end of the VariableDeclaration.
|
||||
*/
|
||||
abstract get end(): number;
|
||||
/**
|
||||
* Check if the node is defined under a specific scope.
|
||||
* @param scope
|
||||
*/
|
||||
isUnderScope(scope: string): boolean;
|
||||
}
|
||||
declare class ScopeTrackerIdentifier extends BaseNode<Identifier> {
|
||||
type: "Identifier";
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerFunctionParam extends BaseNode {
|
||||
type: "FunctionParam";
|
||||
fnNode: Function | ArrowFunctionExpression;
|
||||
constructor(node: Node, scope: string, fnNode: Function | ArrowFunctionExpression);
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.start` instead for now.
|
||||
*/
|
||||
get start(): number;
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.end` instead for now.
|
||||
*/
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerFunction extends BaseNode<Function | ArrowFunctionExpression> {
|
||||
type: "Function";
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerVariable extends BaseNode<Identifier> {
|
||||
type: "Variable";
|
||||
variableNode: VariableDeclaration;
|
||||
constructor(node: Identifier, scope: string, variableNode: VariableDeclaration);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerImport extends BaseNode<ImportDeclarationSpecifier> {
|
||||
type: "Import";
|
||||
importNode: ImportDeclaration;
|
||||
constructor(node: ImportDeclarationSpecifier, scope: string, importNode: ImportDeclaration);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerCatchParam extends BaseNode {
|
||||
type: "CatchParam";
|
||||
catchNode: CatchClause;
|
||||
constructor(node: Node, scope: string, catchNode: CatchClause);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
type ScopeTrackerNode = ScopeTrackerFunctionParam | ScopeTrackerFunction | ScopeTrackerVariable | ScopeTrackerIdentifier | ScopeTrackerImport | ScopeTrackerCatchParam;
|
||||
interface ScopeTrackerOptions {
|
||||
/**
|
||||
* If true, the scope tracker will preserve exited scopes in memory.
|
||||
* This is necessary when you want to do a pre-pass to collect all identifiers before walking, for example.
|
||||
* @default false
|
||||
*/
|
||||
preserveExitedScopes?: boolean;
|
||||
}
|
||||
|
||||
export { ScopeTracker, getUndeclaredIdentifiersInFunction, isBindingIdentifier, parseAndWalk, walk };
|
||||
export type { Identifier, ScopeTrackerNode };
|
||||
323
Frontend-Learner/node_modules/oxc-walker/dist/index.d.ts
generated
vendored
Normal file
323
Frontend-Learner/node_modules/oxc-walker/dist/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
import { Node, Program, IdentifierName, IdentifierReference, BindingIdentifier, LabelIdentifier, TSIndexSignatureName, ParseResult, ParserOptions, Function, ArrowFunctionExpression, VariableDeclaration, ImportDeclarationSpecifier, ImportDeclaration, CatchClause } from 'oxc-parser';
|
||||
|
||||
interface WalkerCallbackContext {
|
||||
/**
|
||||
* The key of the current node within its parent node object, if applicable.
|
||||
*
|
||||
* For instance, when processing a `VariableDeclarator` node, this would be the `declarations` key of the parent `VariableDeclaration` node.
|
||||
* @example
|
||||
* {
|
||||
* type: 'VariableDeclaration',
|
||||
* declarations: [[Object]],
|
||||
* // ...
|
||||
* },
|
||||
* { // <-- when processing this, the key would be 'declarations'
|
||||
* type: 'VariableDeclarator',
|
||||
* // ...
|
||||
* },
|
||||
*/
|
||||
key: string | number | symbol | null | undefined;
|
||||
/**
|
||||
* The zero-based index of the current node within its parent's children array, if applicable.
|
||||
* For instance, when processing a `VariableDeclarator` node,
|
||||
* this would be the index of the current `VariableDeclarator` node within the `declarations` array.
|
||||
*
|
||||
* This is `null` when the node is not part of an array.
|
||||
*
|
||||
* @example
|
||||
* {
|
||||
* type: 'VariableDeclaration',
|
||||
* declarations: [[Object]],
|
||||
* // ...
|
||||
* },
|
||||
* { // <-- when processing this, the index would be 0
|
||||
* type: 'VariableDeclarator',
|
||||
* // ...
|
||||
* },
|
||||
*/
|
||||
index: number | null;
|
||||
/**
|
||||
* The full Abstract Syntax Tree (AST) that is being walked, starting from the root node.
|
||||
*/
|
||||
ast: Program | Node;
|
||||
}
|
||||
interface WalkerThisContextLeave {
|
||||
/**
|
||||
* Remove the current node from the AST.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node removal
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
remove: () => void;
|
||||
/**
|
||||
* Replace the current node with another node.
|
||||
* After replacement, the walker will continue with the next sibling of the replaced node.
|
||||
*
|
||||
* In case the current node was removed in the `enter` phase, this will put the new node in
|
||||
* the place of the removed node - essentially undoing the removal.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node replacement
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
replace: (node: Node) => void;
|
||||
}
|
||||
interface WalkerThisContextEnter extends WalkerThisContextLeave {
|
||||
/**
|
||||
* Skip traversing the child nodes of the current node.
|
||||
*/
|
||||
skip: () => void;
|
||||
/**
|
||||
* Remove the current node and all of its children from the AST.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node removal
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
remove: () => void;
|
||||
/**
|
||||
* Replace the current node with another node.
|
||||
* After replacement, the walker will continue to traverse the children of the new node.
|
||||
*
|
||||
* If you want to replace the current node and skip traversing its children, call `this.skip()` after calling `this.replace(newNode)`.
|
||||
* @remarks
|
||||
* - The `ScopeTracker` currently does not support node replacement
|
||||
* @see this.skip
|
||||
* @see ScopeTracker
|
||||
*/
|
||||
replace: (node: Node) => void;
|
||||
}
|
||||
type WalkerCallback<T extends WalkerThisContextLeave> = (this: T, node: Node, parent: Node | null, ctx: WalkerCallbackContext) => void;
|
||||
type WalkerEnter = WalkerCallback<WalkerThisContextEnter>;
|
||||
type WalkerLeave = WalkerCallback<WalkerThisContextLeave>;
|
||||
|
||||
interface _WalkOptions {
|
||||
/**
|
||||
* The instance of `ScopeTracker` to use for tracking declarations and references.
|
||||
* @see ScopeTracker
|
||||
* @default undefined
|
||||
*/
|
||||
scopeTracker: ScopeTracker;
|
||||
}
|
||||
interface WalkOptions extends Partial<_WalkOptions> {
|
||||
/**
|
||||
* The function to be called when entering a node.
|
||||
*/
|
||||
enter: WalkerEnter;
|
||||
/**
|
||||
* The function to be called when leaving a node.
|
||||
*/
|
||||
leave: WalkerLeave;
|
||||
}
|
||||
|
||||
type Identifier = IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier | TSIndexSignatureName;
|
||||
/**
|
||||
* Walk the AST with the given options.
|
||||
* @param input The AST to walk.
|
||||
* @param options The options to be used when walking the AST. Here you can specify the callbacks for entering and leaving nodes, as well as other options.
|
||||
*/
|
||||
declare function walk(input: Program | Node, options: Partial<WalkOptions>): Node | null;
|
||||
interface ParseAndWalkOptions extends WalkOptions {
|
||||
/**
|
||||
* The options for `oxc-parser` to use when parsing the code.
|
||||
*/
|
||||
parseOptions: ParserOptions;
|
||||
}
|
||||
/**
|
||||
* Parse the code and walk the AST with the given callback, which is called when entering a node.
|
||||
* @param code The string with the code to parse and walk. This can be JavaScript, TypeScript, jsx, or tsx.
|
||||
* @param sourceFilename The filename of the source code. This is used to determine the language of the code, unless
|
||||
* it is specified in the parse options.
|
||||
* @param callback The callback to be called when entering a node.
|
||||
*/
|
||||
declare function parseAndWalk(code: string, sourceFilename: string, callback: WalkerEnter): ParseResult;
|
||||
/**
|
||||
* Parse the code and walk the AST with the given callback(s).
|
||||
* @param code The string with the code to parse and walk. This can be JavaScript, TypeScript, jsx, or tsx.
|
||||
* @param sourceFilename The filename of the source code. This is used to determine the language of the code, unless
|
||||
* it is specified in the parse options.
|
||||
* @param options The options to be used when walking the AST. Here you can specify the callbacks for entering and leaving nodes, as well as other options.
|
||||
*/
|
||||
declare function parseAndWalk(code: string, sourceFilename: string, options: Partial<ParseAndWalkOptions>): ParseResult;
|
||||
|
||||
interface ScopeTrackerProtected {
|
||||
processNodeEnter: (node: Node) => void;
|
||||
processNodeLeave: (node: Node) => void;
|
||||
}
|
||||
/**
|
||||
* Tracks variable scopes and identifier declarations within a JavaScript AST.
|
||||
*
|
||||
* Maintains a stack of scopes, each represented as a map from identifier names to their declaration nodes,
|
||||
* enabling efficient lookup of the declaration.
|
||||
*
|
||||
* The ScopeTracker is designed to integrate with the `walk` function,
|
||||
* it automatically manages scope creation and identifier tracking,
|
||||
* so only query and inspection methods are exposed for external use.
|
||||
*
|
||||
* ### Scope tracking
|
||||
* A new scope is created when entering blocks, function parameters, loop variables, etc.
|
||||
* Note that this representation may split a single JavaScript lexical scope into multiple internal scopes,
|
||||
* meaning it doesn't mirror JavaScript’s scoping 1:1.
|
||||
*
|
||||
* Scopes are represented using a string-based index like `"0-1-2"`, which tracks depth and ancestry.
|
||||
*
|
||||
* #### Root scope
|
||||
* The root scope is represented by an empty string `""`.
|
||||
*
|
||||
* #### Scope key format
|
||||
* Scope keys are hierarchical strings that uniquely identify each scope and its position in the tree.
|
||||
* They are constructed using a depth-based indexing scheme, where:
|
||||
*
|
||||
* - the root scope is represented by an empty string `""`.
|
||||
* - the first child scope is `"0"`.
|
||||
* - a parallel sibling of `"0"` becomes `"1"`, `"2"`, etc.
|
||||
* - a nested scope under `"0"` is `"0-0"`, then its sibling is `"0-1"`, and so on.
|
||||
*
|
||||
* Each segment in the key corresponds to the zero-based index of the scope at that depth level in
|
||||
* the order of AST traversal.
|
||||
*
|
||||
* ### Additional features
|
||||
* - supports freezing the tracker to allow for second passes through the AST without modifying the scope data
|
||||
* (useful for doing a pre-pass to collect all identifiers before walking).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const scopeTracker = new ScopeTracker()
|
||||
* walk(code, {
|
||||
* scopeTracker,
|
||||
* enter(node) {
|
||||
* // ...
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @see parseAndWalk
|
||||
* @see walk
|
||||
*/
|
||||
declare class ScopeTracker {
|
||||
protected scopeIndexStack: number[];
|
||||
protected scopeIndexKey: string;
|
||||
protected scopes: Map<string, Map<string, ScopeTrackerNode>>;
|
||||
protected options: Partial<ScopeTrackerOptions>;
|
||||
protected isFrozen: boolean;
|
||||
constructor(options?: ScopeTrackerOptions);
|
||||
protected updateScopeIndexKey(): void;
|
||||
protected pushScope(): void;
|
||||
protected popScope(): void;
|
||||
protected declareIdentifier(name: string, data: ScopeTrackerNode): void;
|
||||
protected declareFunctionParameter(param: Node, fn: Function | ArrowFunctionExpression): void;
|
||||
protected declarePattern(pattern: Node, parent: VariableDeclaration | ArrowFunctionExpression | CatchClause | Function): void;
|
||||
protected processNodeEnter: ScopeTrackerProtected["processNodeEnter"];
|
||||
protected processNodeLeave: ScopeTrackerProtected["processNodeLeave"];
|
||||
/**
|
||||
* Check if an identifier is declared in the current scope or any parent scope.
|
||||
* @param name the identifier name to check
|
||||
*/
|
||||
isDeclared(name: string): boolean;
|
||||
/**
|
||||
* Get the declaration node for a given identifier name.
|
||||
* @param name the identifier name to look up
|
||||
*/
|
||||
getDeclaration(name: string): ScopeTrackerNode | null;
|
||||
/**
|
||||
* Get the current scope key.
|
||||
*/
|
||||
getCurrentScope(): string;
|
||||
/**
|
||||
* Check if the current scope is a child of a specific scope.
|
||||
* @example
|
||||
* ```ts
|
||||
* // current scope is 0-1
|
||||
* isCurrentScopeUnder('0') // true
|
||||
* isCurrentScopeUnder('0-1') // false
|
||||
* ```
|
||||
*
|
||||
* @param scope the parent scope key to check against
|
||||
* @returns `true` if the current scope is a child of the specified scope, `false` otherwise (also when they are the same)
|
||||
*/
|
||||
isCurrentScopeUnder(scope: string): boolean;
|
||||
/**
|
||||
* Freezes the ScopeTracker, preventing further modifications to its state.
|
||||
* It also resets the scope index stack to its initial state so that the tracker can be reused.
|
||||
*
|
||||
* This is useful for second passes through the AST.
|
||||
*/
|
||||
freeze(): void;
|
||||
}
|
||||
declare function isBindingIdentifier(node: Node, parent: Node | null): boolean;
|
||||
declare function getUndeclaredIdentifiersInFunction(node: Function | ArrowFunctionExpression): string[];
|
||||
declare abstract class BaseNode<T extends Node = Node> {
|
||||
abstract type: string;
|
||||
readonly scope: string;
|
||||
node: T;
|
||||
constructor(node: T, scope: string);
|
||||
/**
|
||||
* The starting position of the entire node relevant for code transformation.
|
||||
* For instance, for a reference to a variable (ScopeTrackerVariable -> Identifier), this would refer to the start of the VariableDeclaration.
|
||||
*/
|
||||
abstract get start(): number;
|
||||
/**
|
||||
* The ending position of the entire node relevant for code transformation.
|
||||
* For instance, for a reference to a variable (ScopeTrackerVariable -> Identifier), this would refer to the end of the VariableDeclaration.
|
||||
*/
|
||||
abstract get end(): number;
|
||||
/**
|
||||
* Check if the node is defined under a specific scope.
|
||||
* @param scope
|
||||
*/
|
||||
isUnderScope(scope: string): boolean;
|
||||
}
|
||||
declare class ScopeTrackerIdentifier extends BaseNode<Identifier> {
|
||||
type: "Identifier";
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerFunctionParam extends BaseNode {
|
||||
type: "FunctionParam";
|
||||
fnNode: Function | ArrowFunctionExpression;
|
||||
constructor(node: Node, scope: string, fnNode: Function | ArrowFunctionExpression);
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.start` instead for now.
|
||||
*/
|
||||
get start(): number;
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.end` instead for now.
|
||||
*/
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerFunction extends BaseNode<Function | ArrowFunctionExpression> {
|
||||
type: "Function";
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerVariable extends BaseNode<Identifier> {
|
||||
type: "Variable";
|
||||
variableNode: VariableDeclaration;
|
||||
constructor(node: Identifier, scope: string, variableNode: VariableDeclaration);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerImport extends BaseNode<ImportDeclarationSpecifier> {
|
||||
type: "Import";
|
||||
importNode: ImportDeclaration;
|
||||
constructor(node: ImportDeclarationSpecifier, scope: string, importNode: ImportDeclaration);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
declare class ScopeTrackerCatchParam extends BaseNode {
|
||||
type: "CatchParam";
|
||||
catchNode: CatchClause;
|
||||
constructor(node: Node, scope: string, catchNode: CatchClause);
|
||||
get start(): number;
|
||||
get end(): number;
|
||||
}
|
||||
type ScopeTrackerNode = ScopeTrackerFunctionParam | ScopeTrackerFunction | ScopeTrackerVariable | ScopeTrackerIdentifier | ScopeTrackerImport | ScopeTrackerCatchParam;
|
||||
interface ScopeTrackerOptions {
|
||||
/**
|
||||
* If true, the scope tracker will preserve exited scopes in memory.
|
||||
* This is necessary when you want to do a pre-pass to collect all identifiers before walking, for example.
|
||||
* @default false
|
||||
*/
|
||||
preserveExitedScopes?: boolean;
|
||||
}
|
||||
|
||||
export { ScopeTracker, getUndeclaredIdentifiersInFunction, isBindingIdentifier, parseAndWalk, walk };
|
||||
export type { Identifier, ScopeTrackerNode };
|
||||
627
Frontend-Learner/node_modules/oxc-walker/dist/index.mjs
generated
vendored
Normal file
627
Frontend-Learner/node_modules/oxc-walker/dist/index.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
import { createRegExp, exactly, anyOf } from 'magic-regexp/further-magic';
|
||||
import { parseSync } from 'oxc-parser';
|
||||
|
||||
function isNode(v) {
|
||||
return v !== null && typeof v === "object" && v.type != null && typeof v.type === "string";
|
||||
}
|
||||
|
||||
class WalkerBase {
|
||||
scopeTracker;
|
||||
enter;
|
||||
leave;
|
||||
contextEnter = {
|
||||
skip: () => {
|
||||
this._skip = true;
|
||||
},
|
||||
remove: () => {
|
||||
this._remove = true;
|
||||
},
|
||||
replace: (node) => {
|
||||
this._replacement = node;
|
||||
}
|
||||
};
|
||||
contextLeave = {
|
||||
remove: this.contextEnter.remove,
|
||||
replace: this.contextEnter.replace
|
||||
};
|
||||
_skip = false;
|
||||
_remove = false;
|
||||
_replacement = null;
|
||||
constructor(handler, options) {
|
||||
this.enter = handler.enter;
|
||||
this.leave = handler.leave;
|
||||
this.scopeTracker = options?.scopeTracker;
|
||||
}
|
||||
replace(parent, key, index, node) {
|
||||
if (!parent || key === null) {
|
||||
return;
|
||||
}
|
||||
if (index !== null) {
|
||||
parent[key][index] = node;
|
||||
} else {
|
||||
parent[key] = node;
|
||||
}
|
||||
}
|
||||
insert(parent, key, index, node) {
|
||||
if (!parent || key === null) return;
|
||||
if (index !== null) {
|
||||
parent[key].splice(index, 0, node);
|
||||
} else {
|
||||
parent[key] = node;
|
||||
}
|
||||
}
|
||||
remove(parent, key, index) {
|
||||
if (!parent || key === null) {
|
||||
return;
|
||||
}
|
||||
if (index !== null) {
|
||||
parent[key].splice(index, 1);
|
||||
} else {
|
||||
delete parent[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WalkerSync extends WalkerBase {
|
||||
constructor(handler, options) {
|
||||
super(handler, options);
|
||||
}
|
||||
traverse(input, key, index, parent) {
|
||||
const ast = input;
|
||||
const ctx = { key: null, index: index ?? null, ast };
|
||||
const hasScopeTracker = !!this.scopeTracker;
|
||||
const _walk = (input2, parent2, key2, index2, skip) => {
|
||||
if (!isNode(input2)) {
|
||||
return null;
|
||||
}
|
||||
this.scopeTracker?.processNodeEnter(input2);
|
||||
let currentNode = input2;
|
||||
let removedInEnter = false;
|
||||
let skipChildren = skip;
|
||||
if (this.enter && !skip) {
|
||||
const _skip = this._skip;
|
||||
const _remove = this._remove;
|
||||
const _replacement = this._replacement;
|
||||
this._skip = false;
|
||||
this._remove = false;
|
||||
this._replacement = null;
|
||||
ctx.key = key2;
|
||||
ctx.index = index2;
|
||||
this.enter.call(this.contextEnter, input2, parent2, ctx);
|
||||
if (this._replacement && !this._remove) {
|
||||
currentNode = this._replacement;
|
||||
this.replace(parent2, key2, index2, this._replacement);
|
||||
}
|
||||
if (this._remove) {
|
||||
removedInEnter = true;
|
||||
currentNode = null;
|
||||
this.remove(parent2, key2, index2);
|
||||
}
|
||||
if (this._skip) {
|
||||
skipChildren = true;
|
||||
}
|
||||
this._skip = _skip;
|
||||
this._remove = _remove;
|
||||
this._replacement = _replacement;
|
||||
}
|
||||
if ((!skipChildren || hasScopeTracker) && currentNode) {
|
||||
for (const k in currentNode) {
|
||||
const node = currentNode[k];
|
||||
if (!node || typeof node !== "object") {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
for (let i = 0; i < node.length; i++) {
|
||||
const child = node[i];
|
||||
if (isNode(child)) {
|
||||
if (_walk(
|
||||
child,
|
||||
currentNode,
|
||||
k,
|
||||
i,
|
||||
skipChildren
|
||||
) === null) {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isNode(node)) {
|
||||
_walk(node, currentNode, k, null, skipChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.scopeTracker?.processNodeLeave(input2);
|
||||
if (this.leave && !skip) {
|
||||
const _replacement = this._replacement;
|
||||
const _remove = this._remove;
|
||||
this._replacement = null;
|
||||
this._remove = false;
|
||||
ctx.key = key2;
|
||||
ctx.index = index2;
|
||||
this.leave.call(this.contextLeave, input2, parent2, ctx);
|
||||
if (this._replacement && !this._remove) {
|
||||
currentNode = this._replacement;
|
||||
if (removedInEnter) {
|
||||
this.insert(parent2, key2, index2, this._replacement);
|
||||
} else {
|
||||
this.replace(parent2, key2, index2, this._replacement);
|
||||
}
|
||||
}
|
||||
if (this._remove) {
|
||||
currentNode = null;
|
||||
this.remove(parent2, key2, index2);
|
||||
}
|
||||
this._replacement = _replacement;
|
||||
this._remove = _remove;
|
||||
}
|
||||
return currentNode;
|
||||
};
|
||||
return _walk(input, parent ?? null, key ?? null, index ?? null, false);
|
||||
}
|
||||
}
|
||||
|
||||
function walk(input, options) {
|
||||
return new WalkerSync(
|
||||
{
|
||||
enter: options.enter,
|
||||
leave: options.leave
|
||||
},
|
||||
{
|
||||
scopeTracker: options.scopeTracker
|
||||
}
|
||||
).traverse(input);
|
||||
}
|
||||
const LANG_RE = createRegExp(
|
||||
exactly("jsx").or("tsx").or("js").or("ts").groupedAs("lang").after(exactly(".").and(anyOf("c", "m").optionally())).at.lineEnd()
|
||||
);
|
||||
function parseAndWalk(code, sourceFilename, arg3) {
|
||||
const lang = sourceFilename?.match(LANG_RE)?.groups?.lang;
|
||||
const { parseOptions: _parseOptions = {}, ...options } = typeof arg3 === "function" ? { enter: arg3 } : arg3;
|
||||
const parseOptions = {
|
||||
sourceType: "module",
|
||||
lang,
|
||||
..._parseOptions
|
||||
};
|
||||
const ast = parseSync(sourceFilename, code, parseOptions);
|
||||
walk(ast.program, options);
|
||||
return ast;
|
||||
}
|
||||
|
||||
class ScopeTracker {
|
||||
scopeIndexStack = [];
|
||||
scopeIndexKey = "";
|
||||
scopes = /* @__PURE__ */ new Map();
|
||||
options;
|
||||
isFrozen = false;
|
||||
constructor(options = {}) {
|
||||
this.options = options;
|
||||
}
|
||||
updateScopeIndexKey() {
|
||||
this.scopeIndexKey = this.scopeIndexStack.slice(0, -1).join("-");
|
||||
}
|
||||
pushScope() {
|
||||
this.scopeIndexStack.push(0);
|
||||
this.updateScopeIndexKey();
|
||||
}
|
||||
popScope() {
|
||||
this.scopeIndexStack.pop();
|
||||
if (this.scopeIndexStack[this.scopeIndexStack.length - 1] !== void 0) {
|
||||
this.scopeIndexStack[this.scopeIndexStack.length - 1]++;
|
||||
}
|
||||
if (!this.options.preserveExitedScopes) {
|
||||
this.scopes.delete(this.scopeIndexKey);
|
||||
}
|
||||
this.updateScopeIndexKey();
|
||||
}
|
||||
declareIdentifier(name, data) {
|
||||
if (this.isFrozen) {
|
||||
return;
|
||||
}
|
||||
let scope = this.scopes.get(this.scopeIndexKey);
|
||||
if (!scope) {
|
||||
scope = /* @__PURE__ */ new Map();
|
||||
this.scopes.set(this.scopeIndexKey, scope);
|
||||
}
|
||||
scope.set(name, data);
|
||||
}
|
||||
declareFunctionParameter(param, fn) {
|
||||
if (this.isFrozen) {
|
||||
return;
|
||||
}
|
||||
const identifiers = getPatternIdentifiers(param);
|
||||
for (const identifier of identifiers) {
|
||||
this.declareIdentifier(
|
||||
identifier.name,
|
||||
new ScopeTrackerFunctionParam(identifier, this.scopeIndexKey, fn)
|
||||
);
|
||||
}
|
||||
}
|
||||
declarePattern(pattern, parent) {
|
||||
if (this.isFrozen) {
|
||||
return;
|
||||
}
|
||||
const identifiers = getPatternIdentifiers(pattern);
|
||||
for (const identifier of identifiers) {
|
||||
this.declareIdentifier(
|
||||
identifier.name,
|
||||
parent.type === "VariableDeclaration" ? new ScopeTrackerVariable(identifier, this.scopeIndexKey, parent) : parent.type === "CatchClause" ? new ScopeTrackerCatchParam(identifier, this.scopeIndexKey, parent) : new ScopeTrackerFunctionParam(
|
||||
identifier,
|
||||
this.scopeIndexKey,
|
||||
parent
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
processNodeEnter = (node) => {
|
||||
switch (node.type) {
|
||||
case "Program":
|
||||
case "BlockStatement":
|
||||
case "StaticBlock":
|
||||
this.pushScope();
|
||||
break;
|
||||
case "FunctionDeclaration":
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(
|
||||
node.id.name,
|
||||
new ScopeTrackerFunction(node, this.scopeIndexKey)
|
||||
);
|
||||
}
|
||||
this.pushScope();
|
||||
for (const param of node.params) {
|
||||
this.declareFunctionParameter(param, node);
|
||||
}
|
||||
break;
|
||||
case "FunctionExpression":
|
||||
this.pushScope();
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(
|
||||
node.id.name,
|
||||
new ScopeTrackerFunction(node, this.scopeIndexKey)
|
||||
);
|
||||
}
|
||||
this.pushScope();
|
||||
for (const param of node.params) {
|
||||
this.declareFunctionParameter(param, node);
|
||||
}
|
||||
break;
|
||||
case "ArrowFunctionExpression":
|
||||
this.pushScope();
|
||||
for (const param of node.params) {
|
||||
this.declareFunctionParameter(param, node);
|
||||
}
|
||||
break;
|
||||
case "VariableDeclaration":
|
||||
for (const decl of node.declarations) {
|
||||
this.declarePattern(decl.id, node);
|
||||
}
|
||||
break;
|
||||
case "ClassDeclaration":
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(
|
||||
node.id.name,
|
||||
new ScopeTrackerIdentifier(node.id, this.scopeIndexKey)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "ClassExpression":
|
||||
this.pushScope();
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(
|
||||
node.id.name,
|
||||
new ScopeTrackerIdentifier(node.id, this.scopeIndexKey)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "ImportDeclaration":
|
||||
for (const specifier of node.specifiers) {
|
||||
this.declareIdentifier(
|
||||
specifier.local.name,
|
||||
new ScopeTrackerImport(specifier, this.scopeIndexKey, node)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "CatchClause":
|
||||
this.pushScope();
|
||||
if (node.param) {
|
||||
this.declarePattern(node.param, node);
|
||||
}
|
||||
break;
|
||||
case "ForStatement":
|
||||
case "ForOfStatement":
|
||||
case "ForInStatement":
|
||||
this.pushScope();
|
||||
if (node.type === "ForStatement" && node.init?.type === "VariableDeclaration") {
|
||||
for (const decl of node.init.declarations) {
|
||||
this.declarePattern(decl.id, node.init);
|
||||
}
|
||||
} else if ((node.type === "ForOfStatement" || node.type === "ForInStatement") && node.left.type === "VariableDeclaration") {
|
||||
for (const decl of node.left.declarations) {
|
||||
this.declarePattern(decl.id, node.left);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
processNodeLeave = (node) => {
|
||||
switch (node.type) {
|
||||
case "Program":
|
||||
case "BlockStatement":
|
||||
case "CatchClause":
|
||||
case "FunctionDeclaration":
|
||||
case "ArrowFunctionExpression":
|
||||
case "StaticBlock":
|
||||
case "ClassExpression":
|
||||
case "ForStatement":
|
||||
case "ForOfStatement":
|
||||
case "ForInStatement":
|
||||
this.popScope();
|
||||
break;
|
||||
case "FunctionExpression":
|
||||
this.popScope();
|
||||
this.popScope();
|
||||
break;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Check if an identifier is declared in the current scope or any parent scope.
|
||||
* @param name the identifier name to check
|
||||
*/
|
||||
isDeclared(name) {
|
||||
if (!this.scopeIndexKey) {
|
||||
return this.scopes.get("")?.has(name) || false;
|
||||
}
|
||||
const indices = this.scopeIndexKey.split("-").map(Number);
|
||||
for (let i = indices.length; i >= 0; i--) {
|
||||
if (this.scopes.get(indices.slice(0, i).join("-"))?.has(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get the declaration node for a given identifier name.
|
||||
* @param name the identifier name to look up
|
||||
*/
|
||||
getDeclaration(name) {
|
||||
if (!this.scopeIndexKey) {
|
||||
return this.scopes.get("")?.get(name) ?? null;
|
||||
}
|
||||
const indices = this.scopeIndexKey.split("-").map(Number);
|
||||
for (let i = indices.length; i >= 0; i--) {
|
||||
const node = this.scopes.get(indices.slice(0, i).join("-"))?.get(name);
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get the current scope key.
|
||||
*/
|
||||
getCurrentScope() {
|
||||
return this.scopeIndexKey;
|
||||
}
|
||||
/**
|
||||
* Check if the current scope is a child of a specific scope.
|
||||
* @example
|
||||
* ```ts
|
||||
* // current scope is 0-1
|
||||
* isCurrentScopeUnder('0') // true
|
||||
* isCurrentScopeUnder('0-1') // false
|
||||
* ```
|
||||
*
|
||||
* @param scope the parent scope key to check against
|
||||
* @returns `true` if the current scope is a child of the specified scope, `false` otherwise (also when they are the same)
|
||||
*/
|
||||
isCurrentScopeUnder(scope) {
|
||||
return isChildScope(this.scopeIndexKey, scope);
|
||||
}
|
||||
/**
|
||||
* Freezes the ScopeTracker, preventing further modifications to its state.
|
||||
* It also resets the scope index stack to its initial state so that the tracker can be reused.
|
||||
*
|
||||
* This is useful for second passes through the AST.
|
||||
*/
|
||||
freeze() {
|
||||
this.isFrozen = true;
|
||||
this.scopeIndexStack = [];
|
||||
this.updateScopeIndexKey();
|
||||
}
|
||||
}
|
||||
function getPatternIdentifiers(pattern) {
|
||||
const identifiers = [];
|
||||
function collectIdentifiers(pattern2) {
|
||||
switch (pattern2.type) {
|
||||
case "Identifier":
|
||||
identifiers.push(pattern2);
|
||||
break;
|
||||
case "AssignmentPattern":
|
||||
collectIdentifiers(pattern2.left);
|
||||
break;
|
||||
case "RestElement":
|
||||
collectIdentifiers(pattern2.argument);
|
||||
break;
|
||||
case "ArrayPattern":
|
||||
for (const element of pattern2.elements) {
|
||||
if (element) {
|
||||
collectIdentifiers(
|
||||
element.type === "RestElement" ? element.argument : element
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "ObjectPattern":
|
||||
for (const property of pattern2.properties) {
|
||||
collectIdentifiers(
|
||||
property.type === "RestElement" ? property.argument : property.value
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
collectIdentifiers(pattern);
|
||||
return identifiers;
|
||||
}
|
||||
function isBindingIdentifier(node, parent) {
|
||||
if (!parent || node.type !== "Identifier") {
|
||||
return false;
|
||||
}
|
||||
switch (parent.type) {
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
if (parent.type !== "ArrowFunctionExpression" && parent.id === node) {
|
||||
return true;
|
||||
}
|
||||
if (parent.params.length) {
|
||||
for (const param of parent.params) {
|
||||
const identifiers = getPatternIdentifiers(param);
|
||||
if (identifiers.includes(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case "ClassDeclaration":
|
||||
case "ClassExpression":
|
||||
return parent.id === node;
|
||||
case "MethodDefinition":
|
||||
return parent.key === node;
|
||||
case "PropertyDefinition":
|
||||
return parent.key === node;
|
||||
case "VariableDeclarator":
|
||||
return getPatternIdentifiers(parent.id).includes(node);
|
||||
case "CatchClause":
|
||||
if (!parent.param) {
|
||||
return false;
|
||||
}
|
||||
return getPatternIdentifiers(parent.param).includes(node);
|
||||
case "Property":
|
||||
return parent.key === node && parent.value !== node;
|
||||
case "MemberExpression":
|
||||
return parent.property === node;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function getUndeclaredIdentifiersInFunction(node) {
|
||||
const scopeTracker = new ScopeTracker({
|
||||
preserveExitedScopes: true
|
||||
});
|
||||
const undeclaredIdentifiers = /* @__PURE__ */ new Set();
|
||||
function isIdentifierUndeclared(node2, parent) {
|
||||
return !isBindingIdentifier(node2, parent) && !scopeTracker.isDeclared(node2.name);
|
||||
}
|
||||
walk(node, {
|
||||
scopeTracker
|
||||
});
|
||||
scopeTracker.freeze();
|
||||
walk(node, {
|
||||
scopeTracker,
|
||||
enter(node2, parent) {
|
||||
if (node2.type === "Identifier" && isIdentifierUndeclared(node2, parent)) {
|
||||
undeclaredIdentifiers.add(node2.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
return Array.from(undeclaredIdentifiers);
|
||||
}
|
||||
function isChildScope(a, b) {
|
||||
return a.startsWith(b) && a.length > b.length;
|
||||
}
|
||||
class BaseNode {
|
||||
scope;
|
||||
node;
|
||||
constructor(node, scope) {
|
||||
this.node = node;
|
||||
this.scope = scope;
|
||||
}
|
||||
/**
|
||||
* Check if the node is defined under a specific scope.
|
||||
* @param scope
|
||||
*/
|
||||
isUnderScope(scope) {
|
||||
return isChildScope(this.scope, scope);
|
||||
}
|
||||
}
|
||||
class ScopeTrackerIdentifier extends BaseNode {
|
||||
type = "Identifier";
|
||||
get start() {
|
||||
return this.node.start;
|
||||
}
|
||||
get end() {
|
||||
return this.node.end;
|
||||
}
|
||||
}
|
||||
class ScopeTrackerFunctionParam extends BaseNode {
|
||||
type = "FunctionParam";
|
||||
fnNode;
|
||||
constructor(node, scope, fnNode) {
|
||||
super(node, scope);
|
||||
this.fnNode = fnNode;
|
||||
}
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.start` instead for now.
|
||||
*/
|
||||
get start() {
|
||||
return this.fnNode.start;
|
||||
}
|
||||
/**
|
||||
* @deprecated The representation of this position may change in the future. Use `.fnNode.end` instead for now.
|
||||
*/
|
||||
get end() {
|
||||
return this.fnNode.end;
|
||||
}
|
||||
}
|
||||
class ScopeTrackerFunction extends BaseNode {
|
||||
type = "Function";
|
||||
get start() {
|
||||
return this.node.start;
|
||||
}
|
||||
get end() {
|
||||
return this.node.end;
|
||||
}
|
||||
}
|
||||
class ScopeTrackerVariable extends BaseNode {
|
||||
type = "Variable";
|
||||
variableNode;
|
||||
constructor(node, scope, variableNode) {
|
||||
super(node, scope);
|
||||
this.variableNode = variableNode;
|
||||
}
|
||||
get start() {
|
||||
return this.variableNode.start;
|
||||
}
|
||||
get end() {
|
||||
return this.variableNode.end;
|
||||
}
|
||||
}
|
||||
class ScopeTrackerImport extends BaseNode {
|
||||
type = "Import";
|
||||
importNode;
|
||||
constructor(node, scope, importNode) {
|
||||
super(node, scope);
|
||||
this.importNode = importNode;
|
||||
}
|
||||
get start() {
|
||||
return this.importNode.start;
|
||||
}
|
||||
get end() {
|
||||
return this.importNode.end;
|
||||
}
|
||||
}
|
||||
class ScopeTrackerCatchParam extends BaseNode {
|
||||
type = "CatchParam";
|
||||
catchNode;
|
||||
constructor(node, scope, catchNode) {
|
||||
super(node, scope);
|
||||
this.catchNode = catchNode;
|
||||
}
|
||||
get start() {
|
||||
return this.catchNode.start;
|
||||
}
|
||||
get end() {
|
||||
return this.catchNode.end;
|
||||
}
|
||||
}
|
||||
|
||||
export { ScopeTracker, getUndeclaredIdentifiersInFunction, isBindingIdentifier, parseAndWalk, walk };
|
||||
60
Frontend-Learner/node_modules/oxc-walker/package.json
generated
vendored
Normal file
60
Frontend-Learner/node_modules/oxc-walker/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "oxc-walker",
|
||||
"type": "module",
|
||||
"version": "0.6.0",
|
||||
"description": "",
|
||||
"license": "MIT",
|
||||
"repository": "oxc-project/oxc-walker",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/index.mjs"
|
||||
},
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"oxc-parser": ">=0.98.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"magic-regexp": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "24.10.1",
|
||||
"@vitest/coverage-v8": "4.0.10",
|
||||
"knip": "^5.69.1",
|
||||
"lint-staged": "16.2.6",
|
||||
"oxc-parser": "0.98.0",
|
||||
"oxfmt": "^0.14.0",
|
||||
"oxlint": "^1.29.0",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"typescript": "5.9.3",
|
||||
"unbuild": "3.6.1",
|
||||
"vitest": "4.0.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"oxc-walker": "link:."
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,mjs,cjs}": [
|
||||
"pnpm run fmt"
|
||||
],
|
||||
"*.{js,ts,mjs,cjs,json,.*rc}": [
|
||||
"pnpm run lint --fix"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"dev": "vitest dev",
|
||||
"lint": "oxlint",
|
||||
"fmt": "oxfmt",
|
||||
"test": "pnpm test:unit && pnpm test:types",
|
||||
"test:unit": "vitest",
|
||||
"test:types": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue