This commit is contained in:
2024-11-28 23:08:17 +01:00
parent 8895fde030
commit 0dda8e760c
16116 changed files with 2866428 additions and 71 deletions

21
node_modules/vue-eslint-parser/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Toru Nagashima
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.

330
node_modules/vue-eslint-parser/README.md generated vendored Normal file
View File

@@ -0,0 +1,330 @@
# vue-eslint-parser
[![npm version](https://img.shields.io/npm/v/vue-eslint-parser.svg)](https://www.npmjs.com/package/vue-eslint-parser)
[![Downloads/month](https://img.shields.io/npm/dm/vue-eslint-parser.svg)](http://www.npmtrends.com/vue-eslint-parser)
[![Build Status](https://github.com/vuejs/vue-eslint-parser/workflows/CI/badge.svg)](https://github.com/vuejs/vue-eslint-parser/actions)
[![Coverage Status](https://codecov.io/gh/vuejs/vue-eslint-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/vuejs/vue-eslint-parser)
The ESLint custom parser for `.vue` files.
## ⤴️ Motivation
This parser allows us to lint the `<template>` of `.vue` files. We can make mistakes easily on `<template>` if we use complex directives and expressions in the template. This parser and the rules of [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) would catch some of the mistakes.
## 💿 Installation
```bash
npm install --save-dev eslint vue-eslint-parser
```
- Requires Node.js ^14.17.0, 16.0.0 or later.
- Requires ESLint 6.0.0 or later.
## 📖 Usage
1. Write `parser` option into your `.eslintrc.*` file.
2. Use glob patterns or `--ext .vue` CLI option.
```json
{
"extends": "eslint:recommended",
"parser": "vue-eslint-parser"
}
```
```console
$ eslint "src/**/*.{js,vue}"
# or
$ eslint src --ext .vue
```
## 🔧 Options
`parserOptions` has the same properties as what [espree](https://github.com/eslint/espree#usage), the default parser of ESLint, is supporting.
For example:
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2018,
"ecmaFeatures": {
"globalReturn": false,
"impliedStrict": false,
"jsx": false
}
}
}
```
### parserOptions.parser
You can use `parserOptions.parser` property to specify a custom parser to parse `<script>` tags.
Other properties than parser would be given to the specified parser.
For example:
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@babel/eslint-parser",
"sourceType": "module"
}
}
```
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module"
}
}
```
You can also specify an object and change the parser separately for `<script lang="...">`.
```jsonc
{
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": {
// Script parser for `<script>`
"js": "espree",
// Script parser for `<script lang="ts">`
"ts": "@typescript-eslint/parser",
// Script parser for vue directives (e.g. `v-if=` or `:attribute=`)
// and vue interpolations (e.g. `{{variable}}`).
// If not specified, the parser determined by `<script lang ="...">` is used.
"<template>": "espree",
}
}
}
```
When using JavaScript configuration (`.eslintrc.js`), you can also give the parser object directly.
```js
const tsParser = require("@typescript-eslint/parser")
const espree = require("espree")
module.exports = {
parser: "vue-eslint-parser",
parserOptions: {
// Single parser
parser: tsParser,
// Multiple parser
parser: {
js: espree,
ts: tsParser,
}
},
}
```
If the `parserOptions.parser` is `false`, the `vue-eslint-parser` skips parsing `<script>` tags completely.
This is useful for people who use the language ESLint community doesn't provide custom parser implementation.
### parserOptions.vueFeatures
You can use `parserOptions.vueFeatures` property to specify how to parse related to Vue features.
For example:
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"vueFeatures": {
"filter": true,
"interpolationAsNonHTML": true,
"styleCSSVariableInjection": true,
"customMacros": []
}
}
}
```
### parserOptions.vueFeatures.filter
You can use `parserOptions.vueFeatures.filter` property to specify whether to parse the Vue2 filter. If you specify `false`, the parser does not parse `|` as a filter.
For example:
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"vueFeatures": {
"filter": false
}
}
}
```
If you specify `false`, it can be parsed in the same way as Vue 3.
The following template parses as a bitwise operation.
```vue
<template>
<div>{{ a | b }}</div>
</template>
```
However, the following template that are valid in Vue 2 cannot be parsed.
```vue
<template>
<div>{{ a | valid:filter }}</div>
</template>
```
### parserOptions.vueFeatures.interpolationAsNonHTML
You can use `parserOptions.vueFeatures.interpolationAsNonHTML` property to specify whether to parse the interpolation as HTML. If you specify `true`, the parser handles the interpolation as non-HTML (However, you can use HTML escaping in the interpolation). Default is `true`.
For example:
```json
{
"parser": "vue-eslint-parser",
"parserOptions": {
"vueFeatures": {
"interpolationAsNonHTML": true
}
}
}
```
If you specify `true`, it can be parsed in the same way as Vue 3.
The following template can be parsed well.
```vue
<template>
<div>{{a<b}}</div>
</template>
```
But, it cannot be parsed with Vue 2.
### parserOptions.vueFeatures.styleCSSVariableInjection
If set to `true`, to parse expressions in `v-bind` CSS functions inside `<style>` tags. `v-bind()` is parsed into the `VExpressionContainer` AST node and held in the `VElement` of `<style>`. Default is `true`.
See also to [here](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0043-sfc-style-variables.md).
### parserOptions.vueFeatures.customMacros
Specifies an array of names of custom macros other than Vue standard macros.
For example, if you have a custom macro `defineFoo()` and you want it processed by the parser, specify `["defineFoo"]`.
Note that this option only works in `<script setup>`.
### parserOptions.templateTokenizer
**This is an experimental feature. It may be changed or deleted without notice in the minor version.**
You can use `parserOptions.templateTokenizer` property to specify custom tokenizers to parse `<template lang="...">` tags.
For example to enable parsing of pug templates:
```jsonc
{
"parser": "vue-eslint-parser",
"parserOptions": {
"templateTokenizer": {
// template tokenizer for `<template lang="pug">`
"pug": "vue-eslint-parser-template-tokenizer-pug",
}
}
}
```
This option is only intended for plugin developers. **Be careful** when using this option directly, as it may change behaviour of rules you might have enabled.
If you just want **pug** support, use [eslint-plugin-vue-pug](https://github.com/rashfael/eslint-plugin-vue-pug) instead, which uses this option internally.
See [implementing-custom-template-tokenizers.md](./docs/implementing-custom-template-tokenizers.md) for information on creating your own template tokenizer.
## 🎇 Usage for custom rules / plugins
- This parser provides `parserServices` to traverse `<template>`.
- `defineTemplateBodyVisitor(templateVisitor, scriptVisitor, options)` ... returns ESLint visitor to traverse `<template>`.
- `getTemplateBodyTokenStore()` ... returns ESLint `TokenStore` to get the tokens of `<template>`.
- `getDocumentFragment()` ... returns the root `VDocumentFragment`.
- `defineCustomBlocksVisitor(context, customParser, rule, scriptVisitor)` ... returns ESLint visitor that parses and traverses the contents of the custom block.
- `defineDocumentVisitor(documentVisitor, options)` ... returns ESLint visitor to traverses the document.
- [ast.md](./docs/ast.md) is `<template>` AST specification.
- [mustache-interpolation-spacing.js](https://github.com/vuejs/eslint-plugin-vue/blob/b434ff99d37f35570fa351681e43ba2cf5746db3/lib/rules/mustache-interpolation-spacing.js) is an example.
### `defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor, options)`
*Arguments*
- `templateBodyVisitor` ... Event handlers for `<template>`.
- `scriptVisitor` ... Event handlers for `<script>` or scripts. (optional)
- `options` ... Options. (optional)
- `templateBodyTriggerSelector` ... Script AST node selector that triggers the templateBodyVisitor. Default is `"Program:exit"`. (optional)
```ts
import { AST } from "vue-eslint-parser"
export function create(context) {
return context.parserServices.defineTemplateBodyVisitor(
// Event handlers for <template>.
{
VElement(node: AST.VElement): void {
//...
}
},
// Event handlers for <script> or scripts. (optional)
{
Program(node: AST.ESLintProgram): void {
//...
}
},
// Options. (optional)
{
templateBodyTriggerSelector: "Program:exit"
}
)
}
```
## ⚠️ Known Limitations
Some rules make warnings due to the outside of `<script>` tags.
Please disable those rules for `.vue` files as necessary.
- [eol-last](http://eslint.org/docs/rules/eol-last)
- [linebreak-style](http://eslint.org/docs/rules/linebreak-style)
- [max-len](http://eslint.org/docs/rules/max-len)
- [max-lines](http://eslint.org/docs/rules/max-lines)
- [no-trailing-spaces](http://eslint.org/docs/rules/no-trailing-spaces)
- [unicode-bom](http://eslint.org/docs/rules/unicode-bom)
- Other rules which are using the source code text instead of AST might be confused as well.
## 📰 Changelog
- [GitHub Releases](https://github.com/vuejs/vue-eslint-parser/releases)
## 🍻 Contributing
Welcome contributing!
Please use GitHub's Issues/PRs.
If you want to write code, please execute `npm install && npm run setup` after you cloned this repository.
The `npm install` command installs dependencies.
The `npm run setup` command initializes ESLint as git submodules for tests.
### Development Tools
- `npm test` runs tests and measures coverage.
- `npm run build` compiles TypeScript source code to `index.js`, `index.js.map`, and `index.d.ts`.
- `npm run coverage` shows the coverage result of `npm test` command with the default browser.
- `npm run clean` removes the temporary files which are created by `npm test` and `npm run build`.
- `npm run lint` runs ESLint.
- `npm run setup` setups submodules to develop.
- `npm run update-fixtures` updates files in `test/fixtures/ast` directory based on `test/fixtures/ast/*/source.vue` files.
- `npm run watch` runs `build`, `update-fixtures`, and tests with `--watch` option.

667
node_modules/vue-eslint-parser/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,667 @@
// Generated by dts-bundle v0.7.3
// Dependencies for this module:
// ../eslint-scope
// ../@typescript-eslint/utils
// ../eslint-visitor-keys
declare module 'vue-eslint-parser' {
import * as AST from "vue-eslint-parser/ast";
export function parseForESLint(code: string, parserOptions: any): AST.ESLintExtendedProgram;
export function parse(code: string, options: any): AST.ESLintProgram;
export { AST };
export const meta: {
name: string;
version: string | undefined;
};
}
declare module 'vue-eslint-parser/ast' {
export * from "vue-eslint-parser/ast/errors";
export * from "vue-eslint-parser/ast/locations";
export * from "vue-eslint-parser/ast/nodes";
export * from "vue-eslint-parser/ast/tokens";
export * from "vue-eslint-parser/ast/traverse";
}
declare module 'vue-eslint-parser/ast/errors' {
export class ParseError extends SyntaxError {
code?: ErrorCode;
index: number;
lineNumber: number;
column: number;
static fromCode(code: ErrorCode, offset: number, line: number, column: number): ParseError;
static normalize(x: any): ParseError | null;
constructor(message: string, code: ErrorCode | undefined, offset: number, line: number, column: number);
static isParseError(x: any): x is ParseError;
}
export type ErrorCode = "abrupt-closing-of-empty-comment" | "absence-of-digits-in-numeric-character-reference" | "cdata-in-html-content" | "character-reference-outside-unicode-range" | "control-character-in-input-stream" | "control-character-reference" | "eof-before-tag-name" | "eof-in-cdata" | "eof-in-comment" | "eof-in-tag" | "incorrectly-closed-comment" | "incorrectly-opened-comment" | "invalid-first-character-of-tag-name" | "missing-attribute-value" | "missing-end-tag-name" | "missing-semicolon-after-character-reference" | "missing-whitespace-between-attributes" | "nested-comment" | "noncharacter-character-reference" | "noncharacter-in-input-stream" | "null-character-reference" | "surrogate-character-reference" | "surrogate-in-input-stream" | "unexpected-character-in-attribute-name" | "unexpected-character-in-unquoted-attribute-value" | "unexpected-equals-sign-before-attribute-name" | "unexpected-null-character" | "unexpected-question-mark-instead-of-tag-name" | "unexpected-solidus-in-tag" | "unknown-named-character-reference" | "end-tag-with-attributes" | "duplicate-attribute" | "end-tag-with-trailing-solidus" | "non-void-html-element-start-tag-with-trailing-solidus" | "x-invalid-end-tag" | "x-invalid-namespace" | "x-missing-interpolation-end";
}
declare module 'vue-eslint-parser/ast/locations' {
export interface Location {
line: number;
column: number;
}
export interface LocationRange {
start: Location;
end: Location;
}
export type Offset = number;
export type OffsetRange = [Offset, Offset];
export interface HasLocation {
range: OffsetRange;
loc: LocationRange;
start?: number;
end?: number;
}
}
declare module 'vue-eslint-parser/ast/nodes' {
import type { ScopeManager } from "eslint-scope";
import type { ParseError } from "vue-eslint-parser/ast/errors";
import type { HasLocation } from "vue-eslint-parser/ast/locations";
import type { Token } from "vue-eslint-parser/ast/tokens";
import type { TSESTree } from "@typescript-eslint/utils";
export interface HasParent {
parent?: Node | null;
}
export type Node = ESLintNode | VNode | VForExpression | VOnExpression | VSlotScopeExpression | VGenericExpression | VFilterSequenceExpression | VFilter;
export type ESLintNode = ESLintIdentifier | ESLintLiteral | ESLintProgram | ESLintSwitchCase | ESLintCatchClause | ESLintVariableDeclarator | ESLintStatement | ESLintExpression | ESLintProperty | ESLintAssignmentProperty | ESLintSuper | ESLintTemplateElement | ESLintSpreadElement | ESLintPattern | ESLintClassBody | ESLintMethodDefinition | ESLintPropertyDefinition | ESLintStaticBlock | ESLintPrivateIdentifier | ESLintModuleDeclaration | ESLintModuleSpecifier | ESLintImportExpression | ESLintLegacyRestProperty;
export interface ESLintExtendedProgram {
ast: ESLintProgram;
services?: {};
visitorKeys?: {
[type: string]: string[];
};
scopeManager?: ScopeManager;
}
export interface ESLintProgram extends HasLocation, HasParent {
type: "Program";
sourceType: "script" | "module";
body: (ESLintStatement | ESLintModuleDeclaration)[];
templateBody?: VElement & HasConcreteInfo;
tokens?: Token[];
comments?: Token[];
errors?: ParseError[];
}
export type ESLintStatement = ESLintExpressionStatement | ESLintBlockStatement | ESLintEmptyStatement | ESLintDebuggerStatement | ESLintWithStatement | ESLintReturnStatement | ESLintLabeledStatement | ESLintBreakStatement | ESLintContinueStatement | ESLintIfStatement | ESLintSwitchStatement | ESLintThrowStatement | ESLintTryStatement | ESLintWhileStatement | ESLintDoWhileStatement | ESLintForStatement | ESLintForInStatement | ESLintForOfStatement | ESLintDeclaration;
export interface ESLintEmptyStatement extends HasLocation, HasParent {
type: "EmptyStatement";
}
export interface ESLintBlockStatement extends HasLocation, HasParent {
type: "BlockStatement";
body: ESLintStatement[];
}
export interface ESLintExpressionStatement extends HasLocation, HasParent {
type: "ExpressionStatement";
expression: ESLintExpression;
}
export interface ESLintIfStatement extends HasLocation, HasParent {
type: "IfStatement";
test: ESLintExpression;
consequent: ESLintStatement;
alternate: ESLintStatement | null;
}
export interface ESLintSwitchStatement extends HasLocation, HasParent {
type: "SwitchStatement";
discriminant: ESLintExpression;
cases: ESLintSwitchCase[];
}
export interface ESLintSwitchCase extends HasLocation, HasParent {
type: "SwitchCase";
test: ESLintExpression | null;
consequent: ESLintStatement[];
}
export interface ESLintWhileStatement extends HasLocation, HasParent {
type: "WhileStatement";
test: ESLintExpression;
body: ESLintStatement;
}
export interface ESLintDoWhileStatement extends HasLocation, HasParent {
type: "DoWhileStatement";
body: ESLintStatement;
test: ESLintExpression;
}
export interface ESLintForStatement extends HasLocation, HasParent {
type: "ForStatement";
init: ESLintVariableDeclaration | ESLintExpression | null;
test: ESLintExpression | null;
update: ESLintExpression | null;
body: ESLintStatement;
}
export interface ESLintForInStatement extends HasLocation, HasParent {
type: "ForInStatement";
left: ESLintVariableDeclaration | ESLintPattern;
right: ESLintExpression;
body: ESLintStatement;
}
export interface ESLintForOfStatement extends HasLocation, HasParent {
type: "ForOfStatement";
left: ESLintVariableDeclaration | ESLintPattern;
right: ESLintExpression;
body: ESLintStatement;
await: boolean;
}
export interface ESLintLabeledStatement extends HasLocation, HasParent {
type: "LabeledStatement";
label: ESLintIdentifier;
body: ESLintStatement;
}
export interface ESLintBreakStatement extends HasLocation, HasParent {
type: "BreakStatement";
label: ESLintIdentifier | null;
}
export interface ESLintContinueStatement extends HasLocation, HasParent {
type: "ContinueStatement";
label: ESLintIdentifier | null;
}
export interface ESLintReturnStatement extends HasLocation, HasParent {
type: "ReturnStatement";
argument: ESLintExpression | null;
}
export interface ESLintThrowStatement extends HasLocation, HasParent {
type: "ThrowStatement";
argument: ESLintExpression;
}
export interface ESLintTryStatement extends HasLocation, HasParent {
type: "TryStatement";
block: ESLintBlockStatement;
handler: ESLintCatchClause | null;
finalizer: ESLintBlockStatement | null;
}
export interface ESLintCatchClause extends HasLocation, HasParent {
type: "CatchClause";
param: ESLintPattern | null;
body: ESLintBlockStatement;
}
export interface ESLintWithStatement extends HasLocation, HasParent {
type: "WithStatement";
object: ESLintExpression;
body: ESLintStatement;
}
export interface ESLintDebuggerStatement extends HasLocation, HasParent {
type: "DebuggerStatement";
}
export type ESLintDeclaration = ESLintFunctionDeclaration | ESLintVariableDeclaration | ESLintClassDeclaration;
export interface ESLintFunctionDeclaration extends HasLocation, HasParent {
type: "FunctionDeclaration";
async: boolean;
generator: boolean;
id: ESLintIdentifier | null;
params: ESLintPattern[];
body: ESLintBlockStatement;
}
export interface ESLintVariableDeclaration extends HasLocation, HasParent {
type: "VariableDeclaration";
kind: "var" | "let" | "const";
declarations: ESLintVariableDeclarator[];
}
export interface ESLintVariableDeclarator extends HasLocation, HasParent {
type: "VariableDeclarator";
id: ESLintPattern;
init: ESLintExpression | null;
}
export interface ESLintClassDeclaration extends HasLocation, HasParent {
type: "ClassDeclaration";
id: ESLintIdentifier | null;
superClass: ESLintExpression | null;
body: ESLintClassBody;
}
export interface ESLintClassBody extends HasLocation, HasParent {
type: "ClassBody";
body: (ESLintMethodDefinition | ESLintPropertyDefinition | ESLintStaticBlock)[];
}
export interface ESLintMethodDefinition extends HasLocation, HasParent {
type: "MethodDefinition";
kind: "constructor" | "method" | "get" | "set";
computed: boolean;
static: boolean;
key: ESLintExpression | ESLintPrivateIdentifier;
value: ESLintFunctionExpression;
}
export interface ESLintPropertyDefinition extends HasLocation, HasParent {
type: "PropertyDefinition";
computed: boolean;
static: boolean;
key: ESLintExpression | ESLintPrivateIdentifier;
value: ESLintExpression | null;
}
export interface ESLintStaticBlock extends HasLocation, HasParent, Omit<ESLintBlockStatement, "type"> {
type: "StaticBlock";
body: ESLintStatement[];
}
export interface ESLintPrivateIdentifier extends HasLocation, HasParent {
type: "PrivateIdentifier";
name: string;
}
export type ESLintModuleDeclaration = ESLintImportDeclaration | ESLintExportNamedDeclaration | ESLintExportDefaultDeclaration | ESLintExportAllDeclaration;
export type ESLintModuleSpecifier = ESLintImportSpecifier | ESLintImportDefaultSpecifier | ESLintImportNamespaceSpecifier | ESLintExportSpecifier;
export interface ESLintImportDeclaration extends HasLocation, HasParent {
type: "ImportDeclaration";
specifiers: (ESLintImportSpecifier | ESLintImportDefaultSpecifier | ESLintImportNamespaceSpecifier)[];
source: ESLintLiteral;
}
export interface ESLintImportSpecifier extends HasLocation, HasParent {
type: "ImportSpecifier";
imported: ESLintIdentifier | ESLintStringLiteral;
local: ESLintIdentifier;
}
export interface ESLintImportDefaultSpecifier extends HasLocation, HasParent {
type: "ImportDefaultSpecifier";
local: ESLintIdentifier;
}
export interface ESLintImportNamespaceSpecifier extends HasLocation, HasParent {
type: "ImportNamespaceSpecifier";
local: ESLintIdentifier;
}
export interface ESLintImportExpression extends HasLocation, HasParent {
type: "ImportExpression";
source: ESLintExpression;
}
export interface ESLintExportNamedDeclaration extends HasLocation, HasParent {
type: "ExportNamedDeclaration";
declaration?: ESLintDeclaration | null;
specifiers: ESLintExportSpecifier[];
source?: ESLintLiteral | null;
}
export interface ESLintExportSpecifier extends HasLocation, HasParent {
type: "ExportSpecifier";
local: ESLintIdentifier | ESLintStringLiteral;
exported: ESLintIdentifier | ESLintStringLiteral;
}
export interface ESLintExportDefaultDeclaration extends HasLocation, HasParent {
type: "ExportDefaultDeclaration";
declaration: ESLintDeclaration | ESLintExpression;
}
export interface ESLintExportAllDeclaration extends HasLocation, HasParent {
type: "ExportAllDeclaration";
exported: ESLintIdentifier | ESLintStringLiteral | null;
source: ESLintLiteral;
}
export type ESLintExpression = ESLintThisExpression | ESLintArrayExpression | ESLintObjectExpression | ESLintFunctionExpression | ESLintArrowFunctionExpression | ESLintYieldExpression | ESLintLiteral | ESLintUnaryExpression | ESLintUpdateExpression | ESLintBinaryExpression | ESLintAssignmentExpression | ESLintLogicalExpression | ESLintMemberExpression | ESLintConditionalExpression | ESLintCallExpression | ESLintNewExpression | ESLintSequenceExpression | ESLintTemplateLiteral | ESLintTaggedTemplateExpression | ESLintClassExpression | ESLintMetaProperty | ESLintIdentifier | ESLintAwaitExpression | ESLintChainExpression;
export interface ESLintIdentifier extends HasLocation, HasParent {
type: "Identifier";
name: string;
}
interface ESLintLiteralBase extends HasLocation, HasParent {
type: "Literal";
value: string | boolean | null | number | RegExp | bigint;
raw: string;
regex?: {
pattern: string;
flags: string;
};
bigint?: string;
}
export interface ESLintStringLiteral extends ESLintLiteralBase {
value: string;
regex?: undefined;
bigint?: undefined;
}
export interface ESLintBooleanLiteral extends ESLintLiteralBase {
value: boolean;
regex?: undefined;
bigint?: undefined;
}
export interface ESLintNullLiteral extends ESLintLiteralBase {
value: null;
regex?: undefined;
bigint?: undefined;
}
export interface ESLintNumberLiteral extends ESLintLiteralBase {
value: number;
regex?: undefined;
bigint?: undefined;
}
export interface ESLintRegExpLiteral extends ESLintLiteralBase {
value: null | RegExp;
regex: {
pattern: string;
flags: string;
};
bigint?: undefined;
}
export interface ESLintBigIntLiteral extends ESLintLiteralBase {
value: null | bigint;
regex?: undefined;
bigint: string;
}
export type ESLintLiteral = ESLintStringLiteral | ESLintBooleanLiteral | ESLintNullLiteral | ESLintNumberLiteral | ESLintRegExpLiteral | ESLintBigIntLiteral;
export interface ESLintThisExpression extends HasLocation, HasParent {
type: "ThisExpression";
}
export interface ESLintArrayExpression extends HasLocation, HasParent {
type: "ArrayExpression";
elements: (ESLintExpression | ESLintSpreadElement)[];
}
export interface ESLintObjectExpression extends HasLocation, HasParent {
type: "ObjectExpression";
properties: (ESLintProperty | ESLintSpreadElement | ESLintLegacySpreadProperty)[];
}
export interface ESLintProperty extends HasLocation, HasParent {
type: "Property";
kind: "init" | "get" | "set";
method: boolean;
shorthand: boolean;
computed: boolean;
key: ESLintExpression;
value: ESLintExpression | ESLintPattern;
}
export interface ESLintFunctionExpression extends HasLocation, HasParent {
type: "FunctionExpression";
async: boolean;
generator: boolean;
id: ESLintIdentifier | null;
params: ESLintPattern[];
body: ESLintBlockStatement;
}
export interface ESLintArrowFunctionExpression extends HasLocation, HasParent {
type: "ArrowFunctionExpression";
async: boolean;
generator: boolean;
id: ESLintIdentifier | null;
params: ESLintPattern[];
body: ESLintBlockStatement | ESLintExpression;
}
export interface ESLintSequenceExpression extends HasLocation, HasParent {
type: "SequenceExpression";
expressions: ESLintExpression[];
}
export interface ESLintUnaryExpression extends HasLocation, HasParent {
type: "UnaryExpression";
operator: "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";
prefix: boolean;
argument: ESLintExpression;
}
export interface ESLintBinaryExpression extends HasLocation, HasParent {
type: "BinaryExpression";
operator: "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "**" | "|" | "^" | "&" | "in" | "instanceof";
left: ESLintExpression | ESLintPrivateIdentifier;
right: ESLintExpression;
}
export interface ESLintAssignmentExpression extends HasLocation, HasParent {
type: "AssignmentExpression";
operator: "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "<<=" | ">>=" | ">>>=" | "|=" | "^=" | "&=" | "||=" | "&&=" | "??=";
left: ESLintPattern;
right: ESLintExpression;
}
export interface ESLintUpdateExpression extends HasLocation, HasParent {
type: "UpdateExpression";
operator: "++" | "--";
argument: ESLintExpression;
prefix: boolean;
}
export interface ESLintLogicalExpression extends HasLocation, HasParent {
type: "LogicalExpression";
operator: "||" | "&&" | "??";
left: ESLintExpression;
right: ESLintExpression;
}
export interface ESLintConditionalExpression extends HasLocation, HasParent {
type: "ConditionalExpression";
test: ESLintExpression;
alternate: ESLintExpression;
consequent: ESLintExpression;
}
export interface ESLintCallExpression extends HasLocation, HasParent {
type: "CallExpression";
optional: boolean;
callee: ESLintExpression | ESLintSuper;
arguments: (ESLintExpression | ESLintSpreadElement)[];
}
export interface ESLintSuper extends HasLocation, HasParent {
type: "Super";
}
export interface ESLintNewExpression extends HasLocation, HasParent {
type: "NewExpression";
callee: ESLintExpression;
arguments: (ESLintExpression | ESLintSpreadElement)[];
}
export interface ESLintMemberExpression extends HasLocation, HasParent {
type: "MemberExpression";
optional: boolean;
computed: boolean;
object: ESLintExpression | ESLintSuper;
property: ESLintExpression | ESLintPrivateIdentifier;
}
export interface ESLintYieldExpression extends HasLocation, HasParent {
type: "YieldExpression";
delegate: boolean;
argument: ESLintExpression | null;
}
export interface ESLintAwaitExpression extends HasLocation, HasParent {
type: "AwaitExpression";
argument: ESLintExpression;
}
export interface ESLintTemplateLiteral extends HasLocation, HasParent {
type: "TemplateLiteral";
quasis: ESLintTemplateElement[];
expressions: ESLintExpression[];
}
export interface ESLintTaggedTemplateExpression extends HasLocation, HasParent {
type: "TaggedTemplateExpression";
tag: ESLintExpression;
quasi: ESLintTemplateLiteral;
}
export interface ESLintTemplateElement extends HasLocation, HasParent {
type: "TemplateElement";
tail: boolean;
value: {
cooked: string | null;
raw: string;
};
}
export interface ESLintClassExpression extends HasLocation, HasParent {
type: "ClassExpression";
id: ESLintIdentifier | null;
superClass: ESLintExpression | null;
body: ESLintClassBody;
}
export interface ESLintMetaProperty extends HasLocation, HasParent {
type: "MetaProperty";
meta: ESLintIdentifier;
property: ESLintIdentifier;
}
export type ESLintPattern = ESLintIdentifier | ESLintObjectPattern | ESLintArrayPattern | ESLintRestElement | ESLintAssignmentPattern | ESLintMemberExpression | ESLintLegacyRestProperty;
export interface ESLintObjectPattern extends HasLocation, HasParent {
type: "ObjectPattern";
properties: (ESLintAssignmentProperty | ESLintRestElement | ESLintLegacyRestProperty)[];
}
export interface ESLintAssignmentProperty extends ESLintProperty {
value: ESLintPattern;
kind: "init";
method: false;
}
export interface ESLintArrayPattern extends HasLocation, HasParent {
type: "ArrayPattern";
elements: ESLintPattern[];
}
export interface ESLintRestElement extends HasLocation, HasParent {
type: "RestElement";
argument: ESLintPattern;
}
export interface ESLintSpreadElement extends HasLocation, HasParent {
type: "SpreadElement";
argument: ESLintExpression;
}
export interface ESLintAssignmentPattern extends HasLocation, HasParent {
type: "AssignmentPattern";
left: ESLintPattern;
right: ESLintExpression;
}
export type ESLintChainElement = ESLintCallExpression | ESLintMemberExpression;
export interface ESLintChainExpression extends HasLocation, HasParent {
type: "ChainExpression";
expression: ESLintChainElement;
}
export interface ESLintLegacyRestProperty extends HasLocation, HasParent {
type: "RestProperty" | "ExperimentalRestProperty";
argument: ESLintPattern;
}
export interface ESLintLegacySpreadProperty extends HasLocation, HasParent {
type: "SpreadProperty" | "ExperimentalSpreadProperty";
argument: ESLintExpression;
}
export const NS: Readonly<{
HTML: "http://www.w3.org/1999/xhtml";
MathML: "http://www.w3.org/1998/Math/MathML";
SVG: "http://www.w3.org/2000/svg";
XLink: "http://www.w3.org/1999/xlink";
XML: "http://www.w3.org/XML/1998/namespace";
XMLNS: "http://www.w3.org/2000/xmlns/";
}>;
export type Namespace = typeof NS.HTML | typeof NS.MathML | typeof NS.SVG | typeof NS.XLink | typeof NS.XML | typeof NS.XMLNS;
export interface Variable {
id: ESLintIdentifier;
kind: "v-for" | "scope" | "generic";
references: Reference[];
}
export interface Reference {
id: ESLintIdentifier;
mode: "rw" | "r" | "w";
variable: Variable | null;
isValueReference?: boolean;
isTypeReference?: boolean;
}
export interface VForExpression extends HasLocation, HasParent {
type: "VForExpression";
parent: VExpressionContainer;
left: ESLintPattern[];
right: ESLintExpression;
}
export interface VOnExpression extends HasLocation, HasParent {
type: "VOnExpression";
parent: VExpressionContainer;
body: ESLintStatement[];
}
export interface VSlotScopeExpression extends HasLocation, HasParent {
type: "VSlotScopeExpression";
parent: VExpressionContainer;
params: ESLintPattern[];
}
export interface VGenericExpression extends HasLocation, HasParent {
type: "VGenericExpression";
parent: VExpressionContainer;
params: TSESTree.TSTypeParameterDeclaration["params"];
rawParams: string[];
}
export interface VFilterSequenceExpression extends HasLocation, HasParent {
type: "VFilterSequenceExpression";
parent: VExpressionContainer;
expression: ESLintExpression;
filters: VFilter[];
}
export interface VFilter extends HasLocation, HasParent {
type: "VFilter";
parent: VFilterSequenceExpression;
callee: ESLintIdentifier;
arguments: (ESLintExpression | ESLintSpreadElement)[];
}
export type VNode = VAttribute | VDirective | VDirectiveKey | VDocumentFragment | VElement | VEndTag | VExpressionContainer | VIdentifier | VLiteral | VStartTag | VText;
export interface VText extends HasLocation, HasParent {
type: "VText";
parent: VDocumentFragment | VElement;
value: string;
}
export interface VExpressionContainer extends HasLocation, HasParent {
type: "VExpressionContainer";
parent: VDocumentFragment | VElement | VDirective | VDirectiveKey;
expression: ESLintExpression | VFilterSequenceExpression | VForExpression | VOnExpression | VSlotScopeExpression | VGenericExpression | null;
references: Reference[];
}
export interface VIdentifier extends HasLocation, HasParent {
type: "VIdentifier";
parent: VAttribute | VDirectiveKey;
name: string;
rawName: string;
}
export interface VDirectiveKey extends HasLocation, HasParent {
type: "VDirectiveKey";
parent: VDirective;
name: VIdentifier;
argument: VExpressionContainer | VIdentifier | null;
modifiers: VIdentifier[];
}
export interface VLiteral extends HasLocation, HasParent {
type: "VLiteral";
parent: VAttribute;
value: string;
}
export interface VAttribute extends HasLocation, HasParent {
type: "VAttribute";
parent: VStartTag;
directive: false;
key: VIdentifier;
value: VLiteral | null;
}
export interface VDirective extends HasLocation, HasParent {
type: "VAttribute";
parent: VStartTag;
directive: true;
key: VDirectiveKey;
value: VExpressionContainer | null;
}
export interface VStartTag extends HasLocation, HasParent {
type: "VStartTag";
parent: VElement;
selfClosing: boolean;
attributes: (VAttribute | VDirective)[];
}
export interface VEndTag extends HasLocation, HasParent {
type: "VEndTag";
parent: VElement;
}
export interface HasConcreteInfo {
tokens: Token[];
comments: Token[];
errors: ParseError[];
}
export interface VElement extends HasLocation, HasParent {
type: "VElement";
parent: VDocumentFragment | VElement;
namespace: Namespace;
name: string;
rawName: string;
startTag: VStartTag;
children: (VElement | VText | VExpressionContainer)[];
endTag: VEndTag | null;
variables: Variable[];
}
export interface VDocumentFragment extends HasLocation, HasParent, HasConcreteInfo {
type: "VDocumentFragment";
parent: null;
children: (VElement | VText | VExpressionContainer | VStyleElement)[];
}
export interface VStyleElement extends VElement {
type: "VElement";
name: "style";
style: true;
children: (VText | VExpressionContainer)[];
}
export {};
}
declare module 'vue-eslint-parser/ast/tokens' {
import type { HasLocation } from "vue-eslint-parser/ast/locations";
export interface Token extends HasLocation {
type: string;
value: string;
}
}
declare module 'vue-eslint-parser/ast/traverse' {
import type { VisitorKeys } from "eslint-visitor-keys";
import type { Node } from "vue-eslint-parser/ast/nodes";
export const KEYS: Readonly<{
[type: string]: readonly string[] | undefined;
}>;
function getFallbackKeys(node: Node): string[];
export interface Visitor {
visitorKeys?: VisitorKeys;
enterNode(node: Node, parent: Node | null): void;
leaveNode(node: Node, parent: Node | null): void;
}
export function traverseNodes(node: Node, visitor: Visitor): void;
export { getFallbackKeys };
}

6486
node_modules/vue-eslint-parser/index.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/vue-eslint-parser/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
Copyright JS Foundation and other contributors, https://js.foundation
Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,70 @@
[![npm version](https://img.shields.io/npm/v/eslint-scope.svg)](https://www.npmjs.com/package/eslint-scope)
[![Downloads](https://img.shields.io/npm/dm/eslint-scope.svg)](https://www.npmjs.com/package/eslint-scope)
[![Build Status](https://github.com/eslint/eslint-scope/workflows/CI/badge.svg)](https://github.com/eslint/eslint-scope/actions)
# ESLint Scope
ESLint Scope is the [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm) scope analyzer used in ESLint. It is a fork of [escope](http://github.com/estools/escope).
## Install
```
npm i eslint-scope --save
```
## 📖 Usage
To use in an ESM file:
```js
import * as eslintScope from 'eslint-scope';
```
To use in a CommonJS file:
```js
const eslintScope = require('eslint-scope');
```
Example:
```js
import * as eslintScope from 'eslint-scope';
import * as espree from 'espree';
import estraverse from 'estraverse';
const ast = espree.parse(code, { range: true });
const scopeManager = eslintScope.analyze(ast);
const currentScope = scopeManager.acquire(ast); // global scope
estraverse.traverse(ast, {
enter (node, parent) {
// do stuff
if (/Function/.test(node.type)) {
currentScope = scopeManager.acquire(node); // get current function scope
}
},
leave(node, parent) {
if (/Function/.test(node.type)) {
currentScope = currentScope.upper; // set to parent scope
}
// do stuff
}
});
```
## Contributing
Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the [ESLint Contributor Guidelines](http://eslint.org/docs/developer-guide/contributing), so please be sure to read them before contributing. If you're not sure where to dig in, check out the [issues](https://github.com/eslint/eslint-scope/issues).
## Build Commands
* `npm test` - run all linting and tests
* `npm run lint` - run all linting
## License
ESLint Scope is licensed under a permissive BSD 2-clause license.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Variable from "./variable.js";
/**
* @constructor Definition
*/
class Definition {
constructor(type, name, node, parent, index, kind) {
/**
* @member {string} Definition#type - type of the occurrence (e.g. "Parameter", "Variable", ...).
*/
this.type = type;
/**
* @member {espree.Identifier} Definition#name - the identifier AST node of the occurrence.
*/
this.name = name;
/**
* @member {espree.Node} Definition#node - the enclosing node of the identifier.
*/
this.node = node;
/**
* @member {espree.Node?} Definition#parent - the enclosing statement node of the identifier.
*/
this.parent = parent;
/**
* @member {number?} Definition#index - the index in the declaration statement.
*/
this.index = index;
/**
* @member {string?} Definition#kind - the kind of the declaration statement.
*/
this.kind = kind;
}
}
/**
* @constructor ParameterDefinition
*/
class ParameterDefinition extends Definition {
constructor(name, node, index, rest) {
super(Variable.Parameter, name, node, null, index, null);
/**
* Whether the parameter definition is a part of a rest parameter.
* @member {boolean} ParameterDefinition#rest
*/
this.rest = rest;
}
}
export {
ParameterDefinition,
Definition
};
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,172 @@
/*
Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2013 Alex Seville <hi@alexanderseville.com>
Copyright (C) 2014 Thiago de Arruda <tpadilha84@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Escope (<a href="http://github.com/estools/escope">escope</a>) is an <a
* href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>
* scope analyzer extracted from the <a
* href="http://github.com/estools/esmangle">esmangle project</a/>.
* <p>
* <em>escope</em> finds lexical scopes in a source program, i.e. areas of that
* program where different occurrences of the same identifier refer to the same
* variable. With each scope the contained variables are collected, and each
* identifier reference in code is linked to its corresponding variable (if
* possible).
* <p>
* <em>escope</em> works on a syntax tree of the parsed source code which has
* to adhere to the <a
* href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">
* Mozilla Parser API</a>. E.g. <a href="https://github.com/eslint/espree">espree</a> is a parser
* that produces such syntax trees.
* <p>
* The main interface is the {@link analyze} function.
* @module escope
*/
/* eslint no-underscore-dangle: ["error", { "allow": ["__currentScope"] }] */
import assert from "assert";
import ScopeManager from "./scope-manager.js";
import Referencer from "./referencer.js";
import Reference from "./reference.js";
import Variable from "./variable.js";
import eslintScopeVersion from "./version.js";
/**
* Set the default options
* @returns {Object} options
*/
function defaultOptions() {
return {
optimistic: false,
directive: false,
nodejsScope: false,
impliedStrict: false,
sourceType: "script", // one of ['script', 'module', 'commonjs']
ecmaVersion: 5,
childVisitorKeys: null,
fallback: "iteration"
};
}
/**
* Preform deep update on option object
* @param {Object} target Options
* @param {Object} override Updates
* @returns {Object} Updated options
*/
function updateDeeply(target, override) {
/**
* Is hash object
* @param {Object} value Test value
* @returns {boolean} Result
*/
function isHashObject(value) {
return typeof value === "object" && value instanceof Object && !(value instanceof Array) && !(value instanceof RegExp);
}
for (const key in override) {
if (Object.prototype.hasOwnProperty.call(override, key)) {
const val = override[key];
if (isHashObject(val)) {
if (isHashObject(target[key])) {
updateDeeply(target[key], val);
} else {
target[key] = updateDeeply({}, val);
}
} else {
target[key] = val;
}
}
}
return target;
}
/**
* Main interface function. Takes an Espree syntax tree and returns the
* analyzed scopes.
* @function analyze
* @param {espree.Tree} tree Abstract Syntax Tree
* @param {Object} providedOptions Options that tailor the scope analysis
* @param {boolean} [providedOptions.optimistic=false] the optimistic flag
* @param {boolean} [providedOptions.directive=false] the directive flag
* @param {boolean} [providedOptions.ignoreEval=false] whether to check 'eval()' calls
* @param {boolean} [providedOptions.nodejsScope=false] whether the whole
* script is executed under node.js environment. When enabled, escope adds
* a function scope immediately following the global scope.
* @param {boolean} [providedOptions.impliedStrict=false] implied strict mode
* (if ecmaVersion >= 5).
* @param {string} [providedOptions.sourceType='script'] the source type of the script. one of 'script', 'module', and 'commonjs'
* @param {number} [providedOptions.ecmaVersion=5] which ECMAScript version is considered
* @param {Object} [providedOptions.childVisitorKeys=null] Additional known visitor keys. See [esrecurse](https://github.com/estools/esrecurse)'s the `childVisitorKeys` option.
* @param {string} [providedOptions.fallback='iteration'] A kind of the fallback in order to encounter with unknown node. See [esrecurse](https://github.com/estools/esrecurse)'s the `fallback` option.
* @returns {ScopeManager} ScopeManager
*/
function analyze(tree, providedOptions) {
const options = updateDeeply(defaultOptions(), providedOptions);
const scopeManager = new ScopeManager(options);
const referencer = new Referencer(options, scopeManager);
referencer.visit(tree);
assert(scopeManager.__currentScope === null, "currentScope should be null.");
return scopeManager;
}
export {
/** @name module:escope.version */
eslintScopeVersion as version,
/** @name module:escope.Reference */
Reference,
/** @name module:escope.Variable */
Variable,
/** @name module:escope.ScopeManager */
ScopeManager,
/** @name module:escope.Referencer */
Referencer,
analyze
};
/** @name module:escope.Definition */
export { Definition } from "./definition.js";
/** @name module:escope.PatternVisitor */
export { default as PatternVisitor } from "./pattern-visitor.js";
/** @name module:escope.Scope */
export { Scope } from "./scope.js";
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,153 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable no-undefined */
import estraverse from "estraverse";
import esrecurse from "esrecurse";
const { Syntax } = estraverse;
/**
* Get last array element
* @param {Array} xs array
* @returns {any} Last elment
*/
function getLast(xs) {
return xs[xs.length - 1] || null;
}
class PatternVisitor extends esrecurse.Visitor {
static isPattern(node) {
const nodeType = node.type;
return (
nodeType === Syntax.Identifier ||
nodeType === Syntax.ObjectPattern ||
nodeType === Syntax.ArrayPattern ||
nodeType === Syntax.SpreadElement ||
nodeType === Syntax.RestElement ||
nodeType === Syntax.AssignmentPattern
);
}
constructor(options, rootPattern, callback) {
super(null, options);
this.rootPattern = rootPattern;
this.callback = callback;
this.assignments = [];
this.rightHandNodes = [];
this.restElements = [];
}
Identifier(pattern) {
const lastRestElement = getLast(this.restElements);
this.callback(pattern, {
topLevel: pattern === this.rootPattern,
rest: lastRestElement !== null && lastRestElement !== undefined && lastRestElement.argument === pattern,
assignments: this.assignments
});
}
Property(property) {
// Computed property's key is a right hand node.
if (property.computed) {
this.rightHandNodes.push(property.key);
}
// If it's shorthand, its key is same as its value.
// If it's shorthand and has its default value, its key is same as its value.left (the value is AssignmentPattern).
// If it's not shorthand, the name of new variable is its value's.
this.visit(property.value);
}
ArrayPattern(pattern) {
for (let i = 0, iz = pattern.elements.length; i < iz; ++i) {
const element = pattern.elements[i];
this.visit(element);
}
}
AssignmentPattern(pattern) {
this.assignments.push(pattern);
this.visit(pattern.left);
this.rightHandNodes.push(pattern.right);
this.assignments.pop();
}
RestElement(pattern) {
this.restElements.push(pattern);
this.visit(pattern.argument);
this.restElements.pop();
}
MemberExpression(node) {
// Computed property's key is a right hand node.
if (node.computed) {
this.rightHandNodes.push(node.property);
}
// the object is only read, write to its property.
this.rightHandNodes.push(node.object);
}
//
// ForInStatement.left and AssignmentExpression.left are LeftHandSideExpression.
// By spec, LeftHandSideExpression is Pattern or MemberExpression.
// (see also: https://github.com/estree/estree/pull/20#issuecomment-74584758)
// But espree 2.0 parses to ArrayExpression, ObjectExpression, etc...
//
SpreadElement(node) {
this.visit(node.argument);
}
ArrayExpression(node) {
node.elements.forEach(this.visit, this);
}
AssignmentExpression(node) {
this.assignments.push(node);
this.visit(node.left);
this.rightHandNodes.push(node.right);
this.assignments.pop();
}
CallExpression(node) {
// arguments are right hand nodes.
node.arguments.forEach(a => {
this.rightHandNodes.push(a);
});
this.visit(node.callee);
}
}
export default PatternVisitor;
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,166 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const READ = 0x1;
const WRITE = 0x2;
const RW = READ | WRITE;
/**
* A Reference represents a single occurrence of an identifier in code.
* @constructor Reference
*/
class Reference {
constructor(ident, scope, flag, writeExpr, maybeImplicitGlobal, partial, init) {
/**
* Identifier syntax node.
* @member {espreeIdentifier} Reference#identifier
*/
this.identifier = ident;
/**
* Reference to the enclosing Scope.
* @member {Scope} Reference#from
*/
this.from = scope;
/**
* Whether the reference comes from a dynamic scope (such as 'eval',
* 'with', etc.), and may be trapped by dynamic scopes.
* @member {boolean} Reference#tainted
*/
this.tainted = false;
/**
* The variable this reference is resolved with.
* @member {Variable} Reference#resolved
*/
this.resolved = null;
/**
* The read-write mode of the reference. (Value is one of {@link
* Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}).
* @member {number} Reference#flag
* @private
*/
this.flag = flag;
if (this.isWrite()) {
/**
* If reference is writeable, this is the tree being written to it.
* @member {espreeNode} Reference#writeExpr
*/
this.writeExpr = writeExpr;
/**
* Whether the Reference might refer to a partial value of writeExpr.
* @member {boolean} Reference#partial
*/
this.partial = partial;
/**
* Whether the Reference is to write of initialization.
* @member {boolean} Reference#init
*/
this.init = init;
}
this.__maybeImplicitGlobal = maybeImplicitGlobal;
}
/**
* Whether the reference is static.
* @function Reference#isStatic
* @returns {boolean} static
*/
isStatic() {
return !this.tainted && this.resolved && this.resolved.scope.isStatic();
}
/**
* Whether the reference is writeable.
* @function Reference#isWrite
* @returns {boolean} write
*/
isWrite() {
return !!(this.flag & Reference.WRITE);
}
/**
* Whether the reference is readable.
* @function Reference#isRead
* @returns {boolean} read
*/
isRead() {
return !!(this.flag & Reference.READ);
}
/**
* Whether the reference is read-only.
* @function Reference#isReadOnly
* @returns {boolean} read only
*/
isReadOnly() {
return this.flag === Reference.READ;
}
/**
* Whether the reference is write-only.
* @function Reference#isWriteOnly
* @returns {boolean} write only
*/
isWriteOnly() {
return this.flag === Reference.WRITE;
}
/**
* Whether the reference is read-write.
* @function Reference#isReadWrite
* @returns {boolean} read write
*/
isReadWrite() {
return this.flag === Reference.RW;
}
}
/**
* @constant Reference.READ
* @private
*/
Reference.READ = READ;
/**
* @constant Reference.WRITE
* @private
*/
Reference.WRITE = WRITE;
/**
* @constant Reference.RW
* @private
*/
Reference.RW = RW;
export default Reference;
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,654 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-undefined */
import estraverse from "estraverse";
import esrecurse from "esrecurse";
import Reference from "./reference.js";
import Variable from "./variable.js";
import PatternVisitor from "./pattern-visitor.js";
import { Definition, ParameterDefinition } from "./definition.js";
import assert from "assert";
const { Syntax } = estraverse;
/**
* Traverse identifier in pattern
* @param {Object} options options
* @param {pattern} rootPattern root pattern
* @param {Refencer} referencer referencer
* @param {callback} callback callback
* @returns {void}
*/
function traverseIdentifierInPattern(options, rootPattern, referencer, callback) {
// Call the callback at left hand identifier nodes, and Collect right hand nodes.
const visitor = new PatternVisitor(options, rootPattern, callback);
visitor.visit(rootPattern);
// Process the right hand nodes recursively.
if (referencer !== null && referencer !== undefined) {
visitor.rightHandNodes.forEach(referencer.visit, referencer);
}
}
// Importing ImportDeclaration.
// http://people.mozilla.org/~jorendorff/es6-draft.html#sec-moduledeclarationinstantiation
// https://github.com/estree/estree/blob/master/es6.md#importdeclaration
// FIXME: Now, we don't create module environment, because the context is
// implementation dependent.
class Importer extends esrecurse.Visitor {
constructor(declaration, referencer) {
super(null, referencer.options);
this.declaration = declaration;
this.referencer = referencer;
}
visitImport(id, specifier) {
this.referencer.visitPattern(id, pattern => {
this.referencer.currentScope().__define(pattern,
new Definition(
Variable.ImportBinding,
pattern,
specifier,
this.declaration,
null,
null
));
});
}
ImportNamespaceSpecifier(node) {
const local = (node.local || node.id);
if (local) {
this.visitImport(local, node);
}
}
ImportDefaultSpecifier(node) {
const local = (node.local || node.id);
this.visitImport(local, node);
}
ImportSpecifier(node) {
const local = (node.local || node.id);
if (node.name) {
this.visitImport(node.name, node);
} else {
this.visitImport(local, node);
}
}
}
// Referencing variables and creating bindings.
class Referencer extends esrecurse.Visitor {
constructor(options, scopeManager) {
super(null, options);
this.options = options;
this.scopeManager = scopeManager;
this.parent = null;
this.isInnerMethodDefinition = false;
}
currentScope() {
return this.scopeManager.__currentScope;
}
close(node) {
while (this.currentScope() && node === this.currentScope().block) {
this.scopeManager.__currentScope = this.currentScope().__close(this.scopeManager);
}
}
pushInnerMethodDefinition(isInnerMethodDefinition) {
const previous = this.isInnerMethodDefinition;
this.isInnerMethodDefinition = isInnerMethodDefinition;
return previous;
}
popInnerMethodDefinition(isInnerMethodDefinition) {
this.isInnerMethodDefinition = isInnerMethodDefinition;
}
referencingDefaultValue(pattern, assignments, maybeImplicitGlobal, init) {
const scope = this.currentScope();
assignments.forEach(assignment => {
scope.__referencing(
pattern,
Reference.WRITE,
assignment.right,
maybeImplicitGlobal,
pattern !== assignment.left,
init
);
});
}
visitPattern(node, options, callback) {
let visitPatternOptions = options;
let visitPatternCallback = callback;
if (typeof options === "function") {
visitPatternCallback = options;
visitPatternOptions = { processRightHandNodes: false };
}
traverseIdentifierInPattern(
this.options,
node,
visitPatternOptions.processRightHandNodes ? this : null,
visitPatternCallback
);
}
visitFunction(node) {
let i, iz;
// FunctionDeclaration name is defined in upper scope
// NOTE: Not referring variableScope. It is intended.
// Since
// in ES5, FunctionDeclaration should be in FunctionBody.
// in ES6, FunctionDeclaration should be block scoped.
if (node.type === Syntax.FunctionDeclaration) {
// id is defined in upper scope
this.currentScope().__define(node.id,
new Definition(
Variable.FunctionName,
node.id,
node,
null,
null,
null
));
}
// FunctionExpression with name creates its special scope;
// FunctionExpressionNameScope.
if (node.type === Syntax.FunctionExpression && node.id) {
this.scopeManager.__nestFunctionExpressionNameScope(node);
}
// Consider this function is in the MethodDefinition.
this.scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
const that = this;
/**
* Visit pattern callback
* @param {pattern} pattern pattern
* @param {Object} info info
* @returns {void}
*/
function visitPatternCallback(pattern, info) {
that.currentScope().__define(pattern,
new ParameterDefinition(
pattern,
node,
i,
info.rest
));
that.referencingDefaultValue(pattern, info.assignments, null, true);
}
// Process parameter declarations.
for (i = 0, iz = node.params.length; i < iz; ++i) {
this.visitPattern(node.params[i], { processRightHandNodes: true }, visitPatternCallback);
}
// if there's a rest argument, add that
if (node.rest) {
this.visitPattern({
type: "RestElement",
argument: node.rest
}, pattern => {
this.currentScope().__define(pattern,
new ParameterDefinition(
pattern,
node,
node.params.length,
true
));
});
}
// In TypeScript there are a number of function-like constructs which have no body,
// so check it exists before traversing
if (node.body) {
// Skip BlockStatement to prevent creating BlockStatement scope.
if (node.body.type === Syntax.BlockStatement) {
this.visitChildren(node.body);
} else {
this.visit(node.body);
}
}
this.close(node);
}
visitClass(node) {
if (node.type === Syntax.ClassDeclaration) {
this.currentScope().__define(node.id,
new Definition(
Variable.ClassName,
node.id,
node,
null,
null,
null
));
}
this.visit(node.superClass);
this.scopeManager.__nestClassScope(node);
if (node.id) {
this.currentScope().__define(node.id,
new Definition(
Variable.ClassName,
node.id,
node
));
}
this.visit(node.body);
this.close(node);
}
visitProperty(node) {
let previous;
if (node.computed) {
this.visit(node.key);
}
const isMethodDefinition = node.type === Syntax.MethodDefinition;
if (isMethodDefinition) {
previous = this.pushInnerMethodDefinition(true);
}
this.visit(node.value);
if (isMethodDefinition) {
this.popInnerMethodDefinition(previous);
}
}
visitForIn(node) {
if (node.left.type === Syntax.VariableDeclaration && node.left.kind !== "var") {
this.scopeManager.__nestForScope(node);
}
if (node.left.type === Syntax.VariableDeclaration) {
this.visit(node.left);
this.visitPattern(node.left.declarations[0].id, pattern => {
this.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true, true);
});
} else {
this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
let maybeImplicitGlobal = null;
if (!this.currentScope().isStrict) {
maybeImplicitGlobal = {
pattern,
node
};
}
this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, true, false);
});
}
this.visit(node.right);
this.visit(node.body);
this.close(node);
}
visitVariableDeclaration(variableTargetScope, type, node, index) {
const decl = node.declarations[index];
const init = decl.init;
this.visitPattern(decl.id, { processRightHandNodes: true }, (pattern, info) => {
variableTargetScope.__define(
pattern,
new Definition(
type,
pattern,
decl,
node,
index,
node.kind
)
);
this.referencingDefaultValue(pattern, info.assignments, null, true);
if (init) {
this.currentScope().__referencing(pattern, Reference.WRITE, init, null, !info.topLevel, true);
}
});
}
AssignmentExpression(node) {
if (PatternVisitor.isPattern(node.left)) {
if (node.operator === "=") {
this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
let maybeImplicitGlobal = null;
if (!this.currentScope().isStrict) {
maybeImplicitGlobal = {
pattern,
node
};
}
this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, !info.topLevel, false);
});
} else {
this.currentScope().__referencing(node.left, Reference.RW, node.right);
}
} else {
this.visit(node.left);
}
this.visit(node.right);
}
CatchClause(node) {
this.scopeManager.__nestCatchScope(node);
this.visitPattern(node.param, { processRightHandNodes: true }, (pattern, info) => {
this.currentScope().__define(pattern,
new Definition(
Variable.CatchClause,
node.param,
node,
null,
null,
null
));
this.referencingDefaultValue(pattern, info.assignments, null, true);
});
this.visit(node.body);
this.close(node);
}
Program(node) {
this.scopeManager.__nestGlobalScope(node);
if (this.scopeManager.isGlobalReturn()) {
// Force strictness of GlobalScope to false when using node.js scope.
this.currentScope().isStrict = false;
this.scopeManager.__nestFunctionScope(node, false);
}
if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
this.scopeManager.__nestModuleScope(node);
}
if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
this.currentScope().isStrict = true;
}
this.visitChildren(node);
this.close(node);
}
Identifier(node) {
this.currentScope().__referencing(node);
}
// eslint-disable-next-line class-methods-use-this
PrivateIdentifier() {
// Do nothing.
}
UpdateExpression(node) {
if (PatternVisitor.isPattern(node.argument)) {
this.currentScope().__referencing(node.argument, Reference.RW, null);
} else {
this.visitChildren(node);
}
}
MemberExpression(node) {
this.visit(node.object);
if (node.computed) {
this.visit(node.property);
}
}
Property(node) {
this.visitProperty(node);
}
PropertyDefinition(node) {
const { computed, key, value } = node;
if (computed) {
this.visit(key);
}
if (value) {
this.scopeManager.__nestClassFieldInitializerScope(value);
this.visit(value);
this.close(value);
}
}
StaticBlock(node) {
this.scopeManager.__nestClassStaticBlockScope(node);
this.visitChildren(node);
this.close(node);
}
MethodDefinition(node) {
this.visitProperty(node);
}
BreakStatement() {} // eslint-disable-line class-methods-use-this
ContinueStatement() {} // eslint-disable-line class-methods-use-this
LabeledStatement(node) {
this.visit(node.body);
}
ForStatement(node) {
// Create ForStatement declaration.
// NOTE: In ES6, ForStatement dynamically generates
// per iteration environment. However, escope is
// a static analyzer, we only generate one scope for ForStatement.
if (node.init && node.init.type === Syntax.VariableDeclaration && node.init.kind !== "var") {
this.scopeManager.__nestForScope(node);
}
this.visitChildren(node);
this.close(node);
}
ClassExpression(node) {
this.visitClass(node);
}
ClassDeclaration(node) {
this.visitClass(node);
}
CallExpression(node) {
// Check this is direct call to eval
if (!this.scopeManager.__ignoreEval() && node.callee.type === Syntax.Identifier && node.callee.name === "eval") {
// NOTE: This should be `variableScope`. Since direct eval call always creates Lexical environment and
// let / const should be enclosed into it. Only VariableDeclaration affects on the caller's environment.
this.currentScope().variableScope.__detectEval();
}
this.visitChildren(node);
}
BlockStatement(node) {
if (this.scopeManager.__isES6()) {
this.scopeManager.__nestBlockScope(node);
}
this.visitChildren(node);
this.close(node);
}
ThisExpression() {
this.currentScope().variableScope.__detectThis();
}
WithStatement(node) {
this.visit(node.object);
// Then nest scope for WithStatement.
this.scopeManager.__nestWithScope(node);
this.visit(node.body);
this.close(node);
}
VariableDeclaration(node) {
const variableTargetScope = (node.kind === "var") ? this.currentScope().variableScope : this.currentScope();
for (let i = 0, iz = node.declarations.length; i < iz; ++i) {
const decl = node.declarations[i];
this.visitVariableDeclaration(variableTargetScope, Variable.Variable, node, i);
if (decl.init) {
this.visit(decl.init);
}
}
}
// sec 13.11.8
SwitchStatement(node) {
this.visit(node.discriminant);
if (this.scopeManager.__isES6()) {
this.scopeManager.__nestSwitchScope(node);
}
for (let i = 0, iz = node.cases.length; i < iz; ++i) {
this.visit(node.cases[i]);
}
this.close(node);
}
FunctionDeclaration(node) {
this.visitFunction(node);
}
FunctionExpression(node) {
this.visitFunction(node);
}
ForOfStatement(node) {
this.visitForIn(node);
}
ForInStatement(node) {
this.visitForIn(node);
}
ArrowFunctionExpression(node) {
this.visitFunction(node);
}
ImportDeclaration(node) {
assert(this.scopeManager.__isES6() && this.scopeManager.isModule(), "ImportDeclaration should appear when the mode is ES6 and in the module context.");
const importer = new Importer(node, this);
importer.visit(node);
}
visitExportDeclaration(node) {
if (node.source) {
return;
}
if (node.declaration) {
this.visit(node.declaration);
return;
}
this.visitChildren(node);
}
// TODO: ExportDeclaration doesn't exist. for bc?
ExportDeclaration(node) {
this.visitExportDeclaration(node);
}
ExportAllDeclaration(node) {
this.visitExportDeclaration(node);
}
ExportDefaultDeclaration(node) {
this.visitExportDeclaration(node);
}
ExportNamedDeclaration(node) {
this.visitExportDeclaration(node);
}
ExportSpecifier(node) {
// TODO: `node.id` doesn't exist. for bc?
const local = (node.id || node.local);
this.visit(local);
}
MetaProperty() { // eslint-disable-line class-methods-use-this
// do nothing.
}
}
export default Referencer;
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,255 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable no-underscore-dangle */
import {
BlockScope,
CatchScope,
ClassFieldInitializerScope,
ClassStaticBlockScope,
ClassScope,
ForScope,
FunctionExpressionNameScope,
FunctionScope,
GlobalScope,
ModuleScope,
SwitchScope,
WithScope
} from "./scope.js";
import assert from "assert";
/**
* @constructor ScopeManager
*/
class ScopeManager {
constructor(options) {
this.scopes = [];
this.globalScope = null;
this.__nodeToScope = new WeakMap();
this.__currentScope = null;
this.__options = options;
this.__declaredVariables = new WeakMap();
}
__useDirective() {
return this.__options.directive;
}
__isOptimistic() {
return this.__options.optimistic;
}
__ignoreEval() {
return this.__options.ignoreEval;
}
isGlobalReturn() {
return this.__options.nodejsScope || this.__options.sourceType === "commonjs";
}
isModule() {
return this.__options.sourceType === "module";
}
isImpliedStrict() {
return this.__options.impliedStrict;
}
isStrictModeSupported() {
return this.__options.ecmaVersion >= 5;
}
// Returns appropriate scope for this node.
__get(node) {
return this.__nodeToScope.get(node);
}
/**
* Get variables that are declared by the node.
*
* "are declared by the node" means the node is same as `Variable.defs[].node` or `Variable.defs[].parent`.
* If the node declares nothing, this method returns an empty array.
* CAUTION: This API is experimental. See https://github.com/estools/escope/pull/69 for more details.
* @param {Espree.Node} node a node to get.
* @returns {Variable[]} variables that declared by the node.
*/
getDeclaredVariables(node) {
return this.__declaredVariables.get(node) || [];
}
/**
* acquire scope from node.
* @function ScopeManager#acquire
* @param {Espree.Node} node node for the acquired scope.
* @param {?boolean} [inner=false] look up the most inner scope, default value is false.
* @returns {Scope?} Scope from node
*/
acquire(node, inner) {
/**
* predicate
* @param {Scope} testScope scope to test
* @returns {boolean} predicate
*/
function predicate(testScope) {
if (testScope.type === "function" && testScope.functionExpressionScope) {
return false;
}
return true;
}
const scopes = this.__get(node);
if (!scopes || scopes.length === 0) {
return null;
}
// Heuristic selection from all scopes.
// If you would like to get all scopes, please use ScopeManager#acquireAll.
if (scopes.length === 1) {
return scopes[0];
}
if (inner) {
for (let i = scopes.length - 1; i >= 0; --i) {
const scope = scopes[i];
if (predicate(scope)) {
return scope;
}
}
} else {
for (let i = 0, iz = scopes.length; i < iz; ++i) {
const scope = scopes[i];
if (predicate(scope)) {
return scope;
}
}
}
return null;
}
/**
* acquire all scopes from node.
* @function ScopeManager#acquireAll
* @param {Espree.Node} node node for the acquired scope.
* @returns {Scopes?} Scope array
*/
acquireAll(node) {
return this.__get(node);
}
/**
* release the node.
* @function ScopeManager#release
* @param {Espree.Node} node releasing node.
* @param {?boolean} [inner=false] look up the most inner scope, default value is false.
* @returns {Scope?} upper scope for the node.
*/
release(node, inner) {
const scopes = this.__get(node);
if (scopes && scopes.length) {
const scope = scopes[0].upper;
if (!scope) {
return null;
}
return this.acquire(scope.block, inner);
}
return null;
}
attach() { } // eslint-disable-line class-methods-use-this
detach() { } // eslint-disable-line class-methods-use-this
__nestScope(scope) {
if (scope instanceof GlobalScope) {
assert(this.__currentScope === null);
this.globalScope = scope;
}
this.__currentScope = scope;
return scope;
}
__nestGlobalScope(node) {
return this.__nestScope(new GlobalScope(this, node));
}
__nestBlockScope(node) {
return this.__nestScope(new BlockScope(this, this.__currentScope, node));
}
__nestFunctionScope(node, isMethodDefinition) {
return this.__nestScope(new FunctionScope(this, this.__currentScope, node, isMethodDefinition));
}
__nestForScope(node) {
return this.__nestScope(new ForScope(this, this.__currentScope, node));
}
__nestCatchScope(node) {
return this.__nestScope(new CatchScope(this, this.__currentScope, node));
}
__nestWithScope(node) {
return this.__nestScope(new WithScope(this, this.__currentScope, node));
}
__nestClassScope(node) {
return this.__nestScope(new ClassScope(this, this.__currentScope, node));
}
__nestClassFieldInitializerScope(node) {
return this.__nestScope(new ClassFieldInitializerScope(this, this.__currentScope, node));
}
__nestClassStaticBlockScope(node) {
return this.__nestScope(new ClassStaticBlockScope(this, this.__currentScope, node));
}
__nestSwitchScope(node) {
return this.__nestScope(new SwitchScope(this, this.__currentScope, node));
}
__nestModuleScope(node) {
return this.__nestScope(new ModuleScope(this, this.__currentScope, node));
}
__nestFunctionExpressionNameScope(node) {
return this.__nestScope(new FunctionExpressionNameScope(this, this.__currentScope, node));
}
__isES6() {
return this.__options.ecmaVersion >= 6;
}
}
export default ScopeManager;
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,772 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-undefined */
import estraverse from "estraverse";
import Reference from "./reference.js";
import Variable from "./variable.js";
import { Definition } from "./definition.js";
import assert from "assert";
const { Syntax } = estraverse;
/**
* Test if scope is struct
* @param {Scope} scope scope
* @param {Block} block block
* @param {boolean} isMethodDefinition is method definition
* @param {boolean} useDirective use directive
* @returns {boolean} is strict scope
*/
function isStrictScope(scope, block, isMethodDefinition, useDirective) {
let body;
// When upper scope is exists and strict, inner scope is also strict.
if (scope.upper && scope.upper.isStrict) {
return true;
}
if (isMethodDefinition) {
return true;
}
if (scope.type === "class" || scope.type === "module") {
return true;
}
if (scope.type === "block" || scope.type === "switch") {
return false;
}
if (scope.type === "function") {
if (block.type === Syntax.ArrowFunctionExpression && block.body.type !== Syntax.BlockStatement) {
return false;
}
if (block.type === Syntax.Program) {
body = block;
} else {
body = block.body;
}
if (!body) {
return false;
}
} else if (scope.type === "global") {
body = block;
} else {
return false;
}
// Search 'use strict' directive.
if (useDirective) {
for (let i = 0, iz = body.body.length; i < iz; ++i) {
const stmt = body.body[i];
if (stmt.type !== Syntax.DirectiveStatement) {
break;
}
if (stmt.raw === "\"use strict\"" || stmt.raw === "'use strict'") {
return true;
}
}
} else {
for (let i = 0, iz = body.body.length; i < iz; ++i) {
const stmt = body.body[i];
if (stmt.type !== Syntax.ExpressionStatement) {
break;
}
const expr = stmt.expression;
if (expr.type !== Syntax.Literal || typeof expr.value !== "string") {
break;
}
if (expr.raw !== null && expr.raw !== undefined) {
if (expr.raw === "\"use strict\"" || expr.raw === "'use strict'") {
return true;
}
} else {
if (expr.value === "use strict") {
return true;
}
}
}
}
return false;
}
/**
* Register scope
* @param {ScopeManager} scopeManager scope manager
* @param {Scope} scope scope
* @returns {void}
*/
function registerScope(scopeManager, scope) {
scopeManager.scopes.push(scope);
const scopes = scopeManager.__nodeToScope.get(scope.block);
if (scopes) {
scopes.push(scope);
} else {
scopeManager.__nodeToScope.set(scope.block, [scope]);
}
}
/**
* Should be statically
* @param {Object} def def
* @returns {boolean} should be statically
*/
function shouldBeStatically(def) {
return (
(def.type === Variable.ClassName) ||
(def.type === Variable.Variable && def.parent.kind !== "var")
);
}
/**
* @constructor Scope
*/
class Scope {
constructor(scopeManager, type, upperScope, block, isMethodDefinition) {
/**
* One of "global", "module", "function", "function-expression-name", "block", "switch", "catch", "with", "for",
* "class", "class-field-initializer", "class-static-block".
* @member {string} Scope#type
*/
this.type = type;
/**
* The scoped {@link Variable}s of this scope, as <code>{ Variable.name
* : Variable }</code>.
* @member {Map} Scope#set
*/
this.set = new Map();
/**
* The tainted variables of this scope, as <code>{ Variable.name :
* boolean }</code>.
* @member {Map} Scope#taints */
this.taints = new Map();
/**
* Generally, through the lexical scoping of JS you can always know
* which variable an identifier in the source code refers to. There are
* a few exceptions to this rule. With 'global' and 'with' scopes you
* can only decide at runtime which variable a reference refers to.
* Moreover, if 'eval()' is used in a scope, it might introduce new
* bindings in this or its parent scopes.
* All those scopes are considered 'dynamic'.
* @member {boolean} Scope#dynamic
*/
this.dynamic = this.type === "global" || this.type === "with";
/**
* A reference to the scope-defining syntax node.
* @member {espree.Node} Scope#block
*/
this.block = block;
/**
* The {@link Reference|references} that are not resolved with this scope.
* @member {Reference[]} Scope#through
*/
this.through = [];
/**
* The scoped {@link Variable}s of this scope. In the case of a
* 'function' scope this includes the automatic argument <em>arguments</em> as
* its first element, as well as all further formal arguments.
* @member {Variable[]} Scope#variables
*/
this.variables = [];
/**
* Any variable {@link Reference|reference} found in this scope. This
* includes occurrences of local variables as well as variables from
* parent scopes (including the global scope). For local variables
* this also includes defining occurrences (like in a 'var' statement).
* In a 'function' scope this does not include the occurrences of the
* formal parameter in the parameter list.
* @member {Reference[]} Scope#references
*/
this.references = [];
/**
* For 'global' and 'function' scopes, this is a self-reference. For
* other scope types this is the <em>variableScope</em> value of the
* parent scope.
* @member {Scope} Scope#variableScope
*/
this.variableScope =
this.type === "global" ||
this.type === "module" ||
this.type === "function" ||
this.type === "class-field-initializer" ||
this.type === "class-static-block"
? this
: upperScope.variableScope;
/**
* Whether this scope is created by a FunctionExpression.
* @member {boolean} Scope#functionExpressionScope
*/
this.functionExpressionScope = false;
/**
* Whether this is a scope that contains an 'eval()' invocation.
* @member {boolean} Scope#directCallToEvalScope
*/
this.directCallToEvalScope = false;
/**
* @member {boolean} Scope#thisFound
*/
this.thisFound = false;
this.__left = [];
/**
* Reference to the parent {@link Scope|scope}.
* @member {Scope} Scope#upper
*/
this.upper = upperScope;
/**
* Whether 'use strict' is in effect in this scope.
* @member {boolean} Scope#isStrict
*/
this.isStrict = scopeManager.isStrictModeSupported()
? isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective())
: false;
/**
* List of nested {@link Scope}s.
* @member {Scope[]} Scope#childScopes
*/
this.childScopes = [];
if (this.upper) {
this.upper.childScopes.push(this);
}
this.__declaredVariables = scopeManager.__declaredVariables;
registerScope(scopeManager, this);
}
__shouldStaticallyClose(scopeManager) {
return (!this.dynamic || scopeManager.__isOptimistic());
}
__shouldStaticallyCloseForGlobal(ref) {
// On global scope, let/const/class declarations should be resolved statically.
const name = ref.identifier.name;
if (!this.set.has(name)) {
return false;
}
const variable = this.set.get(name);
const defs = variable.defs;
return defs.length > 0 && defs.every(shouldBeStatically);
}
__staticCloseRef(ref) {
if (!this.__resolve(ref)) {
this.__delegateToUpperScope(ref);
}
}
__dynamicCloseRef(ref) {
// notify all names are through to global
let current = this;
do {
current.through.push(ref);
current = current.upper;
} while (current);
}
__globalCloseRef(ref) {
// let/const/class declarations should be resolved statically.
// others should be resolved dynamically.
if (this.__shouldStaticallyCloseForGlobal(ref)) {
this.__staticCloseRef(ref);
} else {
this.__dynamicCloseRef(ref);
}
}
__close(scopeManager) {
let closeRef;
if (this.__shouldStaticallyClose(scopeManager)) {
closeRef = this.__staticCloseRef;
} else if (this.type !== "global") {
closeRef = this.__dynamicCloseRef;
} else {
closeRef = this.__globalCloseRef;
}
// Try Resolving all references in this scope.
for (let i = 0, iz = this.__left.length; i < iz; ++i) {
const ref = this.__left[i];
closeRef.call(this, ref);
}
this.__left = null;
return this.upper;
}
// To override by function scopes.
// References in default parameters isn't resolved to variables which are in their function body.
__isValidResolution(ref, variable) { // eslint-disable-line class-methods-use-this, no-unused-vars
return true;
}
__resolve(ref) {
const name = ref.identifier.name;
if (!this.set.has(name)) {
return false;
}
const variable = this.set.get(name);
if (!this.__isValidResolution(ref, variable)) {
return false;
}
variable.references.push(ref);
variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
if (ref.tainted) {
variable.tainted = true;
this.taints.set(variable.name, true);
}
ref.resolved = variable;
return true;
}
__delegateToUpperScope(ref) {
if (this.upper) {
this.upper.__left.push(ref);
}
this.through.push(ref);
}
__addDeclaredVariablesOfNode(variable, node) {
if (node === null || node === undefined) {
return;
}
let variables = this.__declaredVariables.get(node);
if (variables === null || variables === undefined) {
variables = [];
this.__declaredVariables.set(node, variables);
}
if (variables.indexOf(variable) === -1) {
variables.push(variable);
}
}
__defineGeneric(name, set, variables, node, def) {
let variable;
variable = set.get(name);
if (!variable) {
variable = new Variable(name, this);
set.set(name, variable);
variables.push(variable);
}
if (def) {
variable.defs.push(def);
this.__addDeclaredVariablesOfNode(variable, def.node);
this.__addDeclaredVariablesOfNode(variable, def.parent);
}
if (node) {
variable.identifiers.push(node);
}
}
__define(node, def) {
if (node && node.type === Syntax.Identifier) {
this.__defineGeneric(
node.name,
this.set,
this.variables,
node,
def
);
}
}
__referencing(node, assign, writeExpr, maybeImplicitGlobal, partial, init) {
// because Array element may be null
if (!node || node.type !== Syntax.Identifier) {
return;
}
// Specially handle like `this`.
if (node.name === "super") {
return;
}
const ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init);
this.references.push(ref);
this.__left.push(ref);
}
__detectEval() {
let current = this;
this.directCallToEvalScope = true;
do {
current.dynamic = true;
current = current.upper;
} while (current);
}
__detectThis() {
this.thisFound = true;
}
__isClosed() {
return this.__left === null;
}
/**
* returns resolved {Reference}
* @function Scope#resolve
* @param {Espree.Identifier} ident identifier to be resolved.
* @returns {Reference} reference
*/
resolve(ident) {
let ref, i, iz;
assert(this.__isClosed(), "Scope should be closed.");
assert(ident.type === Syntax.Identifier, "Target should be identifier.");
for (i = 0, iz = this.references.length; i < iz; ++i) {
ref = this.references[i];
if (ref.identifier === ident) {
return ref;
}
}
return null;
}
/**
* returns this scope is static
* @function Scope#isStatic
* @returns {boolean} static
*/
isStatic() {
return !this.dynamic;
}
/**
* returns this scope has materialized arguments
* @function Scope#isArgumentsMaterialized
* @returns {boolean} arguemnts materialized
*/
isArgumentsMaterialized() { // eslint-disable-line class-methods-use-this
return true;
}
/**
* returns this scope has materialized `this` reference
* @function Scope#isThisMaterialized
* @returns {boolean} this materialized
*/
isThisMaterialized() { // eslint-disable-line class-methods-use-this
return true;
}
isUsedName(name) {
if (this.set.has(name)) {
return true;
}
for (let i = 0, iz = this.through.length; i < iz; ++i) {
if (this.through[i].identifier.name === name) {
return true;
}
}
return false;
}
}
class GlobalScope extends Scope {
constructor(scopeManager, block) {
super(scopeManager, "global", null, block, false);
this.implicit = {
set: new Map(),
variables: [],
/**
* List of {@link Reference}s that are left to be resolved (i.e. which
* need to be linked to the variable they refer to).
* @member {Reference[]} Scope#implicit#left
*/
left: []
};
}
__close(scopeManager) {
const implicit = [];
for (let i = 0, iz = this.__left.length; i < iz; ++i) {
const ref = this.__left[i];
if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
implicit.push(ref.__maybeImplicitGlobal);
}
}
// create an implicit global variable from assignment expression
for (let i = 0, iz = implicit.length; i < iz; ++i) {
const info = implicit[i];
this.__defineImplicit(info.pattern,
new Definition(
Variable.ImplicitGlobalVariable,
info.pattern,
info.node,
null,
null,
null
));
}
this.implicit.left = this.__left;
return super.__close(scopeManager);
}
__defineImplicit(node, def) {
if (node && node.type === Syntax.Identifier) {
this.__defineGeneric(
node.name,
this.implicit.set,
this.implicit.variables,
node,
def
);
}
}
}
class ModuleScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "module", upperScope, block, false);
}
}
class FunctionExpressionNameScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "function-expression-name", upperScope, block, false);
this.__define(block.id,
new Definition(
Variable.FunctionName,
block.id,
block,
null,
null,
null
));
this.functionExpressionScope = true;
}
}
class CatchScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "catch", upperScope, block, false);
}
}
class WithScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "with", upperScope, block, false);
}
__close(scopeManager) {
if (this.__shouldStaticallyClose(scopeManager)) {
return super.__close(scopeManager);
}
for (let i = 0, iz = this.__left.length; i < iz; ++i) {
const ref = this.__left[i];
ref.tainted = true;
this.__delegateToUpperScope(ref);
}
this.__left = null;
return this.upper;
}
}
class BlockScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "block", upperScope, block, false);
}
}
class SwitchScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "switch", upperScope, block, false);
}
}
class FunctionScope extends Scope {
constructor(scopeManager, upperScope, block, isMethodDefinition) {
super(scopeManager, "function", upperScope, block, isMethodDefinition);
// section 9.2.13, FunctionDeclarationInstantiation.
// NOTE Arrow functions never have an arguments objects.
if (this.block.type !== Syntax.ArrowFunctionExpression) {
this.__defineArguments();
}
}
isArgumentsMaterialized() {
// TODO(Constellation)
// We can more aggressive on this condition like this.
//
// function t() {
// // arguments of t is always hidden.
// function arguments() {
// }
// }
if (this.block.type === Syntax.ArrowFunctionExpression) {
return false;
}
if (!this.isStatic()) {
return true;
}
const variable = this.set.get("arguments");
assert(variable, "Always have arguments variable.");
return variable.tainted || variable.references.length !== 0;
}
isThisMaterialized() {
if (!this.isStatic()) {
return true;
}
return this.thisFound;
}
__defineArguments() {
this.__defineGeneric(
"arguments",
this.set,
this.variables,
null,
null
);
this.taints.set("arguments", true);
}
// References in default parameters isn't resolved to variables which are in their function body.
// const x = 1
// function f(a = x) { // This `x` is resolved to the `x` in the outer scope.
// const x = 2
// console.log(a)
// }
__isValidResolution(ref, variable) {
// If `options.nodejsScope` is true, `this.block` becomes a Program node.
if (this.block.type === "Program") {
return true;
}
const bodyStart = this.block.body.range[0];
// It's invalid resolution in the following case:
return !(
variable.scope === this &&
ref.identifier.range[0] < bodyStart && // the reference is in the parameter part.
variable.defs.every(d => d.name.range[0] >= bodyStart) // the variable is in the body.
);
}
}
class ForScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "for", upperScope, block, false);
}
}
class ClassScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "class", upperScope, block, false);
}
}
class ClassFieldInitializerScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "class-field-initializer", upperScope, block, true);
}
}
class ClassStaticBlockScope extends Scope {
constructor(scopeManager, upperScope, block) {
super(scopeManager, "class-static-block", upperScope, block, true);
}
}
export {
Scope,
GlobalScope,
ModuleScope,
FunctionExpressionNameScope,
CatchScope,
WithScope,
BlockScope,
SwitchScope,
FunctionScope,
ForScope,
ClassScope,
ClassFieldInitializerScope,
ClassStaticBlockScope
};
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,87 @@
/*
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* A Variable represents a locally scoped identifier. These include arguments to
* functions.
* @constructor Variable
*/
class Variable {
constructor(name, scope) {
/**
* The variable name, as given in the source code.
* @member {string} Variable#name
*/
this.name = name;
/**
* List of defining occurrences of this variable (like in 'var ...'
* statements or as parameter), as AST nodes.
* @member {espree.Identifier[]} Variable#identifiers
*/
this.identifiers = [];
/**
* List of {@link Reference|references} of this variable (excluding parameter entries)
* in its defining scope and all nested scopes. For defining
* occurrences only see {@link Variable#defs}.
* @member {Reference[]} Variable#references
*/
this.references = [];
/**
* List of defining occurrences of this variable (like in 'var ...'
* statements or as parameter), as custom objects.
* @member {Definition[]} Variable#defs
*/
this.defs = [];
this.tainted = false;
/**
* Whether this is a stack variable.
* @member {boolean} Variable#stack
*/
this.stack = true;
/**
* Reference to the enclosing Scope.
* @member {Scope} Variable#scope
*/
this.scope = scope;
}
}
Variable.CatchClause = "CatchClause";
Variable.Parameter = "Parameter";
Variable.FunctionName = "FunctionName";
Variable.ClassName = "ClassName";
Variable.Variable = "Variable";
Variable.ImportBinding = "ImportBinding";
Variable.ImplicitGlobalVariable = "ImplicitGlobalVariable";
export default Variable;
/* vim: set sw=4 ts=4 et tw=80 : */

View File

@@ -0,0 +1,3 @@
const version = "7.2.2";
export default version;

View File

@@ -0,0 +1,63 @@
{
"name": "eslint-scope",
"description": "ECMAScript scope analyzer for ESLint",
"homepage": "http://github.com/eslint/eslint-scope",
"main": "./dist/eslint-scope.cjs",
"type": "module",
"exports": {
".": {
"import": "./lib/index.js",
"require": "./dist/eslint-scope.cjs"
},
"./package.json": "./package.json"
},
"version": "7.2.2",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"repository": "eslint/eslint-scope",
"funding": "https://opencollective.com/eslint",
"bugs": {
"url": "https://github.com/eslint/eslint-scope/issues"
},
"license": "BSD-2-Clause",
"scripts": {
"build": "rollup -c",
"lint": "npm run build && node Makefile.js lint",
"update-version": "node tools/update-version.js",
"test": "npm run build && node Makefile.js test",
"prepublishOnly": "npm run update-version && npm run build",
"generate-release": "eslint-generate-release",
"generate-alpharelease": "eslint-generate-prerelease alpha",
"generate-betarelease": "eslint-generate-prerelease beta",
"generate-rcrelease": "eslint-generate-prerelease rc",
"publish-release": "eslint-publish-release"
},
"files": [
"LICENSE",
"README.md",
"lib",
"dist/eslint-scope.cjs"
],
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"devDependencies": {
"@typescript-eslint/parser": "^4.28.1",
"c8": "^7.7.3",
"chai": "^4.3.4",
"eslint": "^7.29.0",
"eslint-config-eslint": "^7.0.0",
"eslint-plugin-jsdoc": "^35.4.1",
"eslint-plugin-node": "^11.1.0",
"eslint-release": "^3.2.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.1",
"mocha": "^9.0.1",
"npm-license": "^0.3.3",
"rollup": "^2.52.7",
"shelljs": "^0.8.4",
"typescript": "^4.3.5"
}
}

View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) Open JS Foundation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,244 @@
[![npm version](https://img.shields.io/npm/v/espree.svg)](https://www.npmjs.com/package/espree)
[![npm downloads](https://img.shields.io/npm/dm/espree.svg)](https://www.npmjs.com/package/espree)
[![Build Status](https://github.com/eslint/espree/workflows/CI/badge.svg)](https://github.com/eslint/espree/actions)
[![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=9348450)](https://www.bountysource.com/trackers/9348450-eslint?utm_source=9348450&utm_medium=shield&utm_campaign=TRACKER_BADGE)
# Espree
Espree started out as a fork of [Esprima](http://esprima.org) v1.2.2, the last stable published released of Esprima before work on ECMAScript 6 began. Espree is now built on top of [Acorn](https://github.com/ternjs/acorn), which has a modular architecture that allows extension of core functionality. The goal of Espree is to produce output that is similar to Esprima with a similar API so that it can be used in place of Esprima.
## Usage
Install:
```
npm i espree
```
To use in an ESM file:
```js
import * as espree from "espree";
const ast = espree.parse(code);
```
To use in a Common JS file:
```js
const espree = require("espree");
const ast = espree.parse(code);
```
## API
### `parse()`
`parse` parses the given code and returns a abstract syntax tree (AST). It takes two parameters.
- `code` [string]() - the code which needs to be parsed.
- `options (Optional)` [Object]() - read more about this [here](#options).
```js
import * as espree from "espree";
const ast = espree.parse(code);
```
**Example :**
```js
const ast = espree.parse('let foo = "bar"', { ecmaVersion: 6 });
console.log(ast);
```
<details><summary>Output</summary>
<p>
```
Node {
type: 'Program',
start: 0,
end: 15,
body: [
Node {
type: 'VariableDeclaration',
start: 0,
end: 15,
declarations: [Array],
kind: 'let'
}
],
sourceType: 'script'
}
```
</p>
</details>
### `tokenize()`
`tokenize` returns the tokens of a given code. It takes two parameters.
- `code` [string]() - the code which needs to be parsed.
- `options (Optional)` [Object]() - read more about this [here](#options).
Even if `options` is empty or undefined or `options.tokens` is `false`, it assigns it to `true` in order to get the `tokens` array
**Example :**
```js
import * as espree from "espree";
const tokens = espree.tokenize('let foo = "bar"', { ecmaVersion: 6 });
console.log(tokens);
```
<details><summary>Output</summary>
<p>
```
Token { type: 'Keyword', value: 'let', start: 0, end: 3 },
Token { type: 'Identifier', value: 'foo', start: 4, end: 7 },
Token { type: 'Punctuator', value: '=', start: 8, end: 9 },
Token { type: 'String', value: '"bar"', start: 10, end: 15 }
```
</p>
</details>
### `version`
Returns the current `espree` version
### `VisitorKeys`
Returns all visitor keys for traversing the AST from [eslint-visitor-keys](https://github.com/eslint/eslint-visitor-keys)
### `latestEcmaVersion`
Returns the latest ECMAScript supported by `espree`
### `supportedEcmaVersions`
Returns an array of all supported ECMAScript versions
## Options
```js
const options = {
// attach range information to each node
range: false,
// attach line/column location information to each node
loc: false,
// create a top-level comments array containing all comments
comment: false,
// create a top-level tokens array containing all tokens
tokens: false,
// Set to 3, 5 (the default), 6, 7, 8, 9, 10, 11, 12, 13, 14 or 15 to specify the version of ECMAScript syntax you want to use.
// You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), 2021 (same as 12), 2022 (same as 13), 2023 (same as 14) or 2024 (same as 15) to use the year-based naming.
// You can also set "latest" to use the most recently supported version.
ecmaVersion: 3,
allowReserved: true, // only allowed when ecmaVersion is 3
// specify which type of script you're parsing ("script", "module", or "commonjs")
sourceType: "script",
// specify additional language features
ecmaFeatures: {
// enable JSX parsing
jsx: false,
// enable return in global scope (set to true automatically when sourceType is "commonjs")
globalReturn: false,
// enable implied strict mode (if ecmaVersion >= 5)
impliedStrict: false
}
}
```
## Esprima Compatibility Going Forward
The primary goal is to produce the exact same AST structure and tokens as Esprima, and that takes precedence over anything else. (The AST structure being the [ESTree](https://github.com/estree/estree) API with JSX extensions.) Separate from that, Espree may deviate from what Esprima outputs in terms of where and how comments are attached, as well as what additional information is available on AST nodes. That is to say, Espree may add more things to the AST nodes than Esprima does but the overall AST structure produced will be the same.
Espree may also deviate from Esprima in the interface it exposes.
## Contributing
Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the [ESLint Contributor Guidelines](http://eslint.org/docs/developer-guide/contributing), so please be sure to read them before contributing. If you're not sure where to dig in, check out the [issues](https://github.com/eslint/espree/issues).
Espree is licensed under a permissive BSD 2-clause license.
## Security Policy
We work hard to ensure that Espree is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md).
## Build Commands
* `npm test` - run all linting and tests
* `npm run lint` - run all linting
## Differences from Espree 2.x
* The `tokenize()` method does not use `ecmaFeatures`. Any string will be tokenized completely based on ECMAScript 6 semantics.
* Trailing whitespace no longer is counted as part of a node.
* `let` and `const` declarations are no longer parsed by default. You must opt-in by using an `ecmaVersion` newer than `5` or setting `sourceType` to `module`.
* The `esparse` and `esvalidate` binary scripts have been removed.
* There is no `tolerant` option. We will investigate adding this back in the future.
## Known Incompatibilities
In an effort to help those wanting to transition from other parsers to Espree, the following is a list of noteworthy incompatibilities with other parsers. These are known differences that we do not intend to change.
### Esprima 1.2.2
* Esprima counts trailing whitespace as part of each AST node while Espree does not. In Espree, the end of a node is where the last token occurs.
* Espree does not parse `let` and `const` declarations by default.
* Error messages returned for parsing errors are different.
* There are two addition properties on every node and token: `start` and `end`. These represent the same data as `range` and are used internally by Acorn.
### Esprima 2.x
* Esprima 2.x uses a different comment attachment algorithm that results in some comments being added in different places than Espree. The algorithm Espree uses is the same one used in Esprima 1.2.2.
## Frequently Asked Questions
### Why another parser
[ESLint](http://eslint.org) had been relying on Esprima as its parser from the beginning. While that was fine when the JavaScript language was evolving slowly, the pace of development increased dramatically and Esprima had fallen behind. ESLint, like many other tools reliant on Esprima, has been stuck in using new JavaScript language features until Esprima updates, and that caused our users frustration.
We decided the only way for us to move forward was to create our own parser, bringing us inline with JSHint and JSLint, and allowing us to keep implementing new features as we need them. We chose to fork Esprima instead of starting from scratch in order to move as quickly as possible with a compatible API.
With Espree 2.0.0, we are no longer a fork of Esprima but rather a translation layer between Acorn and Esprima syntax. This allows us to put work back into a community-supported parser (Acorn) that is continuing to grow and evolve while maintaining an Esprima-compatible parser for those utilities still built on Esprima.
### Have you tried working with Esprima?
Yes. Since the start of ESLint, we've regularly filed bugs and feature requests with Esprima and will continue to do so. However, there are some different philosophies around how the projects work that need to be worked through. The initial goal was to have Espree track Esprima and eventually merge the two back together, but we ultimately decided that building on top of Acorn was a better choice due to Acorn's plugin support.
### Why don't you just use Acorn?
Acorn is a great JavaScript parser that produces an AST that is compatible with Esprima. Unfortunately, ESLint relies on more than just the AST to do its job. It relies on Esprima's tokens and comment attachment features to get a complete picture of the source code. We investigated switching to Acorn, but the inconsistencies between Esprima and Acorn created too much work for a project like ESLint.
We are building on top of Acorn, however, so that we can contribute back and help make Acorn even better.
### What ECMAScript features do you support?
Espree supports all ECMAScript 2023 features and partially supports ECMAScript 2024 features.
Because ECMAScript 2024 is still under development, we are implementing features as they are finalized. Currently, Espree supports:
* [RegExp v flag with set notation + properties of strings](https://github.com/tc39/proposal-regexp-v-flag)
See [finished-proposals.md](https://github.com/tc39/proposals/blob/master/finished-proposals.md) to know what features are finalized.
### How do you determine which experimental features to support?
In general, we do not support experimental JavaScript features. We may make exceptions from time to time depending on the maturity of the features.

View File

@@ -0,0 +1,883 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var acorn = require('acorn');
var jsx = require('acorn-jsx');
var visitorKeys = require('eslint-visitor-keys');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var acorn__namespace = /*#__PURE__*/_interopNamespace(acorn);
var jsx__default = /*#__PURE__*/_interopDefaultLegacy(jsx);
var visitorKeys__namespace = /*#__PURE__*/_interopNamespace(visitorKeys);
/**
* @fileoverview Translates tokens between Acorn format and Esprima format.
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
// none!
//------------------------------------------------------------------------------
// Private
//------------------------------------------------------------------------------
// Esprima Token Types
const Token = {
Boolean: "Boolean",
EOF: "<end>",
Identifier: "Identifier",
PrivateIdentifier: "PrivateIdentifier",
Keyword: "Keyword",
Null: "Null",
Numeric: "Numeric",
Punctuator: "Punctuator",
String: "String",
RegularExpression: "RegularExpression",
Template: "Template",
JSXIdentifier: "JSXIdentifier",
JSXText: "JSXText"
};
/**
* Converts part of a template into an Esprima token.
* @param {AcornToken[]} tokens The Acorn tokens representing the template.
* @param {string} code The source code.
* @returns {EsprimaToken} The Esprima equivalent of the template token.
* @private
*/
function convertTemplatePart(tokens, code) {
const firstToken = tokens[0],
lastTemplateToken = tokens[tokens.length - 1];
const token = {
type: Token.Template,
value: code.slice(firstToken.start, lastTemplateToken.end)
};
if (firstToken.loc) {
token.loc = {
start: firstToken.loc.start,
end: lastTemplateToken.loc.end
};
}
if (firstToken.range) {
token.start = firstToken.range[0];
token.end = lastTemplateToken.range[1];
token.range = [token.start, token.end];
}
return token;
}
/**
* Contains logic to translate Acorn tokens into Esprima tokens.
* @param {Object} acornTokTypes The Acorn token types.
* @param {string} code The source code Acorn is parsing. This is necessary
* to correct the "value" property of some tokens.
* @constructor
*/
function TokenTranslator(acornTokTypes, code) {
// token types
this._acornTokTypes = acornTokTypes;
// token buffer for templates
this._tokens = [];
// track the last curly brace
this._curlyBrace = null;
// the source code
this._code = code;
}
TokenTranslator.prototype = {
constructor: TokenTranslator,
/**
* Translates a single Esprima token to a single Acorn token. This may be
* inaccurate due to how templates are handled differently in Esprima and
* Acorn, but should be accurate for all other tokens.
* @param {AcornToken} token The Acorn token to translate.
* @param {Object} extra Espree extra object.
* @returns {EsprimaToken} The Esprima version of the token.
*/
translate(token, extra) {
const type = token.type,
tt = this._acornTokTypes;
if (type === tt.name) {
token.type = Token.Identifier;
// TODO: See if this is an Acorn bug
if (token.value === "static") {
token.type = Token.Keyword;
}
if (extra.ecmaVersion > 5 && (token.value === "yield" || token.value === "let")) {
token.type = Token.Keyword;
}
} else if (type === tt.privateId) {
token.type = Token.PrivateIdentifier;
} else if (type === tt.semi || type === tt.comma ||
type === tt.parenL || type === tt.parenR ||
type === tt.braceL || type === tt.braceR ||
type === tt.dot || type === tt.bracketL ||
type === tt.colon || type === tt.question ||
type === tt.bracketR || type === tt.ellipsis ||
type === tt.arrow || type === tt.jsxTagStart ||
type === tt.incDec || type === tt.starstar ||
type === tt.jsxTagEnd || type === tt.prefix ||
type === tt.questionDot ||
(type.binop && !type.keyword) ||
type.isAssign) {
token.type = Token.Punctuator;
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.jsxName) {
token.type = Token.JSXIdentifier;
} else if (type.label === "jsxText" || type === tt.jsxAttrValueToken) {
token.type = Token.JSXText;
} else if (type.keyword) {
if (type.keyword === "true" || type.keyword === "false") {
token.type = Token.Boolean;
} else if (type.keyword === "null") {
token.type = Token.Null;
} else {
token.type = Token.Keyword;
}
} else if (type === tt.num) {
token.type = Token.Numeric;
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.string) {
if (extra.jsxAttrValueToken) {
extra.jsxAttrValueToken = false;
token.type = Token.JSXText;
} else {
token.type = Token.String;
}
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.regexp) {
token.type = Token.RegularExpression;
const value = token.value;
token.regex = {
flags: value.flags,
pattern: value.pattern
};
token.value = `/${value.pattern}/${value.flags}`;
}
return token;
},
/**
* Function to call during Acorn's onToken handler.
* @param {AcornToken} token The Acorn token.
* @param {Object} extra The Espree extra object.
* @returns {void}
*/
onToken(token, extra) {
const tt = this._acornTokTypes,
tokens = extra.tokens,
templateTokens = this._tokens;
/**
* Flushes the buffered template tokens and resets the template
* tracking.
* @returns {void}
* @private
*/
const translateTemplateTokens = () => {
tokens.push(convertTemplatePart(this._tokens, this._code));
this._tokens = [];
};
if (token.type === tt.eof) {
// might be one last curlyBrace
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
}
return;
}
if (token.type === tt.backQuote) {
// if there's already a curly, it's not part of the template
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
this._curlyBrace = null;
}
templateTokens.push(token);
// it's the end
if (templateTokens.length > 1) {
translateTemplateTokens();
}
return;
}
if (token.type === tt.dollarBraceL) {
templateTokens.push(token);
translateTemplateTokens();
return;
}
if (token.type === tt.braceR) {
// if there's already a curly, it's not part of the template
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
}
// store new curly for later
this._curlyBrace = token;
return;
}
if (token.type === tt.template || token.type === tt.invalidTemplate) {
if (this._curlyBrace) {
templateTokens.push(this._curlyBrace);
this._curlyBrace = null;
}
templateTokens.push(token);
return;
}
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
this._curlyBrace = null;
}
tokens.push(this.translate(token, extra));
}
};
/**
* @fileoverview A collection of methods for processing Espree's options.
* @author Kai Cataldo
*/
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const SUPPORTED_VERSIONS = [
3,
5,
6, // 2015
7, // 2016
8, // 2017
9, // 2018
10, // 2019
11, // 2020
12, // 2021
13, // 2022
14, // 2023
15 // 2024
];
/**
* Get the latest ECMAScript version supported by Espree.
* @returns {number} The latest ECMAScript version.
*/
function getLatestEcmaVersion() {
return SUPPORTED_VERSIONS[SUPPORTED_VERSIONS.length - 1];
}
/**
* Get the list of ECMAScript versions supported by Espree.
* @returns {number[]} An array containing the supported ECMAScript versions.
*/
function getSupportedEcmaVersions() {
return [...SUPPORTED_VERSIONS];
}
/**
* Normalize ECMAScript version from the initial config
* @param {(number|"latest")} ecmaVersion ECMAScript version from the initial config
* @throws {Error} throws an error if the ecmaVersion is invalid.
* @returns {number} normalized ECMAScript version
*/
function normalizeEcmaVersion(ecmaVersion = 5) {
let version = ecmaVersion === "latest" ? getLatestEcmaVersion() : ecmaVersion;
if (typeof version !== "number") {
throw new Error(`ecmaVersion must be a number or "latest". Received value of type ${typeof ecmaVersion} instead.`);
}
// Calculate ECMAScript edition number from official year version starting with
// ES2015, which corresponds with ES6 (or a difference of 2009).
if (version >= 2015) {
version -= 2009;
}
if (!SUPPORTED_VERSIONS.includes(version)) {
throw new Error("Invalid ecmaVersion.");
}
return version;
}
/**
* Normalize sourceType from the initial config
* @param {string} sourceType to normalize
* @throws {Error} throw an error if sourceType is invalid
* @returns {string} normalized sourceType
*/
function normalizeSourceType(sourceType = "script") {
if (sourceType === "script" || sourceType === "module") {
return sourceType;
}
if (sourceType === "commonjs") {
return "script";
}
throw new Error("Invalid sourceType.");
}
/**
* Normalize parserOptions
* @param {Object} options the parser options to normalize
* @throws {Error} throw an error if found invalid option.
* @returns {Object} normalized options
*/
function normalizeOptions(options) {
const ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
const sourceType = normalizeSourceType(options.sourceType);
const ranges = options.range === true;
const locations = options.loc === true;
if (ecmaVersion !== 3 && options.allowReserved) {
// a value of `false` is intentionally allowed here, so a shared config can overwrite it when needed
throw new Error("`allowReserved` is only supported when ecmaVersion is 3");
}
if (typeof options.allowReserved !== "undefined" && typeof options.allowReserved !== "boolean") {
throw new Error("`allowReserved`, when present, must be `true` or `false`");
}
const allowReserved = ecmaVersion === 3 ? (options.allowReserved || "never") : false;
const ecmaFeatures = options.ecmaFeatures || {};
const allowReturnOutsideFunction = options.sourceType === "commonjs" ||
Boolean(ecmaFeatures.globalReturn);
if (sourceType === "module" && ecmaVersion < 6) {
throw new Error("sourceType 'module' is not supported when ecmaVersion < 2015. Consider adding `{ ecmaVersion: 2015 }` to the parser options.");
}
return Object.assign({}, options, {
ecmaVersion,
sourceType,
ranges,
locations,
allowReserved,
allowReturnOutsideFunction
});
}
/* eslint no-param-reassign: 0 -- stylistic choice */
const STATE = Symbol("espree's internal state");
const ESPRIMA_FINISH_NODE = Symbol("espree's esprimaFinishNode");
/**
* Converts an Acorn comment to a Esprima comment.
* @param {boolean} block True if it's a block comment, false if not.
* @param {string} text The text of the comment.
* @param {int} start The index at which the comment starts.
* @param {int} end The index at which the comment ends.
* @param {Location} startLoc The location at which the comment starts.
* @param {Location} endLoc The location at which the comment ends.
* @param {string} code The source code being parsed.
* @returns {Object} The comment object.
* @private
*/
function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc, code) {
let type;
if (block) {
type = "Block";
} else if (code.slice(start, start + 2) === "#!") {
type = "Hashbang";
} else {
type = "Line";
}
const comment = {
type,
value: text
};
if (typeof start === "number") {
comment.start = start;
comment.end = end;
comment.range = [start, end];
}
if (typeof startLoc === "object") {
comment.loc = {
start: startLoc,
end: endLoc
};
}
return comment;
}
var espree = () => Parser => {
const tokTypes = Object.assign({}, Parser.acorn.tokTypes);
if (Parser.acornJsx) {
Object.assign(tokTypes, Parser.acornJsx.tokTypes);
}
return class Espree extends Parser {
constructor(opts, code) {
if (typeof opts !== "object" || opts === null) {
opts = {};
}
if (typeof code !== "string" && !(code instanceof String)) {
code = String(code);
}
// save original source type in case of commonjs
const originalSourceType = opts.sourceType;
const options = normalizeOptions(opts);
const ecmaFeatures = options.ecmaFeatures || {};
const tokenTranslator =
options.tokens === true
? new TokenTranslator(tokTypes, code)
: null;
/*
* Data that is unique to Espree and is not represented internally
* in Acorn.
*
* For ES2023 hashbangs, Espree will call `onComment()` during the
* constructor, so we must define state before having access to
* `this`.
*/
const state = {
originalSourceType: originalSourceType || options.sourceType,
tokens: tokenTranslator ? [] : null,
comments: options.comment === true ? [] : null,
impliedStrict: ecmaFeatures.impliedStrict === true && options.ecmaVersion >= 5,
ecmaVersion: options.ecmaVersion,
jsxAttrValueToken: false,
lastToken: null,
templateElements: []
};
// Initialize acorn parser.
super({
// do not use spread, because we don't want to pass any unknown options to acorn
ecmaVersion: options.ecmaVersion,
sourceType: options.sourceType,
ranges: options.ranges,
locations: options.locations,
allowReserved: options.allowReserved,
// Truthy value is true for backward compatibility.
allowReturnOutsideFunction: options.allowReturnOutsideFunction,
// Collect tokens
onToken(token) {
if (tokenTranslator) {
// Use `tokens`, `ecmaVersion`, and `jsxAttrValueToken` in the state.
tokenTranslator.onToken(token, state);
}
if (token.type !== tokTypes.eof) {
state.lastToken = token;
}
},
// Collect comments
onComment(block, text, start, end, startLoc, endLoc) {
if (state.comments) {
const comment = convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc, code);
state.comments.push(comment);
}
}
}, code);
/*
* We put all of this data into a symbol property as a way to avoid
* potential naming conflicts with future versions of Acorn.
*/
this[STATE] = state;
}
tokenize() {
do {
this.next();
} while (this.type !== tokTypes.eof);
// Consume the final eof token
this.next();
const extra = this[STATE];
const tokens = extra.tokens;
if (extra.comments) {
tokens.comments = extra.comments;
}
return tokens;
}
finishNode(...args) {
const result = super.finishNode(...args);
return this[ESPRIMA_FINISH_NODE](result);
}
finishNodeAt(...args) {
const result = super.finishNodeAt(...args);
return this[ESPRIMA_FINISH_NODE](result);
}
parse() {
const extra = this[STATE];
const program = super.parse();
program.sourceType = extra.originalSourceType;
if (extra.comments) {
program.comments = extra.comments;
}
if (extra.tokens) {
program.tokens = extra.tokens;
}
/*
* Adjust opening and closing position of program to match Esprima.
* Acorn always starts programs at range 0 whereas Esprima starts at the
* first AST node's start (the only real difference is when there's leading
* whitespace or leading comments). Acorn also counts trailing whitespace
* as part of the program whereas Esprima only counts up to the last token.
*/
if (program.body.length) {
const [firstNode] = program.body;
if (program.range) {
program.range[0] = firstNode.range[0];
}
if (program.loc) {
program.loc.start = firstNode.loc.start;
}
program.start = firstNode.start;
}
if (extra.lastToken) {
if (program.range) {
program.range[1] = extra.lastToken.range[1];
}
if (program.loc) {
program.loc.end = extra.lastToken.loc.end;
}
program.end = extra.lastToken.end;
}
/*
* https://github.com/eslint/espree/issues/349
* Ensure that template elements have correct range information.
* This is one location where Acorn produces a different value
* for its start and end properties vs. the values present in the
* range property. In order to avoid confusion, we set the start
* and end properties to the values that are present in range.
* This is done here, instead of in finishNode(), because Acorn
* uses the values of start and end internally while parsing, making
* it dangerous to change those values while parsing is ongoing.
* By waiting until the end of parsing, we can safely change these
* values without affect any other part of the process.
*/
this[STATE].templateElements.forEach(templateElement => {
const startOffset = -1;
const endOffset = templateElement.tail ? 1 : 2;
templateElement.start += startOffset;
templateElement.end += endOffset;
if (templateElement.range) {
templateElement.range[0] += startOffset;
templateElement.range[1] += endOffset;
}
if (templateElement.loc) {
templateElement.loc.start.column += startOffset;
templateElement.loc.end.column += endOffset;
}
});
return program;
}
parseTopLevel(node) {
if (this[STATE].impliedStrict) {
this.strict = true;
}
return super.parseTopLevel(node);
}
/**
* Overwrites the default raise method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @param {string} message The error message.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
raise(pos, message) {
const loc = Parser.acorn.getLineInfo(this.input, pos);
const err = new SyntaxError(message);
err.index = pos;
err.lineNumber = loc.line;
err.column = loc.column + 1; // acorn uses 0-based columns
throw err;
}
/**
* Overwrites the default raise method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @param {string} message The error message.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
raiseRecoverable(pos, message) {
this.raise(pos, message);
}
/**
* Overwrites the default unexpected method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
unexpected(pos) {
let message = "Unexpected token";
if (pos !== null && pos !== void 0) {
this.pos = pos;
if (this.options.locations) {
while (this.pos < this.lineStart) {
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
--this.curLine;
}
}
this.nextToken();
}
if (this.end > this.start) {
message += ` ${this.input.slice(this.start, this.end)}`;
}
this.raise(this.start, message);
}
/*
* Esprima-FB represents JSX strings as tokens called "JSXText", but Acorn-JSX
* uses regular tt.string without any distinction between this and regular JS
* strings. As such, we intercept an attempt to read a JSX string and set a flag
* on extra so that when tokens are converted, the next token will be switched
* to JSXText via onToken.
*/
jsx_readString(quote) { // eslint-disable-line camelcase -- required by API
const result = super.jsx_readString(quote);
if (this.type === tokTypes.string) {
this[STATE].jsxAttrValueToken = true;
}
return result;
}
/**
* Performs last-minute Esprima-specific compatibility checks and fixes.
* @param {ASTNode} result The node to check.
* @returns {ASTNode} The finished node.
*/
[ESPRIMA_FINISH_NODE](result) {
// Acorn doesn't count the opening and closing backticks as part of templates
// so we have to adjust ranges/locations appropriately.
if (result.type === "TemplateElement") {
// save template element references to fix start/end later
this[STATE].templateElements.push(result);
}
if (result.type.includes("Function") && !result.generator) {
result.generator = false;
}
return result;
}
};
};
const version$1 = "9.6.1";
/* eslint-disable jsdoc/no-multi-asterisks -- needed to preserve original formatting of licences */
// To initialize lazily.
const parsers = {
_regular: null,
_jsx: null,
get regular() {
if (this._regular === null) {
this._regular = acorn__namespace.Parser.extend(espree());
}
return this._regular;
},
get jsx() {
if (this._jsx === null) {
this._jsx = acorn__namespace.Parser.extend(jsx__default["default"](), espree());
}
return this._jsx;
},
get(options) {
const useJsx = Boolean(
options &&
options.ecmaFeatures &&
options.ecmaFeatures.jsx
);
return useJsx ? this.jsx : this.regular;
}
};
//------------------------------------------------------------------------------
// Tokenizer
//------------------------------------------------------------------------------
/**
* Tokenizes the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {Token[]} An array of tokens.
* @throws {SyntaxError} If the input code is invalid.
* @private
*/
function tokenize(code, options) {
const Parser = parsers.get(options);
// Ensure to collect tokens.
if (!options || options.tokens !== true) {
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign -- stylistic choice
}
return new Parser(options, code).tokenize();
}
//------------------------------------------------------------------------------
// Parser
//------------------------------------------------------------------------------
/**
* Parses the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {ASTNode} The "Program" AST node.
* @throws {SyntaxError} If the input code is invalid.
*/
function parse(code, options) {
const Parser = parsers.get(options);
return new Parser(options, code).parse();
}
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
const version = version$1;
const name = "espree";
/* istanbul ignore next */
const VisitorKeys = (function() {
return visitorKeys__namespace.KEYS;
}());
// Derive node types from VisitorKeys
/* istanbul ignore next */
const Syntax = (function() {
let key,
types = {};
if (typeof Object.create === "function") {
types = Object.create(null);
}
for (key in VisitorKeys) {
if (Object.hasOwnProperty.call(VisitorKeys, key)) {
types[key] = key;
}
}
if (typeof Object.freeze === "function") {
Object.freeze(types);
}
return types;
}());
const latestEcmaVersion = getLatestEcmaVersion();
const supportedEcmaVersions = getSupportedEcmaVersions();
exports.Syntax = Syntax;
exports.VisitorKeys = VisitorKeys;
exports.latestEcmaVersion = latestEcmaVersion;
exports.name = name;
exports.parse = parse;
exports.supportedEcmaVersions = supportedEcmaVersions;
exports.tokenize = tokenize;
exports.version = version;

View File

@@ -0,0 +1,177 @@
/* eslint-disable jsdoc/no-multi-asterisks -- needed to preserve original formatting of licences */
/**
* @fileoverview Main Espree file that converts Acorn into Esprima output.
*
* This file contains code from the following MIT-licensed projects:
* 1. Acorn
* 2. Babylon
* 3. Babel-ESLint
*
* This file also contains code from Esprima, which is BSD licensed.
*
* Acorn is Copyright 2012-2015 Acorn Contributors (https://github.com/marijnh/acorn/blob/master/AUTHORS)
* Babylon is Copyright 2014-2015 various contributors (https://github.com/babel/babel/blob/master/packages/babylon/AUTHORS)
* Babel-ESLint is Copyright 2014-2015 Sebastian McKenzie <sebmck@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Esprima is Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-enable jsdoc/no-multi-asterisks -- needed to preserve original formatting of licences */
import * as acorn from "acorn";
import jsx from "acorn-jsx";
import espree from "./lib/espree.js";
import espreeVersion from "./lib/version.js";
import * as visitorKeys from "eslint-visitor-keys";
import { getLatestEcmaVersion, getSupportedEcmaVersions } from "./lib/options.js";
// To initialize lazily.
const parsers = {
_regular: null,
_jsx: null,
get regular() {
if (this._regular === null) {
this._regular = acorn.Parser.extend(espree());
}
return this._regular;
},
get jsx() {
if (this._jsx === null) {
this._jsx = acorn.Parser.extend(jsx(), espree());
}
return this._jsx;
},
get(options) {
const useJsx = Boolean(
options &&
options.ecmaFeatures &&
options.ecmaFeatures.jsx
);
return useJsx ? this.jsx : this.regular;
}
};
//------------------------------------------------------------------------------
// Tokenizer
//------------------------------------------------------------------------------
/**
* Tokenizes the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {Token[]} An array of tokens.
* @throws {SyntaxError} If the input code is invalid.
* @private
*/
export function tokenize(code, options) {
const Parser = parsers.get(options);
// Ensure to collect tokens.
if (!options || options.tokens !== true) {
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign -- stylistic choice
}
return new Parser(options, code).tokenize();
}
//------------------------------------------------------------------------------
// Parser
//------------------------------------------------------------------------------
/**
* Parses the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {ASTNode} The "Program" AST node.
* @throws {SyntaxError} If the input code is invalid.
*/
export function parse(code, options) {
const Parser = parsers.get(options);
return new Parser(options, code).parse();
}
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
export const version = espreeVersion;
export const name = "espree";
/* istanbul ignore next */
export const VisitorKeys = (function() {
return visitorKeys.KEYS;
}());
// Derive node types from VisitorKeys
/* istanbul ignore next */
export const Syntax = (function() {
let key,
types = {};
if (typeof Object.create === "function") {
types = Object.create(null);
}
for (key in VisitorKeys) {
if (Object.hasOwnProperty.call(VisitorKeys, key)) {
types[key] = key;
}
}
if (typeof Object.freeze === "function") {
Object.freeze(types);
}
return types;
}());
export const latestEcmaVersion = getLatestEcmaVersion();
export const supportedEcmaVersions = getSupportedEcmaVersions();

View File

@@ -0,0 +1,349 @@
/* eslint no-param-reassign: 0 -- stylistic choice */
import TokenTranslator from "./token-translator.js";
import { normalizeOptions } from "./options.js";
const STATE = Symbol("espree's internal state");
const ESPRIMA_FINISH_NODE = Symbol("espree's esprimaFinishNode");
/**
* Converts an Acorn comment to a Esprima comment.
* @param {boolean} block True if it's a block comment, false if not.
* @param {string} text The text of the comment.
* @param {int} start The index at which the comment starts.
* @param {int} end The index at which the comment ends.
* @param {Location} startLoc The location at which the comment starts.
* @param {Location} endLoc The location at which the comment ends.
* @param {string} code The source code being parsed.
* @returns {Object} The comment object.
* @private
*/
function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc, code) {
let type;
if (block) {
type = "Block";
} else if (code.slice(start, start + 2) === "#!") {
type = "Hashbang";
} else {
type = "Line";
}
const comment = {
type,
value: text
};
if (typeof start === "number") {
comment.start = start;
comment.end = end;
comment.range = [start, end];
}
if (typeof startLoc === "object") {
comment.loc = {
start: startLoc,
end: endLoc
};
}
return comment;
}
export default () => Parser => {
const tokTypes = Object.assign({}, Parser.acorn.tokTypes);
if (Parser.acornJsx) {
Object.assign(tokTypes, Parser.acornJsx.tokTypes);
}
return class Espree extends Parser {
constructor(opts, code) {
if (typeof opts !== "object" || opts === null) {
opts = {};
}
if (typeof code !== "string" && !(code instanceof String)) {
code = String(code);
}
// save original source type in case of commonjs
const originalSourceType = opts.sourceType;
const options = normalizeOptions(opts);
const ecmaFeatures = options.ecmaFeatures || {};
const tokenTranslator =
options.tokens === true
? new TokenTranslator(tokTypes, code)
: null;
/*
* Data that is unique to Espree and is not represented internally
* in Acorn.
*
* For ES2023 hashbangs, Espree will call `onComment()` during the
* constructor, so we must define state before having access to
* `this`.
*/
const state = {
originalSourceType: originalSourceType || options.sourceType,
tokens: tokenTranslator ? [] : null,
comments: options.comment === true ? [] : null,
impliedStrict: ecmaFeatures.impliedStrict === true && options.ecmaVersion >= 5,
ecmaVersion: options.ecmaVersion,
jsxAttrValueToken: false,
lastToken: null,
templateElements: []
};
// Initialize acorn parser.
super({
// do not use spread, because we don't want to pass any unknown options to acorn
ecmaVersion: options.ecmaVersion,
sourceType: options.sourceType,
ranges: options.ranges,
locations: options.locations,
allowReserved: options.allowReserved,
// Truthy value is true for backward compatibility.
allowReturnOutsideFunction: options.allowReturnOutsideFunction,
// Collect tokens
onToken(token) {
if (tokenTranslator) {
// Use `tokens`, `ecmaVersion`, and `jsxAttrValueToken` in the state.
tokenTranslator.onToken(token, state);
}
if (token.type !== tokTypes.eof) {
state.lastToken = token;
}
},
// Collect comments
onComment(block, text, start, end, startLoc, endLoc) {
if (state.comments) {
const comment = convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc, code);
state.comments.push(comment);
}
}
}, code);
/*
* We put all of this data into a symbol property as a way to avoid
* potential naming conflicts with future versions of Acorn.
*/
this[STATE] = state;
}
tokenize() {
do {
this.next();
} while (this.type !== tokTypes.eof);
// Consume the final eof token
this.next();
const extra = this[STATE];
const tokens = extra.tokens;
if (extra.comments) {
tokens.comments = extra.comments;
}
return tokens;
}
finishNode(...args) {
const result = super.finishNode(...args);
return this[ESPRIMA_FINISH_NODE](result);
}
finishNodeAt(...args) {
const result = super.finishNodeAt(...args);
return this[ESPRIMA_FINISH_NODE](result);
}
parse() {
const extra = this[STATE];
const program = super.parse();
program.sourceType = extra.originalSourceType;
if (extra.comments) {
program.comments = extra.comments;
}
if (extra.tokens) {
program.tokens = extra.tokens;
}
/*
* Adjust opening and closing position of program to match Esprima.
* Acorn always starts programs at range 0 whereas Esprima starts at the
* first AST node's start (the only real difference is when there's leading
* whitespace or leading comments). Acorn also counts trailing whitespace
* as part of the program whereas Esprima only counts up to the last token.
*/
if (program.body.length) {
const [firstNode] = program.body;
if (program.range) {
program.range[0] = firstNode.range[0];
}
if (program.loc) {
program.loc.start = firstNode.loc.start;
}
program.start = firstNode.start;
}
if (extra.lastToken) {
if (program.range) {
program.range[1] = extra.lastToken.range[1];
}
if (program.loc) {
program.loc.end = extra.lastToken.loc.end;
}
program.end = extra.lastToken.end;
}
/*
* https://github.com/eslint/espree/issues/349
* Ensure that template elements have correct range information.
* This is one location where Acorn produces a different value
* for its start and end properties vs. the values present in the
* range property. In order to avoid confusion, we set the start
* and end properties to the values that are present in range.
* This is done here, instead of in finishNode(), because Acorn
* uses the values of start and end internally while parsing, making
* it dangerous to change those values while parsing is ongoing.
* By waiting until the end of parsing, we can safely change these
* values without affect any other part of the process.
*/
this[STATE].templateElements.forEach(templateElement => {
const startOffset = -1;
const endOffset = templateElement.tail ? 1 : 2;
templateElement.start += startOffset;
templateElement.end += endOffset;
if (templateElement.range) {
templateElement.range[0] += startOffset;
templateElement.range[1] += endOffset;
}
if (templateElement.loc) {
templateElement.loc.start.column += startOffset;
templateElement.loc.end.column += endOffset;
}
});
return program;
}
parseTopLevel(node) {
if (this[STATE].impliedStrict) {
this.strict = true;
}
return super.parseTopLevel(node);
}
/**
* Overwrites the default raise method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @param {string} message The error message.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
raise(pos, message) {
const loc = Parser.acorn.getLineInfo(this.input, pos);
const err = new SyntaxError(message);
err.index = pos;
err.lineNumber = loc.line;
err.column = loc.column + 1; // acorn uses 0-based columns
throw err;
}
/**
* Overwrites the default raise method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @param {string} message The error message.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
raiseRecoverable(pos, message) {
this.raise(pos, message);
}
/**
* Overwrites the default unexpected method to throw Esprima-style errors.
* @param {int} pos The position of the error.
* @throws {SyntaxError} A syntax error.
* @returns {void}
*/
unexpected(pos) {
let message = "Unexpected token";
if (pos !== null && pos !== void 0) {
this.pos = pos;
if (this.options.locations) {
while (this.pos < this.lineStart) {
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
--this.curLine;
}
}
this.nextToken();
}
if (this.end > this.start) {
message += ` ${this.input.slice(this.start, this.end)}`;
}
this.raise(this.start, message);
}
/*
* Esprima-FB represents JSX strings as tokens called "JSXText", but Acorn-JSX
* uses regular tt.string without any distinction between this and regular JS
* strings. As such, we intercept an attempt to read a JSX string and set a flag
* on extra so that when tokens are converted, the next token will be switched
* to JSXText via onToken.
*/
jsx_readString(quote) { // eslint-disable-line camelcase -- required by API
const result = super.jsx_readString(quote);
if (this.type === tokTypes.string) {
this[STATE].jsxAttrValueToken = true;
}
return result;
}
/**
* Performs last-minute Esprima-specific compatibility checks and fixes.
* @param {ASTNode} result The node to check.
* @returns {ASTNode} The finished node.
*/
[ESPRIMA_FINISH_NODE](result) {
// Acorn doesn't count the opening and closing backticks as part of templates
// so we have to adjust ranges/locations appropriately.
if (result.type === "TemplateElement") {
// save template element references to fix start/end later
this[STATE].templateElements.push(result);
}
if (result.type.includes("Function") && !result.generator) {
result.generator = false;
}
return result;
}
};
};

View File

@@ -0,0 +1,27 @@
/**
* @fileoverview The list of feature flags supported by the parser and their default
* settings.
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
// None!
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
export default {
// React JSX parsing
jsx: false,
// allow return statement in global scope
globalReturn: false,
// allow implied strict mode
impliedStrict: false
};

View File

@@ -0,0 +1,123 @@
/**
* @fileoverview A collection of methods for processing Espree's options.
* @author Kai Cataldo
*/
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const SUPPORTED_VERSIONS = [
3,
5,
6, // 2015
7, // 2016
8, // 2017
9, // 2018
10, // 2019
11, // 2020
12, // 2021
13, // 2022
14, // 2023
15 // 2024
];
/**
* Get the latest ECMAScript version supported by Espree.
* @returns {number} The latest ECMAScript version.
*/
export function getLatestEcmaVersion() {
return SUPPORTED_VERSIONS[SUPPORTED_VERSIONS.length - 1];
}
/**
* Get the list of ECMAScript versions supported by Espree.
* @returns {number[]} An array containing the supported ECMAScript versions.
*/
export function getSupportedEcmaVersions() {
return [...SUPPORTED_VERSIONS];
}
/**
* Normalize ECMAScript version from the initial config
* @param {(number|"latest")} ecmaVersion ECMAScript version from the initial config
* @throws {Error} throws an error if the ecmaVersion is invalid.
* @returns {number} normalized ECMAScript version
*/
function normalizeEcmaVersion(ecmaVersion = 5) {
let version = ecmaVersion === "latest" ? getLatestEcmaVersion() : ecmaVersion;
if (typeof version !== "number") {
throw new Error(`ecmaVersion must be a number or "latest". Received value of type ${typeof ecmaVersion} instead.`);
}
// Calculate ECMAScript edition number from official year version starting with
// ES2015, which corresponds with ES6 (or a difference of 2009).
if (version >= 2015) {
version -= 2009;
}
if (!SUPPORTED_VERSIONS.includes(version)) {
throw new Error("Invalid ecmaVersion.");
}
return version;
}
/**
* Normalize sourceType from the initial config
* @param {string} sourceType to normalize
* @throws {Error} throw an error if sourceType is invalid
* @returns {string} normalized sourceType
*/
function normalizeSourceType(sourceType = "script") {
if (sourceType === "script" || sourceType === "module") {
return sourceType;
}
if (sourceType === "commonjs") {
return "script";
}
throw new Error("Invalid sourceType.");
}
/**
* Normalize parserOptions
* @param {Object} options the parser options to normalize
* @throws {Error} throw an error if found invalid option.
* @returns {Object} normalized options
*/
export function normalizeOptions(options) {
const ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
const sourceType = normalizeSourceType(options.sourceType);
const ranges = options.range === true;
const locations = options.loc === true;
if (ecmaVersion !== 3 && options.allowReserved) {
// a value of `false` is intentionally allowed here, so a shared config can overwrite it when needed
throw new Error("`allowReserved` is only supported when ecmaVersion is 3");
}
if (typeof options.allowReserved !== "undefined" && typeof options.allowReserved !== "boolean") {
throw new Error("`allowReserved`, when present, must be `true` or `false`");
}
const allowReserved = ecmaVersion === 3 ? (options.allowReserved || "never") : false;
const ecmaFeatures = options.ecmaFeatures || {};
const allowReturnOutsideFunction = options.sourceType === "commonjs" ||
Boolean(ecmaFeatures.globalReturn);
if (sourceType === "module" && ecmaVersion < 6) {
throw new Error("sourceType 'module' is not supported when ecmaVersion < 2015. Consider adding `{ ecmaVersion: 2015 }` to the parser options.");
}
return Object.assign({}, options, {
ecmaVersion,
sourceType,
ranges,
locations,
allowReserved,
allowReturnOutsideFunction
});
}

View File

@@ -0,0 +1,263 @@
/**
* @fileoverview Translates tokens between Acorn format and Esprima format.
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
// none!
//------------------------------------------------------------------------------
// Private
//------------------------------------------------------------------------------
// Esprima Token Types
const Token = {
Boolean: "Boolean",
EOF: "<end>",
Identifier: "Identifier",
PrivateIdentifier: "PrivateIdentifier",
Keyword: "Keyword",
Null: "Null",
Numeric: "Numeric",
Punctuator: "Punctuator",
String: "String",
RegularExpression: "RegularExpression",
Template: "Template",
JSXIdentifier: "JSXIdentifier",
JSXText: "JSXText"
};
/**
* Converts part of a template into an Esprima token.
* @param {AcornToken[]} tokens The Acorn tokens representing the template.
* @param {string} code The source code.
* @returns {EsprimaToken} The Esprima equivalent of the template token.
* @private
*/
function convertTemplatePart(tokens, code) {
const firstToken = tokens[0],
lastTemplateToken = tokens[tokens.length - 1];
const token = {
type: Token.Template,
value: code.slice(firstToken.start, lastTemplateToken.end)
};
if (firstToken.loc) {
token.loc = {
start: firstToken.loc.start,
end: lastTemplateToken.loc.end
};
}
if (firstToken.range) {
token.start = firstToken.range[0];
token.end = lastTemplateToken.range[1];
token.range = [token.start, token.end];
}
return token;
}
/**
* Contains logic to translate Acorn tokens into Esprima tokens.
* @param {Object} acornTokTypes The Acorn token types.
* @param {string} code The source code Acorn is parsing. This is necessary
* to correct the "value" property of some tokens.
* @constructor
*/
function TokenTranslator(acornTokTypes, code) {
// token types
this._acornTokTypes = acornTokTypes;
// token buffer for templates
this._tokens = [];
// track the last curly brace
this._curlyBrace = null;
// the source code
this._code = code;
}
TokenTranslator.prototype = {
constructor: TokenTranslator,
/**
* Translates a single Esprima token to a single Acorn token. This may be
* inaccurate due to how templates are handled differently in Esprima and
* Acorn, but should be accurate for all other tokens.
* @param {AcornToken} token The Acorn token to translate.
* @param {Object} extra Espree extra object.
* @returns {EsprimaToken} The Esprima version of the token.
*/
translate(token, extra) {
const type = token.type,
tt = this._acornTokTypes;
if (type === tt.name) {
token.type = Token.Identifier;
// TODO: See if this is an Acorn bug
if (token.value === "static") {
token.type = Token.Keyword;
}
if (extra.ecmaVersion > 5 && (token.value === "yield" || token.value === "let")) {
token.type = Token.Keyword;
}
} else if (type === tt.privateId) {
token.type = Token.PrivateIdentifier;
} else if (type === tt.semi || type === tt.comma ||
type === tt.parenL || type === tt.parenR ||
type === tt.braceL || type === tt.braceR ||
type === tt.dot || type === tt.bracketL ||
type === tt.colon || type === tt.question ||
type === tt.bracketR || type === tt.ellipsis ||
type === tt.arrow || type === tt.jsxTagStart ||
type === tt.incDec || type === tt.starstar ||
type === tt.jsxTagEnd || type === tt.prefix ||
type === tt.questionDot ||
(type.binop && !type.keyword) ||
type.isAssign) {
token.type = Token.Punctuator;
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.jsxName) {
token.type = Token.JSXIdentifier;
} else if (type.label === "jsxText" || type === tt.jsxAttrValueToken) {
token.type = Token.JSXText;
} else if (type.keyword) {
if (type.keyword === "true" || type.keyword === "false") {
token.type = Token.Boolean;
} else if (type.keyword === "null") {
token.type = Token.Null;
} else {
token.type = Token.Keyword;
}
} else if (type === tt.num) {
token.type = Token.Numeric;
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.string) {
if (extra.jsxAttrValueToken) {
extra.jsxAttrValueToken = false;
token.type = Token.JSXText;
} else {
token.type = Token.String;
}
token.value = this._code.slice(token.start, token.end);
} else if (type === tt.regexp) {
token.type = Token.RegularExpression;
const value = token.value;
token.regex = {
flags: value.flags,
pattern: value.pattern
};
token.value = `/${value.pattern}/${value.flags}`;
}
return token;
},
/**
* Function to call during Acorn's onToken handler.
* @param {AcornToken} token The Acorn token.
* @param {Object} extra The Espree extra object.
* @returns {void}
*/
onToken(token, extra) {
const tt = this._acornTokTypes,
tokens = extra.tokens,
templateTokens = this._tokens;
/**
* Flushes the buffered template tokens and resets the template
* tracking.
* @returns {void}
* @private
*/
const translateTemplateTokens = () => {
tokens.push(convertTemplatePart(this._tokens, this._code));
this._tokens = [];
};
if (token.type === tt.eof) {
// might be one last curlyBrace
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
}
return;
}
if (token.type === tt.backQuote) {
// if there's already a curly, it's not part of the template
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
this._curlyBrace = null;
}
templateTokens.push(token);
// it's the end
if (templateTokens.length > 1) {
translateTemplateTokens();
}
return;
}
if (token.type === tt.dollarBraceL) {
templateTokens.push(token);
translateTemplateTokens();
return;
}
if (token.type === tt.braceR) {
// if there's already a curly, it's not part of the template
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
}
// store new curly for later
this._curlyBrace = token;
return;
}
if (token.type === tt.template || token.type === tt.invalidTemplate) {
if (this._curlyBrace) {
templateTokens.push(this._curlyBrace);
this._curlyBrace = null;
}
templateTokens.push(token);
return;
}
if (this._curlyBrace) {
tokens.push(this.translate(this._curlyBrace, extra));
this._curlyBrace = null;
}
tokens.push(this.translate(token, extra));
}
};
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
export default TokenTranslator;

View File

@@ -0,0 +1,3 @@
const version = "9.6.1";
export default version;

View File

@@ -0,0 +1,88 @@
{
"name": "espree",
"description": "An Esprima-compatible JavaScript parser built on Acorn",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
"homepage": "https://github.com/eslint/espree",
"main": "dist/espree.cjs",
"type": "module",
"exports": {
".": [
{
"import": "./espree.js",
"require": "./dist/espree.cjs",
"default": "./dist/espree.cjs"
},
"./dist/espree.cjs"
],
"./package.json": "./package.json"
},
"version": "9.6.1",
"files": [
"lib",
"dist/espree.cjs",
"espree.js"
],
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"repository": "eslint/espree",
"bugs": {
"url": "https://github.com/eslint/espree/issues"
},
"funding": "https://opencollective.com/eslint",
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.4.1"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.2.0",
"c8": "^7.11.0",
"chai": "^4.3.6",
"eslint": "^8.44.0",
"eslint-config-eslint": "^8.0.0",
"eslint-plugin-n": "^16.0.0",
"eslint-release": "^3.2.0",
"esprima-fb": "^8001.2001.0-dev-harmony-fb",
"globals": "^13.20.0",
"lint-staged": "^13.2.0",
"mocha": "^9.2.2",
"npm-run-all": "^4.1.5",
"rollup": "^2.41.2",
"shelljs": "^0.3.0",
"yorkie": "^2.0.0"
},
"keywords": [
"ast",
"ecmascript",
"javascript",
"parser",
"syntax",
"acorn"
],
"gitHooks": {
"pre-commit": "lint-staged"
},
"scripts": {
"unit": "npm-run-all -s unit:*",
"unit:esm": "c8 mocha --color --reporter progress --timeout 30000 'tests/lib/**/*.js'",
"unit:cjs": "mocha --color --reporter progress --timeout 30000 tests/lib/commonjs.cjs",
"test": "npm-run-all -p unit lint",
"lint": "eslint . --report-unused-disable-directives",
"fixlint": "npm run lint -- --fix",
"build": "rollup -c rollup.config.js",
"build:debug": "npm run build -- -m",
"update-version": "node tools/update-version.js",
"pretest": "npm run build",
"prepublishOnly": "npm run update-version && npm run build",
"sync-docs": "node sync-docs.js",
"generate-release": "eslint-generate-release",
"generate-alpharelease": "eslint-generate-prerelease alpha",
"generate-betarelease": "eslint-generate-prerelease beta",
"generate-rcrelease": "eslint-generate-prerelease rc",
"publish-release": "eslint-publish-release"
}
}

103
node_modules/vue-eslint-parser/package.json generated vendored Normal file
View File

@@ -0,0 +1,103 @@
{
"name": "vue-eslint-parser",
"version": "9.4.3",
"description": "The ESLint custom parser for `.vue` files.",
"engines": {
"node": "^14.17.0 || >=16.0.0"
},
"main": "index.js",
"files": [
"index.*"
],
"peerDependencies": {
"eslint": ">=6.0.0"
},
"dependencies": {
"debug": "^4.3.4",
"eslint-scope": "^7.1.1",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.1",
"esquery": "^1.4.0",
"lodash": "^4.17.21",
"semver": "^7.3.6"
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/eslint-parser": "^7.16.3",
"@babel/plugin-syntax-decorators": "^7.16.0",
"@babel/plugin-syntax-pipeline-operator": "^7.16.0",
"@babel/plugin-syntax-typescript": "^7.16.0",
"@types/debug": "^4.1.7",
"@types/eslint": "^8.4.6",
"@types/estree": "^1.0.0",
"@types/lodash": "^4.14.186",
"@types/mocha": "^9.0.0",
"@types/node": "^18.8.4",
"@types/semver": "^7.3.12",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"chokidar": "^3.5.2",
"codecov": "^3.8.3",
"cross-spawn": "^7.0.3",
"dts-bundle": "^0.7.3",
"eslint": "^8.12.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-jsonc": "^2.2.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-node-dependencies": "^0.8.0",
"eslint-plugin-prettier": "^4.0.0",
"fs-extra": "^10.0.0",
"jsonc-eslint-parser": "^2.0.3",
"mocha": "^9.1.3",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"opener": "^1.5.2",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"rollup": "^2.60.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-sourcemaps": "^0.6.3",
"ts-node": "^10.4.0",
"typescript": "~4.8.4",
"wait-on": "^6.0.0",
"warun": "^1.0.0"
},
"scripts": {
"prebuild": "npm run -s clean",
"build": "tsc --module es2015 && rollup -c -o index.js && dts-bundle --name vue-eslint-parser --main .temp/index.d.ts --out ../index.d.ts",
"clean": "rimraf .nyc_output .temp coverage index.*",
"codecov": "codecov",
"coverage": "opener ./coverage/lcov-report/index.html",
"lint": "eslint src test package.json --ext .js,.ts",
"setup": "git submodule update --init && cd test/fixtures/eslint && npm install",
"pretest": "run-s build lint",
"test": "npm run -s test:mocha",
"test:mocha": "mocha --require ts-node/register \"test/*.js\" --reporter dot --timeout 60000",
"test:cover": "nyc mocha \"test/*.js\" --reporter dot --timeout 60000",
"test:debug": "mocha --require ts-node/register/transpile-only \"test/*.js\" --reporter dot --timeout 60000",
"update-fixtures": "ts-node --transpile-only scripts/update-fixtures-ast.js && ts-node --transpile-only scripts/update-fixtures-document-fragment.js",
"preversion": "npm test",
"version": "npm run -s build",
"postversion": "git push && git push --tags",
"prewatch": "npm run -s clean",
"watch": "run-p watch:*",
"watch:tsc": "tsc --module es2015 --watch",
"watch:rollup": "wait-on .temp/index.js && rollup -c -o index.js --watch",
"watch:test": "wait-on index.js && warun index.js \"test/*.js\" \"test/fixtures/ast/*/*.json\" \"test/fixtures/*\" --debounce 1000 --no-initial -- nyc mocha \"test/*.js\" --reporter dot --timeout 10000",
"watch:update-ast": "wait-on index.js && warun index.js \"test/fixtures/ast/*/*.vue\" -- node scripts/update-fixtures-ast.js",
"watch:coverage-report": "wait-on coverage/lcov-report/index.html && opener coverage/lcov-report/index.html"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vue-eslint-parser.git"
},
"keywords": [],
"author": "Toru Nagashima",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/vue-eslint-parser/issues"
},
"homepage": "https://github.com/vuejs/vue-eslint-parser#readme",
"funding": "https://github.com/sponsors/mysticatea"
}