@isentinel/eslint-config 3.0.0-beta.3 → 3.0.0-beta.5

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/README.md CHANGED
@@ -117,6 +117,20 @@ set:
117
117
  }
118
118
  ```
119
119
 
120
+ Alternatively, you can install `@isentinel/tsconfig` from pnpm:
121
+
122
+ ```bash
123
+ pnpm install --save-dev @isentinel/tsconfig
124
+ ```
125
+
126
+ And use it in your `tsconfig.json`:
127
+
128
+ ```json
129
+ {
130
+ "extends": "@isentinel/tsconfig/roblox"
131
+ }
132
+ ```
133
+
120
134
  ### ESLint
121
135
 
122
136
  The `ts/no-non-null-assertion` rule is enabled by default, which will warn you
package/dist/cli.js CHANGED
@@ -6,12 +6,12 @@ import { hideBin } from "yargs/helpers";
6
6
  import fs from "node:fs";
7
7
  import fsp from "node:fs/promises";
8
8
  import path from "node:path";
9
- import parse from "parse-gitignore-ts";
9
+ import { existsSync, readFileSync } from "fs";
10
10
  import { execSync } from "node:child_process";
11
11
 
12
12
  //#region package.json
13
13
  var name = "@isentinel/eslint-config";
14
- var version = "3.0.0-beta.3";
14
+ var version = "3.0.0-beta.5";
15
15
  var description = "iSentinel's ESLint config";
16
16
  var keywords = [
17
17
  "eslint-config",
@@ -59,6 +59,7 @@ var dependencies = {
59
59
  "@eslint/markdown": "catalog:prod",
60
60
  "@isentinel/dict-rbxts": "catalog:prod",
61
61
  "@isentinel/dict-roblox": "catalog:prod",
62
+ "@isentinel/eslint-plugin-comment-length": "catalog:prod",
62
63
  "@prettier/plugin-oxc": "catalog:prod",
63
64
  "@shopify/eslint-plugin": "catalog:prod",
64
65
  "@stylistic/eslint-plugin": "catalog:prod",
@@ -73,13 +74,11 @@ var dependencies = {
73
74
  "eslint-plugin-antfu": "catalog:prod",
74
75
  "eslint-plugin-arrow-return-style-x": "catalog:prod",
75
76
  "eslint-plugin-better-max-params": "catalog:prod",
76
- "eslint-plugin-comment-length": "catalog:prod",
77
77
  "eslint-plugin-de-morgan": "catalog:prod",
78
78
  "eslint-plugin-format-lua": "catalog:prod",
79
79
  "eslint-plugin-import-lite": "catalog:prod",
80
80
  "eslint-plugin-jsdoc": "catalog:prod",
81
81
  "eslint-plugin-jsonc": "catalog:prod",
82
- "eslint-plugin-n": "catalog:peer",
83
82
  "eslint-plugin-no-only-tests": "catalog:prod",
84
83
  "eslint-plugin-package-json": "catalog:prod",
85
84
  "eslint-plugin-perfectionist": "catalog:prod",
@@ -94,13 +93,10 @@ var dependencies = {
94
93
  "eslint-plugin-yml": "catalog:prod",
95
94
  "jsonc-eslint-parser": "catalog:prod",
96
95
  "local-pkg": "catalog:prod",
97
- "parse-gitignore-ts": "catalog:prod",
98
96
  "prettier": "catalog:prod",
99
97
  "prettier-plugin-jsdoc": "catalog:prod",
100
98
  "prompts": "catalog:prod",
101
99
  "toml-eslint-parser": "catalog:prod",
102
- "tsdown": "catalog:dev",
103
- "type-fest": "catalog:prod",
104
100
  "yaml-eslint-parser": "catalog:prod",
105
101
  "yargs": "catalog:prod"
106
102
  };
@@ -109,6 +105,7 @@ var devDependencies = {
109
105
  "@eslint-react/eslint-plugin": "catalog:peer",
110
106
  "@eslint/config-inspector": "catalog:dev",
111
107
  "@isentinel/eslint-config": "workspace:*",
108
+ "@isentinel/tsconfig": "catalog:dev",
112
109
  "@stylistic/eslint-plugin-migrate": "catalog:dev",
113
110
  "@types/fs-extra": "catalog:dev",
114
111
  "@types/node": "catalog:dev",
@@ -119,8 +116,9 @@ var devDependencies = {
119
116
  "eslint": "catalog:peer",
120
117
  "eslint-plugin-eslint-plugin": "catalog:peer",
121
118
  "eslint-plugin-jest": "catalog:peer",
122
- "eslint-plugin-pnpm": "catalog:prod",
119
+ "eslint-plugin-n": "catalog:peer",
123
120
  "eslint-plugin-react-roblox-hooks": "catalog:peer",
121
+ "eslint-plugin-unused-imports": "catalog:dev",
124
122
  "eslint-typegen": "catalog:dev",
125
123
  "esno": "catalog:dev",
126
124
  "execa": "catalog:dev",
@@ -128,9 +126,12 @@ var devDependencies = {
128
126
  "fs-extra": "catalog:dev",
129
127
  "jiti": "catalog:dev",
130
128
  "lint-staged": "catalog:dev",
129
+ "parse-gitignore-ts": "catalog:dev",
131
130
  "pnpm-workspace-yaml": "catalog:dev",
132
131
  "rimraf": "catalog:dev",
133
132
  "simple-git-hooks": "catalog:dev",
133
+ "tsdown": "catalog:dev",
134
+ "type-fest": "catalog:dev",
134
135
  "typescript": "catalog:dev"
135
136
  };
136
137
  var peerDependencies = {
@@ -150,8 +151,8 @@ var peerDependenciesMeta = {
150
151
  "eslint-plugin-n": { "optional": true },
151
152
  "eslint-plugin-react-roblox-hooks": { "optional": true }
152
153
  };
153
- var packageManager = "pnpm@10.14.0";
154
- var engines = { "node": ">=22.1.0" };
154
+ var packageManager = "pnpm@10.15.1";
155
+ var engines = { "node": ">=22.16.0" };
155
156
  var publishConfig = { "access": "public" };
156
157
  var package_default = {
157
158
  name,
@@ -233,6 +234,218 @@ const dependenciesMap = {
233
234
  test: ["eslint-plugin-jest"]
234
235
  };
235
236
 
237
+ //#endregion
238
+ //#region node_modules/.pnpm/parse-gitignore-ts@1.0.0/node_modules/parse-gitignore-ts/dist/index.mjs
239
+ var isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
240
+ var INVALID_PATH_CHARS_REGEX = /[<>:"|?*\n\r\t\f\x00-\x1F]/;
241
+ var GLOBSTAR_REGEX = /(?:^|\/)[*]{2}($|\/)/;
242
+ var MAX_PATH_LENGTH = 248;
243
+ var isValidPath = (input) => {
244
+ if (typeof input === "string") return input.length <= MAX_PATH_LENGTH && !INVALID_PATH_CHARS_REGEX.test(input);
245
+ return false;
246
+ };
247
+ var split = (str) => String(str).split(/\r\n?|\n/);
248
+ var isComment = (str) => str.startsWith("#");
249
+ var isParsed = (input) => isObject(input) && "patterns" in input && "sections" in input;
250
+ var patterns = (input) => {
251
+ return split(input).map((l) => l.trim()).filter((line) => line !== "" && !isComment(line));
252
+ };
253
+ var parse = (input, options = {}) => {
254
+ let filepath = options.path;
255
+ if (isParsed(input)) return input;
256
+ if (isValidPath(input) && existsSync(input)) {
257
+ filepath = input;
258
+ input = readFileSync(input);
259
+ }
260
+ const lines = split(input);
261
+ const names = /* @__PURE__ */ new Map();
262
+ const parsed = {
263
+ sections: [],
264
+ patterns: []
265
+ };
266
+ let section = {
267
+ name: "default",
268
+ patterns: []
269
+ };
270
+ let prev = null;
271
+ for (const line of lines) {
272
+ const value = line.trim();
273
+ if (value.startsWith("#")) {
274
+ const match = /^#+\s*(.*)\s*$/.exec(value);
275
+ const name$1 = match ? match[1] : "";
276
+ if (prev) {
277
+ names.delete(prev.name);
278
+ prev.comment = prev.comment ? `${prev.comment}
279
+ ${value}` : value;
280
+ prev.name = name$1 ? `${prev.name.trim()}
281
+ ${name$1.trim()}` : prev.name.trim();
282
+ names.set(prev.name.toLowerCase().trim(), prev);
283
+ continue;
284
+ }
285
+ section = {
286
+ name: name$1.trim(),
287
+ comment: value,
288
+ patterns: []
289
+ };
290
+ names.set(section.name.toLowerCase(), section);
291
+ parsed.sections.push(section);
292
+ prev = section;
293
+ continue;
294
+ }
295
+ if (value !== "") {
296
+ section.patterns.push(value);
297
+ parsed.patterns.push(value);
298
+ }
299
+ prev = null;
300
+ }
301
+ if (options.dedupe === true || options.unique === true) return dedupe(parsed, {
302
+ ...options,
303
+ format: false
304
+ });
305
+ parsed.path = filepath;
306
+ parsed.input = Buffer.from(input.toString());
307
+ const result = parsed;
308
+ result.format = (opts = {}) => format(result, {
309
+ ...options,
310
+ ...opts
311
+ });
312
+ result.dedupe = (opts = {}) => dedupe(result, {
313
+ ...options,
314
+ ...opts
315
+ });
316
+ result.globs = (opts = {}) => globs(result, {
317
+ path: filepath,
318
+ ...options,
319
+ ...opts
320
+ });
321
+ return result;
322
+ };
323
+ var parseFile = (filepath, options = {}) => {
324
+ return parse(readFileSync(filepath, "utf8"), options);
325
+ };
326
+ var dedupe = (input, options = {}) => {
327
+ const parsed = parse(input, {
328
+ ...options,
329
+ dedupe: false
330
+ });
331
+ const names = /* @__PURE__ */ new Map();
332
+ const res = {
333
+ sections: [],
334
+ patterns: []
335
+ };
336
+ const patternsSet = /* @__PURE__ */ new Set();
337
+ let current;
338
+ for (const section of parsed.sections) {
339
+ const { name: name$1 = "", comment, patterns: patterns2 } = section;
340
+ const key = name$1.trim().toLowerCase();
341
+ for (const pattern of patterns2) patternsSet.add(pattern);
342
+ if (name$1 && names.has(key)) {
343
+ current = names.get(key);
344
+ current.patterns = [...current.patterns, ...patterns2];
345
+ } else {
346
+ current = {
347
+ name: name$1,
348
+ comment,
349
+ patterns: patterns2
350
+ };
351
+ res.sections.push(current);
352
+ names.set(key, current);
353
+ }
354
+ }
355
+ for (const section of res.sections) section.patterns = [...new Set(section.patterns)];
356
+ res.patterns = [...patternsSet];
357
+ const result = res;
358
+ result.format = (opts = {}) => format(result, {
359
+ ...options,
360
+ ...opts
361
+ });
362
+ result.dedupe = (opts = {}) => dedupe(result, {
363
+ ...options,
364
+ ...opts
365
+ });
366
+ result.globs = (opts = {}) => globs(result, {
367
+ path: parsed.path,
368
+ ...options,
369
+ ...opts
370
+ });
371
+ return result;
372
+ };
373
+ var glob = (pattern, options) => {
374
+ if (GLOBSTAR_REGEX.test(pattern)) return pattern;
375
+ let relative = false;
376
+ if (pattern.startsWith("/")) {
377
+ pattern = pattern.slice(1);
378
+ relative = true;
379
+ } else if (pattern.slice(1, pattern.length - 1).includes("/")) relative = true;
380
+ pattern += pattern.endsWith("/") ? "**/" : "/**";
381
+ return relative ? pattern : `**/${pattern}`;
382
+ };
383
+ var globs = (input, options = {}) => {
384
+ const parsed = parse(input, options);
385
+ const result = [];
386
+ let index = 0;
387
+ const inputPatterns = parsed.patterns.concat(options.ignore || []).concat((options.unignore || []).map((p) => !p.startsWith("!") ? "!" + p : p));
388
+ const push = (prefix, pattern) => {
389
+ const prev = result[result.length - 1];
390
+ const type$1 = prefix ? "unignore" : "ignore";
391
+ if (prev && prev.type === type$1) {
392
+ if (!prev.patterns.includes(pattern)) prev.patterns.push(pattern);
393
+ } else {
394
+ result.push({
395
+ type: type$1,
396
+ path: options.path || null,
397
+ patterns: [pattern],
398
+ index
399
+ });
400
+ index++;
401
+ }
402
+ };
403
+ for (let pattern of inputPatterns) {
404
+ let prefix = "";
405
+ if (pattern.startsWith("!")) {
406
+ pattern = pattern.slice(1);
407
+ prefix = "!";
408
+ }
409
+ push(prefix, pattern.startsWith("/") ? pattern.slice(1) : pattern);
410
+ push(prefix, glob(pattern));
411
+ }
412
+ return result;
413
+ };
414
+ var formatSection = (section = {
415
+ name: "",
416
+ patterns: []
417
+ }) => {
418
+ const output = [section.comment || ""];
419
+ if (section.patterns?.length) {
420
+ output.push(section.patterns.join("\n"));
421
+ output.push("");
422
+ }
423
+ return output.join("\n");
424
+ };
425
+ var format = (input, options = {}) => {
426
+ const parsed = parse(input, options);
427
+ const fn = options.formatSection || formatSection;
428
+ const sections = parsed.sections || parsed;
429
+ const output = [];
430
+ for (const section of [].concat(sections)) output.push(fn(section));
431
+ return output.join("\n");
432
+ };
433
+ var parseGitignore = parse;
434
+ parseGitignore.file = parseFile;
435
+ parseGitignore.parse = parse;
436
+ parseGitignore.dedupe = dedupe;
437
+ parseGitignore.format = format;
438
+ parseGitignore.globs = globs;
439
+ parseGitignore.formatSection = formatSection;
440
+ parseGitignore.patterns = patterns;
441
+ var index_default = parseGitignore;
442
+ /*!
443
+ * parse-gitignore-ts
444
+ * TypeScript version of parse-gitignore <https://github.com/jonschlinkert/parse-gitignore>
445
+ * Original Copyright (c) 2015-present, Jon Schlinkert.
446
+ * Released under the MIT License.
447
+ */
448
+
236
449
  //#endregion
237
450
  //#region src/cli/utils.ts
238
451
  function getEslintConfigContent(mainConfig, additionalConfigs) {
@@ -260,24 +473,21 @@ async function updateEslintFiles(result) {
260
473
  const pathESLintIgnore = path.join(cwd, ".eslintignore");
261
474
  const pathPackageJSON = path.join(cwd, "package.json");
262
475
  const packageContent = await fsp.readFile(pathPackageJSON, "utf-8");
263
- const parsedPackage = JSON.parse(packageContent);
264
- const configFileName = parsedPackage.type === "module" ? "eslint.config.js" : "eslint.config.mjs";
476
+ const configFileName = JSON.parse(packageContent)["type"] === "module" ? "eslint.config.js" : "eslint.config.mjs";
265
477
  const pathFlatConfig = path.join(cwd, configFileName);
266
478
  const eslintIgnores = [];
267
479
  if (fs.existsSync(pathESLintIgnore)) {
268
480
  log.step(ansis.cyan("Migrating existing .eslintignore"));
269
481
  const content = await fsp.readFile(pathESLintIgnore, "utf-8");
270
- const parsed = parse(content);
271
- const globs = parsed.globs();
272
- for (const glob of globs) if (glob.type === "ignore") eslintIgnores.push(...glob.patterns);
273
- else eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`));
482
+ const globs$1 = index_default(content).globs();
483
+ for (const glob$1 of globs$1) if (glob$1.type === "ignore") eslintIgnores.push(...glob$1.patterns);
484
+ else eslintIgnores.push(...glob$1.patterns.map((pattern) => `!${pattern}`));
274
485
  }
275
486
  const configLines = [];
276
487
  if (eslintIgnores.length) configLines.push(`ignores: ${JSON.stringify(eslintIgnores)},`);
277
488
  for (const framework of result.frameworks) configLines.push(`${framework}: true,`);
278
489
  const mainConfig = configLines.map((index) => ` ${index}`).join("\n");
279
- const additionalConfig = [];
280
- const eslintConfigContent = getEslintConfigContent(mainConfig, additionalConfig);
490
+ const eslintConfigContent = getEslintConfigContent(mainConfig, []);
281
491
  await fsp.writeFile(pathFlatConfig, eslintConfigContent);
282
492
  log.success(ansis.green(`Created ${configFileName}`));
283
493
  const files$1 = fs.readdirSync(cwd);
@@ -305,7 +515,7 @@ async function updatePackageJson(result) {
305
515
  const parsedPackage = JSON.parse(packageContent);
306
516
  parsedPackage.devDependencies ??= {};
307
517
  parsedPackage.devDependencies["@isentinel/eslint-config"] = `^${version}`;
308
- parsedPackage.devDependencies.eslint ??= versionsMap.eslint;
518
+ parsedPackage.devDependencies["eslint"] ??= versionsMap.eslint;
309
519
  const addedPackages = [];
310
520
  for (const framework of result.frameworks) if (framework in dependenciesMap) for (const dep of dependenciesMap[framework]) {
311
521
  parsedPackage.devDependencies[dep] = versionsMap[dep];
@@ -340,7 +550,7 @@ async function updateVscodeSettings(result) {
340
550
  //#endregion
341
551
  //#region src/cli/run.ts
342
552
  async function run(options = {}) {
343
- const argumentSkipPrompt = !!(process.env.SKIP_PROMPT ?? "") || options.yes;
553
+ const argumentSkipPrompt = !!(process.env["SKIP_PROMPT"] ?? "") || options.yes;
344
554
  const argumentTemplate = options.frameworks?.map((framework) => framework.trim()).filter((framework) => {
345
555
  return frameworks.includes(framework);
346
556
  });
@@ -401,7 +611,7 @@ function header() {
401
611
  const versionText = `v${package_default.version}`;
402
612
  intro(introText + ansis.dim(versionText));
403
613
  }
404
- const instance = yargs(hideBin(process.argv)).scriptName("@isentinel/eslint-config").usage("").command("*", "Run the initialization or migration", (args) => {
614
+ yargs(hideBin(process.argv)).scriptName("@isentinel/eslint-config").usage("").command("*", "Run the initialization or migration", (args) => {
405
615
  return args.option("yes", {
406
616
  alias: "y",
407
617
  description: "Skip prompts and use default values",
@@ -423,8 +633,7 @@ const instance = yargs(hideBin(process.argv)).scriptName("@isentinel/eslint-conf
423
633
  log.error(ansis.red(`✘ ${String(err)}`));
424
634
  process.exit(1);
425
635
  }
426
- }).showHelpOnFail(false).alias("h", "help").version("version", package_default.version).alias("v", "version");
427
- instance.help().argv;
636
+ }).showHelpOnFail(false).alias("h", "help").version("version", package_default.version).alias("v", "version").help().argv;
428
637
 
429
638
  //#endregion
430
639
  export { };
@@ -0,0 +1,96 @@
1
+ import { createRequire } from "module";
2
+
3
+ //#region node_modules/.pnpm/eslint-plugin-unused-imports@4.2.0_@typescript-eslint+eslint-plugin@8.39.1_@typescript-_d725b5966c09a4e636f423644baea633/node_modules/eslint-plugin-unused-imports/dist/index.mjs
4
+ var commaFilter = { filter: (token) => token.value === "," };
5
+ var includeCommentsFilter = { includeComments: true };
6
+ function makePredicate(isImport, addFixer) {
7
+ return (problem, context) => {
8
+ const sourceCode = context.sourceCode || context.getSourceCode();
9
+ const { parent } = problem.node ?? sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(problem.loc.start));
10
+ return parent ? /^Import(|Default|Namespace)Specifier$/.test(parent.type) == isImport ? Object.assign(problem, addFixer?.(parent, sourceCode)) : false : isImport ? false : problem;
11
+ };
12
+ }
13
+ var unusedVarsPredicate = makePredicate(false);
14
+ var unusedImportsPredicate = makePredicate(true, (parent, sourceCode) => ({ fix(fixer) {
15
+ const grandParent = parent.parent;
16
+ if (!grandParent) return null;
17
+ if (grandParent.specifiers.length === 1) {
18
+ const nextToken = sourceCode.getTokenAfter(grandParent, includeCommentsFilter);
19
+ const newLinesBetween = nextToken ? nextToken.loc.start.line - grandParent.loc.start.line : 0;
20
+ const endOfReplaceRange = nextToken ? nextToken.range[0] : grandParent.range[1];
21
+ const count = Math.max(0, newLinesBetween - 1);
22
+ return [fixer.remove(grandParent), fixer.replaceTextRange([grandParent.range[1], endOfReplaceRange], "\n".repeat(count))];
23
+ }
24
+ if (parent !== grandParent.specifiers[grandParent.specifiers.length - 1]) {
25
+ const comma = sourceCode.getTokenAfter(parent, commaFilter);
26
+ const prevNode = sourceCode.getTokenBefore(parent);
27
+ return [
28
+ fixer.removeRange([prevNode.range[1], parent.range[0]]),
29
+ fixer.remove(parent),
30
+ fixer.remove(comma)
31
+ ];
32
+ }
33
+ if (grandParent.specifiers.filter((specifier) => specifier.type === "ImportSpecifier").length === 1) {
34
+ const start = sourceCode.getTokenBefore(parent, commaFilter);
35
+ const end = sourceCode.getTokenAfter(parent, { filter: (token) => token.value === "}" });
36
+ return fixer.removeRange([start.range[0], end.range[1]]);
37
+ }
38
+ return fixer.removeRange([sourceCode.getTokenBefore(parent, commaFilter).range[0], parent.range[1]]);
39
+ } }));
40
+ function createRuleWithPredicate(name, baseRule, predicate) {
41
+ return {
42
+ ...baseRule,
43
+ meta: {
44
+ ...baseRule.meta,
45
+ fixable: "code",
46
+ docs: {
47
+ ...baseRule.meta?.docs,
48
+ url: `https://github.com/sweepline/eslint-plugin-unused-imports/blob/master/docs/rules/${name}.md`
49
+ }
50
+ },
51
+ create(context) {
52
+ return baseRule.create(Object.create(context, { report: {
53
+ enumerable: true,
54
+ value(problem) {
55
+ const result = predicate(problem, context);
56
+ if (result) context.report(result);
57
+ }
58
+ } }));
59
+ }
60
+ };
61
+ }
62
+ var rule;
63
+ var require2 = createRequire(import.meta.url);
64
+ function getBaseRule() {
65
+ if (!rule) rule = getRuleFromTSLintPlugin() ?? getRuleFromTSLint() ?? getESLintBaseRule();
66
+ return rule;
67
+ }
68
+ function getRuleFromTSLintPlugin() {
69
+ try {
70
+ return require2("@typescript-eslint/eslint-plugin").rules["no-unused-vars"];
71
+ } catch (_) {
72
+ return null;
73
+ }
74
+ }
75
+ function getRuleFromTSLint() {
76
+ try {
77
+ return require2("typescript-eslint").plugin.rules["no-unused-vars"];
78
+ } catch (_) {
79
+ return null;
80
+ }
81
+ }
82
+ function getESLintBaseRule() {
83
+ return new (require2("eslint")).Linter({ configType: "eslintrc" }).getRules().get("no-unused-vars");
84
+ }
85
+ var no_unused_vars_default = createRuleWithPredicate("no-unused-vars", getBaseRule(), unusedVarsPredicate);
86
+ var no_unused_imports_default = createRuleWithPredicate("no-unused-imports", getBaseRule(), unusedImportsPredicate);
87
+ var index_default = {
88
+ meta: { name: "unused-imports" },
89
+ rules: {
90
+ "no-unused-vars": no_unused_vars_default,
91
+ "no-unused-imports": no_unused_imports_default
92
+ }
93
+ };
94
+
95
+ //#endregion
96
+ export { index_default as default };