低代码平台-在线编译

354 阅读1分钟

需求背景

当我们在低代码平台中编写源码时,如果使用了ES6+的语法(async/await等),会导致低版本浏览器无法正常运行代码,这时我们需要借助编译工具将ES6+代码转成ES5代码,保障代码的兼容性。

主要需求

  1. 获取项目编译配置
  2. 根据配置生成对应代码

技术方案

编译配置

{
  "module": {
    "rules": [
      {
        "test": /.js$/,
        "exclude": /node_modules/,
        "use": {
          "loader": "babel-loader",
          "options": {
            "presets": ["@babel/preset-env"]
          }
        }
      }
    ]
  }
}

💡 注意,此配置为部分配置项,仅在预览、发布状态下编译。

主要流程

获取编译配置并显示

  1. 获取pageData下的webpackConfig字符串
  2. 前端通过CodeEditor组件展示对应配置

后端编译流程

  1. /server/routers/page.js下getPageRenderData方法增加webpack编译代码
  2. 获取pageData对象下webpackConfig字段,并转为对象
  3. 创建子进程进行转码工作
  4. 创建转码输入文件/server/build/[pageData._id]/script.main.input.js,渲染ejs模板/server/views/common/script-main-code.ejs,并将内容写入入口文件
  5. 调用webpack(options)生成编译实例complier
  6. 调用complier.run方法编译代码,编译后代码写入出口文件/server/build/[pageData._id]/script.main.output.js
  7. 读取编译后的出口文件写入模板script-main-code.ejs对应位置
  8. 删除临时编译文件
const fs = require('fs');
const webpack = require('webpack');

// 编译代码
scriptMainCode = await ctx.render('common/script-main-code', baseData);

// webpackConfig
const webpackConfigSource = pageData.webpackConfig || {};
const webpackConfigOptions = new Function(`return ${JSON.stringify(webpackConfigSource)}`)();

if (Object.keys(webpackConfigOptions).length && !sandbox && pageData.webpackEnable) {
  // scriptMainCode = babel.transform(scriptMainCode, webpackConfigOptions).code;
  
  const buildFolderPath = path.resolve(__dirname, `../build/${pageData._id}`);
  const buildInputFileName = `script.main.input.js`;
  const buildOutputFileNmae = `script.main.output.js`;
  const buildInputPath = path.resolve(buildFolderPath, `./${buildInputFileName}`);
  const buildOutputPath = path.resolve(buildFolderPath, `./${buildOutputFileNmae}`);
  
  // 创建构建目录
  await exitsFolder(buildFolderPath);
  
  await writeFile(buildInputPath, scriptMainCode);
  
  const webpackOptions = {
    mode: 'production',
    entry: buildInputPath,
    output: {
      path: buildFolderPath,
      filename: buildOutputFileNmae
    },
    ...webpackConfigOptions
  };
  
  const webpackCompiler = (options) => {
    return new Promise((resolve, reject) => {
      const compiler = webpack(options);
      
      // new webpack.ProgressPlugin().apply(compiler);
      
      compiler.run((err, stats) => {
        if (!err) {
          resolve(stats);
        } else {
          reject(err);
        }
      });
    });
  };
  
  await webpackCompiler(webpackOptions);
  
  scriptMainCode = await readFile(buildOutputPath);
  
  fs.unlink(buildInputPath, () => { });
  fs.unlink(buildOutputPath, () => { });
}

转码前

转码后