@angular-eslint/template-parser 15.2.2-alpha.9 → 16.0.0-alpha.1

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.
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertElementSourceSpanToLoc = exports.convertNodeSourceSpanToLoc = void 0;
4
+ const bundled_angular_compiler_1 = require("@angular-eslint/bundled-angular-compiler");
5
+ function convertNodeSourceSpanToLoc(sourceSpan) {
6
+ return {
7
+ start: {
8
+ line: sourceSpan.start.line + 1,
9
+ column: sourceSpan.start.col,
10
+ },
11
+ end: {
12
+ line: sourceSpan.end.line + 1,
13
+ column: sourceSpan.end.col,
14
+ },
15
+ };
16
+ }
17
+ exports.convertNodeSourceSpanToLoc = convertNodeSourceSpanToLoc;
18
+ function convertElementSourceSpanToLoc(context, node) {
19
+ if (node.type !== 'Element$1') {
20
+ // We explicitly throw an exception since this function should not be used
21
+ // with non-element nodes, e.g. `TextAttribute` or `MethodDefinition`, etc.
22
+ throw new Error('convertElementSourceSpanToLoc is intended to be used only with elements.');
23
+ }
24
+ // Void elements are "self-closed" elements, e.g. `<img />` or `<area />`.
25
+ // The Angular compiler explicitly doesn't set the end source span for void
26
+ // elements, but we still have to find its location to be able to report failures.
27
+ if ((0, bundled_angular_compiler_1.getHtmlTagDefinition)(node.name).isVoid) {
28
+ // Fallback to the original `node` if the
29
+ // `tryToFindTheVoidNodeThatMatchesTheSourceSpan` returns nothing.
30
+ node = (tryToFindTheVoidNodeThatMatchesTheSourceSpan(context, node) ||
31
+ node);
32
+ }
33
+ return convertNodeSourceSpanToLoc(node.sourceSpan);
34
+ }
35
+ exports.convertElementSourceSpanToLoc = convertElementSourceSpanToLoc;
36
+ function tryToFindTheVoidNodeThatMatchesTheSourceSpan(context, node) {
37
+ // Previously, `codelyzer` used `TemplateParser` to parse a template into an AST tree.
38
+ // The `TemplateParser` used `HtmlParser`, because `HtmlParser` still sets the end span
39
+ // for void elements.
40
+ const { rootNodes } = getHtmlParser().parse(context.getSourceCode().getText(), context.getFilename());
41
+ return lookupTheVoidNode(rootNodes, node.sourceSpan);
42
+ }
43
+ function lookupTheVoidNode(rootNodes, sourceSpan) {
44
+ for (const node of rootNodes) {
45
+ if (
46
+ // We can't compare by `node.sourceSpan == sourceSpan` since references
47
+ // will differ. But comparing `line` and` offset` is the
48
+ // correct way, because they will not differ.
49
+ node.sourceSpan.start.line === sourceSpan.start.line &&
50
+ node.sourceSpan.start.offset === sourceSpan.start.offset) {
51
+ return node;
52
+ }
53
+ // `HtmlParser` will return a list of root nodes, these nodes
54
+ // can be either text or elements. Elements might have child elements.
55
+ if (node instanceof bundled_angular_compiler_1.Element) {
56
+ const voidNodeBeingLookedUp = lookupTheVoidNode(node.children, sourceSpan);
57
+ if (voidNodeBeingLookedUp !== null) {
58
+ return voidNodeBeingLookedUp;
59
+ }
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ let htmlParser = null;
65
+ // Initialize the `HtmlParser` class lazily only when the function is
66
+ // invoked for the first time.
67
+ function getHtmlParser() {
68
+ return htmlParser || (htmlParser = new bundled_angular_compiler_1.HtmlParser());
69
+ }
package/dist/index.js CHANGED
@@ -1 +1,215 @@
1
- var e=require("@angular-eslint/bundled-angular-compiler"),t=require("eslint-scope");function n(e){return{start:{line:e.start.line+1,column:e.start.col},end:{line:e.end.line+1,column:e.end.col}}}function r(t,r){if("Element$1"!==r.type)throw new Error("convertElementSourceSpanToLoc is intended to be used only with elements.");return e.getHtmlTagDefinition(r.name).isVoid&&(r=function(t,n){const{rootNodes:r}=(s||(s=new e.HtmlParser)).parse(t.getSourceCode().getText(),t.getFilename());return o(r,n.sourceSpan)}(t,r)||r),n(r.sourceSpan)}function o(t,n){for(const r of t){if(r.sourceSpan.start.line===n.start.line&&r.sourceSpan.start.offset===n.start.offset)return r;if(r instanceof e.Element){const e=o(r.children,n);if(null!==e)return e}}return null}let s=null;const a={ASTWithSource:["ast"],Binary:["left","right"],BoundAttribute:["value"],BoundEvent:["handler"],BoundText:["value"],Conditional:["condition","trueExp","falseExp"],Element$1:["children","inputs","outputs","attributes"],Interpolation:["expressions"],PrefixNot:["expression"],Program:["templateNodes"],PropertyRead:["receiver"],Template:["templateAttrs","children","inputs"],BindingPipe:["exp"]};function l(e){let t=null;return"comments"!==e&&"leadingComments"!==e&&"loc"!==e&&"parent"!==e&&"range"!==e&&"tokens"!==e&&"trailingComments"!==e&&null!==(t=this[e])&&"object"==typeof t&&("string"==typeof t.type||Array.isArray(t))}function i(e){return null!==e&&"object"==typeof e&&"string"==typeof e.type}function c(e){let t=0,r=0;const o=a[e.type]||function(e){return Object.keys(e).filter(l,e)}(e);for(!e.loc&&e.sourceSpan&&(e.loc=n(e.sourceSpan)),t=0;t<o.length;++t){const n=e[o[t]],s=Array.isArray(n);if(void 0!==n.type&&(n.__originalType=n.type),s||n.type||(n.type=n.constructor.name),s)for(r=0;r<n.length;++r){const e=n[r];void 0!==e.type&&(e.__originalType=e.type),e.type&&"number"!=typeof e.type||(e.type=e.constructor.name),i(e)&&c(e)}else i(n)&&c(n)}}class u extends Error{constructor(e,t,n,r,o){super(e),this.fileName=void 0,this.index=void 0,this.lineNumber=void 0,this.column=void 0,this.fileName=t,this.index=n,this.lineNumber=r,this.column=o,Object.defineProperty(this,"name",{value:new.target.name,enumerable:!1,configurable:!0})}}function p(e){return new u(e.msg,e.span.start.file.url,e.span.start.offset,e.span.start.line+1,e.span.start.col+1)}function f(o,s){var l,i;const u=e.parseTemplate(o,s.filePath,{preserveWhitespaces:!0,preserveLineEndings:!0,collectCommentNodes:!0});let f=[];Array.isArray(u.commentNodes)&&(f=u.commentNodes);const m={type:"Program",comments:(d=f,d.map(e=>({type:"Block",value:e.value,loc:n(e.sourceSpan),range:[e.sourceSpan.start.offset,e.sourceSpan.end.offset]})).sort((e,t)=>e.range[0]-t.range[0])),tokens:[],range:[0,0],loc:{start:{line:0,column:0},end:{line:0,column:0}},templateNodes:u.nodes,value:o};var d;const y=new t.ScopeManager({});new t.Scope(y,"module",null,m,!1),c(m);const g=function(e){let t=null;return e.templateNodes.forEach(e=>{const n=e.startSourceSpan||e.sourceSpan;t?n&&n.start.offset<t.start.offset&&(t=n):t=n}),t}(m),S=function(e){let t=null;return e.templateNodes.forEach(e=>{const n=e.endSourceSpan||e.sourceSpan;t?n&&n.end.offset>t.end.offset&&(t=n):t=n}),t}(m);if(g&&S&&(m.range=[g.start.offset,S.end.offset],m.loc={start:n(g).start,end:n(S).end}),null!=(l=s.suppressParseErrors)&&!l&&null!=(i=u.errors)&&i.length)throw p(u.errors[0]);return{ast:m,scopeManager:y,visitorKeys:a,services:{convertNodeSourceSpanToLoc:n,convertElementSourceSpanToLoc:r}}}exports.TemplateParseError=u,exports.createTemplateParseError=p,exports.parse=function(e,t){return f(e,t).ast},exports.parseForESLint=f;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parse = exports.parseForESLint = exports.createTemplateParseError = exports.TemplateParseError = void 0;
4
+ const bundled_angular_compiler_1 = require("@angular-eslint/bundled-angular-compiler");
5
+ const eslint_scope_1 = require("eslint-scope");
6
+ const convert_source_span_to_loc_1 = require("./convert-source-span-to-loc");
7
+ const KEYS = {
8
+ ASTWithSource: ['ast'],
9
+ Binary: ['left', 'right'],
10
+ BoundAttribute: ['value'],
11
+ BoundEvent: ['handler'],
12
+ BoundText: ['value'],
13
+ Conditional: ['condition', 'trueExp', 'falseExp'],
14
+ Element$1: ['children', 'inputs', 'outputs', 'attributes'],
15
+ Interpolation: ['expressions'],
16
+ PrefixNot: ['expression'],
17
+ Program: ['templateNodes'],
18
+ PropertyRead: ['receiver'],
19
+ Template: ['templateAttrs', 'children', 'inputs'],
20
+ BindingPipe: ['exp'],
21
+ };
22
+ function fallbackKeysFilter(key) {
23
+ let value = null;
24
+ return (key !== 'comments' &&
25
+ key !== 'leadingComments' &&
26
+ key !== 'loc' &&
27
+ key !== 'parent' &&
28
+ key !== 'range' &&
29
+ key !== 'tokens' &&
30
+ key !== 'trailingComments' &&
31
+ (value = this[key]) !== null &&
32
+ typeof value === 'object' &&
33
+ (typeof value.type === 'string' || Array.isArray(value)));
34
+ }
35
+ function getFallbackKeys(node) {
36
+ return Object.keys(node).filter(fallbackKeysFilter, node);
37
+ }
38
+ function isNode(node) {
39
+ return (node !== null &&
40
+ typeof node === 'object' &&
41
+ typeof node.type === 'string');
42
+ }
43
+ /**
44
+ * ESLint requires all Nodes to have `type` and `loc` properties before it can
45
+ * work with the custom AST.
46
+ */
47
+ function preprocessNode(node) {
48
+ let i = 0;
49
+ let j = 0;
50
+ const keys = KEYS[node.type] || getFallbackKeys(node);
51
+ if (!node.loc && node.sourceSpan) {
52
+ node.loc = (0, convert_source_span_to_loc_1.convertNodeSourceSpanToLoc)(node.sourceSpan);
53
+ }
54
+ for (i = 0; i < keys.length; ++i) {
55
+ const child = node[keys[i]];
56
+ const isArr = Array.isArray(child);
57
+ if (child.type !== undefined) {
58
+ // Angular sometimes uses a prop called type already
59
+ child.__originalType = child.type;
60
+ }
61
+ if (!isArr && !child.type) {
62
+ child.type = child.constructor.name;
63
+ }
64
+ if (isArr) {
65
+ for (j = 0; j < child.length; ++j) {
66
+ const c = child[j];
67
+ if (c.type !== undefined) {
68
+ // Angular sometimes uses a prop called type already
69
+ c.__originalType = c.type;
70
+ }
71
+ // Pay attention to the condition `typeof c.type === number`,
72
+ // Angular compiler sets `type` property for some AST nodes,
73
+ // e.g. for the `BoundAttribute`, which is a `BindingType`.
74
+ if (!c.type || typeof c.type === 'number') {
75
+ c.type = c.constructor.name;
76
+ }
77
+ if (isNode(c)) {
78
+ preprocessNode(c);
79
+ }
80
+ }
81
+ }
82
+ else if (isNode(child)) {
83
+ preprocessNode(child);
84
+ }
85
+ }
86
+ }
87
+ function getStartSourceSpanFromAST(ast) {
88
+ let startSourceSpan = null;
89
+ ast.templateNodes.forEach((node) => {
90
+ const nodeSourceSpan = node.startSourceSpan || node.sourceSpan;
91
+ if (!startSourceSpan) {
92
+ startSourceSpan = nodeSourceSpan;
93
+ return;
94
+ }
95
+ if (nodeSourceSpan &&
96
+ nodeSourceSpan.start.offset < startSourceSpan.start.offset) {
97
+ startSourceSpan = nodeSourceSpan;
98
+ return;
99
+ }
100
+ });
101
+ return startSourceSpan;
102
+ }
103
+ function getEndSourceSpanFromAST(ast) {
104
+ let endSourceSpan = null;
105
+ ast.templateNodes.forEach((node) => {
106
+ const nodeSourceSpan = node.endSourceSpan || node.sourceSpan;
107
+ if (!endSourceSpan) {
108
+ endSourceSpan = nodeSourceSpan;
109
+ return;
110
+ }
111
+ if (nodeSourceSpan &&
112
+ nodeSourceSpan.end.offset > endSourceSpan.end.offset) {
113
+ endSourceSpan = nodeSourceSpan;
114
+ return;
115
+ }
116
+ });
117
+ return endSourceSpan;
118
+ }
119
+ function convertNgAstCommentsToTokens(comments) {
120
+ const commentTokens = comments.map((comment) => {
121
+ return {
122
+ // In an HTML context, effectively all our comments are Block comments
123
+ type: 'Block',
124
+ value: comment.value,
125
+ loc: (0, convert_source_span_to_loc_1.convertNodeSourceSpanToLoc)(comment.sourceSpan),
126
+ range: [comment.sourceSpan.start.offset, comment.sourceSpan.end.offset],
127
+ };
128
+ });
129
+ /**
130
+ * ESLint requires this to be sorted by Token#range[0]
131
+ * https://eslint.org/docs/developer-guide/working-with-custom-parsers
132
+ */
133
+ return commentTokens.sort((a, b) => a.range[0] - b.range[0]);
134
+ }
135
+ class TemplateParseError extends Error {
136
+ constructor(message, fileName, index, lineNumber, column) {
137
+ super(message);
138
+ this.fileName = fileName;
139
+ this.index = index;
140
+ this.lineNumber = lineNumber;
141
+ this.column = column;
142
+ Object.defineProperty(this, 'name', {
143
+ value: new.target.name,
144
+ enumerable: false,
145
+ configurable: true,
146
+ });
147
+ }
148
+ }
149
+ exports.TemplateParseError = TemplateParseError;
150
+ function createTemplateParseError(parseError) {
151
+ const message = parseError.msg;
152
+ const fileName = parseError.span.start.file.url;
153
+ const index = parseError.span.start.offset;
154
+ const lineNumber = parseError.span.start.line + 1;
155
+ const column = parseError.span.start.col + 1;
156
+ return new TemplateParseError(message, fileName, index, lineNumber, column);
157
+ }
158
+ exports.createTemplateParseError = createTemplateParseError;
159
+ function parseForESLint(code, options) {
160
+ var _a, _b;
161
+ const angularCompilerResult = (0, bundled_angular_compiler_1.parseTemplate)(code, options.filePath, {
162
+ preserveWhitespaces: true,
163
+ preserveLineEndings: true,
164
+ collectCommentNodes: true,
165
+ });
166
+ let ngAstCommentNodes = [];
167
+ if (Array.isArray(angularCompilerResult.commentNodes)) {
168
+ ngAstCommentNodes = angularCompilerResult.commentNodes;
169
+ }
170
+ const ast = {
171
+ type: 'Program',
172
+ comments: convertNgAstCommentsToTokens(ngAstCommentNodes),
173
+ tokens: [],
174
+ range: [0, 0],
175
+ loc: {
176
+ start: { line: 0, column: 0 },
177
+ end: { line: 0, column: 0 },
178
+ },
179
+ templateNodes: angularCompilerResult.nodes,
180
+ value: code,
181
+ };
182
+ // @ts-expect-error The types for ScopeManager seem to be wrong, it requires a configuration object or it will throw at runtime
183
+ const scopeManager = new eslint_scope_1.ScopeManager({});
184
+ // @ts-expect-error Create a global scope for the ScopeManager, the types for Scope also seem to be wrong
185
+ new eslint_scope_1.Scope(scopeManager, 'module', null, ast, false);
186
+ preprocessNode(ast);
187
+ const startSourceSpan = getStartSourceSpanFromAST(ast);
188
+ const endSourceSpan = getEndSourceSpanFromAST(ast);
189
+ if (startSourceSpan && endSourceSpan) {
190
+ ast.range = [startSourceSpan.start.offset, endSourceSpan.end.offset];
191
+ ast.loc = {
192
+ start: (0, convert_source_span_to_loc_1.convertNodeSourceSpanToLoc)(startSourceSpan).start,
193
+ end: (0, convert_source_span_to_loc_1.convertNodeSourceSpanToLoc)(endSourceSpan).end,
194
+ };
195
+ }
196
+ // TODO: Stop suppressing parse errors by default in v14
197
+ const suppressParseErrors = (_a = options.suppressParseErrors) !== null && _a !== void 0 ? _a : true;
198
+ if (!suppressParseErrors && ((_b = angularCompilerResult.errors) === null || _b === void 0 ? void 0 : _b.length)) {
199
+ throw createTemplateParseError(angularCompilerResult.errors[0]);
200
+ }
201
+ return {
202
+ ast,
203
+ scopeManager,
204
+ visitorKeys: KEYS,
205
+ services: {
206
+ convertNodeSourceSpanToLoc: convert_source_span_to_loc_1.convertNodeSourceSpanToLoc,
207
+ convertElementSourceSpanToLoc: convert_source_span_to_loc_1.convertElementSourceSpanToLoc,
208
+ },
209
+ };
210
+ }
211
+ exports.parseForESLint = parseForESLint;
212
+ function parse(code, options) {
213
+ return parseForESLint(code, options).ast;
214
+ }
215
+ exports.parse = parse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-eslint/template-parser",
3
- "version": "15.2.2-alpha.9+449bb7a",
3
+ "version": "16.0.0-alpha.1",
4
4
  "description": "Angular Template parser for ESLint",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -17,12 +17,12 @@
17
17
  "LICENSE"
18
18
  ],
19
19
  "dependencies": {
20
- "@angular-eslint/bundled-angular-compiler": "15.2.2-alpha.9+449bb7a",
20
+ "@angular-eslint/bundled-angular-compiler": "16.0.0-alpha.1",
21
21
  "eslint-scope": "^7.0.0"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "eslint": "^7.20.0 || ^8.0.0",
25
25
  "typescript": "*"
26
26
  },
27
- "gitHead": "449bb7ae1934072c00a39ffb2fe8973a39da9df4"
27
+ "gitHead": "ffcbae10749bc12760a9d442d4293179c6c19042"
28
28
  }
@@ -1,6 +0,0 @@
1
- import type { ParseSourceSpan, TmplAstElement } from '@angular-eslint/bundled-angular-compiler';
2
- import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
3
- export declare function convertNodeSourceSpanToLoc(sourceSpan: ParseSourceSpan): TSESTree.SourceLocation;
4
- export declare function convertElementSourceSpanToLoc(context: Readonly<TSESLint.RuleContext<string, readonly unknown[]>>, node: TmplAstElement & {
5
- type: string;
6
- }): TSESTree.SourceLocation;
@@ -1,44 +0,0 @@
1
- import type { ParseError } from '@angular-eslint/bundled-angular-compiler';
2
- import type { TSESTree } from '@typescript-eslint/types';
3
- import { ScopeManager } from 'eslint-scope';
4
- import { convertElementSourceSpanToLoc, convertNodeSourceSpanToLoc } from './convert-source-span-to-loc';
5
- type NodeOrTokenType = any;
6
- interface Node {
7
- [key: string]: any;
8
- type: NodeOrTokenType;
9
- }
10
- interface VisitorKeys {
11
- [nodeName: string]: string[];
12
- }
13
- interface Token extends TSESTree.BaseNode {
14
- type: NodeOrTokenType;
15
- value: string;
16
- }
17
- interface AST extends Node, Token {
18
- comments: Token[];
19
- tokens: Token[];
20
- templateNodes: any[];
21
- }
22
- export declare class TemplateParseError extends Error {
23
- readonly fileName: string;
24
- readonly index: number;
25
- readonly lineNumber: number;
26
- readonly column: number;
27
- constructor(message: string, fileName: string, index: number, lineNumber: number, column: number);
28
- }
29
- export declare function createTemplateParseError(parseError: ParseError): TemplateParseError;
30
- export interface ParserOptions {
31
- filePath: string;
32
- suppressParseErrors?: boolean;
33
- }
34
- declare function parseForESLint(code: string, options: ParserOptions): {
35
- ast: AST;
36
- scopeManager: ScopeManager;
37
- visitorKeys: VisitorKeys;
38
- services: {
39
- convertElementSourceSpanToLoc: typeof convertElementSourceSpanToLoc;
40
- convertNodeSourceSpanToLoc: typeof convertNodeSourceSpanToLoc;
41
- };
42
- };
43
- export { parseForESLint };
44
- export declare function parse(code: string, options: ParserOptions): AST;