init.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import { exec as shell_exec } from 'child_process';
  2. import fs from 'fs';
  3. import path from 'path';
  4. import util from 'util';
  5. const MAX_BUFFER = 10 * Math.pow(2, 20);
  6. export function exec(command: string, stdout?: ((...args) => void) | NodeJS.WriteStream, stderr?: ((...args) => void) | NodeJS.WriteStream) {
  7. return new Promise<string>((resolve, reject) => {
  8. try {
  9. let stdoutbuf = '';
  10. let stderrbuf = '';
  11. // EXEC CHILD PROCESS
  12. const p = shell_exec(command, { maxBuffer: MAX_BUFFER }, (err, out) => {
  13. if (err) return reject(err);
  14. if (stdoutbuf.length > 0 && typeof stdout === 'function') stdout(stdoutbuf);
  15. if (stderrbuf.length > 0 && typeof stderr === 'function') stderr(stderrbuf);
  16. resolve(out);
  17. });
  18. // PIPE STDOUT
  19. if (typeof stdout === 'function') {
  20. p.stdout.on('data', chunk => {
  21. stdoutbuf += chunk;
  22. let i = -1;
  23. while ((i = stdoutbuf.indexOf('\n')) >= 0) {
  24. const line = stdoutbuf.substring(0, i);
  25. stdoutbuf = stdoutbuf.substring(i + 1);
  26. if (typeof stdout === 'function') {
  27. stdout(line);
  28. }
  29. }
  30. });
  31. } else if (typeof stdout !== 'undefined') {
  32. p.stdout.pipe(stdout);
  33. }
  34. // PIPE STDERR
  35. if (typeof stderr === 'function') {
  36. p.stderr.on('data', chunk => {
  37. stderrbuf += chunk;
  38. let i = -1;
  39. while ((i = stderrbuf.indexOf('\n')) >= 0) {
  40. const line = stderrbuf.substring(0, i);
  41. stderrbuf = stderrbuf.substring(i + 1);
  42. if (typeof stderr === 'function') {
  43. stderr(line);
  44. }
  45. }
  46. });
  47. } else if (typeof stderr !== 'undefined') {
  48. p.stderr.pipe(stderr);
  49. }
  50. } catch (err) {
  51. reject(err);
  52. }
  53. });
  54. }
  55. const cli = {
  56. blue: v => `\x1b[34m${v}\x1b[0m`,
  57. green: v => `\x1b[32m${v}\x1b[0m`,
  58. yellow: v => `\x1b[33m${v}\x1b[0m`,
  59. red: v => `\x1b[31m${v}\x1b[0m`
  60. };
  61. async function readFile(path: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
  62. return new Promise<string>((resolve, reject) => {
  63. fs.readFile(path, { encoding }, (err, data) => {
  64. if (err) return reject(err);
  65. try {
  66. resolve(data.toString());
  67. } catch (e) {
  68. reject(e);
  69. }
  70. });
  71. });
  72. }
  73. async function readJsonFile<T>(path: string, encoding: BufferEncoding = 'utf8'): Promise<T> {
  74. return new Promise<T>(async (resolve, reject) => {
  75. try {
  76. resolve(JSON.parse(await readFile(path, encoding)) as T);
  77. } catch (e) {
  78. reject(e);
  79. }
  80. });
  81. }
  82. (async () => {
  83. try {
  84. const projectRootDir = path.resolve(process.cwd());
  85. if (!fs.existsSync(path.resolve(projectRootDir, 'package.json'))) {
  86. console.log(cli.blue('[INFO]'), 'Not an NPM project yet - initializing...');
  87. await exec('npm init -y', process.stdout, process.stderr);
  88. }
  89. const cloneDir = path.resolve(projectRootDir, '.clone');
  90. if (fs.existsSync(cloneDir)) {
  91. console.log(cli.blue('[WARNING]'), `There's an old .clone directory. Removing first...`);
  92. await util.promisify(fs.rm)(cloneDir, { force: true, recursive: true });
  93. }
  94. console.log(cli.blue('[INFO]'), 'Cloning boilerplate repository (via HTTPS)...');
  95. await exec(`git clone https://gogs.hostbbq.com/hostbbq/express-starter.git "${cloneDir}"`, process.stdout, process.stderr);
  96. let pkgJson = await readJsonFile<any>(path.resolve(cloneDir, 'package.json'));
  97. console.log(cli.blue('[INFO]'), 'Installing module dependencies...');
  98. let dependencies = Object.keys(pkgJson.dependencies).join('" "');
  99. await exec(`npm install "${dependencies}"`, process.stdout, process.stderr);
  100. dependencies = Object.keys(pkgJson.devDependencies).join('" "');
  101. await exec(`npm install --save-dev "${dependencies}"`, process.stdout, process.stderr);
  102. console.log(cli.blue('[INFO]'), 'Copying project files...');
  103. const copyFiles = ['.env', '.gitignore', '.prettierrc.js', 'tsconfig.json', '.vscode/settings.json', 'src', 'public', 'private'];
  104. for (const cpFile of copyFiles) {
  105. const sourceFile = path.resolve(cloneDir, cpFile);
  106. const targetFile = path.resolve(projectRootDir, cpFile);
  107. const targetDir = path.dirname(targetFile);
  108. if (!fs.existsSync(targetDir)) {
  109. await util.promisify(fs.mkdir)(targetDir, { recursive: true, mode: 0o777 });
  110. }
  111. const stat = await util.promisify(fs.stat)(sourceFile);
  112. if (stat.isFile()) {
  113. await util.promisify(fs.copyFile)(sourceFile, targetFile);
  114. } else {
  115. await exec(`cp -r "${sourceFile}" "${targetFile}"`);
  116. }
  117. }
  118. console.log(cli.blue('[INFO]'), 'Creating startup script');
  119. pkgJson = await readJsonFile<any>(path.resolve(projectRootDir, 'package.json'));
  120. pkgJson.scripts = {
  121. ...pkgJson.scripts,
  122. build: 'tsc -b',
  123. start: 'npm run build && node dist/index.js'
  124. };
  125. await util.promisify(fs.writeFile)(path.resolve(projectRootDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
  126. console.log(cli.blue('[INFO]'), 'Creating chat data directory');
  127. await util.promisify(fs.mkdir)(path.resolve(projectRootDir, 'chatdata'));
  128. console.log(cli.blue('[INFO]'), 'Cleanup: Removing .clone directory...');
  129. await util.promisify(fs.rm)(cloneDir, { force: true, recursive: true });
  130. console.log();
  131. console.log(cli.green('[SUCCESS] You can now start your express server with `npm start`'));
  132. process.exit(0);
  133. } catch (e) {
  134. console.error(e);
  135. process.exit(1);
  136. }
  137. })();