js-beautify 中的 TypeScript 复合项目格式化:references 处理

js-beautify 中的 TypeScript 复合项目格式化:references 处理

【免费下载链接】js-beautify Beautifier for javascript 【免费下载链接】js-beautify 项目地址: https://gitcode.com/gh_mirrors/js/js-beautify

痛点与挑战

在 TypeScript 复合项目开发中,开发者经常面临跨文件引用(/// <reference>)导致的格式化混乱问题。当项目中存在大量相互引用的 .ts 文件时,使用 js-beautify 进行代码格式化往往会出现缩进错误、引用路径错乱等问题,严重影响代码可读性和团队协作效率。本文将系统介绍如何在 TypeScript 复合项目中配置和优化 js-beautify,实现对 references 引用的完美处理。

核心原理

TypeScript 引用机制

TypeScript 提供了两种主要的引用方式:

  • 三斜杠指令/// <reference path="..." />):用于声明文件间的依赖关系
  • 模块引用import/export):ES6 标准模块系统

在复合项目中,这两种引用方式经常混合使用,给代码格式化工具带来挑战。

js-beautify 工作流程

mermaid

js-beautify 通过以下步骤处理 TypeScript 文件:

  1. 将输入代码解析为抽象语法树(AST)
  2. 生成语法 Token 流
  3. 分析文件间引用关系
  4. 根据配置规则重新排版代码
  5. 输出格式化后的代码

配置方案

基础配置

在项目根目录创建 .jsbeautifyrc 文件,添加以下基础配置:

{
  "indent_size": 2,
  "indent_char": " ",
  "indent_level": 0,
  "indent_with_tabs": false,
  "preserve_newlines": true,
  "max_preserve_newlines": 10,
  "jslint_happy": false,
  "space_after_anon_function": true,
  "brace_style": "collapse",
  "keep_array_indentation": false,
  "keep_function_indentation": false,
  "space_before_conditional": true,
  "break_chained_methods": false,
  "eval_code": false,
  "unescape_strings": false,
  "wrap_line_length": 0,
  "wrap_attributes": "auto",
  "end_with_newline": true
}

TypeScript 引用处理配置

针对 references 处理,需要添加以下专项配置:

{
  "typescript": {
    "handle_references": true,
    "reference_path_align": true,
    "preserve_reference_order": true,
    "reference_comment_space": 2
  }
}
配置项类型默认值说明
handle_referencesbooleanfalse是否特殊处理三斜杠引用
reference_path_alignbooleanfalse是否对齐引用路径
preserve_reference_orderbooleantrue是否保留引用顺序
reference_comment_spacenumber1引用注释后的空格数

实现步骤

1. 安装与集成

# 安装 js-beautify
npm install js-beautify --save-dev

# 安装 TypeScript 支持插件
npm install @types/js-beautify --save-dev

package.json 中添加格式化脚本:

{
  "scripts": {
    "format": "js-beautify --config .jsbeautifyrc -r src/**/*.ts"
  }
}

2. 处理单文件引用

创建 format-ts.js 脚本,实现对单个 TypeScript 文件的引用处理:

const beautify = require('js-beautify').js;
const fs = require('fs');
const path = require('path');

function formatTsFile(filePath, config) {
  const content = fs.readFileSync(filePath, 'utf8');
  
  // 提取并处理三斜杠引用
  const references = [];
  const lines = content.split('\n');
  
  let inReferences = true;
  const codeLines = [];
  
  for (const line of lines) {
    if (inReferences && line.trim().startsWith('/// <reference')) {
      references.push(line.trim());
    } else {
      inReferences = false;
      codeLines.push(line);
    }
  }
  
  // 格式化引用
  const formattedReferences = references
    .sort((a, b) => a.localeCompare(b))
    .map(ref => {
      // 对齐路径
      if (config.typescript.reference_path_align) {
        const pathMatch = ref.match(/path="([^"]+)"/);
        if (pathMatch) {
          const filePath = pathMatch[1];
          const maxPathLength = Math.max(...references.map(r => r.match(/path="([^"]+)"/)?.[1]?.length || 0));
          return ref.replace(/path="[^"]+"/, `path="${filePath.padEnd(maxPathLength)}"`);
        }
      }
      return ref;
    });
  
  // 格式化代码内容
  const codeContent = codeLines.join('\n');
  const formattedCode = beautify(codeContent, config);
  
  // 重组文件内容
  return [
    ...formattedReferences,
    '', // 引用与代码间添加空行
    formattedCode
  ].join('\n');
}

// 使用示例
const config = JSON.parse(fs.readFileSync('.jsbeautifyrc', 'utf8'));
formatTsFile('src/main.ts', config);

3. 处理复合项目引用

对于包含多个相互引用的 TypeScript 文件的复合项目,创建 format-project.js

const fs = require('fs');
const path = require('path');
const { formatTsFile } = require('./format-ts');

function findTsFiles(dir, fileList = []) {
  const files = fs.readdirSync(dir);
  
  for (const file of files) {
    const filePath = path.join(dir, file);
    if (fs.statSync(filePath).isDirectory()) {
      findTsFiles(filePath, fileList);
    } else if (path.extname(file) === '.ts') {
      fileList.push(filePath);
    }
  }
  
  return fileList;
}

function formatTsProject(rootDir, config) {
  const tsFiles = findTsFiles(rootDir);
  
  // 构建引用图谱
  const referenceGraph = {};
  
  tsFiles.forEach(file => {
    const content = fs.readFileSync(file, 'utf8');
    const references = [];
    
    content.split('\n').forEach(line => {
      const refMatch = line.trim().match(//// <reference path="([^"]+)" \/>/);
      if (refMatch) {
        const refPath = path.resolve(path.dirname(file), refMatch[1]);
        references.push(refPath);
      }
    });
    
    referenceGraph[file] = references;
  });
  
  // 拓扑排序,确保引用在前的文件先格式化
  const sortedFiles = topologicalSort(referenceGraph);
  
  // 格式化所有文件
  sortedFiles.forEach(file => {
    const formattedContent = formatTsFile(file, config);
    fs.writeFileSync(file, formattedContent, 'utf8');
    console.log(`Formatted: ${file}`);
  });
}

function topologicalSort(graph) {
  const visited = new Set();
  const result = [];
  
  function dfs(node) {
    if (visited.has(node)) return;
    visited.add(node);
    
    (graph[node] || []).forEach(dep => {
      dfs(dep);
    });
    
    result.unshift(node);
  }
  
  Object.keys(graph).forEach(node => {
    dfs(node);
  });
  
  return result;
}

// 使用示例
const config = JSON.parse(fs.readFileSync('.jsbeautifyrc', 'utf8'));
formatTsProject('src', config);

高级优化

1. 引用路径规范化

实现引用路径的自动规范化,统一使用相对路径:

function normalizeReferencePaths(filePath, references, config) {
  const dirName = path.dirname(filePath);
  
  return references.map(ref => {
    const refMatch = ref.match(//// <reference path="([^"]+)" \/>/);
    if (!refMatch) return ref;
    
    const absolutePath = path.resolve(dirName, refMatch[1]);
    const relativePath = path.relative(dirName, absolutePath);
    
    // 统一使用相对路径,避免混合使用绝对路径
    return `/// <reference path="${relativePath}" />`;
  });
}

2. 循环引用检测

添加循环引用检测功能,避免格式化陷入死循环:

function detectCircularReferences(graph) {
  const visited = new Set();
  const recursionStack = new Set();
  const cycles = [];
  
  function dfs(node) {
    if (recursionStack.has(node)) {
      // 检测到循环引用
      const cycle = [...recursionStack];
      cycle.push(node);
      cycles.push(cycle);
      return true;
    }
    
    if (visited.has(node)) return false;
    
    visited.add(node);
    recursionStack.add(node);
    
    for (const neighbor of graph[node] || []) {
      if (dfs(neighbor)) return true;
    }
    
    recursionStack.delete(node);
    return false;
  }
  
  Object.keys(graph).forEach(node => {
    if (!visited.has(node)) {
      dfs(node);
    }
  });
  
  return cycles;
}

常见问题解决方案

问题1:引用路径对齐混乱

症状:格式化后三斜杠引用路径不对齐,影响可读性。

解决方案

function alignReferencePaths(references) {
  // 找到最长的路径长度
  const maxPathLength = Math.max(...references.map(ref => {
    const match = ref.match(/path="([^"]+)"/);
    return match ? match[1].length : 0;
  }));
  
  // 对齐所有路径
  return references.map(ref => {
    return ref.replace(/path="([^"]+)"/, (match, path) => {
      // 补全空格使路径长度一致
      const paddedPath = path.padEnd(maxPathLength);
      return `path="${paddedPath}"`;
    });
  });
}

问题2:引用顺序混乱

症状:格式化后引用顺序改变,破坏项目逻辑结构。

解决方案

function sortReferences(references, sortType = 'alphabetical') {
  switch (sortType) {
    case 'alphabetical':
      // 按字母顺序排序
      return [...references].sort((a, b) => {
        const pathA = a.match(/path="([^"]+)"/)[1];
        const pathB = b.match(/path="([^"]+)"/)[1];
        return pathA.localeCompare(pathB);
      });
      
    case 'relative':
      // 按相对路径深度排序
      return [...references].sort((a, b) => {
        const pathA = a.match(/path="([^"]+)"/)[1];
        const pathB = b.match(/path="([^"]+)"/)[1];
        const depthA = pathA.split('/').length;
        const depthB = pathB.split('/').length;
        return depthA - depthB || pathA.localeCompare(pathB);
      });
      
    default:
      return references;
  }
}

自动化集成

Git Hooks 集成

使用 husky 在提交前自动格式化代码:

# 安装 husky
npm install husky --save-dev

# 启用 Git hooks
npx husky install

# 添加 pre-commit hook
npx husky add .husky/pre-commit "npm run format"

VS Code 集成

.vscode/settings.json 中添加:

{
  "editor.defaultFormatter": "HookyQR.beautify",
  "editor.formatOnSave": true,
  "beautify.config": ".jsbeautifyrc",
  "beautify.language": {
    "ts": {
      "type": ["typescript", "tsx"],
      "filename": [".ts", ".tsx"]
    }
  }
}

性能优化

对于大型项目,可通过以下方式提升格式化性能:

// 缓存已格式化文件
const formatCache = new Map();

function formatTsFileCached(filePath, config) {
  const stats = fs.statSync(filePath);
  const cacheKey = `${filePath}:${stats.mtimeMs}`;
  
  if (formatCache.has(cacheKey)) {
    return formatCache.get(cacheKey);
  }
  
  const formattedContent = formatTsFile(filePath, config);
  formatCache.set(cacheKey, formattedContent);
  
  // 限制缓存大小
  if (formatCache.size > 100) {
    const oldestKey = formatCache.keys().next().value;
    formatCache.delete(oldestKey);
  }
  
  return formattedContent;
}

总结与展望

通过本文介绍的方法,开发者可以有效解决 TypeScript 复合项目中使用 js-beautify 格式化时遇到的 references 处理问题。关键要点包括:

  1. 配置专门的 TypeScript 引用处理规则
  2. 实现文件引用的拓扑排序
  3. 规范化和对齐引用路径
  4. 集成自动化工具提升开发效率

未来,随着 js-beautify 对 TypeScript 支持的不断增强,我们期待能有更原生的解决方案来处理复合项目引用问题。建议开发者关注官方仓库的更新,并积极参与社区贡献,共同完善这一工具生态。

扩展资源

  • 官方文档:js-beautify GitHub 仓库(https://gitcode.com/gh_mirrors/js/js-beautify)
  • TypeScript 引用手册:TypeScript 官方文档中的三斜杠指令部分
  • 相关工具
    • tsconfig.json 配置优化工具
    • TypeScript 项目结构分析器
    • 代码质量检测集成工具(ESLint + Prettier + js-beautify)

【免费下载链接】js-beautify Beautifier for javascript 【免费下载链接】js-beautify 项目地址: https://gitcode.com/gh_mirrors/js/js-beautify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值