Created
September 27, 2024 15:27
-
-
Save nevillco/f4abe97b579a6cb86382404243305c8b to your computer and use it in GitHub Desktop.
Scaffold TCA Feature in Tuist Project script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
# This script does the following, for a given ComposableArchitecture feature. | |
# Suppose we are running `./Scripts/scaffold_tca_feature.sh LoginFeature` as an example. | |
# | |
# - Create intermediate folders and boilerplate files for 4 targets: | |
# LoginFeatureKit, LoginFeatureUI, LoginFeatureKitTests, LoginFeatureUITests | |
# - Create a Tuist manifest file representing the targets at: | |
# ./Tuist/ProjectDescriptionHelpers/Targets/Features/LoginFeature.swift | |
# - Insert the new targets into Tuist’s `Project.swift`, maintaining an alphabetical array of targets. | |
# - Commit the changes and regenerate the project. | |
# | |
# A bunch of aspects of this script are opinionated based on my own infrastructure. | |
# For example, a `CFTargetGroup` is an abstraction bundling a bunch of `ProjectDescription.Target`s. | |
# Therefore, don’t expect to just "drop in" this script to your project - it is | |
# posted as a gist only as a reference/inspiration to others using Tuist as a way to improve automation and velocity. | |
NEW_FEATURE_NAME=$1 | |
if [[ -z "$NEW_FEATURE_NAME" ]]; then | |
echo "No feature name provided. Provide a feature name, capitalized, like: './Scripts/scaffold_tca_feature.sh MyNewFeature'" | |
exit 1 | |
fi | |
echo "Scaffolding ${NEW_FEATURE_NAME}Kit target." | |
mkdir -p "Targets/Features/${NEW_FEATURE_NAME}Kit/Sources" | |
cat <<EOF > "Targets/Features/${NEW_FEATURE_NAME}Kit/Sources/${NEW_FEATURE_NAME}.swift" | |
import ComposableArchitecture | |
@Reducer public struct $NEW_FEATURE_NAME { | |
@ObservableState public struct State { | |
public init() { } | |
} | |
public init() { } | |
} | |
EOF | |
echo "Scaffolding ${NEW_FEATURE_NAME}UI target." | |
mkdir -p "Targets/Features/${NEW_FEATURE_NAME}UI/Sources" | |
cat <<EOF > "Targets/Features/${NEW_FEATURE_NAME}UI/Sources/${NEW_FEATURE_NAME}View.swift" | |
import ComposableArchitecture | |
import ${NEW_FEATURE_NAME}Kit | |
import SwiftUI | |
public struct ${NEW_FEATURE_NAME}View: View { | |
@Bindable var store: StoreOf<${NEW_FEATURE_NAME}> | |
public init(store: StoreOf<${NEW_FEATURE_NAME}>) { | |
self.store = store | |
} | |
public var body: some View { | |
Text("Hello, world!") | |
} | |
} | |
EOF | |
echo "Scaffolding (empty) test targets." | |
mkdir -p "Targets/Features/${NEW_FEATURE_NAME}Kit/Tests" | |
touch "Targets/Features/${NEW_FEATURE_NAME}Kit/Tests/${NEW_FEATURE_NAME}Tests.swift" | |
mkdir -p "Targets/Features/${NEW_FEATURE_NAME}UI/Tests" | |
touch "Targets/Features/${NEW_FEATURE_NAME}UI/Tests/${NEW_FEATURE_NAME}ViewTests.swift" | |
echo "Scaffolding Tuist manifest file." | |
TUIST_FILE="Tuist/ProjectDescriptionHelpers/Targets/Features/${NEW_FEATURE_NAME}.swift" | |
lowercase_first_letter() { | |
input="$1" | |
echo "$(tr '[:upper:]' '[:lower:]' <<< "${input:0:1}")${input:1}" | |
} | |
NEW_FEATURE_NAME_LOWER=$(lowercase_first_letter "$NEW_FEATURE_NAME") | |
cat <<EOF > $TUIST_FILE | |
import CFTuist | |
public extension CFTargetGroup { | |
static let ${NEW_FEATURE_NAME_LOWER} = Self.projectDefault.feature( | |
kitTarget: .init( | |
name: .${NEW_FEATURE_NAME_LOWER}Kit | |
), | |
uiTarget: .init( | |
name: .${NEW_FEATURE_NAME_LOWER}UI | |
) | |
) | |
} | |
public extension CFTargetDependency { | |
static let ${NEW_FEATURE_NAME_LOWER}Kit: Self = .target(.${NEW_FEATURE_NAME_LOWER}Kit) | |
static let ${NEW_FEATURE_NAME_LOWER}UI: Self = .target(.${NEW_FEATURE_NAME_LOWER}UI) | |
} | |
public extension CFTarget.Name { | |
static let ${NEW_FEATURE_NAME_LOWER}Kit: Self = "${NEW_FEATURE_NAME}Kit" | |
static let ${NEW_FEATURE_NAME_LOWER}UI: Self = "${NEW_FEATURE_NAME}UI" | |
} | |
EOF | |
echo "Adding new target group .${NEW_FEATURE_NAME_LOWER} to Tuist Project.swift file." | |
# File to modify | |
FILE="Project.swift" | |
# Find the line number where 'targetGroups: [' starts | |
START_LINE=$(grep -n 'targetGroups: \[' "$FILE" | cut -d: -f1) | |
if [ -z "$START_LINE" ]; then | |
echo "Error: 'targetGroups: [' not found in $FILE" | |
exit 1 | |
fi | |
# Find the line number where the 'targetGroups' array ends | |
END_LINE=$(awk "NR>$START_LINE-1 && /],/ {print NR; exit}" "$FILE") | |
if [ -z "$END_LINE" ]; then | |
echo "Error: Closing bracket for 'targetGroups' not found." | |
exit 1 | |
fi | |
# Extract the existing items | |
sed -n "${START_LINE},${END_LINE}p" "$FILE" | sed '1d;$d' | sed 's/^[[:space:]]*\.//;s/,$//' > tmp_targetGroups.txt | |
# Add the new item | |
echo "$NEW_FEATURE_NAME_LOWER" | sed 's/^[[:space:]]*\.//' >> tmp_targetGroups.txt | |
# Sort the items alphabetically | |
SORTED_ITEMS=$(sort tmp_targetGroups.txt) | |
# Reconstruct the 'targetGroups' section | |
{ | |
sed -n "1,$((START_LINE - 1))p" "$FILE" | |
echo " targetGroups: [" | |
echo "$SORTED_ITEMS" | sed 's/^/ ./' | sed 's/$/,/' | |
echo " ]," | |
sed -n "$((END_LINE + 1)),\$p" "$FILE" | |
} > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" | |
# Clean up | |
rm tmp_targetGroups.txt | |
echo "Successfully added .$NEW_FEATURE_NAME_LOWER to Tuist Project.swift file." | |
echo "Committing changes." | |
git add . && git commit -m "Scaffold ${NEW_FEATURE_NAME}" | |
echo "Done scaffolding! Regenerating the project." | |
tuist generate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment