UNPKG

express-21c

Version:

Express Module' application generator

264 lines (226 loc) 8.57 kB
#!/usr/bin/env node /** * ES6+ module grammer create code * author callor@callor.com */ import fs from 'fs' import parseArgs from 'minimist' import path from 'path' import { defaultDependency, sequelizeOption, cssOptions, viewOptions } from '../config/package_options.js' import sortedObject from 'sorted-object' import helpMessage from '../modules/help.js' import { VERSION, confirm, mkdir, fileWrite, finish, loadTemplate, copyTemplate, copyTemplateMulti } from '../modules/public_module.js' const MODE_0755 = parseInt('0755', 8) // CLI const unknown = [] const args = parseArgs(process.argv.slice(2), { alias: { c: 'css', e: 'ejs', p: 'pug', f: 'force', s: 'sequelize', h: 'help', v: 'view' }, boolean: ['ejs', 'pug', 'hbs', 'hogan', 'force', 'sequelize', 'git', 'help', 'version'], default: { css: true, view: true }, string: ['css', 'view'], unknown: function (s) { if (s.charAt(0) === '-') { unknown.push(s) } }, }) args['!'] = unknown const consoleMessage = (type, message) => { console.error() message.split('\n').forEach(function (line) { console.error(` ${type}: %s`, line) }) console.error() } // const warning = (message) => { // console.error(); // message.split("\n").forEach(function (line) { // console.error(" warning: %s", line); // }); // console.error(); // }; const createAppName = (pathName) => { return path .basename(pathName) .replace(/[^A-Za-z0-9.-]+/g, '-') .replace(/^[-_.]+|-+$/g, '') .toLowerCase() } const createApplication = (appArgs) => { const { appName, dir, options } = appArgs if (dir !== '.') { mkdir(dir, '.') } // project directory create mkdir(dir, 'bin') mkdir(dir, 'routes') mkdir(dir, 'views') mkdir(dir, 'public') mkdir(dir, 'public/js') mkdir(dir, 'public/images') mkdir(dir, 'public/css') // Package const packages = { name: appName, version: '0.0.0', private: true, type: 'module', scripts: { start: 'node ./bin/www.js', }, dependencies: {}, } // app.js.ejs file open const app = loadTemplate('ejs/app.js') const www = loadTemplate('ejs/www.js') // middleWare import and setting app.locals.importModulesList = {} app.locals.middleWareList = [] app.locals.sequelizeModuesList = {} // Request logger app.locals.importModulesList.logger = 'morgan' app.locals.middleWareList.push("logger('dev')") // Body parsers app.locals.middleWareList.push('express.json()') app.locals.middleWareList.push('express.urlencoded({ extended: false })') // Cookie parser app.locals.importModulesList.cookieParser = 'cookie-parser' app.locals.middleWareList.push('cookieParser()') // basic dependency add for (const dep of Object.keys(defaultDependency)) { packages.dependencies[dep] = defaultDependency[dep] } app.locals.middleWareList.push('express.static(path.join("public"))') // sample Router Setting app.locals.routerModules = {} // routes import list app.locals.routerMounts = [] // router use setting list // Index router mount app.locals.routerModules.indexRouter = '../routes/index.js' app.locals.routerMounts.push({ path: '/', module: 'indexRouter' }) // User router mount app.locals.routerModules.usersRouter = '../routes/users.js' app.locals.routerMounts.push({ path: '/users', module: 'usersRouter' }) app.locals.view = { engine: options.view } packages.dependencies[options.view] = viewOptions[options.view] www.locals.appName = appName // mysql sequelize enable if (options.sequelize) { app.locals.sequelizeModuesList.DB = '../models/index.js' packages.dependencies.sequelize = sequelizeOption.sequelize packages.dependencies.mysql2 = sequelizeOption.mysql2 mkdir(dir, 'models') mkdir(dir, 'config') copyTemplateMulti('models', dir + '/models', '*.js') copyTemplateMulti('models/config', dir + '/config', '*.js') } // package.json create packages.dependencies = sortedObject(packages.dependencies) // www.js, app.js write fileWrite(path.join(dir, 'bin/app.js'), app.render()) fileWrite(path.join(dir, 'bin/www.js'), www.render(), MODE_0755) fileWrite(path.join(dir, 'package.json'), JSON.stringify(packages, null, 2) + '\n') // router copy copyTemplateMulti('routes', dir + '/routes', '*.js') // view copy if (options.view) { copyTemplateMulti('views', dir + '/views', `*.${options.view}`) } else copyTemplate('views/index.html', path.join(dir, 'public/index.html')) copyTemplateMulti('javascript', dir + '/public/js', '*.js') copyTemplateMulti('images', dir + '/public/images', '*.*') // css templage copy if (options.css === true) { copyTemplateMulti('css', dir + '/public/css', '*.css') } else if (typeof options.css === 'string') { copyTemplateMulti('css', dir + '/public/css', `*.${options.css}`) } copyTemplateMulti('', dir + '/', 'README.md') // CSS Engine support switch (options.css) { case 'compass': app.locals.importModulesList.compass = 'node-compass' app.locals.middleWareList.push("compass({ mode: 'expanded' })") packages.dependencies['node-compass'] = cssOptions.nodeCompass break case 'less': app.locals.importModulesList.lessMiddleware = 'less-middleware' app.locals.middleWareList.push("lessMiddleware(path.join('public'))") packages.dependencies['less-middleware'] = cssOptions.lessMiddleware break case 'sass': app.locals.importModulesList.sassMiddleware = 'node-sass-middleware' app.locals.middleWareList.push("sassMiddleware({\n src: path.join('public'),\n dest: path.join('public'),\n indentedSyntax: true, // true = .sass and false = .scss\n sourceMap: true\n})") packages.dependencies['node-sass-middleware'] = cssOptions.nodeSassMmiddleware break case 'stylus': app.locals.importModulesList.stylus = 'stylus' app.locals.middleWareList.push("stylus.middleware(path.join('public'))") packages.dependencies.stylus = cssOptions.stylus break } finish(dir, appName, app) } const isEmptyDir = (dir, cb) => { let fileContents try { fileContents = fs.readdirSync(dir) } catch (err) { if (err && err.code !== 'ENOENT') throw err } return !fileContents || !fileContents.length } const main = async (options, done) => { // top-level argument direction if (options['!'].length > 0) { helpMessage() consoleMessage('error', `unknown option '${options['!'][0]}' `) } else if (options.help) { helpMessage() } else if (options.version) { console.log('version:', VERSION) } else if (!options.css) { helpMessage() consoleMessage('error', "option '-c, --css <engine>' argument missing") } else if (!options.view) { helpMessage() consoleMessage('error', "option '-v, --view <engine>' argument missing") } else { // Path const destinationPath = options._[0] || '.' // App name const appName = createAppName(path.resolve(destinationPath)) || 'hello-world' // --ejs, --pug, --hjs or--hbs option if (options.view === true) { options.view = options.pug && 'pug' options.view = options.view || (options.ejs && 'ejs') options.view = options.view || (options.hjs && 'hjs') options.view = options.view || (options.hbs && 'hbs') options.view = options.view || (options.hogan && 'hogan') // 선택된 view 가 없으면 options.view 가 false 가 되므로 // 기본값 설정을 위하여 true 로 다시 복귀한다 options.view = options.view || true } // 설정이 없으면 pug 를 기본 view 로 설정 if (options.view === true) { options.view = 'pug' consoleMessage('warning', `option '--${options.view}' has been renamed to '--view=${options.view}'`) } // 이미 있는 디렉토리인지 검사 const dirExists = isEmptyDir(destinationPath) const appArgs = { appName, dir: destinationPath, options, done, } if (dirExists || options.force) { createApplication(appArgs) } else { confirm('directory is not empty, continue? [y/N] ', (ok) => { ok || console.error('aborting') ok && createApplication(appArgs) }) } } } const exit = (arg) => {} main(args, exit)