astro-eslint-parser 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Yosuke Ota
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # astro-eslint-parser
2
+
3
+ [Astro] parser for [ESLint].
4
+ You can check it on [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/playground).
5
+
6
+ [![NPM license](https://img.shields.io/npm/l/astro-eslint-parser.svg)](https://www.npmjs.com/package/astro-eslint-parser)
7
+ [![NPM version](https://img.shields.io/npm/v/astro-eslint-parser.svg)](https://www.npmjs.com/package/astro-eslint-parser)
8
+ [![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/astro-eslint-parser&maxAge=3600)](http://www.npmtrends.com/astro-eslint-parser)
9
+ [![NPM downloads](https://img.shields.io/npm/dw/astro-eslint-parser.svg)](http://www.npmtrends.com/astro-eslint-parser)
10
+ [![NPM downloads](https://img.shields.io/npm/dm/astro-eslint-parser.svg)](http://www.npmtrends.com/astro-eslint-parser)
11
+ [![NPM downloads](https://img.shields.io/npm/dy/astro-eslint-parser.svg)](http://www.npmtrends.com/astro-eslint-parser)
12
+ [![NPM downloads](https://img.shields.io/npm/dt/astro-eslint-parser.svg)](http://www.npmtrends.com/astro-eslint-parser)
13
+ [![Build Status](https://github.com/ota-meshi/astro-eslint-parser/workflows/CI/badge.svg?branch=main)](https://github.com/ota-meshi/astro-eslint-parser/actions?query=workflow%3ACI)
14
+
15
+ This parser is in the ***experimental stages*** of development.
16
+
17
+ <!--
18
+ ### ESLint Plugins Using astro-eslint-parser
19
+
20
+ #### [@ota-meshi/eslint-plugin-astro](https://ota-meshi.github.io/eslint-plugin-astro/)
21
+
22
+ ESLint plugin for Astro.
23
+ It provides many unique check rules by using the template AST.
24
+ -->
25
+
26
+ ## 💿 Installation
27
+
28
+ ```bash
29
+ npm install --save-dev eslint astro-eslint-parser
30
+ ```
31
+
32
+ ## 📖 Usage
33
+
34
+ 1. Write `overrides.parser` option into your `.eslintrc.*` file.
35
+ 2. Use glob patterns or `--ext .astro` CLI option.
36
+
37
+ ```json
38
+ {
39
+ "extends": "eslint:recommended",
40
+ "overrides": [
41
+ {
42
+ "files": ["*.astro"],
43
+ "parser": "astro-eslint-parser"
44
+ }
45
+ ]
46
+ }
47
+ ```
48
+
49
+ ```console
50
+ $ eslint "src/**/*.{js,astro}"
51
+ # or
52
+ $ eslint src --ext .astro
53
+ ```
54
+
55
+ ## 🔧 Options
56
+
57
+ `parserOptions` has the same properties as what [espree](https://github.com/eslint/espree#usage), the default parser of ESLint, is supporting.
58
+ For example:
59
+
60
+ ```json
61
+ {
62
+ "parser": "astro-eslint-parser",
63
+ "parserOptions": {
64
+ "sourceType": "module",
65
+ "ecmaVersion": 2021,
66
+ "ecmaFeatures": {
67
+ "globalReturn": false,
68
+ "impliedStrict": false,
69
+ "jsx": false
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### parserOptions.parser
76
+
77
+ You can use `parserOptions.parser` property to specify a custom parser to parse `<script>` tags.
78
+ Other properties than parser would be given to the specified parser.
79
+ For example:
80
+
81
+ ```json
82
+ {
83
+ "parser": "astro-eslint-parser",
84
+ "parserOptions": {
85
+ "parser": "@typescript-eslint/parser"
86
+ }
87
+ }
88
+ ```
89
+
90
+ For example, if you are using the `"@typescript-eslint/parser"`, and if you want to use TypeScript in `<script>` of `.astro`, you need to add more `parserOptions` configuration.
91
+
92
+ ```js
93
+ module.exports = {
94
+ // ...
95
+ parser: "@typescript-eslint/parser",
96
+ parserOptions: {
97
+ // ...
98
+ project: "path/to/your/tsconfig.json",
99
+ extraFileExtensions: [".astro"], // This is a required setting in `@typescript-eslint/parser` v4.24.0.
100
+ },
101
+ overrides: [
102
+ {
103
+ files: ["*.astro"],
104
+ parser: "astro-eslint-parser",
105
+ // Parse the `<script>` in `.astro` as TypeScript by adding the following configuration.
106
+ parserOptions: {
107
+ parser: "@typescript-eslint/parser",
108
+ },
109
+ },
110
+ // ...
111
+ ],
112
+ // ...
113
+ }
114
+ ```
115
+
116
+ ## :computer: Editor Integrations
117
+
118
+ ### Visual Studio Code
119
+
120
+ Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
121
+
122
+ You have to configure the `eslint.validate` option of the extension to check `.astro` files, because the extension targets only `*.js` or `*.jsx` files by default.
123
+
124
+ Example **.vscode/settings.json**:
125
+
126
+ ```json
127
+ {
128
+ "eslint.validate": [
129
+ "javascript",
130
+ "javascriptreact",
131
+ "astro"
132
+ ]
133
+ }
134
+ ```
135
+
136
+ ## Usage for Custom Rules / Plugins
137
+
138
+ - TBD
139
+
140
+ <!-- - [AST.md](./docs/AST.md) is AST specification. You can check it on the [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/). -->
141
+ <!-- - I have already [implemented some rules] in the [`@ota-meshi/eslint-plugin-astro`]. The source code for these rules will be helpful to you. -->
142
+
143
+ ## :beers: Contributing
144
+
145
+ Welcome contributing!
146
+
147
+ Please use GitHub's Issues/PRs.
148
+
149
+ ## :lock: License
150
+
151
+ See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
152
+
153
+ [Astro]: https://astro.build/
154
+ [ESLint]: https://eslint.org/
package/lib/ast.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import type { TSESTree } from "@typescript-eslint/types";
2
+ export declare type AstroNode = AstroProgram | AstroRootFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute;
3
+ /** Node of Astro program root */
4
+ export interface AstroProgram extends Omit<TSESTree.Program, "type" | "body"> {
5
+ type: "Program";
6
+ body: (TSESTree.Program["body"][number] | AstroRootFragment | AstroHTMLComment)[];
7
+ sourceType: "script" | "module";
8
+ comments: TSESTree.Comment[];
9
+ tokens: TSESTree.Token[];
10
+ parent: undefined;
11
+ }
12
+ /** Node of Astro fragment root */
13
+ export interface AstroRootFragment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
14
+ type: "AstroRootFragment";
15
+ children: TSESTree.JSXFragment["children"];
16
+ parent: AstroProgram;
17
+ }
18
+ /** Node of Astro html comment */
19
+ export interface AstroHTMLComment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
20
+ type: "AstroHTMLComment";
21
+ value: string;
22
+ parent: AstroRootFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
23
+ }
24
+ /** Node of Astro doctype */
25
+ export interface AstroDoctype extends Omit<TSESTree.BaseNode, "type" | "parent"> {
26
+ type: "AstroDoctype";
27
+ parent: AstroRootFragment;
28
+ }
29
+ /** Node of Astro shorthand attribute */
30
+ export interface AstroShorthandAttribute extends Omit<TSESTree.JSXAttribute, "type" | "parent"> {
31
+ type: "AstroShorthandAttribute";
32
+ value: TSESTree.JSXExpressionContainer;
33
+ parent: TSESTree.JSXElement | TSESTree.JSXFragment;
34
+ }
35
+ /** Node of Astro template-literal attribute */
36
+ export interface AstroTemplateLiteralAttribute extends Omit<TSESTree.JSXAttribute, "type" | "parent"> {
37
+ type: "AstroTemplateLiteralAttribute";
38
+ value: TSESTree.JSXExpressionContainer & {
39
+ expression: TSESTree.TemplateLiteral;
40
+ };
41
+ parent: TSESTree.JSXElement | TSESTree.JSXFragment;
42
+ }
package/lib/ast.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,33 @@
1
+ import type { AttributeNode, CommentNode, Node, ParentNode, TagLikeNode } from "@astrojs/compiler/types";
2
+ /**
3
+ * Checks if the given node is TagLikeNode
4
+ */
5
+ export declare function isTag(node: Node): node is Node & TagLikeNode;
6
+ /**
7
+ * Checks if the given node is ParentNode
8
+ */
9
+ export declare function isParent(node: Node): node is ParentNode;
10
+ /** walk element nodes */
11
+ export declare function walkElements(parent: ParentNode, cb: (n: Node, parent: ParentNode) => void): void;
12
+ /** walk nodes */
13
+ export declare function walk(parent: ParentNode, enter: (n: Node | AttributeNode, parent: ParentNode) => void, leave?: (n: Node | AttributeNode, parent: ParentNode) => void): void;
14
+ /**
15
+ * Get end offset of start tag
16
+ */
17
+ export declare function getStartTagEndOffset(node: TagLikeNode, code: string): number;
18
+ /**
19
+ * Get end offset of attribute
20
+ */
21
+ export declare function getAttributeEndOffset(node: AttributeNode, code: string): number;
22
+ /**
23
+ * Get start offset of attribute value
24
+ */
25
+ export declare function getAttributeValueStartOffset(node: AttributeNode, code: string): number;
26
+ /**
27
+ * Get end offset of comment
28
+ */
29
+ export declare function getCommentEndOffset(node: CommentNode, code: string): number;
30
+ /**
31
+ * Skip spaces
32
+ */
33
+ export declare function skipSpaces(string: string, position: number): number;
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.skipSpaces = exports.getCommentEndOffset = exports.getAttributeValueStartOffset = exports.getAttributeEndOffset = exports.getStartTagEndOffset = exports.walk = exports.walkElements = exports.isParent = exports.isTag = void 0;
4
+ /**
5
+ * Checks if the given node is TagLikeNode
6
+ */
7
+ function isTag(node) {
8
+ return (node.type === "element" ||
9
+ node.type === "custom-element" ||
10
+ node.type === "component" ||
11
+ node.type === "fragment");
12
+ }
13
+ exports.isTag = isTag;
14
+ /**
15
+ * Checks if the given node is ParentNode
16
+ */
17
+ function isParent(node) {
18
+ return Array.isArray(node.children);
19
+ }
20
+ exports.isParent = isParent;
21
+ /** walk element nodes */
22
+ function walkElements(parent, cb) {
23
+ let children = parent.children;
24
+ if (parent.type === "root" && children.every((n) => n.position)) {
25
+ // The order of comments and frontmatter may be changed.
26
+ children = [...children].sort((a, b) => a.position.start.offset - b.position.start.offset);
27
+ }
28
+ for (const node of children) {
29
+ cb(node, parent);
30
+ if (isParent(node)) {
31
+ walkElements(node, cb);
32
+ }
33
+ }
34
+ }
35
+ exports.walkElements = walkElements;
36
+ /** walk nodes */
37
+ function walk(parent, enter, leave) {
38
+ let children = parent.children;
39
+ if (parent.type === "root" && children.every((n) => n.position)) {
40
+ // The order of comments and frontmatter may be changed.
41
+ children = [...children].sort((a, b) => a.position.start.offset - b.position.start.offset);
42
+ }
43
+ for (const node of children) {
44
+ enter(node, parent);
45
+ if (isTag(node)) {
46
+ for (const attr of node.attributes) {
47
+ enter(attr, node);
48
+ leave === null || leave === void 0 ? void 0 : leave(attr, node);
49
+ }
50
+ }
51
+ if (isParent(node)) {
52
+ walk(node, enter, leave);
53
+ }
54
+ leave === null || leave === void 0 ? void 0 : leave(node, parent);
55
+ }
56
+ }
57
+ exports.walk = walk;
58
+ /**
59
+ * Get end offset of start tag
60
+ */
61
+ function getStartTagEndOffset(node, code) {
62
+ const lastAttr = node.attributes[node.attributes.length - 1];
63
+ let beforeCloseIndex;
64
+ if (lastAttr) {
65
+ beforeCloseIndex = getAttributeEndOffset(lastAttr, code);
66
+ }
67
+ else {
68
+ const info = getTokenInfo(code, [`<${node.name}`], node.position.start.offset);
69
+ beforeCloseIndex = info.index + info.match.length;
70
+ }
71
+ const info = getTokenInfo(code, [[">", "/>"]], beforeCloseIndex);
72
+ return info.index + info.match.length;
73
+ }
74
+ exports.getStartTagEndOffset = getStartTagEndOffset;
75
+ /**
76
+ * Get end offset of attribute
77
+ */
78
+ function getAttributeEndOffset(node, code) {
79
+ let info;
80
+ if (node.kind === "empty") {
81
+ info = getTokenInfo(code, [node.name], node.position.start.offset);
82
+ }
83
+ else if (node.kind === "quoted") {
84
+ info = getTokenInfo(code, [[`"${node.value}"`, `'${node.value}'`, node.value]], getAttributeValueStartOffset(node, code));
85
+ }
86
+ else if (node.kind === "expression") {
87
+ info = getTokenInfo(code, ["{", node.value, "}"], getAttributeValueStartOffset(node, code));
88
+ }
89
+ else if (node.kind === "shorthand") {
90
+ info = getTokenInfo(code, ["{", node.name, "}"], node.position.start.offset);
91
+ }
92
+ else if (node.kind === "spread") {
93
+ info = getTokenInfo(code, ["{", "...", node.name, "}"], node.position.start.offset);
94
+ }
95
+ else if (node.kind === "template-literal") {
96
+ info = getTokenInfo(code, [`\`${node.value}\``], getAttributeValueStartOffset(node, code));
97
+ }
98
+ else {
99
+ throw new Error(`Unknown attr kind: ${node.kind}`);
100
+ }
101
+ return info.index + info.match.length;
102
+ }
103
+ exports.getAttributeEndOffset = getAttributeEndOffset;
104
+ /**
105
+ * Get start offset of attribute value
106
+ */
107
+ function getAttributeValueStartOffset(node, code) {
108
+ let info;
109
+ if (node.kind === "quoted") {
110
+ info = getTokenInfo(code, [node.name, "=", [`"`, `'`, node.value]], node.position.start.offset);
111
+ }
112
+ else if (node.kind === "expression") {
113
+ info = getTokenInfo(code, [node.name, "=", "{"], node.position.start.offset);
114
+ }
115
+ else if (node.kind === "template-literal") {
116
+ info = getTokenInfo(code, [node.name, "=", "`"], node.position.start.offset);
117
+ }
118
+ else {
119
+ throw new Error(`Unknown attr kind: ${node.kind}`);
120
+ }
121
+ return info.index;
122
+ }
123
+ exports.getAttributeValueStartOffset = getAttributeValueStartOffset;
124
+ /**
125
+ * Get end offset of comment
126
+ */
127
+ function getCommentEndOffset(node, code) {
128
+ const info = getTokenInfo(code, ["<!--", node.value, "-->"], node.position.start.offset);
129
+ return info.index + info.match.length;
130
+ }
131
+ exports.getCommentEndOffset = getCommentEndOffset;
132
+ /**
133
+ * Get token info
134
+ */
135
+ function getTokenInfo(string, tokens, position) {
136
+ let lastMatch;
137
+ for (const t of tokens) {
138
+ const index = lastMatch
139
+ ? lastMatch.index + lastMatch.match.length
140
+ : position;
141
+ const m = typeof t === "string"
142
+ ? matchOfStr(t, index)
143
+ : matchOfForMulti(t, index);
144
+ if (m == null) {
145
+ throw new Error(`Unknown token at ${index}, expected: ${JSON.stringify(t)}, actual: ${JSON.stringify(string.slice(index, index + 10))}`);
146
+ }
147
+ lastMatch = m;
148
+ }
149
+ return lastMatch;
150
+ /**
151
+ * For string
152
+ */
153
+ function matchOfStr(search, position) {
154
+ const index = search.trim() === search ? skipSpaces(string, position) : position;
155
+ if (string.startsWith(search, index)) {
156
+ return {
157
+ match: search,
158
+ index,
159
+ };
160
+ }
161
+ return null;
162
+ }
163
+ /**
164
+ * For multi
165
+ */
166
+ function matchOfForMulti(search, position) {
167
+ for (const s of search) {
168
+ const m = matchOfStr(s, position);
169
+ if (m) {
170
+ return m;
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ }
176
+ /**
177
+ * Skip spaces
178
+ */
179
+ function skipSpaces(string, position) {
180
+ const re = /\s*/g;
181
+ re.lastIndex = position;
182
+ const match = re.exec(string);
183
+ if (match) {
184
+ return match.index + match[0].length;
185
+ }
186
+ return position;
187
+ }
188
+ exports.skipSpaces = skipSpaces;
@@ -0,0 +1,48 @@
1
+ import type { TSESTree } from "@typescript-eslint/types";
2
+ import type { ESLintExtendedProgram } from "../parser";
3
+ declare type RangeAndLoc = {
4
+ range: TSESTree.Range;
5
+ loc: TSESTree.SourceLocation;
6
+ };
7
+ export declare class Context {
8
+ readonly code: string;
9
+ readonly parserOptions: any;
10
+ readonly locs: LinesAndColumns;
11
+ private readonly locsMap;
12
+ private state;
13
+ constructor(code: string, parserOptions: any);
14
+ getLocFromIndex(index: number): {
15
+ line: number;
16
+ column: number;
17
+ };
18
+ /**
19
+ * Get the location information of the given indexes.
20
+ */
21
+ getLocations(start: number, end: number): RangeAndLoc;
22
+ /**
23
+ * Build token
24
+ */
25
+ buildToken(type: TSESTree.Token["type"], range: TSESTree.Range): TSESTree.Token;
26
+ /**
27
+ * get text
28
+ */
29
+ getText(range: TSESTree.Range): string;
30
+ isTypeScript(): boolean;
31
+ remapCR({ ast, visitorKeys }: ESLintExtendedProgram): void;
32
+ }
33
+ export declare class LinesAndColumns {
34
+ readonly code: string;
35
+ private readonly crs;
36
+ private readonly lineStartIndices;
37
+ constructor(origCode: string);
38
+ getLocFromIndex(index: number): {
39
+ line: number;
40
+ column: number;
41
+ };
42
+ getIndexFromLoc(loc: {
43
+ line: number;
44
+ column: number;
45
+ }): number;
46
+ getCRs(): number[];
47
+ }
48
+ export {};
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LinesAndColumns = exports.Context = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const resolve_parser_1 = require("../parser/resolve-parser");
10
+ const traverse_1 = require("../traverse");
11
+ class Context {
12
+ constructor(code, parserOptions) {
13
+ this.locsMap = new Map();
14
+ this.state = {};
15
+ this.locs = new LinesAndColumns(code);
16
+ this.code = this.locs.code;
17
+ this.parserOptions = parserOptions;
18
+ }
19
+ getLocFromIndex(index) {
20
+ let loc = this.locsMap.get(index);
21
+ if (!loc) {
22
+ loc = this.locs.getLocFromIndex(index);
23
+ this.locsMap.set(index, loc);
24
+ }
25
+ return {
26
+ line: loc.line,
27
+ column: loc.column,
28
+ };
29
+ }
30
+ /**
31
+ * Get the location information of the given indexes.
32
+ */
33
+ getLocations(start, end) {
34
+ return {
35
+ range: [start, end],
36
+ loc: {
37
+ start: this.getLocFromIndex(start),
38
+ end: this.getLocFromIndex(end),
39
+ },
40
+ };
41
+ }
42
+ /**
43
+ * Build token
44
+ */
45
+ buildToken(type, range) {
46
+ return Object.assign({ type, value: this.getText(range) }, this.getLocations(...range));
47
+ }
48
+ /**
49
+ * get text
50
+ */
51
+ getText(range) {
52
+ return this.code.slice(range[0], range[1]);
53
+ }
54
+ isTypeScript() {
55
+ var _a, _b;
56
+ if (this.state.isTypeScript != null) {
57
+ return this.state.isTypeScript;
58
+ }
59
+ const parserName = (0, resolve_parser_1.getParserName)({}, (_a = this.parserOptions) === null || _a === void 0 ? void 0 : _a.parser);
60
+ if (parserName === "@typescript-eslint/parser") {
61
+ return (this.state.isTypeScript = true);
62
+ }
63
+ if (parserName.includes("@typescript-eslint/parser")) {
64
+ let targetPath = parserName;
65
+ while (targetPath) {
66
+ const pkgPath = path_1.default.join(targetPath, "package.json");
67
+ if (fs_1.default.existsSync(pkgPath)) {
68
+ try {
69
+ return (this.state.isTypeScript =
70
+ ((_b = JSON.parse(fs_1.default.readFileSync(pkgPath, "utf-8"))) === null || _b === void 0 ? void 0 : _b.name) === "@typescript-eslint/parser");
71
+ }
72
+ catch (_c) {
73
+ return (this.state.isTypeScript = false);
74
+ }
75
+ }
76
+ const parent = path_1.default.dirname(targetPath);
77
+ if (targetPath === parent) {
78
+ break;
79
+ }
80
+ targetPath = parent;
81
+ }
82
+ }
83
+ return (this.state.isTypeScript = false);
84
+ }
85
+ remapCR({ ast, visitorKeys }) {
86
+ const crs = this.locs.getCRs();
87
+ if (!crs.length) {
88
+ return;
89
+ }
90
+ const cache = {};
91
+ /**
92
+ * Remap index
93
+ */
94
+ function remapIndex(index) {
95
+ let result = cache[index];
96
+ if (result != null) {
97
+ return result;
98
+ }
99
+ result = index;
100
+ for (const cr of crs) {
101
+ if (cr < result) {
102
+ result++;
103
+ }
104
+ else {
105
+ break;
106
+ }
107
+ }
108
+ return (cache[index] = result);
109
+ }
110
+ /**
111
+ * Remap range
112
+ */
113
+ function remapRange(range) {
114
+ return [remapIndex(range[0]), remapIndex(range[1])];
115
+ }
116
+ (0, traverse_1.traverseNodes)(ast, {
117
+ visitorKeys,
118
+ enterNode(node) {
119
+ node.range = remapRange(node.range);
120
+ },
121
+ leaveNode() {
122
+ // ignore
123
+ },
124
+ });
125
+ for (const token of ast.tokens || []) {
126
+ token.range = remapRange(token.range);
127
+ }
128
+ for (const comment of ast.comments || []) {
129
+ comment.range = remapRange(comment.range);
130
+ }
131
+ }
132
+ }
133
+ exports.Context = Context;
134
+ class LinesAndColumns {
135
+ constructor(origCode) {
136
+ const len = origCode.length;
137
+ const lineStartIndices = [0];
138
+ const crs = [];
139
+ let code = "";
140
+ for (let index = 0; index < len;) {
141
+ const c = origCode[index++];
142
+ if (c === "\r") {
143
+ const next = origCode[index++] || "";
144
+ if (next === "\n") {
145
+ code += next;
146
+ crs.push(index - 2);
147
+ }
148
+ else {
149
+ code += `\n${next}`;
150
+ }
151
+ lineStartIndices.push(code.length);
152
+ }
153
+ else {
154
+ code += c;
155
+ if (c === "\n") {
156
+ lineStartIndices.push(code.length);
157
+ }
158
+ }
159
+ }
160
+ this.lineStartIndices = lineStartIndices;
161
+ this.code = code;
162
+ this.crs = crs;
163
+ }
164
+ getLocFromIndex(index) {
165
+ const lineNumber = sortedLastIndex(this.lineStartIndices, index);
166
+ return {
167
+ line: lineNumber,
168
+ column: index - this.lineStartIndices[lineNumber - 1],
169
+ };
170
+ }
171
+ getIndexFromLoc(loc) {
172
+ const lineStartIndex = this.lineStartIndices[loc.line - 1];
173
+ const positionIndex = lineStartIndex + loc.column;
174
+ return positionIndex;
175
+ }
176
+ getCRs() {
177
+ return this.crs;
178
+ }
179
+ }
180
+ exports.LinesAndColumns = LinesAndColumns;
181
+ /**
182
+ * Uses a binary search to determine the highest index at which value should be inserted into array in order to maintain its sort order.
183
+ */
184
+ function sortedLastIndex(array, value) {
185
+ let lower = 0;
186
+ let upper = array.length;
187
+ while (lower < upper) {
188
+ const mid = Math.floor(lower + (upper - lower) / 2);
189
+ const target = array[mid];
190
+ if (target < value) {
191
+ lower = mid + 1;
192
+ }
193
+ else if (target > value) {
194
+ upper = mid;
195
+ }
196
+ else {
197
+ return mid + 1;
198
+ }
199
+ }
200
+ return upper;
201
+ }