From dac686eb9e1033da127a853c9a86f0716b24a500 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 10:00:30 -0400 Subject: [PATCH 01/13] Fixes switch-case indent rule in eslintconfig. --- .eslintrc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.eslintrc b/.eslintrc index d52c62e..3dfb856 100644 --- a/.eslintrc +++ b/.eslintrc @@ -24,10 +24,10 @@ "README.md" ], "rules": { - "indent": ["error", "tab"], - "eol-last": ["error", "always", { "SwitchCase": 1 }], + "indent": ["error", "tab", { "SwitchCase": 1 }], + "eol-last": ["error", "always"], "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "no-console": ["error", { "allow": ["info", "warn", "error"] }] + "no-console": ["error", { "allow": ["info", "warn", "error", "clear"] }] } } From 081a6e50590785651a1b85bbf99f0f381619a6a4 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 10:00:42 -0400 Subject: [PATCH 02/13] Removes unnecessary escape characters. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index ff40a0a..7aaa744 100644 --- a/index.js +++ b/index.js @@ -902,7 +902,7 @@ function logSuccess() { }); logInfo({ title: 'What\'s next?', - description: `Head over to your new theme directory to install dependencies\nand start cooking something up! If we\'ve initialized a repository for you, we\ncommited the initial files and added a remote origin, but we didn\'t push\nupstream. It\'s also a good idea to check your LICENSE file to fill out any\nplaceholders that may be in the text. ${chalk.bold('Now, go build something beautiful.')}`, + description: `Head over to your new theme directory to install dependencies\nand start cooking something up! If we've initialized a repository for you, we\ncommited the initial files and added a remote origin, but we didn't push\nupstream. It's also a good idea to check your LICENSE file to fill out any\nplaceholders that may be in the text. ${chalk.bold('Now, go build something beautiful.')}`, emoji: '⚡', padding: 'bottom', }); From c29fa735ef6df470195cc45176d114d5f57ace3c Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 10:56:53 -0400 Subject: [PATCH 03/13] Adds proper names to workflow jobs. --- .github/workflows/publish.yml | 4 +++- .github/workflows/release.yml | 4 +++- package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b2323da..ccf02e8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,10 +2,12 @@ name: Publish on: release: - types: [published] + types: + - published jobs: publish: + name: Publish runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1582d3b..ff37ed2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,10 +2,12 @@ name: Release on: push: - branches: [master] + branches: + - master jobs: release: + name: Release runs-on: ubuntu-latest steps: diff --git a/package.json b/package.json index ccb9fc3..e58d614 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,9 @@ "eslint": "~8.46.0" }, "scripts": { + "test": "node index.js -p tests", "lint": "eslint .", "fix": "eslint --fix .", - "test": "node index.js -p tests", "clean-tests": "rm -rf tests/**", "clean-deps": "rm -rf node_modules/ package-lock.json", "clean": "npm run clean-deps && npm run clean-tests" From b8ff0ef6abda3ff340e504803e9d96bb05ded30d Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 10:57:21 -0400 Subject: [PATCH 04/13] Adds a test workflow for PRs to main branch. --- .github/workflows/test.yml | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4ba4c11 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,45 @@ +name: Test + +on: + pull_request: + branches: + - master + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it. + - name: Checkout Repo + id: checkout_repo + uses: actions/checkout@v4 + + # Gets the Node version from .nvmrc and sets it as an environment variable. + - name: Set Node Version + id: set_node_version + run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV + + # Setup Node. + - name: Setup Node + id: setup_node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + # Installs npm and composer dependencies. + - name: Install + id: install + run: npm ci + + # Lint all files. + - name: Lint + id: lint + run: npm run lint + + # Runs a test theme creation with all options set to skip prompt. Note we also set + # the path to `tests` and enable the `verbose` option for debugging. + - name: Create Theme + id: create_theme + run: npm test test-theme -- -v -p "tests" -N "Test Theme" -X "1.0.0" -T "parent-theme" -U "https://github.com/dreamsicle-io/test" -B "https://github.com/dreamsicle-io/test/issues" -R "git@github.com:dreamsicle-io/test.git" -r "git" -d "Just another WordPress theme." -A "Dreamsicle" -E "hello@dreamsicle.io" -u "https://www.dreamsicle.io" -L "gpl-3.0" --t "accessibility-ready,translation-ready" -W "6.1.0" -w "6.4.0" -F "test" -C "Test" -c "TEST" From 741f3e0a6960b4ab16fd8a03f201bc215f7f9a91 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 10:59:26 -0400 Subject: [PATCH 05/13] 3.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5ce932..7c5f397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dreamsicle.io/create-wp-theme", - "version": "3.0.2", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dreamsicle.io/create-wp-theme", - "version": "3.0.2", + "version": "3.1.0", "license": "GPL-3.0", "dependencies": { "chalk": "~4.1.2", diff --git a/package.json b/package.json index e58d614..2f9aae4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dreamsicle.io/create-wp-theme", - "version": "3.0.2", + "version": "3.1.0", "type": "module", "description": "A command line tool for creating modern, optimized WordPress themes.", "main": "index.js", From 376aff0e22c4409960bec95341445d900bb771c7 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 11:54:13 -0400 Subject: [PATCH 06/13] Updates README. --- README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1494a8a..6a4faf2 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Create WP Theme -Create WP Theme is a node command line tool that will scaffold a new WordPress theme with an opinionated file structure and just the right amount of starter code to get a developer started building a modern WordPress theme. This package contains just the `npx @dreamsicle.io/create-wp-theme` command, all of the actual boilerplate code comes from [WP Theme Assets](https://github.com/dreamsicle-io/wp-theme-assets). +Create WP Theme is a node command line tool that will scaffold a new WordPress theme with an opinionated file structure and just the right amount of starter code. This package contains just the `npx @dreamsicle.io/create-wp-theme` command, all of the actual boilerplate code comes from [WP Theme Assets](https://github.com/dreamsicle-io/wp-theme-assets). ## Usage Open a terminal, `cd` to the `/path/to/wordpress/wp-content/themes` directory of a local WordPress instance, and fire the `create-wp-theme` command. ```shell -npx @dreamsicle.io/create-wp-theme [options] +npx @dreamsicle.io/create-wp-theme [options] ``` ## Getting Started -All that is necessary to start using the tool is a single argument of `file`, which corresponds to a param-cased string that will serve as the theme directory, the WordPress text-domain, and the package name. This will also serve as regular expression to replace all instances of `wp-theme` in the cloned package files. +All that is necessary to start using the tool is a single argument of `dir`, which corresponds to a kebab-cased string that will serve as the theme directory, the WordPress text-domain, and the package name. ### 1. Run the `create-wp-theme` command @@ -103,7 +103,35 @@ themeAuthorEmail: hello@dreamsicle.com themeAuthorURI: https://www.dreamsicle.com ``` -## Help +## File Generation + +The tool will rename files and generate file contents if it detects placeholders in the supported [WP Theme Assets](https://github.com/dreamsicle-io/wp-theme-assets) files. Note that casing will be adjusted automatically to ensure proper conventions. + +### File Content Replacement + +| Placeholder | Replacement | Description | +| ----------- | ------------------------ | ------------------------------------------- | +| `wp-theme` | `` | The kebab-cased directory argument. | +| `wp_theme` | `-F`, `--functionPrefix` | The snake-cased function prefix option. | +| `WP_Theme` | `-C`, `--classPrefix` | The pascal-snake-cased class prefix option. | +| `WP_THEME` | `-c`, `--constantPrefix` | The constant-cased constant prefix option. | +| `WP Theme` | `-N`, `--themeName` | The unmodified theme name option. | + +### File Renaming + +| Placeholder | Replacement | Description | +| ------------------ | --------------------- | ------------------------------------- | +| `class-wp-theme-*` | `-C`, `--classPrefix` | The parsed class prefix, kebab-cased. | + +## Licenses + +The tool will write a license for you according to the [SPDX](https://spdx.dev/) license expression provided in the `-L` or `--themeLicense` option from the [GitHub License API](https://docs.github.com/en/rest/licenses/licenses). You may use any of the [supported licenses](https://api.github.com/licenses?per_page=1000&page=1) to automatically write a license for you on theme creation. While SPDX technically supports [hundreds of license identifiers](https://spdx.org/licenses/), this tool will _only_ automatically write licenses for those supported by the Github License API. + +To skip license generation, use `UNLICENSED` as the license identifier. + +> **Note:** Some licenses ship with placeholders for things like company name, date, etc. Make sure to fill these out once your theme is generated. + +## Help To get help with the tool and to learn more about usage and the available options, use the `--help` or `-h` flag. This will output all help information available including how to use the command, arguments, option flags, option descriptions, and option defaults. From 6af5bcdca0f33a88cb14f27707978de2756d716f Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 11:54:47 -0400 Subject: [PATCH 07/13] Updates comments and log messages. --- index.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 7aaa744..92f3431 100644 --- a/index.js +++ b/index.js @@ -560,7 +560,7 @@ async function runPrompt() { // Introduce the prompt and log the pre-processed data for debugging. logInfo({ title: 'Let\'s get started', - description: `This tool will guide you through configuring your theme.\nFor each prompt, set a value and hit "ENTER" to continue. To exit early, hit\n"CMD+C" on Mac, or "CTRL+C" on Windows. For help, run "create-wp-theme -h" to\noutput the tool's help information. If you need to log or view issues, visit\n${pkg.bugs.url}.`, + description: `This tool will guide you through configuring your theme.\nFor each prompt, set a value and hit "ENTER" to continue. To exit early, hit\n"CMD+C" on Mac, or "CTRL+C" on Windows. For help, run the command with the "-h"\nor "--help" flags to output the tool's help information. If you need to log or\nview issues, visit ${pkg.bugs.url}.`, emoji: '⚡', padding: 'both', verbose: options.verbose, @@ -808,7 +808,7 @@ async function writeLicense() { function putPackage() { // Double check if the directory exists already and throw an error if not to - // avoid accidnetally removing important data on the machine. + // avoid accidentally removing important data on the machine. if (fs.existsSync(themePath)) throw new Error(`There is already a directory at "${themePath}"`); // Copy the final build from the tmp directory to the real directory and clean the tmp directory. fs.cpSync(tmpThemePath, themePath, { recursive: true, force: true }); @@ -866,8 +866,7 @@ function initGitRepo() { function initRepo() { try { - // Switch on the repo type and initialize a git repo with - // remote origin based on repo type. + // Switch on the repo type and initialize a repo with remote origin. switch (options.themeRepoType) { case 'git': { initGitRepo(); @@ -883,7 +882,7 @@ function initRepo() { } } } catch (error) { - // We don't want Git errors to exit the process, so don't throw. + // We don't want repo errors to exit the process, so don't throw. // Instead, catch them and log them so the user is aware, while // allowing the process to continue. logError(error, options.verbose); @@ -891,7 +890,7 @@ function initRepo() { } function logSuccess() { - // Prepare the final theme path. + // Prepare the relative theme path. const relPath = path.relative(process.cwd(), themePath); // Log information to the console. logInfo({ From 7e4085e8a5b42de0826668f495b3c9288cd37462 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 11:59:24 -0400 Subject: [PATCH 08/13] Updates README. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6a4faf2..7defaaf 100644 --- a/README.md +++ b/README.md @@ -119,9 +119,9 @@ The tool will rename files and generate file contents if it detects placeholders ### File Renaming -| Placeholder | Replacement | Description | -| ------------------ | --------------------- | ------------------------------------- | -| `class-wp-theme-*` | `-C`, `--classPrefix` | The parsed class prefix, kebab-cased. | +| Placeholder | Replacement | Description | +| ------------------ | --------------------- | -------------------------------------------- | +| `class-wp-theme-*` | `-C`, `--classPrefix` | The parsed class prefix option, kebab-cased. | ## Licenses From 5e312a186c2b94079ddff68fd2ca0412fa7c83e7 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 12:26:40 -0400 Subject: [PATCH 09/13] Adds logging before fetch license call in case the call takes a long time or fails. Updates log emojis for async logs. --- index.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 92f3431..92c87ba 100644 --- a/index.js +++ b/index.js @@ -110,6 +110,10 @@ const tmpThemeReadmePath = path.join(tmpThemePath, 'README.md'); const gitURL = 'https://github.com/dreamsicle-io/wp-theme-assets.git'; const gitBranch = 'master'; +// Construct license settings. +const licenseAPIEndpoint = 'https://api.github.com/licenses'; +const licenseAPIDocsURL = 'https://docs.github.com/en/rest/licenses/licenses'; + /** * Construct option definitions. * @type {OptionDef[]} @@ -604,7 +608,7 @@ function clonePackage() { logInfo({ title: 'Cloning package', description: `${gitURL} (${gitBranch})`, - emoji: '📥', + emoji: '⏳', verbose: options.verbose, dataLabel: 'Package information', data: { @@ -756,7 +760,7 @@ function replaceRename() { */ async function fetchLicense(slug) { const formattedSlug = encodeURIComponent(slug.toLowerCase()); - const response = await fetch(`https://api.github.com/licenses/${formattedSlug}`, { + const response = await fetch(`${licenseAPIEndpoint}/${formattedSlug}`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -775,8 +779,25 @@ async function fetchLicense(slug) { async function writeLicense() { try { if (options.themeLicense === 'UNLICENSED') { + // If `UNLICENSED` write the file with only `UNLICENSED` as its content. fs.writeFileSync(tmpThemeLicPath, 'UNLICENSED', { encoding: 'utf8' }); } else { + // Let the user know we are fetching the license asynchronously, in case + // the API takes a while to respond. + logInfo({ + title: 'Fetching license', + description: `Fetching license for SPDX ID: "${options.themeLicense}"`, + emoji: '⏳', + verbose: options.verbose, + dataLabel: 'Request information', + data: { + spdx: options.themeLicense, + provider: 'GitHub', + endpoint: licenseAPIEndpoint, + documentation: licenseAPIDocsURL, + }, + }); + // Fetch the license from GitHub. const license = await fetchLicense(options.themeLicense); logInfo({ title: 'License fetched', @@ -791,8 +812,10 @@ async function writeLicense() { api: license.url, }, }); + // Write the license content. fs.writeFileSync(tmpThemeLicPath, license.body, { encoding: 'utf8' }); } + // Log license generation success. logInfo({ title: 'License written', description: path.relative(tmpThemePath, tmpThemeLicPath), From 72fb17c9f2c628216323d67008e9fcf5f8ac7955 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 12:38:06 -0400 Subject: [PATCH 10/13] Fixes option flag in test workflow. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ba4c11..4b57304 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,4 +42,4 @@ jobs: # the path to `tests` and enable the `verbose` option for debugging. - name: Create Theme id: create_theme - run: npm test test-theme -- -v -p "tests" -N "Test Theme" -X "1.0.0" -T "parent-theme" -U "https://github.com/dreamsicle-io/test" -B "https://github.com/dreamsicle-io/test/issues" -R "git@github.com:dreamsicle-io/test.git" -r "git" -d "Just another WordPress theme." -A "Dreamsicle" -E "hello@dreamsicle.io" -u "https://www.dreamsicle.io" -L "gpl-3.0" --t "accessibility-ready,translation-ready" -W "6.1.0" -w "6.4.0" -F "test" -C "Test" -c "TEST" + run: npm test test-theme -- -v -p "tests" -N "Test Theme" -X "1.0.0" -T "parent-theme" -U "https://github.com/dreamsicle-io/test" -B "https://github.com/dreamsicle-io/test/issues" -R "git@github.com:dreamsicle-io/test.git" -r "git" -d "Just another WordPress theme." -A "Dreamsicle" -E "hello@dreamsicle.io" -u "https://www.dreamsicle.io" -L "gpl-3.0" -t "accessibility-ready,translation-ready" -W "6.1.0" -w "6.4.0" -F "test" -C "Test" -c "TEST" From 6f6be6ca26bedaf221102ef227b3237707a64611 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 13:43:06 -0400 Subject: [PATCH 11/13] Moves license provider string to a const. --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 92c87ba..921da3b 100644 --- a/index.js +++ b/index.js @@ -111,6 +111,7 @@ const gitURL = 'https://github.com/dreamsicle-io/wp-theme-assets.git'; const gitBranch = 'master'; // Construct license settings. +const licenseProvider = 'GitHub'; const licenseAPIEndpoint = 'https://api.github.com/licenses'; const licenseAPIDocsURL = 'https://docs.github.com/en/rest/licenses/licenses'; @@ -792,7 +793,7 @@ async function writeLicense() { dataLabel: 'Request information', data: { spdx: options.themeLicense, - provider: 'GitHub', + provider: licenseProvider, endpoint: licenseAPIEndpoint, documentation: licenseAPIDocsURL, }, From 0353d46d7014dbd35d6a7f0df0a493f208aaee98 Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 14:00:48 -0400 Subject: [PATCH 12/13] Updates commander to 8.3.0 in order to take advantage of getOptionValueSource() method. --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c5f397..a3e2d10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "change-case": "^5.4.4", "co": "~4.6.0", "co-prompt": "~1.0.0", - "commander": "~8.2.0", + "commander": "~8.3.0", "semver": "~7.6.2", "zod": "~3.23.8" }, @@ -293,9 +293,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/commander": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.2.0.tgz", - "integrity": "sha512-LLKxDvHeL91/8MIyTAD5BFMNtoIwztGPMiM/7Bl8rIPmHCZXRxmSWr91h57dpOpnQ6jIUqEWdXE/uBYMfiVZDA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "engines": { "node": ">= 12" } diff --git a/package.json b/package.json index 2f9aae4..e8e2f71 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "change-case": "^5.4.4", "co": "~4.6.0", "co-prompt": "~1.0.0", - "commander": "~8.2.0", + "commander": "~8.3.0", "semver": "~7.6.2", "zod": "~3.23.8" }, From edc5ae91f2e85acba84fd4dee7c1d98852748e3d Mon Sep 17 00:00:00 2001 From: Noah Mason Date: Wed, 5 Jun 2024 14:01:52 -0400 Subject: [PATCH 13/13] Adds more accurate checking of whether the user should get a prompt by determining if the option value was provided by them or not. --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 921da3b..956bb95 100644 --- a/index.js +++ b/index.js @@ -577,10 +577,13 @@ async function runPrompt() { // Note this cannot be a `forEach()` loop, because `yield` can only be // used inside of a `for` loop. for (const optionDef of optionDefs) { + // Determine if the option has already been set by the user. + // Possible options value sources: `default`, `env`, `config`, `cli`. + const isUserProvided = ['env', 'cli'].includes(program.getOptionValueSource(optionDef.key)); // If the option matches its default, we can safely assume it was // not passed from the CLI, and we should prompt for it. Also check // if the option definition marks the option as `isPrompted`. - if (optionDef.isPrompted && (options[optionDef.key] === optionDef.default)) { + if (optionDef.isPrompted && ! isUserProvided) { const promptMessage = `${chalk.bold.cyanBright(optionDef.title + ':')} ${chalk.dim('(' + options[optionDef.key] + ')')} `; /** * If there is a prompt value for this option, set it. If not,