@o/cli 0.16.19 → 0.16.20

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.
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Reusable prompt helpers using @clack/prompts
3
+ */
4
+
5
+ import * as p from '@clack/prompts'
6
+ import pc from 'picocolors'
7
+
8
+ import type { PortCheck, PrerequisiteCheck } from '../types'
9
+
10
+ export function displayWelcome(projectName = 'Takeout'): void {
11
+ console.info()
12
+ p.intro(pc.bgCyan(pc.black(` ${projectName} Starter Kit `)))
13
+ console.info()
14
+ p.note(
15
+ pc.dim(
16
+ "You can re-run 'bun onboard' anytime to reconfigure or skip to specific steps.\nNo need to decide everything upfront!"
17
+ ),
18
+ pc.cyan('Tip')
19
+ )
20
+ }
21
+
22
+ export function displayOutro(message: string): void {
23
+ p.outro(pc.green(message))
24
+ }
25
+
26
+ export function displayPrerequisites(checks: PrerequisiteCheck[]): void {
27
+ const items = checks.map((check) => {
28
+ const icon = check.installed ? pc.green('✓') : pc.red('✗')
29
+ const message = check.message || ''
30
+ const recommendation = check.recommendation
31
+ ? `\n ${pc.dim(check.recommendation)}`
32
+ : ''
33
+ return `${icon} ${pc.bold(check.name)}: ${message}${recommendation}`
34
+ })
35
+
36
+ p.note(items.join('\n'), 'Prerequisites')
37
+ }
38
+
39
+ export function displayPortConflicts(conflicts: PortCheck[]): void {
40
+ if (conflicts.length === 0) return
41
+
42
+ const items = conflicts.map((conflict) => {
43
+ const pid = conflict.pid ? ` (PID: ${conflict.pid})` : ''
44
+ return `${pc.yellow('⚠')} Port ${conflict.port} (${conflict.name})${pid}`
45
+ })
46
+
47
+ p.note(items.join('\n'), pc.yellow('Port Conflicts Detected'))
48
+ }
49
+
50
+ export async function confirmContinue(
51
+ message: string,
52
+ defaultValue = true
53
+ ): Promise<boolean> {
54
+ const result = await p.confirm({
55
+ message,
56
+ initialValue: defaultValue,
57
+ })
58
+
59
+ if (p.isCancel(result)) {
60
+ p.cancel('Operation cancelled.')
61
+ process.exit(0)
62
+ }
63
+
64
+ return result
65
+ }
66
+
67
+ export async function promptText(
68
+ message: string,
69
+ defaultValue?: string,
70
+ placeholder?: string
71
+ ): Promise<string> {
72
+ const result = await p.text({
73
+ message,
74
+ defaultValue,
75
+ placeholder,
76
+ })
77
+
78
+ if (p.isCancel(result)) {
79
+ p.cancel('Operation cancelled.')
80
+ process.exit(0)
81
+ }
82
+
83
+ return result
84
+ }
85
+
86
+ export async function promptPassword(message: string): Promise<string> {
87
+ const result = await p.password({
88
+ message,
89
+ })
90
+
91
+ if (p.isCancel(result)) {
92
+ p.cancel('Operation cancelled.')
93
+ process.exit(0)
94
+ }
95
+
96
+ return result
97
+ }
98
+
99
+ export async function promptSelect<T extends string>(
100
+ message: string,
101
+ options: { value: T; label: string; hint?: string }[]
102
+ ): Promise<T | 'cancel'> {
103
+ const result = await p.select({
104
+ message,
105
+ options: options as any,
106
+ })
107
+
108
+ if (p.isCancel(result)) {
109
+ return 'cancel' as const
110
+ }
111
+
112
+ return result as T
113
+ }
114
+
115
+ export async function promptStartStep(
116
+ hasLocalPackages = true
117
+ ): Promise<
118
+ 'full' | 'prerequisites' | 'identity' | 'ports' | 'eject' | 'production' | 'cancel'
119
+ > {
120
+ console.info()
121
+ console.info(pc.gray('What would you like to do?'))
122
+ console.info()
123
+
124
+ return promptSelect('Select starting point:', [
125
+ {
126
+ value: 'full',
127
+ label: 'Complete setup',
128
+ hint: 'Run through all setup steps',
129
+ },
130
+ {
131
+ value: 'prerequisites',
132
+ label: 'Check prerequisites',
133
+ hint: 'Verify bun, docker, git, etc.',
134
+ },
135
+ {
136
+ value: 'identity',
137
+ label: 'Project identity',
138
+ hint: 'Customize app name and bundle ID',
139
+ },
140
+ {
141
+ value: 'ports',
142
+ label: 'Check ports',
143
+ hint: 'Verify required ports are available',
144
+ },
145
+ ...(hasLocalPackages
146
+ ? [
147
+ {
148
+ value: 'eject' as const,
149
+ label: 'Eject from monorepo',
150
+ hint: 'Remove ./packages, use published versions',
151
+ },
152
+ ]
153
+ : []),
154
+ {
155
+ value: 'production',
156
+ label: 'Production deployment',
157
+ hint: 'Configure production environment and deployment',
158
+ },
159
+ ])
160
+ }
161
+
162
+ export async function promptOldSelect<T extends string>(
163
+ message: string,
164
+ options: { value: T; label: string; hint?: string }[]
165
+ ): Promise<T> {
166
+ const result = await p.select<string>({
167
+ message,
168
+ options,
169
+ })
170
+
171
+ if (p.isCancel(result)) {
172
+ p.cancel('Operation cancelled.')
173
+ process.exit(0)
174
+ }
175
+
176
+ return result as T
177
+ }
178
+
179
+ export function showSpinner(message: string): ReturnType<typeof p.spinner> {
180
+ const s = p.spinner()
181
+ s.start(message)
182
+ return s
183
+ }
184
+
185
+ export function showError(message: string): void {
186
+ p.log.error(pc.red(message))
187
+ }
188
+
189
+ export function showWarning(message: string): void {
190
+ p.log.warning(pc.yellow(message))
191
+ }
192
+
193
+ export function showSuccess(message: string): void {
194
+ p.log.success(pc.green(message))
195
+ }
196
+
197
+ export function showInfo(message: string): void {
198
+ p.log.info(pc.blue(message))
199
+ }
200
+
201
+ export function showStep(message: string): void {
202
+ p.log.step(message)
203
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Utility functions for listing and discovering scripts
3
+ */
4
+
5
+ import { existsSync, statSync } from 'node:fs'
6
+ import { join, relative } from 'node:path'
7
+ import { fileURLToPath } from 'node:url'
8
+
9
+ import pc from 'picocolors'
10
+
11
+ import {
12
+ type ScriptMetadata,
13
+ discoverScripts,
14
+ getAllScriptMetadata,
15
+ getLocalScriptsDir,
16
+ } from './script-utils'
17
+
18
+ // find scripts package root using import.meta.resolve
19
+ function findScriptsPackageRoot(): string | null {
20
+ try {
21
+ const resolved = import.meta.resolve('@o/scripts/package.json')
22
+ const packageJsonPath = fileURLToPath(new URL(resolved))
23
+ const packageRoot = join(packageJsonPath, '..')
24
+ const srcPath = join(packageRoot, 'src')
25
+
26
+ if (existsSync(srcPath)) {
27
+ return srcPath
28
+ }
29
+ } catch {
30
+ // scripts package not found
31
+ }
32
+
33
+ return null
34
+ }
35
+
36
+ // format script listing with pre-fetched metadata
37
+ function formatScriptList(
38
+ title: string,
39
+ scripts: Map<string, string>,
40
+ metadata: Map<string, ScriptMetadata>
41
+ ): void {
42
+ if (scripts.size === 0) return
43
+
44
+ console.info()
45
+ console.info(pc.bold(pc.cyan(title)))
46
+ console.info()
47
+
48
+ const categories = new Map<string, Array<[string, string]>>()
49
+ const rootScripts: Array<[string, string]> = []
50
+
51
+ for (const [name, path] of scripts) {
52
+ if (name.includes('/')) {
53
+ const category = name.split('/')[0]!
54
+ if (!categories.has(category)) {
55
+ categories.set(category, [])
56
+ }
57
+ categories.get(category)!.push([name, path])
58
+ } else {
59
+ rootScripts.push([name, path])
60
+ }
61
+ }
62
+
63
+ for (const [name] of rootScripts) {
64
+ let line = ` ${pc.green(name)}`
65
+ const meta = metadata.get(name)
66
+ if (meta?.description) {
67
+ line += pc.dim(` - ${meta.description}`)
68
+ }
69
+ console.info(line)
70
+ }
71
+
72
+ for (const [category, categoryScripts] of categories) {
73
+ console.info()
74
+ console.info(` ${pc.yellow(category)}/`)
75
+
76
+ for (const [name] of categoryScripts) {
77
+ const shortName = name.substring(category.length + 1)
78
+ let line = ` ${pc.green(shortName)}`
79
+ const meta = metadata.get(name)
80
+ if (meta?.description) {
81
+ line += pc.dim(` - ${meta.description}`)
82
+ }
83
+ console.info(line)
84
+ }
85
+ }
86
+ }
87
+
88
+ // list all available scripts
89
+ export async function listAllScripts(includeCommands = true) {
90
+ console.info()
91
+ console.info(pc.bold(pc.cyan('Takeout CLI - Project Scripts & Commands')))
92
+ console.info()
93
+ console.info(pc.dim(' Manage scripts, run tasks, and configure your project'))
94
+ console.info()
95
+
96
+ if (includeCommands) {
97
+ console.info(pc.bold(pc.cyan('Built-in Commands')))
98
+ console.info()
99
+ console.info(` ${pc.green('onboard')} - Setup wizard for new projects`)
100
+ console.info(` ${pc.green('docs')} - View documentation`)
101
+ console.info(` ${pc.green('env:setup')} - Setup environment variables`)
102
+ console.info(` ${pc.green('run')} - Run scripts in parallel`)
103
+ console.info(` ${pc.green('script')} - Manage and run scripts`)
104
+ console.info(` ${pc.green('skills')} - Manage Claude Code skills`)
105
+ console.info(` ${pc.green('sync')} - Sync fork with upstream Takeout`)
106
+ console.info(` ${pc.green('changed')} - Show changes since last sync`)
107
+ console.info(` ${pc.green('completion')} - Shell completion setup`)
108
+ }
109
+
110
+ const localScripts = discoverScripts(getLocalScriptsDir())
111
+ const builtInDir = findScriptsPackageRoot()
112
+ const builtInScripts = builtInDir ? discoverScripts(builtInDir) : new Map()
113
+
114
+ const allScripts = new Map([...localScripts, ...builtInScripts])
115
+ const metadata = await getAllScriptMetadata(allScripts)
116
+
117
+ if (localScripts.size > 0) {
118
+ formatScriptList('Local Scripts', localScripts, metadata)
119
+ }
120
+
121
+ if (builtInScripts.size > 0) {
122
+ formatScriptList('Built-in Scripts', builtInScripts, metadata)
123
+ }
124
+
125
+ if (localScripts.size === 0 && builtInScripts.size === 0) {
126
+ console.info()
127
+ console.info(pc.yellow('No scripts found'))
128
+ console.info()
129
+ console.info(
130
+ pc.dim(`Create scripts in ${relative(process.cwd(), getLocalScriptsDir())}/`)
131
+ )
132
+ console.info(pc.dim(`Or install @o/scripts package for built-in scripts`))
133
+ }
134
+
135
+ console.info()
136
+ console.info(pc.bold('Usage:'))
137
+ console.info(
138
+ ` ${pc.cyan('tko <command>')} ${pc.dim('Run a built-in command')}`
139
+ )
140
+ console.info(
141
+ ` ${pc.cyan('tko <script-name>')} ${pc.dim('Execute direct script')}`
142
+ )
143
+ console.info(
144
+ ` ${pc.cyan('tko <group> <script>')} ${pc.dim('Execute nested script')}`
145
+ )
146
+ console.info(` ${pc.cyan('tko script new <path>')} ${pc.dim('Create a new script')}`)
147
+ console.info()
148
+ }
149
+
150
+ // check if a name is a category and list its scripts
151
+ export async function listCategoryScripts(categoryName: string): Promise<boolean> {
152
+ const localDir = getLocalScriptsDir()
153
+ const categoryPath = join(localDir, categoryName)
154
+
155
+ if (existsSync(categoryPath) && statSync(categoryPath).isDirectory()) {
156
+ const categoryScripts = discoverScripts(categoryPath)
157
+
158
+ if (categoryScripts.size > 0) {
159
+ const metadata = await getAllScriptMetadata(categoryScripts)
160
+
161
+ console.info()
162
+ console.info(pc.bold(pc.cyan(`${categoryName} Scripts`)))
163
+ console.info()
164
+
165
+ for (const [name] of categoryScripts) {
166
+ const shortName = name.replace(`${categoryName}/`, '')
167
+ const meta = metadata.get(name)
168
+ let line = ` ${pc.green(shortName)}`
169
+
170
+ if (meta?.description) {
171
+ line += pc.dim(` - ${meta.description}`)
172
+ }
173
+
174
+ if (meta?.args && meta.args.length > 0) {
175
+ line += pc.dim(` [${meta.args.join(', ')}]`)
176
+ }
177
+
178
+ console.info(line)
179
+ }
180
+
181
+ console.info()
182
+ console.info(pc.dim(`Run: tko ${categoryName}/<name> [args...] to execute`))
183
+ console.info()
184
+ } else {
185
+ console.info()
186
+ console.info(pc.yellow(`No scripts found in ${categoryName}/`))
187
+ console.info()
188
+ }
189
+
190
+ return true
191
+ }
192
+
193
+ return false
194
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Core utility functions for script discovery and metadata
3
+ */
4
+
5
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
6
+ import { join, relative } from 'node:path'
7
+
8
+ import { setInterceptCmd } from '@o/scripts/cmd'
9
+
10
+ export interface ScriptMetadata {
11
+ description?: string
12
+ args?: string[]
13
+ }
14
+
15
+ // get local scripts directory
16
+ export function getLocalScriptsDir(): string {
17
+ return join(process.cwd(), 'scripts')
18
+ }
19
+
20
+ // batch extract metadata for all scripts via import interception
21
+ export async function getAllScriptMetadata(
22
+ scripts: Map<string, string>
23
+ ): Promise<Map<string, ScriptMetadata>> {
24
+ const collected = new Map<string, { description: string; args?: string }>()
25
+ let currentName = ''
26
+
27
+ // interceptor stays set — never cleared so floating .run() promises always bail
28
+ setInterceptCmd((info) => {
29
+ collected.set(currentName, info)
30
+ })
31
+
32
+ for (const [name, path] of scripts) {
33
+ // only import scripts that use cmd (safe to import)
34
+ try {
35
+ const head = readFileSync(path, 'utf-8').slice(0, 200)
36
+ if (!head.includes('cmd')) continue
37
+ } catch {
38
+ continue
39
+ }
40
+
41
+ currentName = name
42
+ try {
43
+ await import(path)
44
+ } catch {}
45
+ }
46
+
47
+ const results = new Map<string, ScriptMetadata>()
48
+ for (const [name, info] of collected) {
49
+ const metadata: ScriptMetadata = { description: info.description }
50
+ if (info.args) {
51
+ const flags = [...info.args.matchAll(/--?([a-z-]+)/gi)]
52
+ if (flags.length > 0) {
53
+ metadata.args = flags.map((m) => `--${m[1]}`)
54
+ }
55
+ }
56
+ results.set(name, metadata)
57
+ }
58
+
59
+ return results
60
+ }
61
+
62
+ // discover scripts in a directory (no grandchildren)
63
+ export function discoverScripts(dir: string, baseDir: string = dir): Map<string, string> {
64
+ const scripts = new Map<string, string>()
65
+
66
+ if (!existsSync(dir)) {
67
+ return scripts
68
+ }
69
+
70
+ try {
71
+ const entries = readdirSync(dir)
72
+
73
+ for (const entry of entries) {
74
+ const fullPath = join(dir, entry)
75
+ const stat = statSync(fullPath)
76
+
77
+ if (stat.isDirectory()) {
78
+ if (entry === 'helpers' || entry === 'internal') continue
79
+ // one level deep only
80
+ const subEntries = readdirSync(fullPath)
81
+ for (const subEntry of subEntries) {
82
+ const subPath = join(fullPath, subEntry)
83
+ if (
84
+ statSync(subPath).isFile() &&
85
+ (subEntry.endsWith('.ts') || subEntry.endsWith('.js'))
86
+ ) {
87
+ const relativePath = relative(baseDir, subPath).split('\\').join('/')
88
+ const scriptName = relativePath.replace(/\.(ts|js)$/, '')
89
+ scripts.set(scriptName, subPath)
90
+ }
91
+ }
92
+ } else if (entry.endsWith('.ts') || entry.endsWith('.js')) {
93
+ const relativePath = relative(baseDir, fullPath).split('\\').join('/')
94
+ const scriptName = relativePath.replace(/\.(ts|js)$/, '')
95
+ scripts.set(scriptName, fullPath)
96
+ }
97
+ }
98
+ } catch {
99
+ // ignore directory read errors
100
+ }
101
+
102
+ return scripts
103
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Sync utilities for updating docs and scripts between repositories
3
+ */
4
+
5
+ import { createHash } from 'node:crypto'
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs'
7
+ import { join, relative, dirname } from 'node:path'
8
+
9
+ import { confirm } from '@clack/prompts'
10
+ import pc from 'picocolors'
11
+
12
+ export interface FileToSync {
13
+ name: string
14
+ sourcePath: string
15
+ targetPath: string
16
+ status: 'new' | 'modified' | 'identical'
17
+ sourceSize?: number
18
+ targetSize?: number
19
+ }
20
+
21
+ export function getFileHash(filePath: string): string {
22
+ const content = readFileSync(filePath)
23
+ return createHash('md5').update(content).digest('hex')
24
+ }
25
+
26
+ export function compareFiles(
27
+ sourcePath: string,
28
+ targetPath: string
29
+ ): 'new' | 'modified' | 'identical' {
30
+ if (!existsSync(targetPath)) {
31
+ return 'new'
32
+ }
33
+
34
+ const sourceHash = getFileHash(sourcePath)
35
+ const targetHash = getFileHash(targetPath)
36
+
37
+ return sourceHash === targetHash ? 'identical' : 'modified'
38
+ }
39
+
40
+ export async function syncFileWithConfirmation(file: FileToSync): Promise<boolean> {
41
+ const relPath = relative(process.cwd(), file.targetPath)
42
+
43
+ if (file.status === 'identical') {
44
+ console.info(pc.dim(` ✓ ${file.name} (already up to date)`))
45
+ return false
46
+ }
47
+
48
+ const statusColor = file.status === 'new' ? pc.green : pc.yellow
49
+ const statusText = file.status === 'new' ? 'NEW' : 'MODIFIED'
50
+
51
+ console.info()
52
+ console.info(statusColor(` ${statusText}: ${file.name}`))
53
+
54
+ if (file.status === 'modified') {
55
+ const sourceSize = file.sourceSize || 0
56
+ const targetSize = file.targetSize || 0
57
+ const diff = sourceSize - targetSize
58
+
59
+ console.info(pc.dim(` Source: ${sourceSize} bytes`))
60
+ console.info(pc.dim(` Target: ${targetSize} bytes`))
61
+
62
+ if (diff > 0) {
63
+ console.info(pc.cyan(` Source is ${diff} bytes larger`))
64
+ } else if (diff < 0) {
65
+ console.info(pc.magenta(` Target is ${Math.abs(diff)} bytes larger`))
66
+ }
67
+ }
68
+
69
+ console.info(pc.dim(` → ${relPath}`))
70
+
71
+ const shouldSync = await confirm({
72
+ message: `Overwrite this file?`,
73
+ initialValue: true,
74
+ })
75
+
76
+ if (shouldSync === false || typeof shouldSync === 'symbol') {
77
+ console.info(pc.dim(` ⊘ Skipped`))
78
+ return false
79
+ }
80
+
81
+ // ensure target directory exists
82
+ const targetDir = dirname(file.targetPath)
83
+ if (!existsSync(targetDir)) {
84
+ mkdirSync(targetDir, { recursive: true })
85
+ }
86
+
87
+ // copy file
88
+ const content = readFileSync(file.sourcePath)
89
+ writeFileSync(file.targetPath, content)
90
+
91
+ console.info(pc.green(` ✓ Synced`))
92
+ return true
93
+ }
94
+
95
+ export function getFileSize(filePath: string): number {
96
+ try {
97
+ return statSync(filePath).size
98
+ } catch {
99
+ return 0
100
+ }
101
+ }
@@ -0,0 +1,58 @@
1
+ const { withDangerousMod } = require('@expo/config-plugins')
2
+ const fs = require('node:fs')
3
+ const path = require('node:path')
4
+
5
+ /**
6
+ * expo config plugin to fix expo-modules-core 55.x swift 6 strict concurrency errors
7
+ * that break TurboModule registration (PlatformConstants not found)
8
+ * adapted from tamagui/kitchen-sink
9
+ * see: https://github.com/expo/expo/issues/42525
10
+ */
11
+ function withExpoModulesCoreSwift6Fix(config) {
12
+ return withDangerousMod(config, [
13
+ 'ios',
14
+ async (config) => {
15
+ const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile')
16
+
17
+ if (!fs.existsSync(podfilePath)) {
18
+ return config
19
+ }
20
+
21
+ let podfile = fs.readFileSync(podfilePath, 'utf8')
22
+
23
+ if (podfile.includes('# workaround: expo-modules-core 55.x')) {
24
+ return config
25
+ }
26
+
27
+ const workaround = `
28
+ # workaround: expo-modules-core 55.x requires Swift 6 mode with isolated
29
+ # conformances (SE-0470) for @MainActor in protocol conformance syntax.
30
+ # SWIFT_STRICT_CONCURRENCY=minimal suppresses concurrency warnings/errors.
31
+ installer.pods_project.targets.each do |target|
32
+ if target.name == 'ExpoModulesCore'
33
+ target.build_configurations.each do |build_config|
34
+ build_config.build_settings['SWIFT_VERSION'] = '6'
35
+ build_config.build_settings['SWIFT_STRICT_CONCURRENCY'] = 'minimal'
36
+ flags = build_config.build_settings['OTHER_SWIFT_FLAGS'] || '$(inherited)'
37
+ unless flags.include?('IsolatedConformances')
38
+ build_config.build_settings['OTHER_SWIFT_FLAGS'] = "#{flags} -enable-upcoming-feature IsolatedConformances"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ `
44
+ const updated = podfile.replace(
45
+ /(post_install do \|installer\|.*?)(^\s+end\s*\nend)/ms,
46
+ `$1${workaround}$2`
47
+ )
48
+
49
+ if (updated !== podfile) {
50
+ fs.writeFileSync(podfilePath, updated)
51
+ }
52
+
53
+ return config
54
+ },
55
+ ])
56
+ }
57
+
58
+ module.exports = withExpoModulesCoreSwift6Fix
@@ -0,0 +1,51 @@
1
+ const { withDangerousMod } = require('@expo/config-plugins')
2
+ const { mergeContents } = require('@expo/config-plugins/build/utils/generateCode')
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ /**
7
+ * expo config plugin to add pre_install hook for op-sqlite static framework compilation
8
+ * required when using useFrameworks: 'static'
9
+ * based on: https://github.com/OP-Engineering/op-sqlite/issues/238
10
+ */
11
+ function withOpSqliteStatic(config) {
12
+ return withDangerousMod(config, [
13
+ 'ios',
14
+ async (config) => {
15
+ const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile')
16
+
17
+ if (!fs.existsSync(podfilePath)) {
18
+ return config
19
+ }
20
+
21
+ const podfileContent = fs.readFileSync(podfilePath, 'utf-8')
22
+
23
+ const setOpSqliteStatic = mergeContents({
24
+ tag: 'op-sqlite-static-library',
25
+ src: podfileContent,
26
+ newSrc: `pre_install do |installer|
27
+ installer.pod_targets.each do |pod|
28
+ if pod.name.eql?('op-sqlite')
29
+ def pod.build_type
30
+ Pod::BuildType.static_library
31
+ end
32
+ end
33
+ end
34
+ end`,
35
+ anchor: /platform :ios.*/,
36
+ offset: 1,
37
+ comment: '#',
38
+ })
39
+
40
+ if (!setOpSqliteStatic.didMerge) {
41
+ return config
42
+ }
43
+
44
+ fs.writeFileSync(podfilePath, setOpSqliteStatic.contents)
45
+
46
+ return config
47
+ },
48
+ ])
49
+ }
50
+
51
+ module.exports = withOpSqliteStatic