@chanyuxi/eslint-waltz 1.4.0 → 1.5.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/README.md CHANGED
@@ -1,39 +1,33 @@
1
+ <p align="center">
2
+ <img src="./assets/images/github_banner_image.png" alt="@chanyuxi/eslint-waltz" width="100%" />
3
+ </p>
4
+
1
5
  # @chanyuxi/eslint-waltz
2
6
 
3
- Composable ESLint flat config for JavaScript, TypeScript, JSONC, React, Tailwind CSS, Perfectionist, and Vitest.
7
+ Composable ESLint flat config for JavaScript, TypeScript, JSONC, imports, React, Tailwind CSS, Perfectionist, and Vitest.
4
8
 
5
9
  Requires Node.js `>=20.19.0`.
6
10
 
7
- ## Install
11
+ ## Quick start
8
12
 
9
- Install the package, ESLint, and the peer dependencies used by the default configuration:
13
+ Run the interactive initializer from your project root:
10
14
 
11
15
  ```bash
12
- pnpm add -D @chanyuxi/eslint-waltz eslint eslint-plugin-jsonc eslint-plugin-import-x eslint-plugin-perfectionist jsonc-eslint-parser
16
+ pnpm dlx @chanyuxi/eslint-waltz init
13
17
  ```
14
18
 
15
- `@eslint/js`, `@stylistic/eslint-plugin`, `globals`, and `jsonc-parser` are included automatically.
19
+ The initializer detects your package manager and existing dependencies, creates
20
+ `eslint.config.ts`, installs missing dependencies for the selected features,
21
+ and can configure ESLint fix-on-save in VS Code while preserving existing
22
+ settings and comments.
16
23
 
17
- Install peers only for the optional modules you enable:
24
+ If the package is already installed, use its local binary:
18
25
 
19
26
  ```bash
20
- # TypeScript
21
- pnpm add -D typescript-eslint
22
-
23
- # React
24
- pnpm add -D eslint-plugin-react-x eslint-plugin-react-debug eslint-plugin-react-dom eslint-plugin-react-hooks-extra eslint-plugin-react-naming-convention eslint-plugin-react-web-api
25
-
26
- # Tailwind CSS
27
- pnpm add -D eslint-plugin-tailwindcss tailwindcss
28
-
29
- # .gitignore support
30
- pnpm add -D eslint-config-flat-gitignore
31
-
32
- # Vitest
33
- pnpm add -D @vitest/eslint-plugin vitest
27
+ pnpm exec eslint-waltz init
34
28
  ```
35
29
 
36
- ## Usage
30
+ ## Configuration
37
31
 
38
32
  ```ts
39
33
  // eslint.config.ts
@@ -45,37 +39,29 @@ export default waltz({
45
39
  })
46
40
  ```
47
41
 
48
- Enabled by default:
49
-
50
- - JavaScript and stylistic rules
51
- - JSONC rules
52
- - import validation
53
- - Perfectionist with `recommended-alphabetical`
42
+ The default configuration includes JavaScript, stylistic rules, JSONC,
43
+ import validation, and Perfectionist's `recommended-alphabetical` preset.
44
+ Optional modules are enabled with `ts`, `react`, `tailwindcss`, `gitignore`,
45
+ and `vitest`. All options are fully typed.
54
46
 
55
- Optional: `ts`, `react`, `tailwindcss`, `gitignore`, and `vitest`.
47
+ ## CLI
56
48
 
57
- More options are documented in the exported TypeScript types.
49
+ ```bash
50
+ eslint-waltz init # create or update eslint.config.ts
51
+ eslint-waltz version # print the installed version
52
+ ```
58
53
 
59
- The potentially expensive module-resolution rules are disabled by default. Enable
60
- them explicitly when your project needs them:
54
+ Use `eslint-waltz --help` to see the available commands. VS Code
55
+ fix-on-save synchronization is part of `init`.
61
56
 
62
- ```ts
63
- export default waltz({
64
- imports: {
65
- overrides: {
66
- 'imports/named': 'error',
67
- 'imports/namespace': 'error',
68
- },
69
- },
70
- })
71
- ```
57
+ ## Advanced options
72
58
 
73
- ## TypeScript file scope
59
+ ### TypeScript scope
74
60
 
75
- When `ts.files` is provided, it is the maximum TypeScript scope used by the
76
- other modules that automatically include TypeScript files. JavaScript files
77
- keep their normal scope, and a module's own `files` option can override this
78
- limit. Vitest additionally intersects its test-file patterns with this scope.
61
+ `ts.files` limits the TypeScript files included by modules that automatically
62
+ support TypeScript. JavaScript files keep their normal scope, and a module's
63
+ own `files` option can narrow or override it. Vitest also intersects its test
64
+ patterns with this scope.
79
65
 
80
66
  ```ts
81
67
  export default waltz({
@@ -84,16 +70,23 @@ export default waltz({
84
70
  })
85
71
  ```
86
72
 
87
- ## VS Code synchronization
73
+ ### Module resolution
88
74
 
89
- Run this command from your project root to enable ESLint fix-on-save settings
90
- while preserving existing `.vscode/settings.json` values and comments:
75
+ Potentially expensive module-resolution rules are disabled by default. Enable
76
+ only the rules you need:
91
77
 
92
- ```bash
93
- npx eslint-waltz sync-vscode
78
+ ```ts
79
+ export default waltz({
80
+ imports: {
81
+ overrides: {
82
+ 'imports/named': 'error',
83
+ 'imports/namespace': 'error',
84
+ },
85
+ },
86
+ })
94
87
  ```
95
88
 
96
- React plugin settings can be customized when needed:
89
+ ### React settings
97
90
 
98
91
  ```ts
99
92
  export default waltz({
package/dist/cli.js CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import process$1 from 'node:process';
3
- import { readFile, mkdir, writeFile } from 'node:fs/promises';
3
+ import { spawn } from 'node:child_process';
4
+ import { readFile, mkdir, writeFile, rm, access } from 'node:fs/promises';
4
5
  import { join } from 'node:path';
6
+ import { outro, note, intro, cancel, select, multiselect, confirm, CANCEL_SYMBOL } from '@clack/prompts';
5
7
 
6
8
  /*---------------------------------------------------------------------------------------------
7
9
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1508,30 +1510,430 @@ async function syncVSCodeSettings(cwd = process.cwd()) {
1508
1510
  return { changed, filePath };
1509
1511
  }
1510
1512
 
1511
- async function main() {
1512
- const [command] = process$1.argv.slice(2);
1513
- if (command === '--help' || command === '-h') {
1514
- printHelp();
1515
- return;
1516
- }
1517
- if (command !== 'sync-vscode') {
1518
- printHelp();
1519
- process$1.exitCode = 1;
1520
- return;
1521
- }
1522
- const result = await syncVSCodeSettings();
1523
- console.log(result.changed
1524
- ? `Updated ${result.filePath}`
1525
- : `${result.filePath} is already synchronized`);
1513
+ const packageManagers = ['pnpm', 'npm', 'yarn', 'bun'];
1514
+ const perfectionistPresets = [
1515
+ 'recommended-alphabetical',
1516
+ 'recommended-natural',
1517
+ 'recommended-line-length',
1518
+ 'recommended-custom',
1519
+ ];
1520
+ const features = [
1521
+ 'json',
1522
+ 'imports',
1523
+ 'perfectionist',
1524
+ 'typescript',
1525
+ 'react',
1526
+ 'tailwindcss',
1527
+ 'gitignore',
1528
+ 'vitest',
1529
+ ];
1530
+ const eslintConfigFiles = [
1531
+ 'eslint.config.ts',
1532
+ 'eslint.config.mts',
1533
+ 'eslint.config.cts',
1534
+ 'eslint.config.js',
1535
+ 'eslint.config.mjs',
1536
+ 'eslint.config.cjs',
1537
+ ];
1538
+ class PromptCancelledError extends Error {
1539
+ }
1540
+ async function runInit(runtime) {
1541
+ const cwd = runtime.cwd ?? process$1.cwd();
1542
+ const output = runtime.output ?? process$1.stdout;
1543
+ const prompts = runtime.prompts
1544
+ ?? createClackPrompts(runtime.input ?? process$1.stdin, output);
1545
+ try {
1546
+ prompts.intro(`eslint-waltz ${runtime.metadata.version}`);
1547
+ const projectPackage = await readProjectPackage(cwd);
1548
+ const existingConfigs = await findExistingConfigs(cwd);
1549
+ if (existingConfigs.length > 0) {
1550
+ const shouldReplace = await prompts.confirm(`Replace ${existingConfigs.join(', ')} with eslint.config.ts?`, false);
1551
+ if (!shouldReplace) {
1552
+ prompts.cancel('Initialization cancelled.');
1553
+ return;
1554
+ }
1555
+ }
1556
+ const installed = getInstalledDependencies(projectPackage);
1557
+ const selections = await collectSelections(cwd, projectPackage, installed, prompts);
1558
+ const requiredDependencies = getRequiredDependencies(runtime.metadata, selections);
1559
+ const missingDependencies = requiredDependencies.filter(({ name }) => !installed.has(name));
1560
+ let shouldInstall = false;
1561
+ if (missingDependencies.length > 0) {
1562
+ prompts.note(missingDependencies.map(({ spec }) => spec).join('\n'), 'Dependencies to install');
1563
+ shouldInstall = await prompts.confirm('Install these dependencies?', true);
1564
+ }
1565
+ if (shouldInstall) {
1566
+ const install = runtime.install ?? installDependencies;
1567
+ await install(selections.packageManager, missingDependencies.map(({ spec }) => spec), cwd);
1568
+ }
1569
+ const configPath = join(cwd, 'eslint.config.ts');
1570
+ await writeFile(configPath, createConfig(selections), 'utf8');
1571
+ for (const fileName of existingConfigs) {
1572
+ if (fileName !== 'eslint.config.ts') {
1573
+ await rm(join(cwd, fileName));
1574
+ }
1575
+ }
1576
+ const completed = [
1577
+ `${existingConfigs.length > 0 ? 'Updated' : 'Created'} ${configPath}`,
1578
+ ];
1579
+ if (selections.vscode) {
1580
+ const result = await syncVSCodeSettings(cwd);
1581
+ completed.push(result.changed
1582
+ ? `Updated ${result.filePath}`
1583
+ : `${result.filePath} is already synchronized`);
1584
+ }
1585
+ if (missingDependencies.length > 0 && !shouldInstall) {
1586
+ prompts.note(formatInstallCommand(selections.packageManager, missingDependencies.map(({ spec }) => spec)), 'Dependency installation skipped');
1587
+ }
1588
+ prompts.note(completed.join('\n'), 'Files');
1589
+ prompts.outro('eslint-waltz is ready.');
1590
+ }
1591
+ catch (error) {
1592
+ if (error instanceof PromptCancelledError) {
1593
+ prompts.cancel('Initialization cancelled.');
1594
+ return;
1595
+ }
1596
+ throw error;
1597
+ }
1598
+ }
1599
+ async function collectSelections(cwd, projectPackage, installed, prompts) {
1600
+ const detectedPackageManager = await detectPackageManager(cwd, projectPackage);
1601
+ const packageManager = await prompts.select('Which package manager should install the dependencies?', packageManagers.map(value => ({ label: value, value })), detectedPackageManager);
1602
+ const selectedFeatures = await prompts.multiselect('Which features should be enabled?', [
1603
+ { hint: 'default', label: 'JSON and JSONC', value: 'json' },
1604
+ { hint: 'default', label: 'Import validation', value: 'imports' },
1605
+ { hint: 'default', label: 'Perfectionist sorting', value: 'perfectionist' },
1606
+ { label: 'TypeScript', value: 'typescript' },
1607
+ { label: 'React', value: 'react' },
1608
+ { label: 'Tailwind CSS', value: 'tailwindcss' },
1609
+ { label: '.gitignore', value: 'gitignore' },
1610
+ { label: 'Vitest', value: 'vitest' },
1611
+ ], await detectInitialFeatures(cwd, installed));
1612
+ const enabledFeatures = new Set(selectedFeatures);
1613
+ for (const feature of enabledFeatures) {
1614
+ if (!isFeature(feature)) {
1615
+ throw new Error(`Unsupported feature: ${feature}`);
1616
+ }
1617
+ }
1618
+ const perfectionist = enabledFeatures.has('perfectionist')
1619
+ ? await selectPerfectionistPreset(prompts)
1620
+ : 'recommended-alphabetical';
1621
+ const vscode = await prompts.confirm('Configure VS Code fix-on-save settings?', await fileExists(join(cwd, '.vscode')));
1622
+ if (!isPackageManager(packageManager)) {
1623
+ throw new Error(`Unsupported package manager: ${packageManager}`);
1624
+ }
1625
+ return {
1626
+ features: enabledFeatures,
1627
+ packageManager,
1628
+ perfectionist,
1629
+ vscode,
1630
+ };
1631
+ }
1632
+ function createClackPrompts(input, output) {
1633
+ const io = { input, output };
1634
+ return {
1635
+ cancel: message => cancel(message, io),
1636
+ async confirm(message, initial) {
1637
+ return unwrapPromptResult(await confirm({
1638
+ ...io,
1639
+ initialValue: initial,
1640
+ message,
1641
+ }));
1642
+ },
1643
+ intro: message => intro(message, io),
1644
+ async multiselect(message, choices, initial) {
1645
+ return unwrapPromptResult(await multiselect({
1646
+ ...io,
1647
+ initialValues: initial,
1648
+ message,
1649
+ options: [...choices],
1650
+ required: false,
1651
+ }));
1652
+ },
1653
+ note: (message, title) => note(message, title, io),
1654
+ outro: message => outro(message, io),
1655
+ async select(message, choices, initial) {
1656
+ return unwrapPromptResult(await select({
1657
+ ...io,
1658
+ initialValue: initial,
1659
+ message,
1660
+ options: [...choices],
1661
+ }));
1662
+ },
1663
+ };
1664
+ }
1665
+ function createConfig(selections) {
1666
+ const options = [];
1667
+ const { features } = selections;
1668
+ if (features.has('gitignore')) {
1669
+ options.push(' gitignore: true,');
1670
+ }
1671
+ if (!features.has('imports')) {
1672
+ options.push(' imports: false,');
1673
+ }
1674
+ if (!features.has('json')) {
1675
+ options.push(' json: false,');
1676
+ }
1677
+ if (!features.has('perfectionist')) {
1678
+ options.push(' perfectionist: false,');
1679
+ }
1680
+ else if (selections.perfectionist !== 'recommended-alphabetical') {
1681
+ options.push(` perfectionist: { preset: '${selections.perfectionist}' },`);
1682
+ }
1683
+ if (features.has('react')) {
1684
+ options.push(' react: true,');
1685
+ }
1686
+ if (features.has('tailwindcss')) {
1687
+ options.push(' tailwindcss: true,');
1688
+ }
1689
+ if (features.has('typescript')) {
1690
+ options.push(' ts: true,');
1691
+ }
1692
+ if (features.has('vitest')) {
1693
+ options.push(' vitest: true,');
1694
+ }
1695
+ const invocation = options.length === 0
1696
+ ? 'waltz()'
1697
+ : `waltz({\n${options.join('\n')}\n})`;
1698
+ return `import waltz from '@chanyuxi/eslint-waltz'\n\nexport default ${invocation}\n`;
1699
+ }
1700
+ async function detectInitialFeatures(cwd, installed) {
1701
+ const initial = ['json', 'imports', 'perfectionist'];
1702
+ if (installed.has('typescript')
1703
+ || installed.has('typescript-eslint')
1704
+ || await fileExists(join(cwd, 'tsconfig.json'))) {
1705
+ initial.push('typescript');
1706
+ }
1707
+ if (installed.has('react') || installed.has('preact')) {
1708
+ initial.push('react');
1709
+ }
1710
+ if (installed.has('tailwindcss')) {
1711
+ initial.push('tailwindcss');
1712
+ }
1713
+ if (await fileExists(join(cwd, '.gitignore'))) {
1714
+ initial.push('gitignore');
1715
+ }
1716
+ if (installed.has('vitest')) {
1717
+ initial.push('vitest');
1718
+ }
1719
+ return initial;
1720
+ }
1721
+ async function detectPackageManager(cwd, projectPackage) {
1722
+ const configured = projectPackage.packageManager?.split('@')[0];
1723
+ if (isPackageManager(configured)) {
1724
+ return configured;
1725
+ }
1726
+ const lockFiles = [
1727
+ ['pnpm', 'pnpm-lock.yaml'],
1728
+ ['yarn', 'yarn.lock'],
1729
+ ['bun', 'bun.lock'],
1730
+ ['bun', 'bun.lockb'],
1731
+ ['npm', 'package-lock.json'],
1732
+ ];
1733
+ for (const [packageManager, lockFile] of lockFiles) {
1734
+ if (await fileExists(join(cwd, lockFile))) {
1735
+ return packageManager;
1736
+ }
1737
+ }
1738
+ return 'npm';
1739
+ }
1740
+ async function fileExists(filePath) {
1741
+ try {
1742
+ await access(filePath);
1743
+ return true;
1744
+ }
1745
+ catch {
1746
+ return false;
1747
+ }
1748
+ }
1749
+ async function findExistingConfigs(cwd) {
1750
+ const results = await Promise.all(eslintConfigFiles.map(async (fileName) => ({
1751
+ exists: await fileExists(join(cwd, fileName)),
1752
+ fileName,
1753
+ })));
1754
+ return results.filter(result => result.exists).map(result => result.fileName);
1755
+ }
1756
+ function formatInstallCommand(packageManager, dependencies) {
1757
+ return getInstallCommand(packageManager, dependencies).join(' ');
1526
1758
  }
1527
- function printHelp() {
1528
- console.log(`Usage: eslint-waltz <command>
1759
+ function getDependencySpec(metadata, name) {
1760
+ if (name === metadata.name) {
1761
+ return `${name}@${metadata.version}`;
1762
+ }
1763
+ const version = metadata.peerDependencies?.[name]
1764
+ ?? metadata.dependencies?.[name]
1765
+ ?? metadata.devDependencies?.[name];
1766
+ return version ? `${name}@${version}` : name;
1767
+ }
1768
+ function getInstallCommand(packageManager, dependencies) {
1769
+ switch (packageManager) {
1770
+ case 'npm':
1771
+ return ['npm', 'install', '--save-dev', ...dependencies];
1772
+ case 'yarn':
1773
+ return ['yarn', 'add', '--dev', ...dependencies];
1774
+ default:
1775
+ return [packageManager, 'add', '-D', ...dependencies];
1776
+ }
1777
+ }
1778
+ function getInstalledDependencies(projectPackage) {
1779
+ return new Set([
1780
+ ...Object.keys(projectPackage.dependencies ?? {}),
1781
+ ...Object.keys(projectPackage.devDependencies ?? {}),
1782
+ ...Object.keys(projectPackage.optionalDependencies ?? {}),
1783
+ ]);
1784
+ }
1785
+ function getRequiredDependencies(metadata, selections) {
1786
+ const names = [metadata.name, 'eslint', 'jiti'];
1787
+ const { features } = selections;
1788
+ if (features.has('gitignore')) {
1789
+ names.push('eslint-config-flat-gitignore');
1790
+ }
1791
+ if (features.has('imports')) {
1792
+ names.push('eslint-plugin-import-x');
1793
+ }
1794
+ if (features.has('json')) {
1795
+ names.push('eslint-plugin-jsonc', 'jsonc-eslint-parser');
1796
+ }
1797
+ if (features.has('perfectionist')) {
1798
+ names.push('eslint-plugin-perfectionist');
1799
+ }
1800
+ if (features.has('react')) {
1801
+ names.push('eslint-plugin-react-debug', 'eslint-plugin-react-dom', 'eslint-plugin-react-hooks-extra', 'eslint-plugin-react-naming-convention', 'eslint-plugin-react-web-api', 'eslint-plugin-react-x');
1802
+ }
1803
+ if (features.has('tailwindcss')) {
1804
+ names.push('eslint-plugin-tailwindcss', 'tailwindcss');
1805
+ }
1806
+ if (features.has('typescript')) {
1807
+ names.push('typescript', 'typescript-eslint');
1808
+ }
1809
+ if (features.has('vitest')) {
1810
+ names.push('@vitest/eslint-plugin', 'vitest');
1811
+ }
1812
+ return names.map(name => ({
1813
+ name,
1814
+ spec: getDependencySpec(metadata, name),
1815
+ }));
1816
+ }
1817
+ async function installDependencies(packageManager, dependencies, cwd) {
1818
+ const [command, ...args] = getInstallCommand(packageManager, dependencies);
1819
+ const child = spawn(command, args, {
1820
+ cwd,
1821
+ shell: process$1.platform === 'win32',
1822
+ stdio: 'inherit',
1823
+ });
1824
+ await new Promise((resolve, reject) => {
1825
+ child.on('error', reject);
1826
+ child.on('exit', (code) => {
1827
+ if (code === 0) {
1828
+ resolve();
1829
+ }
1830
+ else {
1831
+ reject(new Error(`${command} exited with code ${code ?? 'unknown'}.`));
1832
+ }
1833
+ });
1834
+ });
1835
+ }
1836
+ function isFeature(value) {
1837
+ return features.some(feature => feature === value);
1838
+ }
1839
+ function isPackageManager(value) {
1840
+ return packageManagers.some(packageManager => packageManager === value);
1841
+ }
1842
+ function isPerfectionistPreset(value) {
1843
+ return perfectionistPresets.some(preset => preset === value);
1844
+ }
1845
+ async function readProjectPackage(cwd) {
1846
+ const filePath = join(cwd, 'package.json');
1847
+ try {
1848
+ return JSON.parse(await readFile(filePath, 'utf8'));
1849
+ }
1850
+ catch (error) {
1851
+ if (error.code === 'ENOENT') {
1852
+ throw new Error(`package.json was not found in ${cwd}.`);
1853
+ }
1854
+ throw new Error(`Unable to read ${filePath}: ${error instanceof Error ? error.message : error}`);
1855
+ }
1856
+ }
1857
+ async function selectPerfectionistPreset(prompts) {
1858
+ const preset = await prompts.select('Which Perfectionist preset should be used?', [
1859
+ {
1860
+ hint: 'recommended',
1861
+ label: 'Alphabetical',
1862
+ value: 'recommended-alphabetical',
1863
+ },
1864
+ { label: 'Natural', value: 'recommended-natural' },
1865
+ { label: 'Line length', value: 'recommended-line-length' },
1866
+ { label: 'Custom', value: 'recommended-custom' },
1867
+ ], 'recommended-alphabetical');
1868
+ if (!isPerfectionistPreset(preset)) {
1869
+ throw new Error(`Unsupported Perfectionist preset: ${preset}`);
1870
+ }
1871
+ return preset;
1872
+ }
1873
+ function unwrapPromptResult(result) {
1874
+ if (result === CANCEL_SYMBOL) {
1875
+ throw new PromptCancelledError();
1876
+ }
1877
+ return result;
1878
+ }
1879
+
1880
+ async function readPackageMetadata() {
1881
+ for (const relativePath of ['../package.json', '../../package.json']) {
1882
+ try {
1883
+ const source = await readFile(new URL(relativePath, import.meta.url), 'utf8');
1884
+ const metadata = JSON.parse(source);
1885
+ if (metadata.name === '@chanyuxi/eslint-waltz') {
1886
+ return metadata;
1887
+ }
1888
+ }
1889
+ catch (error) {
1890
+ if (error.code !== 'ENOENT') {
1891
+ throw error;
1892
+ }
1893
+ }
1894
+ }
1895
+ throw new Error('Unable to locate the eslint-waltz package metadata.');
1896
+ }
1897
+
1898
+ async function runCli(args, runtime = {}) {
1899
+ const output = runtime.output ?? process$1.stdout;
1900
+ const [command, ...rest] = args;
1901
+ if ((command === '--help' || command === '-h') && rest.length === 0) {
1902
+ printHelp(output);
1903
+ return 0;
1904
+ }
1905
+ if (command === 'version' && rest.length === 0) {
1906
+ const metadata = runtime.metadata ?? await readPackageMetadata();
1907
+ output.write(`${metadata.version}\n`);
1908
+ return 0;
1909
+ }
1910
+ if (command === 'init' && rest.length === 0) {
1911
+ const metadata = runtime.metadata ?? await readPackageMetadata();
1912
+ await runInit({
1913
+ cwd: runtime.cwd,
1914
+ input: runtime.input,
1915
+ install: runtime.install,
1916
+ metadata,
1917
+ output,
1918
+ prompts: runtime.prompts,
1919
+ });
1920
+ return 0;
1921
+ }
1922
+ printHelp(output);
1923
+ return 1;
1924
+ }
1925
+ function printHelp(output) {
1926
+ output.write(`Usage: eslint-waltz <command>
1529
1927
 
1530
1928
  Commands:
1531
- sync-vscode Create or update .vscode/settings.json for ESLint fix-on-save
1929
+ version Display the installed eslint-waltz version
1930
+ init Interactively create eslint.config.ts
1532
1931
  `);
1533
1932
  }
1534
- main().catch((error) => {
1933
+
1934
+ runCli(process$1.argv.slice(2)).then((exitCode) => {
1935
+ process$1.exitCode = exitCode;
1936
+ }, (error) => {
1535
1937
  console.error(error instanceof Error ? error.message : error);
1536
1938
  process$1.exitCode = 1;
1537
1939
  });
package/dist/core.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { WaltzOptions, LinterConfig } from './types.js';
2
+
3
+ declare function waltz(options?: WaltzOptions, ...orders: LinterConfig[]): Promise<LinterConfig[]>;
4
+
5
+ export { waltz as default };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { WaltzOptions, LinterConfig } from './types.js';
2
- export { ImportsOptions, JsOptions, JsoncOptions, PerfectionistOptions, PerfectionistPreset, ReactOptions, SharedOptions, StylisticOptions, TailwindcssOptions, TsOptions, UnresolvedLinterConfig, VitestOptions } from './types.js';
1
+ import waltz from './core.js';
2
+ export { ImportsOptions, JsOptions, JsoncOptions, LinterConfig, PerfectionistOptions, PerfectionistPreset, ReactOptions, SharedOptions, StylisticOptions, TailwindcssOptions, TsOptions, UnresolvedLinterConfig, VitestOptions, WaltzOptions } from './types.js';
3
3
 
4
- declare function waltz(options?: WaltzOptions, ...orders: LinterConfig[]): Promise<LinterConfig[]>;
5
4
 
6
- export { LinterConfig, WaltzOptions, waltz as default };
5
+
6
+ export { waltz as default };
package/dist/index.js CHANGED
@@ -92,32 +92,6 @@ const JSON_FILES = '**/*.{json,json5,jsonc}';
92
92
  const PACKAGE_JSON_FILES = ['**/package.json'];
93
93
  const TS_CONFIG_FILES = ['**/[jt]sconfig.json', '**/[jt]sconfig.*.json'];
94
94
  const INTERNAL_IMPORT_GLOB = '@/**';
95
- function getImportOrderGroups() {
96
- return [
97
- 'type',
98
- 'builtin',
99
- 'external',
100
- 'internal',
101
- 'parent',
102
- 'sibling',
103
- 'index',
104
- 'object',
105
- 'unknown',
106
- ];
107
- }
108
- function getPerfectionistImportGroups() {
109
- return [
110
- 'type',
111
- 'builtin',
112
- 'external',
113
- 'internal',
114
- 'parent',
115
- 'sibling',
116
- 'index',
117
- 'style',
118
- 'unknown',
119
- ];
120
- }
121
95
 
122
96
  function getScriptFiles(scope) {
123
97
  return scope.isEnableTypeScript
@@ -200,7 +174,17 @@ async function importsConfig(scriptScope, options = {}) {
200
174
  orderImportKind: 'asc',
201
175
  },
202
176
  'distinctGroup': false,
203
- 'groups': getImportOrderGroups(),
177
+ 'groups': [
178
+ 'type',
179
+ 'builtin',
180
+ 'external',
181
+ 'internal',
182
+ 'parent',
183
+ 'sibling',
184
+ 'index',
185
+ 'object',
186
+ 'unknown',
187
+ ],
204
188
  'named': true,
205
189
  'newlines-between': 'always',
206
190
  'pathGroups': [
@@ -606,7 +590,17 @@ async function perfectionistConfig(options = {}, scriptScope) {
606
590
  'error',
607
591
  {
608
592
  ...importSortOptions,
609
- groups: getPerfectionistImportGroups(),
593
+ groups: [
594
+ 'type',
595
+ 'builtin',
596
+ 'external',
597
+ 'internal',
598
+ 'parent',
599
+ 'sibling',
600
+ 'index',
601
+ 'style',
602
+ 'unknown',
603
+ ],
610
604
  internalPattern: ['^@/.+'],
611
605
  },
612
606
  ],
@@ -855,8 +849,9 @@ async function stylisticConfig(options = {}, scriptScope) {
855
849
  '@stylistic/jsx-curly-newline': 'error',
856
850
  '@stylistic/jsx-curly-spacing': ['error', 'never'],
857
851
  '@stylistic/jsx-equals-spacing': 'error',
858
- '@stylistic/jsx-first-prop-new-line': 'error',
859
- '@stylistic/jsx-function-call-newline': ['error', 'multiline'],
852
+ // My style is to place each attribute in JSX on its own line, which makes it easier to read.
853
+ '@stylistic/jsx-first-prop-new-line': ['error', 'multiprop'],
854
+ '@stylistic/jsx-function-call-newline': ['error', 'always'],
860
855
  '@stylistic/jsx-indent-props': ['error', 2],
861
856
  '@stylistic/jsx-max-props-per-line': [
862
857
  'error',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chanyuxi/eslint-waltz",
3
3
  "type": "module",
4
- "version": "1.4.0",
4
+ "version": "1.5.1",
5
5
  "packageManager": "pnpm@10.4.1",
6
6
  "description": "A composable ESLint flat config for JavaScript, TypeScript, JSONC, imports, React, Tailwind CSS, Perfectionist, and Vitest.",
7
7
  "author": "chanyuxi <2438149743@qq.com> (https://github.com/chanyuxi)",
@@ -107,6 +107,7 @@
107
107
  }
108
108
  },
109
109
  "dependencies": {
110
+ "@clack/prompts": "^1.8.1",
110
111
  "@eslint/js": "^10.0.1",
111
112
  "@stylistic/eslint-plugin": "^5.10.0",
112
113
  "globals": "^17.12.0",