common-boilerplate

0.15.0 • Public • Published

common-boilerplate

base class for boilerplate

NPM version NPM quality NPM download

Continuous Integration Test coverage

Write your boilerplate

use create-common-boilerplate for quick start.

$ npm init common-boilerplate

Lifecycle

- ask question
- list all file from boilerplate paths
- render files to target dir
- do post jobs

Directory

├── bin
│   └── cli.js
│
├── boilerplate
│   ├── lib
│   ├── test
│   ├── README.md
│   ├── _.eslintrc
│   ├── _.gitignore
│   ├── _package.json
│   └── index.js
│
├── test
│   └── index.test.js
├── index.js
├── README.md
└── package.json
  • index.js is your Boilerplate Logic, the main entry.
  • boilerplate/** is your template dir, will be copy to dest.

Boilerplate Entry

// index.js
const Boilerplate = require('common-boilerplate');

class MainBoilerplate extends Boilerplate {
  // must provide your directory
  get [Symbol.for('boilerplate#root')]() {
    return __dirname;
  }
};

module.exports = MainBoilerplate;

Ask questions

Inquirer is built-in to provide prompt helper.

Add your questions:

class MainBoilerplate extends Boilerplate {
  async askQuestions() {
    const answers = await this.prompt([
      {
        name: 'name',
        type: 'input',
        message: 'Project Name: ',
        default: () => this.locals.name, // set default from locals
      },
      {
        type: 'list',
        name: 'type',
        message: 'choose your type:',
        choices: [ 'simple', 'plugin', 'framework' ],
      },
    ]);
    this.setLocals(answers);

    // use built-in questions
    await this.askGit();
  }
};

Built-in Questions:

  • askNpm(): ask for name / scope / description, and pkgName getter.
  • askGit(): ask for repository

Locals

this.locals is used to fill the template, it's merge from built-in -> argv -> user's prompt answer;

Built-in:

  • name - project name, by default to git repository name
  • user - user info
    • name - git config user.name
    • email - git config user.email
    • author - ${user} <${email}>
  • gitInfo - git url info
    • extract from git config remote.origin.url
    • see git-url-parse for more details.
  • npm - npm global cli name, will guest by order: tnpm -> cnpm -> npm
  • registry - npm registry url, not set by default

Template Render

Built-in render is nunjucks.

And use micromatch to match this.templateRules to treat as template.

this.templateRules = [ '!res/**' ];

File Name Convert

  • also use template render, so {{name}}.test.js is supported.
  • some file is special, so you can't use it's origin name
    • such as boilerplate/package.json, npm will read files and ignore your files.
    • use _ as prefix, such as _package.json / _.gitignore / _.eslintrc
    • add your mapping by this.fileMapping

Default mappings:

this.fileMapping = {
  gitignore: '.gitignore',
  _gitignore: '.gitignore',
  '_.gitignore': '.gitignore',
  '_package.json': 'package.json',
  '_.eslintrc': '.eslintrc',
  '_.eslintignore': '.eslintignore',
  '_.npmignore': '.npmignore',
};

Logger

Provide powerful cli logger for developer, see consola for more details.

debug is disabled by default, use --verbose or DEBUG= to print all logs.

this.logger.info('this is info log');

this.logger.level = 'DEBUG';

HttpClient

Provide httpclient for developer, see urllib for more details.

await this.request(url, opts);

Use this.requestOpts as default request options.

RunScript

Provide runscript for developer, see runscript for more details.

cwd is set to target dir, and will use this.local.npm as cli.

await this.runScript('ci', { grep: 'home.test.js' }, {});

await this.installDeps({ optional : false });

await this.runTest({});

CommandLine argv

Also support custom argv:

  • argv will convert to camelCase, such as --page-size=1 -> pageSize
  • dot prop will convert to nested object, such as --page.size=1 -> { page: { size: '1' } }
  • see yargs#optionskey for more details
class MainBoilerplate extends Boilerplate {
  // use as `--test=123 --str=456`
  initOptions() {
    const options = Object.assign({}, super.initOptions());

    options.test = {
      type: 'string',
      description: 'just a test',
    };

    options.str = {
      type: 'string',
      description: 'just a str',
    };

    return options;
  }
};

Built-in:

  • --baseDir= - directory of application, default to process.cwd()
  • --npm= - npm cli, tnpm/cnpm/npm, will auto guess
  • --registry= - npm registry url, also support alias -r=china, will auto guest from npm cli.
  • --force - force to override directory if it's not empty

Boilerplate Chain

Support mutli-level boilerplate, so you can share logic between boilerplates.

class ShareBoilerplate extends Boilerplate {
  // must provide your directory
  get [Symbol.for('boilerplate#root')]() {
    return __dirname;
  }
};
module.exports = ShareBoilerplate;
// child
class MainBoilerplate extends ShareBoilerplate {
  // must provide your directory
  get [Symbol.for('boilerplate#root')]() {
    return __dirname;
  }

  // example for ignore some files from parent
  async listFiles(...args) {
    const files = await super.listFiles(...args);
    files['github.png'] = undefined;
    return files;
  }
};
module.exports = MainBoilerplate;
  • must provide getter Symbol.for('boilerplate#root') to announce your root, and boilerplate directory is required to exists at your root directory.
  • will auto load all files from boilerplate, same key will be override.
  • you could custom by async listFiles(), such as ignore some files from parent.

Unit Testing

Use Coffee and assert-file.

const coffee = require('coffee');
const assertFile = require('assert-file');
const { rimraf, mkdirp } = require('mz-modules');

describe('test/index.test.js', () => {
  const fixtures = path.join(__dirname, 'fixtures');
  const tmpDir = path.join(__dirname, '.tmp');

  beforeEach(async () => {
    await rimraf(tmpDir);
    await mkdirp(tmpDir);
  });

  it('should work', async () => {
    // run cli
    await coffee.fork(path.join(fixtures, 'simple/bin/cli.js'), [], { cwd: tmpDir })
      // .debug()
      // tell coffee to listen prompt event then auto answer
      .waitForPrompt()
      // answer to the questions
      .writeKey('example\n')
      .writeKey('ENTER')
      // emit `DOWN` key to select the second choise
      .writeKey('DOWN', 'ENTER')
      .expect('stdout', /npm install --no-package-lock/)
      .expect('stdout', /1 passing/)
      .expect('code', 0)
      .end();

    // expect to be exists
    assertFile(`${tmpDir}/.gitignore`);

    // check with `includes`
    assertFile(`${tmpDir}/README.md`, 'name = example');

    // check with regex
    assertFile(`${tmpDir}/README.md`, /name = example/);

    // check whether contains json
    assertFile(`${tmpDir}/package.json`, {
      name: 'example',
      boilerplate: {
        name: 'common-boilerplate-test-project',
        version: '1.0.0',
      }
    });
  });
});

Readme

Keywords

none

Package Sidebar

Install

npm i common-boilerplate

Weekly Downloads

0

Version

0.15.0

License

MIT

Unpacked Size

30 kB

Total Files

5

Last publish

Collaborators

  • akitasummer
  • gxkl
  • gemwuu
  • eggjs-admin
  • fengmk2
  • atian25
  • dead_horse
  • wanghx
  • hyj1991
  • killagu
  • coolme200
  • mansonchor.zzw
  • hubcarl