eslint-plugin-ember-template-lint 0.18.0 → 0.21.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.
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ root: true,
3
+
4
+ plugins: {
5
+ 'ember-template-lint': require('../index'),
6
+ },
7
+
8
+ rules: {}
9
+ };
@@ -0,0 +1,21 @@
1
+ const info = require('../ember-template-lint/info');
2
+ const base = require('./base');
3
+
4
+ const configs = {};
5
+
6
+ Object.entries(info.configs).forEach(([name, config]) => {
7
+ Object.entries(config.rules).forEach(([name, conf]) => {
8
+ if (typeof conf == 'boolean') {
9
+ config.rules[name] = [conf ? 'error' : 'off'];
10
+ }
11
+ });
12
+ configs[name] = {...base, rules: config.rules};
13
+ });
14
+ configs['config'] = {
15
+ ...base,
16
+ rules: info.configuredRules,
17
+ overrides: info.configuredOverrides,
18
+ };
19
+
20
+ module.exports = configs;
21
+
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ root: true,
3
+
4
+ plugins: ['ember-template-lint'],
5
+
6
+ rules: {}
7
+ };
@@ -0,0 +1,21 @@
1
+ const info = require('../ember-template-lint/info');
2
+ const base = require('./base');
3
+
4
+ const configs = {};
5
+
6
+ Object.entries(info.configs).forEach(([name, config]) => {
7
+ Object.entries(config.rules).forEach(([name, conf]) => {
8
+ if (typeof conf == 'boolean') {
9
+ config.rules[name] = [conf ? 'error' : 'off'];
10
+ }
11
+ });
12
+ configs[name] = {...base, rules: config.rules};
13
+ });
14
+ configs['config'] = {
15
+ ...base,
16
+ rules: info.configuredRules,
17
+ overrides: info.configuredOverrides,
18
+ };
19
+
20
+ module.exports = configs;
21
+
@@ -0,0 +1,35 @@
1
+ const templateLintConfig = {
2
+ rules: {},
3
+ plugins: [],
4
+ overrides: [],
5
+ };
6
+
7
+ let current = templateLintConfig;
8
+
9
+ function registerRule(name, config) {
10
+ current.rules[name] = Array.isArray(config) ? config[0] : config;
11
+ }
12
+
13
+ function registerPlugin(name) {
14
+ current.plugins.push(name);
15
+ }
16
+
17
+ function startOverride(files) {
18
+ current = {
19
+ files,
20
+ rules: {}
21
+ };
22
+ templateLintConfig.overrides.push(current);
23
+ }
24
+
25
+ function finishOverride() {
26
+ current = templateLintConfig;
27
+ }
28
+
29
+ module.exports = {
30
+ registerRule,
31
+ registerPlugin,
32
+ startOverride,
33
+ finishOverride,
34
+ templateLintConfig
35
+ };
@@ -0,0 +1,133 @@
1
+ const synckit = require('synckit');
2
+ const syncFn = synckit.createSyncFn(require.resolve('./worker'));
3
+ const { runTemplateLint } = require('../rules/lint');
4
+ const { registerRule, templateLintConfig } = require('../ember-template-lint/config');
5
+
6
+ const lintWithEslintConfigs = syncFn({ config: templateLintConfig });
7
+ const lintConfigs = syncFn();
8
+
9
+ const activeRules = new Map();
10
+ const allMessages = {};
11
+
12
+ const reporter = {
13
+ setup(context) {
14
+ this.getSourceCode = context.getSourceCode;
15
+ this.getPhysicalFilename = context.getPhysicalFilename;
16
+ },
17
+ report(message) {
18
+ message.meta = {
19
+ fixable: 'code'
20
+ };
21
+ allMessages[message.rule] = allMessages[message.rule] || [];
22
+ allMessages[message.rule].push(message);
23
+ }
24
+ };
25
+
26
+ class Rule {
27
+
28
+ constructor(name) {
29
+ this.create = this.create.bind(this);
30
+ this.name = name;
31
+ this.meta = {
32
+ fixable: 'code'
33
+ };
34
+ }
35
+ create(context) {
36
+ const rule = this;
37
+ let options = context.options;
38
+ if (options.length === 0) {
39
+ options = true;
40
+ }
41
+ registerRule(this.name, options);
42
+ const visitor = {
43
+ enter(node) {
44
+ if (!activeRules.get(node)) {
45
+ activeRules.set(node, 0);
46
+ reporter.setup(context);
47
+ runTemplateLint(node, reporter);
48
+ }
49
+ activeRules.set(node, activeRules.get(node) + 1);
50
+ },
51
+ exit(node) {
52
+ const messages = allMessages[rule.name] || [];
53
+ messages.forEach(m => context.report(m));
54
+ activeRules.set(node, activeRules.get(node) - 1);
55
+ allMessages[rule.name] = [];
56
+ },
57
+ };
58
+ return {
59
+ 'Program': (node) => node.isHbs && visitor.enter(node),
60
+ 'Program:exit': (node) => node.isHbs && visitor.exit(node),
61
+ 'GlimmerTemplate': visitor.enter,
62
+ 'GlimmerTemplate:exit': visitor.exit,
63
+ };
64
+ }
65
+ }
66
+
67
+ function createRules(rules) {
68
+ const created = rules.map(r => new Rule(r));
69
+ const map = {};
70
+ created.forEach(r => {
71
+ map[r.name] = r;
72
+ });
73
+ return map;
74
+ }
75
+
76
+ const configs = {
77
+ ...lintWithEslintConfigs.configs,
78
+ ...lintConfigs.configs
79
+ };
80
+
81
+ const rules = [...new Set([...lintConfigs.rules, ...lintWithEslintConfigs.rules])];
82
+
83
+ delete configs.recommended.overrides;
84
+
85
+ Object.values(configs).forEach((config) => {
86
+ const rules = {};
87
+ Object.entries(config.rules).forEach(([rule, conf]) => {
88
+ rules['ember-template-lint/' + rule] = conf;
89
+ });
90
+ config.rules = rules;
91
+ });
92
+
93
+ // enable all rules
94
+ const configuredRules = {};
95
+
96
+ for (const rule of rules) {
97
+ configuredRules['ember-template-lint/' + rule] = [];
98
+ configuredRules['ember-template-lint/' + rule].push(2);
99
+ configuredRules['ember-template-lint/' + rule].push({ __placeholder__: true });
100
+ }
101
+
102
+ Object.entries(lintConfigs.configuredRules).forEach(([name, conf]) => {
103
+ configuredRules['ember-template-lint/' + name] = [];
104
+ configuredRules['ember-template-lint/' + name].push(conf.severity);
105
+ if (typeof conf.config !== 'boolean') {
106
+ configuredRules['ember-template-lint/' + name].push(conf.config);
107
+ }
108
+ });
109
+
110
+ const configuredOverrides = [];
111
+
112
+ for (const configuredOverride of lintConfigs.configuredOverrides) {
113
+ const configuredRules = {};
114
+ Object.entries(configuredOverride.rules).forEach(([name, conf]) => {
115
+ configuredRules['ember-template-lint/' + name] = [];
116
+ configuredRules['ember-template-lint/' + name].push(conf.severity);
117
+ if (typeof conf.config !== 'boolean') {
118
+ configuredRules['ember-template-lint/' + name].push(conf.config);
119
+ }
120
+ });
121
+ configuredOverrides.push({
122
+ files: configuredOverride.files,
123
+ rules: configuredRules
124
+ });
125
+ }
126
+
127
+
128
+ module.exports = {
129
+ configs: configs,
130
+ rules: createRules(rules),
131
+ configuredRules,
132
+ configuredOverrides
133
+ };
@@ -0,0 +1,13 @@
1
+ const { runAsWorker } = require('synckit');
2
+
3
+ runAsWorker(async (options) => {
4
+ const Lint = await import('ember-template-lint');
5
+ const lint = new Lint.default(options);
6
+ await lint.loadConfig();
7
+ return {
8
+ configs: lint.config.loadedConfigurations,
9
+ rules: Object.keys(lint.config.loadedRules),
10
+ configuredRules: lint.config.rules,
11
+ configuredOverrides: lint.config.overrides,
12
+ };
13
+ });
package/lib/index.js CHANGED
@@ -8,10 +8,9 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const configs = require('./config');
12
- const base = require('./config/base');
11
+ const configs = require('./config-legacy');
12
+ const base = require('./config-legacy/base');
13
13
  const templateRules = require('./ember-template-lint/info');
14
- const processor = require('./processor');
15
14
 
16
15
  //------------------------------------------------------------------------------
17
16
  // Plugin Definition
@@ -25,8 +24,5 @@ module.exports = {
25
24
  configs: {
26
25
  base,
27
26
  ...configs
28
- },
29
- processors: {
30
- 'noop': processor,
31
27
  }
32
28
  };
@@ -0,0 +1,57 @@
1
+
2
+ class Scope {
3
+ type = 'global';
4
+ variables = [];
5
+ through= [];
6
+ set = new Map();
7
+ upper = null;
8
+ childScopes = [];
9
+ references = [];
10
+ block = null;
11
+ }
12
+
13
+ class ScopeManager {
14
+ globalScope = new Scope();
15
+ scopes = [this.globalScope];
16
+
17
+ acquire() {
18
+ return;
19
+ }
20
+
21
+ getDeclaredVariables() {
22
+ return [];
23
+ }
24
+ }
25
+
26
+ module.exports = {
27
+ parseForESLint(code) {
28
+ const comments = [];
29
+ const types = new Set(['Program']);
30
+ const ast = {};
31
+ ast.body = [];
32
+ ast.tokens = [ast];
33
+ ast.range = [0, code.length];
34
+ const lines = code.split('\n');
35
+ ast.loc = {
36
+ start: {
37
+ line: 1,
38
+ column: 1
39
+ },
40
+ end: {
41
+ line: lines.length,
42
+ column: lines[lines.length - 1].length
43
+ }
44
+ };
45
+ ast.comments = comments;
46
+ const visitorKeys = {};
47
+ types.forEach((t) => {
48
+ visitorKeys[t] = [];
49
+ });
50
+ ast.type = 'Program';
51
+ ast.isHbs = true;
52
+ ast.contents = code;
53
+ const scope = new ScopeManager();
54
+ scope.globalScope.block = ast;
55
+ return { ast, scopeManager: scope, visitorKeys };
56
+ }
57
+ };
@@ -113,12 +113,23 @@ async function _applyFixes(options, results, columnOffset) {
113
113
  return currentSource;
114
114
  }
115
115
 
116
- runAsWorker(async (filename, text, options, columnOffset=0) => {
116
+ async function runTemplateLint(filename, text, options, columnOffset=0) {
117
117
  const Lint = await import('ember-template-lint');
118
118
  const lint = new Lint.default(options);
119
119
  process.env.emberTemplateLintFileName = filename;
120
120
  process.env.emberTemplateLintFixMode = false;
121
121
  await lint.loadConfig();
122
+ const processedConf = lint.config;
123
+ delete options.config;
124
+ await lint.loadConfig();
125
+ lint.config.rules = {
126
+ ...lint.config.rules,
127
+ ...processedConf.rules
128
+ };
129
+ lint.config.overrides = [
130
+ ...lint.config.overrides,
131
+ ...processedConf.overrides
132
+ ];
122
133
  let fileConfig = lint._moduleStatusCache.getConfigForFile(options);
123
134
  if (fileConfig.loadedRules['prettier']) {
124
135
  const rule = fileConfig.loadedRules['prettier'].prototype;
@@ -131,14 +142,16 @@ runAsWorker(async (filename, text, options, columnOffset=0) => {
131
142
 
132
143
  const messages = await lint.verify({
133
144
  source: text,
134
- filePath: filename.replace('.gts', '.hbs')
145
+ filePath: filename,
135
146
  });
136
147
  process.env.emberTemplateLintFixMode = true;
137
148
  await _applyFixes.call(lint,{
138
149
  source: text,
139
- filePath: filename.replace('.gts', '.hbs'),
150
+ filePath: filename,
140
151
  }, messages, columnOffset);
141
152
  return {
142
153
  messages,
143
154
  };
144
- });
155
+ }
156
+
157
+ runAsWorker(runTemplateLint);
package/lib/rules/lint.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
  const synckit = require('synckit');
3
+ const path = require('path');
3
4
  const DocumentLines = require('../utils/document');
4
5
  const { templateLintConfig } = require('../ember-template-lint/config');
5
6
 
@@ -22,7 +23,13 @@ function runTemplateLint(node, context) {
22
23
 
23
24
  try {
24
25
  const syncFn = synckit.createSyncFn(require.resolve('./hbs-worker'));
25
- const response = syncFn(filename, text, { config: templateLintConfig }, columnOffset);
26
+ const config = JSON.parse(JSON.stringify(templateLintConfig));
27
+ for (const key of Object.keys(config.rules)) {
28
+ if (config.rules[key].__placeholder__) {
29
+ delete config.rules[key];
30
+ }
31
+ }
32
+ const response = syncFn(filename, text, { config, workingDir: path.dirname(filename) }, columnOffset);
26
33
  const lintMessages = response.messages;
27
34
  lintMessages.forEach((m) => {
28
35
  if (m.fix) {
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @typedef {{ line: number; character: number }} Position
3
+ */
4
+
5
+ // Helper class to convert line/column from and to offset
6
+ // taken and adapt from https://github.com/typed-ember/glint/blob/main/packages/core/src/language-server/util/position.ts
7
+ class DocumentLines {
8
+ /**
9
+ * @param {string} contents
10
+ */
11
+ constructor(contents) {
12
+ this.lineStarts = computeLineStarts(contents);
13
+ }
14
+
15
+ /**
16
+ * @param {Position} position
17
+ * @return {number}
18
+ */
19
+ positionToOffset(position) {
20
+ const { line, character } = position;
21
+ return this.lineStarts[line] + character;
22
+ }
23
+
24
+ /**
25
+ *
26
+ * @param {number} position
27
+ * @return {Position}
28
+ */
29
+ offsetToPosition(position) {
30
+ const lineStarts = this.lineStarts;
31
+ let line = 0;
32
+ while (line + 1 < lineStarts.length && lineStarts[line + 1] <= position) {
33
+ line++;
34
+ }
35
+ const character = position - lineStarts[line];
36
+ return { line, character };
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @returns {number[]}
42
+ * @param {string} text
43
+ */
44
+ function computeLineStarts(text) {
45
+ const result = [];
46
+ let pos = 0;
47
+ let lineStart = 0;
48
+ while (pos < text.length) {
49
+ const ch = text.codePointAt(pos);
50
+ pos++;
51
+ switch (ch) {
52
+ case 13 /* carriageReturn */: {
53
+ if (text.codePointAt(pos) === 10 /* lineFeed */) {
54
+ pos++;
55
+ }
56
+ }
57
+ // falls through
58
+ case 10 /* lineFeed */: {
59
+ result.push(lineStart);
60
+ lineStart = pos;
61
+ break;
62
+ }
63
+ default: {
64
+ if (ch > 127 /* maxAsciiCharacter */ && isLineBreak(ch)) {
65
+ result.push(lineStart);
66
+ lineStart = pos;
67
+ }
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ result.push(lineStart);
73
+ return result;
74
+ }
75
+
76
+ /* istanbul ignore next */
77
+ /**
78
+ * @param {number} ch
79
+ * @return {boolean}
80
+ */
81
+ function isLineBreak(ch) {
82
+ // ES5 7.3:
83
+ // The ECMAScript line terminator characters are listed in Table 3.
84
+ // Table 3: Line Terminator Characters
85
+ // Code Unit Value Name Formal Name
86
+ // \u000A Line Feed <LF>
87
+ // \u000D Carriage Return <CR>
88
+ // \u2028 Line separator <LS>
89
+ // \u2029 Paragraph separator <PS>
90
+ // Only the characters in Table 3 are treated as line terminators. Other new line or line
91
+ // breaking characters are treated as white space but not as line terminators.
92
+ return (
93
+ ch === 10 /* lineFeed */ ||
94
+ ch === 13 /* carriageReturn */ ||
95
+ ch === 8232 /* lineSeparator */ ||
96
+ ch === 8233 /* paragraphSeparator */
97
+ );
98
+ }
99
+
100
+ module.exports = DocumentLines;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-ember-template-lint",
3
- "version": "0.18.0",
3
+ "version": "0.21.0",
4
4
  "description": "Provide linting for ember template",
5
5
  "keywords": [
6
6
  "eslint",
@@ -10,20 +10,15 @@
10
10
  "author": "Patrick Pircher",
11
11
  "main": "lib/index.js",
12
12
  "repository": "patricklx/eslint-plugin-ember-template-lint",
13
- "scripts": {
14
- "test": "jest",
15
- "lint:js": "eslint --cache .",
16
- "test:watch": "jest --watchAll"
17
- },
18
13
  "dependencies": {
19
- "@glimmer/syntax": "^0.84.3",
20
- "@typescript-eslint/parser": "^5.59.7",
21
- "@typescript-eslint/typescript-estree": "^5.59.7",
14
+ "@glimmer/syntax": "^0.93.0",
15
+ "@typescript-eslint/parser": "^8.16.0",
16
+ "@typescript-eslint/typescript-estree": "^8.16.0",
22
17
  "ember-eslint-parser": "^0.5.6",
23
18
  "ember-template-recast": "^6.1.5",
24
19
  "ember-template-lint": "^6.0.0",
25
20
  "prettier-linter-helpers": "^1.0.0",
26
- "synckit": "^0.8.5",
21
+ "synckit": "^0.9.2",
27
22
  "typescript": "^5.0.4"
28
23
  },
29
24
  "peerDependencies": {
@@ -33,7 +28,7 @@
33
28
  "ember-template-lint-plugin-prettier": "^5.0.0",
34
29
  "eslint": "^8.41.0",
35
30
  "eslint-plugin-jest": "^27.2.1",
36
- "eslint-plugin-node": "^11.1.0",
31
+ "eslint-plugin-n": "^17.14.0",
37
32
  "jest": "^29.5.0"
38
33
  },
39
34
  "jest": {
@@ -46,6 +41,13 @@
46
41
  },
47
42
  "license": "ISC",
48
43
  "files": [
49
- "lib/rules/*.js"
50
- ]
51
- }
44
+ "lib/**/*.js"
45
+ ],
46
+ "scripts": {
47
+ "test": "pnpm run /test:.*/",
48
+ "test:jest": "jest",
49
+ "lint:js": "eslint --cache lib tests",
50
+ "lint:js:fix": "eslint --cache lib tests --fix",
51
+ "test-watch": "jest --watchAll"
52
+ }
53
+ }