Skip to content

Commit 4b89849

Browse files
Merge pull request #176 from ember-tooling/copilot/convert-library-to-esm
Convert library to ESM
2 parents f5cfc76 + 313d09a commit 4b89849

9 files changed

Lines changed: 215 additions & 218 deletions

File tree

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module.exports = {
22
env: {
33
browser: true,
4-
commonjs: true,
54
es2021: true,
65
},
76
extends: ['standard', 'plugin:prettier/recommended', 'plugin:n/recommended'],
@@ -10,24 +9,16 @@ module.exports = {
109
env: {
1110
node: true,
1211
},
13-
files: ['.eslintrc.{js,cjs}'],
12+
files: ['.eslintrc.cjs', '.prettierrc.cjs'],
1413
parserOptions: {
1514
sourceType: 'script',
1615
},
1716
},
18-
{
19-
env: {
20-
node: true,
21-
},
22-
files: ['tests/**/*.js'],
23-
parserOptions: {
24-
sourceType: 'module',
25-
},
26-
},
2717
],
2818
ignorePatterns: ['tests/fixtures/**/*.js'],
2919
parserOptions: {
3020
ecmaVersion: 'latest',
21+
sourceType: 'module',
3122
},
3223
rules: {},
3324
};
File renamed without changes.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"license": "ISC",
1111
"author": "",
12+
"type": "module",
1213
"exports": {
1314
".": "./src/parser/gjs-gts-parser.js",
1415
"./hbs": "./src/parser/hbs-parser.js",
@@ -23,7 +24,7 @@
2324
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
2425
"lint:js": "eslint . --max-warnings=0",
2526
"lint:js:fix": "eslint . --fix --max-warnings=0",
26-
"lint:package": "pnpm publint",
27+
"lint:package": "publint",
2728
"bench": "node --expose-gc tests/parser.bench.mjs",
2829
"bench:compare": "node scripts/bench-compare.mjs",
2930
"bench:summary": "./scripts/local-bench-summary.sh",

src/parser/gjs-gts-parser.js

Lines changed: 84 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
const tsconfigUtils = require('@typescript-eslint/tsconfig-utils');
2-
const babelParser = require('@babel/eslint-parser/experimental-worker');
3-
const { registerParsedFile } = require('../preprocessor/noop');
4-
const {
5-
patchTs,
6-
replaceExtensions,
7-
syncMtsGtsSourceFiles,
8-
typescriptParser,
9-
} = require('./ts-patch');
10-
const { transformForLint, preprocessGlimmerTemplates, convertAst } = require('./transforms');
1+
import { createRequire } from 'node:module';
2+
import tsconfigUtils from '@typescript-eslint/tsconfig-utils';
3+
import babelParser from '@babel/eslint-parser/experimental-worker';
4+
import { registerParsedFile } from '../preprocessor/noop.js';
5+
import { patchTs, replaceExtensions, syncMtsGtsSourceFiles, typescriptParser } from './ts-patch.js';
6+
import { transformForLint, preprocessGlimmerTemplates, convertAst } from './transforms.js';
7+
8+
const require = createRequire(import.meta.url);
119

1210
/**
1311
* implements https://eslint.org/docs/latest/extend/custom-parsers
@@ -131,86 +129,86 @@ function getAllowJs(options) {
131129
/**
132130
* @type {import('eslint').ParserModule}
133131
*/
134-
module.exports = {
135-
meta: {
136-
name: 'ember-eslint-parser',
137-
version: '*',
138-
},
139-
140-
parseForESLint(code, options) {
141-
const allowGjsWasSet = options.allowGjs !== undefined;
142-
const allowGjs = allowGjsWasSet ? options.allowGjs : getAllowJs(options);
143-
let actualAllowGjs;
144-
// Only patch TypeScript if we actually need it.
145-
if (options.programs || options.projectService || options.project) {
146-
({ allowGjs: actualAllowGjs } = patchTs({ allowGjs }));
147-
}
148-
registerParsedFile(options.filePath);
149-
let jsCode = code;
150-
const info = transformForLint(code, options.filePath);
151-
jsCode = info.output;
132+
export const meta = {
133+
name: 'ember-eslint-parser',
134+
version: '*',
135+
};
152136

153-
const isTypescript = options.filePath.endsWith('.gts') || options.filePath.endsWith('.ts');
154-
let useTypescript = true;
137+
export function parseForESLint(code, options) {
138+
const allowGjsWasSet = options.allowGjs !== undefined;
139+
const allowGjs = allowGjsWasSet ? options.allowGjs : getAllowJs(options);
140+
let actualAllowGjs;
141+
// Only patch TypeScript if we actually need it.
142+
if (options.programs || options.projectService || options.project) {
143+
({ allowGjs: actualAllowGjs } = patchTs({ allowGjs }));
144+
}
145+
registerParsedFile(options.filePath);
146+
let jsCode = code;
147+
const info = transformForLint(code, options.filePath);
148+
jsCode = info.output;
155149

156-
if (options.useBabel || !typescriptParser) {
157-
useTypescript = false;
158-
}
150+
const isTypescript = options.filePath.endsWith('.gts') || options.filePath.endsWith('.ts');
151+
let useTypescript = true;
159152

160-
let result = null;
161-
const filePath = options.filePath;
162-
if (options.project || options.projectService) {
163-
jsCode = replaceExtensions(jsCode);
164-
}
153+
if (options.useBabel || !typescriptParser) {
154+
useTypescript = false;
155+
}
165156

166-
if (isTypescript && !typescriptParser) {
167-
throw new Error('Please install typescript to process gts');
168-
}
157+
let result = null;
158+
const filePath = options.filePath;
159+
if (options.project || options.projectService) {
160+
jsCode = replaceExtensions(jsCode);
161+
}
169162

170-
try {
171-
result =
172-
isTypescript || useTypescript
173-
? typescriptParser.parseForESLint(jsCode, {
174-
...options,
175-
ranges: true,
176-
extraFileExtensions: ['.gts', '.gjs'],
177-
filePath,
178-
})
179-
: babelParser.parseForESLint(jsCode, {
180-
...options,
181-
requireConfigFile: false,
182-
ranges: true,
183-
});
184-
if (!info.templateInfos?.length) {
185-
return result;
186-
}
187-
const preprocessedResult = preprocessGlimmerTemplates(info, code);
188-
preprocessedResult.code = code;
189-
const { templateVisitorKeys } = preprocessedResult;
190-
const visitorKeys = { ...result.visitorKeys, ...templateVisitorKeys };
191-
result.isTypescript = isTypescript || useTypescript;
192-
convertAst(result, preprocessedResult, visitorKeys);
193-
if (result.services?.program) {
194-
// Compare allowJs with the actual program's compiler options
195-
const programAllowJs = result.services.program.getCompilerOptions?.()?.allowJs;
196-
if (
197-
!allowGjsWasSet &&
198-
programAllowJs !== undefined &&
199-
actualAllowGjs !== undefined &&
200-
actualAllowGjs !== programAllowJs
201-
) {
202-
// eslint-disable-next-line no-console
203-
console.warn(
204-
'[ember-eslint-parser] allowJs does not match the actual program. Consider setting allowGjs explicitly.\n' +
205-
` Current: ${allowGjs}, Program: ${programAllowJs}`
206-
);
207-
}
208-
syncMtsGtsSourceFiles(result.services.program);
163+
if (isTypescript && !typescriptParser) {
164+
throw new Error('Please install typescript to process gts');
165+
}
166+
167+
try {
168+
result =
169+
isTypescript || useTypescript
170+
? typescriptParser.parseForESLint(jsCode, {
171+
...options,
172+
ranges: true,
173+
extraFileExtensions: ['.gts', '.gjs'],
174+
filePath,
175+
})
176+
: babelParser.parseForESLint(jsCode, {
177+
...options,
178+
requireConfigFile: false,
179+
ranges: true,
180+
});
181+
if (!info.templateInfos?.length) {
182+
return result;
183+
}
184+
const preprocessedResult = preprocessGlimmerTemplates(info, code);
185+
preprocessedResult.code = code;
186+
const { templateVisitorKeys } = preprocessedResult;
187+
const visitorKeys = { ...result.visitorKeys, ...templateVisitorKeys };
188+
result.isTypescript = isTypescript || useTypescript;
189+
convertAst(result, preprocessedResult, visitorKeys);
190+
if (result.services?.program) {
191+
// Compare allowJs with the actual program's compiler options
192+
const programAllowJs = result.services.program.getCompilerOptions?.()?.allowJs;
193+
if (
194+
!allowGjsWasSet &&
195+
programAllowJs !== undefined &&
196+
actualAllowGjs !== undefined &&
197+
actualAllowGjs !== programAllowJs
198+
) {
199+
// eslint-disable-next-line no-console
200+
console.warn(
201+
'[ember-eslint-parser] allowJs does not match the actual program. Consider setting allowGjs explicitly.\n' +
202+
` Current: ${allowGjs}, Program: ${programAllowJs}`
203+
);
209204
}
210-
return { ...result, visitorKeys };
211-
} catch (e) {
212-
console.error(e);
213-
throw e;
205+
syncMtsGtsSourceFiles(result.services.program);
214206
}
215-
},
216-
};
207+
return { ...result, visitorKeys };
208+
} catch (e) {
209+
console.error(e);
210+
throw e;
211+
}
212+
}
213+
214+
export default { meta, parseForESLint };

src/parser/hbs-parser.js

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const eslintScope = require('eslint-scope');
2-
const DocumentLines = require('../utils/document');
3-
const { processGlimmerTemplate, buildGlimmerVisitorKeys } = require('./transforms');
1+
import * as eslintScope from 'eslint-scope';
2+
import DocumentLines from '../utils/document.js';
3+
import { processGlimmerTemplate, buildGlimmerVisitorKeys } from './transforms.js';
44

55
// Constant: Program + all Glimmer node types. Computed once at module load.
66
const hbsVisitorKeys = { Program: ['body'], ...buildGlimmerVisitorKeys() };
@@ -17,76 +17,76 @@ const hbsVisitorKeys = { Program: ['body'], ...buildGlimmerVisitorKeys() };
1717
/**
1818
* @type {import('eslint').ParserModule}
1919
*/
20-
module.exports = {
21-
meta: {
22-
name: 'ember-eslint-parser/hbs',
23-
version: '*',
24-
},
20+
export const meta = {
21+
name: 'ember-eslint-parser/hbs',
22+
version: '*',
23+
};
2524

26-
parseForESLint(code, options) {
27-
const filePath = (options && options.filePath) || '<hbs>';
28-
const codeLines = new DocumentLines(code);
25+
export function parseForESLint(code, options) {
26+
const filePath = (options && options.filePath) || '<hbs>';
27+
const codeLines = new DocumentLines(code);
2928

30-
let result;
31-
try {
32-
result = processGlimmerTemplate({
33-
templateContent: code,
34-
codeLines,
35-
templateRange: [0, code.length],
29+
let result;
30+
try {
31+
result = processGlimmerTemplate({
32+
templateContent: code,
33+
codeLines,
34+
templateRange: [0, code.length],
35+
});
36+
} catch (e) {
37+
// Transform glimmer parse error to ESLint-compatible error
38+
const loc = e.location || (e.hash && e.hash.loc);
39+
if (loc && loc.start) {
40+
const err = Object.assign(new SyntaxError(e.message), {
41+
lineNumber: loc.start.line,
42+
column: loc.start.column,
43+
index: codeLines.positionToOffset(loc.start),
44+
fileName: filePath,
3645
});
37-
} catch (e) {
38-
// Transform glimmer parse error to ESLint-compatible error
39-
const loc = e.location || (e.hash && e.hash.loc);
40-
if (loc && loc.start) {
41-
const err = Object.assign(new SyntaxError(e.message), {
42-
lineNumber: loc.start.line,
43-
column: loc.start.column,
44-
index: codeLines.positionToOffset(loc.start),
45-
fileName: filePath,
46-
});
47-
throw err;
48-
}
49-
throw e;
46+
throw err;
5047
}
48+
throw e;
49+
}
50+
51+
const { ast: templateNode, comments } = result;
52+
53+
// Wrap in a synthetic Program node (required by ESLint)
54+
const program = {
55+
type: 'Program',
56+
body: [templateNode],
57+
tokens: templateNode.tokens,
58+
comments,
59+
range: [0, code.length],
60+
start: 0,
61+
end: code.length,
62+
loc: {
63+
start: { line: 1, column: 0 },
64+
end: codeLines.offsetToPosition(code.length),
65+
},
66+
};
5167

52-
const { ast: templateNode, comments } = result;
68+
// Build visitor keys: Program + all Glimmer node types
69+
const visitorKeys = hbsVisitorKeys;
5370

54-
// Wrap in a synthetic Program node (required by ESLint)
55-
const program = {
71+
// Create an empty scope manager.
72+
// For HBS, all locals are assumed to be defined at runtime,
73+
// so we don't track variable references (no no-undef errors).
74+
const scopeManager = eslintScope.analyze(
75+
{
5676
type: 'Program',
57-
body: [templateNode],
58-
tokens: templateNode.tokens,
59-
comments,
77+
body: [],
6078
range: [0, code.length],
61-
start: 0,
62-
end: code.length,
63-
loc: {
64-
start: { line: 1, column: 0 },
65-
end: codeLines.offsetToPosition(code.length),
66-
},
67-
};
79+
loc: program.loc,
80+
},
81+
{ range: true }
82+
);
6883

69-
// Build visitor keys: Program + all Glimmer node types
70-
const visitorKeys = hbsVisitorKeys;
84+
return {
85+
ast: program,
86+
scopeManager,
87+
visitorKeys,
88+
services: {},
89+
};
90+
}
7191

72-
// Create an empty scope manager.
73-
// For HBS, all locals are assumed to be defined at runtime,
74-
// so we don't track variable references (no no-undef errors).
75-
const scopeManager = eslintScope.analyze(
76-
{
77-
type: 'Program',
78-
body: [],
79-
range: [0, code.length],
80-
loc: program.loc,
81-
},
82-
{ range: true }
83-
);
84-
85-
return {
86-
ast: program,
87-
scopeManager,
88-
visitorKeys,
89-
services: {},
90-
};
91-
},
92-
};
92+
export default { meta, parseForESLint };

0 commit comments

Comments
 (0)