diff --git a/.vscode/settings.json b/.vscode/settings.json index ff00ade5..fe491347 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ "search.exclude": { "app/styles": false }, - "search.usePCRE2": true + "search.usePCRE2": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" } diff --git a/cli/.tsignore b/cli/.tsignore new file mode 100644 index 00000000..e69de29b diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 00000000..62437ceb --- /dev/null +++ b/cli/README.md @@ -0,0 +1,52 @@ +# Getting Started + +- Ensure you are under the `./cli` folder from the root of the project +- Run the cli tool with `npm run generate` from either the root of the project or the cli folder + +## Goals + +1. Enable developers to get up and running with the project quickly + a. The cli should include prompts for obtaining all the environment variables required by the project +2. Enable developers to easily deploy their generated project to a free hosting platform such as [Heroku](https://www.heroku.com/) or [Zeit](https://zeit.co/) +3. Enable developers to easily deploy to a paid service of their choice + a. The goal is to support [Google Cloud Platform](https://cloud.google.com), [Amazon Web Services](https://aws.amazon.com) and [Digital Ocean](https://digitalocean.com) +4. Build in such a way that the cli can be the base for a web interface with the purpose of enabling non-technical people to configure and deploy their own version of the app - (Pipe Dream) + +## Desired Features + +1. Configure logo by pointing to local file +2. Configure colors by pointing to a `.csv` or manually entering + a. Should support `rgb`, `rgba`, `hex` and `hsl` formats + b. Should support pointing to a local `.csv` file - (Would be nice to support `.tsv` as well) + c. We support up to three themes, default, dark and light + +3. Configure API Key for the DBP platform +4. Specify versions that are available/permissioned - (Needs to have a sensible default, preferably to a public domain resource) +5. Configure deployment options +6. Configure any remaining environment variables - (things such as social media and such) + +## Development Steps + +1. Extract project into a \_source folder that can be used by the cli +2. Implement way of prompting users and receiving feedback +3. Use feedback to perform actions on the \_source files in line with achieving project goals +4. Provide feedback to the user if the action they took was successful or not +5. Create a way for the user to progress past or recover from a failure +6. Research tools for auto-deployments to desired services + a. Make these prompts optional and easy to skip +7. Launch the newly configured project in local browser for user to view their work + +## Tech to Use + +### Languages/Frameworks + +1. Typescript +2. NodeJS + +### Services + +1. Google Cloud Platform +2. Amazon Web Services +3. Digital Ocean +4. Heroku +5. Zeit diff --git a/cli/jest.config.js b/cli/jest.config.js new file mode 100644 index 00000000..bb4f240e --- /dev/null +++ b/cli/jest.config.js @@ -0,0 +1,13 @@ +module.exports = { + globals: { + 'ts-jest': { + tsConfig: 'tsconfig.json', + }, + }, + moduleFileExtensions: ['ts', 'js'], + transform: { + '^.+\\.(ts|tsx)$': 'ts-jest', + }, + testMatch: ['**/test/**/*.test.(ts|js)'], + testEnvironment: 'node', +}; diff --git a/cli/package-lock.json b/cli/package-lock.json new file mode 100644 index 00000000..d3da92bf --- /dev/null +++ b/cli/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": "dbp-reader-cli", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "12.7.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz", + "integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "enquirer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.2.tgz", + "integrity": "sha512-PLhTMPUXlnaIv9D3Cq3/Zr1xb7soeDDgunobyCmYLUG19n24dvC8i+ZZgm2DekGpDnx7JvFSHV7lxfM58PMtbA==", + "requires": { + "ansi-colors": "^3.2.1" + } + }, + "typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==" + } + } +} diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 00000000..741a2791 --- /dev/null +++ b/cli/package.json @@ -0,0 +1,33 @@ +{ + "name": "dbp-reader-cli", + "version": "1.0.0", + "description": "A command line interface for generating webapps based on the dbp-reader codebase", + "main": "build/index.js", + "scripts": { + "test": "jest --forceExit --coverage --verbose", + "watch-test": "npm run test -- --watchAll", + "start": "nodemon ./build/index.js", + "build": "tsc", + "watch": "tsc -w" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/digitalbiblesociety/dbp-reader.git" + }, + "keywords": [ + "cli", + "bible", + "webapp" + ], + "author": "Jesse Hill", + "license": "MIT", + "bugs": { + "url": "https://github.com/digitalbiblesociety/dbp-reader/issues" + }, + "homepage": "https://github.com/digitalbiblesociety/dbp-reader#readme", + "dependencies": { + "@types/node": "^12.7.12", + "enquirer": "^2.3.2", + "typescript": "^3.6.3" + } +} diff --git a/cli/prettierrc.json b/cli/prettierrc.json new file mode 100644 index 00000000..bd750a0a --- /dev/null +++ b/cli/prettierrc.json @@ -0,0 +1,9 @@ +{ + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "arrowParens": "always", + "useTabs": true, + "write": true +} diff --git a/cli/src/enums.ts b/cli/src/enums.ts new file mode 100644 index 00000000..41f3f7d5 --- /dev/null +++ b/cli/src/enums.ts @@ -0,0 +1,5 @@ +export enum EThemeTypes { + light = "light", + default = "default", + dark = "dark" +} diff --git a/cli/src/index.ts b/cli/src/index.ts new file mode 100644 index 00000000..10fdea4b --- /dev/null +++ b/cli/src/index.ts @@ -0,0 +1,103 @@ +import fs from "fs"; +import utils from "util"; +import * as interfaces from "./interfaces"; +import * as enums from "./enums"; +import { prompts } from "./prompts"; + +const promisify = utils.promisify; +const readFilePromise = promisify(fs.readFile); + +const sleep = (ms: number = 500) => + new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, ms); + }); + +// const fs = require("fs"); +// const { prompt } = require("enquirer"); + +const setTheme = async (theme: enums.EThemeTypes) => { + try { + } catch (error) { + console.error("Error setting the theme", error); + const { yes } = await prompts.confirm( + "Do you want to try adding the theme again?" + ); + + if (yes) { + setTheme(theme); + } + } +}; + +const getThemeType = async ( + existingThemes: enums.EThemeTypes[] +): Promise => { + const { type } = await prompts.prompt({ + type: "select", + name: "type", + message: "What theme are you adding?", + choices: [ + enums.EThemeTypes.default, + enums.EThemeTypes.light, + enums.EThemeTypes.dark + ] + }); + console.log("type", type); + + if (existingThemes.includes(type)) { + // You can't have more than one theme of the same type, do you want to replace your existing theme for this type? + // Statement, + // Confirm? + const { yes } = await prompts.confirm( + `Do you really want to replace your existing ${type} theme?` + ); + + if (yes) { + return type; + } else { + throw new Error("Tried to set a theme that already existed"); + } + } + + return type; +}; + +const app = async () => { + console.log("App is running now!"); + const { yes: hasMultipleThemes } = await prompts.confirm( + "Do you have multiple themes? (If not only the default theme will be used)" + ); + const setThemes: interfaces.ISetThemes = []; + let currentThemeToSet = enums.EThemeTypes.default; + + if (hasMultipleThemes) { + try { + for (let count = 0; count < 3; count++) { + currentThemeToSet = await getThemeType(setThemes); + if (currentThemeToSet) { + setThemes.push(currentThemeToSet); + } + + await setTheme(currentThemeToSet); + } + } catch (err) { + console.error(err); + await sleep(300); + console.log("Continuing program"); + await sleep(300); + } + } + // Do you have a dark and light theme? + const { value: logoPath } = await prompts.input( + "Where is your logo? (Please provide a complete path)" + ); + // if yes then collect information for dark and light + console.log("logoPath", logoPath); + // Take logo path and copy that file to where the logo needs to be +}; + +app(); + +module.exports = app; diff --git a/cli/src/interfaces.ts b/cli/src/interfaces.ts new file mode 100644 index 00000000..4cd16bd5 --- /dev/null +++ b/cli/src/interfaces.ts @@ -0,0 +1,5 @@ +import * as enums from "./enums"; + +export interface ISetThemes extends Array { + [index: number]: enums.EThemeTypes; +} diff --git a/cli/src/prompts.ts b/cli/src/prompts.ts new file mode 100644 index 00000000..e60c3b14 --- /dev/null +++ b/cli/src/prompts.ts @@ -0,0 +1,33 @@ +import { prompt } from "enquirer"; + +const confirm = (message: string): Promise<{ yes: boolean }> => + prompt({ + type: "confirm", + name: "yes", + message + }); + +const input = (message: string): Promise<{ value: string }> => + prompt({ + type: "input", + name: "value", + message + }); + +const select = ( + message: string, + choices: string[] +): Promise<{ choice: string }> => + prompt({ + type: "select", + name: "choice", + message, + choices + }); + +export const prompts = { + prompt, + confirm, + input, + select +}; diff --git a/cli/tsconfig.json b/cli/tsconfig.json new file mode 100644 index 00000000..8af249d9 --- /dev/null +++ b/cli/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "lib": ["es2015"], + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "build" + }, + "include": ["./src/**/*"] +}