@anth0nycodes/license-generator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 anth0nycodes
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.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # License Generator
2
+
3
+ An interactive CLI tool to quickly generate open-source licenses for your projects.
4
+
5
+ ## Features
6
+
7
+ - 🚀 Interactive license selection from GitHub's license API
8
+ - 📝 Automatic copyright holder and year detection from git config
9
+ - ✨ Beautiful terminal UI powered by @clack/prompts and inquirer
10
+ - 🔄 Overwrite protection for existing LICENSE files
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm install -g @anth0nycodes/license-generator
16
+ ```
17
+
18
+ Or using npm:
19
+
20
+ ```bash
21
+ npm install -g @anth0nycodes/license-generator
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ generate-license
28
+ ```
29
+
30
+ The CLI will guide you through:
31
+
32
+ 1. **Select a license** - Choose from popular open-source licenses (MIT, Apache-2.0, GPL, etc.)
33
+ 2. **Enter copyright holder** - Defaults to your git username
34
+ 3. **Enter copyright year** - Defaults to the current year
35
+ 4. **Confirm** - The LICENSE file will be created in your current directory
36
+
37
+ ## Supported Licenses
38
+
39
+ This tool uses the GitHub Licenses API, which includes:
40
+
41
+ - GNU Affero General Public License v3.0
42
+ - Apache License 2.0
43
+ - BSD 2-Clause "Simplified" License
44
+ - BSD 3-Clause "New" or "Revised" License
45
+ - Boost Software License 1.0
46
+ - Creative Commons Zero v1.0 Universal
47
+ - Eclipse Public License 2.0
48
+ - GNU General Public License v2.0
49
+ - GNU General Public License v3.0
50
+ - GNU Lesser General Public License v2.1
51
+ - MIT License
52
+ - Mozilla Public License 2.0
53
+ - The Unlicense
54
+
55
+ ## Requirements
56
+
57
+ - Node.js 20.x or higher
58
+
59
+ ## Development
60
+
61
+ ```bash
62
+ # Clone the repository
63
+ git clone https://github.com/anth0nycodes/license-generator.git
64
+ cd license-generator
65
+
66
+ # Install dependencies
67
+ pnpm install
68
+
69
+ # Run in development mode
70
+ pnpm dev
71
+
72
+ # Build
73
+ pnpm build
74
+
75
+ # Run built version
76
+ pnpm start
77
+ ```
78
+
79
+ ## Contributing
80
+
81
+ Contributions are welcome! Please feel free to submit a Pull Request.
82
+
83
+ ## Author
84
+
85
+ **Anthony Hoang**
86
+
87
+ - GitHub: [@anth0nycodes](https://github.com/anth0nycodes)
88
+
89
+ ## Links
90
+
91
+ - [npm package](https://www.npmjs.com/package/@anth0nycodes/license-generator)
92
+ - [GitHub repository](https://github.com/anth0nycodes/license-generator)
93
+ - [Report issues](https://github.com/anth0nycodes/license-generator/issues)
@@ -0,0 +1,3 @@
1
+ export declare function getGitUsername(): string;
2
+ export declare function fileExists(path: string): Promise<boolean>;
3
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,WAG7B;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D"}
@@ -0,0 +1,17 @@
1
+ import { execSync } from "node:child_process";
2
+ import { constants } from "node:fs";
3
+ import { access } from "node:fs/promises";
4
+ export function getGitUsername() {
5
+ const uncleanName = String(execSync("git config user.name"));
6
+ return uncleanName.replace(/\r?\n/g, "");
7
+ }
8
+ export async function fileExists(path) {
9
+ try {
10
+ await access(path, constants.F_OK);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,UAAU,cAAc;IAC5B,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAC7D,OAAO,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ import { program } from "commander";
3
+ import { intro, select, isCancel, cancel } from "@clack/prompts";
4
+ import inquirer from "inquirer";
5
+ import { createLicense, getLicenseContent, getLicenses } from "./license.js";
6
+ import color from "picocolors";
7
+ import { getGitUsername } from "./helpers.js";
8
+ const main = async () => {
9
+ program
10
+ .name("License Generator")
11
+ .description("A CLI application that generates open-source licenses for your repositories.")
12
+ .version("0.1.0");
13
+ // TODO: Add options, so you can manually add a license flag like --license mit
14
+ program.parse();
15
+ intro(color.blueBright("License Generator"));
16
+ const BASE_URL = "https://api.github.com/licenses";
17
+ const licenses = await getLicenses();
18
+ // List all available licenses from github api
19
+ const licenseOption = await select({
20
+ message: "Select a license:",
21
+ options: licenses.map((license) => ({
22
+ value: license.key,
23
+ label: license.name,
24
+ })),
25
+ });
26
+ if (isCancel(licenseOption)) {
27
+ cancel("Operation cancelled.");
28
+ process.exit(0);
29
+ }
30
+ // Grab the inputs for name and year
31
+ let answers;
32
+ try {
33
+ answers = await inquirer.prompt([
34
+ {
35
+ type: "input",
36
+ name: "name",
37
+ message: "Enter name:",
38
+ default: getGitUsername(),
39
+ validate(value) {
40
+ if (value.length === 0)
41
+ return "Name is required";
42
+ return true;
43
+ },
44
+ },
45
+ {
46
+ type: "input",
47
+ name: "year",
48
+ message: "Enter year:",
49
+ default: String(new Date().getFullYear()),
50
+ validate(value) {
51
+ if (value.length === 0)
52
+ return "Year is required";
53
+ if (!/^\d{4}$/.test(value))
54
+ return "Please enter a valid year!";
55
+ return true;
56
+ },
57
+ },
58
+ ]);
59
+ }
60
+ catch (error) {
61
+ cancel("Operation cancelled.");
62
+ process.exit(0);
63
+ }
64
+ // Grab the content of the selected license
65
+ const licenseOptionContent = await getLicenseContent(`${BASE_URL}/${String(licenseOption)}`);
66
+ // Write LICENSE file
67
+ try {
68
+ await createLicense(licenseOptionContent, answers.year, answers.name);
69
+ }
70
+ catch (error) {
71
+ console.error(`Error occurred in createLicense: ${error}`);
72
+ throw error;
73
+ }
74
+ };
75
+ try {
76
+ main();
77
+ }
78
+ catch (error) {
79
+ console.error(`Error in main: ${error}`);
80
+ }
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ interface LicenseShape {
2
+ key: string;
3
+ name: string;
4
+ spdx_id: string;
5
+ url: string;
6
+ node_id: string;
7
+ }
8
+ interface LicenseContentShape {
9
+ key: string;
10
+ name: string;
11
+ description: string;
12
+ permissions: string[];
13
+ conditions: string[];
14
+ limitations: string[];
15
+ body: string;
16
+ }
17
+ export declare function getLicenses(): Promise<LicenseShape[]>;
18
+ export declare function getLicenseContent(url: string): Promise<LicenseContentShape>;
19
+ export declare function createLicense(content: LicenseContentShape, year: string, name: string): Promise<void>;
20
+ export {};
21
+ //# sourceMappingURL=license.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,mBAAmB;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAU3D;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mBAAmB,CAAC,CAgB9B;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,iBA2Bb"}
@@ -0,0 +1,57 @@
1
+ import axios from "axios";
2
+ import color from "picocolors";
3
+ import { writeFile } from "node:fs/promises";
4
+ import { fileExists } from "./helpers.js";
5
+ import { cancel, confirm, spinner } from "@clack/prompts";
6
+ export async function getLicenses() {
7
+ try {
8
+ const { data } = await axios.get("https://api.github.com/licenses");
9
+ return data;
10
+ }
11
+ catch (error) {
12
+ console.error(`Error in getLicenses: ${error}`);
13
+ throw error;
14
+ }
15
+ }
16
+ export async function getLicenseContent(url) {
17
+ try {
18
+ const { data } = await axios.get(url);
19
+ return {
20
+ key: data.key,
21
+ name: data.name,
22
+ description: data.description,
23
+ permissions: data.permissions,
24
+ conditions: data.conditions,
25
+ limitations: data.limitations,
26
+ body: data.body,
27
+ };
28
+ }
29
+ catch (error) {
30
+ console.error(`Error in getLicenseContent: ${error}`);
31
+ throw error;
32
+ }
33
+ }
34
+ export async function createLicense(content, year, name) {
35
+ let uncleanBody = content.body;
36
+ let body = uncleanBody
37
+ .replaceAll("[year]", year)
38
+ .replaceAll("[yyyy]", year)
39
+ .replaceAll("[fullname]", name)
40
+ .replaceAll("[name of copyright owner]", name)
41
+ .replaceAll("<year>", year)
42
+ .replaceAll("<name of author>", name);
43
+ if (await fileExists("LICENSE")) {
44
+ const shouldContinue = await confirm({
45
+ message: "LICENSE file already exists. Overwrite?",
46
+ });
47
+ if (!shouldContinue) {
48
+ cancel("Operation cancelled.");
49
+ process.exit(0);
50
+ }
51
+ }
52
+ const s = spinner();
53
+ s.start(color.yellow("Generating license..."));
54
+ await writeFile("LICENSE", body, "utf8");
55
+ s.stop(color.greenBright("License created!"));
56
+ }
57
+ //# sourceMappingURL=license.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.js","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAoB1D,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAA6B,MAAM,KAAK,CAAC,GAAG,CACxD,iCAAiC,CAClC,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAW;IAEX,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAkC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrE,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA4B,EAC5B,IAAY,EACZ,IAAY;IAEZ,IAAI,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/B,IAAI,IAAI,GAAG,WAAW;SACnB,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC;SAC1B,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC;SAC1B,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC;SAC9B,UAAU,CAAC,2BAA2B,EAAE,IAAI,CAAC;SAC7C,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC;SAC1B,UAAU,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IAExC,IAAI,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC;YACnC,OAAO,EAAE,yCAAyC;SACnD,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,CAAC,sBAAsB,CAAC,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAC/C,MAAM,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAChD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@anth0nycodes/license-generator",
3
+ "version": "0.1.0",
4
+ "description": "An open-source CLI tool to generate licenses for your repository.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "generate-license": "./dist/index.js"
9
+ },
10
+ "keywords": [
11
+ "license",
12
+ "license-generator",
13
+ "license-cli",
14
+ "cli",
15
+ "open-source",
16
+ "oss",
17
+ "node",
18
+ "npm",
19
+ "npx",
20
+ "mit",
21
+ "apache-2.0",
22
+ "boilerplate",
23
+ "project-setup",
24
+ "scaffold"
25
+ ],
26
+ "author": "Anthony Hoang",
27
+ "license": "ISC",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/anth0nycodes/license-generator.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/anth0nycodes/license-generator/issues"
34
+ },
35
+ "homepage": "https://github.com/anth0nycodes/license-generator#readme",
36
+ "devDependencies": {
37
+ "@types/inquirer": "^9.0.9",
38
+ "@types/node": "^25.0.3",
39
+ "tsx": "^4.21.0",
40
+ "typescript": "^5.9.3"
41
+ },
42
+ "dependencies": {
43
+ "@clack/prompts": "^0.11.0",
44
+ "axios": "^1.13.2",
45
+ "commander": "^14.0.2",
46
+ "inquirer": "^13.1.0",
47
+ "picocolors": "^1.1.1"
48
+ },
49
+ "scripts": {
50
+ "dev": "tsx src/index.ts",
51
+ "build": "tsc",
52
+ "start": "node dist/index.js",
53
+ "publish:npm": "pnpm publish --access public"
54
+ }
55
+ }
package/src/helpers.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { execSync } from "node:child_process";
2
+ import { constants } from "node:fs";
3
+ import { access } from "node:fs/promises";
4
+
5
+ export function getGitUsername() {
6
+ const uncleanName = String(execSync("git config user.name"));
7
+ return uncleanName.replace(/\r?\n/g, "");
8
+ }
9
+
10
+ export async function fileExists(path: string): Promise<boolean> {
11
+ try {
12
+ await access(path, constants.F_OK);
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from "commander";
4
+ import { intro, select, isCancel, cancel } from "@clack/prompts";
5
+ import inquirer from "inquirer";
6
+ import { createLicense, getLicenseContent, getLicenses } from "./license.js";
7
+ import color from "picocolors";
8
+ import { getGitUsername } from "./helpers.js";
9
+
10
+ const main = async () => {
11
+ program
12
+ .name("License Generator")
13
+ .description(
14
+ "A CLI application that generates open-source licenses for your repositories.",
15
+ )
16
+ .version("0.1.0");
17
+
18
+ // TODO: Add options, so you can manually add a license flag like --license mit
19
+
20
+ program.parse();
21
+
22
+ intro(color.blueBright("License Generator"));
23
+
24
+ const BASE_URL = "https://api.github.com/licenses";
25
+ const licenses = await getLicenses();
26
+
27
+ // List all available licenses from github api
28
+ const licenseOption = await select({
29
+ message: "Select a license:",
30
+ options: licenses.map((license) => ({
31
+ value: license.key,
32
+ label: license.name,
33
+ })),
34
+ });
35
+
36
+ if (isCancel(licenseOption)) {
37
+ cancel("Operation cancelled.");
38
+ process.exit(0);
39
+ }
40
+
41
+ // Grab the inputs for name and year
42
+ let answers: { name: string; year: string };
43
+
44
+ try {
45
+ answers = await inquirer.prompt([
46
+ {
47
+ type: "input",
48
+ name: "name",
49
+ message: "Enter name:",
50
+ default: getGitUsername(),
51
+ validate(value) {
52
+ if (value.length === 0) return "Name is required";
53
+ return true;
54
+ },
55
+ },
56
+ {
57
+ type: "input",
58
+ name: "year",
59
+ message: "Enter year:",
60
+ default: String(new Date().getFullYear()),
61
+ validate(value) {
62
+ if (value.length === 0) return "Year is required";
63
+ if (!/^\d{4}$/.test(value)) return "Please enter a valid year!";
64
+ return true;
65
+ },
66
+ },
67
+ ]);
68
+ } catch (error) {
69
+ cancel("Operation cancelled.");
70
+ process.exit(0);
71
+ }
72
+
73
+ // Grab the content of the selected license
74
+ const licenseOptionContent = await getLicenseContent(
75
+ `${BASE_URL}/${String(licenseOption)}`,
76
+ );
77
+
78
+ // Write LICENSE file
79
+ try {
80
+ await createLicense(licenseOptionContent, answers.year, answers.name);
81
+ } catch (error) {
82
+ console.error(`Error occurred in createLicense: ${error}`);
83
+ throw error;
84
+ }
85
+ };
86
+
87
+ try {
88
+ main();
89
+ } catch (error) {
90
+ console.error(`Error in main: ${error}`);
91
+ }
package/src/license.ts ADDED
@@ -0,0 +1,87 @@
1
+ import axios from "axios";
2
+ import color from "picocolors";
3
+ import { writeFile } from "node:fs/promises";
4
+ import { fileExists } from "./helpers.js";
5
+ import { cancel, confirm, spinner } from "@clack/prompts";
6
+
7
+ interface LicenseShape {
8
+ key: string;
9
+ name: string;
10
+ spdx_id: string;
11
+ url: string;
12
+ node_id: string;
13
+ }
14
+
15
+ interface LicenseContentShape {
16
+ key: string;
17
+ name: string;
18
+ description: string;
19
+ permissions: string[];
20
+ conditions: string[];
21
+ limitations: string[];
22
+ body: string;
23
+ }
24
+
25
+ export async function getLicenses(): Promise<LicenseShape[]> {
26
+ try {
27
+ const { data }: { data: LicenseShape[] } = await axios.get(
28
+ "https://api.github.com/licenses",
29
+ );
30
+ return data;
31
+ } catch (error) {
32
+ console.error(`Error in getLicenses: ${error}`);
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ export async function getLicenseContent(
38
+ url: string,
39
+ ): Promise<LicenseContentShape> {
40
+ try {
41
+ const { data }: { data: LicenseContentShape } = await axios.get(url);
42
+ return {
43
+ key: data.key,
44
+ name: data.name,
45
+ description: data.description,
46
+ permissions: data.permissions,
47
+ conditions: data.conditions,
48
+ limitations: data.limitations,
49
+ body: data.body,
50
+ };
51
+ } catch (error) {
52
+ console.error(`Error in getLicenseContent: ${error}`);
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ export async function createLicense(
58
+ content: LicenseContentShape,
59
+ year: string,
60
+ name: string,
61
+ ) {
62
+ let uncleanBody = content.body;
63
+
64
+ let body = uncleanBody
65
+ .replaceAll("[year]", year)
66
+ .replaceAll("[yyyy]", year)
67
+ .replaceAll("[fullname]", name)
68
+ .replaceAll("[name of copyright owner]", name)
69
+ .replaceAll("<year>", year)
70
+ .replaceAll("<name of author>", name);
71
+
72
+ if (await fileExists("LICENSE")) {
73
+ const shouldContinue = await confirm({
74
+ message: "LICENSE file already exists. Overwrite?",
75
+ });
76
+
77
+ if (!shouldContinue) {
78
+ cancel("Operation cancelled.");
79
+ process.exit(0);
80
+ }
81
+ }
82
+
83
+ const s = spinner();
84
+ s.start(color.yellow("Generating license..."));
85
+ await writeFile("LICENSE", body, "utf8");
86
+ s.stop(color.greenBright("License created!"));
87
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "bundler",
7
+ "resolveJsonModule": true,
8
+ "allowJs": true,
9
+ "checkJs": false,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "strict": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "declaration": true,
17
+ "declarationMap": true,
18
+ "sourceMap": true,
19
+ "types": ["node"]
20
+ },
21
+ "include": ["src/**/*", "license.ts"],
22
+ "exclude": ["node_modules", "dist"]
23
+ }