@angular-eslint/eslint-plugin-template 22.6.1-alpha.2 → 22.6.1-alpha.3

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,245 @@
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.isFileLikelyToContainComponentDeclarations = isFileLikelyToContainComponentDeclarations;
7
+ exports.preprocessComponentFile = preprocessComponentFile;
8
+ exports.getComponentMetadata = getComponentMetadata;
9
+ exports.postprocessComponentFile = postprocessComponentFile;
10
+ const path_1 = require("path");
11
+ const typescript_1 = __importDefault(require("typescript"));
12
+ const rangeMap = new Map();
13
+ /**
14
+ * Because ultimately a user is in control of how and when this processor gets invoked,
15
+ * we can't fully protect them against doing more work than is necessary in all cases.
16
+ *
17
+ * Therefore, before we do a full parse of a TypeScript file to try and extract one or
18
+ * more Component declarations we want to do a really quick check for whether or not
19
+ * a file is likely to contain them.
20
+ */
21
+ function isFileLikelyToContainComponentDeclarations(text, filename) {
22
+ /**
23
+ * Quickest possible heuristic is based on file extension suffix
24
+ */
25
+ if ([
26
+ '.component.ts',
27
+ '.page.ts',
28
+ '.dialog.ts',
29
+ '.modal.ts',
30
+ '.popover.ts',
31
+ '.bottomsheet.ts',
32
+ '.snackbar.ts',
33
+ ].some((likelySuffix) => filename.endsWith(likelySuffix))) {
34
+ return true;
35
+ }
36
+ /**
37
+ * Next quickest possible heuristic is the presence of the substring 'Component'
38
+ * and the substring '@angular/core' within the file contents
39
+ */
40
+ if (text.includes('Component') && text.includes('@angular/core')) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ function preprocessComponentFile(text, filename,
46
+ /** Lets callers that already parsed the file avoid a second full parse */
47
+ parsedSourceFile) {
48
+ // This effectively instructs ESLint that there were no code blocks to extract for the current file
49
+ const noopResult = [text];
50
+ if (!isFileLikelyToContainComponentDeclarations(text, filename)) {
51
+ return noopResult;
52
+ }
53
+ try {
54
+ const sourceFile = parsedSourceFile ??
55
+ typescript_1.default.createSourceFile(filename, text, typescript_1.default.ScriptTarget.Latest,
56
+ /* setParentNodes */ true);
57
+ const result = [text];
58
+ let id = 0;
59
+ for (const metadata of getComponentMetadata(sourceFile)) {
60
+ /**
61
+ * Ignore Components which have external template files, they will be linted directly,
62
+ * and any that have inline templates which are malformed
63
+ */
64
+ const templateProperty = metadata.properties.find((id) => id && id.name && id.name.getText() === 'template');
65
+ if (metadata.properties.find((id) => id && id.name && id.name.getText() === 'templateUrl') ||
66
+ !templateProperty) {
67
+ continue;
68
+ }
69
+ if (!typescript_1.default.isPropertyAssignment(templateProperty)) {
70
+ continue;
71
+ }
72
+ let templateText;
73
+ const templatePropertyInitializer = templateProperty.initializer;
74
+ if (typescript_1.default.isNoSubstitutionTemplateLiteral(templatePropertyInitializer)) {
75
+ templateText = templatePropertyInitializer.rawText;
76
+ }
77
+ if (typescript_1.default.isTemplateExpression(templatePropertyInitializer)) {
78
+ // The text includes the opening and closing
79
+ // backtick, so trim the first and last characters.
80
+ templateText = templatePropertyInitializer.getText().slice(1, -1);
81
+ }
82
+ if (typescript_1.default.isStringLiteral(templatePropertyInitializer)) {
83
+ templateText = templatePropertyInitializer.text;
84
+ }
85
+ // The template initializer is somehow not a string literal or a string template
86
+ if (!templateText) {
87
+ continue;
88
+ }
89
+ const baseFilename = (0, path_1.basename)(filename);
90
+ const inlineTemplateTmpFilename = `inline-template-${baseFilename}-${++id}.component.html`;
91
+ const start = templateProperty.initializer.getStart();
92
+ const end = templateProperty.initializer.getEnd();
93
+ rangeMap.set(inlineTemplateTmpFilename, {
94
+ sourceFilename: filename,
95
+ range: [start, end],
96
+ lineAndCharacter: {
97
+ start: sourceFile.getLineAndCharacterOfPosition(start),
98
+ end: sourceFile.getLineAndCharacterOfPosition(end),
99
+ },
100
+ });
101
+ /**
102
+ * We are ultimately returning an array containing both the original source,
103
+ * and a new fragment representing each of the inline HTML templates found.
104
+ * Each fragment must have an appropriate .html extension so that it can be
105
+ * linted using the right rules and plugins.
106
+ *
107
+ * The postprocessor will handle tying things back to the right position
108
+ * in the original file, so this temporary filename will never be visible
109
+ * to the end user.
110
+ */
111
+ result.push({
112
+ text: templateText,
113
+ filename: inlineTemplateTmpFilename,
114
+ });
115
+ }
116
+ return result;
117
+ }
118
+ catch (err) {
119
+ console.log(err);
120
+ console.error('preprocess: ERROR could not parse @Component() metadata', filename);
121
+ return noopResult;
122
+ }
123
+ }
124
+ function getComponentMetadata(sourceFile) {
125
+ const metadata = [];
126
+ for (const declaration of getClassDeclarationFromSourceFile(sourceFile)) {
127
+ for (const decorator of typescript_1.default.getDecorators(declaration) ?? []) {
128
+ const expression = decorator.expression;
129
+ if (typescript_1.default.isCallExpression(expression) &&
130
+ typescript_1.default.isIdentifier(expression.expression) &&
131
+ expression.expression.text === 'Component' &&
132
+ expression.arguments.length === 1 &&
133
+ typescript_1.default.isObjectLiteralExpression(expression.arguments[0])) {
134
+ metadata.push(expression.arguments[0]);
135
+ }
136
+ }
137
+ }
138
+ return metadata;
139
+ }
140
+ function getClassDeclarationFromSourceFile(sourceFile) {
141
+ const classDeclarations = [];
142
+ visit(sourceFile);
143
+ return classDeclarations;
144
+ function visit(node) {
145
+ if (typescript_1.default.isClassDeclaration(node)) {
146
+ classDeclarations.push(node);
147
+ return;
148
+ }
149
+ // Class declarations are usually at the top-level, but there are
150
+ // some situations where they might be nested, such as in test files.
151
+ // If the node could have a class declaration somewhere in its
152
+ // descendant nodes, then we will recurse down into each child node.
153
+ // Keywords, tokens and trivia all come before `FirstNode`. They won't
154
+ // contain child nodes anyway, but we can skip them to save some time.
155
+ // Likewise, we can skip nodes that are part of JSDoc comments.
156
+ if (node.kind < typescript_1.default.SyntaxKind.FirstNode ||
157
+ node.kind > typescript_1.default.SyntaxKind.FirstJSDocNode) {
158
+ return;
159
+ }
160
+ // Type nodes can be skipped.
161
+ if (node.kind >= typescript_1.default.SyntaxKind.TypePredicate &&
162
+ node.kind <= typescript_1.default.SyntaxKind.ImportType) {
163
+ return;
164
+ }
165
+ // Some specific kinds of nodes can be skipped because
166
+ // we know that they cannot contain class declarations.
167
+ switch (node.kind) {
168
+ case typescript_1.default.SyntaxKind.InterfaceDeclaration:
169
+ case typescript_1.default.SyntaxKind.EnumDeclaration:
170
+ case typescript_1.default.SyntaxKind.ImportEqualsDeclaration:
171
+ case typescript_1.default.SyntaxKind.ImportDeclaration:
172
+ case typescript_1.default.SyntaxKind.ImportClause:
173
+ return;
174
+ }
175
+ // For everything else, we'll play it safe
176
+ // and recurse down into the child nodes.
177
+ typescript_1.default.forEachChild(node, visit);
178
+ }
179
+ }
180
+ function postprocessComponentFile(multiDimensionalMessages, filename) {
181
+ const messagesFromComponentSource = multiDimensionalMessages[0];
182
+ /**
183
+ * If the Component did not have one or more inline templates defined within it
184
+ * there will only be one item in the multiDimensionalMessages
185
+ */
186
+ if (multiDimensionalMessages.length === 1) {
187
+ return messagesFromComponentSource;
188
+ }
189
+ /**
190
+ * There could be multiple inline templates found within the current file,
191
+ * so they are represented by all of the multiDimensionalMessages after the
192
+ * first one (which is the file itself)
193
+ */
194
+ const messagesFromAllInlineTemplateHTML = multiDimensionalMessages.slice(1);
195
+ /**
196
+ * Adjust message location data to apply it back to the
197
+ * original file
198
+ */
199
+ const res = [
200
+ ...messagesFromComponentSource,
201
+ ...messagesFromAllInlineTemplateHTML.flatMap((messagesFromInlineTemplateHTML, i) => {
202
+ const baseFilename = (0, path_1.basename)(filename);
203
+ const inlineTemplateTmpFilename = `inline-template-${baseFilename}-${i + 1}.component.html`;
204
+ const rangeData = rangeMap.get(inlineTemplateTmpFilename);
205
+ if (!rangeData) {
206
+ return [];
207
+ }
208
+ return messagesFromInlineTemplateHTML.map((message) => {
209
+ // The first line of the inline template starts at the column after
210
+ // the opening quote in the TypeScript file, so we need to adjust
211
+ // the message's column by that amount when the message starts on
212
+ // the first line. The character we recorded was the quote's column,
213
+ // so add one to get the column where the actual string starts.
214
+ if (message.line === 1) {
215
+ message.column += rangeData.lineAndCharacter.start.character + 1;
216
+ }
217
+ // The same thing applies to the end column
218
+ // if it also ends on the first line.
219
+ if (message.endLine === 1) {
220
+ message.endColumn += rangeData.lineAndCharacter.start.character + 1;
221
+ }
222
+ message.line += rangeData.lineAndCharacter.start.line;
223
+ message.endLine += rangeData.lineAndCharacter.start.line;
224
+ // The range defines the range of the value that initializes
225
+ // the `template` property, which includes the opening and
226
+ // closing quotes. Add one to move past the opening quote.
227
+ const startOffset = rangeData.range[0] + 1;
228
+ const adjustFixRange = (fix) => {
229
+ fix.range = [
230
+ startOffset + fix.range[0],
231
+ startOffset + fix.range[1],
232
+ ];
233
+ };
234
+ if (message.fix) {
235
+ adjustFixRange(message.fix);
236
+ }
237
+ // Suggestions carry their own fixes, which are also
238
+ // relative to the inline template and need the same adjustment.
239
+ message.suggestions?.forEach((suggestion) => adjustFixRange(suggestion.fix));
240
+ return message;
241
+ });
242
+ }),
243
+ ];
244
+ return res;
245
+ }
@@ -1,41 +1,29 @@
1
- /**
2
- * Because ultimately a user is in control of how and when this processor gets invoked,
3
- * we can't fully protect them against doing more work than is necessary in all cases.
4
- *
5
- * Therefore, before we do a full parse of a TypeScript file to try and extract one or
6
- * more Component declarations we want to do a really quick check for whether or not
7
- * a file is likely to contain them.
8
- */
9
- export declare function isFileLikelyToContainComponentDeclarations(text: string, filename: string): boolean;
10
- type PreprocessResult = (string | {
11
- text: string;
12
- filename: string;
13
- })[];
14
- export declare function preprocessComponentFile(text: string, filename: string): PreprocessResult;
15
- export declare function postprocessComponentFile(multiDimensionalMessages: {
16
- ruleId: string;
17
- severity: number;
18
- message: string;
19
- line: number;
20
- column: number;
21
- nodeType: string;
22
- messageId: string;
23
- endLine: number;
24
- endColumn: number;
25
- fix?: {
26
- range: number[];
27
- text: string;
1
+ import { preprocessComponentFile, postprocessComponentFile } from './inline-templates';
2
+ export { isFileLikelyToContainComponentDeclarations, preprocessComponentFile, postprocessComponentFile, } from './inline-templates';
3
+ import type { InlineStyleLanguage } from './inline-styles';
4
+ export interface ProcessorOptions {
5
+ inlineTemplates?: boolean;
6
+ inlineStyles?: false | {
7
+ inlineStyleLanguage: InlineStyleLanguage;
28
8
  };
29
- }[][], filename: string): readonly unknown[];
30
- declare const _default: {
9
+ }
10
+ declare const processors: {
31
11
  'extract-inline-html': {
32
12
  meta: {
33
13
  name: string;
34
14
  };
35
15
  preprocess: typeof preprocessComponentFile;
36
16
  postprocess: typeof postprocessComponentFile;
37
- supportsAutofix: boolean;
17
+ supportsAutofix: true;
38
18
  };
39
19
  };
40
- export default _default;
20
+ export declare function createProcessor({ inlineTemplates, inlineStyles, }?: ProcessorOptions): {
21
+ meta: {
22
+ name: string;
23
+ };
24
+ preprocess: typeof preprocessComponentFile;
25
+ postprocess: typeof postprocessComponentFile;
26
+ supportsAutofix: true;
27
+ };
28
+ export default processors;
41
29
  //# sourceMappingURL=processors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"processors.d.ts","sourceRoot":"","sources":["../src/processors.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAgB,0CAA0C,CACxD,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CA2BT;AAED,KAAK,gBAAgB,GAAG,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAC;AAExE,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAqJlB;AAyDD,wBAAgB,wBAAwB,CACtC,wBAAwB,EAAE;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE;QACJ,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,EAAE,EAAE,EACL,QAAQ,EAAE,MAAM,GACf,SAAS,OAAO,EAAE,CAmEpB;;;;;;;;;;;AAED,wBASE"}
1
+ {"version":3,"file":"processors.d.ts","sourceRoot":"","sources":["../src/processors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,0CAA0C,EAC1C,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,KAAK,GAAG;QAAE,mBAAmB,EAAE,mBAAmB,CAAA;KAAE,CAAC;CACrE;AAED,QAAA,MAAM,UAAU;;;;;;;;;CASf,CAAC;AAOF,wBAAgB,eAAe,CAAC,EAC9B,eAAsB,EACtB,YAAoB,GACrB,GAAE,gBAAqB;;;;;;;EAsCvB;AAED,eAAe,UAAU,CAAC"}
@@ -1,267 +1,49 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isFileLikelyToContainComponentDeclarations = isFileLikelyToContainComponentDeclarations;
7
- exports.preprocessComponentFile = preprocessComponentFile;
8
- exports.postprocessComponentFile = postprocessComponentFile;
9
- const path_1 = require("path");
10
- const typescript_1 = __importDefault(require("typescript"));
11
- const rangeMap = new Map();
12
- /**
13
- * Because ultimately a user is in control of how and when this processor gets invoked,
14
- * we can't fully protect them against doing more work than is necessary in all cases.
15
- *
16
- * Therefore, before we do a full parse of a TypeScript file to try and extract one or
17
- * more Component declarations we want to do a really quick check for whether or not
18
- * a file is likely to contain them.
19
- */
20
- function isFileLikelyToContainComponentDeclarations(text, filename) {
21
- /**
22
- * Quickest possible heuristic is based on file extension suffix
23
- */
24
- if ([
25
- '.component.ts',
26
- '.page.ts',
27
- '.dialog.ts',
28
- '.modal.ts',
29
- '.popover.ts',
30
- '.bottomsheet.ts',
31
- '.snackbar.ts',
32
- ].some((likelySuffix) => filename.endsWith(likelySuffix))) {
33
- return true;
34
- }
35
- /**
36
- * Next quickest possible heuristic is the presence of the substring 'Component'
37
- * and the substring '@angular/core' within the file contents
38
- */
39
- if (text.includes('Component') && text.includes('@angular/core')) {
40
- return true;
41
- }
42
- return false;
43
- }
44
- function preprocessComponentFile(text, filename) {
45
- // This effectively instructs ESLint that there were no code blocks to extract for the current file
46
- const noopResult = [text];
47
- if (!isFileLikelyToContainComponentDeclarations(text, filename)) {
48
- return noopResult;
49
- }
50
- try {
51
- const sourceFile = typescript_1.default.createSourceFile(filename, text, typescript_1.default.ScriptTarget.Latest,
52
- /* setParentNodes */ true);
53
- const classDeclarations = getClassDeclarationFromSourceFile(sourceFile);
54
- if (!classDeclarations || !classDeclarations.length) {
55
- return noopResult;
56
- }
57
- /**
58
- * Find all the Component decorators
59
- */
60
- const componentDecoratorNodes = [];
61
- for (const classDeclaration of classDeclarations) {
62
- const classDecorators = typescript_1.default.getDecorators(classDeclaration);
63
- if (!classDecorators) {
64
- continue;
65
- }
66
- for (const decorator of classDecorators) {
67
- if (typescript_1.default.isCallExpression(decorator.expression) &&
68
- typescript_1.default.isIdentifier(decorator.expression.expression) &&
69
- decorator.expression.expression.text === 'Component') {
70
- componentDecoratorNodes.push(decorator);
71
- }
72
- }
73
- }
74
- /**
75
- * Ignore malformed Component files
76
- */
77
- if (!componentDecoratorNodes || !componentDecoratorNodes.length) {
78
- return noopResult;
79
- }
80
- const result = [text];
81
- let id = 0;
82
- for (const componentDecoratorNode of componentDecoratorNodes) {
83
- /**
84
- * Ignore malformed component metadata
85
- */
86
- if (!typescript_1.default.isDecorator(componentDecoratorNode) ||
87
- !typescript_1.default.isCallExpression(componentDecoratorNode.expression) ||
88
- componentDecoratorNode.expression.arguments.length !== 1) {
89
- continue;
90
- }
91
- const metadata = componentDecoratorNode.expression.arguments[0];
92
- if (!typescript_1.default.isObjectLiteralExpression(metadata)) {
93
- continue;
94
- }
95
- /**
96
- * Ignore Components which have external template files, they will be linted directly,
97
- * and any that have inline templates which are malformed
98
- */
99
- const templateProperty = metadata.properties.find((id) => id && id.name && id.name.getText() === 'template');
100
- if (metadata.properties.find((id) => id && id.name && id.name.getText() === 'templateUrl') ||
101
- !templateProperty) {
102
- continue;
103
- }
104
- if (!typescript_1.default.isPropertyAssignment(templateProperty)) {
105
- continue;
106
- }
107
- let templateText;
108
- const templatePropertyInitializer = templateProperty.initializer;
109
- if (typescript_1.default.isNoSubstitutionTemplateLiteral(templatePropertyInitializer)) {
110
- templateText = templatePropertyInitializer.rawText;
111
- }
112
- if (typescript_1.default.isTemplateExpression(templatePropertyInitializer)) {
113
- // The text includes the opening and closing
114
- // backtick, so trim the first and last characters.
115
- templateText = templatePropertyInitializer.getText().slice(1, -1);
116
- }
117
- if (typescript_1.default.isStringLiteral(templatePropertyInitializer)) {
118
- templateText = templatePropertyInitializer.text;
119
- }
120
- // The template initializer is somehow not a string literal or a string template
121
- if (!templateText) {
122
- continue;
123
- }
124
- const baseFilename = (0, path_1.basename)(filename);
125
- const inlineTemplateTmpFilename = `inline-template-${baseFilename}-${++id}.component.html`;
126
- const start = templateProperty.initializer.getStart();
127
- const end = templateProperty.initializer.getEnd();
128
- rangeMap.set(inlineTemplateTmpFilename, {
129
- range: [start, end],
130
- lineAndCharacter: {
131
- start: sourceFile.getLineAndCharacterOfPosition(start),
132
- end: sourceFile.getLineAndCharacterOfPosition(end),
133
- },
134
- });
135
- /**
136
- * We are ultimately returning an array containing both the original source,
137
- * and a new fragment representing each of the inline HTML templates found.
138
- * Each fragment must have an appropriate .html extension so that it can be
139
- * linted using the right rules and plugins.
140
- *
141
- * The postprocessor will handle tying things back to the right position
142
- * in the original file, so this temporary filename will never be visible
143
- * to the end user.
144
- */
145
- result.push({
146
- text: templateText,
147
- filename: inlineTemplateTmpFilename,
148
- });
149
- }
150
- return result;
151
- }
152
- catch (err) {
153
- console.log(err);
154
- console.error('preprocess: ERROR could not parse @Component() metadata', filename);
155
- return noopResult;
156
- }
157
- }
158
- function getClassDeclarationFromSourceFile(sourceFile) {
159
- const classDeclarations = [];
160
- visit(sourceFile);
161
- return classDeclarations;
162
- function visit(node) {
163
- if (typescript_1.default.isClassDeclaration(node)) {
164
- classDeclarations.push(node);
165
- return;
166
- }
167
- // Class declarations are usually at the top-level, but there are
168
- // some situations where they might be nested, such as in test files.
169
- // If the node could have a class declaration somewhere in its
170
- // descendant nodes, then we will recurse down into each child node.
171
- // Keywords, tokens and trivia all come before `FirstNode`. They won't
172
- // contain child nodes anyway, but we can skip them to save some time.
173
- // Likewise, we can skip nodes that are part of JSDoc comments.
174
- if (node.kind < typescript_1.default.SyntaxKind.FirstNode ||
175
- node.kind > typescript_1.default.SyntaxKind.FirstJSDocNode) {
176
- return;
177
- }
178
- // Type nodes can be skipped.
179
- if (node.kind >= typescript_1.default.SyntaxKind.TypePredicate &&
180
- node.kind <= typescript_1.default.SyntaxKind.ImportType) {
181
- return;
182
- }
183
- // Some specific kinds of nodes can be skipped because
184
- // we know that they cannot contain class declarations.
185
- switch (node.kind) {
186
- case typescript_1.default.SyntaxKind.InterfaceDeclaration:
187
- case typescript_1.default.SyntaxKind.EnumDeclaration:
188
- case typescript_1.default.SyntaxKind.ImportEqualsDeclaration:
189
- case typescript_1.default.SyntaxKind.ImportDeclaration:
190
- case typescript_1.default.SyntaxKind.ImportClause:
191
- return;
192
- }
193
- // For everything else, we'll play it safe
194
- // and recurse down into the child nodes.
195
- typescript_1.default.forEachChild(node, visit);
196
- }
197
- }
198
- function postprocessComponentFile(multiDimensionalMessages, filename) {
199
- const messagesFromComponentSource = multiDimensionalMessages[0];
200
- /**
201
- * If the Component did not have one or more inline templates defined within it
202
- * there will only be one item in the multiDimensionalMessages
203
- */
204
- if (multiDimensionalMessages.length === 1) {
205
- return messagesFromComponentSource;
206
- }
207
- /**
208
- * There could be multiple inline templates found within the current file,
209
- * so they are represented by all of the multiDimensionalMessages after the
210
- * first one (which is the file itself)
211
- */
212
- const messagesFromAllInlineTemplateHTML = multiDimensionalMessages.slice(1);
213
- /**
214
- * Adjust message location data to apply it back to the
215
- * original file
216
- */
217
- const res = [
218
- ...messagesFromComponentSource,
219
- ...messagesFromAllInlineTemplateHTML.flatMap((messagesFromInlineTemplateHTML, i) => {
220
- const baseFilename = (0, path_1.basename)(filename);
221
- const inlineTemplateTmpFilename = `inline-template-${baseFilename}-${i + 1}.component.html`;
222
- const rangeData = rangeMap.get(inlineTemplateTmpFilename);
223
- if (!rangeData) {
224
- return [];
225
- }
226
- return messagesFromInlineTemplateHTML.map((message) => {
227
- // The first line of the inline template starts at the column after
228
- // the opening quote in the TypeScript file, so we need to adjust
229
- // the message's column by that amount when the message starts on
230
- // the first line. The character we recorded was the quote's column,
231
- // so add one to get the column where the actual string starts.
232
- if (message.line === 1) {
233
- message.column += rangeData.lineAndCharacter.start.character + 1;
234
- }
235
- // The same thing applies to the end column
236
- // if it also ends on the first line.
237
- if (message.endLine === 1) {
238
- message.endColumn += rangeData.lineAndCharacter.start.character + 1;
239
- }
240
- message.line += rangeData.lineAndCharacter.start.line;
241
- message.endLine += rangeData.lineAndCharacter.start.line;
242
- if (message.fix) {
243
- // The range defines the range of the value that initializes
244
- // the `template` property, which includes the opening and
245
- // closing quotes. Add one to move past the opening quote.
246
- const startOffset = rangeData.range[0] + 1;
247
- message.fix.range = [
248
- startOffset + message.fix.range[0],
249
- startOffset + message.fix.range[1],
250
- ];
251
- }
252
- return message;
253
- });
254
- }),
255
- ];
256
- return res;
257
- }
258
- exports.default = {
3
+ exports.postprocessComponentFile = exports.preprocessComponentFile = exports.isFileLikelyToContainComponentDeclarations = void 0;
4
+ exports.createProcessor = createProcessor;
5
+ const inline_templates_1 = require("./inline-templates");
6
+ const inline_styles_1 = require("./inline-styles");
7
+ var inline_templates_2 = require("./inline-templates");
8
+ Object.defineProperty(exports, "isFileLikelyToContainComponentDeclarations", { enumerable: true, get: function () { return inline_templates_2.isFileLikelyToContainComponentDeclarations; } });
9
+ Object.defineProperty(exports, "preprocessComponentFile", { enumerable: true, get: function () { return inline_templates_2.preprocessComponentFile; } });
10
+ Object.defineProperty(exports, "postprocessComponentFile", { enumerable: true, get: function () { return inline_templates_2.postprocessComponentFile; } });
11
+ const processors = {
259
12
  'extract-inline-html': {
260
13
  meta: {
261
14
  name: 'extract-inline-html',
262
15
  },
263
- preprocess: preprocessComponentFile,
264
- postprocess: postprocessComponentFile,
16
+ preprocess: inline_templates_1.preprocessComponentFile,
17
+ postprocess: inline_templates_1.postprocessComponentFile,
265
18
  supportsAutofix: true,
266
19
  },
267
20
  };
21
+ const configuredProcessors = new Map();
22
+ function createProcessor({ inlineTemplates = true, inlineStyles = false, } = {}) {
23
+ if (typeof inlineTemplates !== 'boolean') {
24
+ throw new Error('inlineTemplates must be a boolean');
25
+ }
26
+ if (inlineStyles === false) {
27
+ if (!inlineTemplates) {
28
+ throw new Error('inlineTemplates and inlineStyles cannot both be disabled');
29
+ }
30
+ return processors['extract-inline-html'];
31
+ }
32
+ if (!inlineStyles ||
33
+ typeof inlineStyles !== 'object' ||
34
+ !inlineStyles.inlineStyleLanguage) {
35
+ throw new Error("inlineStyles requires inlineStyleLanguage (copy it from angular.json; Angular's default is 'css')");
36
+ }
37
+ const { inlineStyleLanguage } = inlineStyles;
38
+ if (!inline_styles_1.inlineStyleLanguages.includes(inlineStyleLanguage)) {
39
+ throw new Error(`createProcessor: unsupported inlineStyleLanguage "${inlineStyleLanguage}", expected one of: ${inline_styles_1.inlineStyleLanguages.join(', ')}`);
40
+ }
41
+ const name = `extract-inline[${inlineTemplates ? 'templates,' : ''}styles=${inlineStyleLanguage}]`;
42
+ let processor = configuredProcessors.get(name);
43
+ if (!processor) {
44
+ processor = (0, inline_styles_1.createInlineStylesProcessor)(inlineStyleLanguage, inlineTemplates);
45
+ configuredProcessors.set(name, processor);
46
+ }
47
+ return processor;
48
+ }
49
+ exports.default = processors;
@@ -1 +1 @@
1
- {"version":3,"file":"eqeqeq.d.ts","sourceRoot":"","sources":["../../src/rules/eqeqeq.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,uBAAuB,CAAC;AAC5D,eAAO,MAAM,SAAS,WAAW,CAAC;;;;AAGlC,wBA4EG;AA8EH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
1
+ {"version":3,"file":"eqeqeq.d.ts","sourceRoot":"","sources":["../../src/rules/eqeqeq.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,uBAAuB,CAAC;AAC5D,eAAO,MAAM,SAAS,WAAW,CAAC;;;;AAGlC,wBA4EG;AAgFH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
@@ -79,12 +79,14 @@ function getSpanLength({ span: { start, end } }) {
79
79
  }
80
80
  const getFix = ({ node, right, end, sourceCode, fixer, }) => {
81
81
  const result = (0, get_nearest_node_from_1.getNearestNodeFrom)(node, isASTWithSource);
82
- if (!result?.source)
83
- return null;
84
- const { source, ast } = result;
85
82
  let startOffset = 0;
86
- while (!isInterpolation(ast) && isLeadingTriviaChar(source[startOffset])) {
87
- startOffset++;
83
+ if (result) {
84
+ const { source, ast } = result;
85
+ if (!source)
86
+ return null;
87
+ while (!isInterpolation(ast) && isLeadingTriviaChar(source[startOffset])) {
88
+ startOffset++;
89
+ }
88
90
  }
89
91
  const endRange = end - startOffset - getSpanLength(right) - 1;
90
92
  let eqOffset = 0;
@@ -268,7 +268,7 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
268
268
  if (checkDuplicateId) {
269
269
  for (const [customId, sourceSpans] of collectedCustomIds) {
270
270
  if (sourceSpans.length <= 1) {
271
- break;
271
+ continue;
272
272
  }
273
273
  for (const sourceSpan of sourceSpans) {
274
274
  const loc = parserServices.convertNodeSourceSpanToLoc(sourceSpan);
@@ -34,5 +34,5 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
34
34
  },
35
35
  });
36
36
  exports.RULE_DOCS_EXTENSION = {
37
- rationale: "Elements like <marquee> and <blink> create distracting motion that can cause problems for users with attention disorders, vestibular disorders, or seizure disorders. Moving text is harder to read for everyone, including users with dyslexia or cognitive disabilities. These elements are also deprecated in HTML5 and not well-supported across browsers. WCAG guidelines require that moving, blinking, or scrolling content can be paused, stopped, or hidden by users. Instead of these elements, use modern CSS animations with appropriate controls, or simply use static content that doesn't distract from the main page content.",
37
+ rationale: "Elements like `<marquee>` and `<blink>` create distracting motion that can cause problems for users with attention disorders, vestibular disorders, or seizure disorders. Moving text is harder to read for everyone, including users with dyslexia or cognitive disabilities. These elements are also deprecated in HTML5 and not well-supported across browsers. WCAG guidelines require that moving, blinking, or scrolling content can be paused, stopped, or hidden by users. Instead of these elements, use modern CSS animations with appropriate controls, or simply use static content that doesn't distract from the main page content.",
38
38
  };