diff --git a/package.json b/package.json index 4933813..165521f 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "type-check": "npx tsc --noEmit", "update-pkg": "npm upgrade -S", "rm": "rmdir /s /q lib", - "test:build": "npx esbuild ./src/**/* --format=cjs --sourcemap --outdir=lib --platform=node", + "test:build": "npx esbuild ./src/**/* ./src/*.ts --format=cjs --sourcemap --outdir=lib --platform=node", "test": "npm run test:build && node --test", "build:build-ci": "npx esbuild ./src/index.ts --format=cjs --outdir=lib --bundle --external:tree-sitter --external:tree-sitter-typescript --external:tree-sitter-php --external:tree-sitter-javascript --external:@babel/preset-typescript --platform=node", "test:ci": "npm run build:build-ci && npm run test", @@ -84,4 +84,4 @@ "esbuild": "0.25.9", "typescript": "^5.9.3" } -} +} \ No newline at end of file diff --git a/src/extractors/auditStrings.ts b/src/extractors/auditStrings.ts index 77d119d..1c32176 100644 --- a/src/extractors/auditStrings.ts +++ b/src/extractors/auditStrings.ts @@ -12,7 +12,7 @@ export function audit(args: Args, translationsUnion: SetOfBlocks) { console.log("\nAudit Complete!"); if (auditor.getResults().length === 0) { console.log("No issues found! 🎉"); - //if there are no errors, we can remove the audit.log file + // if there are no errors, we can remove the audit.log file try { unlinkSync(path.join(args.paths.cwd, "audit.log")); } catch (_error) { @@ -20,10 +20,13 @@ export function audit(args: Args, translationsUnion: SetOfBlocks) { } } else { console.log(`Found ${auditor.getResults().length} issues!`); - const results = auditor.getResults().join("\n"); - console.log(results); + // Print the results if not in silent mode + if (args.options?.silent !== true) { + const results = auditor.getResults().join("\n"); + console.log(results); + } /** Write the audit results to a file */ - writeFileSync(path.join(args.paths.cwd, "audit.log"), results); + writeFileSync(path.join(args.paths.cwd, "audit.log"), auditor.getResults().join("\n")); } } diff --git a/src/extractors/headers.ts b/src/extractors/headers.ts index 292be8a..b0ad037 100644 --- a/src/extractors/headers.ts +++ b/src/extractors/headers.ts @@ -1,4 +1,6 @@ import path from 'node:path' +import process from 'node:process' +import * as readline from 'node:readline' import { SetOfBlocks } from 'gettext-merger' import { modulePath } from '../const.js' import { getEncodingCharset } from '../fs/fs.js' @@ -12,11 +14,13 @@ import { extractPhpPluginData } from './php.js' * Checks if required fields are missing and logs a clear error message * @param {object} headerData - The header data to validate * @param {boolean} debug - Debug mode flag + * @param {boolean} silent - Silent mode flag * @returns {boolean} - true if all required fields are present, false otherwise */ function validateRequiredFields( headerData: I18nHeaders, debug: boolean, + silent = false, ): boolean { // Define the required fields with strict key types const requiredFields: { @@ -40,39 +44,41 @@ function validateRequiredFields( ); if (missingFields.length > 0) { - console.error("\n! Missing required information for POT file header:\n"); + if (!silent) { + console.error("\n! Missing required information for POT file header:\n"); - for (const field of missingFields) { - console.error( - ` - ${field.name} is missing or has a default value (eg. version: 0.0.1")`, - ); - } - - console.error( - "\nPlease provide this information adding the missing fields inside the headers object of the plugin/theme declaration or to the package.json file.", - "\nFor more information check the documentation at https://github.com/wp-blocks/makePot", - ); + for (const field of missingFields) { + console.error( + ` - ${field.name} is missing or has a default value (eg. version: 0.0.1")`, + ); + } - if (missingFields.some((field) => field.key === "email")) { console.error( - "\n\nWordpress didn't require an email field in the headers object but it's required in order to generate a valid pot file.", - '\nPlease add the email field to the package.json file (author field eg. author: "AUTHOR ")', - '\nor inject those information using the --headers flag to the "makePot" command (eg. --headers=email:erik@ck.it).', + "\nPlease provide this information adding the missing fields inside the headers object of the plugin/theme declaration or to the package.json file.", "\nFor more information check the documentation at https://github.com/wp-blocks/makePot", ); - } - if (missingFields && debug) { - console.error( - "\nDebug information:", - "\nMissing fields:", - missingFields, - "\nHeader data:", - headerData, - ); - } + if (missingFields.some((field) => field.key === "email")) { + console.error( + "\n\nWordpress didn't require an email field in the headers object but it's required in order to generate a valid pot file.", + '\nPlease add the email field to the package.json file (author field eg. author: "AUTHOR ")', + '\nor inject those information using the --headers flag to the "makePot" command (eg. --headers=email:erik@ck.it).', + "\nFor more information check the documentation at https://github.com/wp-blocks/makePot", + ); + } - console.error("\n"); + if (missingFields && debug) { + console.error( + "\nDebug information:", + "\nMissing fields:", + missingFields, + "\nHeader data:", + headerData, + ); + } + + console.error("\n"); + } return false; } @@ -153,10 +159,7 @@ export function getAuthorFromPackage( } else if (Array.isArray(value)) { for (const author of value) { if (!author) continue; - if ( - typeof author === "string" || - (typeof author === "object") - ) { + if (typeof author === "string" || typeof author === "object") { authorData = extractAuthorData(author as string | AuthorData); if (authorData) break; } @@ -196,7 +199,9 @@ function consolidateUserHeaderData(args: Args): I18nHeaders { "maintainers", ); // get author data from package.json - const pkgAuthor = getAuthorFromPackage(pkgJsonData as unknown as Record); + const pkgAuthor = getAuthorFromPackage( + pkgJsonData as unknown as Record, + ); // get the current directory name as slug const currentDir = path @@ -215,7 +220,8 @@ function consolidateUserHeaderData(args: Args): I18nHeaders { args.headers?.name?.toString().replace(/ /g, "-") || (args.domain === "theme" ? "THEME NAME" : "PLUGIN NAME"); - const bugs = `https://wordpress.org/support/${args.domain === "theme" ? "themes" : "plugins"}/${slug}`; + const bugs = `https://wordpress.org/support/${args.domain === "theme" ? "themes" : "plugins" + }/${slug}`; return { ...args.headers, @@ -226,7 +232,8 @@ function consolidateUserHeaderData(args: Args): I18nHeaders { email, bugs, license: args.headers?.license || "gpl-2.0 or later", - version: args.headers?.version || (pkgJsonData.version as string) || "0.0.1", + version: + args.headers?.version || (pkgJsonData.version as string) || "0.0.1", language: "en", xDomain: args.headers?.textDomain?.toString() || slug, }; @@ -257,8 +264,32 @@ export async function generateHeader( const { name, version } = getPkgJsonData(modulePath, "name", "version"); // Validate required fields - exit early if validation fails - if (!validateRequiredFields(headerData, args.debug)) { - process.exit(1); // Exit with error code + if (!validateRequiredFields(headerData, args.debug, args.options?.silent)) { + if (args.options?.silent) { + // In silent mode, we use defaults without asking + } else { + // Ask the user if default values should be used + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const answer = await new Promise((resolve) => { + rl.question( + "\nMissing required fields. Use default values? (y/N) ", + resolve, + ); + }); + rl.close(); + + if ( + typeof answer === "string" && + answer.toLowerCase() !== "y" && + answer.toLowerCase() !== "yes" + ) { + process.exit(1); // Exit with error code + } + } } return { diff --git a/src/parser/taskRunner.ts b/src/parser/taskRunner.ts index 545816c..d3ce770 100644 --- a/src/parser/taskRunner.ts +++ b/src/parser/taskRunner.ts @@ -30,19 +30,22 @@ export async function taskRunner( }) .then((consolidated) => { /** Log the results */ - if (args.options?.silent !== true) { - for (const result of consolidated) { - if (result.blocks.length > 0) { - /** - * Add the strings to the destination set - */ - destination.addArray(result.blocks); - const strings = result.blocks.map((b) => b.msgid); - /* Log the results */ + for (const result of consolidated) { + if (result.blocks.length > 0) { + /** + * Add the strings to the destination set + */ + destination.addArray(result.blocks); + const strings = result.blocks.map((b) => b.msgid); + + /* Log the results */ + if (args.options?.silent !== true) { messages.push( `✅ ${result.path} - ${strings.length} strings found [${strings.join(", ")}]`, ); - } else messages.push(`❌ ${result.path} - has no strings`); + } + } else if (args.options?.silent !== true) { + messages.push(`❌ ${result.path} - has no strings`); } } }) diff --git a/tests/generate-header.test.js b/tests/generate-header.test.js new file mode 100644 index 0000000..a1ec3b8 --- /dev/null +++ b/tests/generate-header.test.js @@ -0,0 +1,27 @@ +const { describe, it } = require("node:test"); +const assert = require("node:assert"); +const { generateHeader } = require("../lib/extractors/headers"); +const process = require("node:process"); + +describe("generateHeader", () => { + it("should return default headers when silent is true and fields are missing", async () => { + const args = { + slug: "test-slug", + debug: false, + domain: "plugin", + paths: { cwd: process.cwd(), out: "languages" }, + options: { silent: true }, + headers: { + version: "0.0.1", + author: "AUTHOR", + email: "AUTHOR EMAIL" + }, + }; + + const headers = await generateHeader(args); + + assert.ok(headers, "Headers should be generated"); + assert.strictEqual(headers["Project-Id-Version"], "test-slug 0.0.1"); + assert.strictEqual(headers["Last-Translator"], "AUTHOR "); + }); +});