2.3 Node.js使用API模式的热编译

前言

前面几篇文章都在介绍rollup.js自带的执行配置文件编译的能力,从中可以看到执行配置文件编译的编译自带了watch代码的变更能力。但是用Node.js调用API编译只能执行一遍后就结束。本篇主要讲述利用Node.js编码监听文件,并且执行Node.js调用API进行热编译。

实现例子

  • 编译

demo例子

https://github.com/chenshenhai/rollupjs-note/blob/master/demo/chapter-02-03/

npm i

## 开发模式
npm run dev

## 生产模式
npm run build

实现步骤

步骤1: 目录和准备

.
├── build ## 编译脚本目录
│   ├── build.js # 生产模式编译
│   ├── compile_task.js # 编译任务
│   ├── dev.js # 开发模式编译
│   ├── rollup.config.dev.js # 开发模式配置
│   ├── rollup.config.js # 基础配置
│   └── rollup.config.prod.js # 生产模式配置
├── dist ## 编译结果目录
│   ├── index.js
│   └── lib
│       ├── hello.js
│       └── world.js
├── example ## 例子目录
│   ├── hello.html
│   ├── index.html
│   └── world.html
├── package.json
└── src ## 开发源码
    ├── index.js
    └── lib
        ├── hello.js
        └── world.js

安装对应编译的npm模块

## 安装 rollup.js 基础模块
npm i --save-dev rollup 

## 安装 rollup.js 编译本地开发服务插件
npm i --save-dev rollup-plugin-serve

## 安装 rollup.js 编译代码混淆插件
npm i --save-dev rollup-plugin-uglify

## 安装 rollup.js 编译ES6+的 babel 模块
npm i --save-dev @rollup/plugin-babel @babel/core @babel/preset-env

## 安装开发例子服务 所需模块
npm i --save-dev chalk ## 日志样式模块
npm i --save-dev chokidar ## 目录监听模块
npm i --save-dev koa  koa-static ## 服务模块
npm i --save-dev koa-compose ## koa流程控制模块,用来控制流程编译
  • rollup 模块是rollup编译的核心模块

步骤2: rollup配置

  • 编译基本配置 ./build/rollup.config.js
const path = require('path');
const { babel } = require('@rollup/plugin-babel');

const resolveFile = function(filePath) {
  return path.join(__dirname, '..', filePath)
}

const babelOptions = {
  "presets": ['@babel/preset-env']
}

const plugins = [
  babel(babelOptions),
]

module.exports = [
  {
    input: resolveFile('src/index.js'),
    output: {
      file: resolveFile('dist/index.js'),
      format: 'umd',
      name: 'Demo',
    }, 
    external: ['lib/hello', 'lib/world'],
    plugins,
  },

  {
    input: resolveFile('src/lib/hello.js'),
    output: {
      file: resolveFile('dist/lib/hello.js'),
      format: 'umd',
      name: 'Hello',
    }, 
    plugins,
  },

  {
    input: resolveFile('src/lib/world.js'),
    output: {
      file: resolveFile('dist/lib/world.js'),
      format: 'umd',
      name: 'World',
    }, 
    plugins,
  },
]
  • 开发模式基本配置 ./build/rollup.config.dev.js
const configList = require('./rollup.config');

configList.map((config, index) => {
  config.output.sourcemap = true;
  return config;
})


module.exports = configList;
  • 生产模式基本配置 ./build/rollup.config.prod.js
const { uglify } = require('rollup-plugin-uglify');
const configList = require('./rollup.config');

configList.map((config, index) => {
  config.output.sourcemap = false;
  config.plugins = [
    ...config.plugins,
    ...[
      uglify()
    ]
  ]
  return config;
})

module.exports = configList;
  • 编译任务 ./build/compile_task.js
const path = require('path');
const rollup = require('rollup');
const chalk = require('chalk');
const compose = require('koa-compose');


module.exports = compileTask;

function logger( text = '', opts = { status : 'INFO' } ) {
  let logText = '';
  switch( opts.status)  {
    case 'SUCCESS': 
      logText = `${chalk.bgGreen('[SUCCESS]')} ${chalk.green(text)}`
      break;
    case 'WARN': 
      logText = `${chalk.bgYellow('[WARN]')} ${chalk.yellow(text)}`
      break;
    case 'ERROR': 
      logText = `${chalk.bgRed('[ERROR]')} ${chalk.red(text)}`
      break;
    default:
      logText = `${chalk.bgWhite('[INFO]')} ${chalk.white(text)}`
      break;
  }
  console.log(logText);
}

function compileTask(configList){
  const taskList = [];

  configList.forEach(function(config){
    taskList.push(wrapTask(config));
  });

  compose(taskList)( ).then(function(){
    logger('END', {status: 'SUCCESS'});
  }).catch(function(err){
    console.log(err);
  })
}

function wrapTask( config ) {
  const inputOptions = config;
  const outputOptions = config.output;
  return async function(ctx, next) {
    // create a bundle
    const bundle = await rollup.rollup(inputOptions);

    logger(`开始编译 ${path.basename(inputOptions.input) }`);  
    await bundle.generate(outputOptions);
    // or write the bundle to disk
    await bundle.write(outputOptions);
    logger(`编译结束 ${path.basename(outputOptions.file)}`);   

    await next();
  }
}
  • 开发模式脚本 ./build/dev.js
const path = require('path');
const chokidar = require('chokidar');
const Koa = require('koa');
const KoaStatic = require('koa-static');
const compileTask = require('./compile_task');
const configList = require('./rollup.config.dev');

const app = new Koa();
const projectPath = path.join(__dirname, '..');
const srcPath = path.join(projectPath, 'src')

function watchSrc () {
  chokidar.watch(srcPath, {
    ignored: /(^|[\/\\])\../
  }).on('all', (event, path) => {
    if ( event === 'change' ) {
      compileTask(configList);
    }
  });
}

app.use(KoaStatic(projectPath))
app.listen(3001, function(){
  console.log('[example] http://127.0.0.1:3001/example/index.html');
  console.log('[example] http://127.0.0.1:3001/example/hello.html');
  console.log('[example] http://127.0.0.1:3001/example/world.html');
  compileTask(configList);
  watchSrc()
})
  • 生产模式脚本 ./build/build.js
const compileTask = require('./compile_task');
const configList = require('./rollup.config.prod');

compileTask(configList)
  • ./package.json配置编译执行脚本
    {
    "scripts": {
      "dev": "node ./build/dev.js",
      "build": "node ./build/build.js"
    },
    }
    

步骤3: 待编译ES6源码

  • 源码 ./src/index.js
import hello from './lib/hello';
import world from './lib/world';

export default {
  init() {
    const arr1 = [1,2,3];
    const arr2 = [4,5,6];
    console.log([...arr1, ...arr2]);

    hello.init();
    world.init();
  }
}
  • 源码 ./src/lib/hello.js
export default {
  init() {
    console.log('this lib/hello module!')
  }
}
  • 源码 ./src/lib/world.js
export default {
  init() {
    console.log('this lib/world module')
  }
}

步骤4: 编译结果

  • 在项目目录下执行 生产模式 npm run dev
$ npm run dev

> node ./build/dev.js

[example] http://127.0.0.1:3001/example/index.html
[example] http://127.0.0.1:3001/example/hello.html
[example] http://127.0.0.1:3001/example/world.html
[INFO] 开始编译 index.js
[INFO] 编译结束 index.js
[INFO] 开始编译 hello.js
[INFO] 编译结束 hello.js
[INFO] 开始编译 world.js
[INFO] 编译结束 world.js
[SUCCESS] END
  • 编译结果在目录 ./dist/

步骤5: 浏览器查看结果

  • example 页面1./example/index.html
  • 访问 http://127.0.0.1:3001/example/index.html

    <html>
    <head>
      <script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.js"></script>
    </head>
    <body>
      <p>hello world</p> 
      <script src="./../dist/index.js"></script>
      <script>
        window.Demo.init();
      </script>
    </body>
    </html>
    
    ### 控制台会显示
    > [1, 2, 3, 4, 5, 6]
    > this lib/hello module!
    > this lib/world module
    
  • example 页面1./example/hello.html

  • 访问 http://127.0.0.1:3001/example/hello.html

    <html>
    <head>
      <script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.js"></script>
    </head>
    <body>
      <p>hello page!</p> 
      <script src="./../dist/lib/hello.js"></script>
      <script>
        window.Hello.init();
      </script>
    </body>
    </html>
    
    ### 控制台会显示
    > this lib/hello module!
    
  • example 页面1./example/world.html

  • 访问 http://127.0.0.1:3001/example/world.html
    <html>
    <head>
      <script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.js"></script>
    </head>
    <body>
      <p>world page!</p> 
      <script src="./../dist/lib/world.js"></script>
      <script>
        window.World.init();
      </script>
    </body>
    </html>
    
    ### 控制台会显示
    > this lib/world module
    

results matching ""

    No results matching ""