appshot-cli 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +320 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +382 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/order.d.ts +3 -0
- package/dist/commands/order.d.ts.map +1 -0
- package/dist/commands/order.js +321 -0
- package/dist/commands/order.js.map +1 -0
- package/dist/core/compose.d.ts.map +1 -1
- package/dist/core/compose.js +71 -138
- package/dist/core/compose.js.map +1 -1
- package/dist/core/layout-utils.d.ts +12 -0
- package/dist/core/layout-utils.d.ts.map +1 -0
- package/dist/core/layout-utils.js +28 -0
- package/dist/core/layout-utils.js.map +1 -0
- package/dist/core/text-utils.d.ts +1 -1
- package/dist/core/text-utils.d.ts.map +1 -1
- package/dist/core/text-utils.js +5 -9
- package/dist/core/text-utils.js.map +1 -1
- package/dist/services/doctor.js +1 -1
- package/dist/services/export-validator.d.ts +23 -0
- package/dist/services/export-validator.d.ts.map +1 -0
- package/dist/services/export-validator.js +209 -0
- package/dist/services/export-validator.js.map +1 -0
- package/dist/services/fastlane-config-generator.d.ts +17 -0
- package/dist/services/fastlane-config-generator.d.ts.map +1 -0
- package/dist/services/fastlane-config-generator.js +343 -0
- package/dist/services/fastlane-config-generator.js.map +1 -0
- package/dist/services/fastlane-language-mapper.d.ts +33 -0
- package/dist/services/fastlane-language-mapper.d.ts.map +1 -0
- package/dist/services/fastlane-language-mapper.js +167 -0
- package/dist/services/fastlane-language-mapper.js.map +1 -0
- package/dist/services/screenshot-order.d.ts +41 -0
- package/dist/services/screenshot-order.d.ts.map +1 -0
- package/dist/services/screenshot-order.js +211 -0
- package/dist/services/screenshot-order.js.map +1 -0
- package/dist/services/screenshot-organizer.d.ts +44 -0
- package/dist/services/screenshot-organizer.d.ts.map +1 -0
- package/dist/services/screenshot-organizer.js +234 -0
- package/dist/services/screenshot-organizer.js.map +1 -0
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +49 -159
- package/dist/templates/registry.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ export declare function estimateTextWidth(text: string, fontSize: number): numbe
|
|
|
9
9
|
/**
|
|
10
10
|
* Calculate how many characters fit in a given width
|
|
11
11
|
*/
|
|
12
|
-
export declare function calculateCharsPerLine(width: number, fontSize: number
|
|
12
|
+
export declare function calculateCharsPerLine(width: number, fontSize: number): number;
|
|
13
13
|
/**
|
|
14
14
|
* Smart word wrapping algorithm
|
|
15
15
|
* Wraps text to fit within specified width, returning array of lines
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-utils.d.ts","sourceRoot":"","sources":["../../src/core/text-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"text-utils.d.ts","sourceRoot":"","sources":["../../src/core/text-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKxE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,EAAE,CAiDV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACd,GACL;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAwBrC;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GACzC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CA6BrC"}
|
package/dist/core/text-utils.js
CHANGED
|
@@ -7,8 +7,7 @@ const CHAR_WIDTH_FACTOR_NARROW = 0.5; // Character width for narrow displays (wa
|
|
|
7
7
|
// Width thresholds
|
|
8
8
|
const NARROW_WIDTH_THRESHOLD = 500; // Width below which to use narrow character factor
|
|
9
9
|
// Padding values
|
|
10
|
-
const
|
|
11
|
-
const PADDING_DEFAULT = 40; // Default padding
|
|
10
|
+
const MIN_USABLE_WIDTH = 40;
|
|
12
11
|
/**
|
|
13
12
|
* Estimate text width in pixels for SVG rendering
|
|
14
13
|
* This is an approximation based on average character widths
|
|
@@ -22,15 +21,12 @@ export function estimateTextWidth(text, fontSize) {
|
|
|
22
21
|
/**
|
|
23
22
|
* Calculate how many characters fit in a given width
|
|
24
23
|
*/
|
|
25
|
-
export function calculateCharsPerLine(width, fontSize
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const availableWidth = width - (actualPadding * 2);
|
|
29
|
-
// For watch (narrow width), use smaller character width factor since font is smaller
|
|
30
|
-
const avgCharWidth = width < NARROW_WIDTH_THRESHOLD
|
|
24
|
+
export function calculateCharsPerLine(width, fontSize) {
|
|
25
|
+
const usableWidth = Math.max(width, MIN_USABLE_WIDTH);
|
|
26
|
+
const avgCharWidth = usableWidth < NARROW_WIDTH_THRESHOLD
|
|
31
27
|
? fontSize * CHAR_WIDTH_FACTOR_NARROW
|
|
32
28
|
: fontSize * CHAR_WIDTH_FACTOR_NORMAL;
|
|
33
|
-
return Math.floor(
|
|
29
|
+
return Math.max(1, Math.floor(usableWidth / avgCharWidth));
|
|
34
30
|
}
|
|
35
31
|
/**
|
|
36
32
|
* Smart word wrapping algorithm
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-utils.js","sourceRoot":"","sources":["../../src/core/text-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8CAA8C;AAC9C,MAAM,wBAAwB,GAAG,IAAI,CAAC,CAAE,gDAAgD;AACxF,MAAM,wBAAwB,GAAG,GAAG,CAAC,CAAG,8CAA8C;AAEtF,mBAAmB;AACnB,MAAM,sBAAsB,GAAG,GAAG,CAAC,CAAK,mDAAmD;AAE3F,iBAAiB;AACjB,MAAM,
|
|
1
|
+
{"version":3,"file":"text-utils.js","sourceRoot":"","sources":["../../src/core/text-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/services/doctor.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
issues: string[];
|
|
4
|
+
warnings: string[];
|
|
5
|
+
stats?: {
|
|
6
|
+
totalScreenshots: number;
|
|
7
|
+
deviceCounts: Record<string, number>;
|
|
8
|
+
languageCounts: Record<string, number>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Validate export operation before execution
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateExport(source: string, languages: Map<string, string>, requestedDevices?: string[]): Promise<ValidationResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Validate output directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateOutputDirectory(outputPath: string, requireEmpty?: boolean): Promise<ValidationResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a path is safe to clean (not a system directory)
|
|
21
|
+
*/
|
|
22
|
+
export declare function isSafeToClean(targetPath: string): boolean;
|
|
23
|
+
//# sourceMappingURL=export-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-validator.d.ts","sourceRoot":"","sources":["../../src/services/export-validator.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE;QACN,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC,CAAC;CACH;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAmI3B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAuD3B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAqCzD"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { FASTLANE_LANGUAGES } from './fastlane-language-mapper.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validate export operation before execution
|
|
6
|
+
*/
|
|
7
|
+
export async function validateExport(source, languages, requestedDevices) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
const warnings = [];
|
|
10
|
+
const deviceCounts = {};
|
|
11
|
+
const languageCounts = {};
|
|
12
|
+
// Check source directory exists
|
|
13
|
+
try {
|
|
14
|
+
await fs.access(source);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
issues.push(`Source directory not found: ${source}`);
|
|
18
|
+
return { valid: false, issues, warnings };
|
|
19
|
+
}
|
|
20
|
+
// Check if source is the correct directory structure
|
|
21
|
+
const sourceContents = await fs.readdir(source);
|
|
22
|
+
const allDevices = ['iphone', 'ipad', 'mac', 'watch'];
|
|
23
|
+
const hasDeviceDirectories = sourceContents.some(item => allDevices.includes(item.toLowerCase()));
|
|
24
|
+
if (!hasDeviceDirectories) {
|
|
25
|
+
warnings.push(`No device directories found in ${source}. Expected: ${allDevices.join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
// Use requested devices or default to all
|
|
28
|
+
const devices = requestedDevices && requestedDevices.length > 0
|
|
29
|
+
? requestedDevices
|
|
30
|
+
: allDevices;
|
|
31
|
+
// Count screenshots and validate structure
|
|
32
|
+
let totalScreenshots = 0;
|
|
33
|
+
for (const device of devices) {
|
|
34
|
+
const deviceDir = path.join(source, device);
|
|
35
|
+
deviceCounts[device] = 0;
|
|
36
|
+
let deviceDirExists = false;
|
|
37
|
+
try {
|
|
38
|
+
await fs.access(deviceDir);
|
|
39
|
+
deviceDirExists = true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Device directory doesn't exist
|
|
43
|
+
// If this device was specifically requested, warn about it
|
|
44
|
+
if (requestedDevices && requestedDevices.includes(device)) {
|
|
45
|
+
warnings.push(`No ${device} directory found - requested device will be skipped`);
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
for (const [sourceLang] of languages) {
|
|
50
|
+
const langDir = path.join(deviceDir, sourceLang);
|
|
51
|
+
try {
|
|
52
|
+
await fs.access(langDir);
|
|
53
|
+
const files = await fs.readdir(langDir);
|
|
54
|
+
const screenshots = files.filter(f => f.match(/\.(png|jpg|jpeg)$/i) && !f.startsWith('.'));
|
|
55
|
+
totalScreenshots += screenshots.length;
|
|
56
|
+
deviceCounts[device] += screenshots.length;
|
|
57
|
+
if (!languageCounts[sourceLang]) {
|
|
58
|
+
languageCounts[sourceLang] = 0;
|
|
59
|
+
}
|
|
60
|
+
languageCounts[sourceLang] += screenshots.length;
|
|
61
|
+
// Check for empty directories
|
|
62
|
+
if (screenshots.length === 0) {
|
|
63
|
+
warnings.push(`No screenshots found in ${device}/${sourceLang}/`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Language directory doesn't exist for this device
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// After checking all languages, warn if a requested device has no screenshots
|
|
71
|
+
if (requestedDevices && requestedDevices.includes(device) && deviceCounts[device] === 0 && deviceDirExists) {
|
|
72
|
+
warnings.push(`No screenshots found for requested device: ${device}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Check if any screenshots were found for requested devices
|
|
76
|
+
if (totalScreenshots === 0) {
|
|
77
|
+
if (requestedDevices && requestedDevices.length > 0) {
|
|
78
|
+
// User specifically requested devices, but none have screenshots
|
|
79
|
+
issues.push(`No screenshots found for requested devices: ${requestedDevices.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// No screenshots at all
|
|
83
|
+
issues.push('No screenshots found to export');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Validate language code mappings
|
|
87
|
+
for (const [source, target] of languages) {
|
|
88
|
+
if (!FASTLANE_LANGUAGES.has(target)) {
|
|
89
|
+
warnings.push(`Language code '${target}' (mapped from '${source}') may not be recognized by Fastlane. ` +
|
|
90
|
+
`Consider using one of: ${Array.from(FASTLANE_LANGUAGES).slice(0, 5).join(', ')}...`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Only check for missing iPhone if we're exporting all devices or iPhone is requested
|
|
94
|
+
const checkingIphone = !requestedDevices || requestedDevices.includes('iphone');
|
|
95
|
+
if (checkingIphone && deviceCounts.iphone === 0 && totalScreenshots > 0) {
|
|
96
|
+
warnings.push('No iPhone screenshots found. iPhone screenshots are typically required for App Store submission.');
|
|
97
|
+
}
|
|
98
|
+
// Only warn about App Store resolutions if we're actually exporting those devices
|
|
99
|
+
const exportingIphone = requestedDevices ? requestedDevices.includes('iphone') : true;
|
|
100
|
+
const exportingIpad = requestedDevices ? requestedDevices.includes('ipad') : true;
|
|
101
|
+
if ((exportingIphone && deviceCounts.iphone > 0) || (exportingIpad && deviceCounts.ipad > 0)) {
|
|
102
|
+
const requiredNote = 'Consider using `appshot build --preset iphone-6-9,ipad-13` for required App Store resolutions.';
|
|
103
|
+
if (!warnings.find(w => w.includes('required App Store resolutions'))) {
|
|
104
|
+
warnings.push(requiredNote);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
valid: issues.length === 0,
|
|
109
|
+
issues,
|
|
110
|
+
warnings,
|
|
111
|
+
stats: {
|
|
112
|
+
totalScreenshots,
|
|
113
|
+
deviceCounts,
|
|
114
|
+
languageCounts
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate output directory
|
|
120
|
+
*/
|
|
121
|
+
export async function validateOutputDirectory(outputPath, requireEmpty = false) {
|
|
122
|
+
const issues = [];
|
|
123
|
+
const warnings = [];
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(outputPath);
|
|
126
|
+
if (requireEmpty) {
|
|
127
|
+
const contents = await fs.readdir(outputPath);
|
|
128
|
+
if (contents.length > 0) {
|
|
129
|
+
warnings.push(`Output directory is not empty: ${outputPath}. ` +
|
|
130
|
+
'Use --clean to remove existing files.');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Directory doesn't exist, which is fine - we'll create it
|
|
136
|
+
}
|
|
137
|
+
// Check if we can write to the parent directory or create it
|
|
138
|
+
const parentDir = path.dirname(outputPath);
|
|
139
|
+
// Find the nearest existing ancestor directory
|
|
140
|
+
let checkDir = parentDir;
|
|
141
|
+
let foundExistingDir = false;
|
|
142
|
+
while (checkDir && checkDir !== path.dirname(checkDir)) {
|
|
143
|
+
try {
|
|
144
|
+
await fs.access(checkDir);
|
|
145
|
+
foundExistingDir = true;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Move up to parent
|
|
150
|
+
checkDir = path.dirname(checkDir);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// If we found an existing directory, check if it's writable
|
|
154
|
+
if (foundExistingDir) {
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(checkDir, fs.constants.W_OK);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
issues.push(`Cannot write to directory: ${checkDir}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// No parent directory found at all (shouldn't happen on normal systems)
|
|
164
|
+
issues.push(`No writable parent directory found for: ${outputPath}`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
valid: issues.length === 0,
|
|
168
|
+
issues,
|
|
169
|
+
warnings
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if a path is safe to clean (not a system directory)
|
|
174
|
+
*/
|
|
175
|
+
export function isSafeToClean(targetPath) {
|
|
176
|
+
const normalizedPath = path.resolve(targetPath);
|
|
177
|
+
const unsafePaths = [
|
|
178
|
+
'/',
|
|
179
|
+
'/Users',
|
|
180
|
+
'/System',
|
|
181
|
+
'/Applications',
|
|
182
|
+
'/Library',
|
|
183
|
+
'/usr',
|
|
184
|
+
'/bin',
|
|
185
|
+
'/sbin',
|
|
186
|
+
'/etc',
|
|
187
|
+
'/var',
|
|
188
|
+
'/tmp',
|
|
189
|
+
process.env.HOME || '',
|
|
190
|
+
path.resolve('.'), // Current directory
|
|
191
|
+
path.resolve('..') // Parent directory
|
|
192
|
+
].filter(Boolean);
|
|
193
|
+
// Check if path is a system directory
|
|
194
|
+
if (unsafePaths.includes(normalizedPath)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// Check if path is too short (likely a mistake)
|
|
198
|
+
const pathDepth = normalizedPath.split(path.sep).filter(Boolean).length;
|
|
199
|
+
if (pathDepth < 2) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
// Check if it's in the project directory
|
|
203
|
+
const projectRoot = process.cwd();
|
|
204
|
+
if (!normalizedPath.startsWith(projectRoot)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=export-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-validator.js","sourceRoot":"","sources":["../../src/services/export-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a Deliverfile for Fastlane
|
|
3
|
+
*/
|
|
4
|
+
export declare function generateDeliverfile(languages: string[], screenshotsPath?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Generate a Fastlane lane for appshot integration
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateFastfileLane(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Generate a README for Fastlane integration
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateFastlaneReadme(languages: string[]): string;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a .gitignore for Fastlane directory
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateFastlaneGitignore(): string;
|
|
17
|
+
//# sourceMappingURL=fastlane-config-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastlane-config-generator.d.ts","sourceRoot":"","sources":["../../src/services/fastlane-config-generator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EAAE,EACnB,eAAe,GAAE,MAAwB,GACxC,MAAM,CAqCR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA6I7C;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAwHlE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA6BlD"}
|