eslint-plugin-oxfmt 0.0.0 → 0.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
@@ -5,7 +5,7 @@
5
5
  [![NPM DOWNLOADS](https://img.shields.io/npm/dy/eslint-plugin-oxfmt.svg)](https://www.npmjs.com/package/eslint-plugin-oxfmt)
6
6
  [![LICENSE](https://img.shields.io/github/license/ntnyq/eslint-plugin-oxfmt.svg)](https://github.com/ntnyq/eslint-plugin-oxfmt/blob/main/LICENSE)
7
7
 
8
- A starter template for node projects.
8
+ An ESLint plugin that format code via oxfmt.
9
9
 
10
10
  ## Install
11
11
 
@@ -21,6 +21,33 @@ yarn add eslint-plugin-oxfmt
21
21
  pnpm add eslint-plugin-oxfmt
22
22
  ```
23
23
 
24
+ ## Usage
25
+
26
+ ```ts
27
+ // eslint.config.mjs
28
+
29
+ import pluginOxfmt from 'eslint-plugin-oxfmt'
30
+ import { defineConfig } from 'eslint/config'
31
+
32
+ export default defineConfig([
33
+ {
34
+ ...pluginOxfmt.configs.recommended,
35
+ files: ['**/*.{js,ts,mjs,cjs,jsx,tsx}'],
36
+ rules: {
37
+ 'oxfmt/oxfmt': [
38
+ 'error',
39
+ {
40
+ semi: false,
41
+ useTabs: true,
42
+ singleAttributePerLine: true,
43
+ trailingComma: 'all',
44
+ },
45
+ ],
46
+ },
47
+ },
48
+ ])
49
+ ```
50
+
24
51
  ## License
25
52
 
26
53
  [MIT](./LICENSE) License © 2025-PRESENT [ntnyq](https://github.com/ntnyq)
@@ -0,0 +1,39 @@
1
+ import * as eslint0 from "eslint";
2
+ import { Linter, Rule } from "eslint";
3
+
4
+ //#region src/types.d.ts
5
+ interface PluginOxfmt {
6
+ configs: {
7
+ recommended: Linter.Config<Linter.RulesRecord>;
8
+ };
9
+ meta: {
10
+ name: string;
11
+ version: string;
12
+ };
13
+ rules: {
14
+ oxfmt: Rule.RuleModule;
15
+ };
16
+ }
17
+ //#endregion
18
+ //#region src/meta.d.ts
19
+ declare const meta: {
20
+ name: string;
21
+ version: string;
22
+ };
23
+ //#endregion
24
+ //#region src/rules/index.d.ts
25
+ declare const rules: {
26
+ oxfmt: eslint0.Rule.RuleModule;
27
+ };
28
+ //#endregion
29
+ //#region src/parser.d.ts
30
+ declare const parserPlain: Linter.Parser;
31
+ //#endregion
32
+ //#region src/configs.d.ts
33
+ declare const recommended: Linter.Config<Linter.RulesRecord>;
34
+ declare const configs: PluginOxfmt['configs'];
35
+ //#endregion
36
+ //#region src/index.d.ts
37
+ declare const plugin: PluginOxfmt;
38
+ //#endregion
39
+ export { configs, plugin as default, plugin, meta, parserPlain, recommended, rules };
package/dist/index.mjs ADDED
@@ -0,0 +1,357 @@
1
+ import { join } from "node:path";
2
+ import { createSyncFn } from "synckit";
3
+ import { URL, fileURLToPath } from "node:url";
4
+ import { DIFFERENCE, generateDifferences } from "generate-differences";
5
+
6
+ //#region rolldown:runtime
7
+ var __defProp = Object.defineProperty;
8
+ var __export = (all, symbols) => {
9
+ let target = {};
10
+ for (var name$2 in all) {
11
+ __defProp(target, name$2, {
12
+ get: all[name$2],
13
+ enumerable: true
14
+ });
15
+ }
16
+ if (symbols) {
17
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
18
+ }
19
+ return target;
20
+ };
21
+
22
+ //#endregion
23
+ //#region node_modules/.pnpm/eslint-parser-plain@0.1.1/node_modules/eslint-parser-plain/dist/index.mjs
24
+ var dist_exports = /* @__PURE__ */ __export({
25
+ meta: () => meta$1,
26
+ parseForESLint: () => parseForESLint
27
+ });
28
+ const name$1 = "eslint-parser-plain";
29
+ const version$1 = "0.1.1";
30
+ const parseForESLint = (code) => ({
31
+ ast: {
32
+ type: "Program",
33
+ loc: {
34
+ start: 0,
35
+ end: code.length
36
+ },
37
+ range: [0, code.length],
38
+ body: [],
39
+ comments: [],
40
+ tokens: []
41
+ },
42
+ services: { isPlain: true },
43
+ scopeManager: null,
44
+ visitorKeys: { Program: [] }
45
+ });
46
+ const meta$1 = {
47
+ name: name$1,
48
+ version: version$1
49
+ };
50
+
51
+ //#endregion
52
+ //#region src/parser.ts
53
+ const parserPlain = dist_exports;
54
+
55
+ //#endregion
56
+ //#region src/configs.ts
57
+ const recommended = {
58
+ name: "oxfmt/recommended",
59
+ languageOptions: { parser: parserPlain },
60
+ plugins: { get oxfmt() {
61
+ return src_default;
62
+ } },
63
+ rules: { "oxfmt/oxfmt": "error" }
64
+ };
65
+ const configs = { recommended };
66
+
67
+ //#endregion
68
+ //#region package.json
69
+ var name = "eslint-plugin-oxfmt";
70
+ var version = "0.0.1";
71
+
72
+ //#endregion
73
+ //#region src/meta.ts
74
+ const meta = {
75
+ name,
76
+ version
77
+ };
78
+
79
+ //#endregion
80
+ //#region src/dir.ts
81
+ const dirWorkers = fileURLToPath(new URL("../workers", import.meta.url));
82
+
83
+ //#endregion
84
+ //#region node_modules/.pnpm/show-invisibles@0.0.1/node_modules/show-invisibles/dist/index.mjs
85
+ const DEFAULT_MAPPINGS = /* @__PURE__ */ new Map([
86
+ [" ", "·"],
87
+ ["\n", "⏎"],
88
+ ["\r", "␍"],
89
+ [" ", "↹"]
90
+ ]);
91
+ function showInvisibles(input, options = {}) {
92
+ const { mappings = DEFAULT_MAPPINGS } = options;
93
+ if (typeof input !== "string") throw new TypeError(`Expected input to type string, got ${typeof input}`);
94
+ return input.split("").map((c) => mappings.get(c) || c).join("");
95
+ }
96
+
97
+ //#endregion
98
+ //#region src/reporter.ts
99
+ /**
100
+ * @copyright {@link https://github.com/antfu/eslint-formatting-reporter}
101
+ */
102
+ const messages = {
103
+ [DIFFERENCE.DELETE]: "Delete `{{ deleteText }}`",
104
+ [DIFFERENCE.INSERT]: "Insert `{{ insertText }}`",
105
+ [DIFFERENCE.REPLACE]: "Replace `{{ deleteText }}` with `{{ insertText }}`"
106
+ };
107
+ function reportDifferences(context, source, formatted, offset = 0) {
108
+ if (source === formatted) return;
109
+ const differences = generateDifferences(source, formatted);
110
+ for (const difference of differences) _reportDifference(context, difference, offset);
111
+ }
112
+ function _reportDifference(context, difference, rangeOffset = 0) {
113
+ const { offset, operation } = difference;
114
+ let deleteText = "";
115
+ let insertText = "";
116
+ if ("deleteText" in difference && difference.deleteText) deleteText = difference.deleteText;
117
+ if ("insertText" in difference && difference.insertText) insertText = difference.insertText;
118
+ const range = [offset + rangeOffset, offset + rangeOffset + deleteText.length];
119
+ const [start, end] = range.map((index) => context.sourceCode.getLocFromIndex(index));
120
+ context.report({
121
+ loc: {
122
+ end,
123
+ start
124
+ },
125
+ messageId: operation,
126
+ data: {
127
+ deleteText: showInvisibles(deleteText),
128
+ insertText: showInvisibles(insertText)
129
+ },
130
+ fix: (fixer) => fixer.replaceTextRange(range, insertText)
131
+ });
132
+ }
133
+
134
+ //#endregion
135
+ //#region src/rules/oxfmt.ts
136
+ let formatViaOxfmt;
137
+ const oxfmt = {
138
+ meta: {
139
+ defaultOptions: [],
140
+ fixable: "code",
141
+ messages,
142
+ type: "layout",
143
+ docs: {
144
+ description: "Format code via oxfmt",
145
+ recommended: true,
146
+ url: "https://github.com/ntnyq/eslint-plugin-oxfmt"
147
+ },
148
+ schema: [{
149
+ additionalProperties: false,
150
+ type: "object",
151
+ properties: {
152
+ arrowParens: {
153
+ description: `Include parentheses around a sole arrow function parameter. (Default: "always")`,
154
+ enum: ["always", "avoid"],
155
+ type: "string"
156
+ },
157
+ bracketSameLine: {
158
+ description: `Put the > of a multi-line JSX element at the end of the last line\ninstead of being alone on the next line. (Default: false)`,
159
+ type: "boolean"
160
+ },
161
+ bracketSpacing: {
162
+ description: `Print spaces between brackets in object literals. (Default: true)`,
163
+ type: "boolean"
164
+ },
165
+ embeddedLanguageFormatting: {
166
+ description: `Control whether formats quoted code embedded in the file. (Default: "auto")`,
167
+ enum: ["auto", "off"],
168
+ type: "string"
169
+ },
170
+ endOfLine: {
171
+ description: `Which end of line characters to apply. (Default: "lf")`,
172
+ enum: [
173
+ "lf",
174
+ "crlf",
175
+ "cr"
176
+ ],
177
+ type: "string"
178
+ },
179
+ experimentalSortImports: {
180
+ additionalProperties: false,
181
+ description: `Experimental: Sort import statements. Disabled by default.`,
182
+ type: "object",
183
+ properties: {
184
+ groups: {
185
+ description: `Custom groups configuration for organizing imports.\nEach array element represents a group, and multiple group names in the same array are treated as one.\nAccepts both string and string[] as group elements.`,
186
+ type: "array",
187
+ items: {
188
+ type: "array",
189
+ items: { type: "string" }
190
+ }
191
+ },
192
+ ignoreCase: {
193
+ description: `Ignore case when sorting. (Default: true)`,
194
+ type: "boolean"
195
+ },
196
+ internalPattern: {
197
+ description: `Glob patterns to identify internal imports.`,
198
+ type: "array",
199
+ items: { type: "string" }
200
+ },
201
+ newlinesBetween: {
202
+ description: `Add newlines between import groups. (Default: true)`,
203
+ type: "boolean"
204
+ },
205
+ order: {
206
+ description: `Sort order. (Default: "asc")`,
207
+ enum: ["asc", "desc"],
208
+ type: "string"
209
+ },
210
+ partitionByComment: {
211
+ description: `Partition imports by comments. (Default: false)`,
212
+ type: "boolean"
213
+ },
214
+ partitionByNewline: {
215
+ description: `Partition imports by newlines. (Default: false)`,
216
+ type: "boolean"
217
+ },
218
+ sortSideEffects: {
219
+ description: `Sort side-effect imports. (Default: "false")`,
220
+ type: "boolean"
221
+ }
222
+ }
223
+ },
224
+ experimentalSortPackageJson: {
225
+ description: `Experimental: Sort package.json keys. (Default: true)`,
226
+ type: "boolean"
227
+ },
228
+ ignorePatterns: {
229
+ description: `Ignore files matching these glob patterns. Current working directory is used as the root.`,
230
+ type: "array",
231
+ items: { type: "string" }
232
+ },
233
+ jsxSingleQuote: {
234
+ description: `Use single quotes instead of double quotes in JSX. (Default: false)`,
235
+ type: "boolean"
236
+ },
237
+ objectWrap: {
238
+ description: `How to wrap object literals when they could fit on one line or span multiple lines. (Default: "preserve")\nNOTE: In addition to Prettier's "preserve" and "collapse", we also support "always".`,
239
+ enum: [
240
+ "preserve",
241
+ "collapse",
242
+ "always"
243
+ ],
244
+ type: "string"
245
+ },
246
+ printWidth: {
247
+ description: `The line length that the printer will wrap on. (Default: 100)`,
248
+ type: "integer"
249
+ },
250
+ quoteProps: {
251
+ description: `Change when properties in objects are quoted. (Default: "as-needed")`,
252
+ enum: [
253
+ "as-needed",
254
+ "consistent",
255
+ "preserve"
256
+ ],
257
+ type: "string"
258
+ },
259
+ semi: {
260
+ description: `Print semicolons at the ends of statements. (Default: true)`,
261
+ type: "boolean"
262
+ },
263
+ singleAttributePerLine: {
264
+ description: `Put each attribute on a new line in JSX. (Default: false)`,
265
+ type: "boolean"
266
+ },
267
+ singleQuote: {
268
+ description: `Use single quotes instead of double quotes. (Default: false)`,
269
+ type: "boolean"
270
+ },
271
+ tabWidth: {
272
+ description: `Number of spaces per indentation level. (Default: 2)`,
273
+ type: "integer"
274
+ },
275
+ trailingComma: {
276
+ description: `Print trailing commas wherever possible. (Default: "all")`,
277
+ enum: [
278
+ "all",
279
+ "es5",
280
+ "none"
281
+ ],
282
+ type: "string"
283
+ },
284
+ useTabs: {
285
+ description: `Use tabs for indentation or spaces. (Default: false)`,
286
+ type: "boolean"
287
+ }
288
+ }
289
+ }]
290
+ },
291
+ create(context) {
292
+ if (!formatViaOxfmt) formatViaOxfmt = createSyncFn(join(dirWorkers, "oxfmt.mjs"));
293
+ return { Program() {
294
+ const sourceText = context.sourceCode.text;
295
+ try {
296
+ const formatResult = formatViaOxfmt(context.filename, sourceText, { ...context.options?.[0] });
297
+ if (formatResult.errors?.length) for (const error of formatResult.errors) {
298
+ const label = error.labels?.[0];
299
+ if (label) {
300
+ const start = context.sourceCode.getLocFromIndex(label.start);
301
+ const end = context.sourceCode.getLocFromIndex(label.end);
302
+ context.report({
303
+ loc: {
304
+ end,
305
+ start
306
+ },
307
+ message: error.message
308
+ });
309
+ } else context.report({
310
+ message: error.message,
311
+ loc: {
312
+ end: {
313
+ column: 0,
314
+ line: 1
315
+ },
316
+ start: {
317
+ column: 0,
318
+ line: 1
319
+ }
320
+ }
321
+ });
322
+ }
323
+ else reportDifferences(context, sourceText, formatResult.code);
324
+ } catch {
325
+ context.report({
326
+ message: `Failed to format file ${context.filename}`,
327
+ loc: {
328
+ end: {
329
+ column: 0,
330
+ line: 1
331
+ },
332
+ start: {
333
+ column: 0,
334
+ line: 1
335
+ }
336
+ }
337
+ });
338
+ }
339
+ } };
340
+ }
341
+ };
342
+
343
+ //#endregion
344
+ //#region src/rules/index.ts
345
+ const rules = { oxfmt };
346
+
347
+ //#endregion
348
+ //#region src/index.ts
349
+ const plugin = {
350
+ configs,
351
+ meta,
352
+ rules
353
+ };
354
+ var src_default = plugin;
355
+
356
+ //#endregion
357
+ export { configs, src_default as default, meta, parserPlain, plugin, recommended, rules };
@@ -0,0 +1,69 @@
1
+ /* eslint-disable */
2
+ /* prettier-ignore */
3
+ import type { Linter } from 'eslint'
4
+
5
+ export type RuleOptions = {
6
+ /**
7
+ * Format code via oxfmt
8
+ * @see https://github.com/ntnyq/eslint-plugin-oxfmt
9
+ */
10
+ 'oxfmt/oxfmt'?: Linter.RuleEntry<OxfmtOxfmt>
11
+ }
12
+
13
+ /* ======= Declarations ======= */
14
+ // ----- oxfmt/oxfmt -----
15
+ export type OxfmtOxfmt = []|[{
16
+
17
+ arrowParens?: ("always" | "avoid")
18
+
19
+ bracketSameLine?: boolean
20
+
21
+ bracketSpacing?: boolean
22
+
23
+ embeddedLanguageFormatting?: ("auto" | "off")
24
+
25
+ endOfLine?: ("lf" | "crlf" | "cr")
26
+
27
+ experimentalSortImports?: {
28
+
29
+ groups?: string[][]
30
+
31
+ ignoreCase?: boolean
32
+
33
+ internalPattern?: string[]
34
+
35
+ newlinesBetween?: boolean
36
+
37
+ order?: ("asc" | "desc")
38
+
39
+ partitionByComment?: boolean
40
+
41
+ partitionByNewline?: boolean
42
+
43
+ sortSideEffects?: boolean
44
+ }
45
+
46
+ experimentalSortPackageJson?: boolean
47
+
48
+ ignorePatterns?: string[]
49
+
50
+ jsxSingleQuote?: boolean
51
+
52
+ objectWrap?: ("preserve" | "collapse" | "always")
53
+
54
+ printWidth?: number
55
+
56
+ quoteProps?: ("as-needed" | "consistent" | "preserve")
57
+
58
+ semi?: boolean
59
+
60
+ singleAttributePerLine?: boolean
61
+
62
+ singleQuote?: boolean
63
+
64
+ tabWidth?: number
65
+
66
+ trailingComma?: ("all" | "es5" | "none")
67
+
68
+ useTabs?: boolean
69
+ }]
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "eslint-plugin-oxfmt",
3
3
  "type": "module",
4
- "version": "0.0.0",
5
- "description": "__TODO__",
6
- "keywords": [],
4
+ "version": "0.0.1",
5
+ "description": "An ESLint plugin that format code via oxfmt.",
6
+ "keywords": [
7
+ "eslint",
8
+ "eslint-plugin",
9
+ "oxc",
10
+ "oxfmt",
11
+ "oxlint",
12
+ "prettier"
13
+ ],
7
14
  "license": "MIT",
8
15
  "author": {
9
16
  "name": "ntnyq",
@@ -17,45 +24,68 @@
17
24
  "exports": {
18
25
  "./package.json": "./package.json",
19
26
  ".": {
20
- "types": "./dist/index.d.ts",
21
- "default": "./dist/index.js"
27
+ "types": "./dist/index.d.mts",
28
+ "default": "./dist/index.mjs"
29
+ },
30
+ "./rule-options": {
31
+ "types": "./dts/rule-options.d.ts"
22
32
  }
23
33
  },
24
- "main": "./dist/index.js",
25
- "types": "./dist/index.d.ts",
34
+ "main": "./dist/index.mjs",
35
+ "types": "./dist/index.d.mts",
26
36
  "files": [
27
- "dist"
37
+ "dist",
38
+ "dts",
39
+ "workers"
28
40
  ],
29
41
  "publishConfig": {
30
42
  "access": "public"
31
43
  },
32
44
  "sideEffects": false,
45
+ "peerDependencies": {
46
+ "eslint": "^9.5.0"
47
+ },
48
+ "dependencies": {
49
+ "generate-differences": "^0.1.0",
50
+ "oxfmt": "^0.19.0",
51
+ "synckit": "^0.11.11"
52
+ },
33
53
  "devDependencies": {
34
- "@ntnyq/eslint-config": "^5.6.0",
54
+ "@ntnyq/eslint-config": "^5.8.0",
35
55
  "@ntnyq/prettier-config": "^3.0.1",
36
- "@types/node": "^24.9.1",
37
- "bumpp": "^10.3.1",
38
- "eslint": "^9.38.0",
56
+ "@types/json-schema": "^7.0.15",
57
+ "@types/node": "^25.0.3",
58
+ "@typescript/native-preview": "7.0.0-dev.20251204.1",
59
+ "bumpp": "^10.3.2",
60
+ "eslint": "^9.39.2",
61
+ "eslint-parser-plain": "^0.1.1",
62
+ "eslint-typegen": "^2.3.0",
63
+ "eslint-vitest-rule-tester": "^3.0.1",
39
64
  "husky": "^9.1.7",
40
- "nano-staged": "^0.8.0",
65
+ "json-schema": "^0.4.0",
66
+ "nano-staged": "^0.9.0",
41
67
  "npm-run-all2": "^8.0.4",
42
- "prettier": "^3.6.2",
43
- "tsdown": "^0.15.9",
68
+ "prettier": "^3.7.4",
69
+ "show-invisibles": "^0.0.1",
70
+ "tinyglobby": "^0.2.15",
71
+ "tsdown": "^0.18.1",
72
+ "tsx": "^4.21.0",
44
73
  "typescript": "^5.9.3",
45
- "vitest": "^3.2.4"
74
+ "vitest": "^4.0.16"
46
75
  },
47
76
  "nano-staged": {
48
77
  "*.{js,ts,mjs,cjs,md,vue,yml,yaml,toml,json}": "eslint --fix",
49
78
  "*.{css,scss,html}": "prettier -uw"
50
79
  },
51
80
  "scripts": {
52
- "build": "tsdown",
81
+ "build": "pnpm run update:rule-options && tsdown",
53
82
  "dev": "tsdown --watch",
54
83
  "lint": "eslint",
55
84
  "release": "run-s release:check release:version",
56
- "release:check": "run-s lint typecheck test",
85
+ "release:check": "run-s lint typecheck test build",
57
86
  "release:version": "bumpp",
58
87
  "test": "vitest",
59
- "typecheck": "tsc --noEmit"
88
+ "typecheck": "tsgo --noEmit",
89
+ "update:rule-options": "tsx scripts/udpateRuleOptions.ts"
60
90
  }
61
91
  }
@@ -0,0 +1,28 @@
1
+ // @ts-check
2
+
3
+ import { format } from 'oxfmt'
4
+ import { runAsWorker } from 'synckit'
5
+
6
+ /**
7
+ * @import { FormatOptions } from 'oxfmt'
8
+ */
9
+
10
+ runAsWorker(
11
+ async (
12
+ /**
13
+ * @type {string} filename
14
+ */
15
+ filename,
16
+ /**
17
+ * @type {string} source text
18
+ */
19
+ sourceText,
20
+ /**'
21
+ * @type {FormatOptions} format options
22
+ */
23
+ options,
24
+ ) => {
25
+ const formatResult = await format(filename, sourceText, options)
26
+ return formatResult
27
+ },
28
+ )
package/dist/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- //#region src/index.d.ts
2
- declare const msg = "hello world";
3
- declare function greet(): string;
4
- //#endregion
5
- export { greet, msg };
package/dist/index.js DELETED
@@ -1,9 +0,0 @@
1
- //#region src/index.ts
2
- const msg = "hello world";
3
- function greet() {
4
- console.log(msg);
5
- return msg;
6
- }
7
-
8
- //#endregion
9
- export { greet, msg };