(https://oluwasegun-aa.github.io/)",
43 | "license": "MIT",
44 | "bugs": {
45 | "url": "https://github.com/Oluwasegun-AA/md-generator/issues"
46 | },
47 | "homepage": "https://github.com/Oluwasegun-AA/md-generator#readme",
48 | "husky": {
49 | "hooks": {
50 | "pre-push": "lint-staged"
51 | }
52 | },
53 | "lint-staged": {
54 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
55 | "prettier --write",
56 | "git add"
57 | ]
58 | },
59 | "dependencies": {
60 | "boxen": "4.1.0",
61 | "chalk": "2.4.2",
62 | "commander": "3.0.1",
63 | "ejs": "^3.1.8",
64 | "git-repo-name": "1.0.1",
65 | "inquirer": "6.4.1",
66 | "load-json-file": "6.2.0",
67 | "ora": "4.0.1",
68 | "pad": "3.2.0"
69 | },
70 | "devDependencies": {
71 | "@babel/cli": "^7.19.3",
72 | "@babel/core": "7.4.5",
73 | "@babel/plugin-transform-runtime": "^7.19.6",
74 | "@babel/preset-env": "7.4.5",
75 | "@babel/runtime": "7.4.5",
76 | "@types/chai": "4.2.6",
77 | "@types/chai-spies": "1.0.1",
78 | "@types/ejs": "2.7.0",
79 | "@types/git-repo-name": "1.0.0",
80 | "@types/inquirer": "6.5.0",
81 | "@types/lodash": "4.14.149",
82 | "@types/mocha": "5.2.7",
83 | "@types/node": "12.12.14",
84 | "babel-node": "^0.0.1-security",
85 | "chai": "4.2.0",
86 | "chai-spies": "1.0.0",
87 | "coveralls": "3.0.9",
88 | "cross-env": "6.0.3",
89 | "eslint": "5.3.0",
90 | "eslint-config-airbnb": "17.1.0",
91 | "eslint-config-airbnb-base": "13.1.0",
92 | "eslint-plugin-import": "2.18.0",
93 | "husky": "3.0.4",
94 | "lint-staged": "9.2.3",
95 | "lodash": "4.17.21",
96 | "mocha": "10.1.0",
97 | "nyc": "14.1.1",
98 | "prettier": "1.18.2",
99 | "ts-node": "8.5.4",
100 | "tslint-config-airbnb": "5.11.2",
101 | "typescript": "3.7.2"
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/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
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at oluwasegunadepoju@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
--------------------------------------------------------------------------------
/src/templates/files/required/template-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
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at <%= authorEmail %>. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/src/templates/files/required/template-noHtml-README.md:
--------------------------------------------------------------------------------
1 | # Welcome to <%= projectName %> 👋
2 | <% if (isProjectOnNpm) { -%>
3 | [](https://www.npmjs.com/package/<%= projectName %>)
4 | <% } -%>
5 | <% if (projectVersion && !isProjectOnNpm) { -%>
6 | 
7 | <% } -%>
8 | <% if (projectPrerequisites) { -%>
9 | <% projectPrerequisites.map(({ name, value }) => { -%>
10 |  %>-blue.svg)
11 | <% }) -%>
12 | <% } -%>
13 | <% if (projectDocumentationUrl) { -%>
14 | [](<%= projectDocumentationUrl %>)
15 | <% } -%>
16 | <% if (isGithubRepos) { -%>
17 | [](<%= repositoryUrl %>/graphs/commit-activity)
18 | <% } -%>
19 | <% if (licenseName.name && licenseUrl) { -%>
20 | [](<%= licenseUrl %>)
21 | <% } -%>
22 | <% if (authorTwitterUsername) { -%>
23 | [](https://twitter.com/<%= authorTwitterUsername %>)
24 | <% } -%>
25 | <% if (projectDescription) { -%>
26 |
27 | ## Table of Contents
28 | * [Project Overview](#Project-Overview)
29 | * [Prerequisites](#Prerequisites)
30 | * [Features](#Features)
31 | * [Demo](#demo)
32 | * [Usage](#Usage)
33 | * [Installation](#Installation)
34 | * [Known Issues](#Known-issues)
35 | * [Contributing](#contributing)
36 | * [support](#support)
37 | * [License](#License)
38 |
39 | > <%= projectDescription %>
40 | <% } -%>
41 | <% if (projectHomepage) { -%>
42 |
43 | ## Project-Overview
44 |
45 | ### 🏠 [Homepage](<%= projectHomepage %>)
46 | <% } -%>
47 | <% if (projectPrerequisites && projectPrerequisites.length) { -%>
48 |
49 | ## Prerequisites
50 |
51 | <% projectPrerequisites.map(({ name, value }) => { -%>
52 | - <%= name %> <%= value %>
53 | <% }) -%>
54 | <% } -%>
55 | <% if (installCommand) { -%>
56 |
57 | ## Install
58 |
59 | ```sh
60 | <%= installCommand %>
61 | ```
62 | <% } -%>
63 | <% if (usage) { -%>
64 |
65 | ## Usage
66 |
67 | ```sh
68 | <%= usage %>
69 | ```
70 | <% } -%>
71 | <% if (testCommand) { -%>
72 |
73 | ## Run tests
74 |
75 | ```sh
76 | <%= testCommand %>
77 | ```
78 | <% } -%>
79 | <% if (authorName || authorTwitterUsername || authorGithubUsername) { -%>
80 |
81 | ## Known Issues
82 | No known [issues](<%= contributingUrl %>) at the moment.
83 |
84 | ## Author
85 | <% if (authorName) { %>
86 | 👤 **<%= authorName %>**
87 | <% } %>
88 | <% if (authorTwitterUsername) { -%>
89 | * Twitter: [@<%= authorTwitterUsername %>](https://twitter.com/<%= authorTwitterUsername %>)
90 | <% } -%>
91 | <% if (authorGithubUsername) { -%>
92 | * Github: [@<%= authorGithubUsername %>](https://github.com/<%= authorGithubUsername %>)
93 | <% } -%>
94 | <% } -%>
95 | <% if (contributingUrl) { -%>
96 |
97 | ## 🤝 Contributing
98 |
99 | Contributions, issues and feature requests are welcome!
100 |
101 | Feel free to check [issues page](<%= contributingUrl %>).
102 | <% } -%>
103 |
104 | ## Show your support
105 |
106 | Give a ⭐️ if this project helped you!
107 | <% if (authorPatreonUsername) { -%>
108 |
109 | [](https://www.patreon.com/<%= authorPatreonUsername %>)
110 | <% } -%>
111 |
112 | <% if (licenseName && licenseUrl) { -%>
113 |
114 | ## 📝 License
115 |
116 | <% if (authorName && authorGithubUsername) { -%>
117 | Copyright © <%= currentYear %> [<%= authorName %>](https://github.com/<%= authorGithubUsername %>).
118 |
119 | <% } -%>
120 | This project is [<%= licenseName.name %>](<%= licenseUrl %>) licensed.
121 | <% } -%>
122 |
123 | ***
124 | <%- include('../footer.md'); -%>
125 |
--------------------------------------------------------------------------------
/src/templates/files/required/template-html-README.md:
--------------------------------------------------------------------------------
1 | Welcome to <%= projectName %> 👋
2 |
3 | <% if (isProjectOnNpm) { -%>
4 |
5 |
6 |
7 | <% } -%>
8 | <% if (projectVersion && !isProjectOnNpm) { -%>
9 |
10 | <% } -%>
11 | <% if (projectPrerequisites) { -%>
12 | <% projectPrerequisites.map(({ name, value }) => { -%>
13 |
14 | <% }) -%>
15 | <% } -%>
16 | <% if (projectDocumentationUrl) { -%>
17 |
18 |
19 |
20 | <% } -%>
21 | <% if (isGithubRepos) { -%>
22 |
23 |
24 |
25 | <% } -%>
26 | <% if (licenseName.name && licenseUrl) { -%>
27 |
28 |
29 |
30 | <% } -%>
31 | <% if (authorTwitterUsername) { -%>
32 |
33 |
34 |
35 | <% } -%>
36 |
37 |
38 | ## Table of Contents
39 | * [Project Overview](#Project-Overview)
40 | * [Prerequisites](#Prerequisites)
41 | * [Features](#Features)
42 | * [Demo](#demo)
43 | * [Usage](#Usage)
44 | * [Installation](#Installation)
45 | * [Known Issues](#Known-issues)
46 | * [Contributing](#contributing)
47 | * [support](#support)
48 | * [License](#License)
49 |
50 |
51 | ## Project-Overview
52 |
53 | <% if (projectDescription) { -%>
54 |
55 | > <%= projectDescription %>
56 | <% } -%>
57 | <% if (projectHomepage) { -%>
58 |
59 | ### 🏠 [Homepage](<%= projectHomepage %>)
60 | <% } -%>
61 | <% if (projectPrerequisites && projectPrerequisites.length) { -%>
62 |
63 | ## Prerequisites
64 |
65 | <% projectPrerequisites.map(({ name, value }) => { -%>
66 | - <%= name %> <%= value %>
67 | <% }) -%>
68 | <% } -%>
69 | <% if (installCommand) { -%>
70 |
71 | ## Installation
72 |
73 | ```sh
74 | <%= installCommand %>
75 | ```
76 | <% } -%>
77 | <% if (usage) { -%>
78 |
79 | ## Features
80 |
81 | - [x]
82 | - [x]
83 | - [x]
84 |
85 | ## Usage
86 |
87 | ```sh
88 | <%= usage %>
89 | ```
90 | <% } -%>
91 | <% if (testCommand) { -%>
92 |
93 | ## Run tests
94 |
95 | ```sh
96 | <%= testCommand %>
97 | ```
98 | <% } -%>
99 | <% if (authorName || authorTwitterUsername || authorGithubUsername) { -%>
100 |
101 | ## Known Issues
102 | No known [issues](<%= contributingUrl %>) at the moment.
103 |
104 | ## Author
105 | <% if (authorName) { %>
106 | 👤 **<%= authorName %>**
107 | <% } %>
108 | <% if (authorTwitterUsername) { -%>
109 | * Twitter: [@<%= authorTwitterUsername %>](https://twitter.com/<%= authorTwitterUsername %>)
110 | <% } -%>
111 | <% if (authorGithubUsername) { -%>
112 | * Github: [@<%= authorGithubUsername %>](https://github.com/<%= authorGithubUsername %>)
113 | <% } -%>
114 | <% } -%>
115 | <% if (contributingUrl) { -%>
116 |
117 | ## 🤝 Contributing
118 |
119 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](<%= contributingUrl %>).
120 | <% } -%>
121 |
122 | ## support
123 |
124 | Give a ⭐️ if this project helped you!
125 | <% if (authorPatreonUsername) { -%>
126 |
127 |
128 |
129 |
130 | <% } -%>
131 | <% if (licenseName.name && licenseUrl) { -%>
132 |
133 | ## 📝 License
134 |
135 | <% if (authorName && authorGithubUsername) { -%>
136 | Copyright © <%= currentYear %> [<%= authorName %>](https://github.com/<%= authorGithubUsername %>).
137 | <% } -%>
138 | This project is [<%= licenseName.name %>](<%= licenseUrl %>) licensed.
139 | <% } -%>
140 |
141 | ***
142 | <%- include('../footer.md'); -%>
143 |
144 |
--------------------------------------------------------------------------------
/src/core/actions/removeActions/index.ts:
--------------------------------------------------------------------------------
1 | import inquirer from 'inquirer';
2 | import fs from 'fs';
3 | import {
4 | allExistingFiles,
5 | removeDotMdAttribute,
6 | allFiles,
7 | requiredFiles,
8 | optionalFiles,
9 | queryFilesExistence,
10 | } from '../actionsUtils';
11 | import {
12 | removeFiles,
13 | validateRemove,
14 | removeRequired,
15 | removeOptional,
16 | } from '../../questions/setupQuestions/index';
17 | import {
18 | log,
19 | useBox,
20 | castElementsToFormatedString,
21 | } from '../../../common/index';
22 | import { IAllFiles, ICurrentFile } from '../../../../types/typeDeclarations.interface';
23 |
24 | /**
25 | * @description
26 | * Delete file from the codebase
27 | *
28 | * @param filesArray name of files to be deleted (array of strings)
29 | * @param allFiles All files Available files
30 | */
31 | export const deleteFromCodebase = (filesArray: string[], allFiles: any) => {
32 | filesArray.forEach((file: string) => {
33 | const { path }: ICurrentFile = allFiles[file];
34 | fs.unlink(path, (err: any) => {
35 | if (err) log(`Could not delete ${file}`);
36 | });
37 | });
38 | useBox('File(s) removed successfully \nThanks for using md-generator');
39 | };
40 |
41 | /**
42 | * @description
43 | * Handles the delete process, validating if no file is supplied
44 | *
45 | * @param validFilesArray name of Valid files to be deleted (array of strings)
46 | * @param allFiles All files Available files
47 | */
48 | export const deleteFiles = (validFilesArray: string[], allFiles: IAllFiles): void => {
49 | if (validFilesArray.length === 0) {
50 | return log('Error: No file selected, Please select a file\n');
51 | }
52 | inquirer
53 | .prompt(validateRemove(castElementsToFormatedString(validFilesArray)))
54 | .then((answer: any) => {
55 | const { removeFiles }: {removeFiles: boolean} = answer;
56 | if (removeFiles) {
57 | deleteFromCodebase(validFilesArray, allFiles);
58 | }
59 | });
60 | };
61 |
62 | /**
63 | * @description
64 | * Handles the delete process, validating if the filename is valid and if it exists
65 | *
66 | * @param selectedFiles All files parsed to be deleted
67 | * @param filesInfo All available files
68 | * @param mode Question mode
69 | */
70 | const processRemoval = (selectedFiles: string[], filesInfo: IAllFiles, mode: any): void => {
71 | if (selectedFiles.length === 0) {
72 | return log('Error: .md file(s) not found in the codebase\n');
73 | }
74 | inquirer.prompt(mode(selectedFiles)).then((answer: any) => {
75 | const selectedFiles = removeDotMdAttribute(answer.removeFiles);
76 | deleteFiles(selectedFiles, filesInfo);
77 | });
78 | };
79 |
80 | /**
81 | * @description handles item removal when command is passed without an option
82 | */
83 | const removeNonSpecific = (): void => {
84 | processRemoval(allExistingFiles(), allFiles, removeFiles);
85 | };
86 |
87 | /**
88 | * @description
89 | * Handles removal when command is passed with option -F or --file
90 | * @param values Array of file names supplied
91 | */
92 | export const removeSpecificFiles = (values: ICurrentFile[]): void => {
93 | const { foundFiles, filesNotFound } = queryFilesExistence(values);
94 | if (filesNotFound.length > 0) {
95 | const filesList: string = castElementsToFormatedString(filesNotFound);
96 | log('The following file(s) were not found :\n', `${filesList} \n`);
97 | }
98 | if (foundFiles.length > 0) {
99 | deleteFiles(foundFiles, allFiles);
100 | }
101 | };
102 |
103 | /**
104 | * @description
105 | * handles files removals when the option -R or --required is used
106 | */
107 | const removeRequiredFiles = (): void => {
108 | processRemoval(
109 | allExistingFiles(requiredFiles),
110 | requiredFiles,
111 | removeRequired
112 | );
113 | };
114 |
115 | /**
116 | * @description
117 | * handles files removals when the option -O or --optional is used
118 | */
119 | const removeOptionalFiles = (): void => {
120 | processRemoval(
121 | allExistingFiles(optionalFiles),
122 | optionalFiles,
123 | removeOptional
124 | );
125 | };
126 |
127 | /**
128 | * @description
129 | * removes file in the codebase based on the arguments passed
130 | *
131 | * @param values arguments i.e command, payload and command options
132 | */
133 | const removeHandler = (values: any) => {
134 | const {
135 | file,
136 | required,
137 | optional,
138 | resp
139 | } = values;
140 | if (required) return removeRequiredFiles();
141 | if (optional) return removeOptionalFiles();
142 | if (file) return removeSpecificFiles(resp);
143 | removeNonSpecific();
144 | };
145 |
146 | export default removeHandler;
147 |
--------------------------------------------------------------------------------
/src/projectEnv/projectInfo.ts:
--------------------------------------------------------------------------------
1 | import { isNil, get, has } from 'lodash';
2 | import { execSync } from 'child_process';
3 | import { getPackageJson, getProjectName } from './utils';
4 | import { IProjectInfos } from '../../types/typeDeclarations.interface';
5 |
6 | const GITHUB_URL = 'https://github.com/';
7 |
8 | /**
9 | * @description Clean repository url by removing '.git' and 'git+'
10 | *
11 | * @param repoUrl repository url
12 | */
13 | const cleanRepoUrl = (repoUrl: any): string =>
14 | repoUrl
15 | .replace('\n', '')
16 | .replace('git+', '')
17 | .replace('.git', '');
18 |
19 | /**
20 | * @description Get repository url from package.json
21 | */
22 | const getRepoUrlFromPackageJson = async (packageJson: any): Promise => {
23 | const repoUrl = get(packageJson, 'repository.url', undefined);
24 | return isNil(repoUrl) ? undefined : cleanRepoUrl(repoUrl);
25 | };
26 |
27 | /**
28 | * @description Get repository url from git
29 | */
30 | export const getRepoUrlFromGit = (): any => {
31 | try {
32 | const stdout = execSync('git config --get remote.origin.url');
33 | return cleanRepoUrl(stdout);
34 | } catch (err) {
35 | return undefined;
36 | }
37 | };
38 |
39 | /**
40 | * @description Get repository url from package.json or git
41 | *
42 | * @param packageJson package.json file
43 | */
44 | const getRepoUrl = async (packageJson?: any): Promise =>
45 | (await getRepoUrlFromPackageJson(packageJson)) || getRepoUrlFromGit();
46 |
47 | /**
48 | * @description Get repository issues url from package.json or git
49 | *
50 | * @param packageJson package.json file
51 | */
52 | export const getRepoIssuesUrl = async (packageJson: any): Promise => {
53 | let repoIssuesUrl = get(packageJson, 'bugs.url', undefined);
54 |
55 | if (isNil(repoIssuesUrl)) {
56 | const repoUrl = await getRepoUrl();
57 |
58 | if (!isNil(repoUrl)) {
59 | repoIssuesUrl = `${repoUrl}/issues`;
60 | }
61 | }
62 | return repoIssuesUrl;
63 | };
64 |
65 | /**
66 | * @description Check if repository is a Github repository
67 | *
68 | * @param repositoryUrl repository URL
69 | */
70 | const isGithubRepository = (repositoryUrl: string): boolean =>
71 | !isNil(repositoryUrl) && repositoryUrl.includes(GITHUB_URL);
72 |
73 | /**
74 | * @description Get github username from repository url
75 | *
76 | * @param repositoryUrl repository URL
77 | */
78 | const getGithubUsernameFromRepositoryUrl = (repositoryUrl: string): string =>
79 | repositoryUrl.replace(GITHUB_URL, '').split('/')[0];
80 |
81 | /**
82 | * @description Get license url from github repository url
83 | *
84 | * @param repositoryUrl repository URL
85 | */
86 | const getLicenseUrlFromGithubRepositoryUrl = (repositoryUrl: string): string =>
87 | `${repositoryUrl}/blob/master/LICENSE`;
88 |
89 | const getReadmeUrlFromGithubRepositoryUrl = (repositoryUrl: string): string =>
90 | `${repositoryUrl}#readme`;
91 |
92 | /**
93 | * @description Get project author name from package.json
94 | *
95 | * @param packageJson package.json file
96 | * @returns authorName
97 | */
98 | export const getAuthorName = (packageJson: any): any => {
99 | if (has(packageJson, 'author.name')) {
100 | return get(packageJson, 'author.name', undefined);
101 | }
102 | if (has(packageJson, 'author') && typeof packageJson.author === 'string') {
103 | return get(packageJson, 'author', undefined);
104 | }
105 | return undefined;
106 | };
107 |
108 | /**
109 | * @description Get project information from git and package.json
110 | */
111 | const getProjectInfos = async (): Promise => {
112 | const packageJson = await getPackageJson();
113 | const name = getProjectName(packageJson);
114 | const description = get(packageJson, 'description', undefined);
115 | const engines = get(packageJson, 'engines', undefined);
116 | const author = getAuthorName(packageJson);
117 | const version = get(packageJson, 'version', undefined);
118 | const licenseName = { name: get(packageJson, 'license', undefined) };
119 | const homepage = get(packageJson, 'homepage', undefined);
120 | const usage = has(packageJson, 'scripts.start') ? 'npm run start' : undefined;
121 | const testCommand = has(packageJson, 'scripts.test')
122 | ? 'npm run test'
123 | : undefined;
124 | const repositoryUrl = await getRepoUrl(packageJson);
125 | const contributingUrl = await getRepoIssuesUrl(packageJson);
126 | const isGithubRepo = isGithubRepository(repositoryUrl);
127 | const documentationUrl = isGithubRepo
128 | ? getReadmeUrlFromGithubRepositoryUrl(repositoryUrl)
129 | : undefined;
130 | const githubUsername = isGithubRepo
131 | ? getGithubUsernameFromRepositoryUrl(repositoryUrl)
132 | : undefined;
133 | const licenseUrl = isGithubRepo
134 | ? getLicenseUrlFromGithubRepositoryUrl(repositoryUrl)
135 | : undefined;
136 |
137 | return {
138 | name,
139 | description,
140 | version,
141 | author,
142 | homepage,
143 | repositoryUrl,
144 | contributingUrl,
145 | githubUsername,
146 | engines,
147 | licenseName,
148 | licenseUrl,
149 | documentationUrl,
150 | isGithubRepo,
151 | usage,
152 | testCommand,
153 | };
154 | };
155 |
156 | export default getProjectInfos;
157 |
--------------------------------------------------------------------------------
/types/typeDeclarations.interface.ts:
--------------------------------------------------------------------------------
1 | export interface IBOX_CONFIG {
2 | padding: number;
3 | margin: { top: number; bottom: number };
4 | borderColor: string;
5 | align: string;
6 | borderStyle: string;
7 | };
8 |
9 | export interface ICurrentFile {
10 | name: string;
11 | exists: boolean;
12 | path: string;
13 | templatePath?: string;
14 | };
15 |
16 | export interface IProjectInfos {
17 | name: string | any;
18 | description: string | any;
19 | version: string | any;
20 | author: string | any;
21 | homepage: string | any;
22 | repositoryUrl?: string | any;
23 | contributingUrl: string | any;
24 | githubUsername: string | any;
25 | engines: string | any;
26 | licenseName: {
27 | name: string | any;
28 | };
29 | licenseUrl: string | any;
30 | documentationUrl: string | any;
31 | isGithubRepo: boolean | any;
32 | usage: string | any;
33 | testCommand: string | any;
34 | isGithubRepos?: boolean | any;
35 | projectName?: string | any;
36 | projectPrerequisites?: any | any;
37 | isProjectOnNpm?: any | any;
38 | };
39 |
40 | export interface IProjectInfosWithMissingFields {
41 | name?: string | any;
42 | description?: string | any;
43 | version?: string | any;
44 | author?: string | any;
45 | homepage?: string | any;
46 | repositoryUrl?: string | any;
47 | contributingUrl?: string | any;
48 | githubUsername?: string | any;
49 | engines?: string | any;
50 | licenseName?: {
51 | name?: string | any;
52 | } | any;
53 | licenseUrl?: string | any;
54 | documentationUrl?: string | any;
55 | isGithubRepo?: boolean | any;
56 | usage?: string | any;
57 | testCommand?: string | any;
58 | isGithubRepos?: boolean | any;
59 | projectName?: string | any;
60 | projectPrerequisites?: any | any;
61 | isProjectOnNpm?: any | any;
62 | };
63 |
64 | export interface IRequiredFiles {
65 | README?: {
66 | name: string;
67 | exists: boolean;
68 | path: string;
69 | };
70 | LICENSE?: {
71 | name: string;
72 | exists: boolean;
73 | path: string;
74 | };
75 | CODE_OF_CONDUCT?: {
76 | name: string;
77 | exists: boolean;
78 | path: string;
79 | templatePath: string;
80 | };
81 | PULL_REQUEST_TEMPLATE?: {
82 | name: string;
83 | exists: boolean;
84 | path: string;
85 | templatePath: string
86 | };
87 | BUG_REPORT?: {
88 | name: string;
89 | exists: boolean;
90 | path: string;
91 | templatePath: string;
92 | };
93 | FEATURE_REQUEST?: {
94 | name: string;
95 | exists: boolean;
96 | path: string;
97 | templatePath: string
98 | };
99 | };
100 |
101 | // optional files Objects and their details
102 | export interface IOptionalFiles {
103 | CHANGELOG?: {
104 | name: string;
105 | exists: boolean;
106 | path: string;
107 | templatePath: string;
108 | };
109 | SUPPORT?: {
110 | name: string;
111 | exists: boolean;
112 | path: string;
113 | templatePath: string;
114 | };
115 | CONTRIBUTORS?: {
116 | name: string;
117 | exists: boolean;
118 | path: string;
119 | templatePath: string;
120 | };
121 | AUTHORS?: {
122 | name: string;
123 | exists: boolean;
124 | path: string;
125 | templatePath: string;
126 | };
127 | ACKNOWLEDGMENTS?: {
128 | name: string;
129 | exists: boolean;
130 | path: string;
131 | templatePath: string;
132 | };
133 | CODEOWNERS?: {
134 | name: string;
135 | exists: boolean;
136 | path: string;
137 | templatePath: string;
138 | };
139 | };
140 |
141 | export interface IAllFiles extends IRequiredFiles, IOptionalFiles {
142 | };
143 |
144 | export interface ISortedFiles {
145 | validFileNames: string[];
146 | inValidFileNames: string[];
147 | foundFiles: string[];
148 | filesNotFound: string[];
149 | };
150 |
151 | export interface IArguments {
152 | optional?: boolean;
153 | required?: boolean;
154 | all?: string[];
155 | file?: string[];
156 | empty?: boolean;
157 | resp?: ICurrentFile[];
158 | isEmpty?: boolean;
159 | };
160 |
161 | export interface IQuestionResponse {
162 | type?: string;
163 | name?: string;
164 | message?: string;
165 | default?: boolean | string;
166 | filter?: any;
167 | choices?: boolean | string[] | any;
168 | when?: any;
169 | };
170 |
171 | export interface IQuestions {
172 | chooseTemplate?: IQuestionResponse | any;
173 | askProjectName?: IQuestionResponse | any;
174 | askProjectVersion?: IQuestionResponse | any;
175 | askProjectHomepage?: IQuestionResponse | any;
176 | askProjectDescription?: IQuestionResponse | any;
177 | askLicenseName?: IQuestionResponse | any;
178 | askLicenseUrl?: IQuestionResponse | any;
179 | askInstallCommand?: IQuestionResponse | any;
180 | askTestCommand?: IQuestionResponse | any;
181 | askProjectDocumentationUrl?: IQuestionResponse | any;
182 | askAuthorName?: IQuestionResponse | any;
183 | askAuthorGithub?: IQuestionResponse | any;
184 | askAuthorTwitter?: IQuestionResponse | any;
185 | askAuthorPatreon?: IQuestionResponse | any;
186 | askProjectPrerequisites?: IQuestionResponse | any;
187 | askContributing?: IQuestionResponse | any;
188 | askUsage?: IQuestionResponse | any;
189 | askAuthorEmail?: IQuestionResponse | any;
190 | };
191 |
192 | export interface IInputValue {
193 | file: string[];
194 | required: string[];
195 | optional: string[];
196 | isEmpty?: string[];
197 | resp: ICurrentFile[];
198 | all?: string[];
199 | };
200 |
201 | export interface IFilesCategories {
202 | required: boolean;
203 | optional: boolean;
204 | }
205 |
--------------------------------------------------------------------------------
/src/common/alerts.ts:
--------------------------------------------------------------------------------
1 | import boxen from 'boxen';
2 | import pad from 'pad';
3 | import chalk from 'chalk';
4 | import ora from 'ora';
5 | import { IBOX_CONFIG } from '../../types/typeDeclarations.interface';
6 |
7 | // get chalk colors for terminal
8 | const {
9 | red,
10 | gray,
11 | green,
12 | cyan
13 | }: any = chalk;
14 | const whiteUnderline: any = chalk.underline.rgb(174, 174, 174);
15 | const dimWhite: any = chalk.rgb(174, 174, 174);
16 |
17 | /**
18 | * @description
19 | * Logs to the terminal
20 | *
21 | * @param data1 data to be logged
22 | * @param data2 data to be logged
23 | * @param data3 data to be logged
24 | */
25 | const log = (data1: any, data2: any = '', data3: any = ''): void => {
26 | process.stdout.write(`\n${data1}`);
27 | process.stdout.write(`${data2}`);
28 | process.stdout.write(`${data3}`);
29 | };
30 |
31 | // configuration for boxen
32 | const BOX_CONFIG: IBOX_CONFIG| any = {
33 | padding: 1,
34 | margin: { top: 2, bottom: 2 },
35 | borderColor: 'cyan',
36 | align: 'center',
37 | borderStyle: 'double',
38 | };
39 |
40 | /**
41 | * @description
42 | * Encapsulates Items logged in the console within a styled box
43 | * @param text text to be printed on the terminal
44 | */
45 | const useBox: any = (text: string): void => {
46 | log(boxen(text, BOX_CONFIG));
47 | };
48 |
49 | /**
50 | * @description Prints Success message after file creation
51 | */
52 | const showEndMessage: any = (): void =>
53 | useBox('File(s) Created Successfully\nThank you for using md-generator');
54 |
55 | /**
56 | * @description
57 | * Prints the custom help to the terminal
58 | */
59 | const customHelp: any = (): void => {
60 | log('\nCommand-Options :');
61 | log('Usage: md-generator [commands] [command-options]\n');
62 | log(pad('-A, --all', 26), 'Operate on all required/optional .md files');
63 | log(pad('-F, --file', 26), 'Operate on specific .md files');
64 | log(pad('-E, --empty', 26), 'make added files empty');
65 | log(pad('-R --required', 26), 'Operate on required files');
66 | log(pad('-O --optional', 26), 'Operate on optional files\n');
67 | };
68 |
69 | /**
70 | * @description
71 | * prints to the wrong Command Alert to the terminal
72 | *
73 | * @param command the command parsed
74 | */
75 | const wrongCommandAlert: any = (command: string): void =>
76 | log(
77 | `Command "${command}" Does not Exist,\nPlease use --help to get the available commands\n`
78 | );
79 |
80 | /**
81 | * @description
82 | * prints to the no Command Alert to the terminal
83 | */
84 | const noCommandAlert: any = (): void => {
85 | log(
86 | 'Error: No Command Supplied. Please use --help to view the available Commands and Options\n'
87 | );
88 | };
89 |
90 | /**
91 | * @description
92 | * Shows spinner on the terminal
93 | *
94 | * @param text text that accompany the spinner
95 | */
96 | const spinner: any = (text: string): any => ora(text).start();
97 |
98 | /**
99 | * @description
100 | * Takes array of strings and returns a formatted list with the array elements
101 | *
102 | * @param filesArray Array of Strings
103 | */
104 | const castElementsToFormatedString: any = (filesArray: string[]): string => {
105 | let files:string = '';
106 | filesArray.forEach(
107 | (file: string):string => (files += `${cyan(pad('-', 2))} ${dimWhite(`${file}`)}\n`)
108 | );
109 | return files;
110 | };
111 |
112 | /**
113 | * @description
114 | * logs link to github's community page if username and project name is available
115 | *
116 | * @param authorGithubUsername GitHub username
117 | * @param projectName project name
118 | */
119 | const checkCommunityStandardMet: any = (authorGithubUsername: string, projectName: string): void => {
120 | if (authorGithubUsername && projectName) {
121 | log(
122 | `You can check community standards met via https://github.com/${authorGithubUsername}/${projectName}/community \n`
123 | );
124 | } else {
125 | log(
126 | 'You can check community standards met via https://github.com/""/""/community \n'
127 | );
128 | }
129 | };
130 |
131 | /**
132 | * alert showing no file names supplied to the --files option
133 | */
134 | const fileNotDetectedAlert: any = (): void => {
135 | log(
136 | 'Error: File names not detected, please supply file names i.e --file "README.md CONTRIBUTING.md" \n'
137 | );
138 | };
139 |
140 | /**
141 | * Alert showing list of unsupported file names
142 | * @param inValidFileNamesArray invalid file name supplied
143 | */
144 | const unrecognizedFileAlert: any = (inValidFileNamesArray: string[]): void => {
145 | log(
146 | `The following file name(s) is/are not recognized as one of the required/optional .md files\n${castElementsToFormatedString(
147 | inValidFileNamesArray
148 | )}`
149 | );
150 | };
151 |
152 | interface consolePayload {
153 | errorText: string;
154 | override?: boolean;
155 | }
156 | /**
157 | * Alert directing users to the --help command
158 | */
159 | const useHelpAlert: any = ({ errorText, override }: consolePayload): void => {
160 | const genericText = override ? '\n' : ' please use --help to check all supported md Files\n';
161 | log(`${errorText} ${genericText}`);
162 | };
163 |
164 | export {
165 | log,
166 | useBox,
167 | green,
168 | customHelp,
169 | whiteUnderline,
170 | red,
171 | gray,
172 | dimWhite,
173 | cyan,
174 | wrongCommandAlert,
175 | noCommandAlert,
176 | spinner,
177 | useHelpAlert,
178 | showEndMessage,
179 | fileNotDetectedAlert,
180 | unrecognizedFileAlert,
181 | checkCommunityStandardMet,
182 | castElementsToFormatedString,
183 | };
184 |
--------------------------------------------------------------------------------
/src/core/actions/actionsUtils.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { fileNotDetectedAlert } from '../../common/index';
4 | import { IAllFiles, IRequiredFiles, IOptionalFiles, ISortedFiles } from '../../../types/typeDeclarations.interface';
5 |
6 | /**
7 | * @description
8 | * gets the absolute path a file
9 | *
10 | * @param relativePath relative path to the file
11 | */
12 | const getResolvedPath = (relativePath: string) => path.resolve(__dirname, relativePath);
13 |
14 | /**
15 | * @description
16 | * Returns the activated command line option
17 | *
18 | * @param args all arguments available
19 | * @param option option activated
20 | */
21 | const getArgs = (args: [], option:string) => args.find((item: string) => item === option);
22 |
23 | /**
24 | * @description
25 | * returns an Object showing which option has been activated and the payload supplied
26 | *
27 | * @param args all arguments available
28 | * @param resp option activated
29 | */
30 | const getValues = (args: [], resp: any) => ({
31 | file: getArgs(args, 'file'),
32 | required: getArgs(args, 'required'),
33 | optional: getArgs(args, 'optional'),
34 | all: getArgs(args, 'all'),
35 | isEmpty: getArgs(args, 'empty'),
36 | resp,
37 | });
38 |
39 | /**
40 | * @description
41 | * Checks if file exists in the code base
42 | *
43 | * @param relativePath relative path to file
44 | */
45 | const checkFileExist = (relativePath: string) => !!fs.existsSync(relativePath);
46 |
47 | // required files Objects and their details
48 | const requiredFiles: IRequiredFiles = {
49 | README: {
50 | name: 'README.md',
51 | exists: checkFileExist('./README.md'),
52 | path: './README.md',
53 | },
54 | LICENSE: {
55 | name: 'LICENSE',
56 | exists: checkFileExist('./LICENSE'),
57 | path: './LICENSE',
58 | },
59 | CODE_OF_CONDUCT: {
60 | name: 'CODE_OF_CONDUCT.md',
61 | exists: checkFileExist('./CODE_OF_CONDUCT.md'),
62 | path: './CODE_OF_CONDUCT.md',
63 | templatePath: getResolvedPath(
64 | '../../templates/files/required/template-CODE_OF_CONDUCT.md'
65 | ),
66 | },
67 | PULL_REQUEST_TEMPLATE: {
68 | name: 'PULL_REQUEST_TEMPLATE.md',
69 | exists: checkFileExist('./.github/PULL_REQUEST_TEMPLATE.md'),
70 | path: './.github/PULL_REQUEST_TEMPLATE.md',
71 | templatePath: getResolvedPath(
72 | '../../templates/files/required/template-PULL_REQUEST_TEMPLATE.md'
73 | ),
74 | },
75 | BUG_REPORT: {
76 | name: 'bug_report.md',
77 | exists: checkFileExist('./.github/ISSUE_TEMPLATE/bug_report.md'),
78 | path: './.github/ISSUE_TEMPLATE/bug_report.md',
79 | templatePath: getResolvedPath(
80 | '../../templates/files/required/template-BUG_REPORT.md'
81 | ),
82 | },
83 | FEATURE_REQUEST: {
84 | name: 'feature_request.md',
85 | exists: checkFileExist('./.github/ISSUE_TEMPLATE/feature_request.md'),
86 | path: './.github/ISSUE_TEMPLATE/feature_request.md',
87 | templatePath: getResolvedPath(
88 | '../../templates/files/required/template-FEATURE_REQUEST.md'
89 | ),
90 | },
91 | };
92 |
93 | // optional files Objects and their details
94 | const optionalFiles: IOptionalFiles = {
95 | CHANGELOG: {
96 | name: 'CHANGELOG.md',
97 | exists: checkFileExist('./CHANGELOG.md'),
98 | path: './CHANGELOG.md',
99 | templatePath: getResolvedPath(
100 | '../../templates/files/optional/template-CHANGELOG.md'
101 | ),
102 | },
103 | SUPPORT: {
104 | name: 'SUPPORT.md',
105 | exists: checkFileExist('./SUPPORT.md'),
106 | path: './SUPPORT.md',
107 | templatePath: getResolvedPath(
108 | '../../templates/files/optional/template-SUPPORT.md'
109 | ),
110 | },
111 | CONTRIBUTORS: {
112 | name: 'CONTRIBUTORS.md',
113 | exists: checkFileExist('./CONTRIBUTORS.md'),
114 | path: './CONTRIBUTORS.md',
115 | templatePath: getResolvedPath(
116 | '../../templates/files/optional/template-CONTRIBUTORS.md'
117 | ),
118 | },
119 | AUTHORS: {
120 | name: 'AUTHORS.md',
121 | exists: checkFileExist('./AUTHORS.md'),
122 | path: './AUTHORS.md',
123 | templatePath: getResolvedPath(
124 | '../../templates/files/optional/template-AUTHORS.md'
125 | ),
126 | },
127 | ACKNOWLEDGMENTS: {
128 | name: 'ACKNOWLEDGMENTS.md',
129 | exists: checkFileExist('./ACKNOWLEDGMENTS.md'),
130 | path: './ACKNOWLEDGMENTS.md',
131 | templatePath: getResolvedPath(
132 | '../../templates/files/optional/template-ACKNOWLEDGMENTS.md'
133 | ),
134 | },
135 | CODEOWNERS: {
136 | name: 'CODEOWNERS.md',
137 | exists: checkFileExist('./CODEOWNERS.md'),
138 | path: './CODEOWNERS.md',
139 | templatePath: getResolvedPath(
140 | '../../templates/files/optional/template-CODEOWNERS.md'
141 | ),
142 | },
143 | };
144 |
145 | // all files Objects and their details
146 | const allFiles: IAllFiles = { ...requiredFiles, ...optionalFiles };
147 |
148 | /**
149 | * @description
150 | * checks if file exists in the code base
151 |
152 | * @param data array of files names
153 | * @param allMdFiles Object containing all files available
154 | */
155 | const checkFilesExist = (data: string[], allMdFiles: any): any => {
156 | const foundFiles: any[] = [];
157 | const filesNotFound: any[] = [];
158 | data.forEach((item: string) => {
159 | const newItem: string = item.split('.')[0].toUpperCase();
160 | const file: any = Object.keys(allMdFiles).find(
161 | (key: string) => key === newItem && allMdFiles[newItem].exists
162 | );
163 | if (file) {
164 | foundFiles.push(file);
165 | } else {
166 | filesNotFound.push(newItem);
167 | }
168 | });
169 | return { foundFiles, filesNotFound };
170 | };
171 |
172 | /**
173 | * @description
174 | * remove the .md attribute of supplied in the terminal with the file name
175 | *
176 | * @param item Array of file names supplied
177 | */
178 | const removeDotMdAttribute = (item: string[]) => {
179 | const newItem: any[] = [];
180 | item.forEach((key: string) => newItem.push(key.split('.')[0].toUpperCase()));
181 | return newItem;
182 | };
183 |
184 | /**
185 | * @description
186 | * check if file name supplied is available in the npm module
187 | *
188 | * @param files Array of all file names supplied
189 | */
190 | const allExistingFiles = (files: IAllFiles = allFiles) =>
191 | Object.values(files).filter((item: any) => !!item.exists);
192 |
193 | const getArrayOfValues = (files: IAllFiles = allFiles) => Object.values(files);
194 |
195 | /**
196 | * @description
197 | * validate files names with respect to the files supported by the npm module
198 | *
199 | * @param values Array of all file names supplied
200 | */
201 | const queryFilesExistence = (values: any): ISortedFiles => {
202 | const args = !values.parent ? values.join(' ') : values.parent.rawArgs[4];
203 | if ((!args || args.length === 0) && values.parent) {
204 | fileNotDetectedAlert();
205 | process.exit(1);
206 | }
207 | const data = args.split(' ').filter((item: string)=> item.length > 0 );
208 | return checkFilesExist(data, allFiles);
209 | };
210 |
211 | const getItemFromFileName = (fileName: string) =>
212 | Object.values(allFiles).find((value: any) => value.name.toUpperCase().includes(fileName));
213 |
214 | export {
215 | allFiles,
216 | getValues,
217 | requiredFiles,
218 | optionalFiles,
219 | checkFilesExist,
220 | getArrayOfValues,
221 | queryFilesExistence,
222 | allExistingFiles,
223 | removeDotMdAttribute,
224 | getItemFromFileName,
225 | };
226 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | md-generator
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | CLI tool which bootstraps Development by creating "ALL" required .md files to meet "community standards".
28 |
29 | ## Table of Contents
30 |
31 | - [Project Overview](#Project-Overview)
32 | - [Features](#Features)
33 | - [Installation](#Installation)
34 | - [Supported Files](#Supported-Files)
35 | - [Usage](#Usage)
36 | - [Demo](#Demo)
37 | - [Known Issues](#Known-Issues)
38 | - [Contributing](#contributing)
39 | - [Contributors](#Contributors)
40 | - [License](#License)
41 |
42 | ## Project Overview
43 |
44 | md-generator was created to reduce the time spent creating .md files while trying to set up projects. Hence, bootstrapping development with `ALL` desired md files in line with proven community standards.
45 |
46 | ## Features
47 |
48 | - [x] provides interactive file creation
49 | - [x] provides default templates in all created files (which could be further customized)
50 | - [x] checks if the minimum community standard is met and recommends the minimum required .md files
51 | - [x] provides bulk removal of specified .md files
52 | - [x] provides bulk creation of .md files
53 | - [x] optional `--empty` argument to make created files empty if preferred so
54 | - [x] optional `--required` to `create`/`check`/`list` only all files needed to meet the minimum community standard
55 |
56 | ## Installation
57 |
58 | `npm install md-generator` or globally using `npm install -g md-generator`
59 |
60 | - you can interact with the package from the npm registry
61 | using `npx md-generator [command] [options]`
62 |
63 | ## Supported Files
64 | `Required`
65 | - README.md
66 | - LICENSE
67 | - CODE_OF_CONDUCT.md
68 | - PULL_REQUEST_TEMPLATE.md
69 | - CONTRIBUTING.md
70 | - bug_report.md
71 | - feature_request.md
72 |
73 | `Optional`
74 | - CHANGELOG.md
75 | - SUPPORT.md
76 | - CONTRIBUTORS.md
77 | - AUTHORS.md
78 | - ACKNOWLEDGMENTS.md
79 | - CODEOWNERS.md
80 |
81 | ## Usage
82 |
83 | install globally using
84 |
85 | - npm : npm i -g md-generator
86 | - yarn : yarn add -g md-generator
87 |
88 | #### Trigger md-generator using
89 |
90 | - md-generator `[parent-options]`
91 | - md-generator `[commands]` `[command-options] [file names]`
92 |
93 | #### Parent Options:
94 |
95 | | Option | Function |
96 | | :-----------: | :-----------------------: |
97 | | -V, --version | Output the version number |
98 | | -h, --help | Output usage information |
99 |
100 | #### Commands:
101 |
102 | | Command | Function |
103 | | :-------------------------------: | :------------------------------------------------------------: |
104 | | list `[options]` `[File Names]` | List All Required/optional .md files |
105 | | create `[options]` `[File Names]` | Create All/specific files |
106 | | check `[options]` `[File Names]` | Checks codebase for the availability of All/Specific .md files |
107 | | remove `[options]` `[File Names]` | Remove All/specific .md files |
108 |
109 | #### Command-Options :
110 |
111 | | Command Option | Function |
112 | | :------------: | :----------------------------------------: |
113 | | -A, --all | Operate on all required/optional .md files |
114 | | -F, --file | Operate on specific .md files |
115 | | -E, --empty | make added files empty |
116 | | -R --required | Operate on required files |
117 | | -O --optional | Operate on optional files |
118 |
119 | > Note:
120 |
121 | - `File Names` can be with/without the file extension
122 | - multiples `File Names` should be separated with spaces ie `--file "README.md CONTRIBUTING.md"`.
123 |
124 | ## Demo
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | | Cli Command | Function |
136 | | :--------------------------------------------------: | :-------------------------------------------------------: |
137 | | md-generator --help | displays help (all available command and option) |
138 | | md-generator list | lists all supported .md files |
139 | | md-generator list --optional | lists all supported optional .md files |
140 | | md-generator list --required | lists all required .md files to meet community standard |
141 | | md-generator check | checks code base for existing/missing supported .md files |
142 | | md-generator check --required | checks code base for existing/missing required .md files |
143 | | md-generator check --optional | checks code base for existing/missing optional .md files |
144 | | md-generator create | Interactively Generates all desired files |
145 | | md-generator create --file "file1.md file2.md ..." | Creates the supplied files |
146 | | md-generator create --file "file1 file2 ..." --empty | creates supplied files with no template |
147 | | md-generator create --required | Interactively create required .md files |
148 | | md-generator create --optional | Interactively create optional .md files |
149 | | md-generator remove | Interactively deletes all desired .md files |
150 | | md-generator remove --file "file1 file2 ..." | Interactively deletes all specified .md files |
151 |
152 | ## Known issues
153 |
154 | No known [issues](https://github.com/Oluwasegun-AA/md-generator/issues) at the moment. However, [issues](https://github.com/Oluwasegun-AA/md-generator/issues) can be raised when such is noticed
155 |
156 | ## Contributing
157 |
158 | > Feel free to contribute and kindly go through the Pull Request guide, and contributing.md file
159 |
160 | ## Contributors
161 |
162 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!
171 |
172 | ## License
173 |
174 | 
175 |
176 | - **[MIT license]()**
177 | - With ❤️ from Olúwáségun.
178 |
179 | ---
180 |
181 | _This File was generated by [md-generator](https://github.com/oluwasegun-AA/md-generator)_
182 |
--------------------------------------------------------------------------------
/src/core/actions/createActions/index.ts:
--------------------------------------------------------------------------------
1 | import inquirer from 'inquirer';
2 |
3 | import {
4 | createFiles,
5 | createRequired,
6 | createOptional,
7 | selectFileToCreate,
8 | createEmptyFiles,
9 | overrideFiles,
10 | } from '../../questions/setupQuestions/index';
11 | import {
12 | spinner,
13 | fileNotDetectedAlert,
14 | unrecognizedFileAlert,
15 | getFullFileNames,
16 | showEndMessage,
17 | useHelpAlert,
18 | } from '../../../common/index';
19 | import { getInfos } from '../../questions/askQuestions';
20 | import { buildFileContent, writeFile } from '../../../templates/fileWriter';
21 | import {
22 | allFiles,
23 | requiredFiles,
24 | optionalFiles,
25 | getArrayOfValues,
26 | queryFilesExistence,
27 | getItemFromFileName,
28 | } from '../actionsUtils';
29 | import { isEmpty } from 'lodash';
30 |
31 | import { ICurrentFile, ISortedFiles, IAllFiles } from '../../../../types/typeDeclarations.interface';
32 |
33 | /**
34 | * @description
35 | * return all files to be created based on the choice of the user to override the existing file
36 | *
37 | * @param existingFiles - files existing in the code base
38 | * @param noneExistingFiles - files not found in the codebase
39 | */
40 | export const shouldOverride: any = async (existingFiles: string[], noneExistingFiles: string[]): Promise => {
41 | const files: string[] = await inquirer
42 | .prompt(overrideFiles(existingFiles))
43 | .then((item: any) => {
44 | const { override } = item;
45 | if (!override && isEmpty(noneExistingFiles[0])) return process.exit(1);
46 | if (override) return noneExistingFiles.concat(existingFiles);
47 | return noneExistingFiles;
48 | });
49 | return files;
50 | };
51 |
52 | export const getValidFiles: any = (values: ICurrentFile[] | string[]): any => {
53 | const list: string[] =
54 | typeof values[0] === 'object' ? getFullFileNames(values as ICurrentFile[]) : values as string[];
55 | const { foundFiles, filesNotFound }: ISortedFiles = queryFilesExistence(list);
56 | const validFileNames: string[] | ICurrentFile[] = filesNotFound.filter((key: string) =>
57 | Object.keys(allFiles).includes(key));
58 | const inValidFileNames: string[] | ICurrentFile[] = filesNotFound.filter(
59 | (key: string) => !Object.keys(allFiles).includes(key)
60 | );
61 | return {
62 | validFileNames,
63 | inValidFileNames,
64 | foundFiles,
65 | };
66 | };
67 |
68 | /**
69 | * @description
70 | * handles files override for existing files based on the user's choice
71 | *
72 | * @param values - files to be overridden
73 | */
74 | export const handleOverride: any = async (values: string[] | ICurrentFile[]): Promise => {
75 | let { validFileNames }: ISortedFiles = getValidFiles(values);
76 | const { inValidFileNames, foundFiles }: ISortedFiles = getValidFiles(values);
77 | if (foundFiles.length > 0) {
78 | validFileNames = await shouldOverride(foundFiles, validFileNames);
79 | }
80 | return { validFileNames, inValidFileNames };
81 | };
82 |
83 | /**
84 | * @description
85 | * Creates Files
86 | *
87 | * @param USE_DEFAULT determines if questions are asked or default values are used
88 | * @param filesToBeCreated file names (Array of strings)
89 | * @param isEmpty determines if file to be created would be empty
90 | */
91 | // tslint:disable-next-line: max-line-length
92 | export const createMdFiles = async (USE_DEFAULT: boolean, filesToBeCreated: string[] | ICurrentFile[], isEmpty: boolean): Promise => {
93 | const { validFileNames, inValidFileNames }: ISortedFiles = await handleOverride(filesToBeCreated);
94 | if (validFileNames.length === 0 && inValidFileNames.length === 0) {
95 | useHelpAlert({
96 | errorText: 'Error: No option selected, Press to select, to toggle all, to invert selection',
97 | override: true
98 | });
99 | process.exit(1);
100 | }
101 | if (validFileNames.length === 0 && inValidFileNames.length > 0) {
102 | useHelpAlert({
103 | errorText:`${inValidFileNames} not supported,`,
104 | override: false
105 | });
106 | process.exit(1);
107 | }
108 | if (inValidFileNames.length > 0 && validFileNames.length > 0) {
109 | useHelpAlert({
110 | errorText: `The following files are not supported and will not be created ${inValidFileNames}`,
111 | override: true
112 | });
113 | }
114 | // tslint:disable: no-parameter-reassignment
115 | const files: any = validFileNames;
116 | if (validFileNames.includes('README') || validFileNames.includes('LICENSE')) USE_DEFAULT = false;
117 | await getInfos(USE_DEFAULT, files).then((projectInfos: any) => {
118 | files.forEach(async (file: string | ICurrentFile) => {
119 | let pathToTemplate: string;
120 | const { path, templatePath }: ICurrentFile = getItemFromFileName(file as string);
121 | if (file === 'LICENSE') {
122 | pathToTemplate = projectInfos.licenseName.path;
123 | } else {
124 | pathToTemplate = templatePath || projectInfos.templatePath;
125 | }
126 | const fileContent: string = isEmpty
127 | ? ''
128 | : await buildFileContent(projectInfos, pathToTemplate);
129 | await writeFile(fileContent, path);
130 | });
131 | });
132 | spinner().succeed('File(s) created Successfully\n');
133 | showEndMessage();
134 | };
135 |
136 | /**
137 | * @description
138 | * determine which file creation mode is activated with respect to the type of files
139 | *
140 | * @param allItems all files array
141 | * @param mode Question modes
142 | * @param CREATE_EMPTY_FILE determines if questions are asked or default values are used
143 | */
144 | // tslint:disable-next-line: max-line-length
145 | const processCreation = async (allItems: IAllFiles | ICurrentFile[], mode: any, IS_EMPTY_FILE?: boolean): Promise => {
146 | const USE_DEFAULT_VALUES: boolean = true;
147 | let CREATE_EMPTY_FILE: boolean = false;
148 | let filesToBeCreated: ICurrentFile[];
149 | return inquirer.prompt(mode(getArrayOfValues(allItems as IAllFiles)))
150 | .then((answer: any): any => {
151 | const { createFiles } = answer;
152 | if (createFiles === false) return process.exit(1);
153 | inquirer.prompt(createEmptyFiles()).then((res: any) => {
154 | CREATE_EMPTY_FILE = IS_EMPTY_FILE || res.empty;
155 | if (typeof createFiles === 'object') {
156 | filesToBeCreated = Object.values(allItems).filter((file: ICurrentFile) =>
157 | createFiles.includes(file.name));
158 | if (
159 | createFiles.includes('README.md') ||
160 | createFiles.includes('CODE_OF_CONDUCT.md') ||
161 | createFiles.includes('LICENSE')
162 | ) {
163 | return createMdFiles(
164 | !USE_DEFAULT_VALUES,
165 | filesToBeCreated,
166 | CREATE_EMPTY_FILE
167 | );
168 | }
169 | }
170 | if (createFiles === true) {
171 | return createMdFiles(!USE_DEFAULT_VALUES, allItems as ICurrentFile[], CREATE_EMPTY_FILE);
172 | }
173 | return createMdFiles(USE_DEFAULT_VALUES, createFiles, CREATE_EMPTY_FILE);
174 | });
175 | });
176 | };
177 |
178 | /**
179 | * @description
180 | * Check if files to be created is a valid .md file, exists in codebase, or doesn't exist.
181 | *
182 | * @param values Array of files names
183 | */
184 | export const checkCreatableFiles = async (values: ICurrentFile[]): Promise => {
185 | const { validFileNames, inValidFileNames, foundFiles }: ISortedFiles = getValidFiles(
186 | values
187 | );
188 | let validFiles: ICurrentFile[] | string[] = validFileNames.concat(foundFiles);
189 | if (inValidFileNames.length > 0) {
190 | unrecognizedFileAlert(inValidFileNames);
191 | }
192 | if (validFiles.length > 0) {
193 | validFiles = Object.values(allFiles).filter((item: any) =>
194 | validFiles.includes(item.name.split('.')[0]));
195 | } else {
196 | fileNotDetectedAlert();
197 | process.exit(1);
198 | }
199 | return validFiles;
200 | };
201 |
202 | /**
203 | * @description creates file when command is passed with out options
204 | */
205 | const createNonSpecificFiles = (): void => {
206 | processCreation(allFiles, selectFileToCreate);
207 | };
208 |
209 | /**
210 | * @description creates files when command is passed with option -F or --file
211 | * @param isEmpty determines if questions are asked or default values are used
212 | * @param values Files names
213 | */
214 | export const createSpecificFiles = async (isEmpty: boolean, values: ICurrentFile[]): Promise => {
215 | const files = await checkCreatableFiles(values);
216 | processCreation(files, createFiles, isEmpty);
217 | };
218 |
219 | /**
220 | * @description
221 | * Creates files when command is passed with option -R or --required
222 | *
223 | * @param isEmpty determines if questions are asked or default values are used
224 | */
225 | const createRequiredFiles = (isEmpty: boolean): void => {
226 | processCreation(requiredFiles, createRequired, isEmpty);
227 | };
228 |
229 | /**
230 | * @description
231 | * Creates files when command is passed with option -O or --optional
232 | *
233 | * @param isEmpty determines if questions are asked or default values are used
234 | */
235 | const createOptionalFiles = (isEmpty: boolean): void => {
236 | processCreation(optionalFiles, createOptional, isEmpty);
237 | };
238 |
239 | /**
240 | * @description
241 | * handles file creation
242 | *
243 | * @param values arguments i.e command, payload and command options
244 | */
245 | const createHandler = (values: any): any => {
246 | const {
247 | file,
248 | required,
249 | optional,
250 | isEmpty,
251 | resp
252 | } = values;
253 | if (file) return createSpecificFiles(isEmpty, resp);
254 | if (required) return createRequiredFiles(isEmpty);
255 | if (optional) return createOptionalFiles(isEmpty);
256 | createNonSpecificFiles();
257 | };
258 |
259 | export default createHandler;
260 |
--------------------------------------------------------------------------------
/src/templates/files/required/template-MOZILLA-LICENSE.md:
--------------------------------------------------------------------------------
1 | 1. #### Definitions
2 | 1.1. "Contributor"
3 | means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.
4 |
5 | 1.2. “Contributor Version”
6 | means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution.
7 |
8 | 1.3. “Contribution”
9 | means Covered Software of a particular Contributor.
10 |
11 | 1.4. “Covered Software”
12 | means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.
13 |
14 | 1.5. “Incompatible With Secondary Licenses”
15 | means
16 |
17 | that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or
18 |
19 | that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.
20 |
21 | 1.6. “Executable Form”
22 | means any form of the work other than Source Code Form.
23 |
24 | 1.7. “Larger Work”
25 | means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.
26 |
27 | 1.8. “License”
28 | means this document.
29 |
30 | 1.9. “Licensable”
31 | means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.
32 |
33 | 1.10. “Modifications”
34 | means any of the following:
35 |
36 | any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or
37 |
38 | any new file in Source Code Form that contains any Covered Software.
39 |
40 | 1.11. “Patent Claims” of a Contributor
41 | means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.
42 |
43 | 1.12. “Secondary License”
44 | means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.
45 |
46 | 1.13. “Source Code Form”
47 | means the form of the work preferred for making modifications.
48 |
49 | 1.14. “You” (or “Your”)
50 | means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
51 |
52 | 2. #### License Grants and Conditions
53 | 2.1. Grants
54 | Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
55 |
56 | under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and
57 |
58 | under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.
59 |
60 | 2.2. Effective Date
61 | The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.
62 |
63 | 2.3. Limitations on Grant Scope
64 | The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:
65 |
66 | for any code that a Contributor has removed from Covered Software; or
67 |
68 | for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or
69 |
70 | under Patent Claims infringed by Covered Software in the absence of its Contributions.
71 |
72 | This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).
73 |
74 | 2.4. Subsequent Licenses
75 | No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).
76 |
77 | 2.5. Representation
78 | Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.
79 |
80 | 2.6. Fair Use
81 | This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.
82 |
83 | 2.7. Conditions
84 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.
85 |
86 | 3. #### Responsibilities
87 | 3.1. Distribution of Source Form
88 | All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.
89 |
90 | 3.2. Distribution of Executable Form
91 | If You distribute Covered Software in Executable Form then:
92 |
93 | such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and
94 |
95 | You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License.
96 |
97 | 3.3. Distribution of a Larger Work
98 | You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).
99 |
100 | 3.4. Notices
101 | You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.
102 |
103 | 3.5. Application of Additional Terms
104 | You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.
105 |
106 | 4. #### Inability to Comply Due to Statute or Regulation
107 | If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
108 |
109 | 5. #### Termination
110 | 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.
111 |
112 | 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
113 |
114 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.
115 |
116 | 6. #### Disclaimer of Warranty
117 | Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.
118 |
119 | 7. #### Limitation of Liability
120 | Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
121 |
122 | 8. #### Litigation
123 | Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims.
124 |
125 | 9. #### Miscellaneous
126 | This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.
127 |
128 | 10. #### Versions of the License
129 | 10.1. New Versions
130 | Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.
131 |
132 | 10.2. Effect of New Versions
133 | You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.
134 |
135 | 10.3. Modified Versions
136 | If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).
137 |
138 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
139 | If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.
140 |
141 | Exhibit A - Source Code Form License Notice
142 | This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
143 |
144 | If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
145 |
146 | You may add additional accurate notices of copyright ownership.
147 |
148 | Exhibit B - “Incompatible With Secondary Licenses” Notice
149 | This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
150 |
--------------------------------------------------------------------------------