├── .commitlintrc.json ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── BUG.md │ ├── DOCUMENTATION.md │ ├── FEATURE_REQUEST.md │ ├── IMPROVEMENT.md │ └── QUESTION.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .markdownlint.json ├── .npmrc ├── .releaserc.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── jest.config.json ├── lighthouse_google-chrome.gif ├── package-lock.json ├── package.json ├── src ├── __test__ │ └── setup.ts ├── commands │ └── CreateFullstackAppCommand.ts ├── index.ts ├── services │ ├── Project.ts │ ├── Question.ts │ └── Template.ts └── utils │ ├── __test__ │ ├── checkFileExists.test.ts │ ├── copyDirectory.test.ts │ └── loading.test.ts │ ├── checkFileExists.ts │ ├── copyDirectory.ts │ ├── loading.ts │ ├── tryGitInit.ts │ └── validateNpmName.ts ├── templates ├── api │ ├── express │ │ ├── template.json │ │ └── template │ │ │ ├── .dockerignore │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── .husky │ │ │ ├── commit-msg │ │ │ └── pre-commit │ │ │ ├── entrypoint.sh │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── __test__ │ │ │ │ ├── app.test.ts │ │ │ │ ├── setEnvsVars.ts │ │ │ │ ├── setup.ts │ │ │ │ └── utils │ │ │ │ │ ├── __test__ │ │ │ │ │ └── formatErrors.test.ts │ │ │ │ │ └── formatErrors.ts │ │ │ ├── app.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ └── User.ts │ │ │ ├── services │ │ │ │ ├── __docs__ │ │ │ │ │ ├── components.yaml │ │ │ │ │ ├── errors-definitions.yaml │ │ │ │ │ └── utils.yaml │ │ │ │ ├── docs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── users │ │ │ │ │ ├── __docs__ │ │ │ │ │ └── _definitions.yaml │ │ │ │ │ ├── index.ts │ │ │ │ │ └── signup │ │ │ │ │ ├── __docs__ │ │ │ │ │ └── post.yaml │ │ │ │ │ ├── __test__ │ │ │ │ │ └── post.test.ts │ │ │ │ │ └── post.ts │ │ │ ├── tools │ │ │ │ ├── config │ │ │ │ │ ├── __test__ │ │ │ │ │ │ └── constants.test.ts │ │ │ │ │ ├── constants.ts │ │ │ │ │ └── swaggerSpec.ts │ │ │ │ ├── database │ │ │ │ │ ├── __test__ │ │ │ │ │ │ ├── paginateModel.test.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── PostTest.ts │ │ │ │ │ │ │ └── createPosts.ts │ │ │ │ │ ├── paginateModel.ts │ │ │ │ │ └── sequelize.ts │ │ │ │ ├── errors │ │ │ │ │ ├── BadRequestError.ts │ │ │ │ │ ├── CustomError.ts │ │ │ │ │ ├── ForbiddenError.ts │ │ │ │ │ ├── NotFoundError.ts │ │ │ │ │ ├── PayloadTooLargeError.ts │ │ │ │ │ ├── RequestValidationError.ts │ │ │ │ │ ├── TooManyRequestsError.ts │ │ │ │ │ ├── UnauthorizedError.ts │ │ │ │ │ └── __test__ │ │ │ │ │ │ ├── PayloadTooLargeError.test.ts │ │ │ │ │ │ └── TooManyRequestError.test.ts │ │ │ │ ├── middlewares │ │ │ │ │ ├── __test__ │ │ │ │ │ │ └── errorHandler.test.ts │ │ │ │ │ ├── errorHandler.ts │ │ │ │ │ └── validateRequest.ts │ │ │ │ ├── utils │ │ │ │ │ ├── __test__ │ │ │ │ │ │ ├── capitalize.test.ts │ │ │ │ │ │ ├── deleteObjectAttributes.test.ts │ │ │ │ │ │ └── parseIntOrDefaultValue.test.ts │ │ │ │ │ ├── capitalize.ts │ │ │ │ │ ├── deleteObjectAttributes.ts │ │ │ │ │ └── parseIntOrDefaultValue.ts │ │ │ │ └── validations │ │ │ │ │ ├── __test__ │ │ │ │ │ └── alreadyUsedValidation.test.ts │ │ │ │ │ └── alreadyUsedValidation.ts │ │ │ └── typings │ │ │ │ ├── global.d.ts │ │ │ │ └── utils.d.ts │ │ │ └── tsconfig.json │ ├── fastify-prisma │ │ ├── template.json │ │ └── template │ │ │ ├── .dockerignore │ │ │ ├── .env.example │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc.json │ │ │ ├── .gitignore │ │ │ ├── .husky │ │ │ ├── commit-msg │ │ │ └── pre-commit │ │ │ ├── .lintstagedrc.json │ │ │ ├── .prettierignore │ │ │ ├── .prettierrc.json │ │ │ ├── .vscode │ │ │ ├── extensions.json │ │ │ └── settings.json │ │ │ ├── Dockerfile │ │ │ ├── docker-compose.yml │ │ │ ├── jest.config.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── prisma │ │ │ ├── migrations │ │ │ │ ├── 20210513155815_init │ │ │ │ │ └── migration.sql │ │ │ │ └── migration_lock.toml │ │ │ └── schema.prisma │ │ │ ├── src │ │ │ ├── __test__ │ │ │ │ └── setup.ts │ │ │ ├── application.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── Post.ts │ │ │ │ └── utils.ts │ │ │ ├── services │ │ │ │ ├── hello │ │ │ │ │ ├── __test__ │ │ │ │ │ │ └── get.test.ts │ │ │ │ │ ├── get.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── posts │ │ │ │ │ ├── [postId] │ │ │ │ │ ├── __test__ │ │ │ │ │ │ ├── delete.test.ts │ │ │ │ │ │ └── put.test.ts │ │ │ │ │ ├── delete.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── put.ts │ │ │ │ │ ├── __test__ │ │ │ │ │ ├── get.test.ts │ │ │ │ │ └── post.test.ts │ │ │ │ │ ├── get.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── post.ts │ │ │ └── tools │ │ │ │ ├── config │ │ │ │ ├── index.ts │ │ │ │ └── swaggerOptions.ts │ │ │ │ ├── database │ │ │ │ ├── pagination.ts │ │ │ │ └── prisma.ts │ │ │ │ └── plugins │ │ │ │ ├── __test__ │ │ │ │ └── socket-io.test.ts │ │ │ │ └── socket-io.ts │ │ │ └── tsconfig.json │ ├── fastify │ │ ├── template.json │ │ └── template │ │ │ ├── .dockerignore │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── .husky │ │ │ ├── commit-msg │ │ │ └── pre-commit │ │ │ ├── entrypoint.sh │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── __test__ │ │ │ │ └── setup.ts │ │ │ ├── application.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ └── Post.ts │ │ │ ├── services │ │ │ │ ├── hello │ │ │ │ │ ├── __test__ │ │ │ │ │ │ └── get.test.ts │ │ │ │ │ ├── get.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── posts │ │ │ │ │ ├── [postId] │ │ │ │ │ ├── __test__ │ │ │ │ │ │ ├── delete.test.ts │ │ │ │ │ │ └── put.test.ts │ │ │ │ │ ├── delete.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── put.ts │ │ │ │ │ ├── __test__ │ │ │ │ │ ├── get.test.ts │ │ │ │ │ └── post.test.ts │ │ │ │ │ ├── get.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── post.ts │ │ │ ├── tools │ │ │ │ ├── config │ │ │ │ │ └── swaggerOptions.ts │ │ │ │ ├── database │ │ │ │ │ ├── __test__ │ │ │ │ │ │ ├── paginateModel.test.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── PostTest.ts │ │ │ │ │ │ │ └── createPosts.ts │ │ │ │ │ ├── paginateModel.ts │ │ │ │ │ └── sequelize.ts │ │ │ │ └── plugins │ │ │ │ │ ├── __test__ │ │ │ │ │ └── socket-io.test.ts │ │ │ │ │ └── socket-io.ts │ │ │ └── typings │ │ │ │ └── global.d.ts │ │ │ └── tsconfig.json │ ├── nest │ │ ├── template.json │ │ └── template │ │ │ ├── .dockerignore │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── entrypoint.sh │ │ │ ├── nest-cli.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── app.controller.spec.ts │ │ │ ├── app.controller.ts │ │ │ ├── app.module.ts │ │ │ ├── app.service.ts │ │ │ ├── main.ts │ │ │ └── posts │ │ │ │ ├── posts.controller.ts │ │ │ │ ├── posts.entity.ts │ │ │ │ ├── posts.module.ts │ │ │ │ └── posts.service.ts │ │ │ ├── test │ │ │ ├── app.e2e-spec.ts │ │ │ └── jest-e2e.json │ │ │ ├── tsconfig.build.json │ │ │ └── tsconfig.json │ └── strapi │ │ ├── template.json │ │ └── template │ │ ├── .dockerignore │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── api │ │ └── .gitkeep │ │ ├── config │ │ ├── database.js │ │ ├── functions │ │ │ ├── bootstrap.js │ │ │ ├── cron.js │ │ │ └── responses │ │ │ │ └── 404.js │ │ └── server.js │ │ ├── entrypoint.sh │ │ ├── extensions │ │ ├── .gitkeep │ │ └── users-permissions │ │ │ └── config │ │ │ └── jwt.js │ │ ├── favicon.ico │ │ ├── package-lock.json │ │ ├── package.json │ │ └── public │ │ ├── robots.txt │ │ └── uploads │ │ └── .gitkeep ├── common-config │ ├── .commitlintrc.json │ ├── .editorconfig │ ├── .markdownlint.json │ └── .npmrc ├── common-docker │ ├── Dockerfile │ ├── api │ │ └── docker-compose.yml │ └── website │ │ └── docker-compose.yml ├── common-github │ ├── ISSUE_TEMPLATE │ │ ├── BUG.md │ │ ├── DOCUMENTATION.md │ │ ├── FEATURE_REQUEST.md │ │ ├── IMPROVEMENT.md │ │ └── QUESTION.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows │ │ ├── build.yml │ │ ├── lint.yml │ │ ├── release.yml │ │ └── test.yml ├── common │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ └── README.md └── website │ ├── next │ ├── template.json │ └── template │ │ ├── .babelrc.json │ │ ├── .dockerignore │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── .husky │ │ ├── .gitignore │ │ ├── commit-msg │ │ └── pre-commit │ │ ├── components │ │ ├── Head.tsx │ │ ├── Hello.tsx │ │ └── __test__ │ │ │ └── Hello.test.tsx │ │ ├── entrypoint.sh │ │ ├── jest.config.js │ │ ├── next-env.d.ts │ │ ├── next.config.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ │ ├── postcss.config.js │ │ ├── public │ │ ├── images │ │ │ └── icons │ │ │ │ ├── icon-128x128.png │ │ │ │ ├── icon-144x144.png │ │ │ │ ├── icon-152x152.png │ │ │ │ ├── icon-192x192.png │ │ │ │ ├── icon-384x384.png │ │ │ │ ├── icon-512x512.png │ │ │ │ ├── icon-72x72.png │ │ │ │ └── icon-96x96.png │ │ └── manifest.json │ │ ├── tailwind.config.js │ │ └── tsconfig.json │ └── nuxt │ ├── template.json │ └── template │ ├── .dockerignore │ ├── .env.example │ ├── .eslintrc.js │ ├── .gitignore │ ├── .husky │ ├── .gitignore │ ├── commit-msg │ └── pre-commit │ ├── .prettierrc │ ├── components │ └── Logo.vue │ ├── entrypoint.sh │ ├── layouts │ └── default.vue │ ├── nuxt.config.js │ ├── package-lock.json │ ├── package.json │ ├── pages │ └── index.vue │ ├── static │ ├── favicon.ico │ └── icon.png │ ├── stylelint.config.js │ └── tsconfig.json └── tsconfig.json /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { "extends": ["@commitlint/config-conventional"] } 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information see: https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🐛 Bug Report' 3 | about: 'Report an unexpected problem or unintended behavior.' 4 | title: '[Bug]' 5 | labels: 'bug' 6 | --- 7 | 8 | 12 | 13 | ## Steps To Reproduce 14 | 15 | 1. Step 1 16 | 2. Step 2 17 | 18 | ## The current behavior 19 | 20 | ## The expected behavior 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '📜 Documentation' 3 | about: 'Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...).' 4 | title: '[Documentation]' 5 | labels: 'documentation' 6 | --- 7 | 8 | 9 | 10 | ## Documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ## Proposal 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '✨ Feature Request' 3 | about: 'Suggest a new feature idea.' 4 | title: '[Feature]' 5 | labels: 'feature request' 6 | --- 7 | 8 | 9 | 10 | ## Description 11 | 12 | 13 | 14 | ## Describe the solution you'd like 15 | 16 | 17 | 18 | ## Describe alternatives you've considered 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/IMPROVEMENT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🔧 Improvement' 3 | about: 'Improve structure/format/performance/refactor/tests of the code.' 4 | title: '[Improvement]' 5 | labels: 'improvement' 6 | --- 7 | 8 | 9 | 10 | ## Type of Improvement 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ## Proposal 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/QUESTION.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🙋 Question' 3 | about: 'Further information is requested.' 4 | title: '[Question]' 5 | labels: 'question' 6 | --- 7 | 8 | ### Question 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ## What changes this PR introduce? 13 | 14 | ## List any relevant issue numbers 15 | 16 | ## Is there anything you'd like reviewers to focus on? 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # For more information see: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: 'github-actions' 6 | directory: '/' 7 | schedule: 8 | interval: 'daily' 9 | 10 | - package-ecosystem: 'npm' 11 | directory: '/' 12 | schedule: 13 | interval: 'daily' 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 'Build' 2 | 3 | on: 4 | push: 5 | branches: [master, develop] 6 | pull_request: 7 | branches: [master, develop] 8 | 9 | jobs: 10 | build: 11 | runs-on: 'ubuntu-latest' 12 | steps: 13 | - uses: 'actions/checkout@v2' 14 | 15 | - name: 'Use Node.js' 16 | uses: 'actions/setup-node@v2.4.1' 17 | with: 18 | node-version: '16.x' 19 | cache: 'npm' 20 | 21 | - name: 'Install' 22 | run: 'npm install' 23 | 24 | - name: 'Build Package' 25 | run: 'npm run build' 26 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: 'Lint' 2 | 3 | on: 4 | push: 5 | branches: [master, develop] 6 | pull_request: 7 | branches: [master, develop] 8 | 9 | jobs: 10 | lint: 11 | runs-on: 'ubuntu-latest' 12 | steps: 13 | - uses: 'actions/checkout@v2' 14 | 15 | - name: 'Use Node.js' 16 | uses: 'actions/setup-node@v2.4.1' 17 | with: 18 | node-version: '16.x' 19 | cache: 'npm' 20 | 21 | - name: 'Install' 22 | run: 'npm install' 23 | 24 | - run: 'npm run lint:commit -- --to "${{ github.sha }}"' 25 | - run: 'npm run lint:editorconfig' 26 | - run: 'npm run lint:markdown' 27 | - run: 'npm run lint:typescript' 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: 'Release' 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | jobs: 8 | release: 9 | runs-on: 'ubuntu-latest' 10 | steps: 11 | - uses: 'actions/checkout@v2' 12 | 13 | - name: 'Use Node.js' 14 | uses: 'actions/setup-node@v2.4.1' 15 | with: 16 | node-version: '16.x' 17 | cache: 'npm' 18 | 19 | - name: 'Install' 20 | run: 'npm install' 21 | 22 | - name: 'Build Package' 23 | run: 'npm run build' 24 | 25 | - name: 'Release' 26 | run: 'npm run release' 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 'Test' 2 | 3 | on: 4 | push: 5 | branches: [master, develop] 6 | pull_request: 7 | branches: [master, develop] 8 | 9 | jobs: 10 | test: 11 | runs-on: 'ubuntu-latest' 12 | steps: 13 | - uses: 'actions/checkout@v2' 14 | 15 | - name: 'Use Node.js' 16 | uses: 'actions/setup-node@v2.4.1' 17 | with: 18 | node-version: '16.x' 19 | cache: 'npm' 20 | 21 | - name: 'Install' 22 | run: 'npm install' 23 | 24 | - name: 'Test' 25 | run: 'npm run test' 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .npm 6 | 7 | # production 8 | build 9 | 10 | # debug 11 | npm-debug.log* 12 | 13 | # editors 14 | ./.vscode 15 | ./.theia 16 | ./.idea 17 | 18 | # misc 19 | .DS_Store 20 | app-test 21 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint:commit -- --edit 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint:editorconfig 5 | npm run lint:markdown 6 | npm run lint:typescript 7 | npm run build 8 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD013": false, 4 | "MD024": false, 5 | "MD033": false, 6 | "MD041": false 7 | } 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | engine-strict=true 3 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["master"], 3 | "plugins": [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | "preset": "conventionalcommits" 8 | } 9 | ], 10 | [ 11 | "@semantic-release/release-notes-generator", 12 | { 13 | "preset": "conventionalcommits" 14 | } 15 | ], 16 | "@semantic-release/npm", 17 | "@semantic-release/github" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks a lot for your interest in contributing to **create-fullstack-app**! 🎉 4 | 5 | ## Types of contributions 6 | 7 | - Create a new project template 8 | - Reporting a bug 9 | - Improve the CLI 10 | - Correct a spelling error 11 | 12 | ## Pull Requests 13 | 14 | - **Please first discuss** the change you wish to make via [issue](https://github.com/Divlo/create-fullstack-app/issues) before making a change. It might avoid a waste of your time. 15 | 16 | - Ensure your code respect [Typescript Standard Style](https://www.npmjs.com/package/ts-standard). 17 | 18 | - Make sure your **code passes the tests**. 19 | 20 | If you're adding new features to **create-fullstack-app**, please include tests. 21 | 22 | ## Commits 23 | 24 | The commit message guidelines respect [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional) and [Semantic Versioning](https://semver.org/) for releases. 25 | 26 | ### Types 27 | 28 | Types define which kind of changes you made to the project. 29 | 30 | | Types | Description | 31 | | -------- | ------------------------------------------------------------------------------------------------------------ | 32 | | feat | A new feature. | 33 | | fix | A bug fix. | 34 | | docs | Documentation only changes. | 35 | | style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). | 36 | | refactor | A code change that neither fixes a bug nor adds a feature. | 37 | | perf | A code change that improves performance. | 38 | | test | Adding missing tests or correcting existing tests. | 39 | | build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm). | 40 | | ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs). | 41 | | chore | Other changes that don't modify src or test files. | 42 | | revert | Reverts a previous commit. | 43 | 44 | ### Scopes 45 | 46 | Scopes define what part of the code changed. 47 | 48 | There are 2 principal scopes in the project : 49 | 50 | - template 51 | - cli 52 | 53 | ### Examples 54 | 55 | ```sh 56 | git commit -m "feat(cli): new --only-website flag" 57 | git commit -m "docs(readme): update installation process" 58 | git commit -m "fix(template): remove bugs" 59 | ``` 60 | 61 | ## Create a new project template 62 | 63 | You can create a new template by following these steps, for the example we'll add a [fastify](https://www.fastify.io/) template. 64 | 65 | - Create a new folder `/templates//`, since Fastify is a framework for building APIs, you can do it like so for our example : `/templates/api/fastify` (In the folder you created, you can create a new folder called `template` and put your files for the new template). 66 | 67 | _Note :_ Your template folder can't have `LICENSE` or `README.md` files, because it will be added automatically (see `/templates/common`). 68 | 69 | - Then you can create a new json file for the example `/templates/api/fastify/template.json` so you can add a new object with the `name` property. 70 | For example it could be something like this : 71 | 72 | ```json 73 | { 74 | "name": "Fastify with Sequelize (ORM)" 75 | } 76 | ``` 77 | 78 | - Don't forget to update the `README.md` file so everyone know what templates are availables. 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Divlo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "ts-jest", 3 | "testEnvironment": "node", 4 | "setupFilesAfterEnv": ["./__test__/setup.ts"], 5 | "rootDir": "./src" 6 | } 7 | -------------------------------------------------------------------------------- /lighthouse_google-chrome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theoludwig/create-fullstack-app/c01919b2340bb4634861efba5cdbf2c152d8fff1/lighthouse_google-chrome.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-fullstack-app", 3 | "version": "0.0.0-development", 4 | "description": "Create Fullstack TypeScript application with ease.", 5 | "author": "Divlo ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Divlo/create-fullstack-app.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/Divlo/create-fullstack-app/issues" 13 | }, 14 | "homepage": "https://github.com/Divlo/create-fullstack-app#readme", 15 | "keywords": [ 16 | "cli", 17 | "fullstack", 18 | "template", 19 | "react", 20 | "next", 21 | "vue", 22 | "nuxt", 23 | "express", 24 | "sequelize", 25 | "strapi", 26 | "nest" 27 | ], 28 | "main": "build/index.js", 29 | "bin": "build/index.js", 30 | "engines": { 31 | "node": ">=14.0.0", 32 | "npm": ">=7.0.0" 33 | }, 34 | "files": [ 35 | "build", 36 | "templates" 37 | ], 38 | "ts-standard": { 39 | "ignore": [ 40 | "build", 41 | "node_modules", 42 | "templates", 43 | "app-test" 44 | ], 45 | "envs": [ 46 | "node", 47 | "jest" 48 | ], 49 | "report": "stylish" 50 | }, 51 | "scripts": { 52 | "build": "rimraf ./build && tsc", 53 | "start": "node build/index.js app-test", 54 | "dev": "tsc --watch", 55 | "lint:commit": "commitlint", 56 | "lint:editorconfig": "editorconfig-checker", 57 | "lint:markdown": "markdownlint '**/*.md' --dot --ignore node_modules --ignore app-test", 58 | "lint:typescript": "ts-standard", 59 | "release": "semantic-release", 60 | "test": "jest", 61 | "postinstall": "husky install", 62 | "prepublishOnly": "pinst --disable", 63 | "postpublish": "pinst --enable" 64 | }, 65 | "dependencies": { 66 | "chalk": "4.1.2", 67 | "child-process-promise": "2.2.1", 68 | "clipanion": "3.1.0", 69 | "inquirer": "8.1.5", 70 | "log-symbols": "4.1.0", 71 | "make-dir": "3.1.0", 72 | "ora": "5.4.1", 73 | "rimraf": "3.0.2", 74 | "tslib": "2.3.1", 75 | "update-notifier": "5.1.0", 76 | "validate-npm-package-name": "3.0.0" 77 | }, 78 | "devDependencies": { 79 | "@commitlint/cli": "13.2.0", 80 | "@commitlint/config-conventional": "13.2.0", 81 | "@types/child-process-promise": "2.2.2", 82 | "@types/inquirer": "8.1.3", 83 | "@types/jest": "27.0.2", 84 | "@types/mock-fs": "4.13.1", 85 | "@types/node": "16.10.2", 86 | "@types/rimraf": "3.0.2", 87 | "@types/update-notifier": "5.1.0", 88 | "@types/validate-npm-package-name": "3.0.3", 89 | "editorconfig-checker": "4.0.2", 90 | "husky": "7.0.2", 91 | "jest": "27.2.4", 92 | "markdownlint-cli": "0.28.1", 93 | "mock-fs": "5.1.1", 94 | "semantic-release": "18.0.0", 95 | "pinst": "2.1.6", 96 | "ts-jest": "27.0.5", 97 | "ts-standard": "10.0.0", 98 | "typescript": "4.4.3" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/__test__/setup.ts: -------------------------------------------------------------------------------- 1 | import fsMock from 'mock-fs' 2 | 3 | afterEach(async () => { 4 | fsMock.restore() 5 | }) 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import path from 'path' 3 | 4 | import chalk from 'chalk' 5 | import { Builtins, Cli } from 'clipanion' 6 | import updateNotifier from 'update-notifier' 7 | import readPackage from 'read-pkg' 8 | 9 | import { CreateFullstackAppCommand } from './commands/CreateFullstackAppCommand' 10 | 11 | const [, , ...args] = process.argv 12 | 13 | const packageJSON = readPackage.sync({ cwd: path.join(__dirname, '..') }) 14 | 15 | async function main (): Promise { 16 | const cli = new Cli({ 17 | binaryLabel: packageJSON.name, 18 | binaryName: packageJSON.name, 19 | binaryVersion: packageJSON.version 20 | }) 21 | cli.register(CreateFullstackAppCommand) 22 | cli.register(Builtins.HelpCommand) 23 | cli.register(Builtins.VersionCommand) 24 | updateNotifier({ pkg: packageJSON }).notify() 25 | await cli.runExit(args, Cli.defaultContext) 26 | } 27 | 28 | main().catch(() => { 29 | console.error(chalk.red('Error occurred...')) 30 | process.exit(1) 31 | }) 32 | -------------------------------------------------------------------------------- /src/services/Project.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import * as fsWithCallbacks from 'fs' 3 | 4 | import logSymbols from 'log-symbols' 5 | import childProcess from 'child-process-promise' 6 | 7 | import tryGitInit from '../utils/tryGitInit' 8 | import { 9 | Template, 10 | commonConfigTemplatesPath, 11 | commondDockerTemplatesPath, 12 | commondGitHubTemplatesPath 13 | } from './Template' 14 | import { copyDirectory } from '../utils/copyDirectory' 15 | import { loading } from '../utils/loading' 16 | import makeDirectory from 'make-dir' 17 | import { checkFileExists } from '../utils/checkFileExists' 18 | 19 | const fs = fsWithCallbacks.promises 20 | 21 | export interface ProjectOptions { 22 | template: Template 23 | projectPath: string 24 | noInstall: boolean 25 | shouldCreateGitHubFolder: boolean 26 | } 27 | 28 | export class Project implements ProjectOptions { 29 | public template: Template 30 | public projectPath: string 31 | public noInstall: boolean 32 | public shouldCreateGitHubFolder: boolean 33 | 34 | constructor (options: ProjectOptions) { 35 | this.template = options.template 36 | this.projectPath = options.projectPath 37 | this.noInstall = options.noInstall 38 | this.shouldCreateGitHubFolder = options.shouldCreateGitHubFolder 39 | } 40 | 41 | public async create (): Promise { 42 | await this.copyFiles() 43 | process.chdir(this.projectPath) 44 | this.tryGitInit() 45 | if (!this.noInstall) { 46 | await this.installPackages() 47 | } 48 | console.log('\n') 49 | } 50 | 51 | private async copyFiles (): Promise { 52 | await loading( 53 | `Copy the files from the template called : ${this.template.name}.`, 54 | async () => { 55 | await makeDirectory(this.projectPath) 56 | await copyDirectory(commonConfigTemplatesPath, this.projectPath) 57 | if (this.shouldCreateGitHubFolder) { 58 | const githubFolderPath = path.join(this.projectPath, '.github') 59 | await makeDirectory(githubFolderPath) 60 | await copyDirectory(commondGitHubTemplatesPath, githubFolderPath) 61 | } 62 | const hasDockerFiles = await checkFileExists(path.join(this.projectPath, 'Dockerfile')) 63 | if (!hasDockerFiles) { 64 | await fs.copyFile( 65 | path.join(commondDockerTemplatesPath, 'Dockerfile'), 66 | path.join(this.projectPath, 'Dockerfile') 67 | ) 68 | await fs.copyFile( 69 | path.join( 70 | commondDockerTemplatesPath, 71 | this.template.type, 72 | 'docker-compose.yml' 73 | ), 74 | path.join(this.projectPath, 'docker-compose.yml') 75 | ) 76 | } 77 | await copyDirectory( 78 | path.join(this.template.path, 'template'), 79 | this.projectPath 80 | ) 81 | } 82 | ) 83 | } 84 | 85 | private async installPackages (): Promise { 86 | await loading( 87 | 'Installing npm packages. This might take a couple of minutes.', 88 | async () => { 89 | await childProcess.exec('npm install', { 90 | cwd: this.projectPath 91 | }) 92 | } 93 | ) 94 | } 95 | 96 | private tryGitInit (): void { 97 | if (tryGitInit(this.projectPath)) { 98 | console.log(logSymbols.success, 'Initialized a git repository.') 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/services/Question.ts: -------------------------------------------------------------------------------- 1 | import { QuestionCollection, DistinctQuestion } from 'inquirer' 2 | 3 | import { getTemplates } from './Template' 4 | 5 | export interface QuestionsAnswers { 6 | templateWebsite: string 7 | templateAPI: string 8 | } 9 | 10 | export async function getQuestions ( 11 | onlyApi: boolean, 12 | onlyWebsite: boolean 13 | ): Promise { 14 | const templateChoices = await getTemplates() 15 | const questionWebsiteTemplate: DistinctQuestion = { 16 | name: 'templateWebsite', 17 | type: 'list', 18 | message: 'Select a Website template:', 19 | choices: templateChoices.website 20 | } 21 | const questionAPITemplate: DistinctQuestion = { 22 | name: 'templateAPI', 23 | type: 'list', 24 | message: 'Select an API template:', 25 | choices: templateChoices.api 26 | } 27 | 28 | if (onlyApi) { 29 | return [questionAPITemplate] 30 | } 31 | 32 | if (onlyWebsite) { 33 | return [questionWebsiteTemplate] 34 | } 35 | 36 | return [ 37 | questionWebsiteTemplate, 38 | questionAPITemplate 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/services/Template.ts: -------------------------------------------------------------------------------- 1 | import * as fsWithCallbacks from 'fs' 2 | import path from 'path' 3 | 4 | const fs = fsWithCallbacks.promises 5 | 6 | export const templatesPath = path.join(__dirname, '..', '..', 'templates') 7 | export const commonTemplatesPath = path.join(templatesPath, 'common') 8 | export const commonConfigTemplatesPath = path.join(templatesPath, 'common-config') 9 | export const commondDockerTemplatesPath = path.join(templatesPath, 'common-docker') 10 | export const commondGitHubTemplatesPath = path.join(templatesPath, 'common-github') 11 | 12 | export type TemplateType = 'api' | 'website' 13 | 14 | export type TemplateJSON = Omit 15 | 16 | export type Templates = { 17 | [key in TemplateType]: string[] 18 | } 19 | 20 | export interface Template { 21 | name: string 22 | type: TemplateType 23 | path: string 24 | } 25 | 26 | export const getTemplates = async (): Promise => { 27 | const apiTemplatesFolders = await fs.readdir(path.join(templatesPath, 'api')) 28 | const websiteTemplatesFolders = await fs.readdir( 29 | path.join(templatesPath, 'website') 30 | ) 31 | return { 32 | api: apiTemplatesFolders, 33 | website: websiteTemplatesFolders 34 | } 35 | } 36 | 37 | interface GetTemplateOptions { 38 | name: string 39 | type: TemplateType 40 | } 41 | 42 | export const getTemplate = async ( 43 | options: GetTemplateOptions 44 | ): Promise