From 7bd315298b7e054ab06fbe2c7db091681d98de5f Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Fri, 27 Feb 2026 09:03:47 -0800 Subject: [PATCH] fix(widget): handle configs with other SPMPackages --- lib/commands/widget.ts | 6 +- .../config-manipulation/config-transformer.ts | 78 ++++++++++++++++++- .../config-manipulation/config-transformer.ts | 62 +++++++++++++++ 3 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 test/tools/config-manipulation/config-transformer.ts diff --git a/lib/commands/widget.ts b/lib/commands/widget.ts index d448e142c5..5a12e69fe6 100644 --- a/lib/commands/widget.ts +++ b/lib/commands/widget.ts @@ -8,7 +8,6 @@ import * as plist from "plist"; import { injector } from "../common/yok"; import { capitalizeFirstLetter } from "../common/utils"; import { EOL } from "os"; -import { SupportedConfigValues } from "../tools/config-manipulation/config-transformer"; export class WidgetCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; @@ -219,10 +218,7 @@ public struct ${capitalizeFirstLetter(name)}Model: ActivityAttributes { } configData.ios.SPMPackages = spmPackages; - await this.$projectConfigService.setValue( - "", // root - configData as { [key: string]: SupportedConfigValues }, - ); + await this.$projectConfigService.setValue("ios.SPMPackages", spmPackages); if (fs.existsSync(gitIgnorePath)) { const gitIgnore = fs.readFileSync(gitIgnorePath, { diff --git a/lib/tools/config-manipulation/config-transformer.ts b/lib/tools/config-manipulation/config-transformer.ts index 6430ef7b0e..50f926a530 100644 --- a/lib/tools/config-manipulation/config-transformer.ts +++ b/lib/tools/config-manipulation/config-transformer.ts @@ -184,26 +184,56 @@ export class ConfigTransformer implements IConfigTransformer { return `{}`; } + private isBooleanLiteralNode(initializer: any): boolean { + return ( + initializer?.getKind() === SyntaxKind.TrueKeyword || + initializer?.getKind() === SyntaxKind.FalseKeyword + ); + } + + private replaceInitializer( + initializer: any, + newValue: SupportedConfigValues, + ) { + return initializer.replaceWithText(this.createInitializer(newValue)); + } + private setInitializerValue( initializer: any, newValue: SupportedConfigValues, ) { if (Node.isStringLiteral(initializer)) { + if (typeof newValue !== "string") { + return this.replaceInitializer(initializer, newValue); + } return (initializer as StringLiteral).setLiteralValue(newValue as string); } if (Node.isNumericLiteral(initializer)) { + if (typeof newValue !== "number") { + return this.replaceInitializer(initializer, newValue); + } return (initializer as NumericLiteral).setLiteralValue( newValue as number, ); } - if (Node.isBooleanKeyword(initializer)) { + if (this.isBooleanLiteralNode(initializer)) { + if (typeof newValue !== "boolean") { + return this.replaceInitializer(initializer, newValue); + } return (initializer as BooleanLiteral).setLiteralValue( newValue as boolean, ); } + if ( + Node.isArrayLiteralExpression(initializer) || + Node.isObjectLiteralExpression(initializer) + ) { + return this.replaceInitializer(initializer, newValue); + } + if (Node.isIdentifier(initializer)) { return this.setIdentifierValue(initializer as Identifier, newValue); } @@ -211,7 +241,7 @@ export class ConfigTransformer implements IConfigTransformer { throw new Error("Unsupported value type: " + initializer.getKindName()); } - private getInitializerValue(initializer: any) { + private getInitializerValue(initializer: any): any { if (Node.isStringLiteral(initializer)) { return (initializer as StringLiteral).getLiteralValue(); } @@ -220,10 +250,30 @@ export class ConfigTransformer implements IConfigTransformer { return (initializer as NumericLiteral).getLiteralValue(); } - if (Node.isBooleanKeyword(initializer)) { + if (this.isBooleanLiteralNode(initializer)) { return (initializer as BooleanLiteral).getLiteralValue(); } + if (Node.isArrayLiteralExpression(initializer)) { + return initializer + .getElements() + .map((element: any) => this.getInitializerValue(element)); + } + + if (Node.isObjectLiteralExpression(initializer)) { + const result: Record = {}; + for (const property of initializer.getProperties()) { + if (!Node.isPropertyAssignment(property)) { + continue; + } + const name = property.getNameNode().getText().replace(/['\"]/g, ""); + result[name] = this.getInitializerValue( + property.getInitializerOrThrow(), + ); + } + return result; + } + if (Node.isIdentifier(initializer)) { return this.getIdentifierValue(initializer as Identifier); } @@ -278,10 +328,30 @@ export class ConfigTransformer implements IConfigTransformer { return (initializer as NumericLiteral).getLiteralValue(); } - if (Node.isBooleanKeyword(initializer)) { + if (this.isBooleanLiteralNode(initializer)) { return (initializer as BooleanLiteral).getLiteralValue(); } + if (Node.isArrayLiteralExpression(initializer)) { + return initializer + .getElements() + .map((element: any) => this.getInitializerValue(element)); + } + + if (Node.isObjectLiteralExpression(initializer)) { + const result: Record = {}; + for (const property of initializer.getProperties()) { + if (!Node.isPropertyAssignment(property)) { + continue; + } + const name = property.getNameNode().getText().replace(/['\"]/g, ""); + result[name] = this.getInitializerValue( + property.getInitializerOrThrow(), + ); + } + return result; + } + if (Node.isIdentifier(initializer)) { return this.getIdentifierValue(initializer as Identifier); } diff --git a/test/tools/config-manipulation/config-transformer.ts b/test/tools/config-manipulation/config-transformer.ts new file mode 100644 index 0000000000..3ecc6bd5d0 --- /dev/null +++ b/test/tools/config-manipulation/config-transformer.ts @@ -0,0 +1,62 @@ +import { assert } from "chai"; +import { ConfigTransformer } from "../../../lib/tools/config-manipulation/config-transformer"; + +describe("ConfigTransformer", () => { + it("updates existing boolean literals", () => { + const content = `export default { + id: 'org.nativescript.myapp', + discardUncaughtJsExceptions: true, +} as any;`; + + const transformer = new ConfigTransformer(content); + const updated = transformer.setValue("discardUncaughtJsExceptions", false); + const updatedTransformer = new ConfigTransformer(updated); + + assert.strictEqual( + updatedTransformer.getValue("discardUncaughtJsExceptions"), + false, + ); + }); + + it("updates existing ios.SPMPackages array literals", () => { + const content = `import { NativeScriptConfig } from '@nativescript/core' + +export default { + id: 'org.nativescript.myapp', + ios: { + SPMPackages: [ + { + name: 'RiveRuntime', + libs: ['RiveRuntime'], + repositoryURL: 'https://github.com/rive-app/rive-ios.git', + version: '6.11.0', + }, + ], + }, +} as NativeScriptConfig`; + + const spmPackages = [ + { + name: "RiveRuntime", + libs: ["RiveRuntime"], + repositoryURL: "https://github.com/rive-app/rive-ios.git", + version: "6.11.0", + }, + { + name: "SharedWidget", + libs: ["SharedWidget"], + path: "./Shared_Resources/iOS/SharedWidget", + targets: ["widget"], + }, + ]; + + const transformer = new ConfigTransformer(content); + const updated = transformer.setValue("ios.SPMPackages", spmPackages); + const updatedTransformer = new ConfigTransformer(updated); + + assert.deepStrictEqual( + updatedTransformer.getValue("ios.SPMPackages"), + spmPackages, + ); + }); +});