eslint-plugin-ember-template-lint 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,6 +12,7 @@ const allMessages = {};
12
12
  const reporter = {
13
13
  setup(context) {
14
14
  this.getSourceCode = context.getSourceCode;
15
+ this.getPhysicalFilename = context.getPhysicalFilename;
15
16
  },
16
17
  report(message) {
17
18
  message.meta = {
@@ -42,14 +43,8 @@ class Rule {
42
43
  enter(node) {
43
44
  if (!activeRules.get(node)) {
44
45
  activeRules.set(node, 0);
45
- const sourceCode = context.getSourceCode();
46
- const { scopeManager } = sourceCode;
47
- const scopes = scopeManager.scopes.filter(x => x.block.range[0] < node.range[0] && x.block.range[1] > node.range[1]);
48
- const scopeVars = scopes.map(s => s.variables.map(x => x.name)).flat();
49
- const filename = context.getPhysicalFilename();
50
- const start = node.range[0] || 0;
51
46
  reporter.setup(context);
52
- runTemplateLint(node.contents, filename, reporter, scopeVars, start);
47
+ runTemplateLint(node, reporter);
53
48
  }
54
49
  activeRules.set(node, activeRules.get(node) + 1);
55
50
  },
@@ -108,4 +103,4 @@ module.exports = {
108
103
  configs: configs,
109
104
  rules: createRules(rules),
110
105
  configuredRules: configuredRules
111
- };
106
+ };
@@ -1,34 +1,12 @@
1
- const { preprocess, traverse, Source } = require('@glimmer/syntax');
2
1
  const gts = require('ember-template-imports');
3
2
  const typescriptParser = require('@typescript-eslint/parser');
4
3
  const typescriptEstree = require('@typescript-eslint/typescript-estree');
5
4
 
6
- function visitorKeysForAst(source, ast) {
7
- const tokens = [];
8
- const types = new Set();
9
- traverse(ast, {
10
- All(node) {
11
- types.add(`__TEMPLATE__${node.type}`);
12
- tokens.push(node);
13
- }
14
- });
15
- ast.tokens = tokens;
16
- const visitorKeys = {};
17
- types.forEach((t) => {
18
- // visitorKeys[t] = ['body', 'name', 'path', 'params', 'attributes', 'hash', 'modifiers', 'comments', 'value', 'program', 'inverse', 'children']
19
- visitorKeys[t] = [];
20
- });
21
- tokens.forEach((node) => {
22
- node.type = `__TEMPLATE__${node.type}`;
23
- });
24
- return visitorKeys;
25
- }
26
5
 
27
6
  function replaceRange(s, start, end, substitute) {
28
7
  return s.substring(0, start) + substitute + s.substring(end);
29
8
  }
30
9
 
31
-
32
10
  module.exports = {
33
11
  parseForESLint(code, options) {
34
12
  let jsCode = code;
@@ -43,14 +21,15 @@ module.exports = {
43
21
  });
44
22
  const emptyText = '[`' + lines.join('\n').slice(4) + '`]';
45
23
  jsCode = replaceRange(jsCode, ...range, emptyText);
46
- const source = new Source(tpl.contents);
47
- const ast = preprocess(source, { mode: 'codemod' });
48
- ast.range = [tpl.start.index + tpl.start[0].length + 1, tpl.end.index];
49
- ast.contents = tpl.contents;
24
+ const ast = {
25
+ type: '__TEMPLATE__Template',
26
+ };
27
+ ast.range = range;
28
+ ast.contents = template;
50
29
  tpl.ast = ast;
51
30
  });
52
31
  const result = typescriptParser.parseForESLint(jsCode, options);
53
- const visitorKeys = {...result.visitorKeys};
32
+ const visitorKeys = {...result.visitorKeys, '__TEMPLATE__Template': []};
54
33
  result.ast.tokens.forEach((token) => {
55
34
  if (token.type === 'Template') {
56
35
  const range = [token.range[0] - 1, token.range[1] + 1];
@@ -58,8 +37,6 @@ module.exports = {
58
37
  if (!template) return;
59
38
  const ast = template.ast;
60
39
  ast.loc = token.loc;
61
- const source = new Source(code);
62
- Object.assign(visitorKeys, visitorKeysForAst(source, ast));
63
40
  Object.assign(token, ast);
64
41
  }
65
42
  });
@@ -1,4 +1,3 @@
1
- const { preprocess, traverse, Source } = require('@glimmer/syntax');
2
1
 
3
2
  class Scope {
4
3
  type = 'global';
@@ -26,25 +25,22 @@ class ScopeManager {
26
25
 
27
26
  module.exports = {
28
27
  parseForESLint(code) {
29
- const source = new Source(code);
30
- const ast = preprocess(source, { mode: 'codemod' });
31
- const tokens = [];
32
28
  const comments = [];
33
- const types = new Set();
34
- types.add('Program');
35
- traverse(ast, {
36
- All(node) {
37
- types.add(`__TEMPLATE__${node.type}`);
38
- const span = source.spanFor(node.loc);
39
- node.range = [span.getStart().offset, span.getEnd().offset];
40
- if (node.type.toLowerCase().includes('comment')) {
41
- comments.push(node);
42
- return;
43
- }
44
- tokens.push(node);
29
+ const types = new Set(['Program']);
30
+ const ast = {};
31
+ ast.tokens = [ast];
32
+ ast.range = [0, code.length];
33
+ const lines = code.split('\n');
34
+ ast.loc = {
35
+ start: {
36
+ line: 1,
37
+ column: 1
38
+ },
39
+ end: {
40
+ line: lines.length,
41
+ column: lines[lines.length - 1].length
45
42
  }
46
- });
47
- ast.tokens = tokens;
43
+ };
48
44
  ast.comments = comments;
49
45
  const visitorKeys = {};
50
46
  types.forEach((t) => {
@@ -53,9 +49,6 @@ module.exports = {
53
49
  ast.type = 'Program';
54
50
  ast.isHbs = true;
55
51
  ast.contents = code;
56
- tokens.slice(1).forEach((node) => {
57
- node.type = `__TEMPLATE__${node.type}`;
58
- });
59
52
  const scope = new ScopeManager();
60
53
  scope.globalScope.block = ast;
61
54
  return { ast, scopeManager: scope, visitorKeys };
@@ -1,7 +1,7 @@
1
1
  const { runAsWorker } = require('synckit');
2
2
  const { generateDifferences } = require('prettier-linter-helpers');
3
3
 
4
- async function _applyFixes(options, results) {
4
+ async function _applyFixes(options, results, columnOffset) {
5
5
  const { transform } = await import('ember-template-recast');
6
6
  let currentSource = options.source;
7
7
  let fixableIssues = results.filter((r) => r.isFixable);
@@ -14,6 +14,7 @@ async function _applyFixes(options, results) {
14
14
  let fileConfig = this._moduleStatusCache.getConfigForFile(options);
15
15
 
16
16
  let ruleNames = new Set(fixableIssues.map((r) => r.rule));
17
+ const spaces = ' '.repeat(columnOffset);
17
18
 
18
19
  for (let ruleName of ruleNames) {
19
20
  let templateInfos = [{
@@ -37,7 +38,14 @@ async function _applyFixes(options, results) {
37
38
  let { code } = transform(templateInfo.template, () => visitor);
38
39
 
39
40
  if (code !== templateInfo.template) {
40
- const diffs = generateDifferences(templateInfo.template, code);
41
+ let template = templateInfo.template;
42
+ if (columnOffset) {
43
+ template = spaces + template.split('\n').join('\n' + spaces);
44
+ }
45
+ if (columnOffset) {
46
+ code = spaces + code.split('\n').join('\n' + spaces);
47
+ }
48
+ const diffs = generateDifferences(template, code);
41
49
  fixableIssues.filter(r => r.rule === ruleName).forEach((r, i) => {
42
50
  r.fix = diffs[i];
43
51
  });
@@ -48,11 +56,12 @@ async function _applyFixes(options, results) {
48
56
  return currentSource;
49
57
  }
50
58
 
51
- runAsWorker(async (filename, text, options) => {
59
+ runAsWorker(async (filename, text, options, columnOffset=0) => {
52
60
  const Lint = await import('ember-template-lint');
53
61
  const lint = new Lint.default(options);
54
62
  process.env.emberTemplateLintFileName = filename;
55
63
  process.env.emberTemplateLintFixMode = false;
64
+
56
65
  const messages = await lint.verify({
57
66
  source: text,
58
67
  filePath: filename.replace('.gts', '.hbs')
@@ -60,8 +69,8 @@ runAsWorker(async (filename, text, options) => {
60
69
  process.env.emberTemplateLintFixMode = true;
61
70
  await _applyFixes.call(lint,{
62
71
  source: text,
63
- filePath: filename.replace('.gts', '.hbs')
64
- }, messages);
72
+ filePath: filename.replace('.gts', '.hbs'),
73
+ }, messages, columnOffset);
65
74
  return {
66
75
  messages,
67
76
  };
package/lib/rules/lint.js CHANGED
@@ -3,54 +3,54 @@ const synckit = require('synckit');
3
3
  const DocumentLines = require('../utils/document');
4
4
  const { templateLintConfig } = require('../ember-teplate-lint/config');
5
5
 
6
- function runTemplateLint(text, filename, context, scopeVars=[], offset=0) {
7
- const originalDocument = new DocumentLines(text);
8
- try {
9
- let columnOffset = 0;
10
- let lineOffset = 0;
11
- if (text.startsWith('\n')) {
12
- text = text.slice(1);
13
- lineOffset += 1;
14
- }
15
- columnOffset = text.match(/\s+/)[0].length;
6
+ function runTemplateLint(node, context) {
7
+ const sourceCode = context.getSourceCode();
8
+ const { scopeManager, text: sourceCodeText } = sourceCode;
9
+ const scopes = scopeManager.scopes.filter(x => x.block.range[0] < node.range[0] && x.block.range[1] > node.range[1]);
10
+ const scopeVars = scopes.map(s => s.variables.map(x => x.name)).flat();
11
+ const filename = context.getPhysicalFilename();
12
+ let text = node.contents;
13
+ const initialLine = sourceCodeText.split('\n')[node.loc.start.line-1];
14
+ const columnOffset = initialLine.match(/^\s+/)?.[0].length || 0;
15
+ const offset = (node.range[0] || 0);
16
+ const spaces = ' '.repeat(columnOffset);
17
+ const originalDocument = new DocumentLines(spaces + text);
18
+ if (columnOffset) {
16
19
  text = text.split('\n').map(l => l.replace(new RegExp(`^\\s{1,${columnOffset}}`), '')).join('\n');
17
20
  text = text.trim();
21
+ }
22
+
23
+ try {
18
24
  const syncFn = synckit.createSyncFn(require.resolve('./hbs-worker'));
19
- const response = syncFn(filename, text, { config: templateLintConfig });
25
+ const response = syncFn(filename, text, { config: templateLintConfig }, columnOffset);
20
26
  const lintMessages = response.messages;
21
- const document = new DocumentLines(text);
22
27
  lintMessages.forEach((m) => {
23
28
  if (m.fix) {
24
29
  const d = m.fix;
25
- if (d.insertText) {
26
- d.insertText = d.insertText.replace(/\n/g, '\n' + ' '.repeat(columnOffset));
27
- }
28
30
  m.fix.range = [d.offset, d.offset + (d.deleteText?.length || 0)];
29
31
  m.range = m.fix.range;
30
- const start = document.offsetToPosition(m.fix.range[0]);
31
- const end = document.offsetToPosition(m.fix.range[1]);
32
+ const start = originalDocument.offsetToPosition(m.fix.range[0]);
33
+ const end = originalDocument.offsetToPosition(m.fix.range[1]);
32
34
  m.fix.range = [
33
35
  originalDocument.positionToOffset({
34
- line: start.line + lineOffset,
35
- character: start.character + columnOffset - 1
36
+ line: start.line,
37
+ character: start.character
36
38
  }),
37
39
  originalDocument.positionToOffset({
38
- line: end.line + lineOffset,
39
- character: end.character + columnOffset - 1
40
+ line: end.line,
41
+ character: end.character
40
42
  })
41
43
  ];
42
44
  m.fix.range = m.fix.range.map(x => offset + x);
43
- m.range = m.fix.range;
44
- return;
45
45
  }
46
46
  m.range = [
47
47
  originalDocument.positionToOffset({
48
- line: m.line - 1 + lineOffset,
49
- character: m.column + columnOffset - 1
48
+ line: m.line - 1,
49
+ character: m.column
50
50
  }),
51
51
  originalDocument.positionToOffset({
52
- line: m.endLine - 1 + lineOffset,
53
- character: m.endColumn + columnOffset - 1
52
+ line: m.endLine - 1,
53
+ character: m.endColumn
54
54
  })];
55
55
  m.range = m.range.map(x => offset + x);
56
56
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-ember-template-lint",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Provide linting for ember template",
5
5
  "keywords": [
6
6
  "eslint",