@takazudo/mdx-formatter 0.4.2 → 0.5.0-next.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.
@@ -941,14 +941,24 @@ export class HybridFormatter {
941
941
  }
942
942
  return;
943
943
  }
944
+ // Find the actual end of the opening tag (may span multiple lines
945
+ // for elements with attributes like <Danger\n title="..."\n>)
946
+ let openingTagEndLine = startLine;
947
+ for (let i = startLine; i <= endLine; i++) {
948
+ const trimmed = this.lines[i].trim();
949
+ if (trimmed.endsWith('>') && !trimmed.endsWith('/>') && !trimmed.startsWith('</')) {
950
+ openingTagEndLine = i;
951
+ break;
952
+ }
953
+ }
944
954
  // Check if there's an empty line after the opening tag
945
- if (startLine + 1 < this.lines.length) {
946
- const lineAfterOpening = this.lines[startLine + 1];
955
+ if (openingTagEndLine + 1 < this.lines.length) {
956
+ const lineAfterOpening = this.lines[openingTagEndLine + 1];
947
957
  if (lineAfterOpening.trim() !== '') {
948
958
  // Add empty line after opening tag
949
959
  operations.push({
950
960
  type: 'insertLine',
951
- startLine: startLine + 1,
961
+ startLine: openingTagEndLine + 1,
952
962
  content: '',
953
963
  });
954
964
  }
package/dist/index.js CHANGED
@@ -19,8 +19,19 @@ export function detectMdx(content) {
19
19
  export async function format(content, options = {}) {
20
20
  try {
21
21
  const settings = loadConfig(options);
22
- const formatter = new HybridFormatter(content, settings);
23
- return formatter.format();
22
+ let result = content;
23
+ // Some rule interactions (e.g., list indent normalization + addEmptyLinesInBlockJsx)
24
+ // may require multiple passes to converge. 3 iterations is sufficient for all known
25
+ // cases (most files converge in 1, edge cases in 2).
26
+ const MAX_ITERATIONS = 3;
27
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
28
+ const formatter = new HybridFormatter(result, settings);
29
+ const formatted = await formatter.format();
30
+ if (formatted === result)
31
+ break;
32
+ result = formatted;
33
+ }
34
+ return result;
24
35
  }
25
36
  catch {
26
37
  // Silently return original content if formatting fails
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Wrapper for the Rust napi formatter.
3
+ * This module loads the native Rust formatter compiled via napi-rs
4
+ * and exposes the same format() API as the TypeScript implementation.
5
+ */
6
+ import type { FormatOptions } from './types.js';
7
+ /**
8
+ * Check if the Rust formatter is available
9
+ */
10
+ export declare function isRustFormatterAvailable(): boolean;
11
+ /**
12
+ * Format markdown/MDX content using the Rust formatter
13
+ * API-compatible with the TypeScript format() function
14
+ */
15
+ export declare function format(content: string, options?: FormatOptions): Promise<string>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Wrapper for the Rust napi formatter.
3
+ * This module loads the native Rust formatter compiled via napi-rs
4
+ * and exposes the same format() API as the TypeScript implementation.
5
+ */
6
+ import { createRequire } from 'module';
7
+ import { loadConfig } from './load-config.js';
8
+ const require = createRequire(import.meta.url);
9
+ // Try to load the native module
10
+ let nativeFormat = null;
11
+ try {
12
+ const native = require('../crates/mdx-formatter-napi/mdx-formatter-napi.node');
13
+ nativeFormat = native.format;
14
+ }
15
+ catch {
16
+ // Native module not available - not built yet or wrong platform
17
+ }
18
+ /**
19
+ * Check if the Rust formatter is available
20
+ */
21
+ export function isRustFormatterAvailable() {
22
+ return nativeFormat !== null;
23
+ }
24
+ /**
25
+ * Format markdown/MDX content using the Rust formatter
26
+ * API-compatible with the TypeScript format() function
27
+ */
28
+ export async function format(content, options = {}) {
29
+ if (!nativeFormat) {
30
+ throw new Error('Rust formatter not available. Build it first with: cd crates/mdx-formatter-napi && cargo build');
31
+ }
32
+ const settings = loadConfig(options);
33
+ const settingsJson = JSON.stringify(settings);
34
+ return nativeFormat(content, settingsJson);
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takazudo/mdx-formatter",
3
- "version": "0.4.2",
3
+ "version": "0.5.0-next.1",
4
4
  "description": "AST-based markdown and MDX formatter with Japanese text support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -33,29 +33,13 @@
33
33
  "lint:fix": "eslint --fix .",
34
34
  "check": "prettier --check . && eslint .",
35
35
  "check:fix": "prettier --write . && eslint --fix .",
36
+ "test:rust": "vitest run --config vitest.rust.config.ts",
37
+ "build:rust": "cd crates/mdx-formatter-napi && cargo build",
38
+ "build:rust:release": "cd crates/mdx-formatter-napi && cargo build --release",
36
39
  "b4push": "./scripts/run-b4push.sh",
37
40
  "doc:start": "pnpm --dir doc start",
38
- "prepare": "husky",
39
41
  "prepublishOnly": "tsc && vitest run"
40
42
  },
41
- "lint-staged": {
42
- "*.{js,mjs,ts}": [
43
- "prettier --write",
44
- "eslint --fix"
45
- ],
46
- "*.{json,yml,yaml}": [
47
- "prettier --write"
48
- ],
49
- ".claude/**/*.md": [
50
- "prettier --write"
51
- ],
52
- "doc/src/**/*.{ts,tsx,js,jsx,css}": [
53
- "prettier --write"
54
- ],
55
- "doc/docs/**/*.{md,mdx}": [
56
- "npx tsx src/cli.ts --write"
57
- ]
58
- },
59
43
  "keywords": [
60
44
  "markdown",
61
45
  "mdx",
@@ -102,8 +86,7 @@
102
86
  "@types/unist": "^3.0.3",
103
87
  "@vitest/coverage-v8": "^4.0.17",
104
88
  "eslint": "^9.39.2",
105
- "husky": "^9.1.7",
106
- "lint-staged": "^16.2.7",
89
+ "lefthook": "^2.1.4",
107
90
  "typescript": "^5.9.3",
108
91
  "typescript-eslint": "^8.54.0",
109
92
  "vfile": "^6.0.3",