@icebreakers/eslint-config 4.0.0 → 4.0.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
@@ -53,6 +53,9 @@ export default icebreaker({
53
53
  tailwindcss: {
54
54
  tailwindConfig: './tailwind.config.ts',
55
55
  },
56
+ unocss: {
57
+ strict: true,
58
+ },
56
59
  mdx: process.env.LINT_MDX === 'true',
57
60
  a11y: true,
58
61
  nestjs: true,
@@ -67,14 +70,53 @@ export default icebreaker({
67
70
  - `react` – defers to the upstream React preset and unlocks accessibility helpers when `a11y` is enabled. The required React lint plugins are bundled with this package.
68
71
  - `query` – toggles the TanStack Query plugin (`@tanstack/eslint-plugin-query`) and its recommended lint rules. Missing plugin installs are treated as a no-op.
69
72
  - `tailwindcss` – pass `true` to use the built-in Tailwind flat config or provide `{ entryPoint, tailwindConfig }` for Tailwind v4/v3 projects.
73
+ - `unocss` – pass `true` to use the upstream Antfu UnoCSS preset, or provide `{ strict, attributify, configPath }` to keep the same preset while using the Icebreaker wrapper API.
70
74
  - `mdx` – activates MDX linting via `eslint-plugin-mdx`.
71
75
  - `a11y` – wires in JSX (React) and Vue accessibility plugins. Missing framework-specific plugins are skipped independently.
72
76
  - `typescript` – extends the TypeScript preset and applies stricter unused diagnostics. Pair with `nestjs` for Nest specific adjustments.
73
77
  - `nestjs` – enables NestJS-centric TypeScript tweaks (empty decorated constructors, declaration merging, DI parameter properties, etc.).
74
- - `formatters` – keeps the built-in formatting rules enabled by default.
78
+ - `formatters` – keeps the built-in formatting rules enabled by default, including CSS/SCSS/Less formatting through `eslint-plugin-format`.
75
79
  - `test` – relaxes certain Vitest/Jest style rules (`test/prefer-lowercase-title`).
76
80
  - `weapp` – legacy alias for `miniProgram`; kept for backward compatibility.
77
81
 
82
+ ### Formatter Engines
83
+
84
+ `@icebreakers/eslint-config` still uses `eslint-plugin-format` as the formatter
85
+ bridge. The default formatter path stays aligned with the upstream Prettier
86
+ setup. Icebreaker still lets you opt specific file types into `oxfmt` when you
87
+ want to experiment or tune a project explicitly:
88
+
89
+ ```ts
90
+ import { icebreaker } from '@icebreakers/eslint-config'
91
+
92
+ export default icebreaker({
93
+ formatters: {
94
+ css: 'oxfmt',
95
+ html: 'oxfmt',
96
+ markdown: 'oxfmt',
97
+ graphql: 'oxfmt',
98
+ oxfmtOptions: {
99
+ lineWidth: 100,
100
+ },
101
+ },
102
+ })
103
+ ```
104
+
105
+ Support matrix:
106
+
107
+ - default: CSS/SCSS/Less, HTML, GraphQL, Markdown, XML, SVG, Astro, and Slidev
108
+ stay on the upstream Prettier path
109
+ - `css: 'oxfmt'` also switches SCSS and Less to `format/oxfmt`
110
+ - `html: 'oxfmt'` switches HTML to `format/oxfmt`
111
+ - `markdown: 'oxfmt'` switches Markdown to `format/oxfmt`
112
+ - `graphql: 'oxfmt'` switches GraphQL to `format/oxfmt`
113
+
114
+ Current limits:
115
+
116
+ - This is not a full `format/prettier` to `format/oxfmt` migration layer
117
+ - `markdown: 'oxfmt'` cannot be combined with `formatters.slidev`
118
+ - `oxfmtOptions` are passed directly to `format/oxfmt`
119
+
78
120
  ### Mini Program Preset
79
121
 
80
122
  `miniProgram: true` is the recommended API for `weapp-vite`, `wevu`, and native
@@ -162,6 +204,58 @@ export default icebreaker({
162
204
  the `@icebreakers/stylelint-config` options (`presets`, `tailwindcssPreset`,
163
205
  `ignores`, `extends`, `overrides`, `rules`).
164
206
 
207
+ The ESLint bridge surfaces Stylelint diagnostics, but it does not replace a
208
+ full standalone Stylelint workflow. If you want Stylelint CLI, editor-native
209
+ Stylelint integration, or dedicated `stylelint --fix` runs, install `stylelint`
210
+ and `@icebreakers/stylelint-config` in the consuming project as well.
211
+
212
+ Recommended consumer scripts:
213
+
214
+ ```json
215
+ {
216
+ "scripts": {
217
+ "lint": "eslint .",
218
+ "lint:fix": "eslint . --fix",
219
+ "lint:styles": "stylelint \"src/**/*.{css,scss,vue}\"",
220
+ "lint:styles:fix": "stylelint \"src/**/*.{css,scss,vue}\" --fix"
221
+ }
222
+ }
223
+ ```
224
+
225
+ Use `lint:fix` when you want a single ESLint-driven formatting pass, including
226
+ CSS-family files via `formatters`. Add the separate Stylelint scripts when you
227
+ want the full Stylelint toolchain on top.
228
+
229
+ ### UnoCSS Projects
230
+
231
+ The UnoCSS integration is still powered by the upstream Antfu preset, but
232
+ `@icebreakers/eslint-config` adds a small wrapper so the config file path can be
233
+ declared next to the other UnoCSS options:
234
+
235
+ ```ts
236
+ import path from 'node:path'
237
+ import { icebreaker } from '@icebreakers/eslint-config'
238
+
239
+ export default icebreaker({
240
+ unocss: {
241
+ strict: true,
242
+ attributify: false,
243
+ configPath: path.resolve(process.cwd(), './uno.config.ts'),
244
+ },
245
+ })
246
+ ```
247
+
248
+ Behavior details:
249
+
250
+ - `unocss: true` enables the upstream Antfu UnoCSS preset unchanged.
251
+ - `unocss.configPath` is an Icebreaker wrapper for `settings.unocss.configPath`.
252
+ - If `configPath` is omitted, UnoCSS still searches the lint project root for
253
+ `uno.config.*`.
254
+ - If both `unocss.configPath` and `settings.unocss.configPath` are provided,
255
+ `unocss.configPath` wins.
256
+ - If `@unocss/eslint-plugin` is unavailable, the UnoCSS preset is skipped
257
+ instead of throwing.
258
+
165
259
  ### NestJS Projects
166
260
 
167
261
  Enable `nestjs: true` together with the TypeScript preset to apply rules tailored for Nest idioms:
@@ -194,10 +288,14 @@ You may also pass other flat configs (e.g. from in-house presets) as additional
194
288
 
195
289
  - Install the VS Code ESLint extension (`>=3.0.10`).
196
290
  - Set `"eslint.experimental.useFlatConfig": true` for older VS Code builds.
197
- - Use `pnpm lint -- --fix` in a pre-commit hook for consistent formatting.
291
+ - Use `lint:fix` for the default ESLint-driven formatting flow, including
292
+ CSS/SCSS/Less. Add `lint:styles:fix` when your project also uses standalone
293
+ Stylelint runs.
294
+ - If you opt specific file types into `oxfmt`, the same `eslint --fix` flow
295
+ continues to work. Only the formatter engine changes.
198
296
 
199
297
  ## Troubleshooting
200
298
 
201
- - Missing plugin errors usually mean a feature is enabled without its optional dependency being installed in the current workspace. React and Next related presets now auto-skip in that case; other features can be added with `pnpm add -D`.
299
+ - Missing plugin errors usually mean a feature is enabled without its optional dependency being installed in the current workspace. React, Next, and UnoCSS related presets now auto-skip in that case; other features can be added with `pnpm add -D`.
202
300
  - When combining legacy `.eslintrc` projects, prefer `icebreakerLegacy()` and move overrides into flat config format incrementally.
203
301
  - Tailwind class validation reads from your `tailwind.config.*`; double check the path when using monorepo roots or custom build tooling.
package/README.zh.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @icebreakers/eslint-config
2
2
 
3
+ ## UnoCSS 包装层
4
+
5
+ `@icebreakers/eslint-config` 的 UnoCSS 能力仍然复用上游
6
+ `@antfu/eslint-config`,但现在额外提供了一层更贴近本仓库风格的包装 API。
7
+
8
+ 推荐写法:
9
+
10
+ ```ts
11
+ import path from 'node:path'
12
+ import { icebreaker } from '@icebreakers/eslint-config'
13
+
14
+ export default icebreaker({
15
+ unocss: {
16
+ strict: true,
17
+ attributify: false,
18
+ configPath: path.resolve(process.cwd(), './uno.config.ts'),
19
+ },
20
+ })
21
+ ```
22
+
23
+ 行为说明:
24
+
25
+ - `unocss: true`:直接启用上游 Antfu 的 UnoCSS preset
26
+ - `unocss.configPath`:会被自动映射为 `settings.unocss.configPath`
27
+ - 不传 `configPath`:仍然按 UnoCSS 默认行为,从 lint 项目根目录查找 `uno.config.*`
28
+ - 同时传 `unocss.configPath` 和 `settings.unocss.configPath`:以前者为准
29
+ - 如果当前 workspace 没有安装 `@unocss/eslint-plugin`:该 preset 会自动跳过,不会直接抛错
30
+
3
31
  ## 简介
4
32
 
5
33
  `@icebreakers/eslint-config` 基于 `@antfu/eslint-config` 的 flat config 预设,额外补充了 Tailwind CSS、MDX、Vue 无障碍以及 Icebreaker 团队常用的 TypeScript 默认规则。它返回一个 `FlatConfigComposer`,可以按需启用不同预设,并继续追加工作区特定的覆盖项。
@@ -69,10 +97,47 @@ export default icebreaker({
69
97
  - `a11y`:按需引入 JSX 与 Vue 的无障碍规则,缺少某一侧插件时只跳过对应框架配置。
70
98
  - `typescript`:开启 TypeScript 预设,加强未使用诊断,可与 `nestjs` 搭配使用以获得 Nest 专属优化。
71
99
  - `nestjs`:针对 NestJS 场景做 TypeScript 调整(允许带装饰器的空构造函数、依赖注入参数属性、声明合并等)。
72
- - `formatters`:默认启用格式化辅助规则。
100
+ - `formatters`:默认启用格式化辅助规则,其中也包括通过 `eslint-plugin-format` 处理 CSS / SCSS / Less。
73
101
  - `test`:放宽 Vitest / Jest 常见规则,例如关闭 `test/prefer-lowercase-title`。
74
102
  - `weapp`:`miniProgram` 的兼容别名,保留但不再推荐作为主入口。
75
103
 
104
+ ### 格式化引擎
105
+
106
+ `@icebreakers/eslint-config` 仍然通过 `eslint-plugin-format` 承载样式类与文档类文件的格式化链路。
107
+ 默认行为仍然与上游 Prettier 链路保持一致;如果你想在项目里做专项实验,
108
+ 也可以继续手动把部分文件类型切到 `oxfmt`:
109
+
110
+ ```ts
111
+ import { icebreaker } from '@icebreakers/eslint-config'
112
+
113
+ export default icebreaker({
114
+ formatters: {
115
+ css: 'oxfmt',
116
+ html: 'oxfmt',
117
+ markdown: 'oxfmt',
118
+ graphql: 'oxfmt',
119
+ oxfmtOptions: {
120
+ lineWidth: 100,
121
+ },
122
+ },
123
+ })
124
+ ```
125
+
126
+ 支持范围:
127
+
128
+ - 默认值:CSS / SCSS / Less、HTML、GraphQL、Markdown、XML、SVG、Astro、Slidev
129
+ 继续走上游 Prettier 链路
130
+ - `css: 'oxfmt'`:会同时切换 CSS / SCSS / Less
131
+ - `html: 'oxfmt'`:切换 HTML
132
+ - `markdown: 'oxfmt'`:切换 Markdown
133
+ - `graphql: 'oxfmt'`:切换 GraphQL
134
+
135
+ 当前限制:
136
+
137
+ - 这不是一次完整的 `format/prettier -> format/oxfmt` 全量迁移
138
+ - `markdown: 'oxfmt'` 不能与 `formatters.slidev` 同时开启
139
+ - `oxfmtOptions` 会原样透传给 `format/oxfmt`
140
+
76
141
  ### 小程序预设
77
142
 
78
143
  推荐在 `weapp-vite`、`wevu`、原生小程序模板里统一使用 `miniProgram: true`。
@@ -163,6 +228,28 @@ export default icebreaker({
163
228
  `@icebreakers/stylelint-config` 的选项结构,例如 `presets`、
164
229
  `tailwindcssPreset`、`ignores`、`extends`、`overrides`、`rules`。
165
230
 
231
+ 需要注意的是,这个 bridge 只负责把 Stylelint 诊断桥接到 ESLint,
232
+ 并不等价于完整的 Stylelint 工作流。如果你需要独立的 Stylelint CLI、
233
+ 编辑器内原生 Stylelint 集成,或单独执行 `stylelint --fix`,仍然建议在
234
+ 接入方项目里额外安装 `stylelint` 和 `@icebreakers/stylelint-config`。
235
+
236
+ 推荐给接入方使用的脚本约定:
237
+
238
+ ```json
239
+ {
240
+ "scripts": {
241
+ "lint": "eslint .",
242
+ "lint:fix": "eslint . --fix",
243
+ "lint:styles": "stylelint \"src/**/*.{css,scss,vue}\"",
244
+ "lint:styles:fix": "stylelint \"src/**/*.{css,scss,vue}\" --fix"
245
+ }
246
+ }
247
+ ```
248
+
249
+ 如果你希望继续使用一条 `eslint --fix` 主链路,那么 `lint:fix` 默认也会
250
+ 通过 `formatters` 处理 CSS / SCSS / Less。只有在项目需要完整 Stylelint
251
+ 工具链时,再补充 `lint:styles` / `lint:styles:fix`。
252
+
166
253
  ### NestJS 项目
167
254
 
168
255
  建议在 Nest 项目中同时开启 `typescript` 与 `nestjs`,以便应用以下定制:
@@ -195,7 +282,11 @@ export default icebreaker(
195
282
 
196
283
  - VS Code 安装 ESLint 扩展(版本需 ≥ 3.0.10)。
197
284
  - 老版本 VS Code 需在设置中启用 `"eslint.experimental.useFlatConfig": true`。
198
- - Git 钩子或 CI 中执行 `pnpm lint -- --fix` 确保格式一致。
285
+ - 默认可使用 `lint:fix` 作为 ESLint 主修复链路,其中包含 CSS / SCSS /
286
+ Less 的格式化。只有在项目额外启用独立 Stylelint 工作流时,再运行
287
+ `lint:styles:fix`。
288
+ - 如果你把部分文件类型切到 `oxfmt`,仍然继续使用同一条 `eslint --fix`
289
+ 链路,只是底层格式化引擎发生变化。
199
290
 
200
291
  ## 常见问题
201
292
 
package/dist/index.cjs CHANGED
@@ -188,8 +188,8 @@ const MDX_PACKAGES = ["eslint-plugin-mdx"];
188
188
  const VUE_A11Y_PACKAGES = ["eslint-plugin-vuejs-accessibility"];
189
189
  const REACT_A11Y_PACKAGES = ["eslint-plugin-jsx-a11y"];
190
190
  const QUERY_PACKAGES = ["@tanstack/eslint-plugin-query"];
191
- function resolveStylelintConfigLoader() {
192
- return require("url").pathToFileURL(__filename).href.endsWith(".ts") ? new URL("./stylelint.ts", require("url").pathToFileURL(__filename).href).href : new URL("./stylelint.js", require("url").pathToFileURL(__filename).href).href;
191
+ function resolveStylelintConfigLoader(moduleUrl = require("url").pathToFileURL(__filename).href) {
192
+ return moduleUrl.endsWith(".ts") ? new URL("./stylelint.ts", moduleUrl).href : new URL("./stylelint.js", moduleUrl).href;
193
193
  }
194
194
  function resolveTailwindPresets(option) {
195
195
  if (!option) return [];
@@ -294,7 +294,7 @@ function resolveQueryPresets(isEnabled) {
294
294
  }
295
295
  //#endregion
296
296
  //#region ../../node_modules/.pnpm/defu@6.1.6/node_modules/defu/dist/defu.mjs
297
- function isPlainObject(value) {
297
+ function isPlainObject$1(value) {
298
298
  if (value === null || typeof value !== "object") return false;
299
299
  const prototype = Object.getPrototypeOf(value);
300
300
  if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false;
@@ -303,7 +303,7 @@ function isPlainObject(value) {
303
303
  return true;
304
304
  }
305
305
  function _defu(baseObject, defaults, namespace = ".", merger) {
306
- if (!isPlainObject(defaults)) return _defu(baseObject, {}, namespace, merger);
306
+ if (!isPlainObject$1(defaults)) return _defu(baseObject, {}, namespace, merger);
307
307
  const object = { ...defaults };
308
308
  for (const key of Object.keys(baseObject)) {
309
309
  if (key === "__proto__" || key === "constructor") continue;
@@ -311,7 +311,7 @@ function _defu(baseObject, defaults, namespace = ".", merger) {
311
311
  if (value === null || value === void 0) continue;
312
312
  if (merger && merger(object, key, value, namespace)) continue;
313
313
  if (Array.isArray(value) && Array.isArray(object[key])) object[key] = [...value, ...object[key]];
314
- else if (isPlainObject(value) && isPlainObject(object[key])) object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger);
314
+ else if (isPlainObject$1(value) && isPlainObject$1(object[key])) object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger);
315
315
  else object[key] = value;
316
316
  }
317
317
  return object;
@@ -407,13 +407,14 @@ function isPackageAvailable(name, paths) {
407
407
  }
408
408
  function getDefaultFormatterOptions(cwd = node_process.default.cwd()) {
409
409
  const hasXmlPlugin = isPackageAvailable("@prettier/plugin-xml", [ANTFU_PACKAGE_DIR]);
410
+ const hasSlidev = isPackageAvailable("@slidev/cli", [cwd]);
410
411
  return {
411
412
  astro: isPackageAvailable("prettier-plugin-astro", [ANTFU_PACKAGE_DIR]),
412
413
  css: true,
413
414
  graphql: true,
414
415
  html: true,
415
416
  markdown: true,
416
- slidev: isPackageAvailable("@slidev/cli", [cwd]),
417
+ slidev: hasSlidev,
417
418
  svg: hasXmlPlugin,
418
419
  xml: hasXmlPlugin
419
420
  };
@@ -483,11 +484,14 @@ function resolveFormattersOption(input, cwd = node_process.default.cwd()) {
483
484
  if (input === false) return false;
484
485
  const defaults = getDefaultFormatterOptions(cwd);
485
486
  const inferredEndOfLine = inferPrettierEndOfLineFromEditorConfig(cwd);
486
- const defaultsWithPrettier = inferredEndOfLine ? defu({ prettierOptions: { endOfLine: inferredEndOfLine } }, defaults) : defaults;
487
- if (input === void 0) return inferredEndOfLine ? defaultsWithPrettier : true;
488
- if (input === true) return defaultsWithPrettier;
489
- if (isObject(input)) return defu(input, defaultsWithPrettier);
490
- return defaultsWithPrettier;
487
+ const defaultsWithFormattingEngines = inferredEndOfLine ? defu({
488
+ oxfmtOptions: { endOfLine: inferredEndOfLine },
489
+ prettierOptions: { endOfLine: inferredEndOfLine }
490
+ }, defaults) : defaults;
491
+ if (input === void 0) return defaultsWithFormattingEngines;
492
+ if (input === true) return defaultsWithFormattingEngines;
493
+ if (isObject(input)) return defu(input, defaultsWithFormattingEngines);
494
+ return defaultsWithFormattingEngines;
491
495
  }
492
496
  function resolveUserOptions(options) {
493
497
  const resolved = defu({}, options ?? {}, BASE_DEFAULTS);
@@ -578,14 +582,74 @@ const OPTIONAL_ANTFU_FEATURE_PACKAGES = {
578
582
  "eslint-plugin-react-hooks",
579
583
  "eslint-plugin-react-refresh"
580
584
  ],
581
- nextjs: ["@next/eslint-plugin-next"]
585
+ nextjs: ["@next/eslint-plugin-next"],
586
+ unocss: ["@unocss/eslint-plugin"]
582
587
  };
588
+ function isPlainObject(value) {
589
+ return value !== null && Object.prototype.toString.call(value) === "[object Object]";
590
+ }
591
+ function cloneUserDefinedOptions(options) {
592
+ const { settings, ...restOptions } = options;
593
+ if (settings === void 0) return { ...restOptions };
594
+ return {
595
+ ...restOptions,
596
+ settings
597
+ };
598
+ }
599
+ function getSettingsRecord(settings) {
600
+ return isPlainObject(settings) ? settings : {};
601
+ }
602
+ function removeNamespacedSetting(options, namespace) {
603
+ if (!isPlainObject(options.settings)) return options;
604
+ const settings = getSettingsRecord(options.settings);
605
+ if (!(namespace in settings)) return options;
606
+ const { [namespace]: _unusedNamespace, ...restSettings } = settings;
607
+ const { settings: _settings, ...restOptions } = options;
608
+ if (Object.keys(restSettings).length === 0) return { ...restOptions };
609
+ return {
610
+ ...restOptions,
611
+ settings: restSettings
612
+ };
613
+ }
583
614
  function normalizeOptionalAntfuFeatures(options) {
584
- const normalized = { ...options };
615
+ const normalized = cloneUserDefinedOptions(options);
585
616
  if (normalized.react && !hasAllPackages([...OPTIONAL_ANTFU_FEATURE_PACKAGES.react])) normalized.react = false;
586
617
  if (normalized.nextjs && !hasAllPackages([...OPTIONAL_ANTFU_FEATURE_PACKAGES.nextjs])) normalized.nextjs = false;
618
+ if (normalized.unocss && !hasAllPackages([...OPTIONAL_ANTFU_FEATURE_PACKAGES.unocss])) {
619
+ normalized.unocss = false;
620
+ return removeNamespacedSetting(normalized, "unocss");
621
+ }
587
622
  return normalized;
588
623
  }
624
+ function mergeNamespacedSetting(options, namespace, value) {
625
+ const currentSettings = getSettingsRecord(options.settings);
626
+ const currentNamespaceSettings = isPlainObject(currentSettings[namespace]) ? currentSettings[namespace] : {};
627
+ return {
628
+ ...options,
629
+ settings: {
630
+ ...currentSettings,
631
+ [namespace]: {
632
+ ...currentNamespaceSettings,
633
+ ...value
634
+ }
635
+ }
636
+ };
637
+ }
638
+ function normalizeUnoCssOptions(options) {
639
+ if (!options.unocss || typeof options.unocss !== "object") return options;
640
+ const { configPath, ...unocssOptions } = options.unocss;
641
+ const { settings, ...restOptions } = options;
642
+ const normalized = settings === void 0 ? {
643
+ ...restOptions,
644
+ unocss: unocssOptions
645
+ } : {
646
+ ...restOptions,
647
+ settings,
648
+ unocss: unocssOptions
649
+ };
650
+ if (!configPath) return normalized;
651
+ return mergeNamespacedSetting(normalized, "unocss", { configPath });
652
+ }
589
653
  function hasGlobalIgnoreShape(config) {
590
654
  const keys = Object.keys(config).filter((key) => key !== "name");
591
655
  return keys.length === 1 && keys[0] === "ignores";
@@ -615,13 +679,70 @@ function normalizeUserConfig(userConfig) {
615
679
  const resolvedUserConfig = userConfig;
616
680
  return isComposer(resolvedUserConfig) ? resolvedUserConfig : normalizeResolvedUserConfig(resolvedUserConfig);
617
681
  }
682
+ function isFormatterOptionsObject(value) {
683
+ return !!value && typeof value === "object" && !Array.isArray(value);
684
+ }
685
+ function toOxfmtRuleEntry(options) {
686
+ return ["error", { ...options ?? {} }];
687
+ }
688
+ function normalizePrettierFormatterForAntfu(formatter) {
689
+ return formatter === "oxfmt" ? true : formatter;
690
+ }
691
+ function normalizeMarkdownFormatterForAntfu(formatter) {
692
+ return formatter === "oxfmt" ? true : formatter;
693
+ }
694
+ function toAntfuOptions(options) {
695
+ const { formatters, ...restOptions } = options;
696
+ if (!isFormatterOptionsObject(formatters)) return restOptions;
697
+ const { oxfmtOptions: _oxfmtOptions, css, html, markdown, graphql, ...restFormatters } = formatters;
698
+ return {
699
+ ...restOptions,
700
+ formatters: {
701
+ ...restFormatters,
702
+ ...css === void 0 ? {} : { css: normalizePrettierFormatterForAntfu(css) },
703
+ ...html === void 0 ? {} : { html: normalizePrettierFormatterForAntfu(html) },
704
+ ...markdown === void 0 ? {} : { markdown: normalizeMarkdownFormatterForAntfu(markdown) },
705
+ ...graphql === void 0 ? {} : { graphql: normalizePrettierFormatterForAntfu(graphql) }
706
+ }
707
+ };
708
+ }
709
+ function applyOxfmtFormatterOverrides(composer, formatters) {
710
+ if (!isFormatterOptionsObject(formatters)) return composer;
711
+ if (formatters.markdown === "oxfmt" && formatters.slidev) throw new Error("`formatters.markdown: \"oxfmt\"` cannot be combined with `formatters.slidev`.");
712
+ const oxfmtOptions = formatters.oxfmtOptions;
713
+ let nextComposer = composer;
714
+ if (formatters.css === "oxfmt") for (const name of [
715
+ "antfu/formatter/css",
716
+ "antfu/formatter/scss",
717
+ "antfu/formatter/less"
718
+ ]) nextComposer = nextComposer.override(name, { rules: {
719
+ "format/oxfmt": toOxfmtRuleEntry(oxfmtOptions),
720
+ "format/prettier": "off"
721
+ } });
722
+ if (formatters.html === "oxfmt") nextComposer = nextComposer.override("antfu/formatter/html", { rules: {
723
+ "format/oxfmt": toOxfmtRuleEntry(oxfmtOptions),
724
+ "format/prettier": "off"
725
+ } });
726
+ if (formatters.markdown === "oxfmt") nextComposer = nextComposer.override("antfu/formatter/markdown", { rules: {
727
+ "format/oxfmt": toOxfmtRuleEntry(oxfmtOptions),
728
+ "format/prettier": "off",
729
+ "format/dprint": "off"
730
+ } });
731
+ if (formatters.graphql === "oxfmt") nextComposer = nextComposer.override("antfu/formatter/graphql", { rules: {
732
+ "format/oxfmt": toOxfmtRuleEntry(oxfmtOptions),
733
+ "format/prettier": "off"
734
+ } });
735
+ return nextComposer;
736
+ }
618
737
  function icebreaker(options = {}, ...userConfigs) {
619
738
  const [resolved, ...presets] = getPresets(options);
620
- return (0, antfu_exports.antfu)(normalizeOptionalAntfuFeatures(resolved), ...presets, ...userConfigs.map(normalizeUserConfig));
739
+ const normalized = normalizeUnoCssOptions(normalizeOptionalAntfuFeatures(resolved));
740
+ return applyOxfmtFormatterOverrides((0, antfu_exports.antfu)(toAntfuOptions(normalized), ...presets, ...userConfigs.map(normalizeUserConfig)), normalized.formatters);
621
741
  }
622
742
  function icebreakerLegacy(options = {}, ...userConfigs) {
623
743
  const [resolved, ...presets] = getPresets(options, "legacy");
624
- return (0, antfu_exports.antfu)(normalizeOptionalAntfuFeatures(resolved), ...presets, ...userConfigs.map(normalizeUserConfig));
744
+ const normalized = normalizeUnoCssOptions(normalizeOptionalAntfuFeatures(resolved));
745
+ return applyOxfmtFormatterOverrides((0, antfu_exports.antfu)(toAntfuOptions(normalized), ...presets, ...userConfigs.map(normalizeUserConfig)), normalized.formatters);
625
746
  }
626
747
  //#endregion
627
748
  exports.__toESM = __toESM;