From e35eb23ef5b84542b9c4ce1ac9bceb64ee56ee84 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Sun, 8 Dec 2024 21:12:56 -0800 Subject: [PATCH] Support setting NACP metadata properties via the "nacp" object in `package.json` --- .changeset/ten-bears-explain.md | 7 ++++ packages/nro/package.json | 4 +- packages/nro/src/main.ts | 22 +++++++---- packages/nsp/package.json | 2 +- packages/nsp/src/main.ts | 17 +++++--- packages/patch-nacp/package.json | 7 ++-- packages/patch-nacp/src/index.ts | 68 ++++++++++++++++++++++++++++---- pnpm-lock.yaml | 36 ++++++++++------- 8 files changed, 122 insertions(+), 41 deletions(-) create mode 100644 .changeset/ten-bears-explain.md diff --git a/.changeset/ten-bears-explain.md b/.changeset/ten-bears-explain.md new file mode 100644 index 00000000..494fd7b4 --- /dev/null +++ b/.changeset/ten-bears-explain.md @@ -0,0 +1,7 @@ +--- +"@nx.js/patch-nacp": major +"@nx.js/nro": patch +"@nx.js/nsp": patch +--- + +Support setting NACP metadata properties via the "nacp" object in `package.json` diff --git a/packages/nro/package.json b/packages/nro/package.json index cfc5fe37..cf079047 100644 --- a/packages/nro/package.json +++ b/packages/nro/package.json @@ -25,8 +25,8 @@ "license": "MIT", "dependencies": { "@nx.js/patch-nacp": "0.1.0", - "@tootallnate/nacp": "^0.1.0", - "@tootallnate/nro": "^0.1.1", + "@tootallnate/nacp": "^0.2.0", + "@tootallnate/nro": "^0.1.2", "@tootallnate/romfs": "^0.1.0", "bytes": "^3.1.2", "chalk": "^5.3.0", diff --git a/packages/nro/src/main.ts b/packages/nro/src/main.ts index 6b1639d5..5a191623 100755 --- a/packages/nro/src/main.ts +++ b/packages/nro/src/main.ts @@ -52,21 +52,27 @@ console.log(` JPEG size: ${bytes(logoBuf.length).toLowerCase()}`); console.log(await terminalImage.buffer(logoBuf, { height: 18 })); // NACP -const nacp = new NACP(await nxjsNro.nacp!.arrayBuffer()); -const packageJson = patchNACP(nacp, new URL('package.json', appRoot)); +const originalNacp = new NACP(await nxjsNro.nacp!.arrayBuffer()); +const nacp = new NACP(originalNacp.buffer.slice(0)); +const { packageJson, updated, warnings } = patchNACP( + nacp, + new URL('package.json', appRoot), +); console.log(); -console.log(chalk.bold('Setting metadata:')); -console.log(` ID: ${chalk.green(nacp.id.toString(16).padStart(16, '0'))}`); -console.log(` Title: ${chalk.green(nacp.title)}`); -console.log(` Version: ${chalk.green(nacp.version)}`); -console.log(` Author: ${chalk.green(nacp.author)}`); +console.log(chalk.bold('NACP Metadata:')); +for (const warning of warnings) { + console.log(chalk.yellow(`⚠️ ${warning}`)); +} +for (const [k, v] of updated) { + console.log(` ${chalk.green(k)}: ${v}`); +} // RomFS const romfsDir = new URL('romfs/', appRoot); const romfsDirPath = fileURLToPath(romfsDir); const romfs = await RomFS.decode(nxjsNro.romfs!); console.log(); -console.log(chalk.bold('Adding RomFS files:')); +console.log(chalk.bold('RomFS Files:')); function walk(dir: URL, dirEntry: RomFS.RomFsEntry) { for (const name of readdirSync(dir)) { diff --git a/packages/nsp/package.json b/packages/nsp/package.json index a7e098e6..2785342d 100644 --- a/packages/nsp/package.json +++ b/packages/nsp/package.json @@ -31,7 +31,7 @@ "license": "MIT", "dependencies": { "@nx.js/patch-nacp": "0.1.0", - "@tootallnate/nacp": "^0.1.0", + "@tootallnate/nacp": "^0.2.0", "bytes": "^3.1.2", "chalk": "^5.3.0", "jimp": "^1.6.0", diff --git a/packages/nsp/src/main.ts b/packages/nsp/src/main.ts index fb8cff6f..b52a4ad1 100644 --- a/packages/nsp/src/main.ts +++ b/packages/nsp/src/main.ts @@ -100,15 +100,20 @@ try { throw err; } - const packageJson = patchNACP(nacp, new URL('package.json', appRoot)); + const { packageJson, updated, warnings } = patchNACP( + nacp, + new URL('package.json', appRoot), + ); writeFileSync(new URL('control.nacp', controlDir), Buffer.from(nacp.buffer)); const titleid = nacp.id.toString(16).padStart(16, '0'); console.log(); - console.log(chalk.bold('Setting metadata:')); - console.log(` ID: ${chalk.green(titleid)}`); - console.log(` Title: ${chalk.green(nacp.title)}`); - console.log(` Version: ${chalk.green(nacp.version)}`); - console.log(` Author: ${chalk.green(nacp.author)}`); + console.log(chalk.bold('NACP Metadata:')); + for (const warning of warnings) { + console.log(chalk.yellow(`⚠️ ${warning}`)); + } + for (const [k, v] of updated) { + console.log(` ${chalk.green(k)}: ${v}`); + } // RomFS for (const file of readdirSync(baseRomfsDir)) { diff --git a/packages/patch-nacp/package.json b/packages/patch-nacp/package.json index 629ea59c..0fb17366 100644 --- a/packages/patch-nacp/package.json +++ b/packages/patch-nacp/package.json @@ -16,12 +16,13 @@ "author": "Nathan Rajlich ", "license": "MIT", "dependencies": { + "@tootallnate/nacp": "^0.2.0", "parse-author": "^2.0.0", + "title-case": "^4.3.2", "types-package-json": "^2.0.39" }, "devDependencies": { - "@tootallnate/nacp": "^0.1.0", - "@types/parse-author": "^2.0.3", - "@types/node": "^20.10.3" + "@types/node": "^20.10.3", + "@types/parse-author": "^2.0.3" } } diff --git a/packages/patch-nacp/src/index.ts b/packages/patch-nacp/src/index.ts index eff2f89b..c00bafb9 100644 --- a/packages/patch-nacp/src/index.ts +++ b/packages/patch-nacp/src/index.ts @@ -1,14 +1,22 @@ import parseAuthor from 'parse-author'; import { readFileSync } from 'node:fs'; -import type { NACP } from '@tootallnate/nacp'; +import { NACP } from '@tootallnate/nacp'; +import { titleCase } from 'title-case'; import type { PackageJson as BasePackageJson } from 'types-package-json'; +export type PackageJsonNacp = Omit; + export interface PackageJson extends BasePackageJson { titleId?: string; productName?: string; + nacp?: PackageJsonNacp; } +const VALID_NACP_PROPERTIES = Object.getOwnPropertyNames(NACP.prototype); + export function patchNACP(nacp: NACP, packageJsonUrl: URL) { + const warnings: string[] = []; + const updated = new Map(); const packageJsonStr = readFileSync(packageJsonUrl, 'utf8'); const packageJson: PackageJson = JSON.parse(packageJsonStr); const { @@ -17,13 +25,59 @@ export function patchNACP(nacp: NACP, packageJsonUrl: URL) { productName, version, author: rawAuthor, + nacp: pkgNacp = {}, } = packageJson; + + if (titleId) { + warnings.push( + 'The "titleId" property is deprecated. Use "nacp.id" instead.', + ); + nacp.id = titleId; + updated.set('ID', titleId); + } + + const title = productName || name; + if (title) { + if (productName) { + warnings.push( + 'The "productName" property is deprecated. Use "nacp.title" instead.', + ); + } + updated.set('Title', title); + nacp.title = title; + } + + if (version) { + nacp.version = version; + updated.set('Version', version); + } + const author = typeof rawAuthor === 'string' ? parseAuthor(rawAuthor) : rawAuthor; - if (titleId) nacp.id = titleId; - const title = productName || name; - if (title) nacp.title = title; - if (version) nacp.version = version; - if (author?.name) nacp.author = author.name; - return packageJson; + if (author?.name) { + nacp.author = author.name; + updated.set('Author', author.name); + } + + for (const [k, v] of Object.entries(pkgNacp)) { + if (!VALID_NACP_PROPERTIES.includes(k)) { + warnings.push(`Ignoring invalid NACP property: ${JSON.stringify(k)}`); + continue; + } + + // @ts-expect-error + const oldValue = nacp[k]; + // @ts-expect-error + nacp[k] = v; + // @ts-expect-error + const newValue = nacp[k]; + + if (newValue !== oldValue) { + const titleCased = + k === 'id' ? 'ID' : titleCase(k.replace(/([A-Z])/g, ' $1')); + updated.set(titleCased, newValue); + } + } + + return { packageJson, updated, warnings }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12af1721..41bbab55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -586,11 +586,11 @@ importers: specifier: 0.1.0 version: link:../patch-nacp '@tootallnate/nacp': - specifier: ^0.1.0 - version: 0.1.0 + specifier: ^0.2.0 + version: 0.2.0 '@tootallnate/nro': - specifier: ^0.1.1 - version: 0.1.1 + specifier: ^0.1.2 + version: 0.1.2 '@tootallnate/romfs': specifier: ^0.1.0 version: 0.1.0 @@ -617,8 +617,8 @@ importers: specifier: 0.1.0 version: link:../patch-nacp '@tootallnate/nacp': - specifier: ^0.1.0 - version: 0.1.0 + specifier: ^0.2.0 + version: 0.2.0 bytes: specifier: ^3.1.2 version: 3.1.2 @@ -644,16 +644,19 @@ importers: packages/patch-nacp: dependencies: + '@tootallnate/nacp': + specifier: ^0.2.0 + version: 0.2.0 parse-author: specifier: ^2.0.0 version: 2.0.0 + title-case: + specifier: ^4.3.2 + version: 4.3.2 types-package-json: specifier: ^2.0.39 version: 2.0.39 devDependencies: - '@tootallnate/nacp': - specifier: ^0.1.0 - version: 0.1.0 '@types/node': specifier: ^20.10.3 version: 20.10.3 @@ -3787,13 +3790,14 @@ packages: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: false - /@tootallnate/nacp@0.1.0: - resolution: {integrity: sha512-rSiU1x7bDV0/fmApNfD6igmHM/Q3UdfwzxEQYcTiOY4ibpU86mJEWhkdsz4RXZMtZPRo6OlvBkBkLc2bEL8WpQ==} + /@tootallnate/nacp@0.2.0: + resolution: {integrity: sha512-DzETn6PBoezv3t6nIAQNTffSH6+NBdmlLYG7+eKzVGyuvH4KiZUr0/TqrH/Uv7jBO2Vl3xSaOMt6L61yLe0VNA==} + dev: false - /@tootallnate/nro@0.1.1: - resolution: {integrity: sha512-I1oQL9lK34EPectbuPFb7b7YTB8GYgFC7x9iBmRoTP5izJeJxuz8lZZZ2ssVtM3fXBNHHVhUpHLTJv7boQlq/Q==} + /@tootallnate/nro@0.1.2: + resolution: {integrity: sha512-ftaCXHpuScjtoTgA3C7Ne8ChfQo7TvSMcQaXA+y10Zh9qC8Abw6JKk7RCPC2YeVLg1n+1b4H887SB52EaZ4LvQ==} dependencies: - '@tootallnate/nacp': 0.1.0 + '@tootallnate/nacp': 0.2.0 dev: false /@tootallnate/romfs@0.1.0: @@ -8274,6 +8278,10 @@ packages: engines: {node: '>=14.0.0'} dev: true + /title-case@4.3.2: + resolution: {integrity: sha512-I/nkcBo73mO42Idfv08jhInV61IMb61OdIFxk+B4Gu1oBjWBPOLmhZdsli+oJCVaD+86pYQA93cJfFt224ZFAA==} + dev: false + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'}