Skip to content

Commit 3279172

Browse files
authored
Merge pull request #80 from wp-blocks/header-parser
Header parser
2 parents 3d39de5 + 8ac9a11 commit 3279172

18 files changed

Lines changed: 250 additions & 138 deletions

File tree

.github/workflows/node.js.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ jobs:
2525
node-version: 22.x
2626
cache: 'npm'
2727
- run: npm install
28+
- run: npm run lint
29+
- run: npm run type-check
2830
- run: npm run test:ci

src/cli/parseCli.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,16 @@ export function parseCliArgs(
102102
}
103103

104104
// Collect the headers passed via cli
105-
const headers = {};
106-
for (const header of args.headers) {
107-
const [key, value] = header.split(":") as Record<PotHeaders, string>;
108-
headers[key.trim()] = value.trim();
105+
const headers: Record<string, string> = {};
106+
if (args.headers && Array.isArray(args.headers)) {
107+
for (const header of args.headers) {
108+
if (typeof header === "string") {
109+
const [key, value] = header.split(":") as [PotHeaders, string];
110+
if (key && value) {
111+
headers[key.trim()] = value.trim();
112+
}
113+
}
114+
}
109115
}
110116

111117
const parsedArgs: Args = {
@@ -169,11 +175,11 @@ export function parseJsonArgs(
169175
const currentWorkingDirectory = process.cwd();
170176
const slug = path.basename(path.resolve(currentWorkingDirectory));
171177

172-
let scriptName: string;
178+
let scriptName: string | undefined;
173179
if (args.scriptName) {
174-
scriptName = args.scriptName.split(",").map((s) => s.trim());
175-
if (scriptName.length === 1) {
176-
scriptName = scriptName[0];
180+
const scripts = args.scriptName.toString().split(",").map((s) => s.trim());
181+
if (scripts.length === 1) {
182+
scriptName = scripts[0];
177183
}
178184
}
179185

src/extractors/auditStrings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function audit(args: Args, translationsUnion: SetOfBlocks) {
1515
//if there are no errors, we can remove the audit.log file
1616
try {
1717
unlinkSync(path.join(args.paths.cwd, "audit.log"));
18-
} catch (error) {
18+
} catch (_error) {
1919
//ignore
2020
}
2121
} else {

src/extractors/headers.ts

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import path from "node:path";
2-
import { SetOfBlocks } from "gettext-merger";
3-
import { boolean } from "yargs";
4-
import type PackageI18n from "../assets/package-i18n.js";
5-
import { modulePath } from "../const.js";
6-
import { getEncodingCharset } from "../fs/fs.js";
7-
import type { Args, I18nHeaders, PotHeaders } from "../types.js";
8-
import { getPkgJsonData } from "../utils/common.js";
9-
import { buildBlock } from "../utils/extractors.js";
10-
import { extractCssThemeData } from "./css.js";
11-
import { extractPhpPluginData } from "./php.js";
1+
import path from 'node:path'
2+
import { SetOfBlocks } from 'gettext-merger'
3+
import { modulePath } from '../const.js'
4+
import { getEncodingCharset } from '../fs/fs.js'
5+
import type { Args, AuthorData, I18nHeaders, PotHeaders } from '../types.js'
6+
import { getPkgJsonData } from '../utils/common.js'
7+
import { buildBlock } from '../utils/extractors.js'
8+
import { extractCssThemeData } from './css.js'
9+
import { extractPhpPluginData } from './php.js'
1210

1311
/**
1412
* Checks if required fields are missing and logs a clear error message
@@ -20,14 +18,20 @@ function validateRequiredFields(
2018
headerData: I18nHeaders,
2119
debug: boolean,
2220
): boolean {
23-
const requiredFields = [
24-
{ key: "slug", name: "Plugin/Theme slug", placeholder: "PLUGIN NAME" },
25-
{ key: "author", name: "Author name", placeholder: "AUTHOR" },
26-
{ key: "version", name: "Version", placeholder: "" },
27-
{ key: "email", name: "Author email", placeholder: "AUTHOR EMAIL" },
28-
{ key: "xDomain", name: "Text domain", placeholder: "PLUGIN TEXTDOMAIN" },
29-
];
30-
21+
// Define the required fields with strict key types
22+
const requiredFields: {
23+
key: keyof I18nHeaders;
24+
name: string;
25+
placeholder: string;
26+
}[] = [
27+
{ key: "slug", name: "Plugin/Theme slug", placeholder: "PLUGIN NAME" },
28+
{ key: "author", name: "Author name", placeholder: "AUTHOR" },
29+
{ key: "version", name: "Version", placeholder: "" },
30+
{ key: "email", name: "Author email", placeholder: "AUTHOR EMAIL" },
31+
{ key: "xDomain", name: "Text domain", placeholder: "PLUGIN TEXTDOMAIN" },
32+
];
33+
34+
// Filter out the missing or default fields
3135
const missingFields = requiredFields.filter(
3236
(field) =>
3337
!headerData[field.key] ||
@@ -83,11 +87,9 @@ function validateRequiredFields(
8387
* @returns an object with name, email, and website
8488
* @param authorData
8589
*/
86-
function extractAuthorData(authorData: string | object): {
87-
name: string;
88-
email?: string;
89-
website?: string;
90-
} {
90+
function extractAuthorData(
91+
authorData: string | AuthorData,
92+
): AuthorData | undefined {
9193
// Default result with placeholder values
9294
const defaultResult = { name: "AUTHOR", email: "AUTHOR EMAIL" };
9395

@@ -129,41 +131,46 @@ function extractAuthorData(authorData: string | object): {
129131
* @param pkgJsonData The package.json data object
130132
* @returns Author data with name, email and website
131133
*/
132-
export function getAuthorFromPackage(pkgJsonData: PackageI18n): {
133-
name: string;
134-
email?: string;
135-
website?: string;
136-
} {
134+
export function getAuthorFromPackage(
135+
pkgJsonData: Record<string, unknown>,
136+
): AuthorData {
137137
// Check multiple possible locations for author information
138-
const locations = [
138+
const fields = [
139139
"author", // Standard author field
140140
"authors", // Some packages use authors (plural)
141141
"contributors", // Try contributors if no author
142-
"maintainers", // Try maintainers as last resort
143-
];
142+
"maintainers", // Try maintainers as a last resort
143+
] as string[];
144144

145145
// Try each location in order
146-
for (const location of locations) {
147-
if (pkgJsonData[location]) {
148-
let authorData: {
149-
name: string;
150-
email?: string;
151-
website?: string;
152-
};
153-
if (typeof pkgJsonData[location] === "string") {
154-
authorData = extractAuthorData(pkgJsonData[location]);
155-
} else if (typeof pkgJsonData[location] === "object") {
156-
for (const author of pkgJsonData[location]) {
146+
for (const field of fields) {
147+
const value = pkgJsonData[field];
148+
if (value) {
149+
let authorData: AuthorData | undefined;
150+
151+
if (typeof value === "string") {
152+
authorData = extractAuthorData(value);
153+
} else if (Array.isArray(value)) {
154+
for (const author of value) {
157155
if (!author) continue;
158-
authorData = extractAuthorData(author);
159-
if (authorData) break;
156+
if (
157+
typeof author === "string" ||
158+
(typeof author === "object")
159+
) {
160+
authorData = extractAuthorData(author as string | AuthorData);
161+
if (authorData) break;
162+
}
160163
}
164+
} else if (typeof value === "object") {
165+
// Handle single object author field
166+
authorData = extractAuthorData(value as AuthorData);
161167
}
168+
162169
if (
163170
authorData?.name !== "AUTHOR" ||
164171
authorData?.email !== "AUTHOR EMAIL"
165172
) {
166-
return authorData;
173+
return authorData as AuthorData; // Returns the valid author data found
167174
}
168175
}
169176
}
@@ -187,9 +194,9 @@ function consolidateUserHeaderData(args: Args): I18nHeaders {
187194
"authors",
188195
"contributors",
189196
"maintainers",
190-
) as Record<[keyof PackageI18n], string>;
197+
);
191198
// get author data from package.json
192-
const pkgAuthor = getAuthorFromPackage(pkgJsonData);
199+
const pkgAuthor = getAuthorFromPackage(pkgJsonData as unknown as Record<string, unknown>);
193200

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

213220
return {
214221
...args.headers,
215-
name: args.headers?.name || slug,
222+
name: args.headers?.name?.toString() || slug,
216223
author: authorName,
217-
authorString: authorString, // this is the author with email address in this format: author <email>
224+
authorString: authorString, // this is the author + email address in this format: author <email>
218225
slug,
219226
email,
220227
bugs,
221228
license: args.headers?.license || "gpl-2.0 or later",
222-
version: args.headers?.version || pkgJsonData.version || "0.0.1",
229+
version: args.headers?.version || (pkgJsonData.version as string) || "0.0.1",
223230
language: "en",
224-
xDomain: args.headers?.textDomain || slug,
231+
xDomain: args.headers?.textDomain?.toString() || slug,
225232
};
226233
}
227234

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

258264
return {

src/extractors/json.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { JsonSchemaExtractor } from "./schema.js";
1616
export async function parseJsonFile(opts: {
1717
fileContent: string;
1818
filename: "block.json" | "theme.json";
19-
}): Promise<Block[]> {
19+
}): Promise<Block[] | undefined> {
2020
const isTheme = opts.filename === "theme.json";
2121
const schema: { url: string; fallback: I18nSchema } = {
2222
url: isTheme

0 commit comments

Comments
 (0)