Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

egg-init improve #379

Closed
2 tasks
atian25 opened this issue Feb 14, 2017 · 27 comments
Closed
2 tasks

egg-init improve #379

atian25 opened this issue Feb 14, 2017 · 27 comments

Comments

@atian25
Copy link
Member

atian25 commented Feb 14, 2017

@atian25
Copy link
Member Author

atian25 commented Feb 26, 2017

@popomore :
Egg 现在的模版扩展太差,底层需要优化下
#456 (comment)

发个脑洞, boilerplate 的写法:

class Boilerplate {
  getDependencies() {
    return [ 'egg-boilerplate-empty' ];
  }

  getQuestions() {
    return {
      name: {
        desc: 'project name',
      },
    };
  }
}

class SimpleBoilerplate extends Boilerplate {
  // install this first
  getDependencies() {
    return super.getDependencies().concat('egg-boilerplate-abc');
  }

  getQuestions() {
    return Object.assign({}, super.getQuestions(), {
      author: {
        desc: 'project author',
      },
    });
  }

  replaceTemplateHook(tpl, answers) {
  }

  getUsage(){
  }
};

但这样做会不会太复杂了? 做到后面感觉跟 yo 差不多了, cc @popomore

@atian25
Copy link
Member Author

atian25 commented Feb 26, 2017

plop 还没细看, 不过似乎它的理念是跟随项目的, 不太符合

Plop allows your generator code to live INSIDE your project and be versioned right along with the code it generates.

@myfreax
Copy link
Contributor

myfreax commented Feb 26, 2017

这是要做一个自动生成router,controller,view,helper,service的工具吗?

@atian25
Copy link
Member Author

atian25 commented Feb 26, 2017

主要需求是:

  • 骨架的继承,现在骨架之间有些共性的东西没法复用
  • sub generator 生成单个文件

@popomore
Copy link
Member

其他都可以不变,就用 plop 做个底层模版吧,他的 API 可以拷贝文件和渲染模版。

初始化要做的两件事情

  1. 将文件拷贝到某个目录,文件名需要修改
  2. 文件内容需要修改,需要变量替换

@atian25
Copy link
Member Author

atian25 commented Feb 27, 2017

@popomore 这些 API 是给到 boilerplate 那边做定制使用的吧?

@popomore
Copy link
Member

@atian25 是的,这样具体使用什么工具来转换内容是不用管的。egg-init 主要还是把具体初始化化的工具做好

  1. 支持自动更新模板,像 yo 需要重新全局安装
  2. 支持配置文件来选择 boilerplate,现在的 egg-init-config,可以考虑配置默认支持分组
  3. 如果可能,做到自己更新自己,或内核足够小(我们现在更新这么频繁,但全局命令的更新率是很低的)

@atian25
Copy link
Member Author

atian25 commented Feb 27, 2017

还需要:
4. 模板的继承关系,如上面我提到的 getDependencies(),这样我们那些 eslint 什么的修改, 就不用每一个模板都 PR 了。

然后你提到第一点这个也是正在考虑的,如果要做 sub generator 的话,模板肯定要有本地缓存,否则太慢。

如果但判断模板是否更新了,有点问题,因为需要判断模板的依赖有没有更新, 这里还没想好怎么判断,初步想法就是缓存一天就好了,仅能保证当天内第N次的速度,命中率不高。

第三点,目前 tnpm 的提示方式足够么?进一步的自己更新自己内核这个不知道会不会有权限问题。

@popomore
Copy link
Member

感觉这个做依赖很麻烦,还是不要支持了

@atian25
Copy link
Member Author

atian25 commented Feb 27, 2017

目前还有一个痛点,如何更新 _package.json 里面的依赖版本,可以考虑通过改名然后 autod 的方式更新下。

依赖的那个回头做的时候再看看 plug 有没有支持 sub generator 的方式直接调用。

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

昨天思考了下这块,几点考虑:

  • egg-init 极简化,逻辑放到 boilerplate 里面。
  • 应用的 devdep 里面关联自己的骨架,用于后续的 sub generator 执行,也解决了升级问题。
  • 应用的 pkgInfo 提供 boilerplate 节点,{ name, defaults },后者用于存储默认配置,如 controllerStyle: 'class'
  • 提供 boilerplate 基类,提供基础的一些方法,如 init(), addRouter(), npminstall
    • init() 方法的默认实现里面包含 eslint 等基础功能,解决之前提到的重复问题
    • 绝大部分的 boilerplate 子类都用不到这些方法,只需要配置 init() 需要的模板地址即可
    • addRouter() 里面会用到 jscodeshift 来添加路由
    • 子类用到 addRouter() 的场景,如双发,在原来添加到 http 的同时,还添加到 rpc。
  • 参考 plup 实现,不直接使用它。
$ egg-init init [dir] --type=simple
$ egg-init add service user
$ # if not exist `news` controller, create it, then add `list` action, then add router
$ egg-init add controller news.list --style=class --router=/news/list --method=get

待讨论:

  • 上述方案的方向是否 OK?
  • 兼容性
    • egg-init init [dir] 这个要讨论下是否兼容以前的 egg-init [dir]
    • 目前的字符串替换很简单,是否考虑换为 nunjucks ?
// egg-init init [dir] --type=simple
// egg-init add controller <name> --style=class
// egg-init add <type> <name>
class EggInitCommand extends Command {
  * run({ argv, cwd }) {
    const dir = argv.dir;
    let boilerplateName = argv.type;
    let action;
    // 如果目标目录不存在,则视为初始化行为
    if (!fs.existSync(dir)) {
      // 安装 boilerplate
      this.npmInstall(boilerplateName, dir);
      action = 'init';
    } else {
      // 从 pkg 读取当前应用使用的骨架
      boilerplateName = this.getPkgInfo(dir, 'boilerplate.name');
      // egg-init add <type> <name>
      action = argv._[0];
    }
    // 执行 boilerplate
    const boilerplate = require(path.join(dir, 'node_modules', boilerplateName));
    yield boilerplate.run({ action, argv, cwd } );
  }
}

class EggBoilperlate {
  addRouter() {}
  addController() {}
  addPlugin() {}
  addHelper() {}
  addService() {}
  addMiddleware() {}
  addConfig() {}
  addDependency() {}
  * run({ action, argv, cwd }) {
    switch(action) {
      case 'init':
        yield this.init();
        break;
      case 'add':
        const type = argv._[0];
        const method = `add${type}`;
        yield this[method];
        break;
      default:
        this.onAction();
    }
  }
}

class SimpleBoilperlate extends EggBoilperlate {
  addController() {
    super.addController();
    // do sth else, such as mount to RPC and HTTP router
  }
}

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

cc @popomore @fengmk2

@popomore
Copy link
Member

  1. 模版的 api 比较重要,要写下。我们现在是同名拷贝,需要考虑文件名变化的情况。尤其类似 rest api。所以要考虑两件事,从哪到哪,内容做何变化。
  2. Boilerplate 写的不是很清楚,按我理解应该是放到每个脚手架里的,那他还需要依赖 egg-init 提供的基类?peerdep ?
  3. 添加部分功能确实是需要的,是不是第一次初始化完记录一个标识?之后运行就知道是哪个脚手架了,根据目录判断不太靠谱

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

@popomore

嗯,上面只是大概的思路,方向没啥问题的话,我就写详细的 RFC 下。

  1. 如果是首次初始化,默认应该是同名拷贝即可,如果骨架有定制需求,那它自己用提供的 API 来重写 init() 即可。
  2. egg-init 是一个全局的工具,极简化。还需要提供一个 egg-boilperlate 的基类,来提供基础的 API,供具体的骨架去继承。
  3. 上面有提到,第一次初始化完,在应用的 package.json 里面加一个 boilperlate 节点,{ name, defaults } 来声明自己用的 boilperlate,并且该 boilperlate 会被加到 devdep
  4. 上面的示例代码,包含了 3 个 class,分别对应于 egg-init, egg-boilperlateegg-boilperlate-simple 三个库

@popomore
Copy link
Member

Egg-init 只提供功能,不做具体初始化工作,所以应该没有默认的,都是每个脚手架自己实现的

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

嗯,egg-init 只是负责首次的 boilerplate 下载安装,然后后面的执行(初始化和后续的单独添加)都是代理给 boilerplate

egg-boilperlate 提供 API 还有默认的 copy 行为,其他 boilerplate 继承它。

默认的行为还是需要有的,大部分 boilerplate 都不需要提供 添加部分功能,只是需要简单的复制某个目录。

@popomore
Copy link
Member

嗯,那是基类的功能,不是 egg-init 的

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

命令方面,再讨论下:

  1. 目前是 egg-init [dir] --type=simple,这个要兼容,还是用新的 egg-init init [dir] --type=simple
  2. 除了 egg-init add <type> <name> --options 还有其他指令可能会存在么?

@popomore
Copy link
Member

保留 egg-init [dir] --type=simple 吧,这个是最多的使用场景。

其他可以将命令行映射到类方法,比如 egg-init add controller nameboilerplate.addController(name)

@atian25
Copy link
Member Author

atian25 commented Mar 14, 2017

嗯,不做自动映射了,子类自己搞:

init 就是默认 command 了

  * run({ action, argv, cwd }) {
    switch(action) {
      case 'init':
        yield this.init();
        break;
      case 'add':
        const type = argv._[0];
        const method = `add${type}`;
        yield this[method];
        break;
      default:
        this.onAction();
    }
  }

@popomore
Copy link
Member

我认为脚手架应该有更新功能,不只是脚手架本身可以更新,脚手架还得支持更新生成的代码,比如 egg 的脚手架,egg 升级到了 2.0 那在 1.0 的代码下执行会提示是否升级到 2.0。

脚手架更新机制

脚手架工具和脚手架应该是解耦合的,脚手架工具只提供功能,而脚手架提供模版和初始化代码,这个和 yeoman 一样。但是 yeoman 将脚手架视为 npm 模块,只负责查找不管更新。应该每次执行都看下版本更新,始终使用最新的。

  1. 脚手架工具每次执行会检查脚手架是否有新版本,如果有新版本更新则使用最新版
  2. 在本地根据脚手架名和版本缓存,便于比较

脚手架 API

脚手架应该能用完善的 API,支持

  1. 文件添加,拷贝,改名,删除
  2. 文件内容修改,可以自己选择工具,比如 nunjucks jscodeshift

脚手架版本化

脚手架应该支持多版本,并且使用 --upgrade 可以升级

@popomore
Copy link
Member

popomore commented May 5, 2017

@atian25 我会脱离 egg 社区做一个脚手架的工具,这个可以先放放,专心做 egg-script

@atian25
Copy link
Member Author

atian25 commented May 5, 2017 via email

@atian25
Copy link
Member Author

atian25 commented Jul 28, 2017

这篇介绍 vue cli 的文章里面看到 metalsmith 貌似挺有意思的。

@atian25
Copy link
Member Author

atian25 commented Sep 27, 2017

@popomore https://github.com/sapegin/mrm 这个有意思

看了下 https://github.com/sapegin/mrm-core 源码感觉一般,回头我们自己实现也问题不大。

@atian25
Copy link
Member Author

atian25 commented Feb 11, 2018

@dead-horse
Copy link
Member

先关掉了,后续如果再有想法再新开 issue 跟进吧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants