12 Commits

Author SHA1 Message Date
95e60bc04e nork 3.0.4 2022-02-02 16:01:34 +01:00
100eb59f17 nork works 2022-02-01 19:11:54 +01:00
9c03dbef15 nork works 2022-02-01 19:11:48 +01:00
00033fd439 npm fixes 2022-01-13 11:43:47 +01:00
c988ef64c4 Rewrited to TypeScript, better file structure 2022-01-12 23:15:24 +01:00
5eafee03b2 Remove ignored files 2021-12-01 18:12:11 +01:00
f887fc026a cleaning 2021-12-01 18:08:52 +01:00
2f437f9696 gitignore updated 2021-12-01 18:06:43 +01:00
1ebafbef49 rewriting to oop and typescript 2021-12-01 18:05:44 +01:00
67d6029699 version updated 2021-11-09 15:16:12 +01:00
49e34b5f73 bug fix 2021-11-09 15:15:54 +01:00
36758cbadb To be honest, I do not quite remember everything I changed here today. But it is all good, I tell ya. 2021-11-03 17:35:24 +01:00
59 changed files with 1052 additions and 418 deletions

39
.eslintrc.json Normal file
View File

@@ -0,0 +1,39 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single",
{
"allowTemplateLiterals": true
}
],
"semi": [
"error",
"never"
]
}
}

6
.gitignore vendored
View File

@@ -104,4 +104,8 @@ dist
.tern-port .tern-port
# package lock file # package lock file
package-lock.json package-lock.json
_old/
undefined.ts
undefined.ejs

4
.mocharc.json Normal file
View File

@@ -0,0 +1,4 @@
{
"require": ["ts-node/register"],
"watch-files": ["./src/**/*.ts"]
}

0
.npmignore Normal file
View File

1
norkconfig.json Normal file
View File

@@ -0,0 +1 @@
{"lang":"ts"}

View File

@@ -1,32 +1,58 @@
{ {
"name": "nork", "name": "nork",
"version": "1.1.6", "version": "3.0.4",
"description": "The best node.js 'framework' :)", "description": "The best node.js 'framework' :)",
"main": "src/app.js", "main": "dist/app.js",
"bin": "src/app.js", "bin": "dist/app.js",
"scripts": { "types": "dist/app.d.ts",
"start": "node src/app.js" "scripts": {
}, "start": "npm run start:prod",
"keywords": [ "start:dev": "ts-node src/app.ts",
"node", "start:prod": "node dist/app.js",
"framework", "tsc": "tsc -p .",
"express", "clean": "rimraf dist",
"mvc" "copy-assets": "ts-node src/utils/copyAssets",
], "build": "npm-run-all clean tsc copy-assets",
"author": "Filip Rojek", "test": "mocha --config .mocharc.json --watch src/**/*.test.ts",
"license": "MIT", "prepublish": "npm-run-all build"
"dependencies": { },
"colors": "^1.4.0", "keywords": [
"fs-extra": "^10.0.0", "node",
"inquirer": "^8.1.2", "framework",
"pad": "^3.2.0" "express",
}, "mvc"
"repository": { ],
"type": "git", "author": "Filip Rojek",
"url": "git+https://github.com/filiprojek/nork.git" "license": "MIT",
}, "dependencies": {
"bugs": { "colors":"1.4.0",
"url": "https://github.com/filiprojek/nork/issues" "fs-extra": "^10.0.0",
}, "inquirer": "^8.1.2",
"homepage": "https://github.com/filiprojek/nork/blob/master/README.md" "pad": "^3.2.0"
},
"devDependencies": {
"@types/chai": "^4.2.22",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.1.3",
"@types/mocha": "^9.0.0",
"@types/shelljs": "^0.8.11",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"chai": "^4.3.4",
"eslint": "^8.3.0",
"mocha": "^9.1.3",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"shelljs": "^0.8.5",
"ts-node": "^10.4.0",
"typescript": "^4.5.2"
},
"repository": {
"type": "git",
"url": "git+https://github.com/filiprojek/nork.git"
},
"bugs": {
"url": "https://github.com/filiprojek/nork/issues"
},
"homepage": "https://github.com/filiprojek/nork/blob/master/README.md"
} }

24
progress-blog.md Normal file
View File

@@ -0,0 +1,24 @@
/* spell-checker: disable */
# Todo:
- [ ] auth jwt refresh token based system
- https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
- [ ] version of nork control
- [ ] upgrade to newer version system
### 11-24-2021
- zacal jsem predelavat nork do OOP
- co jsem udelal:
- [x] --help
- [x] --version
- [x] setup
- [x] make
- [x] create
- delam na tom
- je treba dopsat par types a fixnout zbytek erroru
- zatim netestovana funkcnost
### 1-10.2022
- dodelal jsem create a otestoval ho
- [x] create

View File

@@ -1,224 +0,0 @@
#!/usr/bin/env node
const inquirer = require('inquirer')
const path = require('path')
const fs = require('fs-extra')
const colors = require('colors')
const pad = require('pad')
const langToLanguage = lang => {
switch (lang) {
case 'js':
return 'Javascript'
case 'ts':
return 'Typescript'
default:
return 'Unknown language'
}
}
const logError = errorMsg => {
console.log(colors.bgYellow.red(errorMsg))
return
}
const logSuccess = (msg = false) => {
if (!msg) {
msg = 'Success!'
}
console.log(colors.cyan(msg))
return
}
const logHelp = (specific = false, command = false) => {
let spc = 27
if (specific) {
// log specific help
if (specific == 'make') {
console.log(`Usage: ${specific}:[component]`)
console.log()
console.log(pad(' make controller [name]', spc), 'create a new controller')
console.log(pad(' make middleware [name]', spc), 'create a new middleware')
console.log(pad(' make model [name]', spc), 'create a new model')
console.log(pad(' make route [name]', spc), 'create a new route')
console.log(pad(' make test [name]', spc), 'create a new test')
console.log(pad(' make view [name]', spc), 'create a new view')
return
}
console.log(`Usage: ${specific} [options]`)
return
}
console.log('Usage: nork <command> [options]')
console.log()
console.log('Options:')
console.log(pad(' -v, --version', spc), 'output the version number')
console.log(pad(' -h, --help', spc), 'output usage information')
console.log()
console.log('Commands:')
console.log(pad(' create [app-name]', spc), 'create a new project')
console.log(pad(' create [app-name] -i', spc), 'create a new project in current directory')
console.log(pad(' make controller [name]', spc), 'create a new controller')
console.log(pad(' make middleware [name]', spc), 'create a new middleware')
console.log(pad(' make model [name]', spc), 'create a new model')
console.log(pad(' make route [name]', spc), 'create a new route')
console.log(pad(' make test [name]', spc), 'create a new test')
console.log(pad(' make view [name]', spc), 'create a new view')
console.log(pad(' setup', spc), 'set up an existing project for nork')
console.log()
console.log(' Run', colors.cyan('nork help <command>'), 'for detailed usage of given command.')
if (command) {
console.log(colors.red('Unknown command'), colors.bold.blue(command))
}
return
}
;(async () => {
if (process.argv[2] == 'create') {
// get info about new project
let projectPath
const data = {}
const questions = [
{
type: 'input',
name: 'project_name',
message: 'Enter project name:',
},
{
type: 'list',
message: "Pick the technology you're using:",
name: 'lang',
choices: [
{ name: 'Typescript', value: 'ts' },
{ name: 'Javascript', value: 'js' },
],
},
{
type: 'input',
name: 'author',
message: 'Enter your name:',
},
]
// remove first question if project name is already known
if (process.argv[3]) {
questions.shift()
}
let answers = await inquirer.prompt(questions)
data.project_name = answers.project_name ? answers.project_name : process.argv[3]
data.lang = answers.lang
data.author = answers.author
// copy skeleton to new project
process.argv.includes('-i') ? projectPath = process.cwd() : projectPath = path.join(process.cwd(), data.project_name)
fs.copySync(path.join(__dirname, './skeletons/express-' + data.lang), projectPath)
// edit package.json file
const pkgJson = require(path.join(projectPath, 'package.json'))
pkgJson.name = data.project_name
pkgJson.author = data.author
fs.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(pkgJson, null, 2), err => {
if (err) return logError(err)
})
console.log(colors.yellow('Project settings'))
console.log(colors.yellow('------------------'))
console.log(pad(colors.gray('Project name: '), 30), data.project_name)
console.log(pad(colors.gray('Author: '), 30), data.author)
console.log(pad(colors.gray('Language: '), 30), langToLanguage(data.lang))
return logSuccess(`Project ${data.project_name} created successfully!`)
}
if (process.argv[2] == 'make') {
if (!process.argv[3] || !process.argv[4]) return logHelp('make')
const component = process.argv[3]
const norkcfg = require(path.join(process.cwd(), 'norkconfig.json'))
let tsComponents = ['controller', 'middleware', 'route']
if (tsComponents.includes(component)) {
let src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.' + norkcfg.lang)
let dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.' + norkcfg.lang)
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err) {
return logError(err.message)
}
return logSuccess()
}
if (component == 'model') {
let src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.js')
let dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.js')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err) {
return logError(err.message)
}
return logSuccess()
}
if (component == 'view') {
let src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.ejs')
let dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.ejs')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err) {
return logError(err.message)
}
return logSuccess()
}
if (component == 'test') {
let src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.js')
let dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.test.js')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err) {
return logError(err.message)
}
return logSuccess()
}
}
if (process.argv[2] == 'setup') {
const questions = {
type: 'list',
message: "Pick the technology you're using:",
name: 'lang',
choices: [
{ name: 'Typescript', value: 'ts' },
{ name: 'Javascript', value: 'js' },
],
}
let answers = await inquirer.prompt(questions)
fs.writeJsonSync(path.join(process.cwd(), './norkconfig.json'), answers)
return logSuccess()
}
if (process.argv[2] == '-v' || process.argv[2] == '--version') {
const pkgJson = require(path.join(__dirname, '../package.json'))
return console.log('nork', pkgJson.version)
}
if (process.argv[2] == 'help' || process.argv[2] == '-h' || process.argv[2] == '--help') {
if (process.argv[3]) {
return logHelp(process.argv[3])
}
return logHelp()
}
process.argv.splice(0, 2)
logHelp(false, process.argv.join(' '))
})()

10
src/app.ts Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env node
import Routes from './routes'
export class App {
constructor() {
Routes.router()
}
}
new App()

69
src/create.ts Normal file
View File

@@ -0,0 +1,69 @@
import pad from 'pad'
import colors from 'colors'
import fs from 'fs-extra'
import path from 'path'
import global from './global'
import { Create as CreateInterface } from './interfaces/CreateInterface'
import inquirer from 'inquirer'
export default class Create {
static async project(projectName: string | boolean = false) {
// get info about new project
let projectPath
const questions = [
{
type: 'input',
name: 'project_name',
message: 'Enter project name:',
},
{
type: 'list',
message: `Pick the technology you're using:`,
name: 'lang',
choices: [
{ name: 'Typescript', value: 'ts' },
{ name: 'Javascript', value: 'js' },
],
},
{
type: 'input',
name: 'author',
message: 'Enter your name:',
},
]
// remove first question if project name is already known
if (projectName) {
questions.shift()
}
const answers = await inquirer.prompt(questions)
const data: CreateInterface = {
project_name: answers.project_name ? answers.project_name : process.argv[3],
lang: answers.lang,
author: answers.author,
}
// copy skeleton to new project
process.argv.includes('-i') ? (projectPath = process.cwd()) : (projectPath = path.join(process.cwd(), data.project_name))
fs.copySync(path.join(__dirname, './skeletons/express-' + data.lang), projectPath)
// edit package.json file
const pkgJson = fs.readJsonSync(path.join(projectPath, 'package.json'))
// const pkgJson = require(path.join(projectPath, 'package.json'))
pkgJson.name = data.project_name
pkgJson.author = data.author
fs.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(pkgJson, null, 2), err => {
if (err) return global.logError(err.message)
})
console.log(colors.yellow('Project settings'))
console.log(colors.yellow('------------------'))
console.log(pad(colors.gray('Project name: '), 30), data.project_name)
console.log(pad(colors.gray('Author: '), 30), data.author)
console.log(pad(colors.gray('Language: '), 30), global.langToLanguage(String(data.lang)))
return global.logSuccess(`Project ${data.project_name} created successfully!`)
}
}

27
src/global.ts Normal file
View File

@@ -0,0 +1,27 @@
import colors from 'colors'
export default class Global {
static logSuccess = (msg: boolean | string = false): string => {
if (!msg) {
msg = 'Success!'
}
console.log(colors.cyan(String(msg)))
return String(msg)
}
static logError(errorMsg: string) {
console.log(colors.bgYellow.red(errorMsg))
return
}
static langToLanguage(lang: string): string {
switch (lang) {
case 'js':
return 'Javascript'
case 'ts':
return 'Typescript'
default:
return 'Unknown language'
}
}
}

66
src/help.ts Normal file
View File

@@ -0,0 +1,66 @@
import pad from 'pad'
import colors from 'colors'
export default class Help {
constructor() {
// this.logHelp()
}
// log help & returns status in string for tests
static makeHelp(): string {
const spc = 27
return `
Usage: make:[component]
${pad(' make controller [name]', spc)} create a new controller
${pad(' make middleware [name]', spc)} create a new middleware
${pad(' make model [name]', spc)} create a new model
${pad(' make route [name]', spc)} create a new route
${pad(' make test [name]', spc)} create a new test
${pad(' make view [name]', spc)} create a new view
`
}
static allHelp(): string {
const spc = 27
return `
Usage: nork <command> [options]
Options:
${pad(' -v, --version', spc)} output the version number
${pad(' -h, --help', spc)} output usage information
Commands:
${pad(' create [app-name]', spc)} create a new project
${pad(' create [app-name] -i', spc)} create a new project in current directory
${pad(' make controller [name]', spc)} create a new controller
${pad(' make interface [name]', spc)} create a new interface
${pad(' make middleware [name]', spc)} create a new middleware
${pad(' make model [name]', spc)} create a new model
${pad(' make route [name]', spc)} create a new route
${pad(' make service [name]', spc)} create a new service
${pad(' make test [name]', spc)} create a new test
${pad(' make view [name]', spc)} create a new view
${pad(' setup', spc)} set up an existing project for nork
Run ${colors.cyan('nork help <command>')} for detailed usage of given command.
`
}
static specificHelp(specific: string): string {
return `Usage: ${specific} [options]`
}
static logHelp = (specific: boolean | string = false): string => {
if (specific) {
// log specific help
if (specific == 'make') {
return this.makeHelp()
}
// else return specific help
return this.specificHelp(String(specific))
}
// if nothing return help all
return this.allHelp()
}
}

View File

@@ -0,0 +1,5 @@
export interface Create {
project_name: string
author: string
lang: string
}

View File

@@ -0,0 +1,3 @@
export interface Norkcfg {
lang: string
}

View File

@@ -0,0 +1,15 @@
export interface Questions {
type: string
message: string
name: string
choices: [
{
name: string
value: string
},
{
name: string
value: string
},
]
}

4
src/interfaces/test.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface interfaceName {
hello: string
world: boolean | null | undefined
}

View File

@@ -1,10 +1,6 @@
import { Request, Response } from 'express' import { Request, Response } from 'express'
const root_get = (req: Request, res: Response) => { export const root_get = (req: Request, res: Response) => {
res.render('home') res.render('home')
return true return true
} }
module.exports = {
root_get,
}

View File

@@ -0,0 +1,4 @@
export interface interfaceName {
hello: string
world: boolean | null | undefined
}

View File

@@ -1,10 +1,8 @@
import { Router, Request, Response, NextFunction } from 'express' import { Router, Request, Response, NextFunction } from 'express'
const router = Router() export const router = Router()
router.use((req: Request, res: Response, next: NextFunction) => { router.use((req: Request, res: Response, next: NextFunction) => {
console.log('Hi :)') console.log('Hi :)')
next() next()
}) })
module.exports = router

View File

@@ -1,17 +0,0 @@
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const modelSchema = new Schema(
{
title: {
type: String,
required: true,
},
},
{
timestamps: true,
},
)
const ModelName = mongoose.model('ModelName', modelSchema)
module.exports = ModelName

View File

@@ -0,0 +1,15 @@
import { Schema, model } from 'mongoose'
const modelSchema = new Schema<any>(
{
title: {
type: String,
required: true,
},
},
{
timestamps: true,
},
)
export default model('ModelName', modelSchema)

View File

@@ -1,8 +1,6 @@
import { Router } from 'express' import { Router } from 'express'
const rootController = require('../controllers/rootController') import * as rootController from '@/controllers/rootController'
const router = Router() export const router = Router()
router.get('/', rootController.root_get) router.get('/', rootController.root_get)
module.exports = router

View File

@@ -0,0 +1,3 @@
export function helloWorld() {
console.log('hello world')
}

View File

@@ -1,9 +0,0 @@
const { getReq, getRes } = require('./modules/reqRes.module.js')
const { root_get } = require('../controllers/rootController.ts')
test('Home page render test', () => {
const req = getReq()
const res = getRes()
expect(root_get(req, res)).toBe(true)
})

76
src/make.ts Normal file
View File

@@ -0,0 +1,76 @@
import path from 'path'
import fs from 'fs-extra'
import global from './global'
import { Norkcfg } from './interfaces/GlobalInterface'
export default class Make {
static component(component: string) {
// const norkcfg = require(path.join(process.cwd(), 'norkconfig.json'))
const norkcfg: Norkcfg = fs.readJsonSync(path.join(process.cwd(), 'norkconfig.json'))
const tsComponents = ['controller', 'middleware', 'route', 'service']
if (tsComponents.includes(component)) {
const src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.' + norkcfg.lang)
const dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.' + norkcfg.lang)
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err: any) {
return global.logError(err.message)
}
return global.logSuccess()
}
if (component == 'model') {
const src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.' + norkcfg.lang)
const dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.' + norkcfg.lang)
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err: any) {
return global.logError(err.message)
}
return global.logSuccess()
}
if (component == 'view') {
const src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.ejs')
const dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.ejs')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err: any) {
return global.logError(err.message)
}
return global.logSuccess()
}
if (component == 'test') {
const src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.js')
const dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.test.js')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err: any) {
return global.logError(err.message)
}
return global.logSuccess()
}
if (component == 'interface') {
if (norkcfg.lang != 'ts') {
return global.logError('error - this is typescript only component')
}
const src = path.join(__dirname, './make-files/express-' + norkcfg.lang + '/' + component + '.ts')
const dest = path.join(process.cwd(), './src/' + component + 's' + '/' + process.argv[4] + '.ts')
try {
fs.copySync(src, dest, { overwrite: false, errorOnExist: true })
} catch (err: any) {
return global.logError(err.message)
}
return global.logSuccess()
}
return global.logError(`error - unknown component ${component}`)
}
}

49
src/routes.ts Normal file
View File

@@ -0,0 +1,49 @@
import Help from './help'
import Version from './version'
import Setup from './setup'
import Make from './make'
import Create from './create'
export default class Routes {
static router(): string {
if (process.argv[2] == 'help' || process.argv[2] == '-h' || process.argv[2] == '--help') {
if (process.argv[3]) {
console.log(Help.logHelp(process.argv[3]))
return 'specific help'
}
console.log(Help.logHelp())
return 'all help'
}
if (process.argv[2] == '-v' || process.argv[2] == '--version') {
console.log(Version.show())
return 'version'
}
if (process.argv[2] == 'setup') {
if (process.argv[3] != 'test') {
Setup.setup()
}
return 'setup'
}
if (process.argv[2] == 'make') {
if (process.argv[3]) {
if (process.argv[4] != 'test') {
Make.component(process.argv[3])
}
return `make ${process.argv[3]}`
}
}
if (process.argv[2] == 'create') {
if (process.argv[4] != 'test') {
Create.project(process.argv[3])
}
return `create ${process.argv[3]}`
}
console.log(Help.logHelp())
return 'all help'
}
}

24
src/setup.ts Normal file
View File

@@ -0,0 +1,24 @@
import inquirer from 'inquirer'
import fs from 'fs-extra'
import path from 'path'
import Global from './global'
import { Questions } from './interfaces/SetupInterface'
export default class Setup {
static async setup(test = false) {
if (!test) {
const questions: Questions = {
type: 'list',
message: `Pick the technology you're using:`,
name: 'lang',
choices: [
{ name: 'Typescript', value: 'ts' },
{ name: 'Javascript', value: 'js' },
],
}
const answers = await inquirer.prompt(Object(questions))
fs.writeJsonSync(path.join(process.cwd(), './norkconfig.json'), answers)
}
return Global.logSuccess()
}
}

View File

@@ -0,0 +1,20 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxBracketSameLine": true,
"jsxSingleQuote": true,
"printWidth": 200,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": false,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "all",
"useTabs": true,
"vueIndentScriptAndStyle": true,
"parser": "typescript"
}

View File

@@ -1,3 +1,4 @@
{ {
"lang": "ts" "lang": "ts",
"db": ""
} }

View File

@@ -1,45 +1,69 @@
{ {
"name": "project-name", "name": "project-name",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node dist/app.js", "start": "node -r tsconfig-paths/register -r ts-node/register dist/server.js",
"dev": "nodemon src/app.ts", "start:dev": "npx nodemon src/server.ts",
"test": "jest", "dev": "nodemon src/server.ts",
"clean": "rimraf dist/*", "test": "jest",
"copy-assets": "ts-node src/utils/copyAssets", "clean": "rimraf dist/*",
"tsc": "tsc -p .", "copy-assets": "ts-node src/utils/copyAssets",
"build": "npm-run-all clean tsc copy-assets" "tsc": "tsc -p .",
}, "build": "npm-run-all clean tsc copy-assets"
"keywords": [], },
"author": "", "keywords": [],
"license": "ISC", "author": "",
"dependencies": { "license": "ISC",
"cors": "^2.8.5", "dependencies": {
"dotenv": "^8.2.0", "colors": "1.4.0",
"ejs": "^3.1.6", "cookie-parser": "^1.4.5",
"express": "^4.17.1", "cors": "^2.8.5",
"mongoose": "^5.12.3", "dotenv": "^8.2.0",
"morgan": "^1.10.0", "ejs": "^3.1.6",
"fs-extra": "^10.0.0" "express": "^4.17.1",
}, "express-validator": "^6.14.0",
"devDependencies": { "fs-extra": "^10.0.0",
"@types/cors": "^2.8.10", "jsonwebtoken": "^8.5.1",
"@types/ejs": "^3.0.6", "mongoose": "^5.12.3",
"@types/express": "^4.17.11", "morgan": "^1.10.0",
"@types/fs-extra": "^9.0.12", "pg": "^8.7.1",
"@types/jest": "^27.0.1", "pg-hstore": "^2.3.4",
"@types/mongoose": "^5.10.5", "sequelize": "^6.15.0"
"@types/morgan": "^1.9.2", },
"@types/node": "^14.14.41", "devDependencies": {
"@types/shelljs": "^0.8.9", "@types/cookie-parser": "^1.4.2",
"jest": "^27.0.6", "@types/cors": "^2.8.10",
"npm-run-all": "^4.1.5", "@types/ejs": "^3.0.6",
"rimraf": "^3.0.2", "@types/express": "^4.17.11",
"shelljs": "^0.8.4", "@types/fs-extra": "^9.0.12",
"ts-jest": "^27.0.5", "@types/jest": "^27.0.1",
"ts-node": "^9.1.1", "@types/jsonwebtoken": "^8.5.8",
"typescript": "^4.2.4" "@types/mongoose": "^5.10.5",
} "@types/morgan": "^1.9.2",
"@types/node": "^14.14.41",
"@types/shelljs": "^0.8.9",
"jest": "^27.0.6",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"shelljs": "^0.8.4",
"ts-jest": "^27.0.5",
"ts-node": "^9.1.1",
"tsconfig-paths": "^3.11.0",
"typescript": "^4.2.4"
},
"nodemonConfig": {
"ignore": [
"**/*.test.ts",
"**/*.spec.ts",
".git",
"node_modules"
],
"watch": [
"src"
],
"exec": "node -r tsconfig-paths/register -r ts-node/register ./src/server.ts",
"ext": "ts, js"
}
} }

View File

@@ -1,2 +1,17 @@
APP_PORT = 8080 # General
APP_PORT = 6060
APP_HOSTNAME = 'localhost'
APP_HOST = 'http://localhost:8080' # frontend url
CORS_WHITELIST = http://172.15.46.21:8080;http://192.168.0.1:8080
JWT_SECRET = ''
# MongoDB
DB_URI = 'mongodb://username:password@localhost:27017/database?authSource=admin' DB_URI = 'mongodb://username:password@localhost:27017/database?authSource=admin'
# PostgreSQL
DB_PORT = 5432
DB_HOST = '127.0.0.1'
DB_USERNAME = ''
DB_PASSWORD = ''
DB_DATABASE = ''

View File

@@ -1,43 +1,36 @@
import express from 'express' import express from 'express'
import morgan from 'morgan' import morgan from 'morgan'
import mongoose from 'mongoose'
import path from 'path' import path from 'path'
import cors from 'cors' import cors from 'cors'
import cookieParser from 'cookie-parser'
import { router as routes } from '@/routes'
import { router as middlewares } from '@/middlewares'
const config = require('./utils/environment') const corsWhitelist = ['http://localhost:8080', 'http://localhost:6060']
const routes = require('./routes') const corsOptions = {
const middlewares = require('./middlewares') origin: function (origin: any, callback: any) {
if (!origin || corsWhitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
},
optionsSuccessStatus: 200,
credentials: true,
}
const port: Number = config.APP_PORT || 8080 export const app = express()
const app = express()
// MongoDB
const dbURI: string = config.DB_URI
mongoose
.connect(dbURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(result => {
console.log('connected to db')
app.listen(port, () => {
console.log(`Server is listening on http://localhost:${port}`)
})
})
.catch(err => {
console.log(err)
})
// Middlewares // Middlewares
app.use(middlewares) app.use(middlewares)
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, 'views')) app.set('views', path.join(__dirname, 'views'))
app.use(cors()) app.use(cors(corsOptions))
app.use(morgan('dev')) app.use(morgan('dev'))
app.use(express.urlencoded({ extended: true })) app.use(express.urlencoded({ extended: true }))
app.use(express.json()) app.use(express.json())
app.use(express.static(path.join(__dirname, 'public'))) app.use(express.static(path.join(__dirname, 'public')))
app.use(cookieParser())
// Routes // Routes
app.use(routes) app.use(routes)

View File

@@ -0,0 +1,49 @@
import mongoose from 'mongoose'
import config from '@/utils/environment'
import { Err, Succ } from '@/services/globalService'
import db from '@/config/postgres.config'
// MongoDB
const dbURI: string = config.DB_URI
function connect() {
if (!config.NORK.db) {
new Err(500, 'no database is in norkcfg.json')
return false
}
if (config.NORK.db == 'mongodb') {
mongoose
.connect(dbURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => {
new Succ(200, 'connected to db')
return true
})
.catch((err: any) => {
new Err(500, err)
return false
})
}
if (config.NORK.db == 'postgresql') {
db.sync()
.then(() => {
new Succ(200, 'connected to db')
return true
})
.catch((err: any) => {
new Err(500, `Can't connect to db\n${err}`)
return false
})
}
if (config.NORK.db.length > 0) {
new Err(500, `unsupported database ${config.NORK.db}`)
return false
}
}
export default connect

View File

@@ -0,0 +1,10 @@
import { Sequelize } from 'sequelize'
import config from '@/utils/environment'
const db = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_PASSWORD, {
host: config.DB_HOST,
dialect: 'postgres',
logging: false,
})
export default db

View File

@@ -1,10 +1,6 @@
import { Request, Response } from 'express' import { Request, Response } from 'express'
const root_get = (req: Request, res: Response) => { export const root_get = (req: Request, res: Response) => {
res.render('home') res.render('home')
return true return true
} }
module.exports = {
root_get,
}

View File

@@ -0,0 +1,4 @@
export interface ErrType {
code: number
message: string
}

View File

@@ -0,0 +1,32 @@
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'
import env from '@/utils/environment'
import { Err, Succ } from '@/services/globalService'
// import User from '@/models/User' // uncomment this
export const requireAuth = (req: Request, res: Response, next: NextFunction) => {
const token = req.cookies.jwt
new Err(500, 'uncomment code in authMiddleware before using!')
/* if (token) {
jwt.verify(token, env.JWT_SECRET, async (err: any, decodedToken: any) => {
if (err) {
// console.error(err.message)
res.status(401).send(new Err(401, 'user is not authenticated'))
}
if (!err) {
const user = await User.findByPk(decodedToken.id)
if (user === null) {
res.status(401).send(new Err(401, 'user is not authenticated'))
return
}
res.locals.user = user
new Succ(100, 'user is authenticated')
next()
}
})
}
if (!token) {
res.status(401).send(new Err(401, 'user is not authenticated'))
} */
}

View File

@@ -0,0 +1,13 @@
import { Request, Response, NextFunction } from 'express'
import { validationResult } from 'express-validator'
class Middleware {
handleValidationError(req: Request, res: Response, next: NextFunction) {
const error = validationResult(req)
if (!error.isEmpty()) {
return res.status(400).json(error.array()[0])
}
next()
}
}
export default new Middleware()

View File

@@ -1,8 +1,6 @@
import { Router } from 'express' import { Router } from 'express'
const sayHiMiddleware = require('./sayHiMiddleware') import { router as sayHiMiddleware } from '@/middlewares/sayHiMiddleware'
const router = Router() export const router = Router()
router.use(sayHiMiddleware) router.use(sayHiMiddleware)
module.exports = router

View File

@@ -1,11 +1,9 @@
import { Router, Request, Response, NextFunction } from 'express' import { Router, Request, Response, NextFunction } from 'express'
const router = Router() export const router = Router()
router.use((req: Request, res: Response, next: NextFunction) => { router.use((req: Request, res: Response, next: NextFunction) => {
console.log('Hi :)') console.log('Hi :)')
next() next()
}) })
module.exports = router

View File

@@ -1,13 +1,11 @@
import { Router } from 'express' import { Request, Response, Router } from 'express'
const rootRoutes = require('./rootRoutes') import { router as rootRoutes } from './rootRoutes'
const router = Router() export const router = Router()
router.use(rootRoutes) router.use(rootRoutes)
// 404 // 404
router.use((req, res) => { router.use((req: Request, res: Response) => {
res.status(404).send('E404') res.status(404).send('E404')
}) })
module.exports = router

View File

@@ -1,8 +1,9 @@
import { Router } from 'express' import { Router } from 'express'
const rootController = require('../controllers/rootController') import * as rootController from '@/controllers/rootController'
import rootValidator from '@/validators/rootValidator'
import handleValidation from '@/middlewares/handleValidation'
const router = Router() export const router = Router()
const mws = [handleValidation.handleValidationError]
router.get('/', rootController.root_get) router.get('/', rootValidator.checkRootGet(), mws, rootController.root_get)
module.exports = router

View File

@@ -0,0 +1,24 @@
import http from 'http'
import { app } from '@/app'
import config from '@/utils/environment'
import { Succ } from '@/services/globalService'
import database from '@/config/database'
const port: number = config.APP_PORT || 8080
const hostname: string = config.APP_HOSTNAME || 'localhost'
const server = http.createServer(app)
// Server
export function runServer(): void {
server.listen(port, hostname, () => {
new Succ(200, `Server is listening on http://localhost:${port}`)
})
}
if (!config.NORK.db) {
runServer()
} else {
const db_connection = database()
if (db_connection) {
runServer()
}
}

View File

@@ -0,0 +1,40 @@
import colors from 'colors'
import { ErrType } from '@/interfaces/globalInterface'
export class Err implements ErrType {
code: number
message: string
constructor(code: number, message: string) {
this.code = code
this.message = message
this.drop()
}
drop() {
console.log(colors.bgRed(`${this.code}`) + colors.bgBlack.red(` ${this.message}`))
return {
code: this.code,
message: this.message,
}
}
}
export class Succ {
code: number
message: string
constructor(code: number, message: string) {
this.code = code
this.message = message
this.drop()
}
drop() {
console.log(colors.bgGreen.black(`${this.code}`) + colors.green.bgBlack(` ${this.message}`))
return {
code: this.code,
message: this.message,
}
}
}

View File

@@ -0,0 +1,3 @@
export const helloWorld = () => {
console.log('hello world')
}

View File

@@ -1,9 +0,0 @@
const { getReq, getRes } = require('./modules/reqRes.module.js')
const { root_get } = require('../controllers/rootController.ts')
test('Home page render test', () => {
const req = getReq()
const res = getRes()
expect(root_get(req, res)).toBe(true)
})

View File

@@ -1,16 +0,0 @@
module.exports.getReq = () => {
const req = {}
req.body = {}
return req
}
module.exports.getRes = () => {
const res = {}
res.locals = {}
res.status = () => res
res.json = () => res
res.send = () => res
res.render = () => res
return res
}

View File

@@ -3,4 +3,5 @@ import * as shell from 'shelljs'
// Copy all the view templates // Copy all the view templates
shell.cp('-R', 'src/views', 'dist/') shell.cp('-R', 'src/views', 'dist/')
shell.cp('-R', 'src/public', 'dist/') shell.cp('-R', 'src/public', 'dist/')
shell.cp('-R', 'src/models', 'dist/')
shell.cp('-u', 'src/.env', 'dist/') shell.cp('-u', 'src/.env', 'dist/')

View File

@@ -1,7 +1,40 @@
import path from 'path' import path from 'path'
require('dotenv').config({ path: path.join(__dirname, '../.env') }) import fs from 'fs-extra'
import { Err } from '@/services/globalService'
import dotenv from 'dotenv'
module.exports = { dotenv.config({ path: path.join(__dirname, '../.env') })
APP_PORT: process.env.APP_PORT, const norkcfg = fs.readJSONSync(path.join(__dirname, '../../norkconfig.json'))
DB_URI: process.env.DB_URI,
if (norkcfg.db) {
if (norkcfg.db == 'postgresql') {
if (!process.env.DB_PORT) {
process.env.DB_PORT = '5432'
}
if (!process.env.DB_HOST) {
process.env.DB_HOST = '127.0.0.1'
}
if (!process.env.DB_USERNAME || !process.env.DB_PASSWORD || !process.env.DB_DATABASE) {
new Err(500, 'missing DB parameters in .env file')
process.exit(1)
}
}
}
export default {
// General
APP_PORT: Number(process.env.APP_PORT),
APP_HOST: String(process.env.APP_HOST),
APP_HOSTNAME: process.env.APP_HOSTNAME !== undefined ? String(process.env.APP_HOSTNAME) : null,
CORS_WHITELIST: String(process.env.CORS_WHITELIST),
JWT_SECRET: String(process.env.JWT_SECRET),
// MongoDB
DB_URI: String(process.env.DB_URI),
// PostgreSQL
DB_PORT: Number(process.env.DB_PORT),
DB_HOST: String(process.env.DB_HOST),
DB_USERNAME: String(process.env.DB_USERNAME),
DB_PASSWORD: String(process.env.DB_PASSWORD),
DB_DATABASE: String(process.env.DB_DATABASE),
NORK: norkcfg,
} }

View File

@@ -0,0 +1,9 @@
import { body, param, query } from 'express-validator'
class rootValidator {
checkRootGet() {
return []
}
}
export default new rootValidator()

View File

@@ -14,7 +14,7 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */ // "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */ "outDir": "./dist", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */ // "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
@@ -44,8 +44,22 @@
/* Module Resolution Options */ /* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ "paths": {
"@/*": ["src/","src/*"],
"@dist/*": ["dist/*"],
"@controllers/*": ["src/controllers/"],
"@interfaces/*": ["src/interfaces/*"],
"@middlewares/*": ["src/middlewares/*"],
"@models/*": ["src/models/*"],
"@public/*": ["src/public/*"],
"@routes/*": ["src/routes/*"],
"@services/*": ["src/services/*"],
"@test/*": ["src/test/*"],
"@utils/*": ["src/utils/*"],
"@validators/*": ["src/validators/*"],
"@views/*": ["src/views/*"],
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */ // "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */ // "types": [], /* Type declaration files to be included in compilation. */

23
src/tests/help.test.ts Normal file
View File

@@ -0,0 +1,23 @@
import { assert } from 'chai'
import Help from '../help'
import pad from 'pad'
describe('should return help', () => {
it('returns all help', () => {
const help = Help.logHelp()
const correct = Help.allHelp()
assert.equal(help, correct)
})
it('returns make help', () => {
const help = Help.logHelp('make')
const correct = Help.makeHelp()
assert.equal(help, correct)
})
it('returns specific help', () => {
const help = Help.logHelp('setup')
const correct = Help.specificHelp('setup')
assert.equal(help, correct)
})
})

68
src/tests/router.test.ts Normal file
View File

@@ -0,0 +1,68 @@
import { assert } from 'chai'
import Routes from '../routes'
describe('should return help', () => {
process.argv = []
it('return all help', () => {
const options = ['-h', '--help']
for (let i = 0; i < options.length; i++) {
process.argv[2] = options[i]
process.argv[3] = ''
const routes = Routes.router()
assert.equal(routes, 'all help')
}
})
it('return specific help', () => {
const options = ['-h', '--help']
for (let i = 0; i < options.length; i++) {
process.argv[2] = options[i]
process.argv[3] = 'make'
const routes = Routes.router()
assert.equal(routes, 'specific help')
}
})
})
describe('should return version', () => {
it('return version', () => {
const options = ['-v', '--version']
for (let i = 0; i < options.length; i++) {
process.argv[2] = options[i]
const routes = Routes.router()
assert.equal(routes, 'version')
}
})
})
describe('should return setup', () => {
it('return setup', () => {
process.argv[2] = 'setup'
process.argv[3] = 'test'
const routes = Routes.router()
assert.equal(routes, 'setup')
})
})
describe('should return make', () => {
const options = ['controller', 'middleware', 'route', 'service', 'model', 'view', 'test', 'interface']
for (let i = 0; i < options.length; i++) {
it(`return make ${options[i]}`, () => {
process.argv[2] = 'make'
process.argv[3] = options[i]
process.argv[4] = 'test'
const routes = Routes.router()
assert.equal(routes, `make ${options[i]}`)
})
}
})
describe('should return create', () => {
it('return create testProject', () => {
process.argv[2] = 'create'
process.argv[3] = 'testProject'
process.argv[4] = 'test'
const routes = Routes.router()
assert.equal(routes, 'create testProject')
})
})

11
src/tests/setup.test.ts Normal file
View File

@@ -0,0 +1,11 @@
import { assert } from 'chai'
import Setup from '../setup'
import Global from '../global'
describe('should setup project', () => {
it('setup project', async () => {
const correct: string = Global.logSuccess()
const setup = await Setup.setup(true)
assert.equal(setup, correct)
})
})

View File

@@ -0,0 +1,20 @@
import { assert } from 'chai'
import { App } from '../app'
// Describe tests
describe('some demo tests', () => {
// Create tests
it('adds two number together', () => {
assert(2 + 3 === 5)
})
it('should return Hello plus my name', () => {
assert.equal(App.sayHello('Filip'), 'Hello Filip')
assert.notEqual(App.sayHello('Adam'), 'Hello Filip')
})
it('should return Hello plus my name within instance', () => {
const app = new App('Filip')
assert.equal(app.pozdrav(), 'Hello Filip')
})
})

11
src/tests/version.test.ts Normal file
View File

@@ -0,0 +1,11 @@
import { assert } from 'chai'
import Version from '../version'
import path from 'path'
describe('should return version', () => {
it('return version', () => {
const pkgJson = require(path.join(__dirname, '../../package'))
const actualVersion = pkgJson.version
assert.equal(Version.show(), `nork ${actualVersion}`)
})
})

7
src/utils/copyAssets.ts Normal file
View File

@@ -0,0 +1,7 @@
import * as shell from 'shelljs'
shell.cp('-R', 'src/skeletons', 'dist/')
shell.cp('-R', 'src/interfaces', 'dist/')
shell.cp('-R', 'src/make-files', 'dist/')
shell.chmod('+x', 'dist/app.js')

10
src/version.ts Normal file
View File

@@ -0,0 +1,10 @@
import fs from 'fs-extra'
import path from 'path'
export default class Version {
static show(): string {
const pkgJson = fs.readJsonSync(path.join(__dirname, '../package.json'))
const log = `nork ${pkgJson.version}`
return log
}
}

23
tsconfig.json Normal file
View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"rootDir": "./src", /* Specify the root folder within your source files. */
"strict": true, /* Enable all strict type-checking options. */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"declaration": true,
},
"exclude": [
"_old",
"node_modules",
"src/make-files",
"src/skeletons",
"src/tests",
"src/interfaces",
"src/utils"
]
}