Skip to content

refactor: switch away from conjecture and other deps #868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,447 changes: 810 additions & 1,637 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@
"dependencies": {
"chalk": "^5.2.0",
"commander": "^12.0.0",
"conjecture": "^0.1.2",
"egad": "^0.2.0",
"execa": "^8.0.1",
"execa": "^9.0.0",
"fullname": "^5.0.0",
"github-username": "^9.0.0",
"handlebars": "^4.7.8",
"inquirer": "^9.1.4",
"jsesc": "^3.0.2",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
"simple-git": "^3.22.0",
"simple-git": "^3.28.0",
"stringify-author": "^0.1.3",
"validate-npm-package-name": "^5.0.0"
"tinyglobby": "^0.2.14",
"username": "^7.0.0",
"validate-npm-package-name": "^6.0.0"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.2",
Expand All @@ -49,10 +52,9 @@
"@types/lodash.camelcase": "^4.3.7",
"@types/lodash.kebabcase": "^4.1.7",
"@types/node": "^20.0.0",
"@types/rimraf": "^4.0.5",
"@types/validate-npm-package-name": "^4.0.0",
"prettier": "^3.2.4",
"rimraf": "^5.0.5",
"rimraf": "^6.0.1",
"typescript": "^5.3.3"
},
"type": "module",
Expand Down
155 changes: 155 additions & 0 deletions src/helpers/conjecture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { exec } from "node:child_process";
import githubUsername from "github-username";
import { username } from "username";
import fullName from "fullname";

import fs from "node:fs";
import os from "node:os";
import path from "node:path";

import parse from "parse-git-config";

Check failure on line 10 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (20)

Cannot find module 'parse-git-config' or its corresponding type declarations.

Check failure on line 10 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (18)

Cannot find module 'parse-git-config' or its corresponding type declarations.

Check failure on line 10 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (21)

Cannot find module 'parse-git-config' or its corresponding type declarations.
import extend from "extend-shallow";

Check failure on line 11 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (20)

Cannot find module 'extend-shallow' or its corresponding type declarations.

Check failure on line 11 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (18)

Cannot find module 'extend-shallow' or its corresponding type declarations.

Check failure on line 11 in src/helpers/conjecture.ts

View workflow job for this annotation

GitHub Actions / test_matrix (21)

Cannot find module 'extend-shallow' or its corresponding type declarations.



/*!
* git-user-name <https://github.com/jonschlinkert/git-user-name>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
function gitUserName(options: { path?: string; gitconfig?: any }) {
let gc = gitConfigPath(
extend({ type: "global" }, options && options.gitconfig),
);

options = extend({ cwd: "/", path: gc }, options);

let config = parse.sync(options) || {};

return config.user ? config.user.name : null;
}

/*!
* git-user-email <https://github.com/jonschlinkert/git-user-email>
*
* Copyright (c) 2014-2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
function gitEmail(opts: { path: string | null }) {
opts = extend({ cwd: "/", path: gitConfigPath() }, opts);
let config = parse.sync(opts);
if (typeof config === "object" && config.hasOwnProperty("user")) {
return config.user.email;
}
return null;
}

/*!

* git-config-path <https://github.com/jonschlinkert/git-config-path>
*
* Copyright (c) 2015-present, Jon Schlinkert.
* Licensed under the MIT License.
*/
function gitConfigPath(): string | null;
function gitConfigPath(type: string): string | null;
function gitConfigPath(options: { type?: string; cwd?: string }): string | null;
function gitConfigPath(type: string, options: { cwd?: string }): string | null;
function gitConfigPath(
type?: string | { type?: string; cwd?: string } | null | undefined,
options?: { type?: string; cwd?: string } | null | undefined,
): string | null {
if (typeof type !== "string") {
options = type;
type = null;
}

let opts = Object.assign({ cwd: process.cwd(), type }, options);
let configPath;

if (opts.type === "global") {
configPath = path.join(os.homedir(), ".gitconfig");
} else {
configPath = path.resolve(opts.cwd, ".git/config");
}

if (!fs.existsSync(configPath)) {
if (typeof opts.type === "string") return null;
configPath = path.join(os.homedir(), ".config/git/config");
}

return fs.existsSync(configPath) ? configPath : null;
}

/*!
* conjecture <https://github.com/boneskull/conjecture>
*
* Copyright (c) 2017 Christopher Hiller <[email protected]> (https://boneskull.com)
* Licensed under the Apache License, Version 2.0.
*/

/**
* Attempts to guess the current user's email address via working copy Git
* config, global Git config, or npm config.
* @returns {Promise<string|void>} Email address, if found
*/
function guessEmail(): Promise<string | void> {
let email;
try {
email =
gitEmail({ path: gitConfigPath() }) ||
gitEmail({ path: gitConfigPath("global") });
} catch (e) {
return Promise.reject(e);
}
if (email) {
return Promise.resolve(email);
}
return new Promise((resolve) => {
exec("npm config get email", (err, email = "") => {
if (err) {
// ignored
resolve();
}
resolve(email.trim() || void 0);
});
});
}

/**
* Returns GitHub username from an email address, if the email address was
* supplied, and there was a match. Otherwise returns current user's username.
* @param {string} [email] - Lookup GitHub user by this email
* @returns {Promise<string|void>} Username, if found
*/
async function guessGitHubUsername(
email?: string | undefined,
): Promise<string | void> {
try {
const email_1 = await Promise.resolve(email || guessEmail());
// @ts-expect-error No need to appease TypeScript here
return githubUsername(email_1);
} catch {
return await username();
}
}

/**
* Attempts to guess the (real) name of current user from working copy Git
* username, global Git username, or system full name.
* @returns {string} Full name of author, if found
*/
function guessAuthor(): string {
function getAuthor(path?: string | null) {
return path && gitUserName({ path });
}

return (
getAuthor(gitConfigPath()) ||
getAuthor(gitConfigPath("global")) ||
fullName()
);
}

export { guessEmail, guessGitHubUsername, guessAuthor };
13 changes: 9 additions & 4 deletions src/helpers/filesystem.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import * as fs from "node:fs";
import { generate } from "egad";
import { Config } from "./user-interaction.js";
import { yellow, green } from "./write-help.js";
import { generateFromTemplates } from "./handlebars.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Expand Down Expand Up @@ -79,9 +79,14 @@ export async function makeScaffolding(config: Config): Promise<Config> {
path.join(tempDestPath, ".gitignore"),
);

const result = await generate(tempDestPath, config.destination, config, {
overwrite: config.overwrite,
});
const result = await generateFromTemplates(
tempDestPath,
config.destination,
config,
{
overwrite: config.overwrite,
},
);

fs.rmSync(tempDestPath, {
recursive: true,
Expand Down
86 changes: 86 additions & 0 deletions src/helpers/handlebars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import handlebars from "handlebars";
import { glob } from "tinyglobby";
import fs from "node:fs";
import { readFile, mkdir, writeFile, copyFile } from "node:fs/promises";
import path from "node:path";

interface GenerateOptions {
overwrite?: boolean;
}

const HANDLEBARS_RX = /{{([^{}]+)}}/g;

// Files by basename you always want raw-copied:
const SKIP_NAMES = new Set([
"index.js",
"index.ts",
"index.test.js",
"index.test.ts",
"mock-cert.pem",
"issues.opened.json",
"check_run.created.json",
"check_suite.requested.json",
"app.yml",
"tsconfig.json",
"vitest.config.ts",
]);

export async function generateFromTemplates(
sourceDir: string,
destDir: string,
context: Record<string, any>,
options: GenerateOptions = {},
): Promise<{ path: string; skipped: boolean }[]> {
const filePaths = await glob("**/*", {
cwd: sourceDir,
dot: true,
onlyFiles: true,
});

const results: { path: string; skipped: boolean }[] = [];

for (const relPath of filePaths) {
const srcPath = path.join(sourceDir, relPath);
const destPath = path.join(destDir, relPath);
const filename = path.basename(relPath);

// 1) Always raw-copy blacklisted names or anything under test/fixtures/
const isFixture = relPath.startsWith("test/fixtures/");
const isBlacklisted = SKIP_NAMES.has(filename);
if (isFixture || isBlacklisted) {
const exists = fs.existsSync(destPath);
const skip = exists && !options.overwrite;
if (!skip) {
await mkdir(path.dirname(destPath), { recursive: true });
await copyFile(srcPath, destPath);
}
results.push({ path: destPath, skipped: skip });
continue;
}

// 2) Read file and decide if it needs Handlebars
const raw = await readFile(srcPath, "utf8");
const needsTemplate = HANDLEBARS_RX.test(raw);

const exists = fs.existsSync(destPath);
const skip = exists && !options.overwrite;
if (skip) {
results.push({ path: destPath, skipped: true });
continue;
}

await mkdir(path.dirname(destPath), { recursive: true });

if (needsTemplate) {
// compile with Handlebars
const rendered = handlebars.compile(raw)(context);
await writeFile(destPath, rendered);
} else {
// no templates—copy verbatim
await writeFile(destPath, raw);
}
results.push({ path: destPath, skipped: false });
}

return results;
}
2 changes: 1 addition & 1 deletion src/helpers/user-interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";

import { guessEmail, guessGitHubUsername, guessAuthor } from "conjecture";
import { guessEmail, guessGitHubUsername, guessAuthor } from "./conjecture.js";
import camelCase from "lodash.camelcase";
import * as commander from "commander";
import inquirer, { Answers, Question, QuestionCollection } from "inquirer";
Expand Down
2 changes: 1 addition & 1 deletion templates/basic-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"probot": "^13.0.1"
},
"devDependencies": {
"nock": "^14.0.0-beta.5",
"nock": "^14.0.0",
"smee-client": "^2.0.0"
},
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions templates/basic-js/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Probot, ProbotOctokit } from "probot";
// Requiring our fixtures
//import payload from "./fixtures/issues.opened.json" with { type: "json" };
const issueCreatedBody = { body: "Thanks for opening this issue!" };
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { describe, beforeEach, afterEach, test } from "node:test";
import assert from "node:assert";
Expand Down
2 changes: 1 addition & 1 deletion templates/basic-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"scripts": {
"build": "tsc",
"start": "probot run ./lib/index.js",
"test": "vitest"
"test": "vitest run"
},
"dependencies": {
"probot": "^13.4.5"
Expand Down
2 changes: 1 addition & 1 deletion templates/checks-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"probot": "^13.0.1"
},
"devDependencies": {
"nock": "^14.0.0-beta.5",
"nock": "^14.0.0",
"smee-client": "^2.0.0"
},
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions templates/checks-js/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Probot, ProbotOctokit } from "probot";
// Requiring our fixtures
//import checkSuitePayload from "./fixtures/check_suite.requested" with { type: "json" };
//import checkRunSuccess from "./fixtures/check_run.created" with { type: "json" };
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { describe, beforeEach, afterEach, test } from "node:test";
import assert from "node:assert";
Expand Down
2 changes: 1 addition & 1 deletion templates/deploy-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"probot": "^13.0.1"
},
"devDependencies": {
"nock": "^14.0.0-beta.5",
"nock": "^14.0.0",
"smee-client": "^2.0.0"
},
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions templates/deploy-js/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import myProbotApp from "../index.js";
import { Probot, ProbotOctokit } from "probot";
// Requiring our fixtures
//import payload from "./fixtures/pull_request.opened" with { type: "json" };
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, beforeEach, afterEach, test } from "node:test";
import assert from "node:assert";

Expand Down
Loading
Loading