astro-eslint-parser 0.12.0 → 0.13.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.
package/lib/index.d.ts CHANGED
@@ -41,6 +41,7 @@ declare class Context {
41
41
  }
42
42
  declare class LinesAndColumns {
43
43
  private readonly lineStartIndices;
44
+ private readonly code;
44
45
  private readonly normalizedLineFeed;
45
46
  constructor(origCode: string);
46
47
  getLocFromIndex(index: number): {
package/lib/index.js CHANGED
@@ -64,55 +64,327 @@ var debug = (0, import_debug.default)("astro-eslint-parser");
64
64
 
65
65
  // src/parser/ts-patch.ts
66
66
  var import_module = require("module");
67
- var import_path = __toESM(require("path"));
67
+ var import_path3 = __toESM(require("path"));
68
+ var import_fs2 = __toESM(require("fs"));
69
+ var import_semver = require("semver");
70
+
71
+ // src/parser/ts-for-v5/get-project-config-files.ts
68
72
  var import_fs = __toESM(require("fs"));
69
- function tsPatch(scriptParserOptions) {
73
+ var import_path = __toESM(require("path"));
74
+ function getProjectConfigFiles(options) {
75
+ const tsconfigRootDir = typeof options.tsconfigRootDir === "string" ? options.tsconfigRootDir : process.cwd();
76
+ if (options.project !== true) {
77
+ return Array.isArray(options.project) ? options.project : [options.project];
78
+ }
79
+ let directory = import_path.default.dirname(options.filePath);
80
+ const checkedDirectories = [directory];
81
+ do {
82
+ const tsconfigPath = import_path.default.join(directory, "tsconfig.json");
83
+ if (import_fs.default.existsSync(tsconfigPath)) {
84
+ return [tsconfigPath];
85
+ }
86
+ directory = import_path.default.dirname(directory);
87
+ checkedDirectories.push(directory);
88
+ } while (directory.length > 1 && directory.length >= tsconfigRootDir.length);
89
+ throw new Error(
90
+ `project was set to \`true\` but couldn't find any tsconfig.json relative to '${options.filePath}' within '${tsconfigRootDir}'.`
91
+ );
92
+ }
93
+
94
+ // src/parser/ts-for-v5/programs.ts
95
+ var import_path2 = __toESM(require("path"));
96
+ var tsServices = /* @__PURE__ */ new Map();
97
+ function getTSProgram(code, options, ts) {
98
+ const tsconfigPath = options.project;
99
+ let service2 = tsServices.get(tsconfigPath);
100
+ if (!service2) {
101
+ service2 = new TSService(tsconfigPath, ts);
102
+ tsServices.set(tsconfigPath, service2);
103
+ }
104
+ return service2.getProgram(code, options.filePath);
105
+ }
106
+ var TSService = class {
107
+ constructor(tsconfigPath, ts) {
108
+ this.patchedHostSet = /* @__PURE__ */ new WeakSet();
109
+ this.currTarget = {
110
+ code: "",
111
+ filePath: ""
112
+ };
113
+ this.fileWatchCallbacks = /* @__PURE__ */ new Map();
114
+ this.ts = ts;
115
+ this.watch = this.createWatch(tsconfigPath);
116
+ }
117
+ getProgram(code, filePath) {
118
+ const normalized = normalizeFileName(this.ts, filePath);
119
+ const lastTarget = this.currTarget;
120
+ this.currTarget = {
121
+ code,
122
+ filePath: normalized
123
+ };
124
+ for (const { filePath: targetPath } of [this.currTarget, lastTarget]) {
125
+ if (!targetPath)
126
+ continue;
127
+ this.fileWatchCallbacks.get(targetPath)?.update();
128
+ }
129
+ const program = this.watch.getProgram().getProgram();
130
+ program.getTypeChecker();
131
+ return program;
132
+ }
133
+ createWatch(tsconfigPath) {
134
+ const { ts } = this;
135
+ const watchCompilerHost = ts.createWatchCompilerHost(
136
+ tsconfigPath,
137
+ {
138
+ noEmit: true,
139
+ jsx: ts.JsxEmit.Preserve,
140
+ // This option is required if `includes` only includes `*.astro` files.
141
+ // However, the option is not in the documentation.
142
+ // https://github.com/microsoft/TypeScript/issues/28447
143
+ allowNonTsExtensions: true
144
+ },
145
+ ts.sys,
146
+ (rootNames, options, argHost, oldProgram, configFileParsingDiagnostics, projectReferences) => {
147
+ const host = argHost;
148
+ if (!this.patchedHostSet.has(host)) {
149
+ this.patchedHostSet.add(host);
150
+ const getTargetSourceFile = (fileName, languageVersionOrOptions) => {
151
+ var _a;
152
+ if (this.currTarget.filePath === normalizeFileName(ts, fileName)) {
153
+ return (_a = this.currTarget).sourceFile ?? (_a.sourceFile = ts.createSourceFile(
154
+ this.currTarget.filePath,
155
+ this.currTarget.code,
156
+ languageVersionOrOptions,
157
+ true,
158
+ ts.ScriptKind.TSX
159
+ ));
160
+ }
161
+ return null;
162
+ };
163
+ const original2 = {
164
+ getSourceFile: host.getSourceFile,
165
+ getSourceFileByPath: host.getSourceFileByPath
166
+ };
167
+ host.getSourceFile = (fileName, languageVersionOrOptions, ...args) => {
168
+ return getTargetSourceFile(fileName, languageVersionOrOptions) ?? original2.getSourceFile.call(
169
+ host,
170
+ fileName,
171
+ languageVersionOrOptions,
172
+ ...args
173
+ );
174
+ };
175
+ host.getSourceFileByPath = (fileName, path6, languageVersionOrOptions, ...args) => {
176
+ return getTargetSourceFile(fileName, languageVersionOrOptions) ?? original2.getSourceFileByPath.call(
177
+ host,
178
+ fileName,
179
+ path6,
180
+ languageVersionOrOptions,
181
+ ...args
182
+ );
183
+ };
184
+ }
185
+ return ts.createAbstractBuilder(
186
+ rootNames,
187
+ options,
188
+ host,
189
+ oldProgram,
190
+ configFileParsingDiagnostics,
191
+ projectReferences
192
+ );
193
+ },
194
+ (diagnostic) => {
195
+ throw new Error(formatDiagnostics(ts, [diagnostic]));
196
+ },
197
+ () => {
198
+ },
199
+ void 0,
200
+ [
201
+ {
202
+ extension: ".astro",
203
+ isMixedContent: true,
204
+ scriptKind: ts.ScriptKind.Deferred
205
+ }
206
+ ]
207
+ );
208
+ const original = {
209
+ readFile: watchCompilerHost.readFile
210
+ };
211
+ watchCompilerHost.readFile = (fileName, ...args) => {
212
+ const normalized = normalizeFileName(ts, fileName);
213
+ if (this.currTarget.filePath === normalized) {
214
+ return this.currTarget.code;
215
+ }
216
+ return original.readFile.call(watchCompilerHost, fileName, ...args);
217
+ };
218
+ watchCompilerHost.watchFile = (fileName, callback) => {
219
+ const normalized = normalizeFileName(ts, fileName);
220
+ this.fileWatchCallbacks.set(normalized, {
221
+ update: () => callback(fileName, ts.FileWatcherEventKind.Changed)
222
+ });
223
+ return {
224
+ close: () => {
225
+ this.fileWatchCallbacks.delete(normalized);
226
+ }
227
+ };
228
+ };
229
+ watchCompilerHost.watchDirectory = () => {
230
+ return {
231
+ close: () => {
232
+ }
233
+ };
234
+ };
235
+ watchCompilerHost.afterProgramCreate = (program) => {
236
+ const originalDiagnostics = program.getConfigFileParsingDiagnostics();
237
+ const configFileDiagnostics = originalDiagnostics.filter(
238
+ (diag) => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18003
239
+ );
240
+ if (configFileDiagnostics.length > 0) {
241
+ throw new Error(formatDiagnostics(ts, configFileDiagnostics));
242
+ }
243
+ };
244
+ const watch = ts.createWatchProgram(watchCompilerHost);
245
+ return watch;
246
+ }
247
+ };
248
+ function formatDiagnostics(ts, diagnostics) {
249
+ return ts.formatDiagnostics(diagnostics, {
250
+ getCanonicalFileName: (f) => f,
251
+ getCurrentDirectory: () => process.cwd(),
252
+ getNewLine: () => "\n"
253
+ });
254
+ }
255
+ function normalizeFileName(ts, fileName) {
256
+ let normalized = import_path2.default.normalize(fileName);
257
+ if (normalized.endsWith(import_path2.default.sep)) {
258
+ normalized = normalized.slice(0, -1);
259
+ }
260
+ if (ts.sys.useCaseSensitiveFileNames) {
261
+ return toAbsolutePath(normalized, null);
262
+ }
263
+ return toAbsolutePath(normalized.toLowerCase(), null);
264
+ }
265
+ function toAbsolutePath(filePath, baseDir) {
266
+ return import_path2.default.isAbsolute(filePath) ? filePath : import_path2.default.join(baseDir || process.cwd(), filePath);
267
+ }
268
+
269
+ // src/parser/ts-for-v5/parse-tsx-for-typescript.ts
270
+ var DEFAULT_EXTRA_FILE_EXTENSIONS = [".vue", ".svelte", ".astro"];
271
+ function parseTsxForTypeScript(code, options, tsEslintParser, ts) {
272
+ const programs = [];
273
+ for (const option of iterateOptions(options)) {
274
+ programs.push(getTSProgram(code, option, ts));
275
+ }
276
+ const parserOptions = {
277
+ ...options,
278
+ programs
279
+ };
280
+ return tsEslintParser.parseForESLint(code, parserOptions);
281
+ }
282
+ function* iterateOptions(options) {
283
+ if (!options) {
284
+ throw new Error("`parserOptions` is required.");
285
+ }
286
+ if (!options.filePath) {
287
+ throw new Error("`filePath` is required.");
288
+ }
289
+ if (!options.project) {
290
+ throw new Error(
291
+ "Specify `parserOptions.project`. Otherwise there is no point in using this parser."
292
+ );
293
+ }
294
+ for (const project of getProjectConfigFiles(options)) {
295
+ yield {
296
+ project,
297
+ filePath: options.filePath,
298
+ extraFileExtensions: options.extraFileExtensions || DEFAULT_EXTRA_FILE_EXTENSIONS
299
+ };
300
+ }
301
+ }
302
+
303
+ // src/parser/ts-patch.ts
304
+ function tsPatch(scriptParserOptions, tsParserName) {
305
+ if (tsParserName === "typescript-eslint-parser-for-extra-files") {
306
+ return {
307
+ terminate() {
308
+ }
309
+ };
310
+ }
70
311
  let targetExt = ".astro";
71
312
  if (scriptParserOptions.filePath) {
72
- const ext = import_path.default.extname(scriptParserOptions.filePath);
313
+ const ext = import_path3.default.extname(scriptParserOptions.filePath);
73
314
  if (ext) {
74
315
  targetExt = ext;
75
316
  }
76
317
  }
77
318
  try {
78
319
  const cwd = process.cwd();
79
- const relativeTo = import_path.default.join(cwd, "__placeholder__.js");
320
+ const relativeTo = import_path3.default.join(cwd, "__placeholder__.js");
80
321
  const ts = (0, import_module.createRequire)(relativeTo)("typescript");
81
- const { ensureScriptKind, getScriptKindFromFileName } = ts;
82
- if (typeof ensureScriptKind === "function" && typeof getScriptKindFromFileName === "function") {
83
- ts.ensureScriptKind = function(fileName, ...args) {
84
- if (fileName.endsWith(targetExt)) {
85
- return ts.ScriptKind.TSX;
86
- }
87
- return ensureScriptKind.call(this, fileName, ...args);
88
- };
89
- ts.getScriptKindFromFileName = function(fileName, ...args) {
90
- if (fileName.endsWith(targetExt)) {
91
- return ts.ScriptKind.TSX;
92
- }
93
- return getScriptKindFromFileName.call(this, fileName, ...args);
94
- };
95
- return {
96
- terminate() {
97
- ts.ensureScriptKind = ensureScriptKind;
98
- ts.getScriptKindFromFileName = getScriptKindFromFileName;
99
- }
100
- };
322
+ if ((0, import_semver.satisfies)(ts.version, ">=5")) {
323
+ const result = tsPatchForV5(ts, scriptParserOptions);
324
+ if (result) {
325
+ return result;
326
+ }
327
+ } else {
328
+ const result = tsPatchForV4(ts, targetExt);
329
+ if (result) {
330
+ return result;
331
+ }
101
332
  }
102
333
  } catch {
103
334
  }
104
335
  const tsxFilePath = `${scriptParserOptions.filePath}.tsx`;
105
336
  scriptParserOptions.filePath = tsxFilePath;
106
- if (!import_fs.default.existsSync(tsxFilePath)) {
107
- import_fs.default.writeFileSync(tsxFilePath, "/* temp for astro-eslint-parser */");
337
+ if (!import_fs2.default.existsSync(tsxFilePath)) {
338
+ import_fs2.default.writeFileSync(tsxFilePath, "/* temp for astro-eslint-parser */");
108
339
  return {
109
340
  terminate() {
110
- import_fs.default.unlinkSync(tsxFilePath);
341
+ import_fs2.default.unlinkSync(tsxFilePath);
111
342
  }
112
343
  };
113
344
  }
114
345
  return null;
115
346
  }
347
+ function tsPatchForV5(ts, scriptParserOptions) {
348
+ return {
349
+ terminate() {
350
+ },
351
+ parse(code, parser) {
352
+ return parseTsxForTypeScript(
353
+ code,
354
+ scriptParserOptions,
355
+ parser,
356
+ ts
357
+ );
358
+ }
359
+ };
360
+ }
361
+ function tsPatchForV4(ts, targetExt) {
362
+ const { ensureScriptKind, getScriptKindFromFileName } = ts;
363
+ if (typeof ensureScriptKind !== "function" || typeof getScriptKindFromFileName !== "function") {
364
+ return null;
365
+ }
366
+ ts.ensureScriptKind = function(fileName, ...args) {
367
+ if (fileName.endsWith(targetExt)) {
368
+ return ts.ScriptKind.TSX;
369
+ }
370
+ return ensureScriptKind.call(this, fileName, ...args);
371
+ };
372
+ ts.getScriptKindFromFileName = function(fileName, ...args) {
373
+ if (fileName.endsWith(targetExt)) {
374
+ return ts.ScriptKind.TSX;
375
+ }
376
+ return getScriptKindFromFileName.call(this, fileName, ...args);
377
+ };
378
+ if (ts.ensureScriptKind === ensureScriptKind || ts.getScriptKindFromFileName === getScriptKindFromFileName) {
379
+ return null;
380
+ }
381
+ return {
382
+ terminate() {
383
+ ts.ensureScriptKind = ensureScriptKind;
384
+ ts.getScriptKindFromFileName = getScriptKindFromFileName;
385
+ }
386
+ };
387
+ }
116
388
 
117
389
  // src/context/resolve-parser/parser-object.ts
118
390
  function isParserObject(value) {
@@ -127,9 +399,23 @@ function isBasicParserObject(value) {
127
399
  function maybeTSESLintParserObject(value) {
128
400
  return isEnhancedParserObject(value) && isBasicParserObject(value) && typeof value.createProgram === "function" && typeof value.clearCaches === "function" && typeof value.version === "string";
129
401
  }
402
+ function getTSParserNameFromObject(value) {
403
+ if (!isEnhancedParserObject(value)) {
404
+ return null;
405
+ }
406
+ if (value.name === "typescript-eslint-parser-for-extra-files")
407
+ return "typescript-eslint-parser-for-extra-files";
408
+ if (value.meta?.name === "typescript-eslint/parser")
409
+ return "@typescript-eslint/parser";
410
+ return null;
411
+ }
130
412
  function isTSESLintParserObject(value) {
131
413
  if (!isEnhancedParserObject(value))
132
414
  return false;
415
+ if (value.name === "typescript-eslint-parser-for-extra-files")
416
+ return true;
417
+ if (value.meta?.name === "typescript-eslint/parser")
418
+ return true;
133
419
  try {
134
420
  const result = value.parseForESLint("", {});
135
421
  const services = result.services;
@@ -164,9 +450,9 @@ function parseScriptInternal(code, _ctx, parserOptionsCtx) {
164
450
  try {
165
451
  const parserOptions = parserOptionsCtx.parserOptions;
166
452
  if (parserOptionsCtx.isTypeScript() && parserOptions.filePath && parserOptions.project) {
167
- patchResult = tsPatch(parserOptions);
453
+ patchResult = tsPatch(parserOptions, parserOptionsCtx.getTSParserName());
168
454
  }
169
- const result = isEnhancedParserObject(parser) ? parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions);
455
+ const result = isEnhancedParserObject(parser) ? patchResult?.parse ? patchResult.parse(code, parser) : parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions);
170
456
  if ("ast" in result && result.ast != null) {
171
457
  return result;
172
458
  }
@@ -1335,6 +1621,7 @@ ${next}`;
1335
1621
  }
1336
1622
  }
1337
1623
  this.lineStartIndices = lineStartIndices;
1624
+ this.code = origCode;
1338
1625
  this.normalizedLineFeed = new NormalizedLineFeed(normalizedCode, crs);
1339
1626
  }
1340
1627
  getLocFromIndex(index) {
@@ -1348,9 +1635,15 @@ ${next}`;
1348
1635
  };
1349
1636
  }
1350
1637
  getIndexFromLoc(loc) {
1351
- const lineStartIndex = this.lineStartIndices[loc.line - 1];
1352
- const positionIndex = lineStartIndex + loc.column;
1353
- return positionIndex;
1638
+ const lineIndex = loc.line - 1;
1639
+ if (this.lineStartIndices.length > lineIndex) {
1640
+ const lineStartIndex = this.lineStartIndices[lineIndex];
1641
+ const positionIndex = lineStartIndex + loc.column;
1642
+ return positionIndex;
1643
+ } else if (this.lineStartIndices.length === lineIndex) {
1644
+ return this.code.length + loc.column;
1645
+ }
1646
+ return this.code.length + loc.column;
1354
1647
  }
1355
1648
  getNormalizedLineFeed() {
1356
1649
  return this.normalizedLineFeed;
@@ -1726,18 +2019,18 @@ function remap(result, normalized, originalCode, ctxForAstro) {
1726
2019
  }
1727
2020
 
1728
2021
  // src/context/parser-options.ts
1729
- var import_path3 = __toESM(require("path"));
1730
- var import_fs2 = __toESM(require("fs"));
2022
+ var import_path5 = __toESM(require("path"));
2023
+ var import_fs3 = __toESM(require("fs"));
1731
2024
 
1732
2025
  // src/context/resolve-parser/espree.ts
1733
2026
  var import_module2 = require("module");
1734
- var import_path2 = __toESM(require("path"));
2027
+ var import_path4 = __toESM(require("path"));
1735
2028
  var espreeCache = null;
1736
2029
  function isLinterPath(p) {
1737
2030
  return (
1738
2031
  // ESLint 6 and above
1739
- p.includes(`eslint${import_path2.default.sep}lib${import_path2.default.sep}linter${import_path2.default.sep}linter.js`) || // ESLint 5
1740
- p.includes(`eslint${import_path2.default.sep}lib${import_path2.default.sep}linter.js`)
2032
+ p.includes(`eslint${import_path4.default.sep}lib${import_path4.default.sep}linter${import_path4.default.sep}linter.js`) || // ESLint 5
2033
+ p.includes(`eslint${import_path4.default.sep}lib${import_path4.default.sep}linter.js`)
1741
2034
  );
1742
2035
  }
1743
2036
  function getEspree() {
@@ -1815,39 +2108,59 @@ var ParserOptionsContext = class {
1815
2108
  getParser() {
1816
2109
  return getParser({}, this.parserOptions.parser);
1817
2110
  }
1818
- isTypeScript() {
1819
- if (this.state.isTypeScript != null) {
1820
- return this.state.isTypeScript;
2111
+ getTSParserName() {
2112
+ if (this.state.ts != null) {
2113
+ return this.state.ts === false ? null : this.state.ts.parserName;
1821
2114
  }
1822
2115
  const parserValue = getParserForLang({}, this.parserOptions?.parser);
1823
2116
  if (typeof parserValue !== "string") {
1824
- return this.state.isTypeScript = maybeTSESLintParserObject(parserValue) || isTSESLintParserObject(parserValue);
2117
+ const name2 = getTSParserNameFromObject(parserValue);
2118
+ if (name2) {
2119
+ this.state.ts = { parserName: name2 };
2120
+ return this.state.ts.parserName;
2121
+ }
2122
+ if (maybeTSESLintParserObject(parserValue) || isTSESLintParserObject(parserValue)) {
2123
+ this.state.ts = { parserName: "$unknown$" };
2124
+ return this.state.ts.parserName;
2125
+ }
2126
+ this.state.ts = false;
2127
+ return null;
1825
2128
  }
1826
2129
  const parserName = parserValue;
1827
2130
  if (TS_PARSER_NAMES.includes(parserName)) {
1828
- return this.state.isTypeScript = true;
2131
+ this.state.ts = { parserName };
2132
+ return this.state.ts.parserName;
1829
2133
  }
1830
2134
  if (TS_PARSER_NAMES.some((nm) => parserName.includes(nm))) {
1831
2135
  let targetPath = parserName;
1832
2136
  while (targetPath) {
1833
- const pkgPath = import_path3.default.join(targetPath, "package.json");
1834
- if (import_fs2.default.existsSync(pkgPath)) {
2137
+ const pkgPath = import_path5.default.join(targetPath, "package.json");
2138
+ if (import_fs3.default.existsSync(pkgPath)) {
1835
2139
  try {
1836
- return this.state.isTypeScript = TS_PARSER_NAMES.includes(
1837
- JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"))?.name
1838
- );
2140
+ const pkgName = JSON.parse(import_fs3.default.readFileSync(pkgPath, "utf-8"))?.name;
2141
+ if (TS_PARSER_NAMES.includes(pkgName)) {
2142
+ this.state.ts = { parserName: pkgName };
2143
+ return this.state.ts.parserName;
2144
+ }
2145
+ this.state.ts = false;
2146
+ return null;
1839
2147
  } catch {
1840
- return this.state.isTypeScript = false;
2148
+ this.state.ts = false;
2149
+ return null;
1841
2150
  }
1842
2151
  }
1843
- const parent = import_path3.default.dirname(targetPath);
2152
+ const parent = import_path5.default.dirname(targetPath);
1844
2153
  if (targetPath === parent) {
1845
2154
  break;
1846
2155
  }
1847
2156
  targetPath = parent;
1848
2157
  }
1849
2158
  }
1850
- return this.state.isTypeScript = false;
2159
+ this.state.ts = false;
2160
+ return null;
2161
+ }
2162
+ isTypeScript() {
2163
+ return Boolean(this.getTSParserName());
1851
2164
  }
1852
2165
  };
1853
2166
 
package/lib/index.mjs CHANGED
@@ -30,55 +30,327 @@ var debug = debugFactory("astro-eslint-parser");
30
30
 
31
31
  // src/parser/ts-patch.ts
32
32
  import { createRequire } from "module";
33
- import path from "path";
33
+ import path3 from "path";
34
+ import fs2 from "fs";
35
+ import { satisfies } from "semver";
36
+
37
+ // src/parser/ts-for-v5/get-project-config-files.ts
34
38
  import fs from "fs";
35
- function tsPatch(scriptParserOptions) {
39
+ import path from "path";
40
+ function getProjectConfigFiles(options) {
41
+ const tsconfigRootDir = typeof options.tsconfigRootDir === "string" ? options.tsconfigRootDir : process.cwd();
42
+ if (options.project !== true) {
43
+ return Array.isArray(options.project) ? options.project : [options.project];
44
+ }
45
+ let directory = path.dirname(options.filePath);
46
+ const checkedDirectories = [directory];
47
+ do {
48
+ const tsconfigPath = path.join(directory, "tsconfig.json");
49
+ if (fs.existsSync(tsconfigPath)) {
50
+ return [tsconfigPath];
51
+ }
52
+ directory = path.dirname(directory);
53
+ checkedDirectories.push(directory);
54
+ } while (directory.length > 1 && directory.length >= tsconfigRootDir.length);
55
+ throw new Error(
56
+ `project was set to \`true\` but couldn't find any tsconfig.json relative to '${options.filePath}' within '${tsconfigRootDir}'.`
57
+ );
58
+ }
59
+
60
+ // src/parser/ts-for-v5/programs.ts
61
+ import path2 from "path";
62
+ var tsServices = /* @__PURE__ */ new Map();
63
+ function getTSProgram(code, options, ts) {
64
+ const tsconfigPath = options.project;
65
+ let service2 = tsServices.get(tsconfigPath);
66
+ if (!service2) {
67
+ service2 = new TSService(tsconfigPath, ts);
68
+ tsServices.set(tsconfigPath, service2);
69
+ }
70
+ return service2.getProgram(code, options.filePath);
71
+ }
72
+ var TSService = class {
73
+ constructor(tsconfigPath, ts) {
74
+ this.patchedHostSet = /* @__PURE__ */ new WeakSet();
75
+ this.currTarget = {
76
+ code: "",
77
+ filePath: ""
78
+ };
79
+ this.fileWatchCallbacks = /* @__PURE__ */ new Map();
80
+ this.ts = ts;
81
+ this.watch = this.createWatch(tsconfigPath);
82
+ }
83
+ getProgram(code, filePath) {
84
+ const normalized = normalizeFileName(this.ts, filePath);
85
+ const lastTarget = this.currTarget;
86
+ this.currTarget = {
87
+ code,
88
+ filePath: normalized
89
+ };
90
+ for (const { filePath: targetPath } of [this.currTarget, lastTarget]) {
91
+ if (!targetPath)
92
+ continue;
93
+ this.fileWatchCallbacks.get(targetPath)?.update();
94
+ }
95
+ const program = this.watch.getProgram().getProgram();
96
+ program.getTypeChecker();
97
+ return program;
98
+ }
99
+ createWatch(tsconfigPath) {
100
+ const { ts } = this;
101
+ const watchCompilerHost = ts.createWatchCompilerHost(
102
+ tsconfigPath,
103
+ {
104
+ noEmit: true,
105
+ jsx: ts.JsxEmit.Preserve,
106
+ // This option is required if `includes` only includes `*.astro` files.
107
+ // However, the option is not in the documentation.
108
+ // https://github.com/microsoft/TypeScript/issues/28447
109
+ allowNonTsExtensions: true
110
+ },
111
+ ts.sys,
112
+ (rootNames, options, argHost, oldProgram, configFileParsingDiagnostics, projectReferences) => {
113
+ const host = argHost;
114
+ if (!this.patchedHostSet.has(host)) {
115
+ this.patchedHostSet.add(host);
116
+ const getTargetSourceFile = (fileName, languageVersionOrOptions) => {
117
+ var _a;
118
+ if (this.currTarget.filePath === normalizeFileName(ts, fileName)) {
119
+ return (_a = this.currTarget).sourceFile ?? (_a.sourceFile = ts.createSourceFile(
120
+ this.currTarget.filePath,
121
+ this.currTarget.code,
122
+ languageVersionOrOptions,
123
+ true,
124
+ ts.ScriptKind.TSX
125
+ ));
126
+ }
127
+ return null;
128
+ };
129
+ const original2 = {
130
+ getSourceFile: host.getSourceFile,
131
+ getSourceFileByPath: host.getSourceFileByPath
132
+ };
133
+ host.getSourceFile = (fileName, languageVersionOrOptions, ...args) => {
134
+ return getTargetSourceFile(fileName, languageVersionOrOptions) ?? original2.getSourceFile.call(
135
+ host,
136
+ fileName,
137
+ languageVersionOrOptions,
138
+ ...args
139
+ );
140
+ };
141
+ host.getSourceFileByPath = (fileName, path6, languageVersionOrOptions, ...args) => {
142
+ return getTargetSourceFile(fileName, languageVersionOrOptions) ?? original2.getSourceFileByPath.call(
143
+ host,
144
+ fileName,
145
+ path6,
146
+ languageVersionOrOptions,
147
+ ...args
148
+ );
149
+ };
150
+ }
151
+ return ts.createAbstractBuilder(
152
+ rootNames,
153
+ options,
154
+ host,
155
+ oldProgram,
156
+ configFileParsingDiagnostics,
157
+ projectReferences
158
+ );
159
+ },
160
+ (diagnostic) => {
161
+ throw new Error(formatDiagnostics(ts, [diagnostic]));
162
+ },
163
+ () => {
164
+ },
165
+ void 0,
166
+ [
167
+ {
168
+ extension: ".astro",
169
+ isMixedContent: true,
170
+ scriptKind: ts.ScriptKind.Deferred
171
+ }
172
+ ]
173
+ );
174
+ const original = {
175
+ readFile: watchCompilerHost.readFile
176
+ };
177
+ watchCompilerHost.readFile = (fileName, ...args) => {
178
+ const normalized = normalizeFileName(ts, fileName);
179
+ if (this.currTarget.filePath === normalized) {
180
+ return this.currTarget.code;
181
+ }
182
+ return original.readFile.call(watchCompilerHost, fileName, ...args);
183
+ };
184
+ watchCompilerHost.watchFile = (fileName, callback) => {
185
+ const normalized = normalizeFileName(ts, fileName);
186
+ this.fileWatchCallbacks.set(normalized, {
187
+ update: () => callback(fileName, ts.FileWatcherEventKind.Changed)
188
+ });
189
+ return {
190
+ close: () => {
191
+ this.fileWatchCallbacks.delete(normalized);
192
+ }
193
+ };
194
+ };
195
+ watchCompilerHost.watchDirectory = () => {
196
+ return {
197
+ close: () => {
198
+ }
199
+ };
200
+ };
201
+ watchCompilerHost.afterProgramCreate = (program) => {
202
+ const originalDiagnostics = program.getConfigFileParsingDiagnostics();
203
+ const configFileDiagnostics = originalDiagnostics.filter(
204
+ (diag) => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18003
205
+ );
206
+ if (configFileDiagnostics.length > 0) {
207
+ throw new Error(formatDiagnostics(ts, configFileDiagnostics));
208
+ }
209
+ };
210
+ const watch = ts.createWatchProgram(watchCompilerHost);
211
+ return watch;
212
+ }
213
+ };
214
+ function formatDiagnostics(ts, diagnostics) {
215
+ return ts.formatDiagnostics(diagnostics, {
216
+ getCanonicalFileName: (f) => f,
217
+ getCurrentDirectory: () => process.cwd(),
218
+ getNewLine: () => "\n"
219
+ });
220
+ }
221
+ function normalizeFileName(ts, fileName) {
222
+ let normalized = path2.normalize(fileName);
223
+ if (normalized.endsWith(path2.sep)) {
224
+ normalized = normalized.slice(0, -1);
225
+ }
226
+ if (ts.sys.useCaseSensitiveFileNames) {
227
+ return toAbsolutePath(normalized, null);
228
+ }
229
+ return toAbsolutePath(normalized.toLowerCase(), null);
230
+ }
231
+ function toAbsolutePath(filePath, baseDir) {
232
+ return path2.isAbsolute(filePath) ? filePath : path2.join(baseDir || process.cwd(), filePath);
233
+ }
234
+
235
+ // src/parser/ts-for-v5/parse-tsx-for-typescript.ts
236
+ var DEFAULT_EXTRA_FILE_EXTENSIONS = [".vue", ".svelte", ".astro"];
237
+ function parseTsxForTypeScript(code, options, tsEslintParser, ts) {
238
+ const programs = [];
239
+ for (const option of iterateOptions(options)) {
240
+ programs.push(getTSProgram(code, option, ts));
241
+ }
242
+ const parserOptions = {
243
+ ...options,
244
+ programs
245
+ };
246
+ return tsEslintParser.parseForESLint(code, parserOptions);
247
+ }
248
+ function* iterateOptions(options) {
249
+ if (!options) {
250
+ throw new Error("`parserOptions` is required.");
251
+ }
252
+ if (!options.filePath) {
253
+ throw new Error("`filePath` is required.");
254
+ }
255
+ if (!options.project) {
256
+ throw new Error(
257
+ "Specify `parserOptions.project`. Otherwise there is no point in using this parser."
258
+ );
259
+ }
260
+ for (const project of getProjectConfigFiles(options)) {
261
+ yield {
262
+ project,
263
+ filePath: options.filePath,
264
+ extraFileExtensions: options.extraFileExtensions || DEFAULT_EXTRA_FILE_EXTENSIONS
265
+ };
266
+ }
267
+ }
268
+
269
+ // src/parser/ts-patch.ts
270
+ function tsPatch(scriptParserOptions, tsParserName) {
271
+ if (tsParserName === "typescript-eslint-parser-for-extra-files") {
272
+ return {
273
+ terminate() {
274
+ }
275
+ };
276
+ }
36
277
  let targetExt = ".astro";
37
278
  if (scriptParserOptions.filePath) {
38
- const ext = path.extname(scriptParserOptions.filePath);
279
+ const ext = path3.extname(scriptParserOptions.filePath);
39
280
  if (ext) {
40
281
  targetExt = ext;
41
282
  }
42
283
  }
43
284
  try {
44
285
  const cwd = process.cwd();
45
- const relativeTo = path.join(cwd, "__placeholder__.js");
286
+ const relativeTo = path3.join(cwd, "__placeholder__.js");
46
287
  const ts = createRequire(relativeTo)("typescript");
47
- const { ensureScriptKind, getScriptKindFromFileName } = ts;
48
- if (typeof ensureScriptKind === "function" && typeof getScriptKindFromFileName === "function") {
49
- ts.ensureScriptKind = function(fileName, ...args) {
50
- if (fileName.endsWith(targetExt)) {
51
- return ts.ScriptKind.TSX;
52
- }
53
- return ensureScriptKind.call(this, fileName, ...args);
54
- };
55
- ts.getScriptKindFromFileName = function(fileName, ...args) {
56
- if (fileName.endsWith(targetExt)) {
57
- return ts.ScriptKind.TSX;
58
- }
59
- return getScriptKindFromFileName.call(this, fileName, ...args);
60
- };
61
- return {
62
- terminate() {
63
- ts.ensureScriptKind = ensureScriptKind;
64
- ts.getScriptKindFromFileName = getScriptKindFromFileName;
65
- }
66
- };
288
+ if (satisfies(ts.version, ">=5")) {
289
+ const result = tsPatchForV5(ts, scriptParserOptions);
290
+ if (result) {
291
+ return result;
292
+ }
293
+ } else {
294
+ const result = tsPatchForV4(ts, targetExt);
295
+ if (result) {
296
+ return result;
297
+ }
67
298
  }
68
299
  } catch {
69
300
  }
70
301
  const tsxFilePath = `${scriptParserOptions.filePath}.tsx`;
71
302
  scriptParserOptions.filePath = tsxFilePath;
72
- if (!fs.existsSync(tsxFilePath)) {
73
- fs.writeFileSync(tsxFilePath, "/* temp for astro-eslint-parser */");
303
+ if (!fs2.existsSync(tsxFilePath)) {
304
+ fs2.writeFileSync(tsxFilePath, "/* temp for astro-eslint-parser */");
74
305
  return {
75
306
  terminate() {
76
- fs.unlinkSync(tsxFilePath);
307
+ fs2.unlinkSync(tsxFilePath);
77
308
  }
78
309
  };
79
310
  }
80
311
  return null;
81
312
  }
313
+ function tsPatchForV5(ts, scriptParserOptions) {
314
+ return {
315
+ terminate() {
316
+ },
317
+ parse(code, parser) {
318
+ return parseTsxForTypeScript(
319
+ code,
320
+ scriptParserOptions,
321
+ parser,
322
+ ts
323
+ );
324
+ }
325
+ };
326
+ }
327
+ function tsPatchForV4(ts, targetExt) {
328
+ const { ensureScriptKind, getScriptKindFromFileName } = ts;
329
+ if (typeof ensureScriptKind !== "function" || typeof getScriptKindFromFileName !== "function") {
330
+ return null;
331
+ }
332
+ ts.ensureScriptKind = function(fileName, ...args) {
333
+ if (fileName.endsWith(targetExt)) {
334
+ return ts.ScriptKind.TSX;
335
+ }
336
+ return ensureScriptKind.call(this, fileName, ...args);
337
+ };
338
+ ts.getScriptKindFromFileName = function(fileName, ...args) {
339
+ if (fileName.endsWith(targetExt)) {
340
+ return ts.ScriptKind.TSX;
341
+ }
342
+ return getScriptKindFromFileName.call(this, fileName, ...args);
343
+ };
344
+ if (ts.ensureScriptKind === ensureScriptKind || ts.getScriptKindFromFileName === getScriptKindFromFileName) {
345
+ return null;
346
+ }
347
+ return {
348
+ terminate() {
349
+ ts.ensureScriptKind = ensureScriptKind;
350
+ ts.getScriptKindFromFileName = getScriptKindFromFileName;
351
+ }
352
+ };
353
+ }
82
354
 
83
355
  // src/context/resolve-parser/parser-object.ts
84
356
  function isParserObject(value) {
@@ -93,9 +365,23 @@ function isBasicParserObject(value) {
93
365
  function maybeTSESLintParserObject(value) {
94
366
  return isEnhancedParserObject(value) && isBasicParserObject(value) && typeof value.createProgram === "function" && typeof value.clearCaches === "function" && typeof value.version === "string";
95
367
  }
368
+ function getTSParserNameFromObject(value) {
369
+ if (!isEnhancedParserObject(value)) {
370
+ return null;
371
+ }
372
+ if (value.name === "typescript-eslint-parser-for-extra-files")
373
+ return "typescript-eslint-parser-for-extra-files";
374
+ if (value.meta?.name === "typescript-eslint/parser")
375
+ return "@typescript-eslint/parser";
376
+ return null;
377
+ }
96
378
  function isTSESLintParserObject(value) {
97
379
  if (!isEnhancedParserObject(value))
98
380
  return false;
381
+ if (value.name === "typescript-eslint-parser-for-extra-files")
382
+ return true;
383
+ if (value.meta?.name === "typescript-eslint/parser")
384
+ return true;
99
385
  try {
100
386
  const result = value.parseForESLint("", {});
101
387
  const services = result.services;
@@ -130,9 +416,9 @@ function parseScriptInternal(code, _ctx, parserOptionsCtx) {
130
416
  try {
131
417
  const parserOptions = parserOptionsCtx.parserOptions;
132
418
  if (parserOptionsCtx.isTypeScript() && parserOptions.filePath && parserOptions.project) {
133
- patchResult = tsPatch(parserOptions);
419
+ patchResult = tsPatch(parserOptions, parserOptionsCtx.getTSParserName());
134
420
  }
135
- const result = isEnhancedParserObject(parser) ? parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions);
421
+ const result = isEnhancedParserObject(parser) ? patchResult?.parse ? patchResult.parse(code, parser) : parser.parseForESLint(code, parserOptions) : parser.parse(code, parserOptions);
136
422
  if ("ast" in result && result.ast != null) {
137
423
  return result;
138
424
  }
@@ -1301,6 +1587,7 @@ ${next}`;
1301
1587
  }
1302
1588
  }
1303
1589
  this.lineStartIndices = lineStartIndices;
1590
+ this.code = origCode;
1304
1591
  this.normalizedLineFeed = new NormalizedLineFeed(normalizedCode, crs);
1305
1592
  }
1306
1593
  getLocFromIndex(index) {
@@ -1314,9 +1601,15 @@ ${next}`;
1314
1601
  };
1315
1602
  }
1316
1603
  getIndexFromLoc(loc) {
1317
- const lineStartIndex = this.lineStartIndices[loc.line - 1];
1318
- const positionIndex = lineStartIndex + loc.column;
1319
- return positionIndex;
1604
+ const lineIndex = loc.line - 1;
1605
+ if (this.lineStartIndices.length > lineIndex) {
1606
+ const lineStartIndex = this.lineStartIndices[lineIndex];
1607
+ const positionIndex = lineStartIndex + loc.column;
1608
+ return positionIndex;
1609
+ } else if (this.lineStartIndices.length === lineIndex) {
1610
+ return this.code.length + loc.column;
1611
+ }
1612
+ return this.code.length + loc.column;
1320
1613
  }
1321
1614
  getNormalizedLineFeed() {
1322
1615
  return this.normalizedLineFeed;
@@ -1692,18 +1985,18 @@ function remap(result, normalized, originalCode, ctxForAstro) {
1692
1985
  }
1693
1986
 
1694
1987
  // src/context/parser-options.ts
1695
- import path3 from "path";
1696
- import fs2 from "fs";
1988
+ import path5 from "path";
1989
+ import fs3 from "fs";
1697
1990
 
1698
1991
  // src/context/resolve-parser/espree.ts
1699
1992
  import { createRequire as createRequire2 } from "module";
1700
- import path2 from "path";
1993
+ import path4 from "path";
1701
1994
  var espreeCache = null;
1702
1995
  function isLinterPath(p) {
1703
1996
  return (
1704
1997
  // ESLint 6 and above
1705
- p.includes(`eslint${path2.sep}lib${path2.sep}linter${path2.sep}linter.js`) || // ESLint 5
1706
- p.includes(`eslint${path2.sep}lib${path2.sep}linter.js`)
1998
+ p.includes(`eslint${path4.sep}lib${path4.sep}linter${path4.sep}linter.js`) || // ESLint 5
1999
+ p.includes(`eslint${path4.sep}lib${path4.sep}linter.js`)
1707
2000
  );
1708
2001
  }
1709
2002
  function getEspree() {
@@ -1781,39 +2074,59 @@ var ParserOptionsContext = class {
1781
2074
  getParser() {
1782
2075
  return getParser({}, this.parserOptions.parser);
1783
2076
  }
1784
- isTypeScript() {
1785
- if (this.state.isTypeScript != null) {
1786
- return this.state.isTypeScript;
2077
+ getTSParserName() {
2078
+ if (this.state.ts != null) {
2079
+ return this.state.ts === false ? null : this.state.ts.parserName;
1787
2080
  }
1788
2081
  const parserValue = getParserForLang({}, this.parserOptions?.parser);
1789
2082
  if (typeof parserValue !== "string") {
1790
- return this.state.isTypeScript = maybeTSESLintParserObject(parserValue) || isTSESLintParserObject(parserValue);
2083
+ const name2 = getTSParserNameFromObject(parserValue);
2084
+ if (name2) {
2085
+ this.state.ts = { parserName: name2 };
2086
+ return this.state.ts.parserName;
2087
+ }
2088
+ if (maybeTSESLintParserObject(parserValue) || isTSESLintParserObject(parserValue)) {
2089
+ this.state.ts = { parserName: "$unknown$" };
2090
+ return this.state.ts.parserName;
2091
+ }
2092
+ this.state.ts = false;
2093
+ return null;
1791
2094
  }
1792
2095
  const parserName = parserValue;
1793
2096
  if (TS_PARSER_NAMES.includes(parserName)) {
1794
- return this.state.isTypeScript = true;
2097
+ this.state.ts = { parserName };
2098
+ return this.state.ts.parserName;
1795
2099
  }
1796
2100
  if (TS_PARSER_NAMES.some((nm) => parserName.includes(nm))) {
1797
2101
  let targetPath = parserName;
1798
2102
  while (targetPath) {
1799
- const pkgPath = path3.join(targetPath, "package.json");
1800
- if (fs2.existsSync(pkgPath)) {
2103
+ const pkgPath = path5.join(targetPath, "package.json");
2104
+ if (fs3.existsSync(pkgPath)) {
1801
2105
  try {
1802
- return this.state.isTypeScript = TS_PARSER_NAMES.includes(
1803
- JSON.parse(fs2.readFileSync(pkgPath, "utf-8"))?.name
1804
- );
2106
+ const pkgName = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"))?.name;
2107
+ if (TS_PARSER_NAMES.includes(pkgName)) {
2108
+ this.state.ts = { parserName: pkgName };
2109
+ return this.state.ts.parserName;
2110
+ }
2111
+ this.state.ts = false;
2112
+ return null;
1805
2113
  } catch {
1806
- return this.state.isTypeScript = false;
2114
+ this.state.ts = false;
2115
+ return null;
1807
2116
  }
1808
2117
  }
1809
- const parent = path3.dirname(targetPath);
2118
+ const parent = path5.dirname(targetPath);
1810
2119
  if (targetPath === parent) {
1811
2120
  break;
1812
2121
  }
1813
2122
  targetPath = parent;
1814
2123
  }
1815
2124
  }
1816
- return this.state.isTypeScript = false;
2125
+ this.state.ts = false;
2126
+ return null;
2127
+ }
2128
+ isTypeScript() {
2129
+ return Boolean(this.getTSParserName());
1817
2130
  }
1818
2131
  };
1819
2132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-eslint-parser",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Astro component parser for ESLint",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.mjs",
@@ -53,7 +53,8 @@
53
53
  "astrojs-compiler-sync": "^0.3.0",
54
54
  "debug": "^4.3.4",
55
55
  "eslint-visitor-keys": "^3.0.0",
56
- "espree": "^9.0.0"
56
+ "espree": "^9.0.0",
57
+ "semver": "^7.3.8"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@changesets/changelog-github": "^0.4.6",
@@ -68,8 +69,8 @@
68
69
  "@types/mocha": "^10.0.0",
69
70
  "@types/node": "^18.0.0",
70
71
  "@types/semver": "^7.3.9",
71
- "@typescript-eslint/eslint-plugin": "~5.54.0",
72
- "@typescript-eslint/parser": "~5.54.0",
72
+ "@typescript-eslint/eslint-plugin": "~5.56.0",
73
+ "@typescript-eslint/parser": "~5.56.0",
73
74
  "astro": "^2.0.0",
74
75
  "astro-eslint-parser": ">=0.1.0",
75
76
  "benchmark": "^2.1.4",
@@ -80,7 +81,7 @@
80
81
  "eslint": "^8.15.0",
81
82
  "eslint-config-prettier": "^8.3.0",
82
83
  "eslint-formatter-codeframe": "^7.32.1",
83
- "eslint-plugin-astro": "^0.23.0",
84
+ "eslint-plugin-astro": "^0.26.0",
84
85
  "eslint-plugin-eslint-comments": "^3.2.0",
85
86
  "eslint-plugin-json-schema-validator": "^4.0.0",
86
87
  "eslint-plugin-jsonc": "^2.0.0",
@@ -101,11 +102,11 @@
101
102
  "prettier": "^2.0.5",
102
103
  "prettier-plugin-astro": "^0.8.0",
103
104
  "prettier-plugin-svelte": "^2.7.0",
104
- "semver": "^7.3.5",
105
105
  "string-replace-loader": "^3.0.3",
106
106
  "svelte": "^3.48.0",
107
107
  "tsup": "^6.2.3",
108
- "typescript": "~4.9.0"
108
+ "typescript": "~5.0.0",
109
+ "typescript-eslint-parser-for-extra-files": "^0.2.0"
109
110
  },
110
111
  "publishConfig": {
111
112
  "access": "public"