Refactor + Bugs fixes on Strings + Gestion de la génération des images
78
.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1310"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Imagium"
|
||||||
|
BuildableName = "Imagium"
|
||||||
|
BlueprintName = "Imagium"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Imagium"
|
||||||
|
BuildableName = "Imagium"
|
||||||
|
BlueprintName = "Imagium"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Imagium"
|
||||||
|
BuildableName = "Imagium"
|
||||||
|
BlueprintName = "Imagium"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -118,6 +118,34 @@
|
|||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "FontTool"
|
||||||
|
BuildableName = "FontTool"
|
||||||
|
BlueprintName = "FontTool"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Imagium"
|
||||||
|
BuildableName = "Imagium"
|
||||||
|
BlueprintName = "Imagium"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
@ -152,9 +180,9 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "Strings"
|
BlueprintIdentifier = "Imagium"
|
||||||
BuildableName = "Strings"
|
BuildableName = "Imagium"
|
||||||
BlueprintName = "Strings"
|
BlueprintName = "Imagium"
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
@ -178,9 +206,9 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "Strings"
|
BlueprintIdentifier = "Imagium"
|
||||||
BuildableName = "Strings"
|
BuildableName = "Imagium"
|
||||||
BlueprintName = "Strings"
|
BlueprintName = "Imagium"
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
@ -15,17 +15,17 @@ let package = Package(
|
|||||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
.target(
|
.target(
|
||||||
name: "ResgenSwift",
|
name: "ResgenSwift",
|
||||||
dependencies: ["FontToolCore", "ColorToolCore", "Strings"]
|
dependencies: ["FontTool", "ColorTool", "Strings", "Imagium"]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "FontToolCore",
|
name: "FontTool",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CLIToolCore",
|
"CLIToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "ColorToolCore",
|
name: "ColorTool",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CLIToolCore",
|
"CLIToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||||
@ -33,6 +33,14 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "Strings",
|
name: "Strings",
|
||||||
|
dependencies: [
|
||||||
|
"CLIToolCore",
|
||||||
|
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||||
|
],
|
||||||
|
sources: ["."] // Force include all subdirectories
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "Imagium",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CLIToolCore",
|
"CLIToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Generated from ColorToolCore at 2022-01-10 10:57:08 +0000
|
// Generated from ColorToolCore at 2022-02-14 09:30:19 +0000
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
// Generated from FontToolCore
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIFont {
|
||||||
|
|
||||||
|
enum FontName: String {
|
||||||
|
case LatoItalic = "Lato-Italic"
|
||||||
|
case LatoLightItalic = "Lato-LightItalic"
|
||||||
|
case LatoHairline = "Lato-Hairline"
|
||||||
|
case LatoBold = "Lato-Bold"
|
||||||
|
case LatoBlack = "Lato-Black"
|
||||||
|
case LatoRegular = "Lato-Regular"
|
||||||
|
case LatoBlackItalic = "Lato-BlackItalic"
|
||||||
|
case LatoBoldItalic = "Lato-BoldItalic"
|
||||||
|
case LatoLight = "Lato-Light"
|
||||||
|
case LatoHairlineItalic = "Lato-HairlineItalic"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Getter
|
||||||
|
|
||||||
|
static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
SampleFiles/Images/Generated/UIImage+ImageGenAllScript.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Generated from Imagium at 2022-02-14 09:30:23 +0000
|
||||||
|
// Images from sampleImages
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIImage {
|
||||||
|
|
||||||
|
static var article_notification_pull_detail: UIImage {
|
||||||
|
UIImage(named: "article_notification_pull_detail")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var article_notification_pull: UIImage {
|
||||||
|
UIImage(named: "article_notification_pull")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var new_article: UIImage {
|
||||||
|
UIImage(named: "new_article")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var welcome_background: UIImage {
|
||||||
|
UIImage(named: "welcome_background")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var article_trash: UIImage {
|
||||||
|
UIImage(named: "article_trash")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var ic_close_article: UIImage {
|
||||||
|
UIImage(named: "ic_close_article")!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
SampleFiles/Images/InputImages/Articles/New/new_article.svg
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<svg width="102" height="69" viewBox="0 0 102 69" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M100.089 35.765H72.7522V66.626H100.089V35.765Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M89.1215 46.2884H56.9285V66.6806H89.1215V46.2884Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M100.089 46.2883H89.1216V66.6805H100.089V46.2883Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M84.9199 49.5053H82.8464V53.1039H84.9199V49.5053Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M80.8276 49.5053H78.7542V53.1039H80.8276V49.5053Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M62.8759 60.3557H60.8025V63.9543H62.8759V60.3557Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M97.7973 37.7279H95.7239V41.3266H97.7973V37.7279Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M76.9538 37.7279H74.8804V41.3266H76.9538V37.7279Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M36.4457 27.1985H101.509C101.728 25.2457 100.03 23.1303 98.1679 22.4794C96.251 21.8285 94.1699 21.72 92.4721 20.6351C90.3362 19.2791 89.1861 16.5127 86.7216 15.8076C84.3666 15.1566 82.0117 16.7839 79.5472 16.8924C74.5086 17.1094 70.6202 10.8715 65.6364 11.7394C62.2956 12.336 60.3788 15.8076 57.3666 17.3263C54.8473 18.6282 51.8352 18.3569 48.9873 18.7366C44.1678 19.4418 39.2388 23.3472 36.4457 27.1985Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M0.455092 25.2296L32.2995 0.974118C33.9893 -0.324706 36.3288 -0.324706 38.0185 0.974118L69.8305 25.2296C70.1229 25.4569 70.2854 25.8141 70.2854 26.1713V67.7661C70.2854 68.4155 69.7655 68.9351 69.1156 68.9351H1.20246C0.552574 68.9351 0.0326646 68.4155 0.0326646 67.7661V26.1713C0.0001703 25.8141 0.162643 25.4569 0.455092 25.2296Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path d="M53.973 13.183H16.3121L10.5281 17.5666V68.935H59.7245V17.5666L53.973 13.183Z" fill="#FDB918"/>
|
||||||
|
<path d="M55.9878 6.55914H14.2651C12.8353 6.55914 11.6655 7.72808 11.6655 9.15679V64.7789C11.6655 66.2076 12.8353 67.3765 14.2651 67.3765H55.9878C57.4176 67.3765 58.5874 66.2076 58.5874 64.7789V9.15679C58.5874 7.72808 57.4176 6.55914 55.9878 6.55914Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M35.1265 47.2773L68.4657 67.8312C69.2455 68.3182 70.2528 67.7662 70.2528 66.8246V27.6975C70.2528 26.7883 69.2455 26.2039 68.4657 26.6909L35.1265 47.2773Z" fill="url(#paint1_linear)"/>
|
||||||
|
<path d="M0 27.6974V66.8245C0 67.7336 1.00732 68.3181 1.78719 67.831L35.1264 47.2772L1.78719 26.6908C1.00732 26.2038 0 26.7882 0 27.6974Z" fill="url(#paint2_linear)"/>
|
||||||
|
<path d="M40.3255 44.0627L38.1808 42.4392C36.3611 41.0429 33.8591 41.0429 32.0394 42.4392L29.8948 44.0627L35.0939 47.2773L40.3255 44.0627Z" fill="#E6E6E6"/>
|
||||||
|
<path d="M0 67.1491L35.1264 47.2771L29.9273 44.0626L0 67.1491Z" fill="#FDB918"/>
|
||||||
|
<path d="M70.2528 67.1491L35.1265 47.2771L40.3256 44.0626L70.2528 67.1491Z" fill="#FDB918"/>
|
||||||
|
<path d="M29.1147 17.3064C29.1147 17.3064 28.8872 19.969 35.1262 21.4627L33.7939 21.3977C33.7939 21.3977 29.6996 20.9756 28.7573 19.2547C27.8149 17.5337 29.1147 17.3064 29.1147 17.3064Z" fill="#FDB918"/>
|
||||||
|
<path d="M30.1545 15.9104C33.079 13.053 34.9312 21.4304 34.9637 21.4629C35.0287 21.4629 35.0937 21.4629 35.1587 21.4629C35.1587 21.4629 33.014 9.90333 29.4396 14.709C25.6053 19.7744 30.0245 21.3654 35.1262 21.4629C30.7719 21.333 26.8076 19.1574 30.1545 15.9104Z" fill="#FFE368"/>
|
||||||
|
<path d="M40.9753 17.3064C40.9753 17.3064 41.2028 19.969 34.9639 21.4627L36.2961 21.3977C36.2961 21.3977 40.3904 20.9756 41.3328 19.2547C42.2426 17.5337 40.9753 17.3064 40.9753 17.3064Z" fill="#FDB918"/>
|
||||||
|
<path d="M39.9353 15.9104C37.0108 13.053 35.1586 21.4304 35.1261 21.4629C35.0611 21.4629 34.9961 21.4629 34.9312 21.4629C34.9312 21.4629 37.0758 9.90333 40.6502 14.709C44.452 19.7744 40.0328 21.3654 34.9312 21.4629C39.3179 21.333 43.2497 19.1574 39.9353 15.9104Z" fill="#FFE368"/>
|
||||||
|
<path d="M27.3926 36.237V22.0798C27.3926 21.7226 27.685 21.4304 28.0425 21.4304H42.21C42.5674 21.4304 42.8599 21.7226 42.8599 22.0798V36.237C42.8599 36.5941 42.5674 36.8864 42.21 36.8864H28.0425C27.685 36.8864 27.3926 36.5941 27.3926 36.237Z" fill="#063881"/>
|
||||||
|
<path d="M26.3528 24.645V22.0798C26.3528 21.7226 26.6452 21.4304 27.0027 21.4304H43.2499C43.6073 21.4304 43.8997 21.7226 43.8997 22.0798V24.645C43.8997 25.0021 43.6073 25.2944 43.2499 25.2944H27.0027C26.6452 25.2944 26.3528 25.0021 26.3528 24.645Z" fill="#0B559E"/>
|
||||||
|
<path d="M37.5959 21.4304H32.6567V25.2944H37.5959V21.4304Z" fill="#FFE368"/>
|
||||||
|
<path d="M37.3035 25.2943H32.9492V36.8863H37.3035V25.2943Z" fill="#FDB918"/>
|
||||||
|
<path d="M1.5437 69H68.8777C70.1371 69 70.6539 67.4136 69.6204 66.6839L38.0042 44.6961C36.3248 43.427 33.9673 43.427 32.2557 44.6961L0.736338 66.6839C-0.232496 67.4136 0.284216 69 1.5437 69Z" fill="url(#paint3_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="-0.00678347" y1="34.4878" x2="70.2659" y2="34.4878" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3174" stop-color="#FFE265"/>
|
||||||
|
<stop offset="0.5172" stop-color="#FFDD5D"/>
|
||||||
|
<stop offset="0.6845" stop-color="#FED54E"/>
|
||||||
|
<stop offset="0.8339" stop-color="#FECB39"/>
|
||||||
|
<stop offset="0.9701" stop-color="#FDBD1F"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="9.77743" y1="11.1302" x2="102.999" y2="68.0143" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear" x1="-8.63037" y1="41.2967" x2="84.5914" y2="98.1808" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear" x1="-10.5945" y1="38.7503" x2="87.7585" y2="99.7918" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="111px" height="69px" viewBox="0 0 111 69" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>article_notification_pull</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="article_notification_pull">
|
||||||
|
<path d="M81.130223,39.5772 C81.130223,39.5772 99.360223,32.4231 102.116223,33.2806 C104.871223,34.1381 111.204223,51.339 110.013223,51.8526 C108.758223,52.4481 102.001223,47.0974 102.001223,47.0974 C102.001223,47.0974 100.834223,50.2797 98.387023,49.5337 C95.939623,48.7877 92.897623,44.9219 92.897623,44.9219 C92.897623,44.9219 92.488923,47.0487 89.986323,46.4577 C87.483723,45.8667 83.726623,41.567 83.726623,41.567 L81.130223,39.5772 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M99.259223,10.5629 C97.894023,10.5629 96.788623,9.4412 96.788623,8.0557 C96.788623,7.4619 96.333423,7 95.748223,7 C95.163023,7 94.707823,7.4619 94.707823,8.0557 C94.707823,9.4412 93.602423,10.5629 92.237023,10.5629 C91.651723,10.5629 91.196623,11.0247 91.196623,11.6185 C91.196623,12.2124 91.651723,12.6742 92.237023,12.6742 C93.602423,12.6742 94.707823,13.7959 94.707823,15.1814 C94.707823,15.7752 95.163023,16.2371 95.748223,16.2371 C96.333423,16.2371 96.788623,15.7752 96.788623,15.1814 C96.788623,13.7959 97.894023,12.6742 99.259223,12.6742 C99.844223,12.6742 100.300223,12.2124 100.300223,11.6185 C100.365223,11.0907 99.844223,10.5629 99.259223,10.5629 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M81.193523,11.2243 C80.403523,10.8878 79.962423,9.9515 80.333423,9.0805 C80.502023,8.6847 80.287223,8.3125 79.971223,8.1779 C79.576223,8.0096 79.204323,8.2254 79.069423,8.5421 C78.732123,9.3338 77.796523,9.7772 76.927523,9.407 C76.532523,9.2387 76.160623,9.4544 76.025723,9.7711 C75.857023,10.167 76.071823,10.5391 76.387823,10.6738 C77.177923,11.0103 77.619023,11.9466 77.248023,12.8175 C77.079423,13.2134 77.294223,13.5855 77.610223,13.7201 C78.005223,13.8884 78.377123,13.6727 78.512023,13.356 C78.849323,12.5642 79.784923,12.1209 80.653923,12.4911 C81.048923,12.6594 81.420823,12.4436 81.555723,12.1269 C81.769623,11.8439 81.588623,11.3926 81.193523,11.2243 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<line x1="12.845223" y1="48.192" x2="16.110123" y2="42" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<ellipse id="Oval" fill-opacity="0.1" fill="#000000" fill-rule="nonzero" cx="50.345223" cy="67.5" rx="17.5" ry="1.5"></ellipse>
|
||||||
|
<line x1="14.517923" y1="53.5959" x2="17.782723" y2="47.404" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<line x1="20.806323" y1="52.3897" x2="23.604823" y2="47.0823" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M79.876623,1.78144 C79.226423,1.78144 78.641223,1.2536 78.641223,0.52783 C78.641223,0.19794 78.381123,0 78.121023,0 C77.795923,0 77.600823,0.26392 77.600823,0.52783 C77.600823,1.18763 77.080623,1.78144 76.365423,1.78144 C76.040323,1.78144 75.845223,2.04535 75.845223,2.30927 C75.845223,2.63917 76.105323,2.8371 76.365423,2.8371 C77.015623,2.8371 77.600823,3.3649 77.600823,4.0907 C77.600823,4.4206 77.860923,4.6185 78.121023,4.6185 C78.446123,4.6185 78.641223,4.3546 78.641223,4.0907 C78.641223,3.4309 79.161423,2.8371 79.876623,2.8371 C80.201823,2.8371 80.396823,2.57319 80.396823,2.30927 C80.461923,2.04535 80.201823,1.78144 79.876623,1.78144 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M31.021323,19.8983 C31.021323,19.8983 19.126423,4.2537 16.379623,3.4694 C13.568923,2.76686 -0.694914027,14.1215 0.0264229726,15.2225 C0.756502973,16.3967 9.32942297,15.3706 9.32942297,15.3706 C9.32942297,15.3706 8.62499297,18.7201 11.136323,19.3843 C13.647723,20.0484 18.238723,18.3857 18.238723,18.3857 C18.238723,18.3857 17.521923,20.401 19.905523,21.2288 C22.289123,22.0567 27.760723,20.3628 27.760723,20.3628 L31.021323,19.8983 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.710223,24.4503 L77.707723,58.391 C77.385623,59.3943 76.267023,59.8992 75.270023,59.5733 L21.900823,40.3589 C20.903823,40.033 20.400423,38.9059 20.722523,37.9026 L32.788923,3.88 C32.835323,3.6518 32.954323,3.4149 33.154823,3.2425 C33.674723,2.66083 34.456323,2.41884 35.299323,2.689 L88.604623,21.9853 C89.374923,22.2641 89.834523,23.0254 89.849423,23.7658 C89.875623,23.9852 89.829323,24.2134 89.710223,24.4503 Z" id="Path" fill="#FFD100" fill-rule="nonzero"></path>
|
||||||
|
<path d="M23.390523,38.0285 L52.772523,31.6916 C54.280623,31.3626 55.821223,31.9203 56.778023,33.1416 L75.925523,57.0459 L57.901923,31.4487 C56.910123,29.9348 55.061323,29.2655 53.361523,29.84 L23.390523,38.0285 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.803023,23.9939 L54.122423,34.35 L33.108423,3.4707 C33.610823,2.74268 34.529023,2.41014 35.299323,2.68899 L88.604623,21.9853 C89.374923,22.2641 89.770623,23.1073 89.803023,23.9939 Z" id="Path" fill="#FFDF4F" fill-rule="nonzero"></path>
|
||||||
|
<path d="M34.370723,5.3975 L52.679723,32.148 C53.572623,33.4511 55.113223,34.0088 56.685323,33.598 L87.402923,24.8749 L56.375623,35.9356 C54.611823,36.592 52.771823,35.9959 51.835123,34.327 L34.370723,5.3975 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="111px" height="69px" viewBox="0 0 111 69" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>article_notification_pull_detail</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="article_notification_pull_detail" transform="translate(0.000000, 0.000000)">
|
||||||
|
<path d="M81.1301131,39.5772 C81.1301131,39.5772 99.3602131,32.4231 102.116213,33.2806 C104.871213,34.1381 111.204213,51.339 110.013213,51.8526 C108.758213,52.4481 102.001213,47.0974 102.001213,47.0974 C102.001213,47.0974 100.834213,50.2797 98.3869131,49.5337 C95.9395131,48.7877 92.8975131,44.9219 92.8975131,44.9219 C92.8975131,44.9219 92.4888131,47.0487 89.9862131,46.4577 C87.4836131,45.8667 83.7265131,41.567 83.7265131,41.567 L81.1301131,39.5772 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M99.2592131,10.5629 C97.8940131,10.5629 96.7886131,9.4412 96.7886131,8.0557 C96.7886131,7.4619 96.3334131,7 95.7482131,7 C95.1630131,7 94.7078131,7.4619 94.7078131,8.0557 C94.7078131,9.4412 93.6024131,10.5629 92.2370131,10.5629 C91.6517131,10.5629 91.1966131,11.0247 91.1966131,11.6185 C91.1966131,12.2124 91.6517131,12.6742 92.2370131,12.6742 C93.6024131,12.6742 94.7078131,13.7959 94.7078131,15.1814 C94.7078131,15.7752 95.1630131,16.2371 95.7482131,16.2371 C96.3334131,16.2371 96.7886131,15.7752 96.7886131,15.1814 C96.7886131,13.7959 97.8940131,12.6742 99.2592131,12.6742 C99.8442131,12.6742 100.300213,12.2124 100.300213,11.6185 C100.365213,11.0907 99.8442131,10.5629 99.2592131,10.5629 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M81.1934131,11.2243 C80.4034131,10.8878 79.9623131,9.9515 80.3333131,9.0805 C80.5019131,8.6847 80.2871131,8.3125 79.9711131,8.1779 C79.5761131,8.0096 79.2042131,8.2254 79.0693131,8.5421 C78.7320131,9.3338 77.7964131,9.7772 76.9274131,9.407 C76.5324131,9.2387 76.1605131,9.4544 76.0256131,9.7711 C75.8569131,10.167 76.0717131,10.5391 76.3877131,10.6738 C77.1778131,11.0103 77.6189131,11.9466 77.2479131,12.8175 C77.0793131,13.2134 77.2941131,13.5855 77.6101131,13.7201 C78.0051131,13.8884 78.3770131,13.6727 78.5119131,13.356 C78.8492131,12.5642 79.7848131,12.1209 80.6538131,12.4911 C81.0488131,12.6594 81.4207131,12.4436 81.5556131,12.1269 C81.7695131,11.8439 81.5885131,11.3926 81.1934131,11.2243 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<line x1="12.8452131" y1="48.192" x2="16.1101131" y2="42" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M50.3452131,69 C60.0102131,69 67.8452131,68.3284 67.8452131,67.5 C67.8452131,66.6716 60.0102131,66 50.3452131,66 C40.6802131,66 32.8452131,66.6716 32.8452131,67.5 C32.8452131,68.3284 40.6802131,69 50.3452131,69 Z" id="Path" fill-opacity="0.1" fill="#000000" fill-rule="nonzero"></path>
|
||||||
|
<line x1="14.5178131" y1="53.5959" x2="17.7826131" y2="47.404" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<line x1="20.8064131" y1="52.3897" x2="23.6049131" y2="47.0823" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M79.8766131,1.78144 C79.2264131,1.78144 78.6412131,1.2536 78.6412131,0.52783 C78.6412131,0.19794 78.3811131,0 78.1210131,0 C77.7959131,0 77.6008131,0.26392 77.6008131,0.52783 C77.6008131,1.18763 77.0806131,1.78144 76.3654131,1.78144 C76.0403131,1.78144 75.8452131,2.04535 75.8452131,2.30927 C75.8452131,2.63917 76.1053131,2.8371 76.3654131,2.8371 C77.0156131,2.8371 77.6008131,3.3649 77.6008131,4.0907 C77.6008131,4.4206 77.8609131,4.6185 78.1210131,4.6185 C78.4461131,4.6185 78.6412131,4.3546 78.6412131,4.0907 C78.6412131,3.4309 79.1614131,2.8371 79.8766131,2.8371 C80.2018131,2.8371 80.3968131,2.57319 80.3968131,2.30927 C80.4619131,2.04535 80.2018131,1.78144 79.8766131,1.78144 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M31.0213131,19.8983 C31.0213131,19.8983 19.1264131,4.2537 16.3796131,3.4694 C13.5689131,2.76686 -0.694915869,14.1215 0.0264231309,15.2225 C0.756503131,16.3967 9.32941313,15.3706 9.32941313,15.3706 C9.32941313,15.3706 8.62499313,18.7201 11.1363131,19.3843 C13.6477131,20.0484 18.2387131,18.3857 18.2387131,18.3857 C18.2387131,18.3857 17.5219131,20.401 19.9055131,21.2288 C22.2891131,22.0567 27.7607131,20.3628 27.7607131,20.3628 L31.0213131,19.8983 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.7103131,24.4503 L77.7078131,58.391 C77.3857131,59.3943 76.2671131,59.8992 75.2701131,59.5733 L21.9009131,40.3589 C20.9039131,40.033 20.4005131,38.9059 20.7226131,37.9026 L32.7890131,3.88 C32.8354131,3.6518 32.9544131,3.4149 33.1549131,3.2425 C33.6748131,2.66083 34.4564131,2.41884 35.2994131,2.689 L88.6047131,21.9853 C89.3750131,22.2641 89.8346131,23.0254 89.8495131,23.7658 C89.8757131,23.9852 89.8294131,24.2134 89.7103131,24.4503 Z" id="Path" fill="#FFD100" fill-rule="nonzero"></path>
|
||||||
|
<path d="M23.3906131,38.0285 L52.7726131,31.6916 C54.2807131,31.3626 55.8213131,31.9203 56.7781131,33.1416 L75.9256131,57.0459 L57.9020131,31.4487 C56.9102131,29.9348 55.0614131,29.2655 53.3616131,29.84 L23.3906131,38.0285 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.8030131,23.9939 L54.1224131,34.35 L33.1084131,3.4707 C33.6108131,2.74268 34.5290131,2.41014 35.2993131,2.68899 L88.6046131,21.9853 C89.3749131,22.2641 89.7706131,23.1073 89.8030131,23.9939 Z" id="Path" fill="#FFDF4F" fill-rule="nonzero"></path>
|
||||||
|
<path d="M34.3706131,5.3975 L52.6796131,32.148 C53.5725131,33.4511 55.1131131,34.0088 56.6852131,33.598 L87.4028131,24.8749 L56.3755131,35.9356 C54.6117131,36.592 52.7717131,35.9959 51.8350131,34.327 L34.3706131,5.3975 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="37" height="41" viewBox="0 0 37 41" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 4.87183L5.45856 32.1227C5.68952 33.5346 6.30709 34.8549 7.24275 35.9371C8.17841 37.0194 9.3956 37.8213 10.7593 38.2539L11.3943 38.4553C15.9682 39.9073 20.8795 39.9073 25.4534 38.4553L26.0884 38.2539C27.4518 37.8215 28.6688 37.0199 29.6044 35.938C30.5401 34.8561 31.1578 33.5362 31.3891 32.1246L35.8476 4.87183" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M18.4238 8.74392C28.0467 8.74392 35.8476 7.01039 35.8476 4.87196C35.8476 2.73354 28.0467 1 18.4238 1C8.80091 1 1 2.73354 1 4.87196C1 7.01039 8.80091 8.74392 18.4238 8.74392Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 772 B |
4
SampleFiles/Images/InputImages/ic_close_article.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.41 1.41L10 0L5.705 4.295L1.41 0L0 1.41L4.295 5.705L0 10L1.41 11.41L5.705 7.115L10 11.41L11.41 10L7.115 5.705L11.41 1.41Z"
|
||||||
|
fill="#EFF2F4" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 268 B |
BIN
SampleFiles/Images/InputImages/welcome_background.png
Normal file
After Width: | Height: | Size: 452 KiB |
23
SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "article_notification_pull.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "article_notification_pull@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "article_notification_pull@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
BIN
SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 18 KiB |
23
SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "article_notification_pull_detail.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "article_notification_pull_detail@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "article_notification_pull_detail@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 19 KiB |
23
SampleFiles/Images/imagium.xcassets/article_trash.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "article_trash.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "article_trash@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "article_trash@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
BIN
SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash.png
vendored
Normal file
After Width: | Height: | Size: 977 B |
BIN
SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.9 KiB |
23
SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "ic_close_article.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "ic_close_article@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "ic_close_article@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
BIN
SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article.png
vendored
Normal file
After Width: | Height: | Size: 330 B |
BIN
SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@2x.png
vendored
Normal file
After Width: | Height: | Size: 513 B |
BIN
SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@3x.png
vendored
Normal file
After Width: | Height: | Size: 647 B |
23
SampleFiles/Images/imagium.xcassets/new_article.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "new_article.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "new_article@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "new_article@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
BIN
SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article.png
vendored
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@2x.png
vendored
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@3x.png
vendored
Normal file
After Width: | Height: | Size: 20 KiB |
23
SampleFiles/Images/imagium.xcassets/welcome_background.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "welcome_background.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "welcome_background@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "welcome_background@3x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
BIN
SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background.png
vendored
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@2x.png
vendored
Normal file
After Width: | Height: | Size: 813 KiB |
BIN
SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.4 MiB |
6
SampleFiles/Images/sampleImages.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
i article_notification_pull_detail 150 ?
|
||||||
|
i article_notification_pull 150 ?
|
||||||
|
i new_article 150 ?
|
||||||
|
i welcome_background 320 ?
|
||||||
|
id article_trash 35 ?
|
||||||
|
di ic_close_article 11 11
|
@ -1,4 +1,4 @@
|
|||||||
// Generated from Strings-Stringium at 2022-01-10 10:57:09 +0000
|
// Generated from Strings-Stringium at 2022-02-14 09:30:20 +0000
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Generated from Strings-Tags at 2022-01-10 10:57:09 +0000
|
// Generated from Strings-Tags at 2022-02-14 09:30:20 +0000
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
// typelias Tags = String
|
// typelias Tags = String
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
extension Tags {
|
extension Tags {
|
||||||
|
|
||||||
// MARK: - ScreenTag
|
// MARK: - ScreenTag
|
||||||
|
@ -3,19 +3,23 @@
|
|||||||
FORCE_FLAG="$1"
|
FORCE_FLAG="$1"
|
||||||
|
|
||||||
# Font
|
# Font
|
||||||
swift run -c release FontToolCore $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
|
swift run -c release FontTool $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
|
||||||
--extension-output-path "./Fonts/Generated" \
|
--extension-output-path "./Fonts/Generated" \
|
||||||
--extension-name "UIFont" \
|
--extension-name "UIFont" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Color
|
# Color
|
||||||
swift run -c release ColorToolCore $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
swift run -c release ColorTool $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
||||||
--style all \
|
--style all \
|
||||||
--xcassets-path "./Colors/colors.xcassets" \
|
--xcassets-path "./Colors/colors.xcassets" \
|
||||||
--extension-output-path "./Colors/Generated/" \
|
--extension-output-path "./Colors/Generated/" \
|
||||||
--extension-name "UIColor" \
|
--extension-name "UIColor" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Twine
|
# Twine
|
||||||
swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
||||||
--output-path "./Twine/Generated" \
|
--output-path "./Twine/Generated" \
|
||||||
@ -23,6 +27,8 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
|||||||
--default-lang "en" \
|
--default-lang "en" \
|
||||||
--extension-output-path "./Twine/Generated"
|
--extension-output-path "./Twine/Generated"
|
||||||
|
|
||||||
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Strings
|
# Strings
|
||||||
swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt" \
|
swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt" \
|
||||||
--output-path "./Strings/Generated" \
|
--output-path "./Strings/Generated" \
|
||||||
@ -32,11 +38,20 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt"
|
|||||||
--extension-name "String" \
|
--extension-name "String" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
swift run -c release Strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \
|
swift run -c release Strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \
|
||||||
--lang "ium" \
|
--lang "ium" \
|
||||||
--extension-output-path "./Tags/Generated" \
|
--extension-output-path "./Tags/Generated" \
|
||||||
--extension-name "Tags" \
|
--extension-name "Tags" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
|
swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
|
||||||
|
--xcassets-path "./Images/imagium.xcassets" \
|
||||||
|
--extension-output-path "./Images/Generated" \
|
||||||
|
--extension-name "UIImage" \
|
||||||
|
--extension-suffix "GenAllScript"
|
||||||
|
@ -28,4 +28,24 @@ public class Shell {
|
|||||||
|
|
||||||
return (terminationStatus: task.terminationStatus, output: output)
|
return (terminationStatus: task.terminationStatus, output: output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public static func shell(_ args: [String]) -> (terminationStatus: Int32, output: String?) {
|
||||||
|
let task = Process()
|
||||||
|
task.launchPath = "/usr/bin/env"
|
||||||
|
task.arguments = args
|
||||||
|
|
||||||
|
let pipe = Pipe()
|
||||||
|
task.standardOutput = pipe
|
||||||
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
|
|
||||||
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
|
||||||
|
guard let output: String = String(data: data, encoding: .utf8) else {
|
||||||
|
return (terminationStatus: task.terminationStatus, output: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (terminationStatus: task.terminationStatus, output: output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
32
Sources/ColorTool/ColorToolOptions.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// ColorToolOptions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct ColorToolOptions: ParsableArguments {
|
||||||
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
|
var forceGeneration = false
|
||||||
|
|
||||||
|
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var inputFile: String
|
||||||
|
|
||||||
|
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
||||||
|
var style: String
|
||||||
|
|
||||||
|
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var xcassetsPath: String
|
||||||
|
|
||||||
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.")
|
||||||
|
var extensionName: String = ColorTool.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||||
|
var extensionSuffix: String = ""
|
||||||
|
}
|
@ -18,70 +18,51 @@ struct ColorTool: ParsableCommand {
|
|||||||
static let defaultExtensionName = "UIColor"
|
static let defaultExtensionName = "UIColor"
|
||||||
static let assetsColorsFolderName = "Colors"
|
static let assetsColorsFolderName = "Colors"
|
||||||
|
|
||||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
@OptionGroup var options: ColorToolOptions
|
||||||
var forceGeneration = false
|
|
||||||
|
|
||||||
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
var colorStyle: ColorStyle { ColorStyle(rawValue: options.style) ?? .all }
|
||||||
var inputFile: String
|
var extensionFileName: String { "\(options.extensionName)+Color\(options.extensionSuffix).swift" }
|
||||||
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
|
||||||
var style: String
|
|
||||||
|
|
||||||
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
|
||||||
var xcassetsPath: String
|
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
|
||||||
var extensionOutputPath: String
|
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.")
|
|
||||||
var extensionName: String = Self.defaultExtensionName
|
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
|
||||||
var extensionSuffix: String = ""
|
|
||||||
|
|
||||||
var colorStyle: ColorStyle { ColorStyle(rawValue: style) ?? .all }
|
|
||||||
var extensionFileName: String { "\(extensionName)+Color\(extensionSuffix).swift" }
|
|
||||||
var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[ColorTool] Starting colors generation")
|
print("[ColorTool] Starting colors generation")
|
||||||
|
|
||||||
print("[ColorTool] Will use inputFile \(inputFile) to generate \(colorStyle) colors in xcassets \(xcassetsPath)")
|
print("[ColorTool] Will use inputFile \(options.inputFile) to generate \(colorStyle) colors in xcassets \(options.xcassetsPath)")
|
||||||
print("[ColorTool] Extension will be \(extensionFilePath)")
|
print("[ColorTool] Extension will be \(extensionFilePath)")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
guard fileManager.fileExists(atPath: xcassetsPath) else {
|
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
||||||
let error = ColorToolError.fileNotExists(xcassetsPath)
|
let error = ColorToolError.fileNotExists(options.xcassetsPath)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
ColorTool.exit(withError: error)
|
||||||
}
|
}
|
||||||
guard fileManager.fileExists(atPath: inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = ColorToolError.fileNotExists(inputFile)
|
let error = ColorToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
ColorTool.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||||
print("[ColorTool] Colors are already up to date :) ")
|
print("[ColorTool] Colors are already up to date :) ")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
print("[ColorTool] Will generate colors")
|
print("[ColorTool] Will generate colors")
|
||||||
|
|
||||||
// Delete current colors
|
// Delete current colors
|
||||||
Shell.shell("rm", "-rf", "\(xcassetsPath)/Colors/*")
|
Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
|
||||||
|
|
||||||
// Get colors
|
// Get colors
|
||||||
let colorsToGen = getColorsGen()
|
let colorsToGen = getColorsGen()
|
||||||
|
|
||||||
// Generate all colors in xcassets
|
// Generate all colors in xcassets
|
||||||
let colorAssetHelper = ColorXcassetHelper(xcassetsPath: xcassetsPath, colors: colorsToGen)
|
let colorAssetHelper = ColorXcassetHelper(xcassetsPath: options.xcassetsPath, colors: colorsToGen)
|
||||||
colorAssetHelper.generateXcassetColors()
|
colorAssetHelper.generateXcassetColors()
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen,
|
let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen,
|
||||||
extensionClassname: extensionName,
|
extensionClassname: options.extensionName,
|
||||||
isUIColorExtension: isUIColorExtension())
|
isUIColorExtension: isUIColorExtension())
|
||||||
let extensionHeader = extensionGenerator.getHeader()
|
let extensionHeader = extensionGenerator.getHeader()
|
||||||
let extensionProperties = extensionGenerator.getProperties()
|
let extensionProperties = extensionGenerator.getProperties()
|
||||||
@ -115,8 +96,8 @@ struct ColorTool: ParsableCommand {
|
|||||||
|
|
||||||
private func getColorsGen() -> [GenColor] {
|
private func getColorsGen() -> [GenColor] {
|
||||||
// Get content of input file
|
// Get content of input file
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8)
|
||||||
let colorsByLines = inputFileContent.components(separatedBy: .newlines)
|
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
// Iterate on each line of input file
|
// Iterate on each line of input file
|
||||||
return colorsByLines.enumerated().compactMap { lineNumber, colorLine in
|
return colorsByLines.enumerated().compactMap { lineNumber, colorLine in
|
||||||
@ -155,7 +136,7 @@ struct ColorTool: ParsableCommand {
|
|||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func isUIColorExtension() -> Bool {
|
private func isUIColorExtension() -> Bool {
|
||||||
extensionName == Self.defaultExtensionName
|
options.extensionName == Self.defaultExtensionName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
Sources/FontTool/FontOptions.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// FontOptions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct FontOptions: ParsableArguments {
|
||||||
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
|
var forceGeneration = false
|
||||||
|
|
||||||
|
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var inputFile: String
|
||||||
|
|
||||||
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
|
||||||
|
var extensionName: String = FontTool.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||||
|
var extensionSuffix: String = ""
|
||||||
|
}
|
@ -10,39 +10,27 @@ import CLIToolCore
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct FontTool: ParsableCommand {
|
struct FontTool: ParsableCommand {
|
||||||
|
static var configuration = CommandConfiguration(abstract: "Generate fonts plist info and extension to access fonts easily.")
|
||||||
static let defaultExtensionName = "UIFont"
|
static let defaultExtensionName = "UIFont"
|
||||||
|
|
||||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
@OptionGroup var options: FontOptions
|
||||||
var forceGeneration = false
|
|
||||||
|
|
||||||
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
var extensionFileName: String { "\(options.extensionName)options.+Font\(options.extensionSuffix).swift" }
|
||||||
var inputFile: String
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
|
||||||
var extensionOutputPath: String
|
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
|
|
||||||
var extensionName: String = Self.defaultExtensionName
|
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
|
||||||
var extensionSuffix: String = ""
|
|
||||||
|
|
||||||
var extensionFileName: String { "\(extensionName)+Font\(extensionSuffix).swift" }
|
|
||||||
var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[FontTool] Starting fonts generation")
|
print("[FontTool] Starting fonts generation")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
guard fileManager.fileExists(atPath: inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = FontToolError.fileNotExists(inputFile)
|
let error = FontToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
FontTool.exit(withError: error)
|
FontTool.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||||
print("[FontTool] Fonts are already up to date :) ")
|
print("[FontTool] Fonts are already up to date :) ")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -51,7 +39,7 @@ struct FontTool: ParsableCommand {
|
|||||||
// Get fonts to generate
|
// Get fonts to generate
|
||||||
let fontsToGenerate = getFontsToGenerate()
|
let fontsToGenerate = getFontsToGenerate()
|
||||||
|
|
||||||
let inputFolder = URL(fileURLWithPath: inputFile).deletingLastPathComponent().relativePath
|
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
|
||||||
let fontsFilenames = FontToolHelper
|
let fontsFilenames = FontToolHelper
|
||||||
.getFontsFilenames(fromInputFolder: inputFolder)
|
.getFontsFilenames(fromInputFolder: inputFolder)
|
||||||
.filter { fontNameWithPath in
|
.filter { fontNameWithPath in
|
||||||
@ -71,7 +59,7 @@ struct FontTool: ParsableCommand {
|
|||||||
// Adding fontsFilenames to header (ex: path/to/font.ttf) to make check of regeneration faster
|
// Adding fontsFilenames to header (ex: path/to/font.ttf) to make check of regeneration faster
|
||||||
let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsFilenames)
|
let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsFilenames)
|
||||||
|
|
||||||
let extensionDefinitionOpening = "extension \(extensionName) {\n"
|
let extensionDefinitionOpening = "extension \(options.extensionName) {\n"
|
||||||
let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsNames)
|
let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsNames)
|
||||||
let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsNames, isUIFontExtension: isUIFontExtension())
|
let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsNames, isUIFontExtension: isUIFontExtension())
|
||||||
let extensionDefinitionClosing = "}"
|
let extensionDefinitionClosing = "}"
|
||||||
@ -117,23 +105,13 @@ struct FontTool: ParsableCommand {
|
|||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func getFontsToGenerate() -> [String] {
|
private func getFontsToGenerate() -> [String] {
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8)
|
||||||
return inputFileContent.components(separatedBy: .newlines)
|
return inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isUIFontExtension() -> Bool {
|
private func isUIFontExtension() -> Bool {
|
||||||
extensionName == Self.defaultExtensionName
|
options.extensionName == Self.defaultExtensionName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FontTool.main()
|
FontTool.main()
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
1. R2Font extension
|
|
||||||
swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated --extension-name R2Font
|
|
||||||
|
|
||||||
1. UIFont defualt extension (will gen static property)
|
|
||||||
swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated
|
|
||||||
|
|
||||||
*/
|
|
13
Sources/Imagium/ConvertArgument.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// ConvertArgument.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct ConvertArgument {
|
||||||
|
let width: String?
|
||||||
|
let height: String?
|
||||||
|
}
|
56
Sources/Imagium/FileManagerExtensions.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// FileManagerExtensions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension FileManager {
|
||||||
|
func getAllRegularFileIn(directory: String) -> [String] {
|
||||||
|
var files = [String]()
|
||||||
|
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
||||||
|
let error = ImagiumError.unknown("Cannot enumerate file in \(directory)")
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
for case let fileURL as URL in enumerator {
|
||||||
|
do {
|
||||||
|
let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey])
|
||||||
|
if fileAttributes.isRegularFile! {
|
||||||
|
files.append(fileURL.relativePath)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllImageSetFolderIn(directory: String) -> [String] {
|
||||||
|
var files = [String]()
|
||||||
|
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
||||||
|
let error = ImagiumError.unknown("Cannot enumerate imageset directory in \(directory)")
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
for case let fileURL as URL in enumerator {
|
||||||
|
do {
|
||||||
|
let fileAttributes = try fileURL.resourceValues(forKeys:[.isDirectoryKey])
|
||||||
|
if fileAttributes.isDirectory! && fileURL.lastPathComponent.hasSuffix(".imageset") {
|
||||||
|
files.append(fileURL.lastPathComponent)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
}
|
87
Sources/Imagium/ImageExtensionGenerator.swift
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// ImageExtensionGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 14/02/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CLIToolCore
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ImageExtensionGenerator {
|
||||||
|
|
||||||
|
// MARK: - Extension files
|
||||||
|
|
||||||
|
static func writeStringsFiles(images: [ImageToGen], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
||||||
|
// Get header/footer
|
||||||
|
let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
|
||||||
|
let extensionFooter = Self.getFooter()
|
||||||
|
|
||||||
|
// Create content
|
||||||
|
let extensionContent: String = {
|
||||||
|
var content = ""
|
||||||
|
images.forEach { img in
|
||||||
|
if staticVar {
|
||||||
|
content += "\n\(img.getStaticImageProperty())"
|
||||||
|
} else {
|
||||||
|
content += "\n\(img.getImageProperty())"
|
||||||
|
}
|
||||||
|
content += "\n "
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create file if not exists
|
||||||
|
let fileManager = FileManager()
|
||||||
|
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||||
|
Shell.shell("touch", "\(extensionFilePath)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate extension
|
||||||
|
Self.generateExtensionFile(extensionFilePath: extensionFilePath, extensionHeader, extensionContent, extensionFooter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - pragm
|
||||||
|
|
||||||
|
private static func generateExtensionFile(extensionFilePath: String, _ args: String...) {
|
||||||
|
// Create file if not exists
|
||||||
|
let fileManager = FileManager()
|
||||||
|
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||||
|
Shell.shell("touch", "\(extensionFilePath)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create extension content
|
||||||
|
let extensionContent = args.joined(separator: "\n")
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = ImagiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getHeader(inputFilename: String, extensionClassname: String) -> String {
|
||||||
|
"""
|
||||||
|
// Generated from Imagium at \(Date())
|
||||||
|
// Images from \(inputFilename)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension \(extensionClassname) {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getFooter() -> String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//@objc var onboarding_foreground3: UIImage {
|
||||||
|
// return UIImage(named: "onboarding_foreground3")!
|
||||||
|
// }
|
47
Sources/Imagium/ImageFileParser.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// ImageFileParser.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ImageFileParser {
|
||||||
|
|
||||||
|
static func parse(_ inputFile: String, platform: PlatormTag) -> [ImageToGen] {
|
||||||
|
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||||
|
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||||
|
|
||||||
|
var imagesToGenerate = [ImageToGen]()
|
||||||
|
|
||||||
|
// Parse file
|
||||||
|
stringsByLines.forEach {
|
||||||
|
guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let splittedLine = $0.split(separator: " ")
|
||||||
|
|
||||||
|
let width: Int = {
|
||||||
|
if splittedLine[2] == "?" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return Int(splittedLine[2])!
|
||||||
|
}()
|
||||||
|
let height: Int = {
|
||||||
|
if splittedLine[3] == "?" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return Int(splittedLine[3])!
|
||||||
|
}()
|
||||||
|
|
||||||
|
let image = ImageToGen(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height)
|
||||||
|
imagesToGenerate.append(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagesToGenerate.filter {
|
||||||
|
$0.tags.contains(platform.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
Sources/Imagium/ImageToGen.swift
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct ImageToGen {
|
||||||
|
let name: String
|
||||||
|
let tags: String
|
||||||
|
let width: Int
|
||||||
|
let height: Int
|
||||||
|
|
||||||
|
// MARK: - Convert
|
||||||
|
|
||||||
|
var convertArguments: (x1: ConvertArgument, x2: ConvertArgument, x3: ConvertArgument) {
|
||||||
|
var width1x = ""
|
||||||
|
var height1x = ""
|
||||||
|
var width2x = ""
|
||||||
|
var height2x = ""
|
||||||
|
var width3x = ""
|
||||||
|
var height3x = ""
|
||||||
|
|
||||||
|
if width != -1 {
|
||||||
|
width1x = "\(width)"
|
||||||
|
width2x = "\(width * 2)"
|
||||||
|
width3x = "\(width * 3)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if height != -1 {
|
||||||
|
height1x = "\(height)"
|
||||||
|
height2x = "\(height * 2)"
|
||||||
|
height3x = "\(height * 3)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return (x1: ConvertArgument(width: width1x, height: height1x),
|
||||||
|
x2: ConvertArgument(width: width2x, height: height2x),
|
||||||
|
x3: ConvertArgument(width: width3x, height: height3x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assets
|
||||||
|
|
||||||
|
var contentJson: String {
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "\(name).\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "\(name)@2x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "\(name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension property
|
||||||
|
|
||||||
|
func getImageProperty() -> String {
|
||||||
|
"""
|
||||||
|
var \(name): UIImage {
|
||||||
|
UIImage(named: "\(name)")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStaticImageProperty() -> String {
|
||||||
|
"""
|
||||||
|
static var \(name): UIImage {
|
||||||
|
UIImage(named: "\(name)")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
43
Sources/Imagium/ImagiumError.swift
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// ImagiumError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum ImagiumError: Error {
|
||||||
|
case inputFolderNotFound(String)
|
||||||
|
case fileNotExists(String)
|
||||||
|
case unknownImageExtension(String)
|
||||||
|
case getFileAttributed(String, String)
|
||||||
|
case rsvgConvertNotFound
|
||||||
|
case writeFile(String, String)
|
||||||
|
case unknown(String)
|
||||||
|
|
||||||
|
var localizedDescription: String {
|
||||||
|
switch self {
|
||||||
|
case .inputFolderNotFound(let inputFolder):
|
||||||
|
return " error:[\(Imagium.toolName)] Input folder not found: \(inputFolder)"
|
||||||
|
|
||||||
|
case .fileNotExists(let filename):
|
||||||
|
return " error:[\(Imagium.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
|
case .unknownImageExtension(let filename):
|
||||||
|
return " error:[\(Imagium.toolName)] File \(filename) have an unhandled file extension. Cannot generate image."
|
||||||
|
|
||||||
|
case .getFileAttributed(let filename, let errorDescription):
|
||||||
|
return " error:[\(Imagium.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)"
|
||||||
|
|
||||||
|
case .rsvgConvertNotFound:
|
||||||
|
return " error:[\(Imagium.toolName)] Can't find rsvg-convert (can be installed with 'brew remove imagemagick && brew install imagemagick --with-librsvg')"
|
||||||
|
|
||||||
|
case .writeFile(let subErrorDescription, let filename):
|
||||||
|
return " error:[\(Imagium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||||
|
|
||||||
|
case .unknown(let errorDescription):
|
||||||
|
return " error:[\(Imagium.toolName)] Unknown error: \(errorDescription)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
Sources/Imagium/ImagiumOptions.swift
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// ImagiumOptions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct ImagiumOptions: ParsableArguments {
|
||||||
|
@Flag(name: .customShort("f"), help: "Should force script execution")
|
||||||
|
var forceExecution = false
|
||||||
|
|
||||||
|
@Flag(name: .customShort("F"), help: "Regenerate all images")
|
||||||
|
var forceExecutionAndGeneration = false
|
||||||
|
|
||||||
|
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var inputFile: String
|
||||||
|
|
||||||
|
@Option(help: "Xcassets path where to generate images.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var xcassetsPath: String
|
||||||
|
|
||||||
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@Option(help: "Extension name. If not specified, it will generate an UIImage extension. Using default extension name will generate static property.")
|
||||||
|
var extensionName: String = Imagium.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
|
||||||
|
var extensionSuffix: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
|
||||||
|
--xcassets-path "./Images/imagium.xcassets" \
|
||||||
|
--extension-output-path "./Images/Generated" \
|
||||||
|
--extension-name "UIImage" \
|
||||||
|
--extension-suffix "GenAllScript"
|
||||||
|
*/
|
189
Sources/Imagium/XcassetsGenerator.swift
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CLIToolCore
|
||||||
|
|
||||||
|
class XcassetsGenerator {
|
||||||
|
|
||||||
|
static let outputImageExtension = "png"
|
||||||
|
|
||||||
|
let forceGeneration: Bool
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(forceGeneration: Bool) {
|
||||||
|
self.forceGeneration = forceGeneration
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assets generation
|
||||||
|
|
||||||
|
func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) {
|
||||||
|
let fileManager = FileManager()
|
||||||
|
let svgConverter = Imagium.getSvgConverterPath()
|
||||||
|
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
||||||
|
|
||||||
|
var generatedAssetsPaths = [String]()
|
||||||
|
|
||||||
|
// Generate new assets
|
||||||
|
imagesToGenerate.forEach { imageToGen in
|
||||||
|
// Get image path
|
||||||
|
let imageData: (path: String, ext: String) = {
|
||||||
|
for subfile in allSubFiles {
|
||||||
|
if subfile.hasSuffix("/" + imageToGen.name + ".svg") {
|
||||||
|
return (subfile, "svg")
|
||||||
|
}
|
||||||
|
if subfile.hasSuffix("/" + imageToGen.name + ".png") {
|
||||||
|
return (subfile, "png")
|
||||||
|
}
|
||||||
|
if subfile.hasSuffix("/" + imageToGen.name + ".jpg") {
|
||||||
|
return (subfile, "jpg")
|
||||||
|
}
|
||||||
|
if subfile.hasSuffix("/" + imageToGen.name + ".jepg") {
|
||||||
|
return (subfile, "jepg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let error = ImagiumError.unknownImageExtension(imageToGen.name)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create imageset folder
|
||||||
|
let imagesetName = "\(imageToGen.name).imageset"
|
||||||
|
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
||||||
|
Shell.shell("mkdir", "-p", imagesetPath)
|
||||||
|
|
||||||
|
// Store managed images path
|
||||||
|
generatedAssetsPaths.append(imagesetName)
|
||||||
|
|
||||||
|
// Generate output images path
|
||||||
|
let output1x = "\(imagesetPath)/\(imageToGen.name).\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
let output2x = "\(imagesetPath)/\(imageToGen.name)@2x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
let output3x = "\(imagesetPath)/\(imageToGen.name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
|
||||||
|
// Check if we need to convert image
|
||||||
|
if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) {
|
||||||
|
print("\(imageToGen.name) -> Not regenerating")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert image
|
||||||
|
let convertArguments = imageToGen.convertArguments
|
||||||
|
if imageData.ext == "svg" {
|
||||||
|
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png
|
||||||
|
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png
|
||||||
|
// /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
|
||||||
|
var command1x = ["\(svgConverter)", "\(imageData.path)"]
|
||||||
|
var command2x = ["\(svgConverter)", "\(imageData.path)"]
|
||||||
|
var command3x = ["\(svgConverter)", "\(imageData.path)"]
|
||||||
|
|
||||||
|
self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
|
||||||
|
self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
|
||||||
|
self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
|
||||||
|
|
||||||
|
command1x.append(contentsOf: ["-o", output1x])
|
||||||
|
command2x.append(contentsOf: ["-o", output2x])
|
||||||
|
command3x.append(contentsOf: ["-o", output3x])
|
||||||
|
|
||||||
|
Shell.shell(command1x)
|
||||||
|
Shell.shell(command2x)
|
||||||
|
Shell.shell(command3x)
|
||||||
|
} else {
|
||||||
|
// convert path/to/image.png -resize 200x300 path/to/output.png
|
||||||
|
// convert path/to/image.png -resize 200x path/to/output.png
|
||||||
|
// convert path/to/image.png -resize x300 path/to/output.png
|
||||||
|
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x)
|
||||||
|
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x)
|
||||||
|
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Content.json
|
||||||
|
let imagesetContentJson = imageToGen.contentJson
|
||||||
|
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
||||||
|
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
|
||||||
|
Shell.shell("touch", "\(contentJsonFilePath)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
||||||
|
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
|
||||||
|
|
||||||
|
print("\(imageToGen.name) -> Generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success info
|
||||||
|
let generatedAssetsCount = generatedAssetsPaths.count
|
||||||
|
print("Images generated: \(generatedAssetsCount)")
|
||||||
|
|
||||||
|
// Delete old assets
|
||||||
|
let allImagesetName = Set(fileManager.getAllImageSetFolderIn(directory: xcassetsPath))
|
||||||
|
let imagesetToRemove = allImagesetName.subtracting(Set(generatedAssetsPaths))
|
||||||
|
|
||||||
|
imagesetToRemove.forEach {
|
||||||
|
print("Will remove: \($0)")
|
||||||
|
}
|
||||||
|
|
||||||
|
imagesetToRemove.forEach { itemToRemove in
|
||||||
|
try! fileManager.removeItem(atPath: "\(xcassetsPath)/\(itemToRemove)")
|
||||||
|
}
|
||||||
|
print("Removed \(imagesetToRemove.count) images")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers: SVG command
|
||||||
|
|
||||||
|
private func addConvertArgument(command: inout [String], convertArgument: ConvertArgument) {
|
||||||
|
if let width = convertArgument.width, width.isEmpty == false {
|
||||||
|
command.append("-w")
|
||||||
|
command.append("\(width)")
|
||||||
|
}
|
||||||
|
if let height = convertArgument.height, height.isEmpty == false {
|
||||||
|
command.append("-h")
|
||||||
|
command.append("\(height)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers: bypass generation
|
||||||
|
|
||||||
|
private func shouldBypassGeneration(for image: ImageToGen, xcassetImagePath: String) -> Bool {
|
||||||
|
guard forceGeneration == false else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// File not exists -> do not bypass
|
||||||
|
guard fileManager.fileExists(atPath: xcassetImagePath) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info unavailable -> do not bypass
|
||||||
|
let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath)
|
||||||
|
let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath)
|
||||||
|
guard taskWidth.terminationStatus == 0,
|
||||||
|
taskHeight.terminationStatus == 0 else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentWidth = Int(taskWidth.output ?? "-1") ?? -1
|
||||||
|
let currentheight = Int(taskHeight.output ?? "-1") ?? -1
|
||||||
|
|
||||||
|
// Info unavailable -> do not bypass
|
||||||
|
guard currentWidth > 0 && currentheight > 0 else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check width and height
|
||||||
|
if image.width != -1 && currentWidth == image.width {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if image.height != -1 && currentheight == image.height {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
111
Sources/Imagium/main.swift
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
//
|
||||||
|
// Imagium.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
import CLIToolCore
|
||||||
|
|
||||||
|
enum PlatormTag: String {
|
||||||
|
case droid = "d"
|
||||||
|
case ios = "i"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Imagium: ParsableCommand {
|
||||||
|
|
||||||
|
static var configuration = CommandConfiguration(
|
||||||
|
abstract: "A utility for generate images.",
|
||||||
|
version: "0.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
static let toolName = "Imagium"
|
||||||
|
static let defaultExtensionName = "UIImage"
|
||||||
|
|
||||||
|
var extensionFileName: String { "\(options.extensionName)+Image\(options.extensionSuffix).swift" }
|
||||||
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
|
var inputFilenameWithoutExt: String {
|
||||||
|
URL(fileURLWithPath: options.inputFile)
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptionGroup var options: ImagiumOptions
|
||||||
|
|
||||||
|
mutating func run() {
|
||||||
|
print("[\(Self.toolName)] Starting images generation")
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Will generate images")
|
||||||
|
|
||||||
|
// Parse input file
|
||||||
|
let imagesToGenerate = ImageFileParser.parse(options.inputFile, platform: PlatormTag.ios)
|
||||||
|
|
||||||
|
// Generate xcassets files
|
||||||
|
let inputFolder = URL(fileURLWithPath: options.inputFile)
|
||||||
|
.deletingLastPathComponent()
|
||||||
|
.relativePath
|
||||||
|
let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration)
|
||||||
|
xcassetsGenerator.generateXcassets(inputPath: inputFolder,
|
||||||
|
imagesToGenerate: imagesToGenerate,
|
||||||
|
xcassetsPath: options.xcassetsPath)
|
||||||
|
|
||||||
|
// Generate extension
|
||||||
|
ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate,
|
||||||
|
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||||
|
inputFilename: inputFilenameWithoutExt,
|
||||||
|
extensionName: options.extensionName,
|
||||||
|
extensionFilePath: extensionFilePath)
|
||||||
|
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Images generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Requirements
|
||||||
|
|
||||||
|
private func checkRequirements() -> Bool {
|
||||||
|
guard options.forceExecutionAndGeneration == false else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Input file
|
||||||
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
|
let error = ImagiumError.fileNotExists(options.inputFile)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSVG-Converter
|
||||||
|
_ = Imagium.getSvgConverterPath()
|
||||||
|
|
||||||
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||||
|
print("[\(Self.toolName)] Images are already up to date :) ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
static func getSvgConverterPath() -> String {
|
||||||
|
let taskSvgConverter = Shell.shell("which", "rsvg-convert")
|
||||||
|
if taskSvgConverter.terminationStatus == 0 {
|
||||||
|
return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines)
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = ImagiumError.rsvgConvertNotFound
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Imagium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Imagium.main()
|
@ -1 +1,13 @@
|
|||||||
print("Welcome ResgenSwift")
|
print("Welcome ResgenSwift")
|
||||||
|
|
||||||
|
/*
|
||||||
|
Make resgen as main command and font/color... as subcommands. It could look like:
|
||||||
|
Resgen
|
||||||
|
-> ColorTool
|
||||||
|
-> FontTool
|
||||||
|
-> ImageTool
|
||||||
|
-> Strings
|
||||||
|
-> Twine
|
||||||
|
-> Stringium
|
||||||
|
-> Tag
|
||||||
|
*/
|
||||||
|
@ -8,38 +8,37 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import CLIToolCore
|
import CLIToolCore
|
||||||
|
|
||||||
extension Strings {
|
class StringsFileGenerator {
|
||||||
class StringsFileGenerator {
|
|
||||||
|
|
||||||
// MARK: - Strings Files
|
// MARK: - Strings Files
|
||||||
|
|
||||||
static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
|
static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
|
||||||
var stringsFilesContent = [String: String]()
|
var stringsFilesContent = [String: String]()
|
||||||
for lang in langs {
|
for lang in langs {
|
||||||
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
|
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
|
||||||
defaultLang: defaultLang,
|
defaultLang: defaultLang,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
sections: sections)
|
sections: sections)
|
||||||
}
|
|
||||||
|
|
||||||
// Write strings file content
|
|
||||||
langs.forEach { lang in
|
|
||||||
guard let fileContent = stringsFilesContent[lang] else { return }
|
|
||||||
|
|
||||||
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
|
||||||
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
|
||||||
do {
|
|
||||||
try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
|
|
||||||
} catch (let error) {
|
|
||||||
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
// Write strings file content
|
||||||
var stringsFileContent = """
|
langs.forEach { lang in
|
||||||
|
guard let fileContent = stringsFilesContent[lang] else { return }
|
||||||
|
|
||||||
|
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
||||||
|
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
||||||
|
do {
|
||||||
|
try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
||||||
|
var stringsFileContent = """
|
||||||
/**
|
/**
|
||||||
* Apple Strings File
|
* Apple Strings File
|
||||||
* Generated by ResgenSwift 1.0.0
|
* Generated by ResgenSwift 1.0.0
|
||||||
@ -47,82 +46,82 @@ extension Strings {
|
|||||||
*/\n
|
*/\n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
sections.forEach { section in
|
||||||
|
// Check that at least one string will be generated
|
||||||
|
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
|
||||||
|
return // Go to next section
|
||||||
|
}
|
||||||
|
|
||||||
|
stringsFileContent += "\n/********** \(section.name) **********/\n\n"
|
||||||
|
section.definitions.forEach { definition in
|
||||||
|
let translationOpt: String? = {
|
||||||
|
if definition.tags.contains(Stringium.noTranslationTag) {
|
||||||
|
return definition.translations[defaultLang]
|
||||||
|
}
|
||||||
|
return definition.translations[lang]
|
||||||
|
}()
|
||||||
|
|
||||||
|
if let translation = translationOpt {
|
||||||
|
stringsFileContent += "\"\(definition.name)\" = \"\(translation)\";\n\n"
|
||||||
|
} else {
|
||||||
|
let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringsFileContent
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension file
|
||||||
|
|
||||||
|
static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
||||||
|
let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
|
||||||
|
let extensionFooter = Self.getFooter()
|
||||||
|
|
||||||
|
let extensionContent: String = {
|
||||||
|
var content = ""
|
||||||
sections.forEach { section in
|
sections.forEach { section in
|
||||||
// Check that at least one string will be generated
|
// Check that at least one string will be generated
|
||||||
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
|
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||||
return // Go to next section
|
return // Go to next section
|
||||||
}
|
}
|
||||||
|
|
||||||
stringsFileContent += "\n/********** \(section.name) **********/\n\n"
|
content += "\n\t// MARK: - \(section.name)"
|
||||||
section.definitions.forEach { definition in
|
section.definitions.forEach { definition in
|
||||||
let translationOpt: String? = {
|
if staticVar {
|
||||||
if definition.tags.contains(Stringium.noTranslationTag) {
|
content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
|
||||||
return definition.translations[defaultLang]
|
|
||||||
}
|
|
||||||
return definition.translations[lang]
|
|
||||||
}()
|
|
||||||
|
|
||||||
if let translation = translationOpt {
|
|
||||||
stringsFileContent += "\"\(definition.name)\" = \"\(translation)\"\n\n"
|
|
||||||
} else {
|
} else {
|
||||||
let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
|
content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
content += "\n"
|
||||||
}
|
}
|
||||||
|
return content
|
||||||
|
}()
|
||||||
|
|
||||||
return stringsFileContent
|
// Create file if not exists
|
||||||
|
let fileManager = FileManager()
|
||||||
|
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||||
|
Shell.shell("touch", "\(extensionFilePath)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Extension file
|
// Create extension content
|
||||||
|
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||||
|
|
||||||
static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
// Write content
|
||||||
let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
let extensionFooter = Self.getFooter()
|
do {
|
||||||
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||||
let extensionContent: String = {
|
} catch (let error) {
|
||||||
var content = ""
|
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
sections.forEach { section in
|
print(error.localizedDescription)
|
||||||
// Check that at least one string will be generated
|
Stringium.exit(withError: error)
|
||||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
|
||||||
return // Go to next section
|
|
||||||
}
|
|
||||||
|
|
||||||
content += "\n\t// MARK: - \(section.name)"
|
|
||||||
section.definitions.forEach { definition in
|
|
||||||
if staticVar {
|
|
||||||
content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
|
|
||||||
} else {
|
|
||||||
content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
content += "\n"
|
|
||||||
}
|
|
||||||
return content
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create file if not exists
|
|
||||||
let fileManager = FileManager()
|
|
||||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
|
||||||
Shell.shell("touch", "\(extensionFilePath)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create extension content
|
|
||||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
|
||||||
|
|
||||||
// Write content
|
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
|
||||||
do {
|
|
||||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
|
||||||
} catch (let error) {
|
|
||||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated from Strings-Stringium at \(Date())
|
// Generated from Strings-Stringium at \(Date())
|
||||||
|
|
||||||
@ -132,12 +131,11 @@ extension Strings {
|
|||||||
|
|
||||||
extension \(extensionClassname) {
|
extension \(extensionClassname) {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
private static func getFooter() -> String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,54 +9,53 @@ import Foundation
|
|||||||
import CLIToolCore
|
import CLIToolCore
|
||||||
import CoreVideo
|
import CoreVideo
|
||||||
|
|
||||||
extension Strings {
|
class TagsGenerator {
|
||||||
class TagsGenerator {
|
static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||||
static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
let extensionHeader = Self.getHeader(extensionClassname: extensionName)
|
||||||
let extensionHeader = Self.getHeader(extensionClassname: extensionName)
|
let extensionFooter = Self.getFooter()
|
||||||
let extensionFooter = Self.getFooter()
|
|
||||||
|
|
||||||
let extensionContent: String = {
|
let extensionContent: String = {
|
||||||
var content = ""
|
var content = ""
|
||||||
sections.forEach { section in
|
sections.forEach { section in
|
||||||
// Check that at least one string will be generated
|
// Check that at least one string will be generated
|
||||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||||
return // Go to next section
|
return // Go to next section
|
||||||
}
|
|
||||||
|
|
||||||
content += "\n\t// MARK: - \(section.name)"
|
|
||||||
section.definitions.forEach { definition in
|
|
||||||
if staticVar {
|
|
||||||
content += "\n\n\(definition.getStaticProperty(forLang: lang))"
|
|
||||||
} else {
|
|
||||||
content += "\n\n\(definition.getProperty(forLang: lang))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
content += "\n"
|
|
||||||
}
|
}
|
||||||
return content
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create file if not exists
|
content += "\n\t// MARK: - \(section.name)"
|
||||||
let fileManager = FileManager()
|
section.definitions.forEach { definition in
|
||||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
if staticVar {
|
||||||
Shell.shell("touch", "\(extensionFilePath)")
|
content += "\n\n\(definition.getStaticProperty(forLang: lang))"
|
||||||
|
} else {
|
||||||
|
content += "\n\n\(definition.getProperty(forLang: lang))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content += "\n"
|
||||||
}
|
}
|
||||||
|
return content
|
||||||
|
}()
|
||||||
|
|
||||||
// Create extension content
|
// Create file if not exists
|
||||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
let fileManager = FileManager()
|
||||||
|
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||||
// Write content
|
Shell.shell("touch", "\(extensionFilePath)")
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
|
||||||
do {
|
|
||||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
|
||||||
} catch (let error) {
|
|
||||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
// Create extension content
|
||||||
|
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getHeader(extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated from Strings-Tags at \(Date())
|
// Generated from Strings-Tags at \(Date())
|
||||||
|
|
||||||
@ -66,12 +65,11 @@ extension Strings {
|
|||||||
|
|
||||||
extension \(extensionClassname) {
|
extension \(extensionClassname) {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
private static func getFooter() -> String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,102 +7,100 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Strings {
|
class Definition {
|
||||||
class Definition {
|
let name: String
|
||||||
let name: String
|
var tags = [String]()
|
||||||
var tags = [String]()
|
var comment: String?
|
||||||
var comment: String?
|
var translations = [String: String]()
|
||||||
var translations = [String: String]()
|
var reference: String?
|
||||||
var reference: String?
|
var isPlurals = false
|
||||||
var isPlurals = false
|
|
||||||
|
|
||||||
var isValid: Bool {
|
var isValid: Bool {
|
||||||
name.isEmpty == false &&
|
name.isEmpty == false &&
|
||||||
translations.isEmpty == false
|
translations.isEmpty == false
|
||||||
|
}
|
||||||
|
|
||||||
|
init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
static func match(_ line: String) -> Definition? {
|
||||||
|
guard line.range(of: "\\[(.*?)]$", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
init(name: String) {
|
let definitionName = line
|
||||||
self.name = name
|
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||||
|
.removeLeadingTrailingWhitespace()
|
||||||
|
|
||||||
|
return Definition(name: definitionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
func getNSLocalizedStringProperty(forLang lang: String) -> String {
|
||||||
|
guard let translation = translations[lang] else {
|
||||||
|
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func match(_ line: String) -> Definition? {
|
return """
|
||||||
guard line.range(of: "\\[(.*?)]", options: .regularExpression, range: nil, locale: nil) != nil else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let definitionName = line
|
|
||||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
|
||||||
.removeLeadingTrailingWhitespace()
|
|
||||||
|
|
||||||
return Definition(name: definitionName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
func getNSLocalizedStringProperty(forLang lang: String) -> String {
|
|
||||||
guard let translation = translations[lang] else {
|
|
||||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return """
|
|
||||||
/// Translation in \(lang) :
|
/// Translation in \(lang) :
|
||||||
/// \(translation)
|
/// \(translation)
|
||||||
var \(name): String {
|
var \(name): String {
|
||||||
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
|
||||||
|
guard let translation = translations[lang] else {
|
||||||
|
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
|
return """
|
||||||
guard let translation = translations[lang] else {
|
|
||||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return """
|
|
||||||
/// Translation in \(lang) :
|
/// Translation in \(lang) :
|
||||||
/// \(translation)
|
/// \(translation)
|
||||||
static var \(name): String {
|
static var \(name): String {
|
||||||
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Raw strings
|
||||||
|
|
||||||
|
func getProperty(forLang lang: String) -> String {
|
||||||
|
guard let translation = translations[lang] else {
|
||||||
|
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Raw strings
|
return """
|
||||||
|
|
||||||
func getProperty(forLang lang: String) -> String {
|
|
||||||
guard let translation = translations[lang] else {
|
|
||||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return """
|
|
||||||
/// Translation in \(lang) :
|
/// Translation in \(lang) :
|
||||||
/// \(translation)
|
/// \(translation)
|
||||||
var \(name): String {
|
var \(name): String {
|
||||||
"\(translation)"
|
"\(translation)"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStaticProperty(forLang lang: String) -> String {
|
||||||
|
guard let translation = translations[lang] else {
|
||||||
|
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStaticProperty(forLang lang: String) -> String {
|
return """
|
||||||
guard let translation = translations[lang] else {
|
|
||||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return """
|
|
||||||
/// Translation in \(lang) :
|
/// Translation in \(lang) :
|
||||||
/// \(translation)
|
/// \(translation)
|
||||||
static var \(name): String {
|
static var \(name): String {
|
||||||
"\(translation)"
|
"\(translation)"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,35 +7,33 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Strings {
|
class Section {
|
||||||
class Section {
|
let name: String // OnBoarding
|
||||||
let name: String // OnBoarding
|
var definitions = [Definition]()
|
||||||
var definitions = [Definition]()
|
|
||||||
|
|
||||||
init(name: String) {
|
init(name: String) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
static func match(_ line: String) -> Section? {
|
||||||
|
guard line.range(of: "\\[\\[(.*?)]]$", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
static func match(_ line: String) -> Section? {
|
let sectionName = line
|
||||||
guard line.range(of: "\\[\\[(.*?)]]", options: .regularExpression, range: nil, locale: nil) != nil else {
|
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||||
return nil
|
.removeLeadingTrailingWhitespace()
|
||||||
|
return Section(name: sectionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
||||||
|
let allTags = definitions.flatMap { $0.tags }
|
||||||
|
|
||||||
|
for tag in tags {
|
||||||
|
if allTags.contains(tag) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let sectionName = line
|
|
||||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
|
||||||
.removeLeadingTrailingWhitespace()
|
|
||||||
return Section(name: sectionName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
|
||||||
let allTags = definitions.flatMap { $0.tags }
|
|
||||||
|
|
||||||
for tag in tags {
|
|
||||||
if allTags.contains(tag) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,88 +7,91 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Strings {
|
class TwineFileParser {
|
||||||
|
static func parse(_ inputFile: String) -> [Section] {
|
||||||
|
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||||
|
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||||
|
|
||||||
class TwineFileParser {
|
var sections = [Section]()
|
||||||
static func parse(_ inputFile: String) -> [Section] {
|
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
|
||||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
|
||||||
|
|
||||||
var sections = [Section]()
|
// Parse file
|
||||||
|
stringsByLines.forEach {
|
||||||
|
// Section
|
||||||
|
if let section = Section.match($0) {
|
||||||
|
sections.append(section)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Parse file
|
// Definition
|
||||||
stringsByLines.forEach {
|
if let definition = Definition.match($0) {
|
||||||
// Section
|
sections.last?.definitions.append(definition)
|
||||||
if let section = Section.match($0) {
|
return
|
||||||
sections.append(section)
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Definition
|
// Definition content
|
||||||
if let definition = Definition.match($0) {
|
if $0.isEmpty == false {
|
||||||
sections.last?.definitions.append(definition)
|
// fr = Test => ["fr ", " Test"]
|
||||||
return
|
let splitLine = $0
|
||||||
}
|
.removeLeadingTrailingWhitespace()
|
||||||
|
.split(separator: "=")
|
||||||
|
|
||||||
// Definition content
|
guard let lastDefinition = sections.last?.definitions.last,
|
||||||
if $0.isEmpty == false {
|
let leftElement = splitLine.first else {
|
||||||
// fr = Test => ["fr ", " Test"]
|
return
|
||||||
let splitLine = $0
|
}
|
||||||
.removeLeadingTrailingWhitespace()
|
|
||||||
.split(separator: "=")
|
|
||||||
|
|
||||||
guard let lastDefinition = sections.last?.definitions.last,
|
let rightElement: String = {
|
||||||
let leftElement = splitLine.first,
|
if let last = splitLine.last, splitLine.count == 2 {
|
||||||
let rightElement = splitLine.last else {
|
return String(last)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// "fr " => "fr"
|
|
||||||
let leftHand = String(leftElement.dropLast())
|
|
||||||
// " Test" => "Test"
|
|
||||||
let rightHand = String(rightElement.dropFirst())
|
|
||||||
|
|
||||||
// Handle comments, tags and translation
|
|
||||||
switch leftHand {
|
|
||||||
case "comments":
|
|
||||||
lastDefinition.comment = rightHand
|
|
||||||
|
|
||||||
case "tags":
|
|
||||||
lastDefinition.tags = rightHand
|
|
||||||
.split(separator: ",")
|
|
||||||
.map { String($0) }
|
|
||||||
|
|
||||||
case "ref":
|
|
||||||
lastDefinition.reference = rightHand
|
|
||||||
|
|
||||||
default:
|
|
||||||
lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
|
|
||||||
// Is a plurals strings (fr:one = Test)
|
|
||||||
// Will be handle later
|
|
||||||
//if leftHand.split(separator: ":").count > 1 {
|
|
||||||
// lastDefinition.isPlurals = true
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
|
}()
|
||||||
|
|
||||||
|
// "fr " => "fr"
|
||||||
|
let leftHand = String(leftElement).removeTrailingWhitespace()
|
||||||
|
// " Test" => "Test"
|
||||||
|
let rightHand = String(rightElement).removeLeadingWhitespace()
|
||||||
|
|
||||||
|
// Handle comments, tags and translation
|
||||||
|
switch leftHand {
|
||||||
|
case "comments":
|
||||||
|
lastDefinition.comment = rightHand
|
||||||
|
|
||||||
|
case "tags":
|
||||||
|
lastDefinition.tags = rightHand
|
||||||
|
.split(separator: ",")
|
||||||
|
.map { String($0) }
|
||||||
|
|
||||||
|
case "ref":
|
||||||
|
lastDefinition.reference = rightHand
|
||||||
|
|
||||||
|
default:
|
||||||
|
lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
|
||||||
|
// Is a plurals strings (fr:one = Test)
|
||||||
|
// Will be handle later
|
||||||
|
//if leftHand.split(separator: ":").count > 1 {
|
||||||
|
// lastDefinition.isPlurals = true
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep only valid definition
|
|
||||||
var invalidDefinitionNames = [String]()
|
|
||||||
sections.forEach { section in
|
|
||||||
section.definitions = section.definitions
|
|
||||||
.filter {
|
|
||||||
if $0.isValid == false {
|
|
||||||
invalidDefinitionNames.append($0.name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if invalidDefinitionNames.count > 0 {
|
|
||||||
print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
|
|
||||||
}
|
|
||||||
|
|
||||||
return sections
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep only valid definition
|
||||||
|
var invalidDefinitionNames = [String]()
|
||||||
|
sections.forEach { section in
|
||||||
|
section.definitions = section.definitions
|
||||||
|
.filter {
|
||||||
|
if $0.isValid == false {
|
||||||
|
invalidDefinitionNames.append($0.name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if invalidDefinitionNames.count > 0 {
|
||||||
|
print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
|
||||||
|
}
|
||||||
|
|
||||||
|
return sections
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,103 +9,100 @@ import Foundation
|
|||||||
import CLIToolCore
|
import CLIToolCore
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
extension Strings {
|
struct Stringium: ParsableCommand {
|
||||||
|
static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
|
||||||
|
|
||||||
struct Stringium: ParsableCommand {
|
static let toolName = "Stringium"
|
||||||
static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
|
static let defaultExtensionName = "String"
|
||||||
|
static let noTranslationTag: String = "notranslation"
|
||||||
|
|
||||||
static let toolName = "Stringium"
|
var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).swift" }
|
||||||
static let defaultExtensionName = "String"
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
static let noTranslationTag: String = "notranslation"
|
|
||||||
|
|
||||||
var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).swift" }
|
var langs: [String] {
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
options.langsRaw
|
||||||
|
.split(separator: " ")
|
||||||
var langs: [String] {
|
.map { String($0) }
|
||||||
options.langsRaw
|
}
|
||||||
.split(separator: " ")
|
var inputFilenameWithoutExt: String {
|
||||||
.map { String($0) }
|
URL(fileURLWithPath: options.inputFile)
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
var stringsFileOutputPath: String {
|
||||||
|
var outputPath = options.outputPathRaw
|
||||||
|
if outputPath.last == "/" {
|
||||||
|
outputPath = String(outputPath.dropLast())
|
||||||
}
|
}
|
||||||
var inputFilenameWithoutExt: String {
|
return outputPath
|
||||||
URL(fileURLWithPath: options.inputFile)
|
}
|
||||||
.deletingPathExtension()
|
|
||||||
.lastPathComponent
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
}
|
// arguments defined by another `ParsableArguments` type.
|
||||||
var stringsFileOutputPath: String {
|
@OptionGroup var options: StringiumOptions
|
||||||
var outputPath = options.outputPathRaw
|
|
||||||
if outputPath.last == "/" {
|
mutating func run() {
|
||||||
outputPath = String(outputPath.dropLast())
|
print("[\(Self.toolName)] Starting strings generation")
|
||||||
}
|
|
||||||
return outputPath
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Will generate strings")
|
||||||
|
|
||||||
|
// Parse input file
|
||||||
|
let sections = TwineFileParser.parse(options.inputFile)
|
||||||
|
|
||||||
|
// Generate strings files
|
||||||
|
StringsFileGenerator.writeStringsFiles(sections: sections,
|
||||||
|
langs: langs,
|
||||||
|
defaultLang: options.defaultLang,
|
||||||
|
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||||
|
outputPath: stringsFileOutputPath,
|
||||||
|
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
||||||
|
|
||||||
|
// Generate extension
|
||||||
|
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
||||||
|
defaultLang: options.defaultLang,
|
||||||
|
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||||
|
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||||
|
inputFilename: inputFilenameWithoutExt,
|
||||||
|
extensionName: options.extensionName,
|
||||||
|
extensionFilePath: extensionFilePath)
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Strings generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Requirements
|
||||||
|
|
||||||
|
private func checkRequirements() -> Bool {
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Input file
|
||||||
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
|
let error = StringiumError.fileNotExists(options.inputFile)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
// Langs
|
||||||
// arguments defined by another `ParsableArguments` type.
|
guard langs.isEmpty == false else {
|
||||||
@OptionGroup var options: StringiumOptions
|
let error = StringiumError.langsListEmpty
|
||||||
|
print(error.localizedDescription)
|
||||||
mutating func run() {
|
Stringium.exit(withError: error)
|
||||||
print("[\(Self.toolName)] Starting strings generation")
|
|
||||||
|
|
||||||
// Check requirements
|
|
||||||
guard checkRequirements() else { return }
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate strings")
|
|
||||||
|
|
||||||
// Parse input file
|
|
||||||
let sections = TwineFileParser.parse(options.inputFile)
|
|
||||||
|
|
||||||
// Generate strings files
|
|
||||||
StringsFileGenerator.writeStringsFiles(sections: sections,
|
|
||||||
langs: langs,
|
|
||||||
defaultLang: options.defaultLang,
|
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
|
||||||
outputPath: stringsFileOutputPath,
|
|
||||||
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
|
||||||
|
|
||||||
// Generate extension
|
|
||||||
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
|
||||||
defaultLang: options.defaultLang,
|
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
|
||||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
|
||||||
inputFilename: inputFilenameWithoutExt,
|
|
||||||
extensionName: options.extensionName,
|
|
||||||
extensionFilePath: extensionFilePath)
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Strings generated")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Requirements
|
guard langs.contains(options.defaultLang) else {
|
||||||
|
let error = StringiumError.defaultLangsNotInLangs
|
||||||
private func checkRequirements() -> Bool {
|
print(error.localizedDescription)
|
||||||
let fileManager = FileManager()
|
Stringium.exit(withError: error)
|
||||||
|
|
||||||
// Input file
|
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
|
||||||
let error = StringiumError.fileNotExists(options.inputFile)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Langs
|
|
||||||
guard langs.isEmpty == false else {
|
|
||||||
let error = StringiumError.langsListEmpty
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard langs.contains(options.defaultLang) else {
|
|
||||||
let error = StringiumError.defaultLangsNotInLangs
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if needed to regenerate
|
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
|
||||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||||
|
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,34 +7,32 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Strings {
|
enum StringiumError: Error {
|
||||||
enum StringiumError: Error {
|
case fileNotExists(String)
|
||||||
case fileNotExists(String)
|
case langsListEmpty
|
||||||
case langsListEmpty
|
case defaultLangsNotInLangs
|
||||||
case defaultLangsNotInLangs
|
case writeFile(String, String)
|
||||||
case writeFile(String, String)
|
case langNotDefined(String, String, Bool)
|
||||||
case langNotDefined(String, String, Bool)
|
|
||||||
|
|
||||||
var localizedDescription: String {
|
var localizedDescription: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .fileNotExists(let filename):
|
case .fileNotExists(let filename):
|
||||||
return " error:[\(Stringium.toolName)] File \(filename) does not exists "
|
return " error:[\(Stringium.toolName)] File \(filename) does not exists "
|
||||||
|
|
||||||
case .langsListEmpty:
|
case .langsListEmpty:
|
||||||
return " error:[\(Stringium.toolName)] Langs list is empty"
|
return " error:[\(Stringium.toolName)] Langs list is empty"
|
||||||
|
|
||||||
case .defaultLangsNotInLangs:
|
case .defaultLangsNotInLangs:
|
||||||
return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
|
return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
|
||||||
|
|
||||||
case .writeFile(let subErrorDescription, let filename):
|
case .writeFile(let subErrorDescription, let filename):
|
||||||
return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||||
|
|
||||||
case .langNotDefined(let lang, let definitionName, let isReference):
|
case .langNotDefined(let lang, let definitionName, let isReference):
|
||||||
if isReference {
|
if isReference {
|
||||||
return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
|
return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
|
||||||
}
|
|
||||||
return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
|
|
||||||
}
|
}
|
||||||
|
return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,30 +8,28 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
extension Strings {
|
struct StringiumOptions: ParsableArguments {
|
||||||
struct StringiumOptions: ParsableArguments {
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
var forceGeneration = false
|
||||||
var forceGeneration = false
|
|
||||||
|
|
||||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var outputPathRaw: String
|
var outputPathRaw: String
|
||||||
|
|
||||||
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
||||||
var langsRaw: String
|
var langsRaw: String
|
||||||
|
|
||||||
@Option(help: "Default langs.")
|
@Option(help: "Default langs.")
|
||||||
var defaultLang: String
|
var defaultLang: String
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
|
@Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
|
||||||
var extensionName: String = Stringium.defaultExtensionName
|
var extensionName: String = Stringium.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+String{extensionSuffix}.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+String{extensionSuffix}.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String = ""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,63 +9,60 @@ import Foundation
|
|||||||
import CLIToolCore
|
import CLIToolCore
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
extension Strings {
|
struct Tags: ParsableCommand {
|
||||||
|
static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
|
||||||
|
|
||||||
struct Tags: ParsableCommand {
|
static let toolName = "Tags"
|
||||||
static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
|
static let defaultExtensionName = "Tags"
|
||||||
|
static let noTranslationTag: String = "notranslation"
|
||||||
|
|
||||||
static let toolName = "Tags"
|
var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
|
||||||
static let defaultExtensionName = "Tags"
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
static let noTranslationTag: String = "notranslation"
|
|
||||||
|
|
||||||
var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
// arguments defined by another `ParsableArguments` type.
|
||||||
|
@OptionGroup var options: TagsOptions
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
mutating func run() {
|
||||||
// arguments defined by another `ParsableArguments` type.
|
print("[\(Self.toolName)] Starting tagss generation")
|
||||||
@OptionGroup var options: TagsOptions
|
|
||||||
|
|
||||||
mutating func run() {
|
// Check requirements
|
||||||
print("[\(Self.toolName)] Starting tagss generation")
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
// Check requirements
|
print("[\(Self.toolName)] Will generate tags")
|
||||||
guard checkRequirements() else { return }
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate tags")
|
// Parse input file
|
||||||
|
let sections = TwineFileParser.parse(options.inputFile)
|
||||||
|
|
||||||
// Parse input file
|
// Generate extension
|
||||||
let sections = TwineFileParser.parse(options.inputFile)
|
TagsGenerator.writeExtensionFiles(sections: sections,
|
||||||
|
lang: options.lang,
|
||||||
|
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||||
|
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||||
|
extensionName: options.extensionName,
|
||||||
|
extensionFilePath: extensionFilePath)
|
||||||
|
|
||||||
// Generate extension
|
print("[\(Self.toolName)] Tags generated")
|
||||||
TagsGenerator.writeExtensionFiles(sections: sections,
|
}
|
||||||
lang: options.lang,
|
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
|
||||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
|
||||||
extensionName: options.extensionName,
|
|
||||||
extensionFilePath: extensionFilePath)
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Tags generated")
|
// MARK: - Requirements
|
||||||
|
|
||||||
|
private func checkRequirements() -> Bool {
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Input file
|
||||||
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
|
let error = StringiumError.fileNotExists(options.inputFile)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Requirements
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||||
private func checkRequirements() -> Bool {
|
print("[\(Self.toolName)] Tags are already up to date :) ")
|
||||||
let fileManager = FileManager()
|
return false
|
||||||
|
|
||||||
// Input file
|
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
|
||||||
let error = StringiumError.fileNotExists(options.inputFile)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Stringium.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if needed to regenerate
|
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
|
||||||
print("[\(Self.toolName)] Tags are already up to date :) ")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,24 +8,22 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
extension Strings {
|
struct TagsOptions: ParsableArguments {
|
||||||
struct TagsOptions: ParsableArguments {
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
var forceGeneration = false
|
||||||
var forceGeneration = false
|
|
||||||
|
|
||||||
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(help: "Lang to generate. (\"ium\" by default)")
|
@Option(help: "Lang to generate. (\"ium\" by default)")
|
||||||
var lang: String = "ium"
|
var lang: String = "ium"
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
|
@Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
|
||||||
var extensionName: String = Tags.defaultExtensionName
|
var extensionName: String = Tags.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String = ""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,89 +9,86 @@ import Foundation
|
|||||||
import CLIToolCore
|
import CLIToolCore
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
extension Strings {
|
struct Twine: ParsableCommand {
|
||||||
|
static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
|
||||||
|
|
||||||
struct Twine: ParsableCommand {
|
static let toolName = "Twine"
|
||||||
static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
|
static let defaultExtensionName = "String"
|
||||||
|
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
||||||
|
|
||||||
static let toolName = "Twine"
|
var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } }
|
||||||
static let defaultExtensionName = "String"
|
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
||||||
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
|
||||||
var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } }
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
// arguments defined by another `ParsableArguments` type.
|
||||||
.deletingPathExtension()
|
@OptionGroup var options: TwineOptions
|
||||||
.lastPathComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
mutating func run() {
|
||||||
// arguments defined by another `ParsableArguments` type.
|
print("[\(Self.toolName)] Starting strings generation")
|
||||||
@OptionGroup var options: TwineOptions
|
|
||||||
|
|
||||||
mutating func run() {
|
// Check requirements
|
||||||
print("[\(Self.toolName)] Starting strings generation")
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
// Check requirements
|
print("[\(Self.toolName)] Will generate strings")
|
||||||
guard checkRequirements() else { return }
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate strings")
|
// Generate strings files (lproj files)
|
||||||
|
for lang in langs {
|
||||||
// Generate strings files (lproj files)
|
|
||||||
for lang in langs {
|
|
||||||
Shell.shell(Self.twineExecutable,
|
|
||||||
"generate-localization-file", options.inputFile,
|
|
||||||
"--lang", "\(lang)",
|
|
||||||
"\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
|
|
||||||
"--tags=ios,iosonly,iosOnly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate extension
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
|
|
||||||
Shell.shell(Self.twineExecutable,
|
Shell.shell(Self.twineExecutable,
|
||||||
"generate-localization-file", options.inputFile,
|
"generate-localization-file", options.inputFile,
|
||||||
"--format", "apple-swift",
|
"--lang", "\(lang)",
|
||||||
"--lang", "\(options.defaultLang)",
|
"\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
|
||||||
extensionFilePath,
|
|
||||||
"--tags=ios,iosonly,iosOnly")
|
"--tags=ios,iosonly,iosOnly")
|
||||||
|
|
||||||
print("[\(Self.toolName)] Strings generated")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Requirements
|
// Generate extension
|
||||||
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
|
||||||
|
Shell.shell(Self.twineExecutable,
|
||||||
|
"generate-localization-file", options.inputFile,
|
||||||
|
"--format", "apple-swift",
|
||||||
|
"--lang", "\(options.defaultLang)",
|
||||||
|
extensionFilePath,
|
||||||
|
"--tags=ios,iosonly,iosOnly")
|
||||||
|
|
||||||
private func checkRequirements() -> Bool {
|
print("[\(Self.toolName)] Strings generated")
|
||||||
let fileManager = FileManager()
|
}
|
||||||
|
|
||||||
// Input file
|
// MARK: - Requirements
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
|
||||||
let error = TwineError.fileNotExists(options.inputFile)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Twine.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Langs
|
private func checkRequirements() -> Bool {
|
||||||
guard langs.isEmpty == false else {
|
let fileManager = FileManager()
|
||||||
let error = TwineError.langsListEmpty
|
|
||||||
print(error.localizedDescription)
|
|
||||||
Twine.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard langs.contains(options.defaultLang) else {
|
// Input file
|
||||||
let error = TwineError.defaultLangsNotInLangs
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
print(error.localizedDescription)
|
let error = TwineError.fileNotExists(options.inputFile)
|
||||||
Twine.exit(withError: error)
|
print(error.localizedDescription)
|
||||||
}
|
Twine.exit(withError: error)
|
||||||
|
|
||||||
// "R2String+" is hardcoded in Twine formatter
|
|
||||||
let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
|
||||||
|
|
||||||
// Check if needed to regenerate
|
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
|
|
||||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Langs
|
||||||
|
guard langs.isEmpty == false else {
|
||||||
|
let error = TwineError.langsListEmpty
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Twine.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard langs.contains(options.defaultLang) else {
|
||||||
|
let error = TwineError.defaultLangsNotInLangs
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Twine.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "R2String+" is hardcoded in Twine formatter
|
||||||
|
let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
||||||
|
|
||||||
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
|
||||||
|
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,25 +7,21 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Strings {
|
enum TwineError: Error {
|
||||||
|
case fileNotExists(String)
|
||||||
|
case langsListEmpty
|
||||||
|
case defaultLangsNotInLangs
|
||||||
|
|
||||||
enum TwineError: Error {
|
var localizedDescription: String {
|
||||||
case fileNotExists(String)
|
switch self {
|
||||||
case langsListEmpty
|
case .fileNotExists(let filename):
|
||||||
case defaultLangsNotInLangs
|
return " error:[\(Twine.toolName)] File \(filename) does not exists "
|
||||||
|
|
||||||
var localizedDescription: String {
|
case .langsListEmpty:
|
||||||
switch self {
|
return " error:[\(Twine.toolName)] Langs list is empty"
|
||||||
case .fileNotExists(let filename):
|
|
||||||
return " error:[\(Twine.toolName)] File \(filename) does not exists "
|
|
||||||
|
|
||||||
case .langsListEmpty:
|
case .defaultLangsNotInLangs:
|
||||||
return " error:[\(Twine.toolName)] Langs list is empty"
|
return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
|
||||||
|
|
||||||
case .defaultLangsNotInLangs:
|
|
||||||
return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import Foundation
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct TwineOptions: ParsableArguments {
|
struct TwineOptions: ParsableArguments {
|
||||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
var forceGeneration = false
|
var forceGeneration = false
|
||||||
|
|
||||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
@ -14,7 +14,7 @@ struct Strings: ParsableCommand {
|
|||||||
abstract: "A utility for generate strings.",
|
abstract: "A utility for generate strings.",
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
|
|
||||||
// Pass an array to `subcommands` to set up a nested tree of subcommands.
|
// Pass an array to `subcommands` to set up a nested tree of subcommands.
|
||||||
// With language support for type-level introspection, this could be
|
// With language support for type-level introspection, this could be
|
||||||
// provided by automatically finding nested `ParsableCommand` types.
|
// provided by automatically finding nested `ParsableCommand` types.
|
||||||
subcommands: [Twine.self, Stringium.self, Tags.self]
|
subcommands: [Twine.self, Stringium.self, Tags.self]
|
||||||
@ -26,3 +26,4 @@ struct Strings: ParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Strings.main()
|
Strings.main()
|
||||||
|
|
||||||
|