@takazudo/mdx-formatter 0.2.0 → 0.3.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.
@@ -53,6 +53,12 @@ export declare class HybridFormatter {
53
53
  extractHtmlFromNode(node: MdxJsxElement): string | null;
54
54
  collectJsxIndentOperations(operations: FormatterOperation[]): void;
55
55
  collectBlockJsxEmptyLineOperations(operations: FormatterOperation[]): void;
56
+ /**
57
+ * Pre-process YAML text to fix values that would cause parsing failures
58
+ * or silent data corruption. Detects unquoted values containing special
59
+ * YAML characters and wraps them in double quotes.
60
+ */
61
+ preprocessYamlForParsing(yamlText: string): string;
56
62
  collectYamlFormatOperations(operations: FormatterOperation[]): void;
57
63
  getLineAtPosition(charPos: number): number;
58
64
  applyOperation(lines: string[], op: FormatterOperation): void;
@@ -893,16 +893,69 @@ export class HybridFormatter {
893
893
  }
894
894
  });
895
895
  }
896
+ /**
897
+ * Pre-process YAML text to fix values that would cause parsing failures
898
+ * or silent data corruption. Detects unquoted values containing special
899
+ * YAML characters and wraps them in double quotes.
900
+ */
901
+ preprocessYamlForParsing(yamlText) {
902
+ const lines = yamlText.split('\n');
903
+ const result = [];
904
+ for (const line of lines) {
905
+ // Match a YAML mapping entry: optional indent, key, colon, space, value
906
+ // Keys must start with a word char, may contain word chars, dots, hyphens
907
+ const match = line.match(/^(\s*)([\w][\w.-]*):\s+(.+)$/);
908
+ if (match) {
909
+ const [, indent, key, value] = match;
910
+ const trimmedValue = value.trim();
911
+ // Skip if already quoted
912
+ if ((trimmedValue.startsWith('"') && trimmedValue.endsWith('"')) ||
913
+ (trimmedValue.startsWith("'") && trimmedValue.endsWith("'"))) {
914
+ result.push(line);
915
+ continue;
916
+ }
917
+ // Skip if the value is a flow sequence [...] or flow mapping {...}
918
+ if ((trimmedValue.startsWith('[') && trimmedValue.endsWith(']')) ||
919
+ (trimmedValue.startsWith('{') && trimmedValue.endsWith('}'))) {
920
+ result.push(line);
921
+ continue;
922
+ }
923
+ // Skip block scalar indicators (>, |, >-, |-, >+, |+)
924
+ if (/^[|>][-+]?$/.test(trimmedValue)) {
925
+ result.push(line);
926
+ continue;
927
+ }
928
+ const needsQuoting = trimmedValue.includes(': ') ||
929
+ trimmedValue.includes(' #') ||
930
+ /^[!&*%@`]/.test(trimmedValue);
931
+ if (needsQuoting) {
932
+ const escaped = trimmedValue.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
933
+ result.push(`${indent}${key}: "${escaped}"`);
934
+ continue;
935
+ }
936
+ }
937
+ result.push(line);
938
+ }
939
+ return result.join('\n');
940
+ }
896
941
  collectYamlFormatOperations(operations) {
897
942
  const yamlSettings = this.settings.formatYamlFrontmatter;
898
943
  visit(this.ast, (node) => {
899
944
  if (node.type === 'yaml' && node.position) {
900
945
  const yamlNode = node;
901
946
  try {
902
- // Parse the YAML content
903
- const parsed = yaml.load(yamlNode.value);
904
- // Format it back with proper formatting
947
+ let yamlToParse = yamlNode.value;
948
+ // Pre-process YAML to fix unsafe values (e.g., unquoted colons)
949
+ if (yamlSettings.fixUnsafeValues !== false) {
950
+ yamlToParse = this.preprocessYamlForParsing(yamlToParse);
951
+ }
952
+ // Parse the YAML content using JSON_SCHEMA to prevent silent
953
+ // data corruption (e.g., dates parsed as Date objects, octals)
954
+ const parsed = yaml.load(yamlToParse, { schema: yaml.JSON_SCHEMA });
955
+ // Format it back with proper formatting using JSON_SCHEMA
956
+ // to preserve string representations (dates, etc.)
905
957
  const formatted = yaml.dump(parsed, {
958
+ schema: yaml.JSON_SCHEMA,
906
959
  indent: yamlSettings.indent || 2,
907
960
  lineWidth: yamlSettings.lineWidth || 100,
908
961
  quotingType: (yamlSettings.quotingType || '"'),
package/dist/settings.js CHANGED
@@ -60,6 +60,7 @@ export const formatterSettings = {
60
60
  quotingType: '"', // Quote type for strings that need quoting: '"' or "'"
61
61
  forceQuotes: false, // Force quotes on all string values
62
62
  noCompatMode: true, // Use YAML 1.2 spec (not 1.1)
63
+ fixUnsafeValues: true, // Pre-process YAML to quote values containing special characters like colons
63
64
  },
64
65
  // Rule 8: Preserve Docusaurus admonitions
65
66
  preserveAdmonitions: {
package/dist/types.d.ts CHANGED
@@ -111,6 +111,7 @@ export interface FormatYamlFrontmatterSetting {
111
111
  quotingType: string;
112
112
  forceQuotes: boolean;
113
113
  noCompatMode: boolean;
114
+ fixUnsafeValues: boolean;
114
115
  }
115
116
  export interface PreserveAdmonitionsSetting {
116
117
  enabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takazudo/mdx-formatter",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AST-based markdown and MDX formatter with Japanese text support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "lint:fix": "eslint --fix .",
34
34
  "check": "prettier --check . && eslint .",
35
35
  "check:fix": "prettier --write . && eslint --fix .",
36
+ "b4push": "./scripts/run-b4push.sh",
36
37
  "doc:start": "pnpm --dir doc start",
37
38
  "prepare": "husky",
38
39
  "prepublishOnly": "tsc && vitest run"