@chanyuxi/eslint-waltz 1.3.2 → 1.5.0

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,106 +1,95 @@
1
- # @chanyuxi/eslint-waltz
2
-
3
- Composable ESLint flat config for JavaScript, TypeScript, JSONC, React, Tailwind CSS, Perfectionist, and Vitest.
4
-
5
- Requires Node.js `>=20.19.0`.
6
-
7
- ## Install
8
-
9
- Install the package, ESLint, and the peer dependencies used by the default configuration:
10
-
11
- ```bash
12
- pnpm add -D @chanyuxi/eslint-waltz eslint eslint-plugin-jsonc eslint-plugin-import-x eslint-plugin-perfectionist jsonc-eslint-parser
13
- ```
14
-
15
- `@eslint/js`, `@stylistic/eslint-plugin`, `globals`, and `jsonc-parser` are included automatically.
16
-
17
- Install peers only for the optional modules you enable:
18
-
19
- ```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
34
- ```
35
-
36
- ## Usage
37
-
38
- ```ts
39
- // eslint.config.ts
40
- import waltz from '@chanyuxi/eslint-waltz'
41
-
42
- export default waltz({
43
- ts: true,
44
- react: true,
45
- })
46
- ```
47
-
48
- Enabled by default:
49
-
50
- - JavaScript and stylistic rules
51
- - JSONC rules
52
- - import validation
53
- - Perfectionist with `recommended-alphabetical`
54
-
55
- Optional: `ts`, `react`, `tailwindcss`, `gitignore`, and `vitest`.
56
-
57
- More options are documented in the exported TypeScript types.
58
-
59
- The potentially expensive module-resolution rules are disabled by default. Enable
60
- them explicitly when your project needs them:
61
-
62
- ```ts
63
- export default waltz({
64
- imports: {
65
- overrides: {
66
- 'imports/named': 'error',
67
- 'imports/namespace': 'error',
68
- },
69
- },
70
- })
71
- ```
72
-
73
- ## TypeScript file scope
74
-
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.
79
-
80
- ```ts
81
- export default waltz({
82
- ts: { files: ['src/**/*.ts'] },
83
- react: true,
84
- })
85
- ```
86
-
87
- ## VS Code synchronization
88
-
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:
91
-
92
- ```bash
93
- npx eslint-waltz sync-vscode
94
- ```
95
-
96
- React plugin settings can be customized when needed:
97
-
98
- ```ts
99
- export default waltz({
100
- react: {
101
- settings: {
102
- 'react-x': { importSource: 'preact' },
103
- },
104
- },
105
- })
106
- ```
1
+ # @chanyuxi/eslint-waltz
2
+
3
+ Composable ESLint flat config for JavaScript, TypeScript, JSONC, imports, React, Tailwind CSS, Perfectionist, and Vitest.
4
+
5
+ Requires Node.js `>=20.19.0`.
6
+
7
+ ## Quick start
8
+
9
+ Run the interactive initializer from your project root:
10
+
11
+ ```bash
12
+ pnpm dlx @chanyuxi/eslint-waltz init
13
+ ```
14
+
15
+ The initializer detects your package manager and existing dependencies, creates
16
+ `eslint.config.ts`, installs missing dependencies for the selected features,
17
+ and can configure ESLint fix-on-save in VS Code while preserving existing
18
+ settings and comments.
19
+
20
+ If the package is already installed, use its local binary:
21
+
22
+ ```bash
23
+ pnpm exec eslint-waltz init
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ ```ts
29
+ // eslint.config.ts
30
+ import waltz from '@chanyuxi/eslint-waltz'
31
+
32
+ export default waltz({
33
+ ts: true,
34
+ react: true,
35
+ })
36
+ ```
37
+
38
+ The default configuration includes JavaScript, stylistic rules, JSONC,
39
+ import validation, and Perfectionist's `recommended-alphabetical` preset.
40
+ Optional modules are enabled with `ts`, `react`, `tailwindcss`, `gitignore`,
41
+ and `vitest`. All options are fully typed.
42
+
43
+ ## CLI
44
+
45
+ ```bash
46
+ eslint-waltz init # create or update eslint.config.ts
47
+ eslint-waltz version # print the installed version
48
+ ```
49
+
50
+ Use `eslint-waltz --help` to see the available commands. VS Code
51
+ fix-on-save synchronization is part of `init`.
52
+
53
+ ## Advanced options
54
+
55
+ ### TypeScript scope
56
+
57
+ `ts.files` limits the TypeScript files included by modules that automatically
58
+ support TypeScript. JavaScript files keep their normal scope, and a module's
59
+ own `files` option can narrow or override it. Vitest also intersects its test
60
+ patterns with this scope.
61
+
62
+ ```ts
63
+ export default waltz({
64
+ ts: { files: ['src/**/*.ts'] },
65
+ react: true,
66
+ })
67
+ ```
68
+
69
+ ### Module resolution
70
+
71
+ Potentially expensive module-resolution rules are disabled by default. Enable
72
+ only the rules you need:
73
+
74
+ ```ts
75
+ export default waltz({
76
+ imports: {
77
+ overrides: {
78
+ 'imports/named': 'error',
79
+ 'imports/namespace': 'error',
80
+ },
81
+ },
82
+ })
83
+ ```
84
+
85
+ ### React settings
86
+
87
+ ```ts
88
+ export default waltz({
89
+ react: {
90
+ settings: {
91
+ 'react-x': { importSource: 'preact' },
92
+ },
93
+ },
94
+ })
95
+ ```
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>
1529
-
1530
- Commands:
1531
- sync-vscode Create or update .vscode/settings.json for ESLint fix-on-save
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>
1927
+
1928
+ Commands:
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
  ],
@@ -1115,6 +1109,12 @@ async function waltz(options = {}, ...orders) {
1115
1109
  const isEnableReact = !!options.react;
1116
1110
  const { scriptScope } = resolveFileScopes(options);
1117
1111
  const configs = [];
1112
+ if (options.globalIgnores) {
1113
+ configs.push({
1114
+ ignores: options.globalIgnores,
1115
+ name: 'waltz/global-ignores',
1116
+ });
1117
+ }
1118
1118
  if (options.gitignore) {
1119
1119
  configs.push(gitignore(options.gitignore));
1120
1120
  }
package/dist/types.d.ts CHANGED
@@ -88,6 +88,13 @@ interface WaltzOptions {
88
88
  * @see https://github.com/antfu/eslint-config-flat-gitignore
89
89
  */
90
90
  gitignore?: boolean | FlatGitignoreOptions;
91
+ /**
92
+ * Glob patterns to ignore globally.
93
+ *
94
+ * These patterns are applied by ESLint before any of Waltz's file-scoped
95
+ * configurations.
96
+ */
97
+ globalIgnores?: Linter.Config['ignores'];
91
98
  /**
92
99
  * Enable rules for validating imports and exports. Import ordering is
93
100
  * provided by Perfectionist when enabled, and by `imports/order` otherwise.
package/package.json CHANGED
@@ -1,151 +1,152 @@
1
- {
2
- "name": "@chanyuxi/eslint-waltz",
3
- "type": "module",
4
- "version": "1.3.2",
5
- "packageManager": "pnpm@10.4.1",
6
- "description": "A composable ESLint flat config for JavaScript, TypeScript, JSONC, imports, React, Tailwind CSS, Perfectionist, and Vitest.",
7
- "author": "chanyuxi <2438149743@qq.com> (https://github.com/chanyuxi)",
8
- "license": "ISC",
9
- "homepage": "https://github.com/chanyuxi/eslint-waltz",
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/chanyuxi/eslint-waltz.git"
13
- },
14
- "bugs": {
15
- "url": "https://github.com/chanyuxi/eslint-waltz/issues"
16
- },
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "keywords": [
21
- "chanyuxi",
22
- "eslint",
23
- "config",
24
- "integration"
25
- ],
26
- "exports": {
27
- ".": {
28
- "types": "./dist/index.d.ts",
29
- "import": "./dist/index.js",
30
- "default": "./dist/index.js"
31
- }
32
- },
33
- "bin": {
34
- "eslint-waltz": "dist/cli.js"
35
- },
36
- "files": [
37
- "dist"
38
- ],
39
- "engines": {
40
- "node": ">=20.19.0"
41
- },
42
- "scripts": {
43
- "build": "rimraf ./dist && pnpm gen && rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript",
44
- "gen": "tsx scripts/typegen.ts",
45
- "lint": "eslint .",
46
- "lint:fix": "eslint . --fix",
47
- "typeCheck": "pnpm gen && tsc --noEmit -p tsconfig.test.json",
48
- "test": "vitest run",
49
- "check": "pnpm run typeCheck && pnpm run lint && pnpm run test",
50
- "changeset": "changeset",
51
- "version": "changeset version && pnpm install --lockfile-only",
52
- "release": "changeset publish",
53
- "prepack": "pnpm build",
54
- "prepare": "husky"
55
- },
56
- "peerDependencies": {
57
- "@vitest/eslint-plugin": "^1.6.27",
58
- "eslint": "^10.10.0",
59
- "eslint-config-flat-gitignore": "^2.4.0",
60
- "eslint-plugin-import-x": "^4.17.1",
61
- "eslint-plugin-jsonc": "^3.4.2",
62
- "eslint-plugin-perfectionist": "^5.11.0",
63
- "eslint-plugin-react-debug": "^5.19.0",
64
- "eslint-plugin-react-dom": "^5.19.0",
65
- "eslint-plugin-react-hooks-extra": "^2.13.0",
66
- "eslint-plugin-react-naming-convention": "^5.19.0",
67
- "eslint-plugin-react-web-api": "^5.19.0",
68
- "eslint-plugin-react-x": "^5.19.0",
69
- "eslint-plugin-tailwindcss": "^4.4.0",
70
- "jsonc-eslint-parser": "^3.3.0",
71
- "tailwindcss": "^4.0.0",
72
- "typescript-eslint": "^8.70.0"
73
- },
74
- "peerDependenciesMeta": {
75
- "@vitest/eslint-plugin": {
76
- "optional": true
77
- },
78
- "eslint-config-flat-gitignore": {
79
- "optional": true
80
- },
81
- "eslint-plugin-react-debug": {
82
- "optional": true
83
- },
84
- "eslint-plugin-react-dom": {
85
- "optional": true
86
- },
87
- "eslint-plugin-react-hooks-extra": {
88
- "optional": true
89
- },
90
- "eslint-plugin-react-naming-convention": {
91
- "optional": true
92
- },
93
- "eslint-plugin-react-web-api": {
94
- "optional": true
95
- },
96
- "eslint-plugin-react-x": {
97
- "optional": true
98
- },
99
- "eslint-plugin-tailwindcss": {
100
- "optional": true
101
- },
102
- "tailwindcss": {
103
- "optional": true
104
- },
105
- "typescript-eslint": {
106
- "optional": true
107
- }
108
- },
109
- "dependencies": {
110
- "@eslint/js": "^10.0.1",
111
- "@stylistic/eslint-plugin": "^5.10.0",
112
- "globals": "^17.12.0",
113
- "jsonc-parser": "^3.3.1"
114
- },
115
- "devDependencies": {
116
- "@changesets/cli": "^3.0.2",
117
- "@commitlint/cli": "^19.8.1",
118
- "@commitlint/config-conventional": "^19.8.1",
119
- "@commitlint/types": "^19.8.1",
120
- "@rollup/plugin-commonjs": "^28.0.6",
121
- "@rollup/plugin-node-resolve": "^16.0.1",
122
- "@rollup/plugin-typescript": "^12.1.4",
123
- "@types/node": "^24.1.0",
124
- "@vitest/eslint-plugin": "^1.6.27",
125
- "eslint": "^10.10.0",
126
- "eslint-config-flat-gitignore": "^2.4.0",
127
- "eslint-plugin-import-x": "^4.17.1",
128
- "eslint-plugin-jsonc": "^3.4.2",
129
- "eslint-plugin-perfectionist": "^5.11.0",
130
- "eslint-plugin-react-debug": "^5.19.0",
131
- "eslint-plugin-react-dom": "^5.19.0",
132
- "eslint-plugin-react-hooks-extra": "^2.13.0",
133
- "eslint-plugin-react-naming-convention": "^5.19.0",
134
- "eslint-plugin-react-web-api": "^5.19.0",
135
- "eslint-plugin-react-x": "^5.19.0",
136
- "eslint-plugin-tailwindcss": "^4.4.0",
137
- "eslint-typegen": "^2.3.1",
138
- "husky": "^9.1.7",
139
- "jiti": "^2.7.0",
140
- "jsonc-eslint-parser": "^3.3.0",
141
- "rimraf": "^6.0.1",
142
- "rollup": "^4.46.2",
143
- "rollup-plugin-dts": "^6.2.1",
144
- "tailwindcss": "^4.3.3",
145
- "tslib": "^2.8.1",
146
- "tsx": "^4.20.3",
147
- "typescript": "^5.8.3",
148
- "typescript-eslint": "^8.70.0",
149
- "vitest": "^4.0.18"
150
- }
151
- }
1
+ {
2
+ "name": "@chanyuxi/eslint-waltz",
3
+ "type": "module",
4
+ "version": "1.5.0",
5
+ "packageManager": "pnpm@10.4.1",
6
+ "description": "A composable ESLint flat config for JavaScript, TypeScript, JSONC, imports, React, Tailwind CSS, Perfectionist, and Vitest.",
7
+ "author": "chanyuxi <2438149743@qq.com> (https://github.com/chanyuxi)",
8
+ "license": "ISC",
9
+ "homepage": "https://github.com/chanyuxi/eslint-waltz",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/chanyuxi/eslint-waltz.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/chanyuxi/eslint-waltz/issues"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "keywords": [
21
+ "chanyuxi",
22
+ "eslint",
23
+ "config",
24
+ "integration"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "default": "./dist/index.js"
31
+ }
32
+ },
33
+ "bin": {
34
+ "eslint-waltz": "dist/cli.js"
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "engines": {
40
+ "node": ">=20.19.0"
41
+ },
42
+ "scripts": {
43
+ "build": "rimraf ./dist && pnpm gen && rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript",
44
+ "gen": "tsx scripts/typegen.ts",
45
+ "lint": "eslint .",
46
+ "lint:fix": "eslint . --fix",
47
+ "typeCheck": "pnpm gen && tsc --noEmit -p tsconfig.test.json",
48
+ "test": "vitest run",
49
+ "check": "pnpm run typeCheck && pnpm run lint && pnpm run test",
50
+ "changeset": "changeset",
51
+ "version": "changeset version && pnpm install --lockfile-only",
52
+ "release": "changeset publish",
53
+ "prepack": "pnpm build",
54
+ "prepare": "husky"
55
+ },
56
+ "peerDependencies": {
57
+ "@vitest/eslint-plugin": "^1.6.27",
58
+ "eslint": "^10.10.0",
59
+ "eslint-config-flat-gitignore": "^2.4.0",
60
+ "eslint-plugin-import-x": "^4.17.1",
61
+ "eslint-plugin-jsonc": "^3.4.2",
62
+ "eslint-plugin-perfectionist": "^5.11.0",
63
+ "eslint-plugin-react-debug": "^5.19.0",
64
+ "eslint-plugin-react-dom": "^5.19.0",
65
+ "eslint-plugin-react-hooks-extra": "^2.13.0",
66
+ "eslint-plugin-react-naming-convention": "^5.19.0",
67
+ "eslint-plugin-react-web-api": "^5.19.0",
68
+ "eslint-plugin-react-x": "^5.19.0",
69
+ "eslint-plugin-tailwindcss": "^4.4.0",
70
+ "jsonc-eslint-parser": "^3.3.0",
71
+ "tailwindcss": "^4.0.0",
72
+ "typescript-eslint": "^8.70.0"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "@vitest/eslint-plugin": {
76
+ "optional": true
77
+ },
78
+ "eslint-config-flat-gitignore": {
79
+ "optional": true
80
+ },
81
+ "eslint-plugin-react-debug": {
82
+ "optional": true
83
+ },
84
+ "eslint-plugin-react-dom": {
85
+ "optional": true
86
+ },
87
+ "eslint-plugin-react-hooks-extra": {
88
+ "optional": true
89
+ },
90
+ "eslint-plugin-react-naming-convention": {
91
+ "optional": true
92
+ },
93
+ "eslint-plugin-react-web-api": {
94
+ "optional": true
95
+ },
96
+ "eslint-plugin-react-x": {
97
+ "optional": true
98
+ },
99
+ "eslint-plugin-tailwindcss": {
100
+ "optional": true
101
+ },
102
+ "tailwindcss": {
103
+ "optional": true
104
+ },
105
+ "typescript-eslint": {
106
+ "optional": true
107
+ }
108
+ },
109
+ "dependencies": {
110
+ "@clack/prompts": "^1.8.1",
111
+ "@eslint/js": "^10.0.1",
112
+ "@stylistic/eslint-plugin": "^5.10.0",
113
+ "globals": "^17.12.0",
114
+ "jsonc-parser": "^3.3.1"
115
+ },
116
+ "devDependencies": {
117
+ "@changesets/cli": "^3.0.2",
118
+ "@commitlint/cli": "^19.8.1",
119
+ "@commitlint/config-conventional": "^19.8.1",
120
+ "@commitlint/types": "^19.8.1",
121
+ "@rollup/plugin-commonjs": "^28.0.6",
122
+ "@rollup/plugin-node-resolve": "^16.0.1",
123
+ "@rollup/plugin-typescript": "^12.1.4",
124
+ "@types/node": "^24.1.0",
125
+ "@vitest/eslint-plugin": "^1.6.27",
126
+ "eslint": "^10.10.0",
127
+ "eslint-config-flat-gitignore": "^2.4.0",
128
+ "eslint-plugin-import-x": "^4.17.1",
129
+ "eslint-plugin-jsonc": "^3.4.2",
130
+ "eslint-plugin-perfectionist": "^5.11.0",
131
+ "eslint-plugin-react-debug": "^5.19.0",
132
+ "eslint-plugin-react-dom": "^5.19.0",
133
+ "eslint-plugin-react-hooks-extra": "^2.13.0",
134
+ "eslint-plugin-react-naming-convention": "^5.19.0",
135
+ "eslint-plugin-react-web-api": "^5.19.0",
136
+ "eslint-plugin-react-x": "^5.19.0",
137
+ "eslint-plugin-tailwindcss": "^4.4.0",
138
+ "eslint-typegen": "^2.3.1",
139
+ "husky": "^9.1.7",
140
+ "jiti": "^2.7.0",
141
+ "jsonc-eslint-parser": "^3.3.0",
142
+ "rimraf": "^6.0.1",
143
+ "rollup": "^4.46.2",
144
+ "rollup-plugin-dts": "^6.2.1",
145
+ "tailwindcss": "^4.3.3",
146
+ "tslib": "^2.8.1",
147
+ "tsx": "^4.20.3",
148
+ "typescript": "^5.8.3",
149
+ "typescript-eslint": "^8.70.0",
150
+ "vitest": "^4.0.18"
151
+ }
152
+ }