Skip to content
Merged
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 changes: 2 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ jobs:
node-version: 22.x
cache: 'npm'
- run: npm install
- run: npm run lint
- run: npm run type-check
- run: npm run test:ci
22 changes: 14 additions & 8 deletions src/cli/parseCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,16 @@ export function parseCliArgs(
}

// Collect the headers passed via cli
const headers = {};
for (const header of args.headers) {
const [key, value] = header.split(":") as Record<PotHeaders, string>;
headers[key.trim()] = value.trim();
const headers: Record<string, string> = {};
if (args.headers && Array.isArray(args.headers)) {
for (const header of args.headers) {
if (typeof header === "string") {
const [key, value] = header.split(":") as [PotHeaders, string];
if (key && value) {
headers[key.trim()] = value.trim();
}
}
}
}

const parsedArgs: Args = {
Expand Down Expand Up @@ -169,11 +175,11 @@ export function parseJsonArgs(
const currentWorkingDirectory = process.cwd();
const slug = path.basename(path.resolve(currentWorkingDirectory));

let scriptName: string;
let scriptName: string | undefined;
if (args.scriptName) {
scriptName = args.scriptName.split(",").map((s) => s.trim());
if (scriptName.length === 1) {
scriptName = scriptName[0];
const scripts = args.scriptName.toString().split(",").map((s) => s.trim());
if (scripts.length === 1) {
scriptName = scripts[0];
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/extractors/auditStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function audit(args: Args, translationsUnion: SetOfBlocks) {
//if there are no errors, we can remove the audit.log file
try {
unlinkSync(path.join(args.paths.cwd, "audit.log"));
} catch (error) {
} catch (_error) {
//ignore
}
} else {
Expand Down
112 changes: 59 additions & 53 deletions src/extractors/headers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import path from "node:path";
import { SetOfBlocks } from "gettext-merger";
import { boolean } from "yargs";
import type PackageI18n from "../assets/package-i18n.js";
import { modulePath } from "../const.js";
import { getEncodingCharset } from "../fs/fs.js";
import type { Args, I18nHeaders, PotHeaders } from "../types.js";
import { getPkgJsonData } from "../utils/common.js";
import { buildBlock } from "../utils/extractors.js";
import { extractCssThemeData } from "./css.js";
import { extractPhpPluginData } from "./php.js";
import path from 'node:path'
import { SetOfBlocks } from 'gettext-merger'
import { modulePath } from '../const.js'
import { getEncodingCharset } from '../fs/fs.js'
import type { Args, AuthorData, I18nHeaders, PotHeaders } from '../types.js'
import { getPkgJsonData } from '../utils/common.js'
import { buildBlock } from '../utils/extractors.js'
import { extractCssThemeData } from './css.js'
import { extractPhpPluginData } from './php.js'

/**
* Checks if required fields are missing and logs a clear error message
Expand All @@ -20,14 +18,20 @@ function validateRequiredFields(
headerData: I18nHeaders,
debug: boolean,
): boolean {
const requiredFields = [
{ key: "slug", name: "Plugin/Theme slug", placeholder: "PLUGIN NAME" },
{ key: "author", name: "Author name", placeholder: "AUTHOR" },
{ key: "version", name: "Version", placeholder: "" },
{ key: "email", name: "Author email", placeholder: "AUTHOR EMAIL" },
{ key: "xDomain", name: "Text domain", placeholder: "PLUGIN TEXTDOMAIN" },
];

// Define the required fields with strict key types
const requiredFields: {
key: keyof I18nHeaders;
name: string;
placeholder: string;
}[] = [
{ key: "slug", name: "Plugin/Theme slug", placeholder: "PLUGIN NAME" },
{ key: "author", name: "Author name", placeholder: "AUTHOR" },
{ key: "version", name: "Version", placeholder: "" },
{ key: "email", name: "Author email", placeholder: "AUTHOR EMAIL" },
{ key: "xDomain", name: "Text domain", placeholder: "PLUGIN TEXTDOMAIN" },
];

// Filter out the missing or default fields
const missingFields = requiredFields.filter(
(field) =>
!headerData[field.key] ||
Expand Down Expand Up @@ -83,11 +87,9 @@ function validateRequiredFields(
* @returns an object with name, email, and website
* @param authorData
*/
function extractAuthorData(authorData: string | object): {
name: string;
email?: string;
website?: string;
} {
function extractAuthorData(
authorData: string | AuthorData,
): AuthorData | undefined {
// Default result with placeholder values
const defaultResult = { name: "AUTHOR", email: "AUTHOR EMAIL" };

Expand Down Expand Up @@ -129,41 +131,46 @@ function extractAuthorData(authorData: string | object): {
* @param pkgJsonData The package.json data object
* @returns Author data with name, email and website
*/
export function getAuthorFromPackage(pkgJsonData: PackageI18n): {
name: string;
email?: string;
website?: string;
} {
export function getAuthorFromPackage(
pkgJsonData: Record<string, unknown>,
): AuthorData {
// Check multiple possible locations for author information
const locations = [
const fields = [
"author", // Standard author field
"authors", // Some packages use authors (plural)
"contributors", // Try contributors if no author
"maintainers", // Try maintainers as last resort
];
"maintainers", // Try maintainers as a last resort
] as string[];

// Try each location in order
for (const location of locations) {
if (pkgJsonData[location]) {
let authorData: {
name: string;
email?: string;
website?: string;
};
if (typeof pkgJsonData[location] === "string") {
authorData = extractAuthorData(pkgJsonData[location]);
} else if (typeof pkgJsonData[location] === "object") {
for (const author of pkgJsonData[location]) {
for (const field of fields) {
const value = pkgJsonData[field];
if (value) {
let authorData: AuthorData | undefined;

if (typeof value === "string") {
authorData = extractAuthorData(value);
} else if (Array.isArray(value)) {
for (const author of value) {
if (!author) continue;
authorData = extractAuthorData(author);
if (authorData) break;
if (
typeof author === "string" ||
(typeof author === "object")
) {
authorData = extractAuthorData(author as string | AuthorData);
if (authorData) break;
}
}
} else if (typeof value === "object") {
// Handle single object author field
authorData = extractAuthorData(value as AuthorData);
}

if (
authorData?.name !== "AUTHOR" ||
authorData?.email !== "AUTHOR EMAIL"
) {
return authorData;
return authorData as AuthorData; // Returns the valid author data found
}
}
}
Expand All @@ -187,9 +194,9 @@ function consolidateUserHeaderData(args: Args): I18nHeaders {
"authors",
"contributors",
"maintainers",
) as Record<[keyof PackageI18n], string>;
);
// get author data from package.json
const pkgAuthor = getAuthorFromPackage(pkgJsonData);
const pkgAuthor = getAuthorFromPackage(pkgJsonData as unknown as Record<string, unknown>);

// get the current directory name as slug
const currentDir = path
Expand All @@ -212,16 +219,16 @@ function consolidateUserHeaderData(args: Args): I18nHeaders {

return {
...args.headers,
name: args.headers?.name || slug,
name: args.headers?.name?.toString() || slug,
author: authorName,
authorString: authorString, // this is the author with email address in this format: author <email>
authorString: authorString, // this is the author + email address in this format: author <email>
slug,
email,
bugs,
license: args.headers?.license || "gpl-2.0 or later",
version: args.headers?.version || pkgJsonData.version || "0.0.1",
version: args.headers?.version || (pkgJsonData.version as string) || "0.0.1",
language: "en",
xDomain: args.headers?.textDomain || slug,
xDomain: args.headers?.textDomain?.toString() || slug,
};
}

Expand Down Expand Up @@ -252,7 +259,6 @@ export async function generateHeader(
// Validate required fields - exit early if validation fails
if (!validateRequiredFields(headerData, args.debug)) {
process.exit(1); // Exit with error code
return null; // This is never reached but helps with TypeScript
}

return {
Expand Down
2 changes: 1 addition & 1 deletion src/extractors/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { JsonSchemaExtractor } from "./schema.js";
export async function parseJsonFile(opts: {
fileContent: string;
filename: "block.json" | "theme.json";
}): Promise<Block[]> {
}): Promise<Block[] | undefined> {
const isTheme = opts.filename === "theme.json";
const schema: { url: string; fallback: I18nSchema } = {
url: isTheme
Expand Down
Loading