From 8e8d406e31166bae0e0ca2c0122b09a09b94e5d0 Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 21 Aug 2024 15:53:18 +0000 Subject: [PATCH 1/2] recursively merge package.json from build configuration --- .../codegen/PackageJsonGenerator.java | 9 +- .../codegen/util/MergeJsonNodes.java | 42 +++++++ .../codegen/util/MergeJsonNodesTest.java | 103 ++++++++++++++++++ 3 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java create mode 100644 smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java index 9190b8a0184..bb35c9d4298 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java @@ -21,6 +21,7 @@ import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.typescript.codegen.util.MergeJsonNodes; import software.amazon.smithy.utils.IoUtils; import software.amazon.smithy.utils.SmithyInternalApi; @@ -43,9 +44,11 @@ static void writePackageJson( ) { // Write the package.json file. InputStream resource = PackageJsonGenerator.class.getResourceAsStream("base-package.json"); - ObjectNode node = Node.parse(IoUtils.toUtf8String(resource)) - .expectObjectNode() - .merge(settings.getPackageJson()); + ObjectNode node = MergeJsonNodes.mergeWithScripts( + Node.parse(IoUtils.toUtf8String(resource)) + .expectObjectNode(), + settings.getPackageJson() + ); // Merge TypeScript dependencies into the package.json file. for (Map.Entry> depEntry : dependencies.entrySet()) { diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java new file mode 100644 index 00000000000..80a7ff4a40c --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.util; + +import java.util.Objects; +import software.amazon.smithy.model.node.ObjectNode; + + +public final class MergeJsonNodes { + + private MergeJsonNodes() {} + + /** + * @param left - original node. + * @param right - overwriting node. + * @return new node with shallow merged fields except the recursively merged "scripts" field. + */ + public static ObjectNode mergeWithScripts(ObjectNode left, ObjectNode right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + + ObjectNode.Builder merged = left.toBuilder(); + right.getMembers().forEach((k, v) -> { + String key = k.getValue(); + if (left.containsMember(key)) { + if (left.getMember(key).get().isObjectNode() && v.isObjectNode() && key.equals("scripts")) { + merged.withMember(key, + MergeJsonNodes.mergeWithScripts(left.expectObjectMember(key), v.expectObjectNode()) + ); + } else { + merged.withMember(key, v); + } + } else { + merged.withMember(key, v); + } + }); + return merged.build(); + } +} diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java new file mode 100644 index 00000000000..caf2fbaa7dc --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java @@ -0,0 +1,103 @@ +package software.amazon.smithy.typescript.codegen.util; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MergeJsonNodesTest { + + @Test + void apply() { + ObjectNode left = ObjectNode.parse(""" + { + "name": "hello, world", + "version": 5, + "scripts": { + "target": "exec", + "target2": "exec2", + "args": ["a", "b", "c"], + "nested": { + "deep": "." + } + }, + "metadata": { + "A": "A", + "B": "B", + "C": "C" + } + } + """).expectObjectNode(); + + ObjectNode right = ObjectNode.parse(""" + { + "version": 6, + "scripts": { + "target": "no-op", + "args": ["a", "b", "c", "d"], + "nested": { + "option": "b" + } + }, + "metadata": { + "A": "A" + } + } + """).expectObjectNode(); + + ObjectNode expected = ObjectNode.parse(""" + { + "name": "hello, world", + "version": 6, + "scripts": { + "target": "no-op", + "target2": "exec2", + "args": ["a", "b", "c", "d"], + "nested": { + "option": "b" + } + }, + "metadata": { + "A": "A" + } + } + """).expectObjectNode(); + + ObjectNode l = expected; + ObjectNode r = MergeJsonNodes.mergeWithScripts(left, right); + + assertEquals( + l.expectStringMember("name"), + r.expectStringMember("name") + ); + assertEquals( + l.expectNumberMember("version"), + r.expectNumberMember("version") + ); + assertEquals( + l.expectObjectMember("scripts").expectStringMember("target"), + r.expectObjectMember("scripts").expectStringMember("target") + ); + assertEquals( + l.expectObjectMember("scripts").expectStringMember("target2"), + r.expectObjectMember("scripts").expectStringMember("target2") + ); + assertEquals( + l.expectObjectMember("scripts").expectArrayMember("args").getElementsAs(Node::toString), + r.expectObjectMember("scripts").expectArrayMember("args").getElementsAs(Node::toString) + ); + assertEquals( + l.expectObjectMember("scripts").expectObjectMember("nested").expectStringMember("option"), + r.expectObjectMember("scripts").expectObjectMember("nested").expectStringMember("option") + ); + assertEquals( + 1, + r.expectObjectMember("scripts").expectObjectMember("nested").getStringMap().size() + ); + assertEquals( + 1, + r.expectObjectMember("metadata").getStringMap().size() + ); + } +} \ No newline at end of file From e1fe4134ceae3cc46eb85b939f75991c9c1b1b60 Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 28 Aug 2024 15:54:40 +0000 Subject: [PATCH 2/2] use existing api --- private/smithy-rpcv2-cbor/package.json | 3 +- .../codegen/PackageJsonGenerator.java | 19 +++- .../codegen/util/MergeJsonNodes.java | 42 ------- .../codegen/util/MergeJsonNodesTest.java | 103 ------------------ .../smithy-build.json | 3 + 5 files changed, 18 insertions(+), 152 deletions(-) delete mode 100644 smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java delete mode 100644 smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java diff --git a/private/smithy-rpcv2-cbor/package.json b/private/smithy-rpcv2-cbor/package.json index 988eacfa697..05028014199 100644 --- a/private/smithy-rpcv2-cbor/package.json +++ b/private/smithy-rpcv2-cbor/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0", - "prepack": "yarn run clean && yarn run build" + "prepack": "yarn run clean && yarn run build", + "merged": "echo \"this is merged from user configuration.\"" }, "main": "./dist-cjs/index.js", "types": "./dist-types/index.d.ts", diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java index bb35c9d4298..068edad84a0 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/PackageJsonGenerator.java @@ -21,7 +21,6 @@ import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; -import software.amazon.smithy.typescript.codegen.util.MergeJsonNodes; import software.amazon.smithy.utils.IoUtils; import software.amazon.smithy.utils.SmithyInternalApi; @@ -44,11 +43,19 @@ static void writePackageJson( ) { // Write the package.json file. InputStream resource = PackageJsonGenerator.class.getResourceAsStream("base-package.json"); - ObjectNode node = MergeJsonNodes.mergeWithScripts( - Node.parse(IoUtils.toUtf8String(resource)) - .expectObjectNode(), - settings.getPackageJson() - ); + + ObjectNode userSuppliedPackageJson = settings.getPackageJson(); + ObjectNode defaultPackageJson = Node.parse(IoUtils.toUtf8String(resource)) + .expectObjectNode(); + + ObjectNode mergedScripts = defaultPackageJson.expectObjectMember("scripts") + .merge( + userSuppliedPackageJson.getObjectMember("scripts") + .orElse(ObjectNode.builder().build()) + ); + + ObjectNode node = defaultPackageJson.merge(userSuppliedPackageJson) + .withMember("scripts", mergedScripts); // Merge TypeScript dependencies into the package.json file. for (Map.Entry> depEntry : dependencies.entrySet()) { diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java deleted file mode 100644 index 80a7ff4a40c..00000000000 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodes.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.typescript.codegen.util; - -import java.util.Objects; -import software.amazon.smithy.model.node.ObjectNode; - - -public final class MergeJsonNodes { - - private MergeJsonNodes() {} - - /** - * @param left - original node. - * @param right - overwriting node. - * @return new node with shallow merged fields except the recursively merged "scripts" field. - */ - public static ObjectNode mergeWithScripts(ObjectNode left, ObjectNode right) { - Objects.requireNonNull(left); - Objects.requireNonNull(right); - - ObjectNode.Builder merged = left.toBuilder(); - right.getMembers().forEach((k, v) -> { - String key = k.getValue(); - if (left.containsMember(key)) { - if (left.getMember(key).get().isObjectNode() && v.isObjectNode() && key.equals("scripts")) { - merged.withMember(key, - MergeJsonNodes.mergeWithScripts(left.expectObjectMember(key), v.expectObjectNode()) - ); - } else { - merged.withMember(key, v); - } - } else { - merged.withMember(key, v); - } - }); - return merged.build(); - } -} diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java deleted file mode 100644 index caf2fbaa7dc..00000000000 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/MergeJsonNodesTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package software.amazon.smithy.typescript.codegen.util; - -import org.junit.jupiter.api.Test; -import software.amazon.smithy.model.node.Node; -import software.amazon.smithy.model.node.ObjectNode; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class MergeJsonNodesTest { - - @Test - void apply() { - ObjectNode left = ObjectNode.parse(""" - { - "name": "hello, world", - "version": 5, - "scripts": { - "target": "exec", - "target2": "exec2", - "args": ["a", "b", "c"], - "nested": { - "deep": "." - } - }, - "metadata": { - "A": "A", - "B": "B", - "C": "C" - } - } - """).expectObjectNode(); - - ObjectNode right = ObjectNode.parse(""" - { - "version": 6, - "scripts": { - "target": "no-op", - "args": ["a", "b", "c", "d"], - "nested": { - "option": "b" - } - }, - "metadata": { - "A": "A" - } - } - """).expectObjectNode(); - - ObjectNode expected = ObjectNode.parse(""" - { - "name": "hello, world", - "version": 6, - "scripts": { - "target": "no-op", - "target2": "exec2", - "args": ["a", "b", "c", "d"], - "nested": { - "option": "b" - } - }, - "metadata": { - "A": "A" - } - } - """).expectObjectNode(); - - ObjectNode l = expected; - ObjectNode r = MergeJsonNodes.mergeWithScripts(left, right); - - assertEquals( - l.expectStringMember("name"), - r.expectStringMember("name") - ); - assertEquals( - l.expectNumberMember("version"), - r.expectNumberMember("version") - ); - assertEquals( - l.expectObjectMember("scripts").expectStringMember("target"), - r.expectObjectMember("scripts").expectStringMember("target") - ); - assertEquals( - l.expectObjectMember("scripts").expectStringMember("target2"), - r.expectObjectMember("scripts").expectStringMember("target2") - ); - assertEquals( - l.expectObjectMember("scripts").expectArrayMember("args").getElementsAs(Node::toString), - r.expectObjectMember("scripts").expectArrayMember("args").getElementsAs(Node::toString) - ); - assertEquals( - l.expectObjectMember("scripts").expectObjectMember("nested").expectStringMember("option"), - r.expectObjectMember("scripts").expectObjectMember("nested").expectStringMember("option") - ); - assertEquals( - 1, - r.expectObjectMember("scripts").expectObjectMember("nested").getStringMap().size() - ); - assertEquals( - 1, - r.expectObjectMember("metadata").getStringMap().size() - ); - } -} \ No newline at end of file diff --git a/smithy-typescript-protocol-test-codegen/smithy-build.json b/smithy-typescript-protocol-test-codegen/smithy-build.json index 836ec8edb60..c9f2c67b36b 100644 --- a/smithy-typescript-protocol-test-codegen/smithy-build.json +++ b/smithy-typescript-protocol-test-codegen/smithy-build.json @@ -19,6 +19,9 @@ "name": "Smithy team", "url": "https://smithy.io/" }, + "scripts": { + "merged": "echo \"this is merged from user configuration.\"" + }, "license": "Apache-2.0" }, "private": true,