From 569d1f1b3a36e36c991917653c02ecd8171ff475 Mon Sep 17 00:00:00 2001 From: Cherrie Chang Date: Mon, 20 Jan 2025 12:04:17 -0500 Subject: [PATCH 1/3] commit changeset --- .changeset/gorgeous-grapes-poke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/gorgeous-grapes-poke.md b/.changeset/gorgeous-grapes-poke.md index 58b8ca5..ad8c18a 100644 --- a/.changeset/gorgeous-grapes-poke.md +++ b/.changeset/gorgeous-grapes-poke.md @@ -2,4 +2,4 @@ "@jspsych/new-timeline": patch --- -This release publishes the @jspsych/new-timeline package on npm. This is a command line tool for creating jsPsych timelines with provided template code. +This release publishes the @jspsych/new-timeline package on npm. This is a command line tool for creating jsPsych timelines with provided template code. All prompts have been standardized to be of the form "Enter...:" From 8317d5bac4c95d880abd64159401eedac19b17d2 Mon Sep 17 00:00:00 2001 From: Cherrie Chang Date: Mon, 20 Jan 2025 14:03:20 -0500 Subject: [PATCH 2/3] commit new changeset; make new-plugin cli.js match new-timeline --- ...grapes-poke.md => nervous-geckos-prove.md} | 0 .changeset/plenty-books-wait.md | 7 - packages/new-plugin/src/cli.js | 357 ++++++++++-------- 3 files changed, 209 insertions(+), 155 deletions(-) rename .changeset/{gorgeous-grapes-poke.md => nervous-geckos-prove.md} (100%) delete mode 100644 .changeset/plenty-books-wait.md diff --git a/.changeset/gorgeous-grapes-poke.md b/.changeset/nervous-geckos-prove.md similarity index 100% rename from .changeset/gorgeous-grapes-poke.md rename to .changeset/nervous-geckos-prove.md diff --git a/.changeset/plenty-books-wait.md b/.changeset/plenty-books-wait.md deleted file mode 100644 index bf42e18..0000000 --- a/.changeset/plenty-books-wait.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych/new-extension": patch -"@jspsych/new-timeline": patch -"@jspsych/new-plugin": patch ---- - -This patch adds README for new-extension and new-timeline; fixes new-timeline package name; and fixes note in README for all 3 packages. diff --git a/packages/new-plugin/src/cli.js b/packages/new-plugin/src/cli.js index a29ad78..cd71714 100755 --- a/packages/new-plugin/src/cli.js +++ b/packages/new-plugin/src/cli.js @@ -15,167 +15,228 @@ const git = simpleGit(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -function formatName(input) { - return input - .trim() - .replace(/[\s_]+/g, "-") // Replace all spaces and underscores with hyphens - .replace(/([a-z])([A-Z])/g, "$1-$2") // Replace camelCase with hyphens - .replace(/[^\w-]/g, "") // Remove all non-word characters - .toLowerCase(); +async function getRepoRoot() { + try { + const rootDir = await git.revparse(["--show-toplevel"]); + return rootDir; + } catch (error) { + console.error("Not a git repository or no repository root found."); + return null; + } } -async function getRepoRoot() { - try { - const rootDir = await git.revparse(['--show-toplevel']); - return rootDir; - } catch (error) { - console.error("Not a git repository or no repository root found."); - return null; +async function getRemoteGitRootUrl() { + try { + const remotes = await git.getRemotes(true); + const originRemote = remotes.find((remote) => remote.name === "origin"); + if (originRemote) { + let remoteGitRootUrl = originRemote.refs.fetch; + if (remoteGitRootUrl.startsWith("git@github.com:")) { + remoteGitRootUrl = remoteGitRootUrl.replace("git@github.com:", "git+https://github.com/"); + } + return remoteGitRootUrl; } + console.warn("No remote named 'origin' found."); + return ""; + } catch (error) { + console.error("Error getting remote root Git URL:", error); + return ""; + } } -async function runPrompts() { - let isGitRepo = await git.checkIsRepo(); - let isContrib = false; - if (isGitRepo) { - isContrib = await git.getRemotes(true).then(remotes => { - return remotes.some(remote => remote.refs.fetch.includes('git@github.com:jspsych/jspsych-contrib.git')); - }); +async function getRemoteGitUrl() { + let remoteGitUrl; + const remoteGitRootUrl = await getRemoteGitRootUrl(); + const repoRoot = await getRepoRoot(); + if (repoRoot) { + const currentDir = process.cwd(); + const relativePath = path.relative(repoRoot, currentDir); + if (relativePath) { + remoteGitUrl = `${remoteGitRootUrl}/tree/main/${relativePath}`; } + return remoteGitUrl; + } + console.warn("No Git repository root found."); + return ""; +} - let destDir = isContrib ? path.join(await getRepoRoot(), 'packages') : process.cwd(); - - const name = await input({ - message: "What would you like to call this plugin package?", - required: true, - transformer: (input) => { - // convert to hyphen case - return formatName(input); - }, - validate: (input) => { - const fullpackageFilename = `${destDir}/plugin-${formatName(input)}`; - if (fs.existsSync(fullpackageFilename)) { - return "A plugin package with this name already exists. Please choose a different name."; - } else { - return true; - } - }, - }); - - const description = await input({ - message: "Enter a brief description of the plugin package:", - required: true, - }); - - const author = await input({ - message: "What is the name of the author of this plugin package?", - required: true, - }); - - const authorUrl = await input({ - message: "Enter a profile URL for the author, e.g. a link to a GitHub profile [Optional]:", - }); - - const language = await select({ - message: "What language would you like to use for your plugin?", - choices: [ - { - name: "TypeScript", - value: "ts", - }, - { - name: "JavaScript", - value: "js", - }, - ], - loop: false, - }); - - let readmePath = ""; - if (!isContrib) { - readmePath = await input({ - message: "Enter the path to the README.md file for this plugin package [Optional]:", - default: `${destDir}/plugin-${name}/README.md` - }); - } - - return { - isContrib: isContrib, - destDir: destDir, - name: name, - description: description, - author: author, - authorUrl: authorUrl, - language: language, - readmePath: readmePath - }; +function getGitHttpsUrl(gitUrl) { + gitUrl = gitUrl.replace("git+", ""); + gitUrl = gitUrl.replace(".git", ""); + return gitUrl; } -async function processAnswers(answers) { - answers.name = formatName(answers.name); - const camelCaseName = - answers.name.charAt(0).toUpperCase() + - answers.name.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase()); - - const globalName = "jsPsych" + camelCaseName; - - const packageFilename = `plugin-${answers.name}`; - const destPath = path.join(answers.destDir, packageFilename); - const readMePath = (() => { - if (answers.isContrib) { - return `https://github.com/jspsych/jspsych-contrib/packages/${packageFilename}/README.md`; - } - else { - return answers.readmePath; - } - })(); - - const templatesDir = path.resolve(__dirname, '../templates'); - - function processTemplate() { - return src(`${templatesDir}/plugin-template-${answers.language}/**/*`) - .pipe(replace("{name}", answers.name)) - .pipe(replace("{full-name}", packageFilename)) - .pipe(replace("{author}", answers.author)) - .pipe(replace("{description}", answers.description)) - .pipe(replace("{authorUrl}", answers.authorUrl)) - .pipe(replace("_globalName_", globalName)) - .pipe(replace("{globalName}", globalName)) - .pipe(replace("{camelCaseName}", camelCaseName)) - .pipe(replace("PluginNamePlugin", `${camelCaseName}Plugin`)) - .pipe(replace("{documentation-url}", readMePath)) - .pipe(dest(destPath)); - } +function hyphenateName(input) { + return input + .trim() + .replace(/[\s_]+/g, "-") // Replace all spaces and underscores with hyphens + .replace(/([a-z])([A-Z])/g, "$1-$2") // Replace camelCase with hyphens + .replace(/[^\w-]/g, "") // Remove all non-word characters + .toLowerCase(); +} - function renameExampleTemplate() { - return src(`${destPath}/examples/index.html`) - .pipe(replace("{name}", answers.name)) - .pipe(replace("{globalName}", globalName)) - .pipe(dest(`${destPath}/examples`)); - } +function camelCaseName(input) { + return ( + input.charAt(0).toUpperCase() + input.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + ); +} - function renameDocsTemplate() { - return src(`${destPath}/docs/docs-template.md`) - .pipe(rename(`${answers.name}.md`)) - .pipe(dest(`${destPath}/docs`)) - .on("end", function () { - deleteSync(`${destPath}/docs/docs-template.md`, { force: true }); - }); - } +async function getCwdInfo() { + // If current directory is the jspsych-contrib repository + if (await git.checkIsRepo()) { + const remotes = await git.getRemotes(true); + return { + isContribRepo: remotes.some((remote) => + remote.refs.fetch.includes("git@github.com:jspsych/jspsych-contrib.git") + ), + destDir: path.join(await getRepoRoot(), "packages"), + }; + } + // If current directory is not the jspsych-contrib repository + else { + return { + isContribRepo: false, + destDir: process.cwd(), + }; + } +} - function renameReadmeTemplate() { - return src(`${destPath}/README.md`) - .pipe( - replace( - `{authorInfo}`, - answers.authorUrl ? `[${answers.author}](${answers.authorUrl})` : `${answers.author}` - ) - ) - .pipe(dest(destPath)); - } +async function runPrompts(cwdInfo) { + const name = await input({ + message: "Enter the name you would like this plugin package to be called:", + required: true, + transformer: (input) => { + return hyphenateName(input); + }, + validate: (input) => { + const packagePath = `${cwdInfo.destDir}/plugin-${hyphenateName(input)}`; + if (fs.existsSync(packagePath)) { + return "A plugin package with this name already exists in this directory. Please choose a different name."; + } else { + return true; + } + }, + }); + + const description = await input({ + message: "Enter a brief description of this plugin package:", + required: true, + }); + + const author = await input({ + message: "Enter the name of the author of this plugin package:", + required: true, + }); + + const authorUrl = await input({ + message: "Enter a profile URL for the author, e.g. a link to their GitHub profile [Optional]:", + }); + + const language = await select({ + message: "What language would you like to use for your plugin package?", + choices: [ + { name: "TypeScript", value: "ts" }, + { name: "JavaScript", value: "js" }, + ], + loop: false, + }); + + // If not in the jspsych-contrib repository, ask for the path to the README.md file + let readmePath; + if (!isContrib) { + readmePath = await input({ + message: "Enter the path to the README.md file for this plugin package [Optional]:", + default: `${getGitHttpsUrl(await getRemoteGitUrl)}/plugin-${name}/README.md`, // '/plugin-${name}/README.md' if not a Git repository + }); + } else { + readmePath = `https://github.com/jspsych/jspsych-contrib/packages/plugin-${name}/README.md`; + } + + return { + name: name, + description: description, + author: author, + authorUrl: authorUrl, + language: language, + readmePath: readmePath, + destDir: cwdInfo.destDir, + isContribRepo: cwdInfo.isContribRepo, + }; +} - series(processTemplate, renameExampleTemplate, renameDocsTemplate, renameReadmeTemplate)(); +async function processAnswers(answers) { + answers.name = hyphenateName(answers.name); + const globalName = "jsPsychPlugin" + camelCaseName(answers.name); + const packageName = `plugin-${answers.name}`; + const destPath = path.join(answers.destDir, packageName); + const npmPackageName = (() => { + if (answers.isContribRepo) { + return `@jspsych-contrib/${packageName}`; + } else { + return packageName; + } + })(); + + const templatesDir = path.resolve(__dirname, "../templates"); + const gitRootUrl = await getRemoteGitRootUrl(); + const gitRootHttpsUrl = getGitHttpsUrl(gitRootUrl); + + function processTemplate() { + return src(`${templatesDir}/plugin-template-${answers.language}/**/*`) + .pipe(replace("{name}", `plugin-${answers.name}`)) + .pipe(replace("{npmPackageName}", npmPackageName)) + .pipe(replace("{author}", answers.author)) + .pipe(replace("{authorUrl}", answers.authorUrl)) + .pipe(replace("{description}", answers.description)) + .pipe(replace("_globalName_", globalName)) + .pipe(replace("{globalName}", globalName)) + .pipe(replace("{camelCaseName}", camelCaseName)) + .pipe(replace("PluginNamePlugin", `${camelCaseName}Plugin`)) + .pipe(replace("{gitRootUrl}", gitRootUrl)) + .pipe(replace("{gitRootHttpsUrl}", gitRootHttpsUrl)) + .pipe(replace("{documentationUrl}", answers.readMePath)) + .pipe(dest(destPath)); + } + + function renameExampleTemplate() { + return src(`${destPath}/examples/index.html`) + .pipe(replace("{name}", answers.name)) + .pipe(replace("{globalName}", globalName)) + .pipe(dest(`${destPath}/examples`)); + } + + function renameDocsTemplate() { + return src(`${destPath}/docs/docs-template.md`) + .pipe(rename(`${answers.name}.md`)) + .pipe(dest(`${destPath}/docs`)) + .on("end", function () { + deleteSync(`${destPath}/docs/docs-template.md`, { force: true }); + }); + } + + function renameReadmeTemplate() { + return src(`${destPath}/README.md`) + .pipe(replace(`{npmPackageName}`, npmPackageName)) + .pipe( + replace( + `{authorInfo}`, + answers.authorUrl ? `[${answers.author}](${answers.authorUrl})` : `${answers.author}` + ) + ) + .pipe( + replace( + `## Loading`, + answers.isTimelinesRepo + ? '## Loading\n\n### In browser\n\n```html\n - + {publishingComment} diff --git a/packages/new-timeline/templates/timeline-template-js/package.json b/packages/new-timeline/templates/timeline-template-js/package.json index 26a3343..3384194 100644 --- a/packages/new-timeline/templates/timeline-template-js/package.json +++ b/packages/new-timeline/templates/timeline-template-js/package.json @@ -1,5 +1,5 @@ { - "name": "@jspsych-timelines/{name}", + "name": "{npmPackageName}", "version": "0.0.1", "description": "{description}", "unpkg": "dist/index.browser.min.js", @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/jspsych/jspsych-timelines.git", + "url": "{gitRootUrl}", "directory": "packages/{name}" }, "author": { @@ -22,9 +22,9 @@ }, "license": "MIT", "bugs": { - "url": "https://github.com/jspsych/jspsych-timelines/issues" + "url": "{gitRootHttpsUrl}/issues" }, - "homepage": "https://github.com/jspsych/jspsych-timelines/tree/main/packages/{name}", + "homepage": "{documentationUrl}", "devDependencies": { "@jspsych/config": "^2.0.0", "jspsych": "^7.0.0" diff --git a/packages/new-timeline/templates/timeline-template-ts/examples/index.html b/packages/new-timeline/templates/timeline-template-ts/examples/index.html index 4ed3288..ab4b611 100644 --- a/packages/new-timeline/templates/timeline-template-ts/examples/index.html +++ b/packages/new-timeline/templates/timeline-template-ts/examples/index.html @@ -3,6 +3,7 @@ + {publishingComment}