├── .prettierignore ├── .vscode └── settings.json ├── .prettierrc ├── templates ├── footer.md ├── default-no-html.md └── default.md ├── jest.config.js ├── src ├── questions │ ├── project-demo-url.js │ ├── author-name.js │ ├── project-name.js │ ├── issues-url.js │ ├── license-name.js │ ├── project-description.js │ ├── project-version.js │ ├── project-homepage.js │ ├── contributing-url.js │ ├── project-documentation-url.js │ ├── author-linkedin.js │ ├── author-patreon.js │ ├── author-twitter.js │ ├── license-url.js │ ├── author-github.js │ ├── install-command.js │ ├── project-demo-url.spec.js │ ├── author-website.js │ ├── author-twitter.spec.js │ ├── package-manager.js │ ├── test-command.js │ ├── author-linkedin.spec.js │ ├── author-patreon.spec.js │ ├── usage.js │ ├── author-name.spec.js │ ├── project-documentation-url.spec.js │ ├── license-name.spec.js │ ├── project-version.spec.js │ ├── issues-url.spec.js │ ├── project-homepage.spec.js │ ├── project-description.spec.js │ ├── author-github.spec.js │ ├── contributing-url.spec.js │ ├── project-name.spec.js │ ├── index.spec.js │ ├── project-prerequisites.js │ ├── install-command.spec.js │ ├── index.js │ ├── usage.spec.js │ ├── test-command.spec.js │ ├── license-url.spec.js │ ├── package-manager.spec.js │ ├── author-website.spec.js │ └── project-prerequisites.spec.js ├── clean-context.spec.js ├── clean-context.js ├── ask-overwrite.js ├── index.js ├── choose-template.js ├── ask-questions.js ├── ask-overwrite.spec.js ├── cli.js ├── choose-template.spec.js ├── ask-questions.spec.js ├── readme.js ├── cli.spec.js ├── utils.js ├── project-infos.js ├── __snapshots__ │ └── readme.spec.js.snap ├── readme.spec.js ├── utils.spec.js └── project-infos.spec.js ├── .gitignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ └── nodejs.yml └── CODE_OF_CONDUCT.md ├── .eslintrc.js ├── LICENSE ├── package.json ├── CONTRIBUTING.md ├── README.md └── CHANGELOG.md /.prettierignore: -------------------------------------------------------------------------------- 1 | templates/* -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": false 4 | } 5 | -------------------------------------------------------------------------------- /templates/footer.md: -------------------------------------------------------------------------------- 1 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: [ 3 | './src/**/*.js', 4 | '!./src/index.js', 5 | '!**/node_modules/**', 6 | '!**/vendor/**' 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/questions/project-demo-url.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | type: 'input', 3 | message: '✨ Project demo url (use empty value to skip)', 4 | name: 'projectDemoUrl' 5 | }) 6 | -------------------------------------------------------------------------------- /src/questions/author-name.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '👤 Author name', 4 | name: 'authorName', 5 | default: projectInfos.author 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/project-name.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '💡 Project name', 4 | name: 'projectName', 5 | default: projectInfos.name 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/issues-url.js: -------------------------------------------------------------------------------- 1 | module.exports = packageJson => ({ 2 | type: 'input', 3 | message: '🔧 Issues page url (use empty value to skip)', 4 | name: 'issuesUrl', 5 | default: packageJson.issuesUrl 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/license-name.js: -------------------------------------------------------------------------------- 1 | module.exports = packageJson => ({ 2 | type: 'input', 3 | message: '📝 License name (use empty value to skip)', 4 | name: 'licenseName', 5 | default: packageJson.licenseName 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/project-description.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '📄 Project description', 4 | name: 'projectDescription', 5 | default: projectInfos.description 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/project-version.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: 'ℹ️ Project version (use empty value to skip)', 4 | name: 'projectVersion', 5 | default: projectInfos.version 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/project-homepage.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '🏠 Project homepage (use empty value to skip)', 4 | name: 'projectHomepage', 5 | default: projectInfos.homepage 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/contributing-url.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '🤝 Contributing guide url (use empty value to skip)', 4 | name: 'contributingUrl', 5 | default: projectInfos.contributingUrl 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/project-documentation-url.js: -------------------------------------------------------------------------------- 1 | module.exports = projectInfos => ({ 2 | type: 'input', 3 | message: '📘 Project documentation url (use empty value to skip)', 4 | name: 'projectDocumentationUrl', 5 | default: projectInfos.documentationUrl 6 | }) 7 | -------------------------------------------------------------------------------- /src/questions/author-linkedin.js: -------------------------------------------------------------------------------- 1 | const { cleanSocialNetworkUsername } = require('../utils') 2 | 3 | module.exports = () => ({ 4 | type: 'input', 5 | message: '💼 LinkedIn username (use empty value to skip)', 6 | name: 'authorLinkedInUsername', 7 | filter: cleanSocialNetworkUsername 8 | }) 9 | -------------------------------------------------------------------------------- /src/questions/author-patreon.js: -------------------------------------------------------------------------------- 1 | const { cleanSocialNetworkUsername } = require('../utils') 2 | 3 | module.exports = () => ({ 4 | type: 'input', 5 | message: '❤️ Patreon username (use empty value to skip)', 6 | name: 'authorPatreonUsername', 7 | filter: cleanSocialNetworkUsername 8 | }) 9 | -------------------------------------------------------------------------------- /src/questions/author-twitter.js: -------------------------------------------------------------------------------- 1 | const { cleanSocialNetworkUsername } = require('../utils') 2 | 3 | module.exports = () => ({ 4 | type: 'input', 5 | message: '🐦 Twitter username (use empty value to skip)', 6 | name: 'authorTwitterUsername', 7 | filter: cleanSocialNetworkUsername 8 | }) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /report 4 | /coverage 5 | 6 | # Log files 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Editor directories and files 12 | .idea 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | *.sw* 18 | 19 | # Fix an error from npm 6.9.0 20 | .git 21 | -------------------------------------------------------------------------------- /src/questions/license-url.js: -------------------------------------------------------------------------------- 1 | const isEmpty = require('lodash/isEmpty') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '📝 License url (use empty value to skip)', 6 | name: 'licenseUrl', 7 | default: projectInfos.licenseUrl, 8 | when: answersContext => !isEmpty(answersContext.licenseName) 9 | }) 10 | -------------------------------------------------------------------------------- /src/questions/author-github.js: -------------------------------------------------------------------------------- 1 | const { cleanSocialNetworkUsername } = require('../utils') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '👤 GitHub username (use empty value to skip)', 6 | name: 'authorGithubUsername', 7 | default: projectInfos.githubUsername, 8 | filter: cleanSocialNetworkUsername 9 | }) 10 | -------------------------------------------------------------------------------- /src/questions/install-command.js: -------------------------------------------------------------------------------- 1 | const isNil = require('lodash/isNil') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '📦 Install command (use empty value to skip)', 6 | name: 'installCommand', 7 | default: answers => { 8 | const packageManager = answers.packageManager || projectInfos.packageManager 9 | return isNil(packageManager) ? undefined : `${packageManager} install` 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/questions/project-demo-url.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectDemoUrl = require('./project-demo-url') 2 | 3 | describe('askProjectDemoUrl', () => { 4 | it('should return the correct question format', () => { 5 | const result = askProjectDemoUrl() 6 | 7 | expect(result).toEqual({ 8 | type: 'input', 9 | message: '✨ Project demo url (use empty value to skip)', 10 | name: 'projectDemoUrl' 11 | }) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/questions/author-website.js: -------------------------------------------------------------------------------- 1 | const { getAuthorWebsiteFromGithubAPI } = require('../utils') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '🏠 Author website (use empty value to skip)', 6 | name: 'authorWebsite', 7 | default: async answers => 8 | answers.authorGithubUsername !== projectInfos.githubUsername 9 | ? getAuthorWebsiteFromGithubAPI(answers.authorGithubUsername) 10 | : projectInfos.authorWebsite 11 | }) 12 | -------------------------------------------------------------------------------- /src/questions/author-twitter.spec.js: -------------------------------------------------------------------------------- 1 | const askAuthorTwitter = require('./author-twitter') 2 | 3 | describe('askAuthorTwitter', () => { 4 | it('should return correct question format', () => { 5 | const result = askAuthorTwitter() 6 | 7 | expect(result).toEqual({ 8 | type: 'input', 9 | message: '🐦 Twitter username (use empty value to skip)', 10 | name: 'authorTwitterUsername', 11 | filter: expect.any(Function) 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/questions/package-manager.js: -------------------------------------------------------------------------------- 1 | const isEmpty = require('lodash/isEmpty') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'list', 5 | message: '📦 Choose Package Manager ', 6 | name: 'packageManager', 7 | choices: [ 8 | { 9 | name: 'npm', 10 | value: 'npm' 11 | }, 12 | { 13 | name: 'yarn', 14 | value: 'yarn' 15 | } 16 | ], 17 | when: () => projectInfos.isJSProject && isEmpty(projectInfos.packageManager) 18 | }) 19 | -------------------------------------------------------------------------------- /src/questions/test-command.js: -------------------------------------------------------------------------------- 1 | const isNil = require('lodash/isNil') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '✅ Test command (use empty value to skip)', 6 | name: 'testCommand', 7 | default: answers => { 8 | const packageManager = answers.packageManager || projectInfos.packageManager 9 | return projectInfos.hasTestCommand && !isNil(packageManager) 10 | ? `${packageManager} run test` 11 | : undefined 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/questions/author-linkedin.spec.js: -------------------------------------------------------------------------------- 1 | const askAuthorLinkedIn = require('./author-linkedin') 2 | 3 | describe('askAuthorLinkedIn', () => { 4 | it('should return correct question format', () => { 5 | const result = askAuthorLinkedIn() 6 | 7 | expect(result).toEqual({ 8 | type: 'input', 9 | message: '💼 LinkedIn username (use empty value to skip)', 10 | name: 'authorLinkedInUsername', 11 | filter: expect.any(Function) 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/questions/author-patreon.spec.js: -------------------------------------------------------------------------------- 1 | const askPatreonUsername = require('./author-patreon') 2 | 3 | describe('askPatreonUsername', () => { 4 | it('should return correct question format', () => { 5 | const result = askPatreonUsername() 6 | 7 | expect(result).toEqual({ 8 | type: 'input', 9 | message: '❤️ Patreon username (use empty value to skip)', 10 | name: 'authorPatreonUsername', 11 | filter: expect.any(Function) 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/questions/usage.js: -------------------------------------------------------------------------------- 1 | const isNil = require('lodash/isNil') 2 | 3 | module.exports = projectInfos => ({ 4 | type: 'input', 5 | message: '🚀 Usage command or instruction (use empty value to skip)', 6 | name: 'usage', 7 | default: answers => { 8 | const packageManager = answers.packageManager || projectInfos.packageManager 9 | return projectInfos.hasStartCommand && !isNil(packageManager) 10 | ? `${packageManager} run start` 11 | : undefined 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/questions/author-name.spec.js: -------------------------------------------------------------------------------- 1 | const askAuthorName = require('./author-name') 2 | 3 | describe('askAuthorName', () => { 4 | it('should return correct question format', () => { 5 | const author = 'Franck Abgrall' 6 | const projectInfos = { author } 7 | 8 | const result = askAuthorName(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '👤 Author name', 13 | name: 'authorName', 14 | default: author 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/questions/project-documentation-url.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectName = require('./project-name') 2 | 3 | describe('askProjectName', () => { 4 | it('should return correct question format', () => { 5 | const name = 'readme-md-generator' 6 | const projectInfos = { name } 7 | 8 | const result = askProjectName(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '💡 Project name', 13 | name: 'projectName', 14 | default: name 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/questions/license-name.spec.js: -------------------------------------------------------------------------------- 1 | const askLicenseName = require('./license-name') 2 | 3 | describe('askLicenseName', () => { 4 | it('should return correct question format', () => { 5 | const licenseName = 'MIT' 6 | const projectInfos = { licenseName } 7 | 8 | const result = askLicenseName(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '📝 License name (use empty value to skip)', 13 | name: 'licenseName', 14 | default: licenseName 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/questions/project-version.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectVersion = require('./project-version') 2 | 3 | describe('askProjectVersion', () => { 4 | it('should return correct question format', () => { 5 | const version = '1.0.0' 6 | const projectInfos = { version } 7 | 8 | const result = askProjectVersion(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: 'ℹ️ Project version (use empty value to skip)', 13 | name: 'projectVersion', 14 | default: version 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/questions/issues-url.spec.js: -------------------------------------------------------------------------------- 1 | const askIssues = require('./issues-url') 2 | 3 | describe('askIssues', () => { 4 | it('should return correct question format', () => { 5 | const issuesUrl = 'https://github.com/kefranabg/readme-md-generator/issues' 6 | const projectInfos = { issuesUrl } 7 | 8 | const result = askIssues(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '🔧 Issues page url (use empty value to skip)', 13 | name: 'issuesUrl', 14 | default: issuesUrl 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/questions/project-homepage.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectHomepage = require('./project-homepage') 2 | 3 | describe('askProjectHomepage', () => { 4 | it('should return correct question format', () => { 5 | const homepage = 'homepage' 6 | const projectInfos = { homepage } 7 | 8 | const result = askProjectHomepage(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '🏠 Project homepage (use empty value to skip)', 13 | name: 'projectHomepage', 14 | default: homepage 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/clean-context.spec.js: -------------------------------------------------------------------------------- 1 | const cleanContext = require('./clean-context') 2 | 3 | describe('cleanContext', () => { 4 | it('should replace licenseName and projectVersion - and _ characters by -- and __', () => { 5 | const context = { 6 | licenseName: 'Apache-2_0', 7 | projectVersion: '1.0_0-alpha' 8 | } 9 | const cleanedContext = { 10 | licenseName: 'Apache--2__0', 11 | projectVersion: '1.0__0--alpha' 12 | } 13 | 14 | const result = cleanContext(context) 15 | 16 | expect(result).toEqual(cleanedContext) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /src/questions/project-description.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectDescription = require('./project-description') 2 | 3 | describe('askProjectDescription', () => { 4 | it('should return correct question format', () => { 5 | const description = 'description' 6 | const projectInfos = { description } 7 | 8 | const result = askProjectDescription(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '📄 Project description', 13 | name: 'projectDescription', 14 | default: description 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/clean-context.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Clean answer context 3 | * 4 | * @param {Object} context 5 | */ 6 | module.exports = context => { 7 | const cleanBadgeText = text => text.replace(/-/g, '--').replace(/_/g, '__') 8 | 9 | // Why doing this? 10 | // See https://github.com/kefranabg/readme-md-generator/pull/141 11 | const licenseName = context.licenseName 12 | .replace(/-/g, '--') 13 | .replace(/_/g, '__') 14 | const projectVersion = cleanBadgeText(context.projectVersion) 15 | 16 | return { 17 | ...context, 18 | licenseName, 19 | projectVersion 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/questions/author-github.spec.js: -------------------------------------------------------------------------------- 1 | const askAuthorGithub = require('./author-github') 2 | 3 | describe('askAuthorGithub', () => { 4 | it('should return correct question format', () => { 5 | const githubUsername = 'kefranabg' 6 | const projectInfos = { githubUsername } 7 | 8 | const result = askAuthorGithub(projectInfos) 9 | 10 | expect(result).toEqual({ 11 | type: 'input', 12 | message: '👤 GitHub username (use empty value to skip)', 13 | name: 'authorGithubUsername', 14 | default: githubUsername, 15 | filter: expect.any(Function) 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [kefranabg] 4 | patreon: FranckAbgrall 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /src/ask-overwrite.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | 3 | const question = { 4 | type: 'list', 5 | message: 6 | '🚨 readme-md-generator will overwrite your current README.md. Are you sure you want to continue? ', 7 | name: 'overwriteReadme', 8 | choices: [ 9 | { 10 | name: 'No', 11 | value: false 12 | }, 13 | { 14 | name: 'Yes ', 15 | value: true 16 | } 17 | ] 18 | } 19 | 20 | /** 21 | * Ask users if they want to overwrite the existing README 22 | */ 23 | module.exports = async () => { 24 | const { overwriteReadme } = await inquirer.prompt([question]) 25 | return overwriteReadme 26 | } 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | jest: true 6 | }, 7 | extends: ['airbnb-base', 'eslint:recommended'], 8 | rules: { 9 | semi: ['error', 'never'], 10 | 'no-use-before-define': ['error', { functions: false }], 11 | 'comma-dangle': 0, 12 | 'no-var': 2, 13 | 'prefer-const': 2, 14 | 'operator-linebreak': 0, 15 | 'no-confusing-arrow': 0, 16 | 'implicit-arrow-linebreak': 0, 17 | indent: 0, 18 | 'no-param-reassign': 0, 19 | 'function-paren-newline': 0, 20 | 'arrow-parens': 0 21 | }, 22 | parserOptions: { 23 | parser: 'babel-eslint' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/questions/contributing-url.spec.js: -------------------------------------------------------------------------------- 1 | const askContributingUrl = require('./contributing-url') 2 | 3 | describe('askContributingUrl', () => { 4 | it('should return correct question format', () => { 5 | const contributingUrl = 6 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md' 7 | const projectInfos = { contributingUrl } 8 | 9 | const result = askContributingUrl(projectInfos) 10 | 11 | expect(result).toEqual({ 12 | type: 'input', 13 | message: '🤝 Contributing guide url (use empty value to skip)', 14 | name: 'contributingUrl', 15 | default: contributingUrl 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /src/questions/project-name.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectDocumentationUrl = require('./project-documentation-url') 2 | 3 | describe('askProjectDocumentationUrl', () => { 4 | it('should return correct question format', () => { 5 | const documentationUrl = 6 | 'https://github.com/kefranabg/readme-md-generator/blob/master/README.md' 7 | const projectInfos = { documentationUrl } 8 | 9 | const result = askProjectDocumentationUrl(projectInfos) 10 | 11 | expect(result).toEqual({ 12 | type: 'input', 13 | message: '📘 Project documentation url (use empty value to skip)', 14 | name: 'projectDocumentationUrl', 15 | default: documentationUrl 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const yargs = require('yargs') 4 | const { noop } = require('lodash') 5 | 6 | const mainProcess = require('./cli') 7 | 8 | yargs 9 | .usage('Usage: $0 [options]') 10 | .command('$0', 'Generate README.md', noop, args => { 11 | const { path: customTemplatePath, yes: useDefaultAnswers } = args 12 | mainProcess({ customTemplatePath, useDefaultAnswers }) 13 | }) 14 | .string('p') 15 | .alias('p', 'path') 16 | .describe('path', 'Path to your own template') 17 | .boolean('yes') 18 | .alias('y', 'yes') 19 | .describe('yes', 'Use default values for all fields') 20 | .help() 21 | .alias('v', 'version') 22 | .epilog( 23 | 'for more information, find our manual at https://github.com/kefranabg/readme-md-generator' 24 | ) 25 | .parse() 26 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [8.x, 10.x, 12.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v1 19 | - uses: actions/cache@v1 20 | with: 21 | path: ~/.npm 22 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 23 | restore-keys: | 24 | ${{ runner.os }}-node- 25 | - name: Install dependencies 26 | run: npm ci 27 | - name: Run prettier check on project files 28 | run: npm run prettier:check 29 | - name: Run linter 30 | run: npm run lint 31 | - name: Run unit tests 32 | run: npm run test:ci 33 | env: 34 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /src/choose-template.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const path = require('path') 3 | 4 | module.exports = async useDefaultAnswers => { 5 | const defaultTemplate = path.resolve(__dirname, '../templates/default.md') 6 | const defaultNoHtmlTemplate = path.resolve( 7 | __dirname, 8 | '../templates/default-no-html.md' 9 | ) 10 | 11 | if (useDefaultAnswers) return defaultTemplate 12 | 13 | const question = { 14 | type: 'list', 15 | message: 16 | '🎨 Use HTML in your README.md for a nicer rendering? (not supported everywhere. ex: Bitbucket)', 17 | name: 'templatePath', 18 | choices: [ 19 | { 20 | name: 'Yes ', 21 | value: defaultTemplate 22 | }, 23 | { 24 | name: 'No', 25 | value: defaultNoHtmlTemplate 26 | } 27 | ] 28 | } 29 | 30 | const { templatePath } = await inquirer.prompt([question]) 31 | 32 | return templatePath 33 | } 34 | -------------------------------------------------------------------------------- /src/questions/index.spec.js: -------------------------------------------------------------------------------- 1 | const questions = require('./') 2 | 3 | describe('questions', () => { 4 | it('should export questions in the correct order', () => { 5 | const questionsNameOrder = Object.keys(questions) 6 | 7 | expect(questionsNameOrder).toEqual([ 8 | 'askProjectName', 9 | 'askProjectVersion', 10 | 'askProjectDescription', 11 | 'askPackageManager', 12 | 'askProjectHomepage', 13 | 'askProjectDemoUrl', 14 | 'askProjectDocumentationUrl', 15 | 'askAuhtorName', 16 | 'askAuthorGithub', 17 | 'askAuthorWebsite', 18 | 'askAuthorTwitter', 19 | 'askAuthorLinkedIn', 20 | 'askAuthorPatreon', 21 | 'askProjectPrerequisites', 22 | 'askLicenseName', 23 | 'askLicenseUrl', 24 | 'askIssuesUrl', 25 | 'askContributingUrl', 26 | 'askInstallCommand', 27 | 'askUsage', 28 | 'askTestCommand' 29 | ]) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/ask-questions.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const { flatMap } = require('lodash') 3 | 4 | const questionsBuilders = require('./questions') 5 | const utils = require('./utils') 6 | 7 | /** 8 | * Ask user questions and return context to generate a README 9 | * 10 | * @param {Object} projectInfos 11 | * @param {Boolean} useDefaultAnswers 12 | */ 13 | module.exports = async (projectInfos, useDefaultAnswers) => { 14 | const questions = flatMap(Object.values(questionsBuilders), questionBuilder => 15 | questionBuilder(projectInfos) 16 | ) 17 | 18 | const answersContext = useDefaultAnswers 19 | ? await utils.getDefaultAnswers(questions) 20 | : await inquirer.prompt(questions) 21 | 22 | return { 23 | isGithubRepos: projectInfos.isGithubRepos, 24 | repositoryUrl: projectInfos.repositoryUrl, 25 | projectPrerequisites: undefined, 26 | isProjectOnNpm: utils.isProjectAvailableOnNpm(answersContext.projectName), 27 | ...answersContext 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /src/questions/project-prerequisites.js: -------------------------------------------------------------------------------- 1 | const isEmpty = require('lodash/isEmpty') 2 | const isNil = require('lodash/isNil') 3 | 4 | /** 5 | * Return engines as formatted choices 6 | * 7 | * @param {Object} engines 8 | */ 9 | const buildFormattedChoices = engines => 10 | isNil(engines) 11 | ? null 12 | : Object.keys(engines).map(key => ({ 13 | name: `${key} ${engines[key]}`, 14 | value: { 15 | name: key, 16 | value: engines[key] 17 | }, 18 | checked: true 19 | })) 20 | 21 | /** 22 | * Check if projectInfos has engines properties 23 | * 24 | * @param {Object} projectInfos 25 | */ 26 | const hasProjectInfosEngines = projectInfos => 27 | !isNil(projectInfos.engines) && !isEmpty(projectInfos.engines) 28 | 29 | module.exports = projectInfos => ({ 30 | type: 'checkbox', 31 | message: '⚠️ Project prerequisites', 32 | name: 'projectPrerequisites', 33 | choices: buildFormattedChoices(projectInfos.engines), 34 | when: () => hasProjectInfosEngines(projectInfos) 35 | }) 36 | -------------------------------------------------------------------------------- /src/questions/install-command.spec.js: -------------------------------------------------------------------------------- 1 | const askInstallCommand = require('./install-command') 2 | 3 | describe('askInstallCommand', () => { 4 | it('should return correct question format', () => { 5 | const result = askInstallCommand() 6 | expect(result).toEqual( 7 | expect.objectContaining({ 8 | type: 'input', 9 | message: '📦 Install command (use empty value to skip)', 10 | name: 'installCommand' 11 | }) 12 | ) 13 | }) 14 | 15 | it('should return undefined default answer when package manager is not defined', () => { 16 | const projectInfos = {} 17 | 18 | const result = askInstallCommand(projectInfos).default({ 19 | packageManager: undefined 20 | }) 21 | 22 | expect(result).toBeUndefined() 23 | }) 24 | 25 | it('should return correct default answer when package manager is defined', () => { 26 | const projectInfos = {} 27 | 28 | const result = askInstallCommand(projectInfos).default({ 29 | packageManager: 'yarn' 30 | }) 31 | 32 | expect(result).toEqual('yarn install') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /src/ask-overwrite.spec.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | 3 | const askOverwrite = require('./ask-overwrite') 4 | 5 | const expectedQuestion = { 6 | type: 'list', 7 | message: 8 | '🚨 readme-md-generator will overwrite your current README.md. Are you sure you want to continue? ', 9 | name: 'overwriteReadme', 10 | choices: [ 11 | { 12 | name: 'No', 13 | value: false 14 | }, 15 | { 16 | name: 'Yes ', 17 | value: true 18 | } 19 | ] 20 | } 21 | 22 | inquirer.prompt = jest.fn(items => 23 | Promise.resolve( 24 | items.reduce((result, item) => { 25 | result[item.name] = 'value' 26 | return result 27 | }, {}) 28 | ) 29 | ) 30 | 31 | describe('ask-overwrite', () => { 32 | beforeEach(() => { 33 | inquirer.prompt.mockClear() 34 | }) 35 | 36 | it('should call prompt right questions', async () => { 37 | await askOverwrite() 38 | expect(inquirer.prompt).toHaveBeenCalledWith([expectedQuestion]) 39 | }) 40 | 41 | it('should return the right value', async () => { 42 | expect(await askOverwrite()).toEqual('value') 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Franck Abgrall 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 | -------------------------------------------------------------------------------- /src/questions/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | module.exports = { 3 | askProjectName: require('./project-name'), 4 | askProjectVersion: require('./project-version'), 5 | askProjectDescription: require('./project-description'), 6 | askPackageManager: require('./package-manager'), 7 | askProjectHomepage: require('./project-homepage'), 8 | askProjectDemoUrl: require('./project-demo-url'), 9 | askProjectDocumentationUrl: require('./project-documentation-url'), 10 | askAuhtorName: require('./author-name'), 11 | askAuthorGithub: require('./author-github'), 12 | askAuthorWebsite: require('./author-website'), 13 | askAuthorTwitter: require('./author-twitter'), 14 | askAuthorLinkedIn: require('./author-linkedin'), 15 | askAuthorPatreon: require('./author-patreon'), 16 | askProjectPrerequisites: require('./project-prerequisites'), 17 | askLicenseName: require('./license-name'), 18 | askLicenseUrl: require('./license-url'), 19 | askIssuesUrl: require('./issues-url'), 20 | askContributingUrl: require('./contributing-url'), 21 | askInstallCommand: require('./install-command'), 22 | askUsage: require('./usage'), 23 | askTestCommand: require('./test-command') 24 | } 25 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | const readme = require('./readme') 2 | const infos = require('./project-infos') 3 | const utils = require('./utils') 4 | const askQuestions = require('./ask-questions') 5 | const cleanContext = require('./clean-context') 6 | 7 | /** 8 | * Main process: 9 | * 1) Check overwrite README.md 10 | * 2) Get README template path 11 | * 3) Gather project infos 12 | * 4) Ask user questions 13 | * 5) Clean answer context 14 | * 6) Build README content 15 | * 7) Create README.md file 16 | * 17 | * @param {Object} args 18 | */ 19 | module.exports = async ({ customTemplatePath, useDefaultAnswers }) => { 20 | if (!(await readme.checkOverwriteReadme())) return 21 | 22 | const templatePath = await readme.getReadmeTemplatePath( 23 | customTemplatePath, 24 | useDefaultAnswers 25 | ) 26 | const projectInformations = await infos.getProjectInfos() 27 | const answersContext = await askQuestions( 28 | projectInformations, 29 | useDefaultAnswers 30 | ) 31 | const cleanedContext = cleanContext(answersContext) 32 | const readmeContent = await readme.buildReadmeContent( 33 | cleanedContext, 34 | templatePath 35 | ) 36 | 37 | await readme.writeReadme(readmeContent) 38 | 39 | utils.showEndMessage() 40 | } 41 | -------------------------------------------------------------------------------- /src/questions/usage.spec.js: -------------------------------------------------------------------------------- 1 | const askUsage = require('./usage') 2 | 3 | describe('askUsage', () => { 4 | it('should return correct question format', () => { 5 | const result = askUsage() 6 | 7 | expect(result).toEqual( 8 | expect.objectContaining({ 9 | type: 'input', 10 | message: '🚀 Usage command or instruction (use empty value to skip)', 11 | name: 'usage' 12 | }) 13 | ) 14 | }) 15 | 16 | it('should return undefined default answer when package manager does not exists', () => { 17 | const projectInfos = { hasStartCommand: true } 18 | 19 | const result = askUsage(projectInfos).default({ 20 | packageManager: undefined 21 | }) 22 | 23 | expect(result).toBeUndefined() 24 | }) 25 | 26 | it('should return undefined default answer when start command does not exists', () => { 27 | const projectInfos = { hasStartCommand: false } 28 | 29 | const result = askUsage(projectInfos).default({ 30 | packageManager: 'yarn' 31 | }) 32 | 33 | expect(result).toBeUndefined() 34 | }) 35 | 36 | it('should return correct default answer when start command and packageManager exists', () => { 37 | const projectInfos = { hasStartCommand: true } 38 | 39 | const result = askUsage(projectInfos).default({ 40 | packageManager: 'yarn' 41 | }) 42 | 43 | expect(result).toEqual('yarn run start') 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/questions/test-command.spec.js: -------------------------------------------------------------------------------- 1 | const askTestCommand = require('./test-command') 2 | 3 | describe('askTestCommand', () => { 4 | it('should return correct question format', () => { 5 | const result = askTestCommand() 6 | expect(result).toEqual( 7 | expect.objectContaining({ 8 | type: 'input', 9 | message: '✅ Test command (use empty value to skip)', 10 | name: 'testCommand' 11 | }) 12 | ) 13 | }) 14 | 15 | it('should return undefined default answer when package manager does not exists', () => { 16 | const projectInfos = { hasTestCommand: true } 17 | 18 | const result = askTestCommand(projectInfos).default({ 19 | packageManager: undefined 20 | }) 21 | 22 | expect(result).toBeUndefined() 23 | }) 24 | 25 | it('should return undefined default answer when test command does not exists', () => { 26 | const projectInfos = { hasTestCommand: false } 27 | 28 | const result = askTestCommand(projectInfos).default({ 29 | packageManager: 'yarn' 30 | }) 31 | 32 | expect(result).toBeUndefined() 33 | }) 34 | 35 | it('should return correct default answer when start command and package manager exists', () => { 36 | const projectInfos = { hasTestCommand: true } 37 | 38 | const result = askTestCommand(projectInfos).default({ 39 | packageManager: 'yarn' 40 | }) 41 | 42 | expect(result).toEqual('yarn run test') 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /src/questions/license-url.spec.js: -------------------------------------------------------------------------------- 1 | const askLicenseUrl = require('./license-url') 2 | 3 | describe('askLicenseUrl', () => { 4 | it('should return correct question format', () => { 5 | const licenseUrl = 6 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE' 7 | const projectInfos = { licenseUrl } 8 | 9 | const result = askLicenseUrl(projectInfos) 10 | 11 | expect(result).toEqual( 12 | expect.objectContaining({ 13 | type: 'input', 14 | message: '📝 License url (use empty value to skip)', 15 | name: 'licenseUrl', 16 | default: licenseUrl 17 | }) 18 | ) 19 | }) 20 | 21 | it('should show this question if licenseName is defined', () => { 22 | const projectInfos = { 23 | licenseUrl: 24 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE' 25 | } 26 | const answersContext = { licenseName: 'MIT' } 27 | 28 | const question = askLicenseUrl(projectInfos) 29 | const result = question.when(answersContext) 30 | 31 | expect(result).toBe(true) 32 | }) 33 | 34 | it('should not show this question if licenseName is not defined', () => { 35 | const projectInfos = { 36 | licenseUrl: 37 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE' 38 | } 39 | const answersContext = {} 40 | 41 | const question = askLicenseUrl(projectInfos) 42 | const result = question.when(answersContext) 43 | 44 | expect(result).toBe(false) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /src/choose-template.spec.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const path = require('path') 3 | 4 | const chooseTemplate = require('./choose-template') 5 | 6 | const defaultTemplatePath = path.resolve(__dirname, '../templates/default.md') 7 | const defaultNoHtmlTemplatePath = path.resolve( 8 | __dirname, 9 | '../templates/default-no-html.md' 10 | ) 11 | 12 | inquirer.prompt = jest.fn(() => 13 | Promise.resolve({ templatePath: defaultTemplatePath }) 14 | ) 15 | 16 | describe('choose-template', () => { 17 | it('should return user choice', async () => { 18 | const result = await chooseTemplate(false) 19 | 20 | expect(result).toEqual(defaultTemplatePath) 21 | }) 22 | 23 | it('should return default template', async () => { 24 | const result = await chooseTemplate(true) 25 | 26 | expect(result).toEqual(defaultTemplatePath) 27 | }) 28 | 29 | it('should call prompt with correct parameters', async () => { 30 | await chooseTemplate(false) 31 | 32 | expect(inquirer.prompt).toHaveBeenNthCalledWith(1, [ 33 | { 34 | type: 'list', 35 | message: 36 | '🎨 Use HTML in your README.md for a nicer rendering? (not supported everywhere. ex: Bitbucket)', 37 | name: 'templatePath', 38 | choices: [ 39 | { 40 | name: 'Yes ', 41 | value: defaultTemplatePath 42 | }, 43 | { 44 | name: 'No', 45 | value: defaultNoHtmlTemplatePath 46 | } 47 | ] 48 | } 49 | ]) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /src/questions/package-manager.spec.js: -------------------------------------------------------------------------------- 1 | const askPackageManager = require('./package-manager') 2 | 3 | const expectedQuestion = { 4 | type: 'list', 5 | message: '📦 Choose Package Manager ', 6 | name: 'packageManager', 7 | choices: [ 8 | { 9 | name: 'npm', 10 | value: 'npm' 11 | }, 12 | { 13 | name: 'yarn', 14 | value: 'yarn' 15 | } 16 | ] 17 | } 18 | 19 | describe('askPackageManager', () => { 20 | it('should return correct question format when package manager is undefined', () => { 21 | const projectInfos = { packageManager: undefined } 22 | const result = askPackageManager(projectInfos) 23 | 24 | expect(result).toEqual(expect.objectContaining(expectedQuestion)) 25 | }) 26 | 27 | it('should not show question for a non JS Project', () => { 28 | const projectInfos = { isJSProject: false, packageManager: undefined } 29 | const result = askPackageManager(projectInfos).when(projectInfos) 30 | 31 | expect(result).toBe(false) 32 | }) 33 | 34 | it('should not show question when package manager has already been detected', () => { 35 | const projectInfos = { isJSProject: true, packageManager: 'yarn' } 36 | const result = askPackageManager(projectInfos).when(projectInfos) 37 | 38 | expect(result).toBe(false) 39 | }) 40 | 41 | it('should show question when package manager is undefined and if project is JS', () => { 42 | const projectInfos = { isJSProject: true, packageManager: undefined } 43 | const result = askPackageManager(projectInfos).when(projectInfos) 44 | 45 | expect(result).toBe(true) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /src/questions/author-website.spec.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | const askAuthorWebsite = require('./author-website') 3 | 4 | jest.mock('node-fetch') 5 | 6 | describe('askAuthorWebsite', () => { 7 | it('should return correct question format', () => { 8 | const authorWebsite = 'authorWebsite' 9 | const projectInfos = { authorWebsite } 10 | 11 | const result = askAuthorWebsite(projectInfos) 12 | 13 | expect(result).toEqual( 14 | expect.objectContaining({ 15 | type: 'input', 16 | message: '🏠 Author website (use empty value to skip)', 17 | name: 'authorWebsite' 18 | }) 19 | ) 20 | }) 21 | 22 | it('should return a new website url if user changes its github username with the previous question', async () => { 23 | const blog = 'https://www.new-website-url.com/' 24 | const projectInfos = { 25 | githubUsername: 'kefranabg', 26 | authorWebsite: 'https://www.franck-abgrall.me/' 27 | } 28 | 29 | fetch.mockReturnValueOnce( 30 | Promise.resolve({ 31 | json: () => Promise.resolve({ blog }) 32 | }) 33 | ) 34 | 35 | const result = await askAuthorWebsite(projectInfos).default({ 36 | authorGithubUsername: 'newGitHubUsername' 37 | }) 38 | 39 | expect(result).toEqual(blog) 40 | }) 41 | 42 | it('should return project infos website url if github username did not change', async () => { 43 | const projectInfos = { 44 | githubUsername: 'kefranabg', 45 | authorWebsite: 'https://www.franck-abgrall.me/' 46 | } 47 | 48 | const result = await askAuthorWebsite(projectInfos).default({ 49 | authorGithubUsername: 'kefranabg' 50 | }) 51 | 52 | expect(result).toEqual(projectInfos.authorWebsite) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "readme-md-generator", 3 | "version": "1.0.0", 4 | "description": "CLI that generates beautiful README.md files.", 5 | "main": "src/index.js", 6 | "bin": { 7 | "readme": "src/index.js" 8 | }, 9 | "dependencies": { 10 | "boxen": "^4.0.0", 11 | "date-fns": "^2.0.1", 12 | "ejs": "^3.0.1", 13 | "git-repo-name": "^1.0.1", 14 | "inquirer": "~7.0.0", 15 | "load-json-file": "^6.0.0", 16 | "lodash": "^4.17.11", 17 | "markdown-escape": "^1.0.2", 18 | "node-fetch": "^2.6.0", 19 | "ora": "4.0.3", 20 | "yargs": "^15.0.1" 21 | }, 22 | "devDependencies": { 23 | "codecov": "^3.5.0", 24 | "eslint": "^6.0.0", 25 | "eslint-config-airbnb-base": "^14.0.0", 26 | "eslint-plugin-import": "^2.17.3", 27 | "jest": "^24.8.0", 28 | "prettier": "^1.17.1" 29 | }, 30 | "scripts": { 31 | "lint": "eslint src", 32 | "prettier": "prettier \"**/*.{js,md,json}\"", 33 | "prettier:check": "npm run prettier -- --check", 34 | "prettier:fix": "npm run prettier -- --write", 35 | "start": "node src/index.js", 36 | "test": "jest", 37 | "test:ci": "jest --coverage && codecov" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/kefranabg/readme-md-generator.git" 42 | }, 43 | "keywords": [ 44 | "readme", 45 | "md", 46 | "cli", 47 | "generator", 48 | "template" 49 | ], 50 | "author": "Franck Abgrall", 51 | "license": "MIT", 52 | "bugs": { 53 | "url": "https://github.com/kefranabg/readme-md-generator/issues" 54 | }, 55 | "homepage": "https://github.com/kefranabg/readme-md-generator#readme", 56 | "publishConfig": { 57 | "registry": "https://npm.pkg.github.com/@kefranabg" 58 | }, 59 | "engines": { 60 | "npm": ">=5.5.0", 61 | "node": ">=9.3.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/questions/project-prerequisites.spec.js: -------------------------------------------------------------------------------- 1 | const askProjectPrerequisites = require('./project-prerequisites') 2 | 3 | describe('askProjectPrerequisites', () => { 4 | it('should return correct question format', () => { 5 | const engines = { 6 | npm: '>=5.5.0', 7 | node: '>= 9.3.0' 8 | } 9 | const projectInfos = { engines } 10 | 11 | const result = askProjectPrerequisites(projectInfos) 12 | 13 | expect(result).toEqual( 14 | expect.objectContaining({ 15 | type: 'checkbox', 16 | message: '⚠️ Project prerequisites', 17 | name: 'projectPrerequisites', 18 | choices: [ 19 | { 20 | checked: true, 21 | name: 'npm >=5.5.0', 22 | value: { name: 'npm', value: '>=5.5.0' } 23 | }, 24 | { 25 | checked: true, 26 | name: 'node >= 9.3.0', 27 | value: { name: 'node', value: '>= 9.3.0' } 28 | } 29 | ] 30 | }) 31 | ) 32 | }) 33 | 34 | it('should not show the question when engines property is empty object', () => { 35 | const projectInfos = { engines: {} } 36 | 37 | const question = askProjectPrerequisites(projectInfos) 38 | const result = question.when() 39 | 40 | expect(result).toEqual(false) 41 | }) 42 | 43 | it('should not show the question when engines property is not defined', () => { 44 | const projectInfos = {} 45 | 46 | const question = askProjectPrerequisites(projectInfos) 47 | const result = question.when() 48 | 49 | expect(result).toEqual(false) 50 | }) 51 | 52 | it('should show the question when engines property is defined and not empty', () => { 53 | const projectInfos = { 54 | engines: { 55 | node: '>=10' 56 | } 57 | } 58 | 59 | const question = askProjectPrerequisites(projectInfos) 60 | const result = question.when() 61 | 62 | expect(result).toEqual(true) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Readme Markdown Generator 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. 6 | Please note we have a [code of conduct](https://github.com/kefranabg/readme-md-generator/blob/master/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 7 | 8 | ## Table of Contents 9 | 10 | - [Setting Up the project locally](#setting-up-the-project-locally) 11 | - [Submitting a Pull Request](#submitting-a-pull-request) 12 | 13 | ## Setting Up the project locally 14 | 15 | To install the project you need to have `node` and `npm` 16 | 17 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone 18 | your fork: 19 | 20 | ```sh 21 | # Clone your fork 22 | git clone https://github.com//readme-md-generator.git 23 | 24 | # Navigate to the newly cloned directory 25 | cd readme-md-generator 26 | ``` 27 | 28 | 2. Your environment needs to be running `node` version >= 9.3.0 and `npm` version >= 5.5.0. 29 | 30 | 3. from the root of the project: `npm` to install all dependencies 31 | 32 | - make sure you have latest `npm` version 33 | 34 | 4. from the root of the project: `npm start` to run the cli. 35 | 36 | > Tip: Keep your `master` branch pointing at the original repository and make 37 | > pull requests from branches on your fork. To do this, run: 38 | > 39 | > ```sh 40 | > git remote add upstream https://github.com/kefranabg/readme-md-generator.git 41 | > git fetch upstream 42 | > git branch --set-upstream-to=upstream/master master 43 | > ``` 44 | > 45 | > This will add the original repository as a "remote" called "upstream," then 46 | > fetch the git information from that remote, then set your local `master` 47 | > branch to use the upstream master branch whenever you run `git pull`. Then you 48 | > can make all of your pull request branches based on this `master` branch. 49 | > Whenever you want to update your version of `master`, do a regular `git pull`. 50 | 51 | ## Submitting a Pull Request 52 | 53 | Please go through existing issues and pull requests to check if somebody else is already working on it. 54 | 55 | Also, make sure to run the tests and lint the code before you commit your 56 | changes. 57 | 58 | ```sh 59 | npm run test 60 | npm run lint 61 | ``` 62 | -------------------------------------------------------------------------------- /src/ask-questions.spec.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | 3 | const questions = require('./questions') 4 | const askQuestions = require('./ask-questions') 5 | 6 | inquirer.prompt = jest.fn(items => 7 | Promise.resolve( 8 | items.reduce((result, item) => { 9 | result[item.name] = 'value' 10 | return result 11 | }, {}) 12 | ) 13 | ) 14 | 15 | jest.mock('./questions', () => ({ 16 | askProjectName: jest.fn(() => ({ 17 | name: 'projectName', 18 | type: 'input', 19 | default: 'defaultProjectName' 20 | })), 21 | askProjectVersion: jest.fn(() => ({ 22 | name: 'projectVersion', 23 | type: 'input' 24 | })), 25 | askProjectDescription: jest.fn(() => ({ 26 | name: 'projectDescription', 27 | type: 'checkbox', 28 | choices: [ 29 | { value: { name: 'choiceOne', value: 1 }, checked: true }, 30 | { value: { name: 'choiceTwo', value: 2 }, checked: false } 31 | ] 32 | })) 33 | })) 34 | 35 | describe('ask-questions', () => { 36 | beforeEach(() => { 37 | inquirer.prompt.mockClear() 38 | }) 39 | 40 | it('should call all builder functions exported by questions', async () => { 41 | const projectInfos = { name: 'readme-md-generator' } 42 | 43 | await askQuestions(projectInfos, false) 44 | 45 | expect(questions.askProjectName).toHaveBeenCalledTimes(1) 46 | expect(questions.askProjectVersion).toHaveBeenCalledTimes(1) 47 | expect(questions.askProjectDescription).toHaveBeenCalledTimes(1) 48 | }) 49 | 50 | it('should use default values with --yes option', async () => { 51 | const result = await askQuestions({}, true) 52 | 53 | expect(inquirer.prompt).not.toHaveBeenCalled() 54 | expect(result).toEqual({ 55 | projectName: 'defaultProjectName', 56 | projectVersion: '', 57 | projectDescription: [{ name: 'choiceOne', value: 1 }], 58 | isGithubRepos: undefined, 59 | repositoryUrl: undefined, 60 | projectPrerequisites: undefined, 61 | isProjectOnNpm: false 62 | }) 63 | }) 64 | 65 | it('should return merged contexts', async () => { 66 | const projectInfos = { name: 'readme-md-generator' } 67 | 68 | const context = await askQuestions(projectInfos, false) 69 | 70 | expect(context).toEqual({ 71 | projectName: 'value', 72 | projectVersion: 'value', 73 | projectDescription: 'value', 74 | isGithubRepos: undefined, 75 | repositoryUrl: undefined, 76 | projectPrerequisites: undefined, 77 | isProjectOnNpm: true 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /src/readme.js: -------------------------------------------------------------------------------- 1 | const ejs = require('ejs') 2 | const ora = require('ora') 3 | const { promisify } = require('util') 4 | const { getYear } = require('date-fns') 5 | const fs = require('fs') 6 | const { isNil, unescape } = require('lodash') 7 | 8 | const chooseTemplate = require('./choose-template') 9 | const askOverwriteReadme = require('./ask-overwrite') 10 | 11 | const README_PATH = 'README.md' 12 | 13 | /** 14 | * Create readme file from the given readmeContent 15 | * 16 | * @param {string} readmeContent 17 | */ 18 | const writeReadme = async readmeContent => { 19 | const spinner = ora('Creating README').start() 20 | 21 | try { 22 | await promisify(fs.writeFile)(README_PATH, unescape(readmeContent)) 23 | spinner.succeed('README created') 24 | } catch (err) { 25 | spinner.fail('README creation fail') 26 | throw err 27 | } 28 | } 29 | 30 | /** 31 | * Get README template content from the given templatePath 32 | * 33 | * @param {string} templatePath 34 | */ 35 | const getReadmeTemplate = async templatePath => { 36 | const spinner = ora('Loading README template').start() 37 | 38 | try { 39 | const template = await promisify(fs.readFile)(templatePath, 'utf8') 40 | spinner.succeed('README template loaded') 41 | return template 42 | } catch (err) { 43 | spinner.fail('README template loading fail') 44 | throw err 45 | } 46 | } 47 | 48 | /** 49 | * Build README content with the given context and templatePath 50 | * 51 | * @param {Object} context 52 | * @param {string} templatePath 53 | */ 54 | const buildReadmeContent = async (context, templatePath) => { 55 | const currentYear = getYear(new Date()) 56 | const template = await getReadmeTemplate(templatePath) 57 | 58 | return ejs.render(template, { 59 | filename: templatePath, 60 | currentYear, 61 | ...context 62 | }) 63 | } 64 | 65 | /** 66 | * Validate template path 67 | * 68 | * @param {string} templatePath 69 | */ 70 | const validateReadmeTemplatePath = templatePath => { 71 | const spinner = ora('Resolving README template path').start() 72 | 73 | try { 74 | fs.lstatSync(templatePath).isFile() 75 | } catch (err) { 76 | spinner.fail(`The template path '${templatePath}' is not valid.`) 77 | throw err 78 | } 79 | 80 | spinner.succeed('README template path resolved') 81 | } 82 | 83 | /** 84 | * Get readme template path 85 | * (either a custom template, or a template that user will choose from prompt) 86 | * 87 | * @param {String} customTemplate 88 | */ 89 | const getReadmeTemplatePath = async (customTemplate, useDefaultAnswers) => { 90 | const templatePath = isNil(customTemplate) 91 | ? await chooseTemplate(useDefaultAnswers) 92 | : customTemplate 93 | 94 | validateReadmeTemplatePath(templatePath) 95 | 96 | return templatePath 97 | } 98 | 99 | /** 100 | * Check if readme generator can overwrite the existed readme 101 | */ 102 | const checkOverwriteReadme = () => 103 | !fs.existsSync(README_PATH) || askOverwriteReadme() 104 | 105 | module.exports = { 106 | writeReadme, 107 | buildReadmeContent, 108 | README_PATH, 109 | getReadmeTemplatePath, 110 | checkOverwriteReadme 111 | } 112 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contribute to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at abgrallkefran@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/cli.spec.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const mainProcess = require('./cli') 3 | const infos = require('./project-infos') 4 | const readme = require('./readme') 5 | const utils = require('./utils') 6 | const askQuestions = require('./ask-questions') 7 | const cleanContext = require('./clean-context') 8 | 9 | inquirer.prompt = jest.fn(items => 10 | Promise.resolve( 11 | items.reduce((result, item) => { 12 | result[item.name] = 'value' 13 | return result 14 | }, {}) 15 | ) 16 | ) 17 | 18 | jest.mock('./ask-questions', () => 19 | jest.fn(() => Promise.resolve({ projectName: 'readme-md-generator' })) 20 | ) 21 | 22 | jest.mock('./clean-context', () => 23 | jest.fn(() => ({ projectName: 'readme-md-generator-after-context-clean' })) 24 | ) 25 | 26 | jest.mock('./questions', () => ({ 27 | askProjectName: jest.fn(() => ({ 28 | name: 'projectName', 29 | type: 'input', 30 | default: 'defaultProjectName' 31 | })), 32 | askProjectVersion: jest.fn(() => ({ 33 | name: 'projectVersion', 34 | type: 'input' 35 | })), 36 | askProjectDescription: jest.fn(() => ({ 37 | name: 'projectDescription', 38 | type: 'checkbox', 39 | choices: [ 40 | { value: { name: 'choiceOne', value: 1 }, checked: true }, 41 | { value: { name: 'choiceTwo', value: 2 }, checked: false } 42 | ] 43 | })) 44 | })) 45 | 46 | describe('mainProcess', () => { 47 | afterEach(() => { 48 | askQuestions.mockClear() 49 | }) 50 | 51 | it('should stop immediatly if user dont want overwrite', async () => { 52 | const customTemplatePath = undefined 53 | const useDefaultAnswers = true 54 | infos.getProjectInfos = jest.fn() 55 | readme.buildReadmeContent = jest.fn() 56 | readme.getReadmeTemplatePath = jest.fn() 57 | readme.writeReadme = jest.fn() 58 | readme.checkOverwriteReadme = jest.fn(() => Promise.resolve(false)) 59 | utils.showEndMessage = jest.fn() 60 | 61 | await mainProcess({ customTemplatePath, useDefaultAnswers }) 62 | 63 | expect(infos.getProjectInfos).not.toHaveBeenCalled() 64 | expect(cleanContext).not.toHaveBeenCalled() 65 | expect(readme.buildReadmeContent).not.toHaveBeenCalled() 66 | expect(readme.getReadmeTemplatePath).not.toHaveBeenCalled() 67 | expect(readme.writeReadme).not.toHaveBeenCalled() 68 | expect(utils.showEndMessage).not.toHaveBeenCalled() 69 | }) 70 | 71 | it('should call main functions with correct args', async () => { 72 | const customTemplatePath = undefined 73 | const useDefaultAnswers = true 74 | const projectInformations = { name: 'readme-md-generator' } 75 | const readmeContent = 'content' 76 | const templatePath = 'path/to/template' 77 | infos.getProjectInfos = jest.fn(() => Promise.resolve(projectInformations)) 78 | readme.buildReadmeContent = jest.fn(() => Promise.resolve(readmeContent)) 79 | readme.getReadmeTemplatePath = jest.fn(() => Promise.resolve(templatePath)) 80 | readme.checkOverwriteReadme = jest.fn(() => Promise.resolve(true)) 81 | readme.writeReadme = jest.fn() 82 | utils.showEndMessage = jest.fn() 83 | 84 | await mainProcess({ customTemplatePath, useDefaultAnswers }) 85 | 86 | expect(readme.getReadmeTemplatePath).toHaveBeenNthCalledWith( 87 | 1, 88 | customTemplatePath, 89 | useDefaultAnswers 90 | ) 91 | expect(infos.getProjectInfos).toHaveBeenCalledTimes(1) 92 | expect(askQuestions).toHaveBeenNthCalledWith( 93 | 1, 94 | projectInformations, 95 | useDefaultAnswers 96 | ) 97 | expect(cleanContext).toHaveBeenNthCalledWith(1, { 98 | projectName: 'readme-md-generator' 99 | }) 100 | expect(readme.buildReadmeContent).toHaveBeenNthCalledWith( 101 | 1, 102 | { projectName: 'readme-md-generator-after-context-clean' }, 103 | templatePath 104 | ) 105 | expect(readme.writeReadme).toHaveBeenNthCalledWith(1, readmeContent) 106 | expect(utils.showEndMessage).toHaveBeenCalledTimes(1) 107 | }) 108 | }) 109 | -------------------------------------------------------------------------------- /templates/default-no-html.md: -------------------------------------------------------------------------------- 1 | # Welcome to <%= projectName %> 👋 2 | <% if (isProjectOnNpm) { -%> 3 | [![Version](https://img.shields.io/npm/v/<%= projectName %>.svg)](https://www.npmjs.com/package/<%= projectName %>) 4 | <% } -%> 5 | <% if (projectVersion && !isProjectOnNpm) { -%> 6 | ![Version](https://img.shields.io/badge/version-<%= projectVersion %>-blue.svg?cacheSeconds=2592000) 7 | <% } -%> 8 | <% if (projectPrerequisites) { -%> 9 | <% projectPrerequisites.map(({ name, value }) => { -%> 10 | ![Prerequisite](https://img.shields.io/badge/<%= name %>-<%= encodeURIComponent(value) %>-blue.svg) 11 | <% }) -%> 12 | <% } -%> 13 | <% if (projectDocumentationUrl) { -%> 14 | [![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](<%= projectDocumentationUrl %>) 15 | <% } -%> 16 | <% if (isGithubRepos) { -%> 17 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](<%= repositoryUrl %>/graphs/commit-activity) 18 | <% } -%> 19 | <% if (licenseName) { -%> 20 | [![License: <%= licenseName %>](https://img.shields.io/<%= isGithubRepos ? `github/license/${authorGithubUsername}/${projectName}` : `badge/License-${licenseName}-yellow.svg` %>)](<%= licenseUrl ? licenseUrl : '#' %>) 21 | <% } -%> 22 | <% if (authorTwitterUsername) { -%> 23 | [![Twitter: <%= authorTwitterUsername %>](https://img.shields.io/twitter/follow/<%= authorTwitterUsername %>.svg?style=social)](https://twitter.com/<%= authorTwitterUsername %>) 24 | <% } -%> 25 | <% if (projectDescription) { -%> 26 | 27 | > <%= projectDescription %> 28 | <% } -%> 29 | <% if (projectHomepage) { -%> 30 | 31 | ### 🏠 [Homepage](<%= projectHomepage %>) 32 | <% } -%> 33 | <% if (projectDemoUrl) { -%> 34 | 35 | ### ✨ [Demo](<%= projectDemoUrl %>) 36 | <% } -%> 37 | <% if (projectPrerequisites && projectPrerequisites.length) { -%> 38 | 39 | ## Prerequisites 40 | 41 | <% projectPrerequisites.map(({ name, value }) => { -%> 42 | - <%= name %> <%= value %> 43 | <% }) -%> 44 | <% } -%> 45 | <% if (installCommand) { -%> 46 | 47 | ## Install 48 | 49 | ```sh 50 | <%= installCommand %> 51 | ``` 52 | <% } -%> 53 | <% if (usage) { -%> 54 | 55 | ## Usage 56 | 57 | ```sh 58 | <%= usage %> 59 | ``` 60 | <% } -%> 61 | <% if (testCommand) { -%> 62 | 63 | ## Run tests 64 | 65 | ```sh 66 | <%= testCommand %> 67 | ``` 68 | <% } -%> 69 | <% if (authorName || authorTwitterUsername || authorGithubUsername) { -%> 70 | 71 | ## Author 72 | <% if (authorName) { %> 73 | 👤 **<%= authorName %>** 74 | <% } %> 75 | <% if (authorWebsite) { -%> 76 | * Website: <%= authorWebsite %> 77 | <% } -%> 78 | <% if (authorTwitterUsername) { -%> 79 | * Twitter: [@<%= authorTwitterUsername %>](https://twitter.com/<%= authorTwitterUsername %>) 80 | <% } -%> 81 | <% if (authorGithubUsername) { -%> 82 | * GitHub: [@<%= authorGithubUsername %>](https://github.com/<%= authorGithubUsername %>) 83 | <% } -%> 84 | <% if (authorLinkedInUsername) { -%> 85 | * LinkedIn: [@<%= authorLinkedInUsername %>](https://linkedin.com/in/<%= authorLinkedInUsername %>) 86 | <% } -%> 87 | <% } -%> 88 | <% if (issuesUrl) { -%> 89 | 90 | ## 🤝 Contributing 91 | 92 | Contributions, issues and feature requests are welcome! 93 | 94 | Feel free to check [issues page](<%= issuesUrl %>). <%= contributingUrl ? `You can also take a look at the [contributing guide](${contributingUrl}).` : '' %> 95 | <% } -%> 96 | 97 | ## Show your support 98 | 99 | Give a ⭐️ if this project helped you! 100 | <% if (authorPatreonUsername) { -%> 101 | 102 | [![support us](https://img.shields.io/badge/become-a patreon%20us-orange.svg?cacheSeconds=2592000)](https://www.patreon.com/<%= authorPatreonUsername %>) 103 | <% } -%> 104 | 105 | <% if (licenseName && licenseUrl) { -%> 106 | 107 | ## 📝 License 108 | 109 | <% if (authorName && authorGithubUsername) { -%> 110 | Copyright © <%= currentYear %> [<%= authorName %>](https://github.com/<%= authorGithubUsername %>). 111 | 112 | <% } -%> 113 | This project is [<%= licenseName %>](<%= licenseUrl %>) licensed. 114 | <% } -%> 115 | 116 | *** 117 | <%- include('footer.md'); -%> 118 | -------------------------------------------------------------------------------- /templates/default.md: -------------------------------------------------------------------------------- 1 |

Welcome to <%= projectName %> 👋

2 |

3 | <% if (isProjectOnNpm) { -%> 4 | 5 | Version 6 | 7 | <% } -%> 8 | <% if (projectVersion && !isProjectOnNpm) { -%> 9 | Version 10 | <% } -%> 11 | <% if (projectPrerequisites) { -%> 12 | <% projectPrerequisites.map(({ name, value }) => { -%> 13 | 14 | <% }) -%> 15 | <% } -%> 16 | <% if (projectDocumentationUrl) { -%> 17 | 18 | Documentation 19 | 20 | <% } -%> 21 | <% if (isGithubRepos) { -%> 22 | 23 | Maintenance 24 | 25 | <% } -%> 26 | <% if (licenseName) { -%> 27 | 28 | License: <%= licenseName %> 29 | 30 | <% } -%> 31 | <% if (authorTwitterUsername) { -%> 32 | 33 | Twitter: <%= authorTwitterUsername %> 34 | 35 | <% } -%> 36 |

37 | <% if (projectDescription) { -%> 38 | 39 | > <%= projectDescription %> 40 | <% } -%> 41 | <% if (projectHomepage) { -%> 42 | 43 | ### 🏠 [Homepage](<%= projectHomepage %>) 44 | <% } -%> 45 | <% if (projectDemoUrl) { -%> 46 | 47 | ### ✨ [Demo](<%= projectDemoUrl %>) 48 | <% } -%> 49 | <% if (projectPrerequisites && projectPrerequisites.length) { -%> 50 | 51 | ## Prerequisites 52 | 53 | <% projectPrerequisites.map(({ name, value }) => { -%> 54 | - <%= name %> <%= value %> 55 | <% }) -%> 56 | <% } -%> 57 | <% if (installCommand) { -%> 58 | 59 | ## Install 60 | 61 | ```sh 62 | <%= installCommand %> 63 | ``` 64 | <% } -%> 65 | <% if (usage) { -%> 66 | 67 | ## Usage 68 | 69 | ```sh 70 | <%= usage %> 71 | ``` 72 | <% } -%> 73 | <% if (testCommand) { -%> 74 | 75 | ## Run tests 76 | 77 | ```sh 78 | <%= testCommand %> 79 | ``` 80 | <% } -%> 81 | <% if (authorName || authorTwitterUsername || authorGithubUsername) { -%> 82 | 83 | ## Author 84 | <% if (authorName) { %> 85 | 👤 **<%= authorName %>** 86 | <% } %> 87 | <% if (authorWebsite) { -%> 88 | * Website: <%= authorWebsite %> 89 | <% } -%> 90 | <% if (authorTwitterUsername) { -%> 91 | * Twitter: [@<%= authorTwitterUsername %>](https://twitter.com/<%= authorTwitterUsername %>) 92 | <% } -%> 93 | <% if (authorGithubUsername) { -%> 94 | * GitHub: [@<%= authorGithubUsername %>](https://github.com/<%= authorGithubUsername %>) 95 | <% } -%> 96 | <% if (authorLinkedInUsername) { -%> 97 | * LinkedIn: [@<%= authorLinkedInUsername %>](https://linkedin.com/in/<%= authorLinkedInUsername %>) 98 | <% } -%> 99 | <% } -%> 100 | <% if (issuesUrl) { -%> 101 | 102 | ## 🤝 Contributing 103 | 104 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](<%= issuesUrl %>). <%= contributingUrl ? `You can also take a look at the [contributing guide](${contributingUrl}).` : '' %> 105 | <% } -%> 106 | 107 | ## Show your support 108 | 109 | Give a ⭐️ if this project helped you! 110 | <% if (authorPatreonUsername) { -%> 111 | 112 | 113 | 114 | 115 | <% } -%> 116 | <% if (licenseName && licenseUrl) { -%> 117 | 118 | ## 📝 License 119 | 120 | <% if (authorName && authorGithubUsername) { -%> 121 | Copyright © <%= currentYear %> [<%= authorName %>](https://github.com/<%= authorGithubUsername %>).
122 | <% } -%> 123 | This project is [<%= licenseName %>](<%= licenseUrl %>) licensed. 124 | <% } -%> 125 | 126 | *** 127 | <%- include('footer.md'); -%> 128 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const loadJsonFile = require('load-json-file') 2 | const isNil = require('lodash/isNil') 3 | const isEmpty = require('lodash/isEmpty') 4 | const boxen = require('boxen') 5 | const path = require('path') 6 | const getReposName = require('git-repo-name') 7 | const fetch = require('node-fetch') 8 | const fs = require('fs') 9 | const escapeMarkdown = require('markdown-escape') 10 | const { execSync } = require('child_process') 11 | 12 | const END_MSG = `README.md was successfully generated. 13 | Thanks for using readme-md-generator!` 14 | 15 | const GITHUB_API_URL = 'https://api.github.com' 16 | 17 | const BOXEN_CONFIG = { 18 | padding: 1, 19 | margin: { top: 2, bottom: 3 }, 20 | borderColor: 'cyan', 21 | align: 'center', 22 | borderStyle: 'double' 23 | } 24 | 25 | /** 26 | * Display end message 27 | */ 28 | const showEndMessage = () => process.stdout.write(boxen(END_MSG, BOXEN_CONFIG)) 29 | 30 | /** 31 | * Get package json name property 32 | * 33 | * @param {Object} packageJson 34 | */ 35 | const getPackageJsonName = (packageJson = {}) => packageJson.name || undefined 36 | /** 37 | * Get git repository name 38 | * 39 | * @param {String} cwd 40 | */ 41 | const getGitRepositoryName = cwd => { 42 | try { 43 | return getReposName.sync({ cwd }) 44 | // eslint-disable-next-line no-empty 45 | } catch (err) { 46 | return undefined 47 | } 48 | } 49 | 50 | /** 51 | * Get project name 52 | */ 53 | const getProjectName = packageJson => { 54 | const cwd = process.cwd() 55 | return ( 56 | getPackageJsonName(packageJson) || 57 | getGitRepositoryName(cwd) || 58 | path.basename(cwd) 59 | ) 60 | } 61 | 62 | /** 63 | * Get package.json content 64 | */ 65 | const getPackageJson = async () => { 66 | try { 67 | return await loadJsonFile('package.json') 68 | } catch (err) { 69 | return undefined 70 | } 71 | } 72 | 73 | /** 74 | * Get the default answer depending on the question type 75 | * 76 | * @param {Object} question 77 | */ 78 | const getDefaultAnswer = async (question, answersContext) => { 79 | if (question.when && !question.when(answersContext)) return undefined 80 | 81 | switch (question.type) { 82 | case 'input': 83 | return typeof question.default === 'function' 84 | ? question.default(answersContext) 85 | : question.default || '' 86 | case 'checkbox': 87 | return question.choices 88 | .filter(choice => choice.checked) 89 | .map(choice => choice.value) 90 | default: 91 | return undefined 92 | } 93 | } 94 | 95 | /** 96 | * Return true if the project is available on NPM, return false otherwise. 97 | * 98 | * @param projectName 99 | * @returns boolean 100 | */ 101 | const isProjectAvailableOnNpm = projectName => { 102 | try { 103 | execSync(`npm view ${projectName}`, { stdio: 'ignore' }) 104 | return true 105 | } catch (err) { 106 | return false 107 | } 108 | } 109 | 110 | /** 111 | * Get default question's answers 112 | * 113 | * @param {Array} questions 114 | */ 115 | const getDefaultAnswers = questions => 116 | questions.reduce(async (answersContextProm, question) => { 117 | const answersContext = await answersContextProm 118 | 119 | return { 120 | ...answersContext, 121 | [question.name]: await getDefaultAnswer(question, answersContext) 122 | } 123 | }, Promise.resolve({})) 124 | 125 | /** 126 | * Clean social network username by removing the @ prefix and 127 | * escaping markdown characters 128 | * 129 | * @param input social network username input 130 | * @returns {*} escaped input without the prefix 131 | */ 132 | const cleanSocialNetworkUsername = input => 133 | escapeMarkdown(input.replace(/^@/, '')) 134 | 135 | /** 136 | * Get author's website from Github API 137 | * 138 | * @param {string} githubUsername 139 | * @returns {string} authorWebsite 140 | */ 141 | const getAuthorWebsiteFromGithubAPI = async githubUsername => { 142 | try { 143 | const userData = await fetch( 144 | `${GITHUB_API_URL}/users/${githubUsername}` 145 | ).then(res => res.json()) 146 | const authorWebsite = userData.blog 147 | return isNil(authorWebsite) || isEmpty(authorWebsite) 148 | ? undefined 149 | : authorWebsite 150 | } catch (err) { 151 | return undefined 152 | } 153 | } 154 | 155 | /** 156 | * Returns a boolean whether a file exists or not 157 | * 158 | * @param {String} filepath 159 | * @returns {Boolean} 160 | */ 161 | const doesFileExist = filepath => { 162 | try { 163 | return fs.existsSync(filepath) 164 | } catch (err) { 165 | return false 166 | } 167 | } 168 | 169 | /** 170 | * Returns the package manager from the lock file 171 | * 172 | * @returns {String} packageManger or undefined 173 | */ 174 | const getPackageManagerFromLockFile = () => { 175 | const packageLockExists = doesFileExist('package-lock.json') 176 | const yarnLockExists = doesFileExist('yarn.lock') 177 | 178 | if (packageLockExists && yarnLockExists) return undefined 179 | if (packageLockExists) return 'npm' 180 | if (yarnLockExists) return 'yarn' 181 | return undefined 182 | } 183 | 184 | module.exports = { 185 | getPackageJson, 186 | showEndMessage, 187 | getProjectName, 188 | END_MSG, 189 | BOXEN_CONFIG, 190 | getDefaultAnswers, 191 | getDefaultAnswer, 192 | cleanSocialNetworkUsername, 193 | isProjectAvailableOnNpm, 194 | getAuthorWebsiteFromGithubAPI, 195 | getPackageManagerFromLockFile, 196 | doesFileExist 197 | } 198 | -------------------------------------------------------------------------------- /src/project-infos.js: -------------------------------------------------------------------------------- 1 | const isNil = require('lodash/isNil') 2 | const get = require('lodash/get') 3 | const has = require('lodash/has') 4 | const ora = require('ora') 5 | const { execSync } = require('child_process') 6 | 7 | const { 8 | getPackageJson, 9 | getProjectName, 10 | getAuthorWebsiteFromGithubAPI, 11 | getPackageManagerFromLockFile 12 | } = require('./utils') 13 | 14 | const GITHUB_URL = 'https://github.com/' 15 | 16 | /** 17 | * Clean repository url by removing '.git' and 'git+' 18 | * 19 | * @param {string} reposUrl 20 | */ 21 | const cleanReposUrl = reposUrl => 22 | reposUrl 23 | .replace('\n', '') 24 | .replace('git+', '') 25 | .replace('.git', '') 26 | 27 | /** 28 | * Get repository url from pakage json 29 | * 30 | * @param {Object} reposUrl 31 | */ 32 | const getReposUrlFromPackageJson = async packageJson => { 33 | const reposUrl = get(packageJson, 'repository.url', undefined) 34 | return isNil(reposUrl) ? undefined : cleanReposUrl(reposUrl) 35 | } 36 | 37 | /** 38 | * Get repository url from git 39 | */ 40 | const getReposUrlFromGit = () => { 41 | try { 42 | const stdout = execSync('git config --get remote.origin.url') 43 | return cleanReposUrl(stdout) 44 | } catch (err) { 45 | return undefined 46 | } 47 | } 48 | 49 | /** 50 | * Get repository url from package.json or git 51 | * 52 | * @param {Object} packageJson 53 | */ 54 | const getReposUrl = async packageJson => 55 | (await getReposUrlFromPackageJson(packageJson)) || getReposUrlFromGit() 56 | 57 | /** 58 | * Get repository issues url from package.json or git 59 | * 60 | * @param {Object} packageJson 61 | */ 62 | const getReposIssuesUrl = async packageJson => { 63 | let reposIssuesUrl = get(packageJson, 'bugs.url', undefined) 64 | 65 | if (isNil(reposIssuesUrl)) { 66 | const reposUrl = await getReposUrl() 67 | 68 | if (!isNil(reposUrl)) { 69 | reposIssuesUrl = `${reposUrl}/issues` 70 | } 71 | } 72 | 73 | return reposIssuesUrl 74 | } 75 | 76 | /** 77 | * Check if repository is a Github repository 78 | * 79 | * @param {string} repositoryUrl 80 | */ 81 | const isGithubRepository = repositoryUrl => 82 | !isNil(repositoryUrl) && repositoryUrl.includes(GITHUB_URL) 83 | 84 | /** 85 | * Get github username from repository url 86 | * 87 | * @param {string} repositoryUrl 88 | */ 89 | const getGithubUsernameFromRepositoryUrl = repositoryUrl => 90 | repositoryUrl.replace(GITHUB_URL, '').split('/')[0] 91 | 92 | /** 93 | * Get license url from github repository url 94 | * 95 | * @param {string} repositoryUrl 96 | */ 97 | const getLicenseUrlFromGithubRepositoryUrl = repositoryUrl => 98 | `${repositoryUrl}/blob/master/LICENSE` 99 | 100 | const getReadmeUrlFromGithubRepositoryUrl = repositoryUrl => 101 | `${repositoryUrl}#readme` 102 | 103 | const getContributingUrlFromRepositoryUrl = repositoryUrl => 104 | `${repositoryUrl}/blob/master/CONTRIBUTING.md` 105 | 106 | /** 107 | * Get project author name from package.json 108 | * 109 | * @param packageJson 110 | * @returns {string} authorName 111 | */ 112 | const getAuthorName = packageJson => { 113 | if (has(packageJson, 'author.name')) { 114 | return get(packageJson, 'author.name', undefined) 115 | } 116 | 117 | if (has(packageJson, 'author') && typeof packageJson.author === 'string') { 118 | return get(packageJson, 'author', undefined) 119 | } 120 | 121 | return undefined 122 | } 123 | 124 | /** 125 | * Get project informations from git and package.json 126 | */ 127 | const getProjectInfos = async () => { 128 | const spinner = ora('Gathering project infos').start() 129 | 130 | const packageJson = await getPackageJson() 131 | const isJSProject = !!packageJson 132 | const packageManager = isJSProject 133 | ? getPackageManagerFromLockFile() 134 | : undefined 135 | const name = getProjectName(packageJson) 136 | const description = get(packageJson, 'description', undefined) 137 | const engines = get(packageJson, 'engines', undefined) 138 | const author = getAuthorName(packageJson) 139 | const version = get(packageJson, 'version', undefined) 140 | const licenseName = get(packageJson, 'license', undefined) 141 | const homepage = get(packageJson, 'homepage', undefined) 142 | const hasStartCommand = has(packageJson, 'scripts.start') 143 | const hasTestCommand = has(packageJson, 'scripts.test') 144 | const repositoryUrl = await getReposUrl(packageJson) 145 | const issuesUrl = await getReposIssuesUrl(packageJson) 146 | const isGithubRepos = isGithubRepository(repositoryUrl) 147 | const contributingUrl = repositoryUrl 148 | ? getContributingUrlFromRepositoryUrl(repositoryUrl) 149 | : undefined 150 | const documentationUrl = isGithubRepos 151 | ? getReadmeUrlFromGithubRepositoryUrl(repositoryUrl) 152 | : undefined 153 | const githubUsername = isGithubRepos 154 | ? getGithubUsernameFromRepositoryUrl(repositoryUrl) 155 | : undefined 156 | const authorWebsite = githubUsername 157 | ? await getAuthorWebsiteFromGithubAPI(githubUsername) 158 | : undefined 159 | const licenseUrl = isGithubRepos 160 | ? getLicenseUrlFromGithubRepositoryUrl(repositoryUrl) 161 | : undefined 162 | 163 | spinner.succeed('Project infos gathered') 164 | 165 | return { 166 | name, 167 | description, 168 | version, 169 | author, 170 | authorWebsite, 171 | homepage, 172 | repositoryUrl, 173 | issuesUrl, 174 | contributingUrl, 175 | githubUsername, 176 | engines, 177 | licenseName, 178 | licenseUrl, 179 | documentationUrl, 180 | isGithubRepos, 181 | hasStartCommand, 182 | hasTestCommand, 183 | isJSProject, 184 | packageManager 185 | } 186 | } 187 | 188 | module.exports = { 189 | getProjectInfos 190 | } 191 | -------------------------------------------------------------------------------- /src/__snapshots__/readme.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`readme buildReadmeContent should return readme default template content 1`] = ` 4 | "

Welcome to readme-md-generator 👋

5 |

6 | 7 | \\"Version\\" 8 | 9 | 10 | 11 | 12 | \\"Documentation\\" 13 | 14 | 15 | \\"Maintenance\\" 16 | 17 | 18 | \\"License: 19 | 20 | 21 | \\"Twitter: 22 | 23 |

24 | 25 | > Generates beautiful README files from git config & package.json infos 26 | 27 | ### 🏠 [Homepage](https://github.com/kefranabg/readme-md-generator#readme) 28 | 29 | ### ✨ [Demo](https://github.com/kefranabg/readme-md-generator#-demo) 30 | 31 | ## Prerequisites 32 | 33 | - npm >=5.5.0 34 | - node >= 9.3.0 35 | 36 | ## Install 37 | 38 | \`\`\`sh 39 | npm install 40 | \`\`\` 41 | 42 | ## Usage 43 | 44 | \`\`\`sh 45 | npm start 46 | \`\`\` 47 | 48 | ## Run tests 49 | 50 | \`\`\`sh 51 | npm run test 52 | \`\`\` 53 | 54 | ## Author 55 | 56 | 👤 **Franck Abgrall** 57 | 58 | * Website: https://www.franck-abgrall.me/ 59 | * Twitter: [@FranckAbgrall](https://twitter.com/FranckAbgrall) 60 | * GitHub: [@kefranabg](https://github.com/kefranabg) 61 | * LinkedIn: [@franckabgrall](https://linkedin.com/in/franckabgrall) 62 | 63 | ## 🤝 Contributing 64 | 65 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/kefranabg/readme-md-generator/issues). You can also take a look at the [contributing guide](https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md). 66 | 67 | ## Show your support 68 | 69 | Give a ⭐️ if this project helped you! 70 | 71 | 72 | 73 | 74 | 75 | ## 📝 License 76 | 77 | Copyright © 2019 [Franck Abgrall](https://github.com/kefranabg).
78 | This project is [MIT](https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE) licensed. 79 | 80 | *** 81 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_" 82 | `; 83 | 84 | exports[`readme buildReadmeContent should return readme default template no html content 1`] = ` 85 | "# Welcome to readme-md-generator 👋 86 | [![Version](https://img.shields.io/npm/v/readme-md-generator.svg)](https://www.npmjs.com/package/readme-md-generator) 87 | ![Prerequisite](https://img.shields.io/badge/npm-%3E%3D5.5.0-blue.svg) 88 | ![Prerequisite](https://img.shields.io/badge/node-%3E%3D%209.3.0-blue.svg) 89 | [![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://github.com/kefranabg/readme-md-generator#readme) 90 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/kefranabg/readme-md-generator/graphs/commit-activity) 91 | [![License: MIT](https://img.shields.io/github/license/kefranabg/readme-md-generator)](https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE) 92 | [![Twitter: FranckAbgrall](https://img.shields.io/twitter/follow/FranckAbgrall.svg?style=social)](https://twitter.com/FranckAbgrall) 93 | 94 | > Generates beautiful README files from git config & package.json infos 95 | 96 | ### 🏠 [Homepage](https://github.com/kefranabg/readme-md-generator#readme) 97 | 98 | ### ✨ [Demo](https://github.com/kefranabg/readme-md-generator#-demo) 99 | 100 | ## Prerequisites 101 | 102 | - npm >=5.5.0 103 | - node >= 9.3.0 104 | 105 | ## Install 106 | 107 | \`\`\`sh 108 | npm install 109 | \`\`\` 110 | 111 | ## Usage 112 | 113 | \`\`\`sh 114 | npm start 115 | \`\`\` 116 | 117 | ## Run tests 118 | 119 | \`\`\`sh 120 | npm run test 121 | \`\`\` 122 | 123 | ## Author 124 | 125 | 👤 **Franck Abgrall** 126 | 127 | * Website: https://www.franck-abgrall.me/ 128 | * Twitter: [@FranckAbgrall](https://twitter.com/FranckAbgrall) 129 | * GitHub: [@kefranabg](https://github.com/kefranabg) 130 | * LinkedIn: [@franckabgrall](https://linkedin.com/in/franckabgrall) 131 | 132 | ## 🤝 Contributing 133 | 134 | Contributions, issues and feature requests are welcome! 135 | 136 | Feel free to check [issues page](https://github.com/kefranabg/readme-md-generator/issues). You can also take a look at the [contributing guide](https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md). 137 | 138 | ## Show your support 139 | 140 | Give a ⭐️ if this project helped you! 141 | 142 | [![support us](https://img.shields.io/badge/become-a patreon%20us-orange.svg?cacheSeconds=2592000)](https://www.patreon.com/FranckAbgrall) 143 | 144 | 145 | ## 📝 License 146 | 147 | Copyright © 2019 [Franck Abgrall](https://github.com/kefranabg). 148 | 149 | This project is [MIT](https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE) licensed. 150 | 151 | *** 152 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_" 153 | `; 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to readme-md-generator 👋

2 |

3 | 4 | 5 | downloads 6 | 7 | 8 | License: MIT 9 | 10 | 11 | 12 | 13 | 14 | gitmoji-changelog 15 | 16 | 17 | Twitter: FranckAbgrall 18 | 19 |

20 | 21 | > CLI that generates beautiful README.md files.
`readme-md-generator` will suggest you default answers by reading your `package.json` and `git` configuration. 22 | 23 | ## ✨ Demo 24 | 25 | `readme-md-generator` is able to read your environment (package.json, git config...) to suggest you default answers during the `README.md` creation process: 26 | 27 |

28 | demo 29 |

30 | 31 | Generated `README.md`: 32 | 33 |

34 | cli output 35 |

36 | 37 | Example of `package.json` with good meta data: 38 | 39 | ```json 40 | // The package.json is not required to run README-MD-GENERATOR 41 | { 42 | "name": "readme-md-generator", 43 | "version": "0.1.3", 44 | "description": "CLI that generates beautiful README.md files.", 45 | "author": "Franck Abgrall", 46 | "license": "MIT", 47 | "homepage": "https://github.com/kefranabg/readme-md-generator#readme", 48 | "repository": { 49 | "type": "git", 50 | "url": "git+https://github.com/kefranabg/readme-md-generator.git" 51 | }, 52 | "bugs": { 53 | "url": "https://github.com/kefranabg/readme-md-generator/issues" 54 | }, 55 | "engines": { 56 | "npm": ">=5.5.0", 57 | "node": ">=9.3.0" 58 | } 59 | } 60 | ``` 61 | 62 | ## 🚀 Usage 63 | 64 | Make sure you have [npx](https://www.npmjs.com/package/npx) installed (`npx` is shipped by default since npm `5.2.0`) 65 | 66 | Just run the following command at the root of your project and answer questions: 67 | 68 | ```sh 69 | npx readme-md-generator 70 | ``` 71 | 72 | Or use default values for all questions (`-y`): 73 | 74 | ```sh 75 | npx readme-md-generator -y 76 | ``` 77 | 78 | Use your own `ejs` README template (`-p`): 79 | 80 | ```sh 81 | npx readme-md-generator -p path/to/my/own/template.md 82 | ``` 83 | 84 | You can find [ejs README template examples here](https://github.com/kefranabg/readme-md-generator/tree/master/templates). 85 | 86 | ## Code Contributors 87 | 88 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. 89 | 90 | 91 | ## Financial Contributors 92 | 93 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/readme-md-generator/contribute)] 94 | 95 | ### Individuals 96 | 97 | 98 | 99 | ### Organizations 100 | 101 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/readme-md-generator/contribute)] 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ## 🤝 Contributing 114 | 115 | Contributions, issues and feature requests are welcome.
116 | Feel free to check [issues page](https://github.com/kefranabg/readme-md-generator/issues) if you want to contribute.
117 | [Check the contributing guide](./CONTRIBUTING.md).
118 | 119 | ## Author 120 | 121 | 👤 **Franck Abgrall** 122 | 123 | - Twitter: [@FranckAbgrall](https://twitter.com/FranckAbgrall) 124 | - Github: [@kefranabg](https://github.com/kefranabg) 125 | 126 | ## Show your support 127 | 128 | Please ⭐️ this repository if this project helped you! 129 | 130 | 131 | 132 | 133 | 134 | ## 📝 License 135 | 136 | Copyright © 2019 [Franck Abgrall](https://github.com/kefranabg).
137 | This project is [MIT](https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE) licensed. 138 | 139 | --- 140 | 141 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 142 | -------------------------------------------------------------------------------- /src/readme.spec.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const ora = require('ora') 3 | const path = require('path') 4 | const chooseTemplate = require('./choose-template') 5 | const askOverwriteReadme = require('./ask-overwrite') 6 | 7 | const defaultTemplatePath = path.resolve(__dirname, '../templates/default.md') 8 | const defaultNoHtmlTemplatePath = path.resolve( 9 | __dirname, 10 | '../templates/default-no-html.md' 11 | ) 12 | chooseTemplate.mockReturnValue(defaultTemplatePath) 13 | 14 | const { 15 | writeReadme, 16 | buildReadmeContent, 17 | README_PATH, 18 | getReadmeTemplatePath, 19 | checkOverwriteReadme 20 | } = require('./readme') 21 | 22 | describe('readme', () => { 23 | const succeed = jest.fn() 24 | const fail = jest.fn() 25 | 26 | ora.mockReturnValue({ 27 | start: () => ({ 28 | succeed, 29 | fail 30 | }) 31 | }) 32 | 33 | afterEach(() => { 34 | jest.clearAllMocks() 35 | }) 36 | 37 | describe('writeReadme', () => { 38 | it('should call ora with correct parameters in success case', async () => { 39 | const readmeContent = 'content' 40 | fs.writeFile = jest.fn((_, __, cb) => cb(null, 'done')) 41 | 42 | await writeReadme(readmeContent) 43 | 44 | expect(ora).toHaveBeenCalledTimes(1) 45 | expect(ora).toHaveBeenCalledWith('Creating README') 46 | expect(succeed).toHaveBeenCalledTimes(1) 47 | expect(succeed).toHaveBeenCalledWith('README created') 48 | }) 49 | 50 | it('should call ora with correct parameters in fail case', async () => { 51 | const readmeContent = 'content' 52 | fs.writeFile = jest.fn(() => { 53 | throw new Error('error') 54 | }) 55 | 56 | try { 57 | await writeReadme(readmeContent) 58 | // eslint-disable-next-line no-empty 59 | } catch (err) {} 60 | 61 | expect(ora).toHaveBeenCalledTimes(1) 62 | expect(ora).toHaveBeenCalledWith('Creating README') 63 | expect(fail).toHaveBeenCalledTimes(1) 64 | expect(fail).toHaveBeenCalledWith('README creation fail') 65 | }) 66 | 67 | it('should call writeFile with correct parameters', async () => { 68 | const readmeContent = 'John & Bryan' 69 | fs.writeFile = jest.fn((_, __, cb) => cb(null, 'done')) 70 | 71 | await writeReadme(readmeContent) 72 | 73 | expect(fs.writeFile).toHaveBeenCalledTimes(1) 74 | expect(fs.writeFile.mock.calls[0][0]).toBe(README_PATH) 75 | expect(fs.writeFile.mock.calls[0][1]).toBe('John & Bryan') 76 | }) 77 | }) 78 | 79 | describe('buildReadmeContent', () => { 80 | const context = { 81 | isGithubRepos: true, 82 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 83 | projectPrerequisites: [ 84 | { name: 'npm', value: '>=5.5.0' }, 85 | { name: 'node', value: '>= 9.3.0' } 86 | ], 87 | projectName: 'readme-md-generator', 88 | projectVersion: '0.1.3', 89 | projectDescription: 90 | 'Generates beautiful README files from git config & package.json infos', 91 | projectDocumentationUrl: 92 | 'https://github.com/kefranabg/readme-md-generator#readme', 93 | projectHomepage: 94 | 'https://github.com/kefranabg/readme-md-generator#readme', 95 | projectDemoUrl: 'https://github.com/kefranabg/readme-md-generator#-demo', 96 | authorName: 'Franck Abgrall', 97 | authorWebsite: 'https://www.franck-abgrall.me/', 98 | authorGithubUsername: 'kefranabg', 99 | authorTwitterUsername: 'FranckAbgrall', 100 | authorLinkedInUsername: 'franckabgrall', 101 | authorPatreonUsername: 'FranckAbgrall', 102 | licenseName: 'MIT', 103 | licenseUrl: 104 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 105 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 106 | contributingUrl: 107 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 108 | installCommand: 'npm install', 109 | usage: 'npm start', 110 | testCommand: 'npm run test', 111 | isProjectOnNpm: true 112 | } 113 | 114 | afterEach(() => { 115 | jest.clearAllMocks() 116 | }) 117 | 118 | it('should call ora with correct parameters in success case', async () => { 119 | await buildReadmeContent(context, defaultTemplatePath) 120 | 121 | expect(ora).toHaveBeenCalledTimes(1) 122 | expect(ora).toHaveBeenCalledWith('Loading README template') 123 | expect(succeed).toHaveBeenCalledTimes(1) 124 | expect(succeed).toHaveBeenCalledWith('README template loaded') 125 | }) 126 | 127 | it('should return readme default template content', async () => { 128 | const result = await buildReadmeContent(context, defaultTemplatePath) 129 | 130 | expect(result).toMatchSnapshot() 131 | }) 132 | 133 | it('should return readme default template no html content', async () => { 134 | const result = await buildReadmeContent( 135 | context, 136 | defaultNoHtmlTemplatePath 137 | ) 138 | 139 | expect(result).toMatchSnapshot() 140 | }) 141 | 142 | it('should call ora with correct parameters in fail case', async () => { 143 | fs.readFile = jest.fn(() => { 144 | throw new Error('error') 145 | }) 146 | 147 | try { 148 | await buildReadmeContent(context, defaultTemplatePath) 149 | // eslint-disable-next-line no-empty 150 | } catch (err) {} 151 | 152 | expect(ora).toHaveBeenCalledTimes(1) 153 | expect(ora).toHaveBeenCalledWith('Loading README template') 154 | expect(fail).toHaveBeenCalledTimes(1) 155 | expect(fail).toHaveBeenCalledWith('README template loading fail') 156 | }) 157 | }) 158 | 159 | describe('getReadmeTemplatePath', () => { 160 | it('should return template that user has selected', async () => { 161 | const useDefaultAnswers = false 162 | const actualResult = await getReadmeTemplatePath( 163 | undefined, 164 | useDefaultAnswers 165 | ) 166 | 167 | expect(actualResult).toEqual(defaultTemplatePath) 168 | expect(chooseTemplate).toHaveBeenNthCalledWith(1, useDefaultAnswers) 169 | }) 170 | 171 | it('should return custom template path if customTemplatePath is defined', async () => { 172 | const customTemplatePath = defaultTemplatePath 173 | 174 | const actualResult = await getReadmeTemplatePath( 175 | customTemplatePath, 176 | false 177 | ) 178 | 179 | expect(actualResult).toEqual(customTemplatePath) 180 | expect(chooseTemplate).not.toHaveBeenCalled() 181 | }) 182 | 183 | it('should throw an error if customTemplate is defined but invalid', () => { 184 | const wrongPath = 'wrong path' 185 | 186 | expect(getReadmeTemplatePath(wrongPath, false)).rejects.toThrow() 187 | }) 188 | 189 | it('should call ora with correct parameters in fail case', async () => { 190 | const wrongPath = 'wrong path' 191 | 192 | try { 193 | await getReadmeTemplatePath(wrongPath, false) 194 | // eslint-disable-next-line no-empty 195 | } catch (err) {} 196 | 197 | expect(ora).toHaveBeenNthCalledWith(1, 'Resolving README template path') 198 | expect(fail).toHaveBeenNthCalledWith( 199 | 1, 200 | "The template path 'wrong path' is not valid." 201 | ) 202 | }) 203 | 204 | it('should call ora with correct parameters in success case', async () => { 205 | await getReadmeTemplatePath(defaultTemplatePath, false) 206 | 207 | expect(ora).toHaveBeenNthCalledWith(1, 'Resolving README template path') 208 | expect(succeed).toHaveBeenNthCalledWith( 209 | 1, 210 | 'README template path resolved' 211 | ) 212 | }) 213 | }) 214 | 215 | describe('checkOverwrite', () => { 216 | it('should return true if README does not exist', async () => { 217 | fs.existsSync = jest.fn(p => p !== README_PATH) 218 | expect(await checkOverwriteReadme()).toEqual(true) 219 | }) 220 | it('should return true if README exist and user want to overwrite it', async () => { 221 | fs.existsSync = jest.fn(p => p === README_PATH) 222 | askOverwriteReadme.mockResolvedValue(true) 223 | expect(await checkOverwriteReadme()).toEqual(true) 224 | }) 225 | it('should return false if README exist and user dont want to overwrite it', async () => { 226 | fs.existsSync = jest.fn(p => p === README_PATH) 227 | askOverwriteReadme.mockResolvedValue(false) 228 | expect(await checkOverwriteReadme()).toEqual(false) 229 | }) 230 | }) 231 | }) 232 | 233 | jest.mock('ora') 234 | jest.mock('./choose-template') 235 | jest.mock('./ask-overwrite') 236 | -------------------------------------------------------------------------------- /src/utils.spec.js: -------------------------------------------------------------------------------- 1 | const loadJsonFile = require('load-json-file') 2 | const boxen = require('boxen') 3 | const path = require('path') 4 | const getReposName = require('git-repo-name') 5 | const fetch = require('node-fetch') 6 | const fs = require('fs') 7 | const { isNil } = require('lodash') 8 | 9 | const realPathBasename = path.basename 10 | const realGetReposNameSync = getReposName.sync 11 | 12 | const { 13 | getPackageJson, 14 | showEndMessage, 15 | getProjectName, 16 | END_MSG, 17 | BOXEN_CONFIG, 18 | getDefaultAnswer, 19 | getDefaultAnswers, 20 | cleanSocialNetworkUsername, 21 | isProjectAvailableOnNpm, 22 | getAuthorWebsiteFromGithubAPI, 23 | doesFileExist, 24 | getPackageManagerFromLockFile 25 | } = require('./utils') 26 | 27 | jest.mock('load-json-file') 28 | jest.mock('boxen') 29 | jest.mock('node-fetch') 30 | jest.mock('fs') 31 | 32 | describe('utils', () => { 33 | describe('getPackageJson', () => { 34 | const packageJsonContent = { 35 | name: 'readme-md-cli' 36 | } 37 | 38 | it('should return package.json content', async () => { 39 | loadJsonFile.mockReturnValueOnce(Promise.resolve(packageJsonContent)) 40 | 41 | const result = await getPackageJson() 42 | 43 | expect(result).toEqual(packageJsonContent) 44 | }) 45 | 46 | it('should return undefined', async () => { 47 | loadJsonFile.mockImplementationOnce(() => { 48 | throw new Error('ERROR') 49 | }) 50 | 51 | const result = await getPackageJson() 52 | 53 | expect(result).toBeUndefined() 54 | }) 55 | }) 56 | 57 | describe('showEndMessage', () => { 58 | boxen.mockReturnValue(END_MSG) 59 | 60 | it('should call boxen with correct parameters', () => { 61 | showEndMessage() 62 | 63 | expect(boxen).toHaveBeenCalledTimes(1) 64 | expect(boxen).toHaveBeenCalledWith(END_MSG, BOXEN_CONFIG) 65 | }) 66 | 67 | it('should call process.stdout.write with correct parameters', () => { 68 | process.stdout.write = jest.fn() 69 | 70 | showEndMessage() 71 | 72 | expect(process.stdout.write).toHaveBeenCalledTimes(1) 73 | expect(process.stdout.write).toHaveBeenCalledWith(END_MSG) 74 | }) 75 | }) 76 | 77 | describe('getProjectName', () => { 78 | const projectName = 'readme-md-generator' 79 | 80 | beforeEach(() => { 81 | path.basename = jest.fn(() => projectName) 82 | getReposName.sync = jest.fn() 83 | }) 84 | 85 | afterEach(() => { 86 | path.basename = realPathBasename 87 | getReposName.sync = realGetReposNameSync 88 | }) 89 | 90 | it('should return package.json name prop when defined', () => { 91 | const packageJson = { name: projectName } 92 | getReposName.sync.mockReturnValueOnce('readme-md-generator') 93 | 94 | const result = getProjectName(packageJson) 95 | 96 | expect(result).toEqual(projectName) 97 | expect(getReposName.sync).not.toHaveBeenCalled() 98 | expect(path.basename).not.toHaveBeenCalled() 99 | }) 100 | 101 | it('should return git repos when package.json it is not defined', () => { 102 | const packageJson = undefined 103 | getReposName.sync.mockReturnValueOnce('readme-md-generator') 104 | 105 | const result = getProjectName(packageJson) 106 | 107 | expect(result).toEqual(projectName) 108 | expect(getReposName.sync).toHaveBeenCalled() 109 | expect(path.basename).not.toHaveBeenCalled() 110 | }) 111 | 112 | it('should return folder basename when package.json and git repos name is undefined', () => { 113 | const packageJson = undefined 114 | getReposName.sync.mockImplementation(() => { 115 | throw new Error('error') 116 | }) 117 | 118 | const result = getProjectName(packageJson) 119 | 120 | expect(result).toEqual(projectName) 121 | expect(getReposName.sync).toHaveBeenCalled() 122 | expect(path.basename).toHaveBeenCalled() 123 | }) 124 | }) 125 | 126 | describe('getDefaultAnswer', () => { 127 | it('should handle input prompts correctly', async () => { 128 | const question = { type: 'input', default: 'default' } 129 | const result = await getDefaultAnswer(question) 130 | expect(result).toEqual(question.default) 131 | }) 132 | 133 | it('should handle choices prompts correctly', async () => { 134 | const value = { name: 'name', value: 'value' } 135 | const question = { 136 | type: 'checkbox', 137 | choices: [{ value, checked: true }, { checked: false }] 138 | } 139 | const result = await getDefaultAnswer(question) 140 | 141 | expect(result).toEqual([value]) 142 | }) 143 | 144 | it('should return empty string for non-defaulted fields', async () => { 145 | const question = { type: 'input' } 146 | const result = await getDefaultAnswer(question) 147 | 148 | expect(result).toEqual('') 149 | }) 150 | 151 | it('should return undefined for invalid types', async () => { 152 | const question = { type: 'invalid' } 153 | const result = await getDefaultAnswer(question) 154 | 155 | expect(result).toEqual(undefined) 156 | }) 157 | 158 | it('should return undefined if when function is defined and return false', async () => { 159 | const answersContext = {} 160 | const question = { 161 | type: 'input', 162 | when: ansewersContext => !isNil(ansewersContext.licenseUrl) 163 | } 164 | 165 | const result = await getDefaultAnswer(question, answersContext) 166 | 167 | expect(result).toEqual(undefined) 168 | }) 169 | 170 | describe('isProjectAvailableOnNpm', () => { 171 | it('should return true if project is available on npm', () => { 172 | const result = isProjectAvailableOnNpm('readme-md-generator') 173 | 174 | expect(result).toBe(true) 175 | }) 176 | 177 | it('should return false if project is not available on npm', () => { 178 | const result = isProjectAvailableOnNpm('bento-starter') 179 | 180 | expect(result).toBe(false) 181 | }) 182 | }) 183 | 184 | it('should return correct value if when function is defined and return true', async () => { 185 | const answersContext = { licenseUrl: 'licenseUrl' } 186 | const question = { 187 | type: 'input', 188 | default: 'default', 189 | when: ansewersContext => !isNil(ansewersContext.licenseUrl) 190 | } 191 | 192 | const result = await getDefaultAnswer(question, answersContext) 193 | 194 | expect(result).toEqual('default') 195 | }) 196 | }) 197 | 198 | describe('getDefaultAnswers', () => { 199 | it('should return default answers from questions', async () => { 200 | const questions = [ 201 | { 202 | type: 'input', 203 | name: 'questionOne', 204 | default: 'answer 1' 205 | }, 206 | { 207 | type: 'input', 208 | name: 'questionTwo', 209 | default: 'answer 2' 210 | } 211 | ] 212 | 213 | const result = await getDefaultAnswers(questions) 214 | 215 | expect(result).toEqual({ 216 | questionOne: 'answer 1', 217 | questionTwo: 'answer 2' 218 | }) 219 | }) 220 | }) 221 | 222 | describe('cleanSocialNetworkUsername', () => { 223 | it('should remove prefixed @', () => { 224 | expect(cleanSocialNetworkUsername('@Slashgear')).toEqual('Slashgear') 225 | }) 226 | 227 | it('should escape markdown characters', () => { 228 | expect(cleanSocialNetworkUsername('Slashgear__')).toEqual( 229 | 'Slashgear\\_\\_' 230 | ) 231 | expect(cleanSocialNetworkUsername('Slashgear**')).toEqual( 232 | 'Slashgear\\*\\*' 233 | ) 234 | }) 235 | 236 | it('should return the same string when string is not prefixed or contains markdown chars', () => { 237 | expect(cleanSocialNetworkUsername('Slashgear')).toEqual('Slashgear') 238 | }) 239 | }) 240 | 241 | describe('getAuthorWebsiteFromGithubAPI', () => { 242 | it('should return author website url when it exists', async () => { 243 | const expectedAuthorWebsite = 'https://www.franck-abgrall.me/' 244 | fetch.mockReturnValueOnce( 245 | Promise.resolve({ 246 | json: () => Promise.resolve({ blog: expectedAuthorWebsite }) 247 | }) 248 | ) 249 | 250 | const githubUsername = 'kefranabg' 251 | const authorWebsite = await getAuthorWebsiteFromGithubAPI(githubUsername) 252 | expect(authorWebsite).toEqual(expectedAuthorWebsite) 253 | }) 254 | 255 | it('should return undefined if author website url does not exist', async () => { 256 | fetch.mockReturnValueOnce(Promise.resolve({ blog: '' })) 257 | const githubUsername = 'kefranabg' 258 | const authorWebsite = await getAuthorWebsiteFromGithubAPI(githubUsername) 259 | expect(authorWebsite).toEqual(undefined) 260 | }) 261 | 262 | it('should return undefined if there is an error', async () => { 263 | fetch.mockImplementationOnce(() => { 264 | throw new Error('ERROR') 265 | }) 266 | const githubUsername = 'kefranabg' 267 | const authorWebsite = await getAuthorWebsiteFromGithubAPI(githubUsername) 268 | expect(authorWebsite).toEqual(undefined) 269 | }) 270 | }) 271 | 272 | describe('doesFileExist', () => { 273 | it('should return true when file exists for a given path', () => { 274 | fs.existsSync.mockReturnValueOnce(true) 275 | expect(doesFileExist('./file-path')).toBe(true) 276 | }) 277 | 278 | it('should return false when file does not exist for a given path', () => { 279 | fs.existsSync.mockReturnValueOnce(false) 280 | expect(doesFileExist('./file-path')).toBe(false) 281 | }) 282 | 283 | it('should return false if fs.existsSync throws an error', () => { 284 | fs.existsSync.mockImplementationOnce(() => { 285 | throw new Error('ERROR') 286 | }) 287 | expect(doesFileExist('./file-path')).toBe(false) 288 | }) 289 | }) 290 | 291 | describe('getPackageManagerFromLockFile', () => { 292 | it('should return npm if only package-lock.json exists', () => { 293 | fs.existsSync.mockImplementation( 294 | filePath => filePath === 'package-lock.json' 295 | ) 296 | 297 | const result = getPackageManagerFromLockFile() 298 | 299 | expect(result).toEqual('npm') 300 | }) 301 | 302 | it('should return yarn if only yarn.lock exists', () => { 303 | fs.existsSync.mockImplementation(filePath => filePath === 'yarn.lock') 304 | 305 | const result = getPackageManagerFromLockFile() 306 | 307 | expect(result).toEqual('yarn') 308 | }) 309 | 310 | it('should return undefined if only yarn.lock and package-lock.json exists', () => { 311 | fs.existsSync.mockImplementation( 312 | filePath => filePath === 'yarn.lock' || filePath === 'package-lock.json' 313 | ) 314 | 315 | const result = getPackageManagerFromLockFile() 316 | 317 | expect(result).toBeUndefined() 318 | }) 319 | 320 | it('should return undefined if only no lock file exists', () => { 321 | fs.existsSync.mockImplementation(() => false) 322 | 323 | const result = getPackageManagerFromLockFile() 324 | 325 | expect(result).toBeUndefined() 326 | }) 327 | }) 328 | }) 329 | -------------------------------------------------------------------------------- /src/project-infos.spec.js: -------------------------------------------------------------------------------- 1 | const ora = require('ora') 2 | const childProcess = require('child_process') 3 | 4 | const utils = require('./utils') 5 | const { getProjectInfos } = require('./project-infos') 6 | 7 | jest.mock('ora') 8 | jest.mock('child_process', () => ({ 9 | execSync: jest.fn() 10 | })) 11 | jest.mock('./utils', () => ({ 12 | getPackageJson: jest.fn(), 13 | getProjectName: jest.fn(() => 'readme-md-generator'), 14 | getAuthorWebsiteFromGithubAPI: jest.fn( 15 | () => 'https://www.franck-abgrall.me/' 16 | ), 17 | getPackageManagerFromLockFile: jest.fn(() => 'yarn') 18 | })) 19 | 20 | const succeed = jest.fn() 21 | const fail = jest.fn() 22 | 23 | ora.mockReturnValue({ 24 | start: () => ({ 25 | succeed, 26 | fail 27 | }) 28 | }) 29 | 30 | describe('projectInfos', () => { 31 | describe('getProjectInfos', () => { 32 | it('should call ora with correct parameters', async () => { 33 | await getProjectInfos() 34 | 35 | expect(ora).toHaveBeenCalledTimes(1) 36 | expect(ora).toHaveBeenCalledWith('Gathering project infos') 37 | expect(succeed).toHaveBeenCalledTimes(1) 38 | expect(succeed).toHaveBeenCalledWith('Project infos gathered') 39 | }) 40 | 41 | it('should return correct infos', async () => { 42 | const packgeJsonInfos = { 43 | name: 'readme-md-generator', 44 | version: '0.1.3', 45 | description: 'CLI that generates beautiful README.md files.', 46 | author: 'Franck Abgrall', 47 | license: 'MIT', 48 | homepage: 'https://github.com/kefranabg/readme-md-generator', 49 | repository: { 50 | type: 'git', 51 | url: 'git+https://github.com/kefranabg/readme-md-generator.git' 52 | }, 53 | bugs: { 54 | url: 'https://github.com/kefranabg/readme-md-generator/issues' 55 | }, 56 | engines: { 57 | npm: '>=5.5.0', 58 | node: '>=9.3.0' 59 | } 60 | } 61 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(packgeJsonInfos)) 62 | childProcess.execSync.mockReturnValue( 63 | 'https://github.com/kefranabg/readme-md-generator.git' 64 | ) 65 | 66 | const projectInfos = await getProjectInfos() 67 | 68 | expect(projectInfos).toEqual({ 69 | name: 'readme-md-generator', 70 | description: 'CLI that generates beautiful README.md files.', 71 | version: '0.1.3', 72 | author: 'Franck Abgrall', 73 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 74 | homepage: 'https://github.com/kefranabg/readme-md-generator', 75 | contributingUrl: 76 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 77 | authorWebsite: 'https://www.franck-abgrall.me/', 78 | githubUsername: 'kefranabg', 79 | engines: { 80 | npm: '>=5.5.0', 81 | node: '>=9.3.0' 82 | }, 83 | licenseName: 'MIT', 84 | licenseUrl: 85 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 86 | documentationUrl: 87 | 'https://github.com/kefranabg/readme-md-generator#readme', 88 | isGithubRepos: true, 89 | isJSProject: true, 90 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 91 | hasStartCommand: false, 92 | hasTestCommand: false, 93 | packageManager: 'yarn' 94 | }) 95 | }) 96 | 97 | it('should return correct infos when repos is not github', async () => { 98 | const packgeJsonInfos = { 99 | name: 'readme-md-generator', 100 | version: '0.1.3', 101 | description: 'CLI that generates beautiful README.md files.', 102 | author: 'Franck Abgrall', 103 | license: 'MIT', 104 | homepage: 'https://gitlab.com/kefranabg/readme-md-generator', 105 | repository: { 106 | type: 'git', 107 | url: 'git+https://gitlab.com/kefranabg/readme-md-generator.git' 108 | }, 109 | bugs: { 110 | url: 'https://gitlab.com/kefranabg/readme-md-generator/issues' 111 | }, 112 | engines: { 113 | npm: '>=5.5.0', 114 | node: '>=9.3.0' 115 | } 116 | } 117 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(packgeJsonInfos)) 118 | childProcess.execSync.mockReturnValue( 119 | 'https://github.com/kefranabg/readme-md-generator.git' 120 | ) 121 | 122 | const projectInfos = await getProjectInfos() 123 | 124 | expect(projectInfos).toEqual({ 125 | name: 'readme-md-generator', 126 | description: 'CLI that generates beautiful README.md files.', 127 | version: '0.1.3', 128 | author: 'Franck Abgrall', 129 | repositoryUrl: 'https://gitlab.com/kefranabg/readme-md-generator', 130 | contributingUrl: 131 | 'https://gitlab.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 132 | homepage: 'https://gitlab.com/kefranabg/readme-md-generator', 133 | githubUsername: undefined, 134 | authorWebsite: undefined, 135 | engines: { 136 | npm: '>=5.5.0', 137 | node: '>=9.3.0' 138 | }, 139 | licenseName: 'MIT', 140 | licenseUrl: undefined, 141 | documentationUrl: undefined, 142 | isGithubRepos: false, 143 | isJSProject: true, 144 | issuesUrl: 'https://gitlab.com/kefranabg/readme-md-generator/issues', 145 | hasStartCommand: false, 146 | hasTestCommand: false, 147 | packageManager: 'yarn' 148 | }) 149 | }) 150 | 151 | it('should return correct infos when package.json is not defined', async () => { 152 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(undefined)) 153 | childProcess.execSync.mockReturnValue( 154 | 'https://github.com/kefranabg/readme-md-generator.git' 155 | ) 156 | 157 | const projectInfos = await getProjectInfos() 158 | 159 | expect(projectInfos).toEqual({ 160 | name: 'readme-md-generator', 161 | description: undefined, 162 | version: undefined, 163 | author: undefined, 164 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 165 | contributingUrl: 166 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 167 | homepage: undefined, 168 | githubUsername: 'kefranabg', 169 | authorWebsite: 'https://www.franck-abgrall.me/', 170 | engines: undefined, 171 | licenseName: undefined, 172 | licenseUrl: 173 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 174 | documentationUrl: 175 | 'https://github.com/kefranabg/readme-md-generator#readme', 176 | isGithubRepos: true, 177 | isJSProject: false, 178 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 179 | hasStartCommand: false, 180 | hasTestCommand: false 181 | }) 182 | }) 183 | 184 | it('should return correct infos when repos is not github and package.json are not defined', async () => { 185 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(undefined)) 186 | childProcess.execSync.mockReturnValue( 187 | 'https://gitlab.com/kefranabg/readme-md-generator.git' 188 | ) 189 | 190 | const projectInfos = await getProjectInfos() 191 | 192 | expect(projectInfos).toEqual({ 193 | name: 'readme-md-generator', 194 | description: undefined, 195 | version: undefined, 196 | author: undefined, 197 | repositoryUrl: 'https://gitlab.com/kefranabg/readme-md-generator', 198 | authorWebsite: undefined, 199 | contributingUrl: 200 | 'https://gitlab.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 201 | homepage: undefined, 202 | githubUsername: undefined, 203 | engines: undefined, 204 | licenseName: undefined, 205 | licenseUrl: undefined, 206 | documentationUrl: undefined, 207 | isGithubRepos: false, 208 | isJSProject: false, 209 | issuesUrl: 'https://gitlab.com/kefranabg/readme-md-generator/issues', 210 | hasStartCommand: false, 211 | hasTestCommand: false 212 | }) 213 | }) 214 | 215 | it('should return correct infos when git config and package.json are not defined', async () => { 216 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(undefined)) 217 | childProcess.execSync.mockImplementation(() => { 218 | throw new Error('error') 219 | }) 220 | 221 | const projectInfos = await getProjectInfos() 222 | 223 | expect(projectInfos).toEqual({ 224 | name: 'readme-md-generator', 225 | description: undefined, 226 | version: undefined, 227 | author: undefined, 228 | repositoryUrl: undefined, 229 | contributingUrl: undefined, 230 | homepage: undefined, 231 | authorWebsite: undefined, 232 | githubUsername: undefined, 233 | engines: undefined, 234 | licenseName: undefined, 235 | licenseUrl: undefined, 236 | documentationUrl: undefined, 237 | isGithubRepos: false, 238 | isJSProject: false, 239 | testCommand: undefined, 240 | hasStartCommand: false, 241 | hasTestCommand: false 242 | }) 243 | }) 244 | 245 | it('should return correct infos when git config is not defined', async () => { 246 | const packgeJsonInfos = { 247 | name: 'readme-md-generator', 248 | version: '0.1.3', 249 | description: 'CLI that generates beautiful README.md files.', 250 | author: 'Franck Abgrall', 251 | license: 'MIT', 252 | homepage: 'https://github.com/kefranabg/readme-md-generator', 253 | repository: { 254 | type: 'git', 255 | url: 'git+https://github.com/kefranabg/readme-md-generator.git' 256 | }, 257 | bugs: { 258 | url: 'https://github.com/kefranabg/readme-md-generator/issues' 259 | }, 260 | engines: { 261 | npm: '>=5.5.0', 262 | node: '>=9.3.0' 263 | } 264 | } 265 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(packgeJsonInfos)) 266 | childProcess.execSync.mockImplementation(() => { 267 | throw new Error('error') 268 | }) 269 | 270 | const projectInfos = await getProjectInfos() 271 | 272 | expect(projectInfos).toEqual({ 273 | name: 'readme-md-generator', 274 | description: 'CLI that generates beautiful README.md files.', 275 | version: '0.1.3', 276 | author: 'Franck Abgrall', 277 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 278 | contributingUrl: 279 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 280 | homepage: 'https://github.com/kefranabg/readme-md-generator', 281 | githubUsername: 'kefranabg', 282 | authorWebsite: 'https://www.franck-abgrall.me/', 283 | engines: { 284 | npm: '>=5.5.0', 285 | node: '>=9.3.0' 286 | }, 287 | licenseName: 'MIT', 288 | licenseUrl: 289 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 290 | documentationUrl: 291 | 'https://github.com/kefranabg/readme-md-generator#readme', 292 | isGithubRepos: true, 293 | isJSProject: true, 294 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 295 | hasStartCommand: false, 296 | hasTestCommand: false, 297 | packageManager: 'yarn' 298 | }) 299 | }) 300 | 301 | it('should return correct infos when author is defined as an object', async () => { 302 | const packgeJsonInfos = { 303 | name: 'readme-md-generator', 304 | version: '0.1.3', 305 | description: 'CLI that generates beautiful README.md files.', 306 | author: { 307 | name: 'Franck Abgrall', 308 | email: 'abgrallkefran@gmail.com', 309 | url: '' 310 | }, 311 | license: 'MIT', 312 | homepage: 'https://github.com/kefranabg/readme-md-generator', 313 | repository: { 314 | type: 'git', 315 | url: 'git+https://github.com/kefranabg/readme-md-generator.git' 316 | }, 317 | bugs: { 318 | url: 'https://github.com/kefranabg/readme-md-generator/issues' 319 | }, 320 | engines: { 321 | npm: '>=5.5.0', 322 | node: '>=9.3.0' 323 | } 324 | } 325 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(packgeJsonInfos)) 326 | childProcess.execSync.mockReturnValue( 327 | 'https://github.com/kefranabg/readme-md-generator.git' 328 | ) 329 | 330 | const projectInfos = await getProjectInfos() 331 | 332 | expect(projectInfos).toEqual({ 333 | name: 'readme-md-generator', 334 | description: 'CLI that generates beautiful README.md files.', 335 | version: '0.1.3', 336 | author: 'Franck Abgrall', 337 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 338 | homepage: 'https://github.com/kefranabg/readme-md-generator', 339 | contributingUrl: 340 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 341 | githubUsername: 'kefranabg', 342 | authorWebsite: 'https://www.franck-abgrall.me/', 343 | engines: { 344 | npm: '>=5.5.0', 345 | node: '>=9.3.0' 346 | }, 347 | licenseName: 'MIT', 348 | licenseUrl: 349 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 350 | documentationUrl: 351 | 'https://github.com/kefranabg/readme-md-generator#readme', 352 | isGithubRepos: true, 353 | isJSProject: true, 354 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 355 | hasStartCommand: false, 356 | hasTestCommand: false, 357 | packageManager: 'yarn' 358 | }) 359 | }) 360 | 361 | it('should return correct infos when lock file is found', async () => { 362 | const packgeJsonInfos = { 363 | name: 'readme-md-generator', 364 | version: '0.1.3', 365 | description: 'CLI that generates beautiful README.md files.', 366 | author: 'Franck Abgrall', 367 | license: 'MIT', 368 | homepage: 'https://github.com/kefranabg/readme-md-generator', 369 | repository: { 370 | type: 'git', 371 | url: 'git+https://github.com/kefranabg/readme-md-generator.git' 372 | }, 373 | bugs: { 374 | url: 'https://github.com/kefranabg/readme-md-generator/issues' 375 | }, 376 | engines: { 377 | npm: '>=5.5.0', 378 | node: '>=9.3.0' 379 | }, 380 | scripts: { 381 | start: 'node src/index.js', 382 | test: 'jest' 383 | } 384 | } 385 | utils.getPackageJson.mockReturnValueOnce(Promise.resolve(packgeJsonInfos)) 386 | utils.getPackageManagerFromLockFile.mockReturnValueOnce('yarn') 387 | childProcess.execSync.mockReturnValue( 388 | 'https://github.com/kefranabg/readme-md-generator.git' 389 | ) 390 | 391 | const projectInfos = await getProjectInfos() 392 | 393 | expect(projectInfos).toEqual({ 394 | name: 'readme-md-generator', 395 | description: 'CLI that generates beautiful README.md files.', 396 | version: '0.1.3', 397 | author: 'Franck Abgrall', 398 | repositoryUrl: 'https://github.com/kefranabg/readme-md-generator', 399 | homepage: 'https://github.com/kefranabg/readme-md-generator', 400 | contributingUrl: 401 | 'https://github.com/kefranabg/readme-md-generator/blob/master/CONTRIBUTING.md', 402 | authorWebsite: 'https://www.franck-abgrall.me/', 403 | githubUsername: 'kefranabg', 404 | engines: { 405 | npm: '>=5.5.0', 406 | node: '>=9.3.0' 407 | }, 408 | licenseName: 'MIT', 409 | licenseUrl: 410 | 'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE', 411 | documentationUrl: 412 | 'https://github.com/kefranabg/readme-md-generator#readme', 413 | isGithubRepos: true, 414 | isJSProject: true, 415 | issuesUrl: 'https://github.com/kefranabg/readme-md-generator/issues', 416 | hasStartCommand: true, 417 | hasTestCommand: true, 418 | packageManager: 'yarn' 419 | }) 420 | }) 421 | }) 422 | }) 423 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## 1.0.0 (2019-12-03) 6 | 7 | ### Added 8 | 9 | - ✨ Ask for package manager when is JS project [[29986ce](https://github.com/kefranabg/readme-md-generator/commit/29986cea9acde108b9e167db5d0def67beaf7384)] 10 | - ✨ Ask user for a link to contributing guide (fixes [#168](https://github.com/kefranabg/readme-md-generator/issues/168)) ([#179](https://github.com/kefranabg/readme-md-generator/issues/179)) [[310a8b1](https://github.com/kefranabg/readme-md-generator/commit/310a8b1f78154d9226c1267ccf18d0db9d9ea09f)] 11 | - ✨ Ask author's linkedin username (fixes [#170](https://github.com/kefranabg/readme-md-generator/issues/170)) ([#177](https://github.com/kefranabg/readme-md-generator/issues/177)) [[10954d1](https://github.com/kefranabg/readme-md-generator/commit/10954d10739b995c431b751d63d5624c17397235)] 12 | 13 | ### Changed 14 | 15 | - ⬆️ Update yargs to the latest version ([#186](https://github.com/kefranabg/readme-md-generator/issues/186)) [[16d7ceb](https://github.com/kefranabg/readme-md-generator/commit/16d7ceb8ec3040d63841397ded83dbdddc28da52)] 16 | - ⬆️ Update ejs to the latest version ([#187](https://github.com/kefranabg/readme-md-generator/issues/187)) [[139e906](https://github.com/kefranabg/readme-md-generator/commit/139e906b1ed1b875ca96869fe8570ca03d9806c2)] 17 | - 🔧 Update CI tiggers ([#184](https://github.com/kefranabg/readme-md-generator/issues/184)) [[6ef2be8](https://github.com/kefranabg/readme-md-generator/commit/6ef2be8ba8e00af16087f42eabd7e4b4ed2243b3)] 18 | - ⬆️ Update ora to the latest version ([#181](https://github.com/kefranabg/readme-md-generator/issues/181)) [[c563d9d](https://github.com/kefranabg/readme-md-generator/commit/c563d9d473e5c17be6a45936b2f58f83b734c4e9)] 19 | - 🔧 Move CI from CircleCI to GitHub actions [[a132525](https://github.com/kefranabg/readme-md-generator/commit/a132525ffe343629db2589378647665747e5cd4f)] 20 | - 🔧 Update FUNDING.yml [[83be6c0](https://github.com/kefranabg/readme-md-generator/commit/83be6c00842eb4321b5c37a7f5617e28f1548cdb)] 21 | 22 | ### Fixed 23 | 24 | - 🐛 Fix version badge label when contains \_ or - ([#190](https://github.com/kefranabg/readme-md-generator/issues/190)) [[6c345b5](https://github.com/kefranabg/readme-md-generator/commit/6c345b50caec810ef48f00dcbac0255f1db1f4a3)] 25 | - 🐛 Execute default function to get default answer when -y flag is passed ([#185](https://github.com/kefranabg/readme-md-generator/issues/185)) [[89cd82d](https://github.com/kefranabg/readme-md-generator/commit/89cd82db18655bbb085eb72c339402f412dd3996)] 26 | - 🐛 Escape markdown characters in social network questions answers ([#183](https://github.com/kefranabg/readme-md-generator/issues/183)) [[d96e310](https://github.com/kefranabg/readme-md-generator/commit/d96e310c5b54c573a139d01f4ad348f9456f7c3c)] 27 | - ✏️ Use gender neutral in comment ([#174](https://github.com/kefranabg/readme-md-generator/issues/174)) [[9f78778](https://github.com/kefranabg/readme-md-generator/commit/9f78778002410c0b82f36015b86443fb366425a7)] 28 | - 🐛 Fix InstallCommand test name [[279711f](https://github.com/kefranabg/readme-md-generator/commit/279711f1cdd18b2d53478c52f577041ea2186675)] 29 | 30 | ### Miscellaneous 31 | 32 | - 📝 Add open collective to readme ([#192](https://github.com/kefranabg/readme-md-generator/issues/192)) [[81e3873](https://github.com/kefranabg/readme-md-generator/commit/81e387357c5c088714291b0a7f18b66cab8cd5c6)] 33 | - 📝 Add anku255 as a contributor for test and bug ([#182](https://github.com/kefranabg/readme-md-generator/issues/182)) [[7624889](https://github.com/kefranabg/readme-md-generator/commit/76248890cf46ec7a27b6986add3d156095deb782)] 34 | 35 | 36 | 37 | ## 0.7.0 (2019-11-01) 38 | 39 | ### Added 40 | 41 | - ✨ Add Author Github website url if exists (fixes [#124](https://github.com/kefranabg/readme-md-generator/issues/124)) ([#145](https://github.com/kefranabg/readme-md-generator/issues/145)) [[541d6e5](https://github.com/kefranabg/readme-md-generator/commit/541d6e53fdc2df42a3a416b5c2f2b5c1422e45db)] 42 | 43 | ### Changed 44 | 45 | - ♻️ re-use prettier regexp across scripts ([#159](https://github.com/kefranabg/readme-md-generator/issues/159)) [[576e367](https://github.com/kefranabg/readme-md-generator/commit/576e367c67f7fbe0013fe2e32e20d17b6d4b8c8f)] 46 | - 🔧 Add prettier fix command ([#158](https://github.com/kefranabg/readme-md-generator/issues/158)) [[da2c700](https://github.com/kefranabg/readme-md-generator/commit/da2c70034165b42d8e25abf1c799f275d7c349b0)] 47 | 48 | ### Fixed 49 | 50 | - 🐛 Don't suggest install default value when project is not a js project ([#169](https://github.com/kefranabg/readme-md-generator/issues/169)) [[672d591](https://github.com/kefranabg/readme-md-generator/commit/672d591ae845e95ef2b8f41b00997e09dfff1a3c)] 51 | - 🐛 Fix static badge generation when license name contains special characters ([#141](https://github.com/kefranabg/readme-md-generator/issues/141)) [[5b8bc08](https://github.com/kefranabg/readme-md-generator/commit/5b8bc08925e54f1c542d84a26283832040232053)] 52 | 53 | ### Miscellaneous 54 | 55 | - 📝 Update README with CONTRIBUTING link [[5ec2ce4](https://github.com/kefranabg/readme-md-generator/commit/5ec2ce4e1404349eb94037dae16a8c569977e90a)] 56 | - 📝 Add CONTRIBUTING.md (fixes [#155](https://github.com/kefranabg/readme-md-generator/issues/155)) ([#160](https://github.com/kefranabg/readme-md-generator/issues/160)) [[f2d6020](https://github.com/kefranabg/readme-md-generator/commit/f2d6020e5fdef4c880ed6830302ca20f0817d192)] 57 | - 📝 Add code of conduct markdown ([#157](https://github.com/kefranabg/readme-md-generator/issues/157)) [[be3ec22](https://github.com/kefranabg/readme-md-generator/commit/be3ec22a7d44c88b59c3c67910ad266b65ff9746)] 58 | 59 | 60 | 61 | ## 0.6.4 (2019-10-09) 62 | 63 | ### Added 64 | 65 | - ✨ Add check for overwrite readme ([#143](https://github.com/kefranabg/readme-md-generator/issues/143)) [[2e12553](https://github.com/kefranabg/readme-md-generator/commit/2e12553df31c65f31c88f556eb8811d5b5672739)] 66 | - ✨ Display license badge even when there is no license url ([#139](https://github.com/kefranabg/readme-md-generator/issues/139)) [[a60627c](https://github.com/kefranabg/readme-md-generator/commit/a60627c93af35a2f87bc2f7ed44ce702871a6f7d)] 67 | - ✨ Ask for demo link during README creation process ([#102](https://github.com/kefranabg/readme-md-generator/issues/102)) ([#130](https://github.com/kefranabg/readme-md-generator/issues/130)) [[f4d6ad4](https://github.com/kefranabg/readme-md-generator/commit/f4d6ad45c3884b24bf60536dd02d3ae17034f1b1)] 68 | - ✨ Add dynamic license badge ([#131](https://github.com/kefranabg/readme-md-generator/issues/131)) [[86bdac5](https://github.com/kefranabg/readme-md-generator/commit/86bdac57c35734ca0bdd252d3e0ae12f9a5e80c8)] 69 | 70 | ### Changed 71 | 72 | - ⬆️ Fix vulnerabilities ([#136](https://github.com/kefranabg/readme-md-generator/issues/136)) [[fb96985](https://github.com/kefranabg/readme-md-generator/commit/fb96985ff9665319f3669deae3e14e31a4fe9df9)] 73 | 74 | 75 | 76 | ## 0.6.3 (2019-09-24) 77 | 78 | ### Added 79 | 80 | - ✨ Update issue templates [[3d5c28b](https://github.com/kefranabg/readme-md-generator/commit/3d5c28b3eb57c18245ef22f47fa8083117f1a08f)] 81 | - ✨ Add link to npm on version badge ([#99](https://github.com/kefranabg/readme-md-generator/issues/99)) [[fdfe6e3](https://github.com/kefranabg/readme-md-generator/commit/fdfe6e374953a255eb72945405e8d8eadb62d3a9)] 82 | - ✨ Add dynamic version badge if package exists on NPM ([#94](https://github.com/kefranabg/readme-md-generator/issues/94)) [[0a11d2a](https://github.com/kefranabg/readme-md-generator/commit/0a11d2ae3a7ea767e4f0138f7b1d21e114fb991a)] 83 | 84 | ### Changed 85 | 86 | - ⬆️ Update ora to the latest version ([#122](https://github.com/kefranabg/readme-md-generator/issues/122)) [[ffb4d11](https://github.com/kefranabg/readme-md-generator/commit/ffb4d111547c23cedf03168c2facbd285c3c7d9d)] 87 | - 🔧 Add gh package registry config ([#115](https://github.com/kefranabg/readme-md-generator/issues/115)) [[af1fb5a](https://github.com/kefranabg/readme-md-generator/commit/af1fb5ac10cfdabc57fc579e3c0a8f9a3d417f64)] 88 | - ⬆️ Upgrade date-fns version ([#113](https://github.com/kefranabg/readme-md-generator/issues/113)) [[eacad62](https://github.com/kefranabg/readme-md-generator/commit/eacad62792e0f95f41b7ed267611265e08b14c2b)] 89 | - ⬆️ Upgrade inquirer to the latest version 🚀 ([#110](https://github.com/kefranabg/readme-md-generator/issues/110)) [[f6fc84e](https://github.com/kefranabg/readme-md-generator/commit/f6fc84ec71a5b11de581745c8d75b7e6c1dd2143)] 90 | - ⬆️ Bump mixin-deep from 1.3.1 to 1.3.2 ([#112](https://github.com/kefranabg/readme-md-generator/issues/112)) [[4e0abc4](https://github.com/kefranabg/readme-md-generator/commit/4e0abc4b574ced8433c0cc48f965d959a100ff04)] 91 | - ⬆️ Bump eslint-utils from 1.3.1 to 1.4.2 ([#111](https://github.com/kefranabg/readme-md-generator/issues/111)) [[f0e09ac](https://github.com/kefranabg/readme-md-generator/commit/f0e09ac3f6b20882bda39c67e552eedd6fe18dce)] 92 | - 🎨 Format Github issue templates [[95f44d2](https://github.com/kefranabg/readme-md-generator/commit/95f44d26062b06439c1ff97a13f61642279d32c5)] 93 | - ⬆️ Update yargs to the latest version ([#104](https://github.com/kefranabg/readme-md-generator/issues/104)) [[d66db07](https://github.com/kefranabg/readme-md-generator/commit/d66db07cd2e0393b5ecb456ee982953a1448eff4)] 94 | - ⬆️ Update inquirer to 6.5.1 ([#101](https://github.com/kefranabg/readme-md-generator/issues/101)) [[af8fbe6](https://github.com/kefranabg/readme-md-generator/commit/af8fbe6acf5f08090d6958e12e09c3a1f3e345ff)] 95 | - ⬆️ Update eslint-config-airbnb-base to 14.0.0 ([#100](https://github.com/kefranabg/readme-md-generator/issues/100)) [[85ffea5](https://github.com/kefranabg/readme-md-generator/commit/85ffea57f3b4ed925ffe827f4194ac066298eb83)] 96 | - ⬆️ Bump lodash from 4.17.11 to 4.17.13 ([#98](https://github.com/kefranabg/readme-md-generator/issues/98)) [[99dc774](https://github.com/kefranabg/readme-md-generator/commit/99dc774fac253a0786b935deb258a8cb3a777176)] 97 | - 🔧 Ignore .git file in order to fix an npm bug [[f940f13](https://github.com/kefranabg/readme-md-generator/commit/f940f130efef5ffdeb3f278e7435570c95363acb)] 98 | 99 | ### Fixed 100 | 101 | - 🐛 Process get stuck because of ora new version ([#152](https://github.com/kefranabg/readme-md-generator/issues/152)) [[4a741b9](https://github.com/kefranabg/readme-md-generator/commit/4a741b9b266ae33c8c04267e6958c26e598655b7)] 102 | - 🐛 Fix badge target link ([#126](https://github.com/kefranabg/readme-md-generator/issues/126)) [[45df9a4](https://github.com/kefranabg/readme-md-generator/commit/45df9a4ab55ffa2ff6e8ac5a33e49bbba0b41efa)] 103 | - ✏️ Capitalize the H in GitHub ([#116](https://github.com/kefranabg/readme-md-generator/issues/116)) [[3d08a76](https://github.com/kefranabg/readme-md-generator/commit/3d08a7610723a0034607bd4d136926220f431a03)] 104 | 105 | ### Miscellaneous 106 | 107 | - 📝 Add anku255 as a contributor ([#144](https://github.com/kefranabg/readme-md-generator/issues/144)) [[32af6a2](https://github.com/kefranabg/readme-md-generator/commit/32af6a23fd22efbc93861c47f13c190c0800b9ed)] 108 | - 📝 Add LucasProcopio as a contributor ([#138](https://github.com/kefranabg/readme-md-generator/issues/138)) [[d67b012](https://github.com/kefranabg/readme-md-generator/commit/d67b012fef502e296963c54f5cf3140a723c477d)] 109 | - 📝 Add tbetous as a contributor ([#137](https://github.com/kefranabg/readme-md-generator/issues/137)) [[a93cd56](https://github.com/kefranabg/readme-md-generator/commit/a93cd56c43ed420245824542e808e3b27654c3ec)] 110 | - 📝 Add david-dasilva as a contributor ([#133](https://github.com/kefranabg/readme-md-generator/issues/133)) [[f6f24e0](https://github.com/kefranabg/readme-md-generator/commit/f6f24e0be239214c5ff03d439aeb18007a513217)] 111 | - 📝 All contributor profile url fixed ([#129](https://github.com/kefranabg/readme-md-generator/issues/129)) [[06bd6f3](https://github.com/kefranabg/readme-md-generator/commit/06bd6f3bf9b8af68c2548bf57524e52939cd192f)] 112 | - 📝 Add Kushagra as a contributor ([#127](https://github.com/kefranabg/readme-md-generator/issues/127)) [[1624878](https://github.com/kefranabg/readme-md-generator/commit/1624878c90dc2f62cf5f692aa4fdedfc1d338f9c)] 113 | - :spakles: Add start script ([#121](https://github.com/kefranabg/readme-md-generator/issues/121)) [[bc3d383](https://github.com/kefranabg/readme-md-generator/commit/bc3d38301c5b09f301a75094d5aed6c85e130da6)] 114 | - 📝 Add bdougie as a contributor ([#117](https://github.com/kefranabg/readme-md-generator/issues/117)) [[2f327f7](https://github.com/kefranabg/readme-md-generator/commit/2f327f7547472093a030ded760508c90c1f21910)] 115 | 116 | 117 | 118 | ## 0.5.2 (2019-07-08) 119 | 120 | ### Added 121 | 122 | - ✨ Filter social network inputs to remove potential @ ([#93](https://github.com/kefranabg/readme-md-generator/issues/93)) [[ccae357](https://github.com/kefranabg/readme-md-generator/commit/ccae357ad2c8a3788a248075644cf27d312f226b)] 123 | 124 | ### Miscellaneous 125 | 126 | - Update readme and contributors to reflect username change ([#91](https://github.com/kefranabg/readme-md-generator/issues/91)) [[b2be60e](https://github.com/kefranabg/readme-md-generator/commit/b2be60e8bb429c9c440c6602f4f4ebec51b6d889)] 127 | 128 | 129 | 130 | ## 0.5.1 (2019-07-05) 131 | 132 | ### Fixed 133 | 134 | - 🐛 Prevent encoding utf-8 chars in README ([#88](https://github.com/kefranabg/readme-md-generator/issues/88)) [[8059756](https://github.com/kefranabg/readme-md-generator/commit/80597564e0fd9e0b8c69c333929d02e9ec9ae0b0)] 135 | 136 | 137 | 138 | ## 0.5.0 (2019-06-27) 139 | 140 | ### Added 141 | 142 | - ✨ Allow user to choose a non HTML README template ([#80](https://github.com/kefranabg/readme-md-generator/issues/80)) [[6d5c884](https://github.com/kefranabg/readme-md-generator/commit/6d5c8848c476fc2770204f215ddd6f48d539b4e0)] 143 | - ✨ Allow user to specify path to custom README template ([#68](https://github.com/kefranabg/readme-md-generator/issues/68)) [[e0d66c0](https://github.com/kefranabg/readme-md-generator/commit/e0d66c002c8108ff3ae142979a5c8003a28a8107)] 144 | - ✨ Get author name from package.json even if author prop is an object ([#75](https://github.com/kefranabg/readme-md-generator/issues/75)) [[688c338](https://github.com/kefranabg/readme-md-generator/commit/688c33833188a5487ff6df024d4993404ee0406c)] 145 | 146 | ### Changed 147 | 148 | - ⬆️ Update eslint to the latest version ([#70](https://github.com/kefranabg/readme-md-generator/issues/70)) [[88c96ac](https://github.com/kefranabg/readme-md-generator/commit/88c96ac31acfa12381a33d39a2953f3405053870)] 149 | - ⬆️ Update inquirer to the latest version ([#67](https://github.com/kefranabg/readme-md-generator/issues/67)) [[59f69e5](https://github.com/kefranabg/readme-md-generator/commit/59f69e51ec1caae17230d9331a2c14b04bd2825e)] 150 | - ♻️ Refactoring inquirer code ([#69](https://github.com/kefranabg/readme-md-generator/issues/69)) [[802d57d](https://github.com/kefranabg/readme-md-generator/commit/802d57d8af2e2cdcdbddea86c2fa2225db6d4516)] 151 | 152 | 153 | 154 | ## 0.4.0 (2019-06-20) 155 | 156 | ### Added 157 | 158 | - ✨ Ask for Patreon username ([#58](https://github.com/kefranabg/readme-md-generator/issues/58)) [[59b57af](https://github.com/kefranabg/readme-md-generator/commit/59b57af7aa5ce91ff0ce1998bff835d781b5df79)] 159 | - ✨ Enable funding [[af10690](https://github.com/kefranabg/readme-md-generator/commit/af10690857df92d29807a37abce055d5351f99f4)] 160 | - ✨ Add -v alias for --version option ([#50](https://github.com/kefranabg/readme-md-generator/issues/50)) [[a30cf02](https://github.com/kefranabg/readme-md-generator/commit/a30cf02531a4d26e85ed5e9db9ada6262d478711)] 161 | 162 | ### Fixed 163 | 164 | - 🐛 Prevent from using inquirer 6.4 [[688c754](https://github.com/kefranabg/readme-md-generator/commit/688c754584bfb7d9191eb30e18563e16f2a7b3ff)] 165 | 166 | ### Miscellaneous 167 | 168 | - 👥 Add contributors [[2533cd8](https://github.com/kefranabg/readme-md-generator/commit/2533cd8f2c8c78a043e67ca2bead9b4606606121)] 169 | - docs: add kefranabg as a contributor ([#57](https://github.com/kefranabg/readme-md-generator/issues/57)) [[e5d9d53](https://github.com/kefranabg/readme-md-generator/commit/e5d9d5376b8341e06005e497728935c2874631b6)] 170 | - 📝 Update README [[0dff1e5](https://github.com/kefranabg/readme-md-generator/commit/0dff1e5562404559b2ad64ccf0c8fc3d1df73f92)] 171 | - 📝 Replace static version badge by a dynamic one [[99a507d](https://github.com/kefranabg/readme-md-generator/commit/99a507d6ebf54b58aaf941e1752e7e4d16565a53)] 172 | - 📝 Update doc to use npx ([#55](https://github.com/kefranabg/readme-md-generator/issues/55)) [[77d6fdc](https://github.com/kefranabg/readme-md-generator/commit/77d6fdc4f0a2d9b86d3169f35ed8eab355ebcd68)] 173 | - 📝 Update badge list on README [[e87ed1a](https://github.com/kefranabg/readme-md-generator/commit/e87ed1ae92f04cc94e06a3e6339090588b3eaa17)] 174 | - 📝 Add available options in README ([#49](https://github.com/kefranabg/readme-md-generator/issues/49)) [[93da519](https://github.com/kefranabg/readme-md-generator/commit/93da51958d04335674201490cb90b9f04f5551bb)] 175 | 176 | 177 | 178 | ## 0.3.0 (2019-06-15) 179 | 180 | ### Added 181 | 182 | - ✨ Add --yes flag ([#45](https://github.com/kefranabg/readme-md-generator/issues/45)) [[f0e2bd6](https://github.com/kefranabg/readme-md-generator/commit/f0e2bd6aaa0ed7b47843482046d4c443db5643c7)] 183 | - ✨ Add prerequisites badges ([#38](https://github.com/kefranabg/readme-md-generator/issues/38)) [[e7e3590](https://github.com/kefranabg/readme-md-generator/commit/e7e3590023027f80acde722aa91824d9bc3d0b26)] 184 | - ✅ Add missing test to readme generation [[d5027e4](https://github.com/kefranabg/readme-md-generator/commit/d5027e4d0e0d31e9059729e208e20908b299f040)] 185 | - ✨ Add Twitter follow badge ([#35](https://github.com/kefranabg/readme-md-generator/issues/35)) [[58034a0](https://github.com/kefranabg/readme-md-generator/commit/58034a00bfdf6ea50e4ffabdd39657dfce317fee)] 186 | - ✨ Improve README template [[7a12c8a](https://github.com/kefranabg/readme-md-generator/commit/7a12c8a687f50da7fee1c2b2352363f3abf1e71f)] 187 | - ✨ Improve README content [[ffd6e1f](https://github.com/kefranabg/readme-md-generator/commit/ffd6e1f9653c31168bf025e797e05951bc9ab15a)] 188 | - ✨ Adding some commands [[6850244](https://github.com/kefranabg/readme-md-generator/commit/685024493f8670559df643b551f383b9cbf434b0)] 189 | - 🔊 Add logs in writeReadme [[772d536](https://github.com/kefranabg/readme-md-generator/commit/772d536a99fea4d0a8024fedb02664ae3a968351)] 190 | - ✨ Add homepage link if exist [[1c1bf08](https://github.com/kefranabg/readme-md-generator/commit/1c1bf08b29ef16a677114d74eb145d3a83f3c35a)] 191 | 192 | ### Changed 193 | 194 | - 💄 change prerequisite badges color ([#43](https://github.com/kefranabg/readme-md-generator/issues/43)) [[e888726](https://github.com/kefranabg/readme-md-generator/commit/e888726b471a28265ed689786f85c8618b9af957)] 195 | - 🔧 Move some dependencies in devDependencies [[0038201](https://github.com/kefranabg/readme-md-generator/commit/0038201ec94f1d31b3473948cdfd9f950466d4ad)] 196 | - 💬 Update badge version in README [[e5d418c](https://github.com/kefranabg/readme-md-generator/commit/e5d418c2b3b7e6fcbb2e5631adb70b51c2b82843)] 197 | 198 | ### Fixed 199 | 200 | - 🐛 Fix current working directory error ([#46](https://github.com/kefranabg/readme-md-generator/issues/46)) [[6c184ba](https://github.com/kefranabg/readme-md-generator/commit/6c184ba009cfafb25adb1ff7ba6e1c4570de7503)] 201 | - 🐛 Fix error when package.json does not exist ([#42](https://github.com/kefranabg/readme-md-generator/issues/42)) [[fe0fc61](https://github.com/kefranabg/readme-md-generator/commit/fe0fc61e4664baf457e1d6f4a35e50e8cd325c10)] 202 | - ✏️ Remove whitespace before punctuation marks ([#36](https://github.com/kefranabg/readme-md-generator/issues/36)) [[094f9fb](https://github.com/kefranabg/readme-md-generator/commit/094f9fb5e7ebe6cc9001d1fe54cd9be0362730bd)] 203 | - 🐛 Get doc url from only if github repos [[62109c4](https://github.com/kefranabg/readme-md-generator/commit/62109c4b315613100b7adf9b9714b5a7e6c00b31)] 204 | 205 | 206 | 207 | ## 0.1.4 (2019-06-11) 208 | 209 | ### Added 210 | 211 | - ✨ Add instruction in README [[8f32674](https://github.com/kefranabg/readme-md-generator/commit/8f3267442fbd1673e9c13969858c2075864d8be6)] 212 | - ✅ Add missing test for cli [[cdd7df6](https://github.com/kefranabg/readme-md-generator/commit/cdd7df640401fbce044c0e042bc02a8ede09b818)] 213 | - ✨ Improve README content [[9bb7e90](https://github.com/kefranabg/readme-md-generator/commit/9bb7e909d838bc6ae364f8ca40115adda590a9c4)] 214 | - ✅ Add cli tests [[2c5ed1e](https://github.com/kefranabg/readme-md-generator/commit/2c5ed1e1fb249f4f10e6ec7b304b1df1fbb38ec7)] 215 | - ✅ Add tests for project infos module [[aae1bc6](https://github.com/kefranabg/readme-md-generator/commit/aae1bc6872a60feb2cfd6e50057be857f6a87db8)] 216 | - ✨ Improve README content [[34d475c](https://github.com/kefranabg/readme-md-generator/commit/34d475cf4d446317430587d5b50644cf18049975)] 217 | - ✅ Add tests for questions [[576a290](https://github.com/kefranabg/readme-md-generator/commit/576a290f77cab6376c50d9edf3d6c51e572988af)] 218 | - ✅ Add tests for readme file [[0e18c2e](https://github.com/kefranabg/readme-md-generator/commit/0e18c2e73a16fb0edc039e3d28de52691c9409df)] 219 | - ✨ Enable code cov ([#23](https://github.com/kefranabg/readme-md-generator/issues/23)) [[46d21b5](https://github.com/kefranabg/readme-md-generator/commit/46d21b5df0f26cba8f9d3eb42492a129d3dc4dd1)] 220 | - ✨ Add log infos during process [[912c602](https://github.com/kefranabg/readme-md-generator/commit/912c602c9022ebcfd84f66ffce96ab814f424cd7)] 221 | - ✨ Improve README [[a8ff226](https://github.com/kefranabg/readme-md-generator/commit/a8ff22685daac89d22ac1756b480a2f6777d0f50)] 222 | - ✨ Improve README [[83796b2](https://github.com/kefranabg/readme-md-generator/commit/83796b29d055a52b9c7a67f9e9405141972b236c)] 223 | - ✨ Add some badges [[3174ab9](https://github.com/kefranabg/readme-md-generator/commit/3174ab9830b47919559a2c4cdc73b4a598b9e9d9)] 224 | - ✨ Add documentation badge [[dd435ca](https://github.com/kefranabg/readme-md-generator/commit/dd435ca85d2d5fa00b8cd28d956b6ff8ce08ea1f)] 225 | - ✨ Add license url and name auto detection [[d2ee432](https://github.com/kefranabg/readme-md-generator/commit/d2ee432d5cd09b163efcfccf7e37354754eb589b)] 226 | - ✨ Add prerequisites section in README [[9924f72](https://github.com/kefranabg/readme-md-generator/commit/9924f723afacee5c03fddd6b0436edf207cd618f)] 227 | - ✨ Add npm version infos [[9ce8bb6](https://github.com/kefranabg/readme-md-generator/commit/9ce8bb64924404f01f2e5a0105b3241b15be1d65)] 228 | - ✨ Add default value for github username if repos is github [[a59520f](https://github.com/kefranabg/readme-md-generator/commit/a59520ffd877cc0a091343d764733d058f6862c1)] 229 | - ✨ Add contributing section [[d0f61f9](https://github.com/kefranabg/readme-md-generator/commit/d0f61f93f25998c2020f14e77165f0990cf43fb4)] 230 | - ✨ Improve README content [[01134d2](https://github.com/kefranabg/readme-md-generator/commit/01134d2b9d8f347335dc132b1c9ea1edddc1aac4)] 231 | - ✨ Improve README content [[5c4c2e8](https://github.com/kefranabg/readme-md-generator/commit/5c4c2e804429e75f912cd45c0192a990d1287a3e)] 232 | - ✨ Improve README content [[0b4fe40](https://github.com/kefranabg/readme-md-generator/commit/0b4fe40281f447132011a3326847e6b6829d08ac)] 233 | - ✨ Improve support section in README [[91a6f9e](https://github.com/kefranabg/readme-md-generator/commit/91a6f9e785a51cfdbc945b759216ef5e1708d682)] 234 | - ✨ Add license to README [[096d44f](https://github.com/kefranabg/readme-md-generator/commit/096d44fc89108fb91ec51b2d10a6a71fdb274ae5)] 235 | - ✨ Renaming questions function names [[8907db0](https://github.com/kefranabg/readme-md-generator/commit/8907db008882dd5b7a76864e59452ee5e7b9e5d7)] 236 | - ✨ Add Greenkeeper 🌴 [[f69b4ad](https://github.com/kefranabg/readme-md-generator/commit/f69b4adc5307be9492dc4ea370696cd84ebf8d53)] 237 | - ✨ Add support section in README [[b16af00](https://github.com/kefranabg/readme-md-generator/commit/b16af00df801992cdd56df54cbb8a913279ab544)] 238 | - ✨ Add footer in README [[3d2cd7d](https://github.com/kefranabg/readme-md-generator/commit/3d2cd7d2d0d6c737d18b3e68b8e19ef40d316e4f)] 239 | - ✨ Update README.md [[699f250](https://github.com/kefranabg/readme-md-generator/commit/699f250223ddd8d11872e3e3023056b15740d3ea)] 240 | - ✨ Add author information in README [[2337d6b](https://github.com/kefranabg/readme-md-generator/commit/2337d6b6a8a933bbb55d4ab6489999dc2fb74363)] 241 | - ✨ Improve author infos logic [[5d4cf6f](https://github.com/kefranabg/readme-md-generator/commit/5d4cf6fe9f790d4d2ee6bcc151e8283c7ac06655)] 242 | - ✨ Add author information in README [[665303a](https://github.com/kefranabg/readme-md-generator/commit/665303a623f359d819872faae4e432cb95e82a8c)] 243 | - 🎉 Initial commit [[500b2e8](https://github.com/kefranabg/readme-md-generator/commit/500b2e81875aeb61f94b6202bcb5d0b264cf7413)] 244 | 245 | ### Changed 246 | 247 | - 🎨 Improve README structure [[1ced8d6](https://github.com/kefranabg/readme-md-generator/commit/1ced8d6ff426f5806d110f2c92b653547187c6c5)] 248 | - 💬 Fix README format [[20005b6](https://github.com/kefranabg/readme-md-generator/commit/20005b6d243d26a8ab8d049e722776218796bb82)] 249 | - 🔧 Update jest config [[3a9d847](https://github.com/kefranabg/readme-md-generator/commit/3a9d84742423d9da5b1c3c19dc96a1846010a44d)] 250 | - 🔧 Add jest config [[5f629ed](https://github.com/kefranabg/readme-md-generator/commit/5f629edd4bb0f9d2eb7b6e7749a92454468c08a3)] 251 | - ♻️ Code refactoring [[79cfbbf](https://github.com/kefranabg/readme-md-generator/commit/79cfbbf7e6cc5eefb6cd1f2a1a6809f4a9c9c593)] 252 | - ♻️ CLI refactoring [[ead151b](https://github.com/kefranabg/readme-md-generator/commit/ead151b9a7c2e177cd9859fb3691c530f09ded3c)] 253 | - ♻️ Code refactoring [[1ae6c40](https://github.com/kefranabg/readme-md-generator/commit/1ae6c40fb89f9099f0dc38e5bb843d2719d23b07)] 254 | - 💬 Rename command in README [[4279e2e](https://github.com/kefranabg/readme-md-generator/commit/4279e2e99e9e21a58104632478be612f9f80dbf8)] 255 | - 💬 Update project name [[2cad8e3](https://github.com/kefranabg/readme-md-generator/commit/2cad8e3cfca8afc33585f0de5e11378add9dad26)] 256 | - 🔧 Add bin config [[56d7e78](https://github.com/kefranabg/readme-md-generator/commit/56d7e7840a48f8d6d653db00538303a2dac07a77)] 257 | - 💬 Improve questions text [[67d611b](https://github.com/kefranabg/readme-md-generator/commit/67d611b4ba9489bdf2297e2ba120eddee0368a7a)] 258 | - ♻️ Utils code refactoring [[97d675e](https://github.com/kefranabg/readme-md-generator/commit/97d675efb088139949cf57e1bc0c8d534f41b136)] 259 | - ♻️ Code refactoring [[c14974c](https://github.com/kefranabg/readme-md-generator/commit/c14974c5b24e370c4af54ca1a7d71375eeb2bfc2)] 260 | - 🎨 Improve README title [[8c79b5e](https://github.com/kefranabg/readme-md-generator/commit/8c79b5efbd5e8b94497b00164642796ce0b29033)] 261 | - 🎨 Improve support section in README [[e855e38](https://github.com/kefranabg/readme-md-generator/commit/e855e385e0c3a8304e7e42cde4162c392269d787)] 262 | - 💬 Update project description and README footer texts [[b1c9a87](https://github.com/kefranabg/readme-md-generator/commit/b1c9a877874b5dd925e072d84cc8e8f8cd856bab)] 263 | - 🎨 Improve template format [[d27649f](https://github.com/kefranabg/readme-md-generator/commit/d27649f1a0a3f6344c74a735040436010dec86b8)] 264 | - ♻️ Questions and utils code refactoring [[a5b5a4c](https://github.com/kefranabg/readme-md-generator/commit/a5b5a4c95da8643af319299448d93db0123fafa1)] 265 | - ♻️ Questions and utils functions refactoring [[177c086](https://github.com/kefranabg/readme-md-generator/commit/177c08699989328d5ceeb71c139b196f58bd23c0)] 266 | - 🚨 Fix file format with prettier [[aa6e3eb](https://github.com/kefranabg/readme-md-generator/commit/aa6e3eb6081cdc0d34c872c078f3a9e415a45d2b)] 267 | 268 | ### Removed 269 | 270 | - 🔥 Remove unused export [[bdc0e3e](https://github.com/kefranabg/readme-md-generator/commit/bdc0e3eb4d2d37240715abb1703eedcbe0dc5e2a)] 271 | 272 | ### Fixed 273 | 274 | - 🐛 Fix badge bad link [[d8c8e45](https://github.com/kefranabg/readme-md-generator/commit/d8c8e45e77f8f5f8c68b60c176da09808e4cbb4c)] 275 | - 🐛 Fix bug when engines infos were not found [[918140e](https://github.com/kefranabg/readme-md-generator/commit/918140e6ad98f01279b5b2b617d4976b330faecd)] 276 | - 🐛 Fix badges links [[acd0a22](https://github.com/kefranabg/readme-md-generator/commit/acd0a22b9885501503d298ed125942b043884c82)] 277 | - 🐛 Fix badge display [[623afd9](https://github.com/kefranabg/readme-md-generator/commit/623afd9cc1831e144bcdf1ba0c0d0fc4c75c9324)] 278 | - 🐛 Fix author infos [[2d8b2c2](https://github.com/kefranabg/readme-md-generator/commit/2d8b2c25096fbc05ac1a24aadfd68e93899782e3)] 279 | 280 | ### Miscellaneous 281 | 282 | - 0.1.4 [[65ddd76](https://github.com/kefranabg/readme-md-generator/commit/65ddd767df2712ca39527a4a30dbbcffa00be87a)] 283 | - 📝 Add demo to README [[19f04e2](https://github.com/kefranabg/readme-md-generator/commit/19f04e232f9363d72b9c0c5e7c8c7f4ab363528d)] 284 | - Merge pull request [#20](https://github.com/kefranabg/readme-md-generator/issues/20) from kefranabg/greenkeeper/initial [[acfa940](https://github.com/kefranabg/readme-md-generator/commit/acfa9407c90edd2eff549f1d21a48b3add60428a)] 285 | - docs(readme): add Greenkeeper badge [[d3ad805](https://github.com/kefranabg/readme-md-generator/commit/d3ad80571868c75e84de42cad13f3af64582bf8f)] 286 | - 📝 Update project description [[6fadc05](https://github.com/kefranabg/readme-md-generator/commit/6fadc05bffd78dfa9855578ce4af708950dc7c4c)] 287 | - Update README [[ee6f44f](https://github.com/kefranabg/readme-md-generator/commit/ee6f44f49ef29dba8163898a36f24efc656f762c)] 288 | - docs(readme): add Greenkeeper badge [[1d6fe09](https://github.com/kefranabg/readme-md-generator/commit/1d6fe09fd824be7489c10978a228acac287360da)] 289 | - Initial commit [[dd97da0](https://github.com/kefranabg/readme-md-generator/commit/dd97da0e0a4540806ec83152b586dd0b8f8421a1)] 290 | --------------------------------------------------------------------------------