开发 Rollup 插件:实现 JSON 文件导入功能

229 阅读4分钟

本文将指导 Rollup 插件开发新手,逐步开发一个用于导入 JSON 文件的插件。从简单的 JSON 导入功能出发,逐步扩展到支持 JSON5 格式,并通过优化性能加入缓存机制。最后,将介绍插件中使用到的关键钩子及其作用,并展示如何在项目中配置和使用该插件。

一、实现基本的 JSON 导入功能

首先,我们开发一个简单的 Rollup 插件,使其能够处理 .json 文件的导入,将 JSON 内容转换为 ES 模块的默认导出。

1.1 编写插件代码

创建 plugins/rollup-plugin-json.mjs 文件,编写如下代码:

export default function jsonPlugin(options = {}) {
  return {
    name: 'json',

    // 处理模块解析
    resolveId(source, importer) {
      if (source.endsWith('.json')) {
        // 返回模块的绝对路径,指示 Rollup 由插件处理该模块
        return source;
      }
      return null;
    },

    // 转换模块内容
    transform(code, id) {
      if (!id.endsWith('.json')) return null;

      let parsed;
      try {
        parsed = JSON.parse(code);
      } catch (err) {
        this.error(`解析 JSON 文件 ${id} 时出错: ${err.message}`);
      }

      const jsonString = JSON.stringify(parsed);
      const transformedCode = `export default ${jsonString};`;

      return {
        code: transformedCode,
        map: { mappings: '' } // 无源映射
      };
    }
  };
}

1.2 配置 Rollup 使用插件

在示例项目中,配置 Rollup 使用该插件。

示例数据 data.json

src/data.json 中添加示例数据:

{
  "name": "rollup"
}

创建 main.js

src/main.js 中导入 JSON 文件:

import data from './data.json';

console.log(`Hello ${data.name}`);

创建 rollup.config.js

在项目根目录下创建 rollup.config.js,配置 Rollup 使用自定义插件:

import jsonPlugin from "./plugins/rollup-plugin-json.mjs";

export default {
  input: "./src/main.js",
  output: {
    file: 'bundle.js',
    format: 'esm',
  },
  plugins: [
    jsonPlugin()
  ],
};

1.3 运行 Rollup 构建

执行 Rollup 构建命令:

npx rollup -c

构建后生成的 bundle.js 内容如下:

var data = {"name":"rollup"};

console.log(`Hello ${data.name}`);

二、扩展支持 JSON5 格式

JSON5 是 JSON 的一个超集,允许更灵活的语法,如单引号、注释等。我们通过集成 json5 库,使插件支持 JSON5 格式。

2.1 安装 JSON5 库

在项目根目录下安装 json5

pnpm add json5

2.2 修改插件代码

修改 plugins/rollup-plugin-json.mjs,添加对 JSON5 的支持:

import JSON5 from 'json5';

export default function jsonPlugin(options = {}) {
  const { allowJSON5 = false } = options;

  return {
    name: 'json',

    resolveId(source, importer) {
      if (source.endsWith('.json')) {
        return source;
      }
      return null;
    },

    transform(code, id) {
      if (!id.endsWith('.json')) return null;

      let parsed;
      try {
        parsed = allowJSON5 ? JSON5.parse(code) : JSON.parse(code);
      } catch (err) {
        this.error(`解析 JSON 文件 ${id} 时出错: ${err.message}`);
      }

      const jsonString = JSON.stringify(parsed);
      const transformedCode = `export default ${jsonString};`;

      return {
        code: transformedCode,
        map: { mappings: '' }
      };
    }
  };
}

2.3 配置插件使用 JSON5

rollup.config.js 中启用 JSON5 支持:

import jsonPlugin from "./plugins/rollup-plugin-json.mjs";

export default {
  input: "./src/main.js",
  output: {
    file: 'bundle.js',
    format: 'esm',
  },
  plugins: [
    jsonPlugin({
      allowJSON5: true
    })
  ],
};

三、优化插件:添加缓存机制

在大型项目中,频繁读取和解析 JSON 文件可能影响构建性能。通过添加缓存机制,可以避免重复处理相同的文件,提升构建效率。

3.1 修改插件代码

修改 plugins/rollup-plugin-json.mjs,引入缓存逻辑:

import JSON5 from 'json5';
import jsonminify from 'jsonminify'; // 若需要压缩

export default function jsonPlugin(options = {}) {
  const {
    allowJSON5 = false,
    minify = false,
  } = options;

  const cache = new Map();

  return {
    name: 'json',

    resolveId(source, importer) {
      if (source.endsWith('.json')) {
        return source;
      }
      return null;
    },

    transform(code, id) {
      if (!id.endsWith('.json')) return null;

      // 缓存检查
      if (cache.has(id)) {
        return cache.get(id);
      }

      let parsed;
      try {
        parsed = allowJSON5 ? JSON5.parse(code) : JSON.parse(code);
      } catch (err) {
        this.error(`解析 JSON 文件 ${id} 时出错: ${err.message}`);
      }

      let jsonString = JSON.stringify(parsed);
      if (minify) {
        jsonString = jsonminify(jsonString);
      }

      const transformedCode = `export default ${jsonString};`;

      const result = {
        code: transformedCode,
        map: { mappings: '' }
      };

      // 存入缓存
      cache.set(id, result);

      return result;
    }
  };
}

3.2 配置压缩选项

rollup.config.js 中启用压缩:

import jsonPlugin from "./plugins/rollup-plugin-json.mjs";

export default {
  input: "./src/main.js",
  output: {
    file: 'bundle.js',
    format: 'esm',
  },
  plugins: [
    jsonPlugin({
      allowJSON5: true,
      minify: true
    })
  ],
};

重新构建项目,插件将压缩 JSON 数据,减小输出文件大小。

四、插件中使用到的关键钩子解析

在开发过程中,我们主要使用了以下 Rollup 钩子:

4.1 resolveId 钩子

作用:决定模块的解析方式,返回模块的路径或 ID。

使用场景:当插件需要处理特定类型的模块(如 .json 文件)时,使用 resolveId 钩子拦截模块解析过程,指示 Rollup 由插件处理该模块。

示例解析

resolveId(source, importer) {
  if (source.endsWith('.json')) {
    return source; // 告诉 Rollup 由插件处理该模块
  }
  return null; // 让 Rollup 继续其他解析逻辑
}

4.2 transform 钩子

作用:转换模块内容,返回转换后的代码和源映射。

使用场景:对模块内容进行处理,如编译、转换、优化等。在我们的插件中,用于将 JSON 内容转换为 ES 模块的默认导出。

五、总结与使用

通过本文的介绍,你已经学会了如何基于 Rollup 开发一个用于导入 JSON 文件的插件。从最基础的 JSON 导入功能出发,逐步扩展到支持 JSON5 格式,并通过添加缓存机制优化插件性能。关键钩子的解析帮助你理解插件在 Rollup 构建过程中的作用。

使用步骤回顾

  1. 编写插件代码:在代码中实现 resolveIdtransform 钩子,处理 JSON 文件的导入与转换。
  2. 扩展功能:集成 json5 支持,允许解析更灵活的 JSON5 格式。
  3. 优化性能:添加缓存机制,避免重复处理相同的 JSON 文件。
  4. 配置 Rollup:在 rollup.config.js 中引入并配置自定义插件,根据需求启用 JSON5 支持和压缩选项。
  5. 验证效果:通过构建示例项目,确保插件正确导入并转换 JSON 文件内容。

掌握了这些基础后,你可以根据项目需求,进一步扩展插件功能,如支持更多文件类型、增加更多配置选项等。Rollup 的插件系统极具灵活性,鼓励开发者根据实际需求进行定制化开发。

希望本文对你开发 Rollup 插件有所帮助,祝你在前端开发的道路上不断进步!