├── .husky ├── .gitignore └── pre-commit ├── .gitignore ├── .npmrc ├── .eslintignore ├── template ├── .npmrc ├── .gitignore ├── .eslintignore ├── src │ ├── bar.ts │ ├── foo.ts │ └── index.ts ├── test │ ├── setup.ts │ ├── bar.test.ts │ └── index.test.ts ├── CHANGELOG.md ├── babel.config.js ├── tsconfig.rollup.json ├── README.md ├── .eslintrc.js ├── build │ ├── resolver.js │ └── rollup.config.js ├── jest.config.js ├── package.json ├── tsconfig.json └── LICENSE ├── CHANGELOG.md ├── .vscode └── settings.json ├── bin ├── draftbook.js └── draftbook-add.js ├── lib ├── handler │ ├── commonHandler.js │ ├── index.js │ ├── huskyHandler.js │ ├── eslintHandler.js │ ├── tsHandler.js │ └── babelHandler.js ├── index.js └── utils.js ├── .eslintrc.js ├── jest.config.js ├── test ├── lib.test.js ├── bin.test.js └── utils.js ├── package.json ├── constants └── index.js ├── README.md └── LICENSE /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry = https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | template -------------------------------------------------------------------------------- /template/.npmrc: -------------------------------------------------------------------------------- 1 | registry = https://registry.npmjs.org/ -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .vscode -------------------------------------------------------------------------------- /template/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modlues 2 | dist 3 | jest.config.js 4 | coverage -------------------------------------------------------------------------------- /template/src/bar.ts: -------------------------------------------------------------------------------- 1 | export function bar () { 2 | return 'I am bar!' 3 | } 4 | -------------------------------------------------------------------------------- /template/src/foo.ts: -------------------------------------------------------------------------------- 1 | export function foo (name) { 2 | return name + ' hello!' 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run pre-commit 5 | -------------------------------------------------------------------------------- /template/test/setup.ts: -------------------------------------------------------------------------------- 1 | // 有 await、async 相关的代码时引入可自行 npm install regenerator-runtime -D 2 | // import 'regenerator-runtime'; 3 | -------------------------------------------------------------------------------- /template/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## [0.0.1] - 2021-07-09 4 | 5 | ### Added 6 | - 初始化项目 7 | 8 | > exp: https://keepachangelog.com/zh-CN/1.0.0/ -------------------------------------------------------------------------------- /template/test/bar.test.ts: -------------------------------------------------------------------------------- 1 | import { bar } from '../src/bar' 2 | 3 | test('bar out put is ok', () => { 4 | // Assert 5 | expect(bar()).toBe('I am bar!') 6 | }) 7 | -------------------------------------------------------------------------------- /template/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import {sdk} from '../src/index'; 2 | 3 | test('sdk out put is ok', () => { 4 | // Assert 5 | expect(sdk.hello(sdk.name)).toBe('sdk hello!'); 6 | }); -------------------------------------------------------------------------------- /template/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', { 5 | useBuiltIns: false 6 | } 7 | ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.1] - 2021-09-29 2 | 3 | ### Changed 4 | - 文档修改 5 | - pre-commit 修改 6 | 7 | ## [1.0.0] - 2021-09-8 8 | 9 | ### Added 10 | - 初始化项目 11 | 12 | > exp: https://keepachangelog.com/zh-CN/1.0.0/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "launch": { 3 | 4 | "configurations": [], 5 | "compounds": [] 6 | }, 7 | "editor.codeActionsOnSave": { 8 | // "source.fixAll": true 9 | } 10 | } -------------------------------------------------------------------------------- /template/src/index.ts: -------------------------------------------------------------------------------- 1 | import { foo } from './foo' 2 | import { bar } from './bar' 3 | 4 | const sdk: any = { 5 | hello: foo, 6 | name: 'sdk', 7 | pro: new Promise((resolve) => { resolve('res') }) 8 | } 9 | 10 | export { sdk, bar } 11 | -------------------------------------------------------------------------------- /bin/draftbook.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const { program } = require('commander') 3 | const pkg = require('../package.json') 4 | 5 | program 6 | .version(pkg.version, '-v, --version', 'output the current version') 7 | .command('add', 'create a new sdk in current dir') 8 | .parse() 9 | -------------------------------------------------------------------------------- /lib/handler/commonHandler.js: -------------------------------------------------------------------------------- 1 | const { getFile } = require('../utils.js') 2 | 3 | exports.commonHandler = (allFiles, { name, eslint }) => { 4 | const packageItem = getFile(allFiles, 'package.json') 5 | const parsedPackage = JSON.parse(packageItem.file) 6 | 7 | parsedPackage.name = name 8 | parsedPackage.files = ['dist'] 9 | packageItem.file = JSON.stringify(parsedPackage, null, 2) 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true 5 | }, 6 | extends: [ 7 | 'standard' 8 | ], 9 | overrides: [ 10 | { 11 | files: 'test/**/*.*', 12 | env: { 13 | 'jest/globals': true 14 | }, 15 | plugins: ['jest'] 16 | } 17 | ], 18 | parserOptions: { 19 | ecmaVersion: 12, 20 | sourceType: 'module' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/handler/index.js: -------------------------------------------------------------------------------- 1 | const { babelHandler } = require('./babelHandler.js') 2 | const { tsHandler } = require('./tsHandler.js') 3 | const { eslintHandler } = require('./eslintHandler.js') 4 | const { commonHandler } = require('./commonHandler.js') 5 | const { huskyHandler } = require('./huskyHandler.js') 6 | 7 | module.exports = { 8 | babelHandler, 9 | tsHandler, 10 | eslintHandler, 11 | commonHandler, 12 | huskyHandler 13 | } 14 | -------------------------------------------------------------------------------- /template/tsconfig.rollup.json: -------------------------------------------------------------------------------- 1 | /** 2 | * tsconfig.json 用于编辑器、jest 等正确识别类型 3 | * 使用 rollup 的时候,需要 exclude test,并且保证入口为 src 4 | **/ 5 | { 6 | "extends": "./tsconfig.json", 7 | "compilerOptions": { 8 | /* 使用 rollup 打包的时候,不需要 tsc 处理 ECMAScript target,babel 会处理 */ 9 | "target": "esnext", 10 | "outDir": "./dist/es", 11 | "rootDir": "./src", 12 | }, 13 | "exclude": [ 14 | "test" 15 | ] 16 | } -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | # SDK-NAME 2 | 3 | ## 介绍 4 | 5 | 介绍 6 | 7 | ## 安装方式 8 | 9 | ``` 10 | npm i [package-name] 11 | ``` 12 | 13 | ## 使用方式 14 | 15 | ```javascript 16 | import PackageName from 'package-name' 17 | 18 | const ins = new PackageName() 19 | 20 | ins.exp() 21 | ``` 22 | 23 | ## API 24 | 25 | 属性 | 说明 | 类型 | 默认值 | 枚举 | 是否必须 | 26 | ----|-----|------|------|------|------| 27 | | prop | intro | type | default | enum | boolean | 28 | -------------------------------------------------------------------------------- /template/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true 6 | }, 7 | extends: ['standard'], 8 | overrides: [{ 9 | files: 'test/**/*.*', 10 | env: { 11 | 'jest/globals': true 12 | }, 13 | plugins: ['jest'] 14 | }], 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | ecmaVersion: 12, 18 | sourceType: 'module' 19 | }, 20 | plugins: [ 21 | '@typescript-eslint' 22 | ], 23 | rules: {} 24 | } 25 | -------------------------------------------------------------------------------- /lib/handler/huskyHandler.js: -------------------------------------------------------------------------------- 1 | 2 | const { getFile } = require('../utils.js') 3 | 4 | exports.huskyHandler = (allFiles, { husky, eslint }) => { 5 | if (husky === true) { 6 | const packageItem = getFile(allFiles, 'package.json') 7 | const parsedPackage = JSON.parse(packageItem.file) 8 | 9 | parsedPackage.scripts.prepare = 'husky install && npx husky add .husky/pre-commit "npx lint-staged"' 10 | parsedPackage.devDependencies['lint-staged'] = '^10.5.4' 11 | parsedPackage.devDependencies.husky = '^6.0.0' 12 | const lintStage = {} 13 | if (eslint) { 14 | lintStage['src/**/*.*'] = [ 15 | 'eslint --fix' 16 | ] 17 | } 18 | parsedPackage['lint-staged'] = lintStage 19 | packageItem.file = JSON.stringify(parsedPackage) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // Indicates whether the coverage information should be collected while executing the test 8 | collectCoverage: true, 9 | 10 | // An array of glob patterns indicating a set of files for which coverage information should be collected 11 | collectCoverageFrom: ['**/lib/**'], 12 | 13 | // The directory where Jest should output its coverage files 14 | coverageDirectory: 'coverage', 15 | 16 | // Indicates which provider should be used to instrument code for coverage 17 | coverageProvider: 'v8', 18 | 19 | // The glob patterns Jest uses to detect test files 20 | testMatch: ['/test/?(*.)+(spec|test).[tj]s?(x)'] 21 | } 22 | -------------------------------------------------------------------------------- /template/build/resolver.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const resolve = require('resolve') 4 | 5 | function mapModuleFieldToMain (pkg, pkgDir) { 6 | const moduleSrcPath = pkg.module 7 | const isModuleFieldAvailable = moduleSrcPath && 8 | fs.existsSync(path.resolve(pkgDir, moduleSrcPath)) 9 | 10 | if (isModuleFieldAvailable) { 11 | return Object.assign({ }, pkg, { main: moduleSrcPath }) 12 | } 13 | return pkg 14 | } 15 | 16 | function defaultResolver (pathStr, options) { 17 | return resolve.sync(pathStr, { 18 | basedir: options.basedir, 19 | extensions: options.extensions, 20 | moduleDirectory: options.moduleDirectory, 21 | paths: options.paths, 22 | packageFilter: mapModuleFieldToMain 23 | }) 24 | } 25 | 26 | module.exports = defaultResolver 27 | -------------------------------------------------------------------------------- /test/lib.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { newSdk } = require('../lib/index') 4 | const { allPossibleAnsers } = require('./utils') 5 | 6 | const snapshot = path.resolve(__dirname, './snapshot-lib') 7 | 8 | describe('Lib Test\n', () => { 9 | beforeAll(() => { 10 | if (!fs.existsSync(snapshot)) { 11 | fs.mkdirSync(snapshot) 12 | } 13 | process.chdir(snapshot) 14 | }) 15 | 16 | allPossibleAnsers.forEach(answer => { 17 | it(`Different Answer Check ${JSON.stringify(answer)}\n`, () => { 18 | newSdk(answer) 19 | }) 20 | }) 21 | 22 | it('Exists SDK Check', () => { 23 | newSdk(allPossibleAnsers[0]) 24 | }) 25 | 26 | afterAll(() => { 27 | fs.rmdirSync(snapshot, { force: true, recursive: true }) 28 | fs.mkdirSync(snapshot) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /lib/handler/eslintHandler.js: -------------------------------------------------------------------------------- 1 | const { getFile } = require('../utils.js') 2 | 3 | exports.eslintHandler = (allFiles, { eslint }) => { 4 | if (eslint === false) { 5 | const eslintrc = getFile(allFiles, '.eslintrc.js') 6 | eslintrc.isDelete = true 7 | 8 | const eslintignore = getFile(allFiles, '.eslintignore') 9 | eslintignore.isDelete = true 10 | 11 | const packageItem = getFile(allFiles, 'package.json') 12 | const parsedPackage = JSON.parse(packageItem.file) 13 | 14 | delete parsedPackage.scripts.eslint 15 | 16 | delete parsedPackage.devDependencies.eslint 17 | delete parsedPackage.devDependencies['eslint-config-standard'] 18 | delete parsedPackage.devDependencies['eslint-plugin-jest'] 19 | delete parsedPackage.devDependencies['@typescript-eslint/parser'] 20 | delete parsedPackage.devDependencies['@typescript-eslint/eslint-plugin'] 21 | 22 | packageItem.file = JSON.stringify(parsedPackage) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require('path') 3 | const { 4 | readGitignore, 5 | readFiles, 6 | writeFiles 7 | } = require('./utils.js') 8 | const { 9 | babelHandler, 10 | tsHandler, 11 | eslintHandler, 12 | commonHandler, 13 | huskyHandler 14 | } = require('./handler/index.js') 15 | 16 | const TEMPLATE_ROOT = path.resolve(__dirname, '../template/') 17 | 18 | exports.newSdk = (answers) => { 19 | // 获取需要忽略的文件。主要用于开发环境 20 | const gitignore = readGitignore(TEMPLATE_ROOT) 21 | // read 获取模板文件 22 | const allFiles = [readFiles(TEMPLATE_ROOT, gitignore)] 23 | 24 | // handler 处理对应的配置 25 | babelHandler(allFiles, answers) 26 | tsHandler(allFiles, answers) 27 | eslintHandler(allFiles, answers) 28 | huskyHandler(allFiles, answers) 29 | commonHandler(allFiles, answers) 30 | 31 | // write 输出模板文件 32 | allFiles[0].name = answers.name 33 | writeFiles({ currentFiles: allFiles, pathValue: path.resolve(process.cwd()), answers }) 34 | } 35 | -------------------------------------------------------------------------------- /template/jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property and type check, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // Automatically clear mock calls and instances between every test 8 | clearMocks: true, 9 | 10 | // Indicates whether the coverage information should be collected while executing the test 11 | collectCoverage: true, 12 | 13 | // The directory where Jest should output its coverage files 14 | coverageDirectory: 'coverage', 15 | 16 | // A preset that is used as a base for Jest's configuration 17 | preset: 'ts-jest/presets/js-with-ts-esm', 18 | 19 | // The paths to modules that run some code to configure or set up the testing environment before each test 20 | setupFiles: ['./test/setup.ts'], 21 | 22 | // The test environment that will be used for testing 23 | testEnvironment: 'jsdom', 24 | 25 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 26 | // 所有匹配的文件都不会被转化,默认是 node_modules。但是如果 node_modules 中某个依赖使用了 esm 规范,是需要转化的。例如 @xxx 相关的依赖 27 | // transformIgnorePatterns: ['/node_modules/(?!@xxx/)'], 28 | 29 | // A path to a custom resolver 30 | // 自定义一个 resolver,优先读取依赖的 esm 入口。和 rollup 对齐 31 | resolver: '/build/resolver.js' 32 | } 33 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template", 3 | "main": "./dist/cjs/index.js", 4 | "module": "./dist/es/index.js", 5 | "types": "./dist/es/types/index.d.ts", 6 | "version": "1.0.0", 7 | "devDependencies": { 8 | "@babel/preset-env": "^7.14.7", 9 | "@rollup/plugin-babel": "^5.3.0", 10 | "@rollup/plugin-commonjs": "^19.0.0", 11 | "@rollup/plugin-node-resolve": "^13.0.0", 12 | "@rollup/plugin-typescript": "^8.2.1", 13 | "@types/jest": "^27.0.0", 14 | "@typescript-eslint/eslint-plugin": "^4.31.0", 15 | "@typescript-eslint/parser": "^4.31.0", 16 | "cross-env": "^7.0.3", 17 | "eslint": "^7.30.0", 18 | "eslint-config-standard": "^16.0.3", 19 | "eslint-plugin-jest": "^24.4.0", 20 | "jest": "^27.0.6", 21 | "rollup": "^2.52.6", 22 | "ts-jest": "^27.0.4", 23 | "tslib": "^2.3.0", 24 | "typescript": "4.2.4" 25 | }, 26 | "peerDependencies": {}, 27 | "dependencies": {}, 28 | "scripts": { 29 | "build": "rm -rf ./dist && rollup -c build/rollup.config.js", 30 | "dev": "rm -rf ./dist && cross-env ROLLUP_ENV=development rollup -c build/rollup.config.js --watch", 31 | "prepatch": "npm run build && npm version prerelease --preid=alpha && npm publish --tag alpha", 32 | "patch": "npm run test && npm run build && npm version patch && npm publish", 33 | "eslint": "eslint", 34 | "test": "jest" 35 | }, 36 | "license": "ISC", 37 | "engines": { 38 | "node": ">=10.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@draftbook/cli", 3 | "version": "1.0.2", 4 | "description": "Draftbook cli,For quickly creating a customized sdk", 5 | "main": "index.js", 6 | "bin": { 7 | "draftbook": "bin/draftbook.js" 8 | }, 9 | "scripts": { 10 | "prepatch": "npm version prerelease --preid=alpha && npm publish --tag alpha", 11 | "patch": "npm run test && npm version patch && npm publish --ignore-scripts", 12 | "test": "jest", 13 | "eslint": "eslint", 14 | "prepare": "husky install && npx husky add .husky/pre-commit \"npm run pre-commit\"", 15 | "pre-commit": "lint-staged" 16 | }, 17 | "lint-staged": { 18 | "(lib|bin|constants|test)/**/*.*": [ 19 | "eslint --fix" 20 | ] 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/draftbookTools/cli.git" 25 | }, 26 | "author": "qqqqqcy", 27 | "dependencies": { 28 | "boxen": "^5.0.1", 29 | "chalk": "^4.1.2", 30 | "commander": "^7.2.0", 31 | "gogocode": "^1.0.16", 32 | "inquirer": "^8.1.1", 33 | "update-notifier": "^5.1.0" 34 | }, 35 | "devDependencies": { 36 | "eslint": "^7.32.0", 37 | "eslint-config-standard": "^16.0.3", 38 | "eslint-plugin-jest": "^24.4.0", 39 | "husky": "^6.0.0", 40 | "jest": "^27.1.0", 41 | "lint-staged": "^11.1.2" 42 | }, 43 | "engines": { 44 | "node": ">=10.0.0" 45 | }, 46 | "license": "ISC", 47 | "bugs": { 48 | "url": "https://github.com/draftbookTools/cli/issues" 49 | }, 50 | "homepage": "https://github.com/draftbookTools/cli#readme" 51 | } 52 | -------------------------------------------------------------------------------- /template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ 7 | "module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 8 | "lib": ["ESNext", "DOM"], /* Specify library files to be included in the compilation. */ 9 | 10 | /* Strict Type-Checking Options */ 11 | "strict": true, /* Enable all strict type-checking options. */ 12 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ 13 | "allowJs": true, 14 | 15 | /* Module Resolution Options */ 16 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 17 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 18 | 19 | /* Advanced Options */ 20 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 21 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 22 | }, 23 | "include": ["src", "test"] 24 | } 25 | -------------------------------------------------------------------------------- /test/bin.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { spawn } = require('child_process') 4 | 5 | const binDir = path.resolve(__dirname, '../bin') 6 | const snapshot = path.resolve(__dirname, './snapshot-bin') 7 | 8 | // bin 文件会被测试,但是无法被计入 Coverage 9 | describe('Bin\n', () => { 10 | beforeAll(() => { 11 | if (!fs.existsSync(snapshot)) { 12 | fs.mkdirSync(snapshot) 13 | } 14 | process.chdir(snapshot) 15 | }) 16 | 17 | it('Draftbook', done => { 18 | const output = spawn('node', [path.resolve(binDir, 'draftbook.js'), '-h']) 19 | const stdout = 'Usage: draftbook [options] [command]\n' + 20 | '\n' + 21 | 'Options:\n' + 22 | ' -v, --version output the current version\n' + 23 | ' -h, --help display help for command\n' + 24 | '\n' + 25 | 'Commands:\n' + 26 | ' add create a new sdk in current dir\n' + 27 | ' help [command] display help for command\n' 28 | 29 | output.stdout.on('data', data => { 30 | expect(stdout.toString()).toEqual(data.toString()) 31 | }) 32 | 33 | output.on('close', code => { 34 | expect(code).toEqual(0) 35 | done() 36 | }) 37 | }) 38 | 39 | it('Draftbook Add', done => { 40 | const output = spawn('node', [path.resolve(binDir, 'draftbook-add.js'), '-h']) 41 | output.stdout.on('data', () => { 42 | // mock 回车 43 | output.stdin.write('\n') 44 | }) 45 | 46 | output.on('close', code => { 47 | expect(code).toEqual(0) 48 | done() 49 | }) 50 | }) 51 | 52 | afterAll(() => { 53 | fs.rmdirSync(snapshot, { force: true, recursive: true }) 54 | fs.mkdirSync(snapshot) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 2 | const { QUESTIONS } = require('../constants') 3 | 4 | function getAllPossibleAnsers () { 5 | const answerList = [] 6 | const answer = { name: '' } 7 | let count = 0 8 | 9 | // 递归生成所有配置`的枚举 10 | function getAnswerRecursively (index) { 11 | // 边界 12 | if (!QUESTIONS[index]) { 13 | answer.name = `test-${++count}` 14 | answerList.push({ ...answer }) 15 | return undefined 16 | } 17 | 18 | const { 19 | name, type, default: defaultVal, choices = [], when 20 | } = QUESTIONS[index] 21 | if (when && !when(answer)) { 22 | answer[name] = defaultVal 23 | getAnswerRecursively(index + 1) 24 | delete answer[name] 25 | } else if (name === 'name') { 26 | getAnswerRecursively(index + 1) 27 | } else if (type === 'list') { 28 | choices.forEach(item => { 29 | if ( 30 | Object.prototype.toString.call(item) === '[object Object]' && item.value 31 | ) { 32 | answer[name] = item.value 33 | getAnswerRecursively(index + 1) 34 | delete answer[name] 35 | } else if (Object.prototype.toString.call(item) === '[object String]') { 36 | answer[name] = item 37 | getAnswerRecursively(index + 1) 38 | delete answer[name] 39 | } 40 | }) 41 | } else if (type === 'confirm') { 42 | answer[name] = true 43 | getAnswerRecursively(index + 1) 44 | answer[name] = false 45 | getAnswerRecursively(index + 1) 46 | delete answer[name] 47 | } else { 48 | console.error('未覆盖的 type,请添加对应测试') 49 | } 50 | return false 51 | } 52 | 53 | getAnswerRecursively(0) 54 | return answerList 55 | } 56 | 57 | module.exports = { 58 | allPossibleAnsers: getAllPossibleAnsers() 59 | } 60 | -------------------------------------------------------------------------------- /bin/draftbook-add.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const updateNotifier = require('update-notifier') 3 | const chalk = require('chalk') 4 | const boxen = require('boxen') 5 | 6 | const pkg = require('../package.json') 7 | const { newSdk } = require('../lib/index') 8 | const { TEMPLATE_NAME, QUESTIONS } = require('../constants') 9 | 10 | console.log('Hi, welcome to Draftbook!') 11 | 12 | const PRESET_TEMPLATES = { 13 | [TEMPLATE_NAME.FAST_START]: { 14 | babel: 'runtime', 15 | framework: 'none', 16 | eslint: true, 17 | husky: true 18 | }, 19 | [TEMPLATE_NAME.CUSTOM]: {} 20 | } 21 | 22 | const distTag = pkg.version.includes('alpha') ? 'alpha' : 'latest' 23 | const notifier = updateNotifier({ 24 | distTag, 25 | pkg, 26 | updateCheckInterval: 0 27 | }) 28 | 29 | notifier 30 | .fetchInfo() 31 | .then(res => { 32 | // 网络异常,获取不到包版本信息时,允许跳过检查 33 | const ignoreVersionCheck = process.argv.join('|').includes('ignoreVersionCheck') 34 | if (res.latest === res.current || ignoreVersionCheck) { 35 | // 主逻辑 36 | inquirer.prompt(QUESTIONS).then(answers => { 37 | newSdk({ ...PRESET_TEMPLATES[answers.template], ...answers }) 38 | }) 39 | } else { 40 | // 不适用 notifier 原生的输出方法,因为第一次运行的时候有几率不提示 41 | console.log(boxen( 42 | [ 43 | `Update available ${chalk.dim(res.current)} → ${chalk.green(res.latest)}`, 44 | `${chalk.bold.blueBright(res.name)}'s version is ${chalk.red('too old')}`, 45 | `Please run ${chalk.cyan(`npm i ${res.name}@${distTag} -g`)} to update.` 46 | ].join('\n'), 47 | { 48 | padding: 1, borderColor: 'yellow', align: 'center', margin: 1 49 | } 50 | )) 51 | } 52 | }).catch(() => { 53 | console.log(chalk.red('Failed to get @baidu/draftbook version info!')) 54 | console.log(chalk.red('Please try again')) 55 | console.log(chalk.red('You can use \'draftbook add --ignoreVersionCheck\' to avoid version check')) 56 | }) 57 | -------------------------------------------------------------------------------- /lib/handler/tsHandler.js: -------------------------------------------------------------------------------- 1 | const $ = require('gogocode') 2 | const { getFile } = require('../utils.js') 3 | 4 | exports.tsHandler = (allFiles, { typescript }) => { 5 | if (typescript === false) { 6 | const rollupConfig = getFile(allFiles, 'rollup.config.js') 7 | rollupConfig.file = $(rollupConfig.file) 8 | .replace('input: resolve($_$1, $_$2)', 'input: resolve($_$1, \'../src/index.js\')') 9 | .remove('import typescript from \'$_$\'') 10 | .remove('const typescriptOptions = $_$') 11 | .remove('typescript($_$)') 12 | .generate() 13 | 14 | const jestConfig = getFile(allFiles, 'jest.config.js') 15 | jestConfig.file = $(jestConfig.file) 16 | .replace('setupFiles: [\'./test/setup.ts\']', 'setupFiles: [\'./test/setup.js\']') 17 | .remove('preset: \'ts-jest/presets/js-with-ts-esm\'') 18 | .generate() 19 | 20 | const tsconfig = getFile(allFiles, 'tsconfig.json') 21 | tsconfig.isDelete = true 22 | const tsconfigRollup = getFile(allFiles, 'tsconfig.rollup.json') 23 | tsconfigRollup.isDelete = true 24 | 25 | const eslintrc = getFile(allFiles, '.eslintrc.js') 26 | eslintrc.file = $(eslintrc.file) 27 | .replace('\'extends\': [$$$, \'@ecomfe/eslint-config/typescript/strict\']', '\'extends\': [$$$]') 28 | .remove('\'parser\': \'@typescript-eslint/parser\'') 29 | .generate() 30 | 31 | const packageItem = getFile(allFiles, 'package.json') 32 | const parsedPackage = JSON.parse(packageItem.file) 33 | 34 | delete parsedPackage.types 35 | delete parsedPackage.devDependencies['@rollup/plugin-typescript'] 36 | delete parsedPackage.devDependencies['@typescript-eslint/parser'] 37 | delete parsedPackage.devDependencies['@typescript-eslint/eslint-plugin'] 38 | delete parsedPackage.devDependencies['@types/jest'] 39 | delete parsedPackage.devDependencies['ts-jest'] 40 | delete parsedPackage.devDependencies.typescript 41 | delete parsedPackage.devDependencies.tslib 42 | 43 | packageItem.file = JSON.stringify(parsedPackage) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/handler/babelHandler.js: -------------------------------------------------------------------------------- 1 | 2 | const $ = require('gogocode') 3 | const { getFile } = require('../utils.js') 4 | 5 | exports.babelHandler = (allFiles, { babel }) => { 6 | if (babel !== 'ignore') { 7 | const rollupConfig = getFile(allFiles, 'rollup.config.js') 8 | const onlySyntax = `{ 9 | ...base, 10 | output: { ...output(ES_ONLY_CONVERTED_SYNTAX), format: FORMAT.ES }, 11 | plugins: plugins(ES_ONLY_CONVERTED_SYNTAX) 12 | }` 13 | 14 | rollupConfig.file = $(rollupConfig.file) 15 | .find('const FORMAT = $_$') 16 | .before('const ES_ONLY_CONVERTED_SYNTAX = \'es-only-converted-syntax\'') 17 | .root() 18 | 19 | .find('babel($_$)') 20 | .replace('babelHelpers: $_$', 'babelHelpers: format === ES_ONLY_CONVERTED_SYNTAX ? $_$ : \'runtime\'') 21 | .root() 22 | 23 | .find('const base = $_$') 24 | .replace('external: [$$$]', 'external: [$$$, /@babel/]') 25 | .root() 26 | 27 | .replace('export default $_$', `export default process.env.POLYFILL_MODE === 'onlySyntax'\n ? ${onlySyntax}\n: $_$`) 28 | .root() 29 | 30 | .generate() 31 | 32 | const babelConfig = getFile(allFiles, 'babel.config.js') 33 | const plugins = `plugins: process.env.POLYFILL_MODE === 'onlySyntax' 34 | ? [] 35 | : [ 36 | [ 37 | '@babel/plugin-transform-runtime', 38 | { 39 | corejs: 3 40 | } 41 | ] 42 | ]` 43 | 44 | babelConfig.file = $(babelConfig.file) 45 | .find('presets: $_$') 46 | .after(plugins) 47 | .root() 48 | 49 | .generate() 50 | 51 | const packageItem = getFile(allFiles, 'package.json') 52 | const parsedPackage = JSON.parse(packageItem.file) 53 | 54 | if (babel === 'runtime') { 55 | parsedPackage.dependencies['@babel/runtime-corejs3'] = '^7.0.0' 56 | } else { 57 | parsedPackage.devDependencies['@babel/runtime-corejs3'] = '^7.0.0' 58 | parsedPackage.peerDependencies['@babel/runtime-corejs3'] = '^7.0.0' 59 | } 60 | 61 | parsedPackage.devDependencies['@babel/plugin-transform-runtime'] = '^7.14.5' 62 | parsedPackage 63 | .scripts['build:onlySytnax'] = 'cross-env POLYFILL_MODE=onlySyntax rollup -c build/rollup.config.js' 64 | parsedPackage.scripts.build += ' && npm run build:onlySytnax' 65 | 66 | packageItem.file = JSON.stringify(parsedPackage) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | function getExt (ext, { typescript }) { 5 | if (ext === '.ts' && !typescript) { 6 | return '.js' 7 | } 8 | return ext 9 | } 10 | 11 | function readGitignore (pathValue) { 12 | const gitignorePath = path.resolve(pathValue, '.gitignore') 13 | if (fs.existsSync(gitignorePath)) { 14 | const gitignore = fs.readFileSync(gitignorePath, { encoding: 'utf-8' }) 15 | return [...gitignore.split('\n'), 'package-lock.json'] 16 | } 17 | return [] 18 | } 19 | 20 | function readFiles (pathValue, gitignore) { 21 | const parsedPath = path.parse(pathValue) 22 | const isDirectory = fs.statSync(pathValue).isDirectory() 23 | if (isDirectory) { 24 | return { 25 | ...parsedPath, 26 | path: pathValue, 27 | file: fs.readdirSync(pathValue) 28 | .filter(item => { 29 | return !gitignore.includes(item) 30 | }) 31 | .map(item => { 32 | return readFiles(path.resolve(pathValue, item), gitignore) 33 | }), 34 | isDirectory: true 35 | } 36 | } 37 | return { 38 | ...parsedPath, 39 | path: pathValue, 40 | file: fs.readFileSync(pathValue, { encoding: 'utf-8' }) 41 | } 42 | } 43 | 44 | function writeFiles ({ currentFiles, pathValue, answers }) { 45 | currentFiles.forEach(({ 46 | isDirectory, isDelete, file, name, ext 47 | }) => { 48 | const filePathValue = path.resolve(pathValue, name) 49 | if (isDirectory) { 50 | if (fs.existsSync(filePathValue)) { 51 | console.log(`${filePathValue} has exists!,\nPlease remove the dir first`) 52 | } else { 53 | fs.mkdirSync(filePathValue) 54 | writeFiles({ currentFiles: file, pathValue: filePathValue, answers }) 55 | } 56 | } else if (!isDelete) { 57 | fs.writeFileSync( 58 | filePathValue + getExt(ext, answers), 59 | file 60 | ) 61 | } 62 | }) 63 | } 64 | 65 | function getFile (files, name) { 66 | let result 67 | files.forEach(item => { 68 | if (result) { 69 | return 70 | } 71 | const { file, isDirectory, ext } = item 72 | if (isDirectory) { 73 | result = getFile(file, name) 74 | } else if (item.name + ext === name) { 75 | result = item 76 | } 77 | }) 78 | return result 79 | } 80 | 81 | module.exports = { 82 | readGitignore, 83 | readFiles, 84 | writeFiles, 85 | getFile 86 | } 87 | -------------------------------------------------------------------------------- /template/build/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import { babel } from '@rollup/plugin-babel' // https://github.com/rollup/plugins/tree/master/packages/babel#readme 3 | import { nodeResolve } from '@rollup/plugin-node-resolve' // https://github.com/rollup/plugins/tree/master/packages/node-resolve 4 | import commonjs from '@rollup/plugin-commonjs' // https://github.com/rollup/plugins/tree/master/packages/commonjs 5 | import typescript from '@rollup/plugin-typescript' // https://github.com/rollup/plugins/tree/master/packages/typescript 6 | 7 | import { name, dependencies, peerDependencies } from '../package.json' 8 | 9 | const FORMAT = { 10 | ES: 'es', 11 | CJS: 'cjs', 12 | UMD: 'umd' 13 | } 14 | 15 | const extensions = ['.js', '.ts'] 16 | 17 | const base = { 18 | input: resolve(__dirname, '../src/index.ts'), 19 | external: [...Object.keys({ 20 | ...dependencies, 21 | ...peerDependencies 22 | })] // https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency 23 | } 24 | const output = format => { 25 | return { 26 | name, 27 | dir: resolve(__dirname, `../dist/${format}`), 28 | format, 29 | globals: {} // peerDependencies 专用,如果没有无需设置 30 | } 31 | } 32 | 33 | const typescriptOptions = { 34 | tsconfig: resolve(__dirname, '../tsconfig.rollup.json'), 35 | declaration: true, 36 | declarationDir: resolve(__dirname, '../dist/es/types') 37 | } 38 | 39 | const plugins = format => { 40 | return [ 41 | nodeResolve({ extensions }), 42 | commonjs(), 43 | babel({ extensions, exclude: ['node_modules/**'], babelHelpers: 'bundled' }), 44 | typescript(format === FORMAT.ES ? typescriptOptions : {}) 45 | ] 46 | } 47 | 48 | // 开发模式 49 | const devServer = [ 50 | { 51 | ...base, 52 | output: output(FORMAT.ES), 53 | plugins: [ 54 | commonjs({}), 55 | typescript(typescriptOptions) 56 | ] 57 | } 58 | ] 59 | const developmentMode = process.env.ROLLUP_ENV === 'development' 60 | 61 | const result = developmentMode 62 | ? devServer 63 | : [ 64 | { 65 | ...base, 66 | output: output(FORMAT.ES), 67 | plugins: plugins(FORMAT.ES) 68 | }, 69 | { 70 | ...base, 71 | output: output(FORMAT.CJS), 72 | plugins: plugins(FORMAT.CJS) 73 | }, 74 | { 75 | ...base, 76 | output: output(FORMAT.UMD), 77 | plugins: plugins(FORMAT.UMD), 78 | external: [...Object.keys({ ...peerDependencies })] 79 | } 80 | ] 81 | 82 | export default result 83 | -------------------------------------------------------------------------------- /constants/index.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | 3 | const TEMPLATE_NAME = { 4 | FAST_START: 'fast-start', 5 | CUSTOM: 'custom' 6 | } 7 | 8 | const QUESTIONS = [ 9 | { 10 | type: 'input', 11 | name: 'name', 12 | message: 'What\'s your SDK name?', 13 | default: 'my-sdk', 14 | validate (data) { 15 | if (data.toLowerCase() === data) { 16 | return true 17 | } 18 | return 'Name cannot contain uppercase' 19 | } 20 | }, 21 | { 22 | type: 'list', 23 | name: 'template', 24 | message: 'Select a template', 25 | default: TEMPLATE_NAME.FAST_START, 26 | choices: [TEMPLATE_NAME.FAST_START, TEMPLATE_NAME.CUSTOM] 27 | }, 28 | { 29 | type: 'list', 30 | name: 'babel', 31 | message: 'How to convert API to backward-compatible versions with Babel', 32 | default: 'ignore', 33 | choices: [ 34 | new inquirer.Separator('API: Promise、[].includes ...'), 35 | new inquirer.Separator('Syntax: {...obj}, ()=>{}, const ...'), 36 | new inquirer.Separator('Either way, it will transform Syntax'), 37 | { 38 | name: 'ignore (Consumer has converted API with @babel/polyfill)', 39 | value: 'ignore' 40 | }, 41 | { 42 | name: 'dependencies @babel/runtime (Consumers didn\'t convert API)', 43 | value: 'runtime' 44 | }, 45 | { 46 | name: 'peerDependencies @babel/runtime (Consumer has converted API with @babel/runtime)', 47 | value: 'peer runtime' 48 | } 49 | ], 50 | when (answers) { 51 | return answers.template === TEMPLATE_NAME.CUSTOM 52 | } 53 | }, 54 | { 55 | type: 'confirm', 56 | name: 'typescript', 57 | message: 'Use Typescript', 58 | default: true 59 | }, 60 | { 61 | type: 'list', 62 | name: 'framework', 63 | message: 'Choice a framework (Look out for vue and react)', 64 | default: 'none', 65 | choices: ['none'], 66 | when (answers) { 67 | return answers.template === TEMPLATE_NAME.CUSTOM 68 | } 69 | }, 70 | { 71 | type: 'confirm', 72 | name: 'eslint', 73 | message: 'Use EsLint', 74 | default: false, 75 | when (answers) { 76 | return answers.template === TEMPLATE_NAME.CUSTOM 77 | } 78 | }, 79 | { 80 | type: 'confirm', 81 | name: 'husky', 82 | message: 'Use Husky', 83 | default: false, 84 | when (answers) { 85 | return answers.template === TEMPLATE_NAME.CUSTOM && 86 | answers.eslint 87 | } 88 | } 89 | ] 90 | 91 | module.exports = { 92 | TEMPLATE_NAME, 93 | QUESTIONS 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @draftbook/cli - SDK 脚手架 2 | 3 | ![license](https://img.shields.io/npm/l/@draftbook/cli?style=for-the-badge) 4 | 5 | Command Line Interface for SDK 6 | 7 | ## Installation 8 | 9 | ```javascript 10 | $ npm install -g @draftbook/cli 11 | ``` 12 | 13 | ## Usage 14 | 15 | 在当前目录下创建一个基于 rollup 配置的项目。代码中预置了常用基础配置和最佳实践,适用于 sdk 开发 16 | 17 | ```bash 18 | draftbook add 19 | ``` 20 | 21 | ## Command 22 | 23 | 目前仅提供新建 sdk 命令 24 | 25 | ```bash 26 | Usage: draftbook [options] [command] 27 | 28 | Options: 29 | -v, --version output the current version 30 | -h, --help display help for command 31 | 32 | Commands: 33 | add create a new sdk in current dir 34 | help [command] display help for command 35 | ``` 36 | 37 | ## Detail 38 | 39 | 创建新 SDK 时,选项说明 40 | 41 | ### 1. What's your SDK name? (my-sdk) 42 | sdk 名称,采用小写字母 + `'-'` 的形式 43 | 44 | ### 2. Select a template (Use arrow keys) 45 | - fast-start 46 | - custom 47 | 48 | 选择预设模板 49 | - fast-start:常用的 sdk 初始化配置,使用 @babel/runtime 处理 api,引入 eslint、husky。可以自行选择是否使用 typescript 50 | - custom:自定义后续规则 51 | 52 | ### 3. How to convert API to backward-compatible versions with Babel 53 | > API: Promise、[].includes ... 54 | > 55 | > Syntax: {...obj}, ()=>{}, const ... 56 | > 57 | > Either way, it will transform Syntax 58 | 59 | - ignore (Consumer has converted API with @babel/polyfill) 60 | - dependencies @babel/runtime (Consumers didn't convert API) 61 | - peerDependencies @babel/runtime (Consumer has converted API with @babel/runtime) 62 | 63 | 如何使用 Babel 将 API 转换为向后兼容的版本。首先要知道的是,兼容问题分为两部分 64 | 65 | - API:可以在代码运行时被兼容的,例如 Promise、[].includes 等 66 | - Syntax:运行时不能被兼容的,例如箭头函数、const 等 67 | 68 | 无论使用哪种配置,sdk 一定会处理 Syntax。而对于 api 69 | - 如果能确认调用方已经处理过了,可以选择不处理,减小 sdk 体积 70 | - 如果无法确认调用方会不会处理,可以选择用 @babel/runtime 处理 71 | - 如果确认调用方是通过 @babel/runtime 来处理的,可以把该依赖设置为同级依赖,一样可以减小 sdk 体积 72 | 73 | 后两种方式 build 时会额外生成一个 `dist/es-only-converted-syntax` 文件, 不做 api 处理,可以特殊使用,减小最终的代码体积 74 | 75 | ### 4. Use Typescript (Y/n) 76 | 是否使用 TypeScript(推荐使用) 77 | 78 | ### 5. Choice a framework (Look out for vue and react) (Use arrow keys) 79 | - none 80 | 81 | 选择 SDK 框架。目前只支持 js,后续增加 vue、react 82 | 83 | ### 6. Use EsLint (y/N) 84 | 是否需要配置 Eslint,用于代风格校验 85 | 86 | ### 7. Use Husky (y/N) 87 | 是否需要配置 Husky,用于 git commit 前的代码校验 88 | 89 | ## Script 90 | 91 | 新生成的代码预置了如下 script。规范流程的同时提高开发效率 92 | 93 | ### 1. build 94 | 95 | 打包代码 96 | 97 | ### 2. dev 98 | 99 | 开发模式。修改源码保存后,实时生成 dist 文件 100 | 101 | ### 3. prepatch 102 | 103 | 打包并发布测试版代码。例如当前代码版本是 `1.0.1` 104 | 105 | 执行代码之后 106 | 107 | ```bash 108 | npm run prepatch 109 | ``` 110 | 111 | 会自动打包、修改代码版本到:`1.0.2-alpha.0`、然后发布代码 112 | 113 | 在 alpha 的基础上执行命令,版本会变成:`1.0.2-alpha.1` 114 | 115 | 调用方可以通过下面的命令安装测试版本代码 116 | 117 | ```bash 118 | npm install [package name]@alpha 119 | ``` 120 | 121 | ### 4. patch 122 | 123 | 打包发布正式版本代码。例如当前代码版本是 `1.0.2-alpha.1` 124 | 125 | 执行代码之后 126 | 127 | ```bash 128 | npm run patch 129 | ``` 130 | 131 | 会自动测试、测试通过会打包、修改代码版本到:`1.0.2`、然后发布代码 132 | 133 | 在正式版基础上执行命令,版本会变成:`1.0.3` 134 | 135 | 调用方可以通过下面的命令安装正式版本代码 136 | 137 | ```bash 138 | npm install [package name] 139 | ``` 140 | 141 | ### 5. test 142 | 执行 jest 测试 143 | 144 | 145 | ## Author 146 | 147 | > qqqqqcy@gmail.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /template/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------